Compare commits

...

28 Commits

Author SHA1 Message Date
nou 32f91d7b2f Add PCL:AstrometricSolution to XISF solved files 2025-08-03 20:58:01 +02:00
nou 69fbad34b6 Add image index to FITSRecordModify 2025-08-03 20:57:33 +02:00
nou e026042604 Use ifFITS isXISF functions 2025-08-03 20:37:40 +02:00
nou bb7e5182af Fix metainfo linter 2025-07-27 17:45:23 +02:00
nou f0152e2496 Fix compilation issue 2025-07-27 17:33:14 +02:00
nou eccf928032 Make path in file manager editable 2025-07-27 16:29:44 +02:00
nou 897306d1c3 Add fitskeyword.ui 2025-07-27 15:40:06 +02:00
nou cbc779090f Prioritize M number then IC for object name 2025-07-27 15:36:19 +02:00
nou d826744f26 Add tabs to file manager 2025-07-27 15:35:25 +02:00
nou 3bdfb12d4f Disable use of QFileSystemModel on ARM platform 2025-07-27 10:06:38 +02:00
nou c416ae9941 Fix suffix handling, do not index PCL: properties 2025-07-26 18:19:46 +02:00
nou abbba2890f Add context menu to hide columns in file manager 2025-07-26 17:50:54 +02:00
nou 9c6847d334 Do not pass XISF properies into WCS 2025-07-26 15:49:21 +02:00
nou af1c26a9fe Use PCL:AstrometricSolution properties to construct WCS 2025-07-26 15:48:58 +02:00
nou e0441a6494 Change type to uint64_t 2025-07-24 20:22:06 +02:00
nou a88f05a9fe Add filemanager 2025-07-21 20:16:34 +02:00
nou b58559a18a Update modify FITS header script 2025-07-13 10:43:15 +02:00
nou 2ac14a6c04 Fix thumbnailer compilation 2025-07-13 10:42:59 +02:00
nou b84256625c Add stellarsolver6 as name 2025-06-12 16:48:09 +02:00
nou 202a2b11b7 Add marked files in batch processing 2025-06-09 19:09:50 +02:00
nou 32f192ed7e Add draw grid button 2025-05-31 00:19:15 +02:00
nou a0422683bd Move source files to src directory 2025-05-30 16:49:33 +02:00
nou ce67b35bfa Mention OpenNGC 2025-05-29 23:44:55 +02:00
nou f016500f12 Include ngc db 2025-05-29 17:39:13 +02:00
nou 6069ebbbac Refractor drawing grid 2025-05-27 16:26:03 +02:00
nou e587d84e05 Remove that empty action 2025-05-26 17:05:19 +02:00
nou c01f2e328a Add sky grid painting 2025-05-26 15:50:37 +02:00
nou 8b498bbe73 Prefer writing keyword as integer 2025-05-24 23:13:14 +02:00
77 changed files with 1592 additions and 101 deletions
+35 -33
View File
@@ -23,39 +23,41 @@ find_library(FITS_LIB cfitsio REQUIRED)
find_library(RAW_LIB NAMES raw_r REQUIRED) find_library(RAW_LIB NAMES raw_r REQUIRED)
find_library(WCS_LIB wcs wcslib REQUIRED) find_library(WCS_LIB wcs wcslib REQUIRED)
find_library(LCMS2_LIB lcms2 REQUIRED) find_library(LCMS2_LIB lcms2 REQUIRED)
find_library(STELLARSOLVER_LIB stellarsolver) find_library(STELLARSOLVER_LIB NAMES stellarsolver stellarsolver6)
add_subdirectory(libXISF) add_subdirectory(libXISF)
set(TENMON_SRC set(TENMON_SRC
about.cpp about.h src/about.cpp src/about.h
batchprocessing.cpp batchprocessing.h batchprocessing.ui src/batchprocessing.cpp src/batchprocessing.h src/batchprocessing.ui
chartgraph.h chartgraph.cpp src/chartgraph.h src/chartgraph.cpp
database.cpp database.h src/database.cpp src/database.h
databaseview.cpp databaseview.h src/databaseview.cpp src/databaseview.h
delete.cpp src/delete.cpp
filesystemwidget.cpp filesystemwidget.h src/filemanager.h src/filemanager.cpp src/filemanager.ui
histogram.cpp histogram.h src/filesystemwidget.cpp src/filesystemwidget.h
httpdownloader.h httpdownloader.cpp src/fitskeyword.ui
imageinfo.cpp imageinfo.h src/histogram.cpp src/histogram.h
imageinfodata.cpp imageinfodata.h src/httpdownloader.h src/httpdownloader.cpp
imageringlist.cpp imageringlist.h src/imageinfo.cpp src/imageinfo.h
imagescrollarea.cpp imagescrollarea.h src/imageinfodata.cpp src/imageinfodata.h
imagewidget.h imagewidget.cpp src/imageringlist.cpp src/imageringlist.h
loadimage.h loadimage.cpp src/imagescrollarea.cpp src/imagescrollarea.h
loadrunable.cpp loadrunable.h src/imagewidget.h src/imagewidget.cpp
main.cpp src/loadimage.h src/loadimage.cpp
mainwindow.cpp mainwindow.h src/loadrunable.cpp src/loadrunable.h
markedfiles.cpp markedfiles.h src/main.cpp
mtfparam.h src/mainwindow.cpp src/mainwindow.h
rawimage.cpp rawimage.h src/markedfiles.cpp src/markedfiles.h
rawimage_sse.cpp src/mtfparam.h
scriptengine.cpp scriptengine.h src/rawimage.cpp src/rawimage.h
settingsdialog.cpp settingsdialog.h src/rawimage_sse.cpp
statusbar.cpp statusbar.h src/scriptengine.cpp src/scriptengine.h
stfslider.cpp stfslider.h src/settingsdialog.cpp src/settingsdialog.h
stretchtoolbar.cpp stretchtoolbar.h src/statusbar.cpp src/statusbar.h
tfloat16.h src/stfslider.cpp src/stfslider.h
src/stretchtoolbar.cpp src/stretchtoolbar.h
src/tfloat16.h
thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h
) )
@@ -77,7 +79,7 @@ endif()
qt_add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC}) qt_add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED) find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR}) target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR} "src")
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON) option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
if(COLOR_MANAGMENT) if(COLOR_MANAGMENT)
@@ -96,9 +98,9 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
endif(MXE) endif(MXE)
target_compile_definitions(tenmon PRIVATE "PLATESOLVER") target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
target_sources(tenmon PRIVATE target_sources(tenmon PRIVATE
solver.cpp solver.h src/solver.cpp src/solver.h
platesolving.cpp platesolving.h platesolving.ui src/platesolving.cpp src/platesolving.h src/platesolving.ui
platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui src/platesolvingsettings.cpp src/platesolvingsettings.h src/platesolvingsettings.ui
) )
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}") message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB) endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
+2
View File
@@ -29,3 +29,5 @@ Then to build run standard cmake sequence
For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver For working plate solving you must have compiled and installed StellarSolver https://github.com/rlancaste/stellarsolver
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
crash. crash.
Using OpenNGC database https://github.com/mattiaverga/OpenNGC under CC-BY-SA-4.0 https://creativecommons.org/licenses/by-sa/4.0/
+1 -1
Submodule libXISF updated: 9a32138f6a...c6581e1122
+141
View File
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 12.699999 12.699999"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="grid.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="9.5144352"
inkscape:cx="39.361243"
inkscape:cy="25.067174"
inkscape:window-width="2510"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<inkscape:path-effect
effect="skeletal"
id="path-effect636"
is_visible="true"
lpeversion="1"
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
hide_knot="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect632"
is_visible="true"
lpeversion="1"
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
hide_knot="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="skeletal"
id="path-effect628"
is_visible="true"
lpeversion="1"
pattern="M 0,4.992138 C 0,2.2364778 2.2364778,0 4.992138,0 c 2.7556601,0 4.9921379,2.2364778 4.9921379,4.992138 0,2.7556601 -2.2364778,4.9921379 -4.9921379,4.9921379 C 2.2364778,9.9842759 0,7.7477981 0,4.992138 Z"
copytype="single_stretched"
prop_scale="1"
scale_y_rel="false"
spacing="0"
normal_offset="0"
tang_offset="0"
prop_units="false"
vertical_pattern="false"
hide_knot="false"
fuse_tolerance="0" />
<inkscape:path-effect
effect="bspline"
id="path-effect624"
is_visible="true"
lpeversion="1"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="spiro"
id="path-effect620"
is_visible="true"
lpeversion="1" />
</defs>
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-dasharray:none"
d="M -5,-5 13,13"
id="path616"
sodipodi:nodetypes="cc" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-opacity:1;stroke-dasharray:none"
id="path643"
cx="-4.9824347"
cy="-4.9865055"
r="12.973718" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path1665"
cx="-4.9600825"
cy="-4.9741392"
r="17.086035" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path1667"
cx="-5.0079365"
cy="-5.0034046"
r="21.147657" />
<path
style="fill:none;stroke:#000000;stroke-width:0.467;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-dasharray:none"
d="M 14.371451,3.5622727 -4.9904999,-5.0054782 4.2432806,13.903978"
id="path1734" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.503;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path1736"
cx="-5.155458"
cy="-5.1256938"
r="9.6808758" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
View File
Binary file not shown.
+5 -3
View File
@@ -19,14 +19,16 @@
<file>../translations/tenmon_pt_BR.qm</file> <file>../translations/tenmon_pt_BR.qm</file>
<file alias="help">../about/help_en</file> <file alias="help">../about/help_en</file>
<file>colormap.png</file> <file>colormap.png</file>
<file>ngc.db</file>
<file>grid.svg</file>
</qresource> </qresource>
<qresource prefix="/" lang="en"> <qresource lang="en" prefix="/">
<file alias="help">../about/help_en</file> <file alias="help">../about/help_en</file>
</qresource> </qresource>
<qresource prefix="/" lang="sk"> <qresource lang="sk" prefix="/">
<file alias="help">../about/help_sk</file> <file alias="help">../about/help_sk</file>
</qresource> </qresource>
<qresource prefix="/" lang="fr"> <qresource lang="fr" prefix="/">
<file alias="help">../about/help_fr</file> <file alias="help">../about/help_fr</file>
</qresource> </qresource>
</RCC> </RCC>
+45 -12
View File
@@ -12,10 +12,12 @@ if(files.length == 0)
throw ""; throw "";
} }
let action = core.getItem(["UPDATE", "ADD", "REMOVE"], "Do you want update, add or remove record?"); let action = core.getItem(["UPDATE", "UPDATE_ADD", "ADD", "REMOVE"], "Do you want update, add or remove record?");
let modify = new FITSRecordModify(); let modify = new FITSRecordModify();
let proceed = false;
if(action == "UPDATE") if(action == "UPDATE")
{ {
let keywords = files[0].fitsKeywords().filter(checkFITS); let keywords = files[0].fitsKeywords().filter(checkFITS);
@@ -25,28 +27,59 @@ if(action == "UPDATE")
value = core.getString("Enter new value", value); value = core.getString("Enter new value", value);
else else
value = core.getFloat("Enter new value", value); value = core.getFloat("Enter new value", value);
modify.updateKeyword(keyword, value);
if(keyword && value)
{
proceed = true;
modify.updateKeyword(keyword, value);
}
}
else if(action == "UPDATE_ADD")
{
let keyword = core.getString("Enter keyword to update");
let value = core.getString("Enter new value");
if(keyword && value)
{
proceed = true;
keyword = keyword.toUpperCase();
modify.updateKeyword(keyword, value);
}
} }
else if(action == "ADD") else if(action == "ADD")
{ {
let keyword = core.getString("Enter keyword to add"); let keyword = core.getString("Enter keyword to add");
let value = core.getString("Enter new value"); let value = core.getString("Enter new value");
keyword = keyword.toUpperCase(); if(keyword && value)
modify.addKeyword(keyword, value); {
proceed = true;
keyword = keyword.toUpperCase();
modify.addKeyword(keyword, value);
}
} }
else if(action == "REMOVE") else if(action == "REMOVE")
{ {
let keywords = files[0].fitsKeywords().filter(checkFITS); let keywords = files[0].fitsKeywords().filter(checkFITS);
let keyword = core.getItem(keywords, "Select keyword to remove"); let keyword = core.getItem(keywords, "Select keyword to remove");
modify.removeKeyword(keyword); if(keyword)
}
for(file of files)
{
if(file.suffix() == "fits" || file.suffix() == "fit" || file.suffix() == "xisf")
{ {
core.log("Modifing " + file.fileName()); proceed = true;
file.modifyFITSRecords(modify); modify.removeKeyword(keyword);
} }
} }
if(proceed)
{
for(file of files)
{
if(file.suffix() == "fits" || file.suffix() == "fit" || file.suffix() == "xisf")
{
core.log("Modifing " + file.fileName());
file.modifyFITSRecords(modify);
}
}
}
else
{
core.log("Canceled");
}
+1 -1
View File
@@ -118,7 +118,7 @@
</release> </release>
<release version="20240816" date="2024-08-16"> <release version="20240816" date="2024-08-16">
<description> <description>
Fix saving image <p>Fix saving image</p>
</description> </description>
</release> </release>
<release version="20240616" date="2024-06-16"> <release version="20240616" date="2024-06-16">
View File
View File
@@ -100,6 +100,7 @@ BatchProcessing::BatchProcessing(Database *database, QWidget *parent) : QDialog(
connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles); connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles);
connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir); connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir);
connect(_ui->addMarkedButton, &QPushButton::released, this, &BatchProcessing::addMarked);
connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath); connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath);
connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths); connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths);
connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript); connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript);
@@ -164,6 +165,17 @@ void BatchProcessing::addDir()
} }
} }
void BatchProcessing::addMarked()
{
QStringList files = _database->getMarkedFiles();
for(const QString &file : files)
{
QFileInfo info(file);
if(info.exists() && info.isReadable())
_ui->pathsList->addItem(file);
};
}
void BatchProcessing::removePath() void BatchProcessing::removePath()
{ {
for(auto &item : _ui->pathsList->selectedItems()) for(auto &item : _ui->pathsList->selectedItems())
@@ -28,6 +28,7 @@ protected:
public slots: public slots:
void addFiles(); void addFiles();
void addDir(); void addDir();
void addMarked();
void removePath(); void removePath();
void removeAllPaths(); void removeAllPaths();
void browse(); void browse();
@@ -52,6 +52,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="addMarkedButton">
<property name="text">
<string>Add marked</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="removeButton"> <widget class="QPushButton" name="removeButton">
<property name="text"> <property name="text">
View File
+75 -6
View File
@@ -16,10 +16,29 @@ bool Database::init(const QLatin1String &connectionName)
QDir dir(path); QDir dir(path);
database = QSqlDatabase::addDatabase("QSQLITE", connectionName); database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
ngc = QSqlDatabase::addDatabase("QSQLITE", connectionName + "ngc");
if(!dir.mkpath(".")) if(!dir.mkpath("."))
return false; return false;
if(ngc.isValid())
{
QString ngcDb = dir.absoluteFilePath("ngc.db");
if(!QFile::exists(ngcDb))
QFile::copy(":/ngc.db", ngcDb);
ngc.setDatabaseName(ngcDb);
if(ngc.open())
{
m_getNgc = QSqlQuery(ngc);
m_getNgc.prepare("SELECT *,IIF(V_Mag IS NULL, B_Mag, V_Mag) AS mag FROM ngc WHERE RA_deg BETWEEN ? AND ? AND DEC_deg BETWEEN ? AND ?");
}
else
{
qDebug() << "Could not open NGC database";
}
}
if(database.isValid()) if(database.isValid())
{ {
database.setDatabaseName(dir.absoluteFilePath("database2.db")); database.setDatabaseName(dir.absoluteFilePath("database2.db"));
@@ -162,7 +181,7 @@ int Database::checkVersion(QSqlDatabase &db)
return -1; return -1;
} }
static QStringList nameFilters = {"*.fit", "*.fits", "*.fz", "*.xisf"}; static QStringList nameFilters = {"*.fit", "*.fits", "*.fz", "*.fts", "*.xisf"};
static int countFiles(const QDir &dir, QStringList &scannedDirs) static int countFiles(const QDir &dir, QStringList &scannedDirs)
{ {
@@ -182,7 +201,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
QStringList scannedDirs; QStringList scannedDirs;
int count = countFiles(dir, scannedDirs); int count = countFiles(dir, scannedDirs);
progress->setMaximum(count); progress->setMaximum(count);
QSqlDatabase database = QSqlDatabase::database();
database.transaction(); database.transaction();
scannedDirs.clear(); scannedDirs.clear();
@@ -200,7 +218,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
void Database::reindex(QProgressDialog *progress) void Database::reindex(QProgressDialog *progress)
{ {
QVariantList deleteids; QVariantList deleteids;
QSqlDatabase database = QSqlDatabase::database();
database.transaction(); database.transaction();
QSqlQuery size("SELECT COUNT(*) FROM fits_files", database); QSqlQuery size("SELECT COUNT(*) FROM fits_files", database);
size.next(); size.next();
@@ -239,6 +256,57 @@ QStringList Database::getFitsKeywords()
return keywords; return keywords;
} }
QVector<SkyObject> Database::getObjects(double minRa, double maxRa, double minDec, double maxDec)
{
QVector<SkyObject> objects;
if(!ngc.isOpen())return objects;
m_getNgc.bindValue(0, minRa);
m_getNgc.bindValue(1, maxRa);
m_getNgc.bindValue(2, minDec);
m_getNgc.bindValue(3, maxDec);
if(m_getNgc.exec())
{
while(m_getNgc.next())
{
QString name;
QString name2;
QString m = m_getNgc.value("M").toString();
QString ic = m_getNgc.value("IC").toString();
if(!m.isEmpty())
{
name = "M" + m;
m.clear();
}
else if(!ic.isEmpty())
{
name = "IC" + ic;
ic.clear();
}
else
{
name = m_getNgc.value("Name").toString();
}
if(!ic.isEmpty())name2 += "IC" + ic + " ";
name2 += m_getNgc.value("Common names").toString();
objects.append({
name,
name2,
{m_getNgc.value("RA_deg").toDouble(), m_getNgc.value("DEC_deg").toDouble()},
m_getNgc.value("MajAx").toDouble(),
m_getNgc.value("MinAx").toDouble(),
m_getNgc.value("PosAng").toDouble(),
m_getNgc.value("mag").isNull() ? NAN : m_getNgc.value("mag").toDouble(),
{0, 0},
});
}
}
return objects;
}
bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs) bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs)
{ {
if(scannedDirs.contains(dir.canonicalPath()))return true; if(scannedDirs.contains(dir.canonicalPath()))return true;
@@ -279,10 +347,10 @@ bool Database::indexFile(const QFileInfo &file)
} }
} }
bool ok; bool ok = false;
if(filePath.endsWith(".xisf", Qt::CaseInsensitive)) if(isXISF(file.suffix()))
ok = readXISFHeader(filePath, info); ok = readXISFHeader(filePath, info);
else else if(isFITS(file.suffix()))
ok = readFITSHeader(filePath, info); ok = readFITSHeader(filePath, info);
qlonglong last_id = -1; qlonglong last_id = -1;
@@ -323,6 +391,7 @@ bool Database::indexFile(const QFileInfo &file)
QVariantList file_id, keys, values, comments; QVariantList file_id, keys, values, comments;
for(const auto &record : info.fitsHeader) for(const auto &record : info.fitsHeader)
{ {
if(record.xisf && record.key.startsWith("PCL:"))continue;
file_id << last_id; file_id << last_id;
keys << QString(record.key); keys << QString(record.key);
values << record.value.toString(); values << record.value.toString();
+5
View File
@@ -6,11 +6,13 @@
#include <QSqlQuery> #include <QSqlQuery>
#include <QDir> #include <QDir>
#include <QProgressDialog> #include <QProgressDialog>
#include "imageinfodata.h"
class Database : public QObject class Database : public QObject
{ {
Q_OBJECT Q_OBJECT
QSqlDatabase database; QSqlDatabase database;
QSqlDatabase ngc;
QSqlQuery m_markQuery; QSqlQuery m_markQuery;
QSqlQuery m_unmarkQuery; QSqlQuery m_unmarkQuery;
QSqlQuery m_isMarkedQuery; QSqlQuery m_isMarkedQuery;
@@ -22,6 +24,8 @@ class Database : public QObject
QSqlQuery m_headerKeywords; QSqlQuery m_headerKeywords;
QSqlQuery m_deleteFile; QSqlQuery m_deleteFile;
QSqlQuery m_getNgc;
int m_progress; int m_progress;
public: public:
explicit Database(QObject *parent = 0); explicit Database(QObject *parent = 0);
@@ -37,6 +41,7 @@ public:
void indexDir(const QDir &dir, QProgressDialog *progress); void indexDir(const QDir &dir, QProgressDialog *progress);
void reindex(QProgressDialog *progress); void reindex(QProgressDialog *progress);
QStringList getFitsKeywords(); QStringList getFitsKeywords();
QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec);
protected: protected:
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs); bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
bool indexFile(const QFileInfo &file); bool indexFile(const QFileInfo &file);
View File
+437
View File
@@ -0,0 +1,437 @@
#include "filemanager.h"
#include "ui_filemanager.h"
#include "ui_fitskeyword.h"
#include <QSettings>
#include <QStandardPaths>
#include <QDesktopServices>
#include "loadimage.h"
PathTabBar::PathTabBar(const QStringList &tabs) :
_tabs(tabs)
{
setTabsClosable(true);
setExpanding(false);
for(auto &t : _tabs)
{
QDir dir(t);
int i = addTab(tabName(t));
setTabToolTip(i, t);
}
connect(this, &QTabBar::currentChanged, [this](int index){
QString path = _tabs.at(index);
emit pathChanged(path);
});
connect(this, &QTabBar::tabCloseRequested, [this](int index){
if(_tabs.size() >= 2)
{
_tabs.remove(index);
removeTab(index);
}
});
connect(this, &QTabBar::currentChanged, [this](int index){
emit tabChanged(_tabs[index]);
});
}
QHBoxLayout *PathTabBar::createLayout()
{
QHBoxLayout *hlayout = new QHBoxLayout();
hlayout->addWidget(this);
hlayout->addStretch(2);
QPushButton *addButton = new QPushButton("+");
connect(addButton, &QPushButton::clicked, [this](){
QString path = _tabs[currentIndex()];
_tabs.append(path);
int i = addTab(tabName(path));
setTabToolTip(i, path);
});
hlayout->addWidget(addButton);
return hlayout;
}
const QStringList &PathTabBar::tabPaths() const
{
return _tabs;
}
QString PathTabBar::currentTabPath() const
{
int index = std::clamp(currentIndex(), 0, (int)_tabs.size());
return _tabs[index];
}
void PathTabBar::pathChanged(const QString &path)
{
QDir dir(path);
int index = currentIndex();
setTabText(index, tabName(path));
setTabToolTip(index, path);
_tabs[index] = path;
}
QString PathTabBar::tabName(const QString &path)
{
QDir dir(path);
if(dir.dirName().isEmpty())
return path;
else
return dir.dirName();
}
FITSSelection::FITSSelection(const QStringList &keywords, QWidget *parent) : QDialog(parent)
,ui(new Ui::FITSKeyword)
{
ui->setupUi(this);
connect(ui->addButton, &QPushButton::clicked, [this](){
auto item = ui->keywordList->findItems(ui->keyword->text(), Qt::MatchFixedString | Qt::MatchCaseSensitive);
if(item.size())return;
ui->keywordList->addItem(ui->keyword->text());
});
connect(ui->removeButton, &QPushButton::clicked, [this](){
auto items = ui->keywordList->selectedItems();
for(auto item : items)
delete item;
});
ui->keywordList->addItems(keywords);
}
FITSSelection::~FITSSelection()
{
delete ui;
}
QStringList FITSSelection::FITSKeywords() const
{
QStringList keywords;
for(int i = 0; i < ui->keywordList->count(); i++)
keywords.append(ui->keywordList->item(i)->text());
return keywords;
}
FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMainWindow(parent)
,ui(new Ui::FileManager)
{
ui->setupUi(this);
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
QString picturesPath;
if(standardLocations.size())
picturesPath = standardLocations.first();
QSettings settings;
QStringList leftTabs = settings.value("filemanager/leftTabPaths", picturesPath).toStringList();
QStringList rightTabs = settings.value("filemanager/rightTabPaths", picturesPath).toStringList();
if(leftTabs.empty())leftTabs.append(picturesPath);
if(rightTabs.empty())rightTabs.append(picturesPath);
ui->leftTab->setOpenFilter(openFilter);
ui->rightTab->setOpenFilter(openFilter);
_rightTabBar = new PathTabBar(rightTabs);
ui->rightLayout->insertLayout(0, _rightTabBar->createLayout());
connect(_rightTabBar, &PathTabBar::tabChanged, ui->rightTab, &DirView::setDir);
_leftTabBar = new PathTabBar(leftTabs);
ui->leftLayout->insertLayout(0, _leftTabBar->createLayout());
connect(_leftTabBar, &PathTabBar::tabChanged, ui->leftTab, &DirView::setDir);
connect(ui->leftTab, &DirView::dirChanged, ui->leftPath, &QLineEdit::setText);
connect(ui->leftTab, &DirView::dirChanged, _leftTabBar, &PathTabBar::pathChanged);
connect(ui->rightTab, &DirView::dirChanged, ui->rightPath, &QLineEdit::setText);
connect(ui->rightTab, &DirView::dirChanged, _rightTabBar, &PathTabBar::pathChanged);
connect(ui->leftTab, &DirView::openFile, this, &FileManager::openFile);
connect(ui->rightTab, &DirView::openFile, this, &FileManager::openFile);
connect(ui->actionLoad_FITS_keywordsLeft, &QAction::toggled, ui->leftTab, &DirView::loadFitsKeywords);
connect(ui->actionLoad_FITS_keywordsRight, &QAction::toggled, ui->rightTab, &DirView::loadFitsKeywords);
ui->leftTab->setDir(_leftTabBar->currentTabPath());
ui->leftTab->setFITSKeywords(settings.value("filemanager/leftFitsKeywords", QStringList("OBJECT")).toStringList());
ui->leftTab->header()->restoreState(settings.value("filemanager/leftTabHeader").toByteArray());
ui->rightTab->setDir(_rightTabBar->currentTabPath());
ui->rightTab->setFITSKeywords(settings.value("filemanager/rightFitsKeywords", QStringList("OBJECT")).toStringList());
ui->rightTab->header()->restoreState(settings.value("filemanager/rightTabHeader").toByteArray());
ui->actionLoad_FITS_keywordsLeft->setChecked(settings.value("filemanager/leftLoadFitsKeywords", true).toBool());
ui->actionLoad_FITS_keywordsRight->setChecked(settings.value("filemanager/rightLoadFitsKeywords", true).toBool());
restoreGeometry(settings.value("filemanager/geometry").toByteArray());
setAttribute(Qt::WA_DeleteOnClose);
connect(ui->actionSelect_columnsLeft, &QAction::triggered, this, &FileManager::selectFITSKeywords);
connect(ui->actionSelect_columnsRight, &QAction::triggered, this, &FileManager::selectFITSKeywords);
connect(ui->leftPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
connect(ui->rightPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
QFileInfoList drives = QDir::drives();
for(auto &drive : drives)
{
QString path = drive.absoluteFilePath();
ui->menuLeft_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->leftTab->setDir(path); });
ui->menuRight_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->rightTab->setDir(path); });
}
}
FileManager::~FileManager()
{
QSettings settings;
settings.setValue("filemanager/leftFitsKeywords", ui->leftTab->FITSKeywords());
settings.setValue("filemanager/leftTabPaths", _leftTabBar->tabPaths());
settings.setValue("filemanager/leftTabHeader", ui->leftTab->header()->saveState());
settings.setValue("filemanager/rightFitsKeywords", ui->rightTab->FITSKeywords());
settings.setValue("filemanager/rightTabPaths", _rightTabBar->tabPaths());
settings.setValue("filemanager/rightTabHeader", ui->rightTab->header()->saveState());
settings.setValue("filemanager/leftLoadFitsKeywords", ui->actionLoad_FITS_keywordsLeft->isChecked());
settings.setValue("filemanager/rightLoadFitsKeywords", ui->actionLoad_FITS_keywordsRight->isChecked());
settings.setValue("filemanager/geometry", saveGeometry());
delete ui;
}
void FileManager::selectFITSKeywords()
{
QStringList columns;
if(sender() == ui->actionSelect_columnsLeft)
columns = ui->leftTab->FITSKeywords();
if(sender() == ui->actionSelect_columnsRight)
columns = ui->rightTab->FITSKeywords();
FITSSelection selection(columns, this);
int ret = selection.exec();
if(ret == QDialog::Accepted)
{
if(sender() == ui->actionSelect_columnsLeft)
ui->leftTab->setFITSKeywords(selection.FITSKeywords());
if(sender() == ui->actionSelect_columnsRight)
ui->rightTab->setFITSKeywords(selection.FITSKeywords());
}
}
void FileManager::pathEdited()
{
if(sender() == ui->leftPath)
{
QDir dir(ui->leftPath->text());
if(dir.exists())
ui->leftTab->setDir(dir.absolutePath());
}
if(sender() == ui->rightPath)
{
QDir dir(ui->rightPath->text());
if(dir.exists())
ui->rightTab->setDir(dir.absolutePath());
}
}
QCache<QString, ImageInfoData>* DirFileSystemModel::getCacheInstance()
{
static bool init = true;
static QCache<QString, ImageInfoData> cache;
if(!init)
{
cache.setMaxCost(10000);
init = false;
}
return &cache;
}
DirFileSystemModel::DirFileSystemModel(QObject *parent) : QFileSystemModel(parent)
{
_cache = getCacheInstance();
setFilter(QDir::AllEntries | QDir::NoDot);
_fitsKeywords = {"OBJECT"};
}
void DirFileSystemModel::setDir(const QString &path)
{
_dir = index(path);
}
QString DirFileSystemModel::dir() const
{
return fileInfo(_dir).canonicalFilePath();
}
void DirFileSystemModel::setFITSKeywords(const QStringList &keywords)
{
beginResetModel();
_fitsKeywords = keywords;
endResetModel();
}
const QStringList &DirFileSystemModel::FITSKeywords() const
{
return _fitsKeywords;
}
Qt::ItemFlags DirFileSystemModel::flags(const QModelIndex &index) const
{
return QFileSystemModel::flags(index) & ~Qt::ItemIsEditable;
}
int DirFileSystemModel::columnCount(const QModelIndex &parent) const
{
return QFileSystemModel::columnCount(parent) + _fitsKeywords.size();
}
QVariant DirFileSystemModel::data(const QModelIndex &index, int role) const
{
if(index.column() >= QFileSystemModel::columnCount() && role == Qt::DisplayRole)
{
QFileInfo info = fileInfo(index);
QString path = info.canonicalFilePath();
QString suffix = info.suffix();
ImageInfoData *infoData = nullptr;
if(_cache->contains(path))
{
infoData = _cache->object(path);
}
else
{
if(_loadFitsKeywords)
{
infoData = new ImageInfoData;
if(isFITS(suffix))
readFITSHeader(path, *infoData);
else if(isXISF(suffix))
readXISFHeader(path, *infoData);
_cache->insert(path, infoData);
}
}
if(infoData)
{
int column = index.column() - QFileSystemModel::columnCount();
if(column < _fitsKeywords.size())
{
const QString &key = _fitsKeywords.at(column);
for(auto &record : infoData->fitsHeader)
if(record.key == key)
return record.value;
}
}
return "";
}
return QFileSystemModel::data(index, role);
}
QVariant DirFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= QFileSystemModel::columnCount())
return _fitsKeywords.at(section - QFileSystemModel::columnCount());
return QFileSystemModel::headerData(section, orientation, role);
}
bool DirFileSystemModel::hasChildren(const QModelIndex &parent) const
{
if(parent.parent() == _dir)return false;
return QFileSystemModel::hasChildren(parent);
}
void DirFileSystemModel::loadFitsKeywords(bool enable)
{
_loadFitsKeywords = enable;
}
DirView::DirView(QWidget *parent) : QTreeView(parent)
{
_dirFileSystemModel = new DirFileSystemModel(this);
_dirFileSystemModel->setRootPath(QDir::drives().first().path());
_dirFileSystemModel->setReadOnly(false);
setDragEnabled(true);
setAcceptDrops(true);
setModel(_dirFileSystemModel);
setSelectionMode(QAbstractItemView::ExtendedSelection);
connect(this, &QTreeView::doubleClicked, [this](const QModelIndex &index){
QFileInfo info = _dirFileSystemModel->fileInfo(index);
if(_dirFileSystemModel->isDir(index))
{
setDir(info.canonicalFilePath());
}
else if(info.isFile())
{
if(_openFilter.contains(info.suffix().toLower()))
emit openFile(info.absoluteFilePath());
else
QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath()));
}
});
header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header(), &QHeaderView::customContextMenuRequested, this, &DirView::headerContextMenu);
}
void DirView::setDir(const QString &path)
{
QString oldPath = _dirFileSystemModel->dir();
#ifdef Q_OS_WINDOWS
const int ROOT_LEN = 3;
#else
const int ROOT_LEN = 1;
#endif
if(oldPath.left(ROOT_LEN) != path.left(ROOT_LEN))
_dirFileSystemModel->setRootPath(path.left(ROOT_LEN));
_dirFileSystemModel->setDir(path);
setRootIndex(_dirFileSystemModel->index(path, 0));
clearSelection();
if(oldPath != path)emit dirChanged(path);
}
QString DirView::dir() const
{
return _dirFileSystemModel->dir();
}
void DirView::setOpenFilter(const QSet<QString> &openFilter)
{
_openFilter = openFilter;
}
void DirView::setFITSKeywords(const QStringList &keywords)
{
QString d = dir();
_dirFileSystemModel->setFITSKeywords(keywords);
setDir(d);
}
const QStringList &DirView::FITSKeywords() const
{
return _dirFileSystemModel->FITSKeywords();
}
void DirView::headerContextMenu(const QPoint &pos)
{
QHeaderView *head = header();
QMenu menu;
int count = head->count();
for(int i = 0; i < count; i++)
{
QAction *a = menu.addAction(head->model()->headerData(i, Qt::Horizontal).toString());
a->setCheckable(true);
a->setChecked(!head->isSectionHidden(i));
a->setData(i);
}
QAction *a = menu.exec(mapToGlobal(pos));
if(a)
{
if(a->isChecked())head->showSection(a->data().toInt());
else head->hideSection(a->data().toInt());
}
}
void DirView::loadFitsKeywords(bool enable)
{
_dirFileSystemModel->loadFitsKeywords(enable);
}
+106
View File
@@ -0,0 +1,106 @@
#ifndef FILEMANAGER_H
#define FILEMANAGER_H
#include <QMainWindow>
#include <QCache>
#include <QFileSystemModel>
#include <QTreeView>
#include <QDialog>
#include <QTabBar>
#include <QHBoxLayout>
#include "imageinfodata.h"
namespace Ui {
class FileManager;
class FITSKeyword;
}
class PathTabBar : public QTabBar
{
Q_OBJECT
public:
explicit PathTabBar(const QStringList &tabs);
QHBoxLayout* createLayout();
const QStringList& tabPaths() const;
QString currentTabPath() const;
public slots:
void pathChanged(const QString &path);
signals:
void tabChanged(const QString &path);
private:
QStringList _tabs;
QString tabName(const QString &path);
};
class FITSSelection : public QDialog
{
Q_OBJECT
public:
FITSSelection(const QStringList &keywords, QWidget *parent = nullptr);
~FITSSelection();
QStringList FITSKeywords() const;
private:
Ui::FITSKeyword *ui;
};
class FileManager : public QMainWindow
{
Q_OBJECT
public:
explicit FileManager(const QSet<QString> &openFilter, QWidget *parent = nullptr);
~FileManager();
public slots:
void selectFITSKeywords();
void pathEdited();
signals:
void openFile(const QString &path);
private:
Ui::FileManager *ui;
PathTabBar *_leftTabBar;
PathTabBar *_rightTabBar;
};
class DirFileSystemModel : public QFileSystemModel
{
Q_OBJECT
mutable QCache<QString, ImageInfoData> *_cache = nullptr;
static QCache<QString, ImageInfoData>* getCacheInstance();
QModelIndex _dir;
QStringList _fitsKeywords;
bool _loadFitsKeywords = true;
public:
explicit DirFileSystemModel(QObject *parent = nullptr);
void setDir(const QString &path);
QString dir() const;
void setFITSKeywords(const QStringList &keywords);
const QStringList& FITSKeywords() const;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
bool hasChildren(const QModelIndex &parent) const override;
public slots:
void loadFitsKeywords(bool enable);
};
class DirView : public QTreeView
{
Q_OBJECT
DirFileSystemModel *_dirFileSystemModel = nullptr;
QSet<QString> _openFilter;
public:
explicit DirView(QWidget *parent = nullptr);
void setDir(const QString &path);
QString dir() const;
void setOpenFilter(const QSet<QString> &openFilter);
void setFITSKeywords(const QStringList &keywords);
const QStringList& FITSKeywords() const;
public slots:
void headerContextMenu(const QPoint &pos);
void loadFitsKeywords(bool enable);
signals:
void dirChanged(const QString &path);
void openFile(const QString &path);
};
#endif // FILEMANAGER_H
+110
View File
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FileManager</class>
<widget class="QMainWindow" name="FileManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1282</width>
<height>858</height>
</rect>
</property>
<property name="windowTitle">
<string>File Manager</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="leftLayout">
<item>
<widget class="QLineEdit" name="leftPath"/>
</item>
<item>
<widget class="DirView" name="leftTab"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="rightLayout">
<item>
<widget class="QLineEdit" name="rightPath"/>
</item>
<item>
<widget class="DirView" name="rightTab"/>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1282</width>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuLeft_Tab">
<property name="title">
<string>Left Tab</string>
</property>
<addaction name="actionLoad_FITS_keywordsLeft"/>
<addaction name="actionSelect_columnsLeft"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuRight_Tab">
<property name="title">
<string>Right Tab</string>
</property>
<addaction name="actionLoad_FITS_keywordsRight"/>
<addaction name="actionSelect_columnsRight"/>
<addaction name="separator"/>
</widget>
<addaction name="menuLeft_Tab"/>
<addaction name="menuRight_Tab"/>
</widget>
<action name="actionLoad_FITS_keywordsLeft">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Load FITS keywords</string>
</property>
</action>
<action name="actionLoad_FITS_keywordsRight">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Load FITS keywords</string>
</property>
</action>
<action name="actionSelect_columnsLeft">
<property name="text">
<string>Select columns</string>
</property>
</action>
<action name="actionSelect_columnsRight">
<property name="text">
<string>Select columns</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>DirView</class>
<extends>QTreeView</extends>
<header>filemanager.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -5,6 +5,7 @@
#include <QMenu> #include <QMenu>
#include <QSettings> #include <QSettings>
#include <QHeaderView> #include <QHeaderView>
#include <QMimeDatabase>
FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent) FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) : QWidget(parent)
, m_model(model) , m_model(model)
@@ -117,6 +118,7 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
{ {
setRootIndex(index); setRootIndex(index);
m_rootDir = m_fileSystemModel->filePath(index); m_rootDir = m_fileSystemModel->filePath(index);
m_fileSystemModel->setRootPath(m_rootDir);
} }
else if(a == resetRoot) else if(a == resetRoot)
{ {
@@ -127,6 +129,7 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
{ {
setRootIndex(rootIndex().parent()); setRootIndex(rootIndex().parent());
m_rootDir = m_fileSystemModel->filePath(rootIndex().parent()); m_rootDir = m_fileSystemModel->filePath(rootIndex().parent());
m_fileSystemModel->setRootPath(m_rootDir);
} }
else if(a == copy) else if(a == copy)
{ {
@@ -3,6 +3,7 @@
#include <QWidget> #include <QWidget>
#include <QFileSystemModel> #include <QFileSystemModel>
#include <QIdentityProxyModel>
#include <QListView> #include <QListView>
#include <QTreeView> #include <QTreeView>
+98
View File
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FITSKeyword</class>
<widget class="QDialog" name="FITSKeyword">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
<string>FITS Columns</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListWidget" name="keywordList">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="keyword"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="addButton">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FITSKeyword</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>215</x>
<y>420</y>
</hint>
<hint type="destinationlabel">
<x>215</x>
<y>221</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FITSKeyword</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>215</x>
<y>420</y>
</hint>
<hint type="destinationlabel">
<x>215</x>
<y>221</y>
</hint>
</hints>
</connection>
</connections>
</ui>
View File
View File
View File
View File
+171 -3
View File
@@ -4,6 +4,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <wcslib/wcshdr.h> #include <wcslib/wcshdr.h>
#include <wcslib/wcsfix.h> #include <wcslib/wcsfix.h>
#include "database.h"
#include "libxisf.h" #include "libxisf.h"
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"}; static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
@@ -85,6 +86,7 @@ WCSDataT::WCSDataT(int width, int height, const QVector<FITSRecord> &header) :
for(const FITSRecord &record : header) for(const FITSRecord &record : header)
{ {
if(record.key.startsWith("PV"))continue; if(record.key.startsWith("PV"))continue;
if(record.xisf)continue;
QByteArray rec; QByteArray rec;
rec.append(record.key.leftJustified(8, ' ')); rec.append(record.key.leftJustified(8, ' '));
@@ -153,9 +155,9 @@ bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const
return false; return false;
} }
void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const bool WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const
{ {
if(wcs == nullptr)return; if(wcs == nullptr)return false;
minRa = 1000; minRa = 1000;
maxRa = -1000; maxRa = -1000;
@@ -208,6 +210,7 @@ void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, dou
if(s.contains(scp)) if(s.contains(scp))
minDec = -90; minDec = -90;
} }
return true;
} }
double hav(double x) double hav(double x)
@@ -266,6 +269,16 @@ QString SkyPoint::toString() const
return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0'); return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0');
} }
QString SkyPoint::RAString() const
{
return toHMS(ra / 15);
}
QString SkyPoint::DECString() const
{
return toDMS(dec);
}
double SkyPoint::fromHMS(const QString &hms) double SkyPoint::fromHMS(const QString &hms)
{ {
double deg = fromDMS(hms); double deg = fromDMS(hms);
@@ -308,11 +321,21 @@ QString SkyPoint::toHMS(double decHour)
QString SkyPoint::toDMS(double deg) QString SkyPoint::toDMS(double deg)
{ {
int sign = deg < 0.0 ? -1 : 1;
deg *= sign;
double d,m,s,md; double d,m,s,md;
md = std::modf(deg, &d) * 60.0; md = std::modf(deg, &d) * 60.0;
s = std::modf(md, &m) * 60.0; s = std::modf(md, &m) * 60.0;
return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); return QString("%1˚ %2' %3\"").arg((int)d * sign, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0'));
}
SkyPoint SkyPoint::operator+(const SkyPoint &p)
{
SkyPoint ret;
ret.ra = ra + p.ra;
ret.dec = dec + p.dec;
return ret;
} }
SkyPointScale ImageInfoData::getCenterRaDec() const SkyPointScale ImageInfoData::getCenterRaDec() const
@@ -403,3 +426,148 @@ SkyPointScale ImageInfoData::getCenterRaDec() const
} }
return ret; return ret;
} }
SkyPoint greatCircle(SkyPoint &p, double dist, double azm)
{
dist = dist * M_PI / 180;
azm = azm * M_PI / 180;
double dec0 = p.DEC() * M_PI / 180;
double ra0 = p.RA() * M_PI / 180;
double dec1 = std::asin(std::sin(dec0) * std::cos(dist) + std::cos(dec0) * std::sin(dist) * std::cos(azm));
double ra1 = ra0 + std::atan2(std::sin(azm) * std::sin(dist) * std::cos(dec0), std::cos(dist) - std::sin(dec0) * std::sin(dec1));
return SkyPoint(ra1 * 180 / M_PI, dec1 * 180 / M_PI);
}
SkyGrid WCSDataT::prepareGrid(uint32_t w, uint32_t h, Database *database)
{
SkyGrid skyGrid;
if(!wcs)return skyGrid;
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
QPointF a,b;
worldToPixel(SkyPoint(crVal1, crVal2), a);
worldToPixel(SkyPoint(crVal1 + 0.01, crVal2), b);
skyGrid.rot_ang = std::atan2(b.y() - a.y(), b.x() - a.x()) / M_PI * -180.0;
if(database)
{
skyGrid.objects = database->getObjects(minRa, maxRa, minDec, maxDec);
for(auto &object : skyGrid.objects)
{
QPointF p;
if(worldToPixel(object.skyPoint, p))
object.pixel = p;
QPointF majax;
worldToPixel(greatCircle(object.skyPoint, (object.min_ax + object.maj_ax) / 120.0, object.pos_ang), majax);
majax -= p;
object.maj_ax = std::sqrt(QPointF::dotProduct(majax, majax));
}
}
double raStep = 15;
double decStep = 15;
double raRange = maxRa - minRa;
double decRange = maxDec - minDec;
const QVector<double> raSteps = {15, 5, 2.5, 1.25, 0.25, 20/240.0, 10/240.0, 5/240.0, 1/240.0};
const QVector<double> decSteps = {20, 10, 5, 2, 1, 20/60.0, 10/60.0, 5/60.0, 2/60.0, 1/60.0, 20/3600.0, 10/3600.0, 5/3600.0, 2/3600.0, 1/3600.0};
for(double ra : raSteps)
{
if(ra * 5 <= raRange)
{
raStep = ra;
break;
}
}
for(double dec : decSteps)
{
if(dec * 5 <= decRange)
{
decStep = dec;
break;
}
}
minRa -= std::fmod(minRa, raStep);
minDec -= std::fmod(minDec, decStep);
if(minRa < 0)minRa -= raStep;
if(minDec < 0)minDec -= decStep;
QRectF clip(0, 0, w, h);
const double step = 0.2;
maxRa += raStep;
maxDec += decStep;
for(double ra = minRa; ra <= maxRa; ra += raStep)
{
QPointF p;
worldToPixel(SkyPoint(ra, minDec), p);
skyGrid.grid.moveTo(p);
for(double dec = minDec + decStep * step; dec <= maxDec; dec += decStep * step)
{
worldToPixel(SkyPoint(ra, dec), p);
skyGrid.grid.lineTo(p);
}
}
for(double dec = minDec; dec <= maxDec; dec += decStep)
{
QPointF p;
worldToPixel(SkyPoint(minRa, dec), p);
skyGrid.grid.moveTo(p);
for(double ra = minRa + raStep * step; ra <= maxRa; ra += raStep * step)
{
worldToPixel(SkyPoint(ra, dec), p);
skyGrid.grid.lineTo(p);
}
}
SkyPoint sp1, sp2,orig;
pixelToWorld(QPointF(-1, -1), orig);
sp1 = orig;
for(uint32_t x = 0; x < w; x++)
{
QPointF p(x, 0);
if(!pixelToWorld(p, sp2))
break;
if(static_cast<int>(sp1.RA() / raStep) != static_cast<int>(sp2.RA() / raStep))
skyGrid.text.append({p, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()});
if(static_cast<int>(sp1.DEC() / decStep) != static_cast<int>(sp2.DEC() / decStep))
skyGrid.text.append({p, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()});
sp1 = sp2;
}
sp1 = orig;
for(uint32_t y = 0; y < h; y++)
{
QPointF p(0, y);
if(!pixelToWorld(p, sp2))
break;
if(static_cast<int>(sp1.RA() / raStep) != static_cast<int>(sp2.RA() / raStep))
skyGrid.text.append({p, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()});
if(static_cast<int>(sp1.DEC() / decStep) != static_cast<int>(sp2.DEC() / decStep))
skyGrid.text.append({p, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()});
sp1 = sp2;
}
skyGrid.empty = false;
return skyGrid;
}
void SkyGrid::clear()
{
empty = true;
grid.clear();
text.clear();
objects.clear();
}
+30 -1
View File
@@ -5,12 +5,15 @@
#include <QPointF> #include <QPointF>
#include <QVector> #include <QVector>
#include <QVariant> #include <QVariant>
#include <QPainterPath>
#include <wcslib/wcs.h> #include <wcslib/wcs.h>
#include <cmath> #include <cmath>
#include <memory> #include <memory>
namespace LibXISF { struct FITSKeyword; struct Property; } namespace LibXISF { struct FITSKeyword; struct Property; }
class Database;
struct FITSRecord struct FITSRecord
{ {
QByteArray key; QByteArray key;
@@ -36,10 +39,13 @@ public:
double RAHour() const { return ra / 15.0; } double RAHour() const { return ra / 15.0; }
double DEC() const { return dec; } double DEC() const { return dec; }
QString toString() const; QString toString() const;
QString RAString() const;
QString DECString() const;
static double fromHMS(const QString &hms); static double fromHMS(const QString &hms);
static double fromDMS(const QString &dms); static double fromDMS(const QString &dms);
static QString toHMS(double decHour); static QString toHMS(double decHour);
static QString toDMS(double deg); static QString toDMS(double deg);
SkyPoint operator+(const SkyPoint &p);
}; };
struct SkyPointScale struct SkyPointScale
@@ -51,6 +57,28 @@ struct SkyPointScale
double scaleHigh = 10000.0; double scaleHigh = 10000.0;
}; };
struct SkyObject
{
QString name;
QString name2;
SkyPoint skyPoint;
double maj_ax;
double min_ax;
double pos_ang;
double mag;
QPointF pixel;
};
struct SkyGrid
{
bool empty = true;
QPainterPath grid;
QVector<QPair<QPointF, QString>> text;
QVector<SkyObject> objects;
double rot_ang = 0;
void clear();
};
class WCSDataT class WCSDataT
{ {
int nwcs = 0; int nwcs = 0;
@@ -65,9 +93,10 @@ public:
~WCSDataT(); ~WCSDataT();
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const; bool pixelToWorld(const QPointF &pixel, SkyPoint &point) const;
bool worldToPixel(const SkyPoint &point, QPointF &pixel) const; bool worldToPixel(const SkyPoint &point, QPointF &pixel) const;
void calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const; bool calculateBounds(double &minRa, double &maxRa, double &minDec, double &maxDec, double &crVal1, double &crVal2) const;
bool valid() const { return wcs; }; bool valid() const { return wcs; };
SkyPointScale getRaDecScale() const; SkyPointScale getRaDecScale() const;
SkyGrid prepareGrid(uint32_t w, uint32_t h, Database *database);
}; };
struct ImageInfoData struct ImageInfoData
@@ -142,6 +142,11 @@ void ImageScrollArea::falseColor(bool enable)
m_imageWidget->falseColor(enable); m_imageWidget->falseColor(enable);
} }
void ImageScrollArea::drawGrid(bool enable)
{
m_imageWidget->drawGrid(enable);
}
QImage ImageScrollArea::renderToImage() QImage ImageScrollArea::renderToImage()
{ {
return m_imageWidget->renderToImage(); return m_imageWidget->renderToImage();
@@ -31,6 +31,7 @@ public slots:
void invert(bool enable); void invert(bool enable);
void superPixel(bool enable); void superPixel(bool enable);
void falseColor(bool enable); void falseColor(bool enable);
void drawGrid(bool enable);
QImage renderToImage(); QImage renderToImage();
protected slots: protected slots:
void scrollEvent(); void scrollEvent();
+51 -1
View File
@@ -180,6 +180,10 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
void ImageWidgetGL::setWCS(std::shared_ptr<WCSDataT> wcs) void ImageWidgetGL::setWCS(std::shared_ptr<WCSDataT> wcs)
{ {
m_wcs = wcs; m_wcs = wcs;
m_grid.clear();
if(m_drawGrid && m_wcs)
m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database);
} }
void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos) void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos)
@@ -496,6 +500,18 @@ void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scal
painter.drawImage(0, 0, img); painter.drawImage(0, 0, img);
} }
void ImageWidgetGL::drawGrid(bool enable)
{
if(m_grid.empty && m_wcs)
m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database);
if(enable != m_drawGrid)
{
m_drawGrid = enable;
update();
}
}
void ImageWidgetGL::paintGL() void ImageWidgetGL::paintGL()
{ {
float dx = m_dx; float dx = m_dx;
@@ -613,9 +629,43 @@ void ImageWidgetGL::paintGL()
m_program->setUniformValue("colormapIdx", m_colormapIdx); m_program->setUniformValue("colormapIdx", m_colormapIdx);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_vao->release(); m_vao->release();
if(m_drawGrid && !m_grid.empty)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
painter.setPen(QPen(Qt::yellow, 1.0 / m_scale));
QTransform tran;
tran.translate(-std::floor(dx), -std::floor(dy));
tran.scale(m_scale, m_scale);
painter.setTransform(tran);
painter.setClipRect(0, 0, m_imgWidth, m_imgHeight);
painter.drawPath(m_grid.grid);
painter.setPen(Qt::yellow);
QFont font({"Arial", "serif-sans"});
font.setPointSizeF(12 / m_scale);
painter.setFont(font);
for(auto &text : m_grid.text)
painter.drawText(QRectF(text.first, QSizeF(4000, 4000)), text.second);
painter.setPen(QPen(Qt::green, 1.0 / m_scale));
QFontMetricsF fontMetric = QFontMetricsF(font);
for(auto &object : m_grid.objects)
{
QRectF rect = fontMetric.boundingRect(object.name);
rect.moveCenter(object.pixel);
painter.setTransform(tran);
painter.drawText(rect, Qt::TextDontClip, object.name);
painter.translate(object.pixel);
painter.rotate(object.pos_ang);
painter.drawEllipse(QPointF(0, 0), object.maj_ax, object.maj_ax);
}
}
} }
} }
} }
void ImageWidgetGL::resizeGL(int w, int h) void ImageWidgetGL::resizeGL(int w, int h)
+5
View File
@@ -8,6 +8,7 @@
#include <QOpenGLTexture> #include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject> #include <QOpenGLVertexArrayObject>
#include <QOpenGLFunctions> #include <QOpenGLFunctions>
#include <QPainterPath>
#include "database.h" #include "database.h"
#include "rawimage.h" #include "rawimage.h"
#include "imageinfodata.h" #include "imageinfodata.h"
@@ -37,6 +38,7 @@ public:
virtual QImage renderToImage() = 0; virtual QImage renderToImage() = 0;
virtual void thumbnailLoaded(const Image *image) = 0; virtual void thumbnailLoaded(const Image *image) = 0;
virtual void showThumbnail(bool enable) = 0; virtual void showThumbnail(bool enable) = 0;
virtual void drawGrid(bool enable) = 0;
static QImage loadColormap(); static QImage loadColormap();
}; };
@@ -70,6 +72,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
GLuint m_debayerTex = 0; GLuint m_debayerTex = 0;
std::shared_ptr<RawImage> m_rawImage; std::shared_ptr<RawImage> m_rawImage;
std::shared_ptr<WCSDataT> m_wcs; std::shared_ptr<WCSDataT> m_wcs;
SkyGrid m_grid;
int m_width, m_height; int m_width, m_height;
int m_imgWidth = -1, m_imgHeight = -1; int m_imgWidth = -1, m_imgHeight = -1;
int m_currentImg = 0; int m_currentImg = 0;
@@ -87,6 +90,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
bool m_selecting = false; bool m_selecting = false;
bool m_sizesDirty = false; bool m_sizesDirty = false;
bool m_srgb = false; bool m_srgb = false;
bool m_drawGrid = false;
int m_thumbnailCount = 0; int m_thumbnailCount = 0;
int m_maxTextureSize = 0; int m_maxTextureSize = 0;
int m_maxArrayLayers = 0; int m_maxArrayLayers = 0;
@@ -116,6 +120,7 @@ public:
QImage renderToImage() override; QImage renderToImage() override;
void thumbnailLoaded(const Image *image) override; void thumbnailLoaded(const Image *image) override;
void showThumbnail(bool enable) override; void showThumbnail(bool enable) override;
void drawGrid(bool enable) override;
protected: protected:
void paintGL() override; void paintGL() override;
void resizeGL(int w, int h) override; void resizeGL(int w, int h) override;
+56 -4
View File
@@ -241,18 +241,59 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
{ {
info.fitsHeader.append(fits); info.fitsHeader.append(fits);
} }
QVector<FITSRecord> xisfWCS;
auto imageproperties = xisfImage.imageProperties(); auto imageproperties = xisfImage.imageProperties();
for(auto prop : imageproperties) for(auto prop : imageproperties)
{ {
info.fitsHeader.append(prop); info.fitsHeader.append(prop);
if(prop.id == "PCL:AstrometricSolution:ReferenceCelestialCoordinates" && prop.value.type() == LibXISF::Variant::Type::F64Vector)
{
auto val = prop.value.value<LibXISF::F64Vector>();
if(val.size() >= 2)
{
xisfWCS.append({"CRVAL1", val[0], "value from PCL:AstrometricSolution"});
xisfWCS.append({"CRVAL2", val[1], "value from PCL:AstrometricSolution"});
}
}
else if(prop.id == "PCL:AstrometricSolution:ReferenceImageCoordinates" && prop.value.type() == LibXISF::Variant::Type::F64Vector)
{
auto val = prop.value.value<LibXISF::F64Vector>();
if(val.size() >= 2)
{
xisfWCS.append({"CRPIX1", val[0], "value from PCL:AstrometricSolution"});
xisfWCS.append({"CRPIX2", val[1], "value from PCL:AstrometricSolution"});
}
}
else if(prop.id == "PCL:AstrometricSolution:LinearTransformationMatrix" && prop.value.type() == LibXISF::Variant::Type::F64Matrix)
{
auto val = prop.value.value<LibXISF::F64Matrix>();
if(val.cols() >= 2 && val.rows() >= 2)
{
xisfWCS.append({"CD1_1", val(0, 0), "value from PCL:AstrometricSolution"});
xisfWCS.append({"CD1_2", val(0, 1), "value from PCL:AstrometricSolution"});
xisfWCS.append({"CD2_1", val(1, 0), "value from PCL:AstrometricSolution"});
xisfWCS.append({"CD2_2", val(1, 1), "value from PCL:AstrometricSolution"});
}
}
else if(prop.id == "PCL:AstrometricSolution:ProjectionSystem")
{
if(prop.value.toString() == "Gnomonic")
{
xisfWCS.append({"CTYPE1", "RA---TAN", "value from PCL:AstrometricSolution"});
xisfWCS.append({"CTYPE", "DEC--TAN", "value from PCL:AstrometricSolution"});
}
}
} }
info.num = xisf.imagesCount(); info.num = xisf.imagesCount();
info.index = index; info.index = index;
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())}); info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())}); info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
if(!info.wcs->valid())info.wcs.reset();
auto wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
if(!wcs->valid() && xisfWCS.size())wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), xisfWCS);
if(wcs->valid())info.wcs = wcs;
RawImage::DataType type; RawImage::DataType type;
switch(xisfImage.sampleFormat()) switch(xisfImage.sampleFormat())
@@ -409,18 +450,19 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
{ {
bool ret = false; bool ret = false;
QElapsedTimer timer; QElapsedTimer timer;
QFileInfo fileInfo(path);
timer.start(); timer.start();
if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive)) if(path.endsWith(".CR2", Qt::CaseInsensitive) || path.endsWith(".CR3", Qt::CaseInsensitive) || path.endsWith(".NEF", Qt::CaseInsensitive) || path.endsWith(".DNG", Qt::CaseInsensitive))
{ {
ret = loadRAW(path, info, rawImage); ret = loadRAW(path, info, rawImage);
qDebug() << "LoadRAW" << timer.elapsed(); qDebug() << "LoadRAW" << timer.elapsed();
} }
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive)) else if(isFITS(fileInfo.suffix()))
{ {
ret = loadFITS(path, info, rawImage, planar, index); ret = loadFITS(path, info, rawImage, planar, index);
qDebug() << "LoadFITS" << timer.elapsed(); qDebug() << "LoadFITS" << timer.elapsed();
} }
else if(path.endsWith(".XISF", Qt::CaseInsensitive)) else if(isXISF(fileInfo.suffix()))
{ {
ret = loadXISF(path, info, rawImage, planar, index); ret = loadXISF(path, info, rawImage, planar, index);
qDebug() << "LoadXISF" << timer.elapsed(); qDebug() << "LoadXISF" << timer.elapsed();
@@ -444,3 +486,13 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
} }
return ret; return ret;
} }
bool isFITS(const QString &suffix)
{
return suffix.compare("fits", Qt::CaseInsensitive) == 0 || suffix.compare("fit", Qt::CaseInsensitive) == 0 || suffix.compare("fts", Qt::CaseInsensitive) == 0 || suffix.compare("fz", Qt::CaseInsensitive) == 0;
}
bool isXISF(const QString &suffix)
{
return suffix.compare("xisf", Qt::CaseInsensitive) == 0;
}
+2
View File
@@ -10,5 +10,7 @@ QString makeUNCPath(const QString &path);
bool readFITSHeader(const QString &path, ImageInfoData &info); bool readFITSHeader(const QString &path, ImageInfoData &info);
bool readXISFHeader(const QString &path, ImageInfoData &info); bool readXISFHeader(const QString &path, ImageInfoData &info);
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false); bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false);
bool isFITS(const QString &suffix);
bool isXISF(const QString &suffix);
#endif // LOADIMAGE_H #endif // LOADIMAGE_H
+4 -3
View File
@@ -165,11 +165,12 @@ void writeFITSImage(fitsfile *fw, std::shared_ptr<RawImage> rawimage, ImageInfoD
double vald = record.value.toDouble(&isdouble); double vald = record.value.toDouble(&isdouble);
int valb = record.value.toString() == "T"; int valb = record.value.toString() == "T";
long long vall = record.value.toLongLong(&isint); long long vall = record.value.toLongLong(&isint);
if(isint)isint = vall == vald;
QByteArray str = record.value.toString().toLatin1(); QByteArray str = record.value.toString().toLatin1();
if(isdouble) if(isint)
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
else if(isint)
fits_write_key(fw, TLONGLONG, record.key.data(), &vall, record.comment.isEmpty() ? nullptr : record.comment.data(), &status); fits_write_key(fw, TLONGLONG, record.key.data(), &vall, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
else if(isdouble)
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
else if(isbool) else if(isbool)
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status); fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
else if(record.key == "COMMENT") else if(record.key == "COMMENT")
View File
+1 -1
View File
@@ -4,7 +4,7 @@
#include <QTranslator> #include <QTranslator>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <stdlib.h> #include <stdlib.h>
#include "thumbnailer/genthumbnail.h" #include "../thumbnailer/genthumbnail.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
+29 -5
View File
@@ -29,6 +29,7 @@
#include "settingsdialog.h" #include "settingsdialog.h"
#include "histogram.h" #include "histogram.h"
#include "batchprocessing.h" #include "batchprocessing.h"
#include "filemanager.h"
#ifdef __linux__ #ifdef __linux__
#include <sys/ioctl.h> #include <sys/ioctl.h>
@@ -66,6 +67,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
_openFilter.append(tr(";;All files (*)")); _openFilter.append(tr(";;All files (*)"));
nameFilter.append({"fit", "fits", "fts", "fz", "xisf", "cr2", "cr3", "nef", "dng"}); nameFilter.append({"fit", "fits", "fts", "fz", "xisf", "cr2", "cr3", "nef", "dng"});
QImageReader::setAllocationLimit(0); QImageReader::setAllocationLimit(0);
_openSuffix = {nameFilter.constBegin(), nameFilter.constEnd()};
m_info = new ImageInfo(this); m_info = new ImageInfo(this);
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this); QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
@@ -92,6 +94,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_stretchPanel, &StretchToolbar::invert, m_image, &ImageScrollArea::invert); connect(m_stretchPanel, &StretchToolbar::invert, m_image, &ImageScrollArea::invert);
connect(m_stretchPanel, &StretchToolbar::superPixel, m_image, &ImageScrollArea::superPixel); connect(m_stretchPanel, &StretchToolbar::superPixel, m_image, &ImageScrollArea::superPixel);
connect(m_stretchPanel, &StretchToolbar::falseColor, m_image, &ImageScrollArea::falseColor); connect(m_stretchPanel, &StretchToolbar::falseColor, m_image, &ImageScrollArea::falseColor);
connect(m_stretchPanel, &StretchToolbar::drawGrid, m_image, &ImageScrollArea::drawGrid);
m_ringList = new ImageRingList(m_database, nameFilter, this); m_ringList = new ImageRingList(m_database, nameFilter, this);
m_filesystem = new FilesystemWidget(m_ringList, this); m_filesystem = new FilesystemWidget(m_ringList, this);
@@ -99,11 +102,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort); connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort); connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
m_filetree = nullptr;
#if !defined(FLATPAK) || !defined(__aarch64__)//bug with QTreeView and QFileSystemModel on ARM64 under flatpak
m_filetree = new Filetree(this); m_filetree = new Filetree(this);
connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile)); connect(m_filetree, &Filetree::fileSelected, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); }); connect(m_filetree, &Filetree::copyFiles, [this](const QString &path){ copyOrMove(true, path); });
connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); }); connect(m_filetree, &Filetree::moveFiles, [this](const QString &path){ copyOrMove(false, path); });
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir)); connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
#endif
m_databaseView = new DataBaseView(m_database, this); m_databaseView = new DataBaseView(m_database, this);
connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile)); connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
@@ -142,11 +148,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
databaseViewDock->hide(); databaseViewDock->hide();
addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock); addDockWidget(Qt::BottomDockWidgetArea, databaseViewDock);
QDockWidget *filetreeDock = new QDockWidget(tr("File tree"), this); QDockWidget *filetreeDock = nullptr;
#if !defined(FLATPAK) || !defined(__aarch64__)
filetreeDock = new QDockWidget(tr("File tree"), this);
filetreeDock->setWidget(m_filetree); filetreeDock->setWidget(m_filetree);
filetreeDock->setObjectName("filetreeDock"); filetreeDock->setObjectName("filetreeDock");
databaseViewDock->hide(); databaseViewDock->hide();
addDockWidget(Qt::LeftDockWidgetArea, filetreeDock); addDockWidget(Qt::LeftDockWidgetArea, filetreeDock);
#endif
Histogram *histogram = new Histogram(this); Histogram *histogram = new Histogram(this);
QDockWidget *histogramDock = new QDockWidget(tr("Histogram"), this); QDockWidget *histogramDock = new QDockWidget(tr("Histogram"), this);
@@ -175,6 +184,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir); fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs); QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs);
fileMenu->addSeparator(); fileMenu->addSeparator();
#if !defined(FLATPAK) || !defined(__aarch64__)
fileMenu->addAction(tr("File manager"), this, &MainWindow::openFileManager);
#endif
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked); fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked); fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked); fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
@@ -190,7 +202,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
fileMenu->addSeparator(); fileMenu->addSeparator();
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode); QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode);
liveModeAction->setCheckable(true); liveModeAction->setCheckable(true);
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, &MainWindow::close); QAction *exitAction = fileMenu->addAction(tr("Exit"), QCoreApplication::instance(), &QCoreApplication::quit, Qt::QueuedConnection);
exitAction->setShortcut(QKeySequence::Quit); exitAction->setShortcut(QKeySequence::Quit);
menuBar()->addMenu(fileMenu); menuBar()->addMenu(fileMenu);
@@ -263,7 +275,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), Qt::Key_F3, m_ringList, &ImageRingList::toggleSlideshow); QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), Qt::Key_F3, m_ringList, &ImageRingList::toggleSlideshow);
slideshowAction->setCheckable(true); slideshowAction->setCheckable(true);
viewMenu->addSeparator(); viewMenu->addSeparator();
viewMenu->addActions(m_stretchPanel->actions()); auto actionList = m_stretchPanel->actions();
actionList.removeFirst();
viewMenu->addActions(actionList);
menuBar()->addMenu(viewMenu); menuBar()->addMenu(viewMenu);
QMenu *selectMenu = new QMenu(tr("Select"), this); QMenu *selectMenu = new QMenu(tr("Select"), this);
@@ -309,7 +323,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
dockMenu->addAction(navigationToolbar->toggleViewAction()); dockMenu->addAction(navigationToolbar->toggleViewAction());
dockMenu->addAction(filesystemDock->toggleViewAction()); dockMenu->addAction(filesystemDock->toggleViewAction());
dockMenu->addAction(databaseViewDock->toggleViewAction()); dockMenu->addAction(databaseViewDock->toggleViewAction());
dockMenu->addAction(filetreeDock->toggleViewAction()); if(filetreeDock)dockMenu->addAction(filetreeDock->toggleViewAction());
dockMenu->addAction(histogramDock->toggleViewAction()); dockMenu->addAction(histogramDock->toggleViewAction());
#ifdef PLATESOLVER #ifdef PLATESOLVER
dockMenu->addAction(_plateSolving->toggleViewAction()); dockMenu->addAction(_plateSolving->toggleViewAction());
@@ -360,7 +374,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); if(filetreeDock)filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
if(_plateSolving)_plateSolving->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
m_stretchPanel->setFloatable(false); m_stretchPanel->setFloatable(false);
} }
} }
@@ -805,6 +820,15 @@ void MainWindow::checkNewVersion()
}); });
} }
void MainWindow::openFileManager()
{
#if !defined(FLATPAK) || !defined(__aarch64__)
FileManager *filemanager = new FileManager(_openSuffix);
connect(filemanager, &FileManager::openFile, this, static_cast<void (MainWindow::*)(const QString&)>(&MainWindow::loadFile));
filemanager->show();
#endif
}
void MainWindow::updateWindowTitle() void MainWindow::updateWindowTitle()
{ {
ImagePtr ptr = m_ringList->currentImage(); ImagePtr ptr = m_ringList->currentImage();
+3 -1
View File
@@ -23,13 +23,14 @@ class MainWindow : public QMainWindow
FilesystemWidget *m_filesystem; FilesystemWidget *m_filesystem;
Filetree *m_filetree; Filetree *m_filetree;
DataBaseView *m_databaseView; DataBaseView *m_databaseView;
PlateSolving *_plateSolving; PlateSolving *_plateSolving = nullptr;
static int socketPair[2]; static int socketPair[2];
QSocketNotifier *socketNotifier; QSocketNotifier *socketNotifier;
QString _lastDir; QString _lastDir;
bool _maximized; bool _maximized;
QString _openFilter; QString _openFilter;
QString _saveFilter; QString _saveFilter;
QSet<QString> _openSuffix;
public: public:
MainWindow(QWidget *parent = 0); MainWindow(QWidget *parent = 0);
~MainWindow() override; ~MainWindow() override;
@@ -67,6 +68,7 @@ public slots:
void showSettingsDialog(); void showSettingsDialog();
void exportCSV(); void exportCSV();
void checkNewVersion(); void checkNewVersion();
void openFileManager();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H
View File
View File
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef PLATESOLVING_H #ifndef PLATESOLVING_H
#define PLATESOLVING_H #define PLATESOLVING_H
#include "qelapsedtimer.h" #include <QElapsedTimer>
#include <QDockWidget> #include <QDockWidget>
class Solver; class Solver;
+2 -2
View File
@@ -403,9 +403,9 @@ uint32_t RawImage::norm() const
} }
} }
uint32_t RawImage::widthBytes() const uint64_t RawImage::widthBytes() const
{ {
return m_ch * m_width * typeSize(m_type); return (uint64_t)m_ch * m_width * typeSize(m_type);
} }
uint32_t RawImage::widthSamples() const uint32_t RawImage::widthSamples() const
+1 -1
View File
@@ -97,7 +97,7 @@ public:
uint64_t size() const; uint64_t size() const;
DataType type() const; DataType type() const;
uint32_t norm() const; uint32_t norm() const;
uint32_t widthBytes() const; uint64_t widthBytes() const;
uint32_t widthSamples() const; uint32_t widthSamples() const;
void* data(); void* data();
const void* data() const; const void* data() const;
+105 -11
View File
@@ -92,6 +92,55 @@ bool ScriptEngine::isMarked(const File *file)
return ret; return ret;
} }
QJSValue ScriptEngine::getObjects(double ra, double dec, double distance)
{
QVector<SkyObject> objects;
QMetaObject::invokeMethod(_database, [this, ra, dec, distance](){
return _database->getObjects(ra - distance, ra + distance, dec - distance, dec + distance); }, Qt::BlockingQueuedConnection, &objects);
QJSValue ret = newArray(objects.size());
qint32 i = 0;
for(auto &object : objects)
{
QJSValue jsObj = newObject();
jsObj.setProperty("name", object.name);
jsObj.setProperty("name2", object.name2);
jsObj.setProperty("ra", object.skyPoint.RA());
jsObj.setProperty("dec", object.skyPoint.DEC());
jsObj.setProperty("mag", object.mag);
ret.setProperty(i++, jsObj);
}
return ret;
}
QJSValue ScriptEngine::getObjects(const QJSValue &bounds)
{
QVector<SkyObject> objects;
double minRa = bounds.property("minRA").toNumber();
double maxRa = bounds.property("maxRA").toNumber();
double minDec = bounds.property("minDEC").toNumber();
double maxDec = bounds.property("maxDEC").toNumber();
QMetaObject::invokeMethod(_database, [this, minRa, maxRa, minDec, maxDec](){
return _database->getObjects(minRa, maxRa, minDec, maxDec); }, Qt::BlockingQueuedConnection, &objects);
QJSValue ret = newArray(objects.size());
qint32 i = 0;
for(auto &object : objects)
{
QJSValue jsObj = newObject();
jsObj.setProperty("name", object.name);
jsObj.setProperty("name2", object.name2);
jsObj.setProperty("ra", object.skyPoint.RA());
jsObj.setProperty("dec", object.skyPoint.DEC());
jsObj.setProperty("mag", object.mag);
ret.setProperty(i++, jsObj);
}
return ret;
}
void ScriptEngine::setMaxThread(int maxthread) void ScriptEngine::setMaxThread(int maxthread)
{ {
int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1); int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1);
@@ -406,16 +455,18 @@ void File::loadFitsKeywords()
{ {
_fitsKeywordsLoaded = true; _fitsKeywordsLoaded = true;
ImageInfoData info; ImageInfoData info;
if(suffix().toLower() == "xisf") if(isXISF(suffix()))
{ {
readXISFHeader(_path, info); readXISFHeader(_path, info);
} }
else if(suffix().toLower() == "fits" || suffix().toLower() == "fit") else if(isFITS(suffix()))
{ {
readFITSHeader(_path, info); readFITSHeader(_path, info);
} }
else return; else return;
_wcs = info.wcs;
for(auto &record : info.fitsHeader) for(auto &record : info.fitsHeader)
{ {
_fitsKeywords.append(record.key); _fitsKeywords.append(record.key);
@@ -557,7 +608,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
_fitsKeywordsLoaded = false; _fitsKeywordsLoaded = false;
_fitsKeywords.clear(); _fitsKeywords.clear();
if(QRegularExpression("(fits?|fz|fts)", QRegularExpression::CaseInsensitiveOption).match(suffix()).hasMatch()) if(isFITS(suffix()))
{ {
fitsfile *file; fitsfile *file;
int status = 0; int status = 0;
@@ -574,16 +625,22 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
int naxis; int naxis;
long naxes[3] = {0}; long naxes[3] = {0};
int type = -1; int type = -1;
std::vector<int> imageIdxs;
for(int i=1; i <= num; i++) for(int i=1; i <= num; i++)
{ {
fits_movabs_hdu(file, i, IMAGE_HDU, &status); fits_movabs_hdu(file, i, IMAGE_HDU, &status);
fits_get_hdu_type(file, &type, &status); fits_get_hdu_type(file, &type, &status);
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status); if(type == IMAGE_HDU)
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0) {
break; fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
if(i == num)return false; if(naxis >= 2 && naxis <= 3)imageIdxs.push_back(i);
}
} }
if(modify->_imageIdx >= imageIdxs.size())return false;
fits_movabs_hdu(file, imageIdxs[modify->_imageIdx], &type, &status);
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);
for(auto &remove : modify->_remove) for(auto &remove : modify->_remove)
{ {
int status = 0;//we ignore errors from here int status = 0;//we ignore errors from here
@@ -680,7 +737,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
return status == 0; return status == 0;
} }
else if(suffix().toLower() == "xisf") else if(isXISF(suffix()))
{ {
try try
{ {
@@ -691,13 +748,16 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
qDebug() << "modify" << in << out; qDebug() << "modify" << in << out;
for(auto &remove : modify->_remove) for(auto &remove : modify->_remove)
modifyXISF.removeFITSKeyword(0, remove.toStdString()); modifyXISF.removeFITSKeyword(modify->_imageIdx, remove.toStdString());
for(auto &record : modify->_update) for(auto &record : modify->_update)
modifyXISF.updateFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}, true); modifyXISF.updateFITSKeyword(modify->_imageIdx, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}, true);
for(auto &record : modify->_add) for(auto &record : modify->_add)
modifyXISF.addFITSKeyword(0, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()}); modifyXISF.addFITSKeyword(modify->_imageIdx, {record.key.toStdString(), record.value.toString().toStdString(), record.comment.toStdString()});
for(auto &property : modify->_property)
modifyXISF.updateProperty(modify->_imageIdx, property);
modifyXISF.save(out.toLocal8Bit().toStdString()); modifyXISF.save(out.toLocal8Bit().toStdString());
modifyXISF.close(); modifyXISF.close();
@@ -706,6 +766,7 @@ bool File::modifyFITSRecords(const FITSRecordModify *modify)
} }
catch(std::filesystem::filesystem_error &err) catch(std::filesystem::filesystem_error &err)
{ {
if(_engine)_engine->newMessage("Failed to modify file " + _path + " " + err.what(), true);
return false; return false;
} }
catch(LibXISF::Error &err) catch(LibXISF::Error &err)
@@ -788,6 +849,24 @@ QJSValue File::stats()
return _stats; return _stats;
} }
QJSValue File::calculatedBounds()
{
QJSValue ret = _engine->newObject();
loadFitsKeywords();
if(_wcs)
{
double minRa, maxRa, minDec, maxDec, crVal1, crVal2;
_wcs->calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2);
ret.setProperty("minRA", minRa);
ret.setProperty("maxRA", maxRa);
ret.setProperty("minDEC", minDec);
ret.setProperty("maxDEC", maxDec);
ret.setProperty("crVal1", crVal1);
ret.setProperty("crVal2", crVal2);
}
return ret;
}
#ifdef PLATESOLVER #ifdef PLATESOLVER
QJSValue File::solve(bool updateHeader) QJSValue File::solve(bool updateHeader)
{ {
@@ -857,6 +936,21 @@ void FITSRecordModify::addKeyword(const QString &key, const QVariant &value, con
_update.append({key.toLatin1(), value, comment.toLatin1()}); _update.append({key.toLatin1(), value, comment.toLatin1()});
} }
void FITSRecordModify::updateProperty(const QString &id, const LibXISF::Variant &value)
{
_property.append(LibXISF::Property(id.toStdString(), value));
}
uint32_t FITSRecordModify::imageIndex() const
{
return _imageIdx;
}
void FITSRecordModify::setImageIndex(uint32_t idx)
{
_imageIdx = idx;
}
bool TextFile::open(const QString &path, const QString &mode) bool TextFile::open(const QString &path, const QString &mode)
{ {
_fr.setFileName(path); _fr.setFileName(path);
+11
View File
@@ -9,6 +9,7 @@
#include <QSemaphore> #include <QSemaphore>
#include "database.h" #include "database.h"
#include "imageinfodata.h" #include "imageinfodata.h"
#include "libxisf.h"
class BatchProcessing; class BatchProcessing;
class Solver; class Solver;
@@ -41,6 +42,8 @@ public:
Q_INVOKABLE void mark(File *file); Q_INVOKABLE void mark(File *file);
Q_INVOKABLE void unmark(File *file); Q_INVOKABLE void unmark(File *file);
Q_INVOKABLE bool isMarked(const File *file); Q_INVOKABLE bool isMarked(const File *file);
Q_INVOKABLE QJSValue getObjects(double ra, double dec, double distance);
Q_INVOKABLE QJSValue getObjects(const QJSValue &bounds);
Q_INVOKABLE void setMaxThread(int maxthread); Q_INVOKABLE void setMaxThread(int maxthread);
Q_INVOKABLE void sync(); Q_INVOKABLE void sync();
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const; Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
@@ -96,6 +99,7 @@ class File : public QObject
bool _fitsKeywordsLoaded = false; bool _fitsKeywordsLoaded = false;
QStringList _fitsKeywords; QStringList _fitsKeywords;
QMultiHash<QString, FITSRecord> _fitsRecords; QMultiHash<QString, FITSRecord> _fitsRecords;
std::shared_ptr<WCSDataT> _wcs;
void loadFitsKeywords(); void loadFitsKeywords();
bool mkpath(const QString &path) const; bool mkpath(const QString &path) const;
QJSValue _stats; QJSValue _stats;
@@ -124,6 +128,7 @@ public:
Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap()); Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap());
Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap()); Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap &params = QVariantMap());
Q_INVOKABLE QJSValue stats(); Q_INVOKABLE QJSValue stats();
Q_INVOKABLE QJSValue calculatedBounds();
#ifdef PLATESOLVER #ifdef PLATESOLVER
Q_INVOKABLE QJSValue solve(bool updateHeader = false); Q_INVOKABLE QJSValue solve(bool updateHeader = false);
Q_INVOKABLE QJSValue extractStars(bool hfr); Q_INVOKABLE QJSValue extractStars(bool hfr);
@@ -136,6 +141,8 @@ class FITSRecordModify : public QObject
QStringList _remove; QStringList _remove;
QVector<FITSRecord> _update; QVector<FITSRecord> _update;
QVector<FITSRecord> _add; QVector<FITSRecord> _add;
QVector<LibXISF::Property> _property;
uint32_t _imageIdx = 0;
friend class File; friend class File;
public: public:
@@ -143,6 +150,10 @@ public:
Q_INVOKABLE void removeKeyword(const QString &key); Q_INVOKABLE void removeKeyword(const QString &key);
Q_INVOKABLE void updateKeyword(const QString &key, const QVariant &value, const QString &comment = QString()); Q_INVOKABLE void updateKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
Q_INVOKABLE void addKeyword(const QString &key, const QVariant &value, const QString &comment = QString()); Q_INVOKABLE void addKeyword(const QString &key, const QVariant &value, const QString &comment = QString());
Q_PROPERTY(uint32_t imageIndex READ imageIndex WRITE setImageIndex);
void updateProperty(const QString &id, const LibXISF::Variant &value);
uint32_t imageIndex() const;
void setImageIndex(uint32_t idx);
}; };
class TextFile : public QObject class TextFile : public QObject
+13
View File
@@ -186,6 +186,19 @@ bool Solver::updateHeader(QString &error)
modify.updateKeyword("CTYPE2", "DEC--TAN", QByteArray("first parameter DEC, projection TANgential")); modify.updateKeyword("CTYPE2", "DEC--TAN", QByteArray("first parameter DEC, projection TANgential"));
modify.updateKeyword("RADESYS", "ICRS", QByteArray("International Celestial Reference System")); modify.updateKeyword("RADESYS", "ICRS", QByteArray("International Celestial Reference System"));
modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates")); modify.updateKeyword("EQUINOX", 2000, QByteArray("Equinox of coordinates"));
LibXISF::F64Matrix matrix(2, 2);
matrix(0, 0) = std::cos(rotationRad) * cdeltx;
matrix(0, 1) =-std::sin(rotationRad) * cdelty;
matrix(1, 0) = std::sin(rotationRad) * cdeltx;
matrix(1, 1) = std::cos(rotationRad) * cdelty;
modify.updateProperty("PCL:AstrometricSolution:ReferenceCelestialCoordinates", LibXISF::F64Vector({solution.ra, solution.dec}));
modify.updateProperty("PCL:AstrometricSolution:ReferenceImageCoordinates", LibXISF::F64Vector({_stats.width / 2.0, _stats.height / 2.0}));
modify.updateProperty("PCL:AstrometricSolution:LinearTransformationMatrix", LibXISF::F64Matrix(matrix));
modify.updateProperty("PCL:AstrometricSolution:ProjectionSystem", LibXISF::String("Gnomonic"));
modify.updateProperty("PCL:AstrometricSolution:ReferenceNativeCoordinates", LibXISF::F64Vector({0, 90}));
bool ret = file.modifyFITSRecords(&modify); bool ret = file.modifyFITSRecords(&modify);
if(!ret)error = tr("Failed to update file header"); if(!ret)error = tr("Failed to update file header");
else emit headerUpdated(_path); else emit headerUpdated(_path);
View File
View File
View File
View File
View File
@@ -89,6 +89,11 @@ StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar")
m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load")); m_autoStretchOnLoad = addAction(QIcon(":/nuke_a.png"), tr("Apply auto stretch on load"));
m_autoStretchOnLoad->setCheckable(true); m_autoStretchOnLoad->setCheckable(true);
QAction *showGridButton = addAction(QIcon(":/grid.svg"), tr("Draw equatorial grid"));
showGridButton->setCheckable(true);
connect(showGridButton, &QAction::toggled, this, &StretchToolbar::drawGrid);
QSettings settings; QSettings settings;
m_autoStretchOnLoad->setChecked(settings.value("stretchtoolbar/autostretch", false).toBool()); m_autoStretchOnLoad->setChecked(settings.value("stretchtoolbar/autostretch", false).toBool());
} }
@@ -33,6 +33,7 @@ signals:
void invert(bool enable); void invert(bool enable);
void superPixel(bool enable); void superPixel(bool enable);
void falseColor(bool enable); void falseColor(bool enable);
void drawGrid(bool enable);
}; };
#endif // STRETCHTOOLBAR_H #endif // STRETCHTOOLBAR_H
View File
+5 -5
View File
@@ -6,9 +6,9 @@ if(BUILD_THUMBNAILER)
Dll.cpp Dll.cpp
loadimage.cpp loadimage.cpp
TenmonThumbnailProvider.cpp TenmonThumbnailProvider.cpp
../rawimage.h ../src/rawimage.h
../rawimage.cpp ../src/rawimage.cpp
../rawimage_sse.cpp) ../src/rawimage_sse.cpp)
set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "") set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "")
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT) target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
@@ -19,8 +19,8 @@ if(BUILD_THUMBNAILER)
qt_add_executable(tenmonthumbnailer qt_add_executable(tenmonthumbnailer
main.cpp main.cpp
loadimage.cpp loadimage.cpp
../rawimage.cpp ../src/rawimage.cpp
../rawimage_sse.cpp) ../src/rawimage_sse.cpp)
target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF) target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF)
+1 -1
View File
@@ -2,7 +2,7 @@
#include <thumbcache.h> // For IThumbnailProvider. #include <thumbcache.h> // For IThumbnailProvider.
#include <new> #include <new>
#include "libxisf.h" #include "libxisf.h"
#include "../rawimage.h" #include "../src/rawimage.h"
bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage); bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage); bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
+2 -3
View File
@@ -1,7 +1,6 @@
#include "genthumbnail.h" #include "genthumbnail.h"
#include "../rawimage.h" #include "../src/rawimage.h"
#include "../loadimage.h" #include "../src/loadimage.h"
int generateThumbnail(const QString &input, const QString &output, uint32_t size) int generateThumbnail(const QString &input, const QString &output, uint32_t size)
{ {
+1 -1
View File
@@ -1,5 +1,5 @@
#include "libxisf.h" #include "libxisf.h"
#include "../rawimage.h" #include "../src/rawimage.h"
#ifdef WIN32 #ifdef WIN32
#include <windows.h> #include <windows.h>
#endif #endif
+1 -1
View File
@@ -1,7 +1,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include "../rawimage.h" #include "../src/rawimage.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" #include "stb_image_write.h"