Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f9f005e7ea | |||
| 5ba6b4863c | |||
| 6c7e078340 | |||
| b58559a18a | |||
| 2ac14a6c04 | |||
| b84256625c | |||
| 202a2b11b7 | |||
| 32f192ed7e | |||
| a0422683bd | |||
| ce67b35bfa | |||
| f016500f12 | |||
| 6069ebbbac | |||
| e587d84e05 | |||
| c01f2e328a | |||
| 8b498bbe73 | |||
| c6bc792ff7 | |||
| 1a307d82f9 | |||
| 8c5e2b2ebf | |||
| 03ad135ef0 | |||
| 2a78a9a41d |
+33
-32
@@ -23,39 +23,39 @@ find_library(FITS_LIB cfitsio REQUIRED)
|
||||
find_library(RAW_LIB NAMES raw_r REQUIRED)
|
||||
find_library(WCS_LIB wcs wcslib REQUIRED)
|
||||
find_library(LCMS2_LIB lcms2 REQUIRED)
|
||||
find_library(STELLARSOLVER_LIB stellarsolver)
|
||||
find_library(STELLARSOLVER_LIB NAMES stellarsolver stellarsolver6)
|
||||
|
||||
add_subdirectory(libXISF)
|
||||
|
||||
set(TENMON_SRC
|
||||
about.cpp about.h
|
||||
batchprocessing.cpp batchprocessing.h batchprocessing.ui
|
||||
chartgraph.h chartgraph.cpp
|
||||
database.cpp database.h
|
||||
databaseview.cpp databaseview.h
|
||||
delete.cpp
|
||||
filesystemwidget.cpp filesystemwidget.h
|
||||
histogram.cpp histogram.h
|
||||
httpdownloader.h httpdownloader.cpp
|
||||
imageinfo.cpp imageinfo.h
|
||||
imageinfodata.cpp imageinfodata.h
|
||||
imageringlist.cpp imageringlist.h
|
||||
imagescrollarea.cpp imagescrollarea.h
|
||||
imagewidget.h imagewidget.cpp
|
||||
loadimage.h loadimage.cpp
|
||||
loadrunable.cpp loadrunable.h
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h
|
||||
markedfiles.cpp markedfiles.h
|
||||
mtfparam.h
|
||||
rawimage.cpp rawimage.h
|
||||
rawimage_sse.cpp
|
||||
scriptengine.cpp scriptengine.h
|
||||
settingsdialog.cpp settingsdialog.h
|
||||
statusbar.cpp statusbar.h
|
||||
stfslider.cpp stfslider.h
|
||||
stretchtoolbar.cpp stretchtoolbar.h
|
||||
tfloat16.h
|
||||
src/about.cpp src/about.h
|
||||
src/batchprocessing.cpp src/batchprocessing.h src/batchprocessing.ui
|
||||
src/chartgraph.h src/chartgraph.cpp
|
||||
src/database.cpp src/database.h
|
||||
src/databaseview.cpp src/databaseview.h
|
||||
src/delete.cpp
|
||||
src/filesystemwidget.cpp src/filesystemwidget.h
|
||||
src/histogram.cpp src/histogram.h
|
||||
src/httpdownloader.h src/httpdownloader.cpp
|
||||
src/imageinfo.cpp src/imageinfo.h
|
||||
src/imageinfodata.cpp src/imageinfodata.h
|
||||
src/imageringlist.cpp src/imageringlist.h
|
||||
src/imagescrollarea.cpp src/imagescrollarea.h
|
||||
src/imagewidget.h src/imagewidget.cpp
|
||||
src/loadimage.h src/loadimage.cpp
|
||||
src/loadrunable.cpp src/loadrunable.h
|
||||
src/main.cpp
|
||||
src/mainwindow.cpp src/mainwindow.h
|
||||
src/markedfiles.cpp src/markedfiles.h
|
||||
src/mtfparam.h
|
||||
src/rawimage.cpp src/rawimage.h
|
||||
src/rawimage_sse.cpp
|
||||
src/scriptengine.cpp src/scriptengine.h
|
||||
src/settingsdialog.cpp src/settingsdialog.h
|
||||
src/statusbar.cpp src/statusbar.h
|
||||
src/stfslider.cpp src/stfslider.h
|
||||
src/stretchtoolbar.cpp src/stretchtoolbar.h
|
||||
src/tfloat16.h
|
||||
thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h
|
||||
)
|
||||
|
||||
@@ -96,9 +96,9 @@ if(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
|
||||
endif(MXE)
|
||||
target_compile_definitions(tenmon PRIVATE "PLATESOLVER")
|
||||
target_sources(tenmon PRIVATE
|
||||
solver.cpp solver.h
|
||||
platesolving.cpp platesolving.h platesolving.ui
|
||||
platesolvingsettings.cpp platesolvingsettings.h platesolvingsettings.ui
|
||||
src/solver.cpp src/solver.h
|
||||
src/platesolving.cpp src/platesolving.h src/platesolving.ui
|
||||
src/platesolvingsettings.cpp src/platesolvingsettings.h src/platesolvingsettings.ui
|
||||
)
|
||||
message(STATUS "Found stellarsolver ${STELLARSOLVER_INCLUDE} ${STELLARSOLVER_LIB}")
|
||||
endif(STELLARSOLVER_INCLUDE AND STELLARSOLVER_LIB)
|
||||
@@ -130,6 +130,7 @@ if(UNIX AND NOT APPLE)
|
||||
install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
||||
install(FILES resources/space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps")
|
||||
install(FILES resources/space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
|
||||
install(FILES space.nouspiro.tenmon.xisf.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages")
|
||||
endif()
|
||||
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||
endif(UNIX AND NOT APPLE)
|
||||
|
||||
@@ -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
|
||||
It is important that you compile StellarSolver with Qt6. By default it use Qt5 but when linked with Qt6 program it will
|
||||
crash.
|
||||
|
||||
Using OpenNGC database https://github.com/mattiaverga/OpenNGC under CC-BY-SA-4.0 https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
@@ -184,6 +184,48 @@ Location of this directory is on Windows: "C:/Users/<USER>/AppData/Roaming
|
||||
core.log(JSON.stringify(profile));</pre></li>
|
||||
<li><b>setSolverProfile(index)</b> set solver profile by index. Valid values are from 1 to 8 as in GUI.</li>
|
||||
<li><b>setSolverProfile(profile)</b> set solver profile. Parameter is same as object returned by <i>getSolverProfile()</i> method</li>
|
||||
<li><b>question(question, buttons, title = "")</b> show dialog with question. First argument <i>question</i> is string. Second argument <i>buttons</i>
|
||||
is array of following strings. "ok", "yes", "no", "yesall", "noall", "abort", "retry", "ignore", "cancel", "discard", "apply", "reset" Third argument <i>title</i> is optional string that show in title bar.
|
||||
It return button that was clicked as a string.
|
||||
<pre>var button = core.question("Apply to all files?", ["yes", "no"]);</pre>
|
||||
</li>
|
||||
<li><b>plot(graph)</b> this method show graph defined by JS object.
|
||||
|
||||
<pre>
|
||||
var chart = {
|
||||
"title": "Chart title", // Title that will show on top of chart
|
||||
"legend":
|
||||
{
|
||||
"visible": true,// default is true
|
||||
"align": "left" // allowed values are "top", "right", "bottom", "left". default is "top"
|
||||
},
|
||||
"series":[ // array of data series
|
||||
{
|
||||
"title": "HFR",
|
||||
"type": "bar", // type of the serie. Can be one of "line", "points", "linePoints", "bar"
|
||||
"y":[2.5,3.1,2.6,2.2] // array of values
|
||||
},
|
||||
{
|
||||
"title": "Ecc",
|
||||
"y":[0.37, 0.4, 0.35, 0.25],
|
||||
"color": "red" // color of serie. It can be name of color like "green" or RGB hex value "#ccddff"
|
||||
},
|
||||
{
|
||||
"title": "Stars",
|
||||
"type": "points", // type of serie. can be "line", "points", "linePoints" and "bar". Default is "line"
|
||||
"shape": "star", // shape of markers. valid only for points
|
||||
"x":[1, 2.5, 3.5, 6],// line, points and linePoints can also have "x" values. Otherwise it assume sequence 0,1,2 ...
|
||||
"y":[523,412,487,510],
|
||||
"y2": true, // if set to true this serie will use secondary Y axis
|
||||
"bestFit": true, // show best fit line
|
||||
"color": "#0000ff"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
core.plot(chart);
|
||||
</pre>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>File</h4>
|
||||
|
||||
@@ -135,6 +135,54 @@ Le deuxième paramètre est la valeur par défaut dans la zone de saisie. Les tr
|
||||
<li><b>getItem(items)</b> affiche une boîte de dialogue de sélection qui permet de sélectionner un élément dans un tableau d'éléments. Lorsque vous appuyez sur Annuler, il renvoie Undefined.</li>
|
||||
<li><b>setStartingSolution(solution)</b> with this you can set starting point and image scale. It accepth object with attributes "ra", "dec", "pixscale".
|
||||
Same object as returned by <i>File.solve()</i> method. You can also call it without paramer in which case it will clear any previously set values.</li>
|
||||
<li><b>getSolverProfile()</b> return solver profile as Object.
|
||||
<pre>var profile = core.getSolverProfile();
|
||||
core.log(JSON.stringify(profile));</pre></li>
|
||||
<li><b>setSolverProfile(index)</b> set solver profile by index. Valid values are from 1 to 8 as in GUI.</li>
|
||||
<li><b>setSolverProfile(profile)</b> set solver profile. Parameter is same as object returned by <i>getSolverProfile()</i> method</li>
|
||||
<li><b>question(question, buttons, title = "")</b> show dialog with question. First argument <i>question</i> is string. Second argument <i>buttons</i>
|
||||
is array of following strings. "ok", "yes", "no", "yesall", "noall", "abort", "retry", "ignore", "cancel", "discard", "apply", "reset" Third argument <i>title</i> is optional string that show in title bar.
|
||||
It return button that was clicked as a string.
|
||||
<pre>var button = core.question("Apply to all files?", ["yes", "no"]);</pre>
|
||||
</li>
|
||||
<li><b>plot(graph)</b> this method show graph defined by JS object.
|
||||
|
||||
<pre>
|
||||
var chart = {
|
||||
"title": "Chart title", // Title that will show on top of chart
|
||||
"legend":
|
||||
{
|
||||
"visible": true,// default is true
|
||||
"align": "left" // allowed values are "top", "right", "bottom", "left". default is "top"
|
||||
},
|
||||
"series":[ // array of data series
|
||||
{
|
||||
"title": "HFR",
|
||||
"type": "bar", // type of the serie. Can be one of "line", "points", "linePoints", "bar"
|
||||
"y":[2.5,3.1,2.6,2.2] // array of values
|
||||
},
|
||||
{
|
||||
"title": "Ecc",
|
||||
"y":[0.37, 0.4, 0.35, 0.25],
|
||||
"color": "red" // color of serie. It can be name of color like "green" or RGB hex value "#ccddff"
|
||||
},
|
||||
{
|
||||
"title": "Stars",
|
||||
"type": "points", // type of serie. can be "line", "points", "linePoints" and "bar". Default is "line"
|
||||
"shape": "star", // shape of markers. valid only for points
|
||||
"x":[1, 2.5, 3.5, 6],// line, points and linePoints can also have "x" values. Otherwise it assume sequence 0,1,2 ...
|
||||
"y":[523,412,487,510],
|
||||
"y2": true, // if set to true this serie will use secondary Y axis
|
||||
"bestFit": true, // show best fit line
|
||||
"color": "#0000ff"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
core.plot(chart);
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>File</h4>
|
||||
|
||||
@@ -126,6 +126,54 @@ V skripte je dostupný globálny objekt nazvaný <b>core</b> ktorý má nasledov
|
||||
<li><b>getItem(items)</b> ukáže dialog pre výber jednej hodnoty z poľa hodnôt. Vracia vybranú hodnotu ako String alebo ak je stlačené tlačidlo zrušiť vráti Undefined.</li>
|
||||
<li><b>setStartingSolution(solution)</b> with this you can set starting point and image scale. It accepth object with attributes "ra", "dec", "pixscale".
|
||||
Same object as returned by <i>File.solve()</i> method. You can also call it without paramer in which case it will clear any previously set values.</li>
|
||||
<li><b>getSolverProfile()</b> return solver profile as Object.
|
||||
<pre>var profile = core.getSolverProfile();
|
||||
core.log(JSON.stringify(profile));</pre></li>
|
||||
<li><b>setSolverProfile(index)</b> set solver profile by index. Valid values are from 1 to 8 as in GUI.</li>
|
||||
<li><b>setSolverProfile(profile)</b> set solver profile. Parameter is same as object returned by <i>getSolverProfile()</i> method</li>
|
||||
<li><b>question(question, buttons, title = "")</b> show dialog with question. First argument <i>question</i> is string. Second argument <i>buttons</i>
|
||||
is array of following strings. "ok", "yes", "no", "yesall", "noall", "abort", "retry", "ignore", "cancel", "discard", "apply", "reset" Third argument <i>title</i> is optional string that show in title bar.
|
||||
It return button that was clicked as a string.
|
||||
<pre>var button = core.question("Apply to all files?", ["yes", "no"]);</pre>
|
||||
</li>
|
||||
<li><b>plot(graph)</b> this method show graph defined by JS object.
|
||||
|
||||
<pre>
|
||||
var chart = {
|
||||
"title": "Chart title", // Title that will show on top of chart
|
||||
"legend":
|
||||
{
|
||||
"visible": true,// default is true
|
||||
"align": "left" // allowed values are "top", "right", "bottom", "left". default is "top"
|
||||
},
|
||||
"series":[ // array of data series
|
||||
{
|
||||
"title": "HFR",
|
||||
"type": "bar", // type of the serie. Can be one of "line", "points", "linePoints", "bar"
|
||||
"y":[2.5,3.1,2.6,2.2] // array of values
|
||||
},
|
||||
{
|
||||
"title": "Ecc",
|
||||
"y":[0.37, 0.4, 0.35, 0.25],
|
||||
"color": "red" // color of serie. It can be name of color like "green" or RGB hex value "#ccddff"
|
||||
},
|
||||
{
|
||||
"title": "Stars",
|
||||
"type": "points", // type of serie. can be "line", "points", "linePoints" and "bar". Default is "line"
|
||||
"shape": "star", // shape of markers. valid only for points
|
||||
"x":[1, 2.5, 3.5, 6],// line, points and linePoints can also have "x" values. Otherwise it assume sequence 0,1,2 ...
|
||||
"y":[523,412,487,510],
|
||||
"y2": true, // if set to true this serie will use secondary Y axis
|
||||
"bestFit": true, // show best fit line
|
||||
"color": "#0000ff"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
core.plot(chart);
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>File</h4>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
||||
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
|
||||
find_program(XDG-MIME xdg-mime)
|
||||
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 resources/space.nouspiro.tenmon.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 resources/space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
execute_process(COMMAND ${XDG-MIME} install --novendor space.nouspiro.tenmon.xisf.xml WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
@@ -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 |
Binary file not shown.
@@ -19,14 +19,16 @@
|
||||
<file>../translations/tenmon_pt_BR.qm</file>
|
||||
<file alias="help">../about/help_en</file>
|
||||
<file>colormap.png</file>
|
||||
<file>ngc.db</file>
|
||||
<file>grid.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/" lang="en">
|
||||
<qresource lang="en" prefix="/">
|
||||
<file alias="help">../about/help_en</file>
|
||||
</qresource>
|
||||
<qresource prefix="/" lang="sk">
|
||||
<qresource lang="sk" prefix="/">
|
||||
<file alias="help">../about/help_sk</file>
|
||||
</qresource>
|
||||
<qresource prefix="/" lang="fr">
|
||||
<qresource lang="fr" prefix="/">
|
||||
<file alias="help">../about/help_fr</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
+45
-12
@@ -12,10 +12,12 @@ if(files.length == 0)
|
||||
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 proceed = false;
|
||||
|
||||
if(action == "UPDATE")
|
||||
{
|
||||
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
||||
@@ -25,28 +27,59 @@ if(action == "UPDATE")
|
||||
value = core.getString("Enter new value", value);
|
||||
else
|
||||
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")
|
||||
{
|
||||
let keyword = core.getString("Enter keyword to add");
|
||||
let value = core.getString("Enter new value");
|
||||
keyword = keyword.toUpperCase();
|
||||
modify.addKeyword(keyword, value);
|
||||
if(keyword && value)
|
||||
{
|
||||
proceed = true;
|
||||
keyword = keyword.toUpperCase();
|
||||
modify.addKeyword(keyword, value);
|
||||
}
|
||||
}
|
||||
else if(action == "REMOVE")
|
||||
{
|
||||
let keywords = files[0].fitsKeywords().filter(checkFITS);
|
||||
let keyword = core.getItem(keywords, "Select keyword to remove");
|
||||
modify.removeKeyword(keyword);
|
||||
}
|
||||
|
||||
for(file of files)
|
||||
{
|
||||
if(file.suffix() == "fits" || file.suffix() == "fit" || file.suffix() == "xisf")
|
||||
if(keyword)
|
||||
{
|
||||
core.log("Modifing " + file.fileName());
|
||||
file.modifyFITSRecords(modify);
|
||||
proceed = true;
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,17 @@
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<releases>
|
||||
<release version="20250429" date="2025-04-29">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add ability to load multiple images in single file</li>
|
||||
<li>New plot() and question() script methods</li>
|
||||
<li>Color highlight of FITS keywords</li>
|
||||
<li>New scripts to batch platesolve and measure stars</li>
|
||||
<li>Stretch toolbar can now be vertical</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="20250318" date="2025-03-18">
|
||||
<description>
|
||||
<ul>
|
||||
@@ -107,7 +118,7 @@
|
||||
</release>
|
||||
<release version="20240816" date="2024-08-16">
|
||||
<description>
|
||||
Fix saving image
|
||||
<p>Fix saving image</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="20240616" date="2024-06-16">
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="image/x-xisf">
|
||||
<comment>Extensible Image Serialization Format</comment>
|
||||
<glob pattern="*.xisf"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -100,6 +100,7 @@ BatchProcessing::BatchProcessing(Database *database, QWidget *parent) : QDialog(
|
||||
|
||||
connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles);
|
||||
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->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths);
|
||||
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()
|
||||
{
|
||||
for(auto &item : _ui->pathsList->selectedItems())
|
||||
@@ -28,6 +28,7 @@ protected:
|
||||
public slots:
|
||||
void addFiles();
|
||||
void addDir();
|
||||
void addMarked();
|
||||
void removePath();
|
||||
void removeAllPaths();
|
||||
void browse();
|
||||
@@ -52,6 +52,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addMarkedButton">
|
||||
<property name="text">
|
||||
<string>Add marked</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeButton">
|
||||
<property name="text">
|
||||
@@ -89,6 +89,7 @@ protected:
|
||||
ChartGraph::ChartGraph(QWidget *parent) : QMainWindow(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setWindowTitle(tr("Chart"));
|
||||
|
||||
_chartView = new ChartView(this);
|
||||
setCentralWidget(_chartView);
|
||||
@@ -216,6 +217,13 @@ void ChartGraph::plot(const QVariant &graph)
|
||||
_chart->addSeries(series);
|
||||
series->attachAxis(xaxis);
|
||||
series->attachAxis(y2 ? y2axis : yaxis);
|
||||
|
||||
if(serie.contains("color"))
|
||||
{
|
||||
QString color = serie["color"].toString();
|
||||
if(QColor::isValidColorName(color))series->setColor(QColor::fromString(color));
|
||||
}
|
||||
|
||||
if(serie["bestFit"].toBool())
|
||||
{
|
||||
series->setBestFitLineVisible(true);
|
||||
@@ -228,11 +236,6 @@ void ChartGraph::plot(const QVariant &graph)
|
||||
if(type == "linePoints")
|
||||
series->setPointsVisible(true);
|
||||
|
||||
if(serie.contains("color"))
|
||||
{
|
||||
QString color = serie["color"].toString();
|
||||
if(QColor::isValidColorName(color))series->setColor(QColor::fromString(color));
|
||||
}
|
||||
}
|
||||
else if(type == "bar")
|
||||
{
|
||||
@@ -16,10 +16,29 @@ bool Database::init(const QLatin1String &connectionName)
|
||||
QDir dir(path);
|
||||
|
||||
database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
||||
ngc = QSqlDatabase::addDatabase("QSQLITE", connectionName + "ngc");
|
||||
|
||||
if(!dir.mkpath("."))
|
||||
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())
|
||||
{
|
||||
database.setDatabaseName(dir.absoluteFilePath("database2.db"));
|
||||
@@ -182,7 +201,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
||||
QStringList scannedDirs;
|
||||
int count = countFiles(dir, scannedDirs);
|
||||
progress->setMaximum(count);
|
||||
QSqlDatabase database = QSqlDatabase::database();
|
||||
database.transaction();
|
||||
|
||||
scannedDirs.clear();
|
||||
@@ -200,7 +218,6 @@ void Database::indexDir(const QDir &dir, QProgressDialog *progress)
|
||||
void Database::reindex(QProgressDialog *progress)
|
||||
{
|
||||
QVariantList deleteids;
|
||||
QSqlDatabase database = QSqlDatabase::database();
|
||||
database.transaction();
|
||||
QSqlQuery size("SELECT COUNT(*) FROM fits_files", database);
|
||||
size.next();
|
||||
@@ -239,6 +256,42 @@ QStringList Database::getFitsKeywords()
|
||||
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 m = m_getNgc.value("M").toString();
|
||||
QString ic = m_getNgc.value("IC").toString();
|
||||
if(!m.isEmpty())name = "M" + m + " ";
|
||||
if(!ic.isEmpty())name += "IC" + ic + " ";
|
||||
name += m_getNgc.value("Name").toString();
|
||||
|
||||
objects.append({
|
||||
name,
|
||||
m_getNgc.value("Common names").toString(),
|
||||
{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)
|
||||
{
|
||||
if(scannedDirs.contains(dir.canonicalPath()))return true;
|
||||
@@ -6,11 +6,13 @@
|
||||
#include <QSqlQuery>
|
||||
#include <QDir>
|
||||
#include <QProgressDialog>
|
||||
#include "imageinfodata.h"
|
||||
|
||||
class Database : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QSqlDatabase database;
|
||||
QSqlDatabase ngc;
|
||||
QSqlQuery m_markQuery;
|
||||
QSqlQuery m_unmarkQuery;
|
||||
QSqlQuery m_isMarkedQuery;
|
||||
@@ -22,6 +24,8 @@ class Database : public QObject
|
||||
QSqlQuery m_headerKeywords;
|
||||
QSqlQuery m_deleteFile;
|
||||
|
||||
QSqlQuery m_getNgc;
|
||||
|
||||
int m_progress;
|
||||
public:
|
||||
explicit Database(QObject *parent = 0);
|
||||
@@ -37,6 +41,7 @@ public:
|
||||
void indexDir(const QDir &dir, QProgressDialog *progress);
|
||||
void reindex(QProgressDialog *progress);
|
||||
QStringList getFitsKeywords();
|
||||
QVector<SkyObject> getObjects(double minRa, double maxRa, double minDec, double maxDec);
|
||||
protected:
|
||||
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
|
||||
bool indexFile(const QFileInfo &file);
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QRegularExpression>
|
||||
#include <wcslib/wcshdr.h>
|
||||
#include <wcslib/wcsfix.h>
|
||||
#include "database.h"
|
||||
#include "libxisf.h"
|
||||
|
||||
static const QVector<QByteArray> noEditableKey = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "EXTEND", "BZERO", "BSCALE"};
|
||||
@@ -153,9 +154,9 @@ bool WCSDataT::worldToPixel(const SkyPoint &point, QPointF &pixel) const
|
||||
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;
|
||||
maxRa = -1000;
|
||||
@@ -208,6 +209,7 @@ void WCSDataT::calculateBounds(double &minRa, double &maxRa, double &minDec, dou
|
||||
if(s.contains(scp))
|
||||
minDec = -90;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double hav(double x)
|
||||
@@ -266,6 +268,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');
|
||||
}
|
||||
|
||||
QString SkyPoint::RAString() const
|
||||
{
|
||||
return toHMS(ra / 15);
|
||||
}
|
||||
|
||||
QString SkyPoint::DECString() const
|
||||
{
|
||||
return toDMS(dec);
|
||||
}
|
||||
|
||||
double SkyPoint::fromHMS(const QString &hms)
|
||||
{
|
||||
double deg = fromDMS(hms);
|
||||
@@ -308,11 +320,21 @@ QString SkyPoint::toHMS(double decHour)
|
||||
|
||||
QString SkyPoint::toDMS(double deg)
|
||||
{
|
||||
int sign = deg < 0.0 ? -1 : 1;
|
||||
deg *= sign;
|
||||
double d,m,s,md;
|
||||
md = std::modf(deg, &d) * 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
|
||||
@@ -403,3 +425,148 @@ SkyPointScale ImageInfoData::getCenterRaDec() const
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -5,12 +5,15 @@
|
||||
#include <QPointF>
|
||||
#include <QVector>
|
||||
#include <QVariant>
|
||||
#include <QPainterPath>
|
||||
#include <wcslib/wcs.h>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
namespace LibXISF { struct FITSKeyword; struct Property; }
|
||||
|
||||
class Database;
|
||||
|
||||
struct FITSRecord
|
||||
{
|
||||
QByteArray key;
|
||||
@@ -36,10 +39,13 @@ public:
|
||||
double RAHour() const { return ra / 15.0; }
|
||||
double DEC() const { return dec; }
|
||||
QString toString() const;
|
||||
QString RAString() const;
|
||||
QString DECString() const;
|
||||
static double fromHMS(const QString &hms);
|
||||
static double fromDMS(const QString &dms);
|
||||
static QString toHMS(double decHour);
|
||||
static QString toDMS(double deg);
|
||||
SkyPoint operator+(const SkyPoint &p);
|
||||
};
|
||||
|
||||
struct SkyPointScale
|
||||
@@ -51,6 +57,28 @@ struct SkyPointScale
|
||||
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
|
||||
{
|
||||
int nwcs = 0;
|
||||
@@ -65,9 +93,10 @@ public:
|
||||
~WCSDataT();
|
||||
bool pixelToWorld(const QPointF &pixel, SkyPoint &point) 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; };
|
||||
SkyPointScale getRaDecScale() const;
|
||||
SkyGrid prepareGrid(uint32_t w, uint32_t h, Database *database);
|
||||
};
|
||||
|
||||
struct ImageInfoData
|
||||
@@ -142,6 +142,11 @@ void ImageScrollArea::falseColor(bool enable)
|
||||
m_imageWidget->falseColor(enable);
|
||||
}
|
||||
|
||||
void ImageScrollArea::drawGrid(bool enable)
|
||||
{
|
||||
m_imageWidget->drawGrid(enable);
|
||||
}
|
||||
|
||||
QImage ImageScrollArea::renderToImage()
|
||||
{
|
||||
return m_imageWidget->renderToImage();
|
||||
@@ -31,6 +31,7 @@ public slots:
|
||||
void invert(bool enable);
|
||||
void superPixel(bool enable);
|
||||
void falseColor(bool enable);
|
||||
void drawGrid(bool enable);
|
||||
QImage renderToImage();
|
||||
protected slots:
|
||||
void scrollEvent();
|
||||
@@ -180,6 +180,10 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
|
||||
void ImageWidgetGL::setWCS(std::shared_ptr<WCSDataT> 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)
|
||||
@@ -496,6 +500,18 @@ void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scal
|
||||
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()
|
||||
{
|
||||
float dx = m_dx;
|
||||
@@ -613,9 +629,43 @@ void ImageWidgetGL::paintGL()
|
||||
m_program->setUniformValue("colormapIdx", m_colormapIdx);
|
||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
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)
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QOpenGLTexture>
|
||||
#include <QOpenGLVertexArrayObject>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QPainterPath>
|
||||
#include "database.h"
|
||||
#include "rawimage.h"
|
||||
#include "imageinfodata.h"
|
||||
@@ -37,6 +38,7 @@ public:
|
||||
virtual QImage renderToImage() = 0;
|
||||
virtual void thumbnailLoaded(const Image *image) = 0;
|
||||
virtual void showThumbnail(bool enable) = 0;
|
||||
virtual void drawGrid(bool enable) = 0;
|
||||
|
||||
static QImage loadColormap();
|
||||
};
|
||||
@@ -70,6 +72,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
|
||||
GLuint m_debayerTex = 0;
|
||||
std::shared_ptr<RawImage> m_rawImage;
|
||||
std::shared_ptr<WCSDataT> m_wcs;
|
||||
SkyGrid m_grid;
|
||||
int m_width, m_height;
|
||||
int m_imgWidth = -1, m_imgHeight = -1;
|
||||
int m_currentImg = 0;
|
||||
@@ -87,6 +90,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
|
||||
bool m_selecting = false;
|
||||
bool m_sizesDirty = false;
|
||||
bool m_srgb = false;
|
||||
bool m_drawGrid = false;
|
||||
int m_thumbnailCount = 0;
|
||||
int m_maxTextureSize = 0;
|
||||
int m_maxArrayLayers = 0;
|
||||
@@ -116,6 +120,7 @@ public:
|
||||
QImage renderToImage() override;
|
||||
void thumbnailLoaded(const Image *image) override;
|
||||
void showThumbnail(bool enable) override;
|
||||
void drawGrid(bool enable) override;
|
||||
protected:
|
||||
void paintGL() override;
|
||||
void resizeGL(int w, int h) override;
|
||||
@@ -165,11 +165,12 @@ void writeFITSImage(fitsfile *fw, std::shared_ptr<RawImage> rawimage, ImageInfoD
|
||||
double vald = record.value.toDouble(&isdouble);
|
||||
int valb = record.value.toString() == "T";
|
||||
long long vall = record.value.toLongLong(&isint);
|
||||
if(isint)isint = vall == vald;
|
||||
QByteArray str = record.value.toString().toLatin1();
|
||||
if(isdouble)
|
||||
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||
else if(isint)
|
||||
if(isint)
|
||||
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)
|
||||
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||
else if(record.key == "COMMENT")
|
||||
+18
-1
@@ -4,10 +4,27 @@
|
||||
#include <QTranslator>
|
||||
#include <QCommandLineParser>
|
||||
#include <stdlib.h>
|
||||
#include "thumbnailer/genthumbnail.h"
|
||||
#include "../thumbnailer/genthumbnail.h"
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
#include <QTimer>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
QApplication app(argc, argv);
|
||||
QFileSystemModel model;
|
||||
QTreeView treeView;
|
||||
|
||||
|
||||
QTimer::singleShot(5000, [&model,&treeView](){
|
||||
treeView.setModel(&model);
|
||||
model.setRootPath("/home");
|
||||
});
|
||||
treeView.resize(800, 600);
|
||||
treeView.show();
|
||||
return app.exec();
|
||||
|
||||
#ifdef __linux__
|
||||
setenv("LC_NUMERIC", "C", 1);
|
||||
#endif
|
||||
@@ -92,6 +92,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
connect(m_stretchPanel, &StretchToolbar::invert, m_image, &ImageScrollArea::invert);
|
||||
connect(m_stretchPanel, &StretchToolbar::superPixel, m_image, &ImageScrollArea::superPixel);
|
||||
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_filesystem = new FilesystemWidget(m_ringList, this);
|
||||
@@ -263,7 +264,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), Qt::Key_F3, m_ringList, &ImageRingList::toggleSlideshow);
|
||||
slideshowAction->setCheckable(true);
|
||||
viewMenu->addSeparator();
|
||||
viewMenu->addActions(m_stretchPanel->actions());
|
||||
auto actionList = m_stretchPanel->actions();
|
||||
actionList.removeFirst();
|
||||
viewMenu->addActions(actionList);
|
||||
menuBar()->addMenu(viewMenu);
|
||||
|
||||
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
||||
@@ -92,6 +92,55 @@ bool ScriptEngine::isMarked(const File *file)
|
||||
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)
|
||||
{
|
||||
int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1);
|
||||
@@ -410,12 +459,14 @@ void File::loadFitsKeywords()
|
||||
{
|
||||
readXISFHeader(_path, info);
|
||||
}
|
||||
else if(suffix().toLower() == "fits" || suffix().toLower() == "fit")
|
||||
else if(suffix().toLower() == "fits" || suffix().toLower() == "fit" || suffix().toLower() == "fz")
|
||||
{
|
||||
readFITSHeader(_path, info);
|
||||
}
|
||||
else return;
|
||||
|
||||
_wcs = info.wcs;
|
||||
|
||||
for(auto &record : info.fitsHeader)
|
||||
{
|
||||
_fitsKeywords.append(record.key);
|
||||
@@ -788,6 +839,24 @@ QJSValue File::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
|
||||
QJSValue File::solve(bool updateHeader)
|
||||
{
|
||||
@@ -41,6 +41,8 @@ public:
|
||||
Q_INVOKABLE void mark(File *file);
|
||||
Q_INVOKABLE void unmark(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 sync();
|
||||
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
|
||||
@@ -96,6 +98,7 @@ class File : public QObject
|
||||
bool _fitsKeywordsLoaded = false;
|
||||
QStringList _fitsKeywords;
|
||||
QMultiHash<QString, FITSRecord> _fitsRecords;
|
||||
std::shared_ptr<WCSDataT> _wcs;
|
||||
void loadFitsKeywords();
|
||||
bool mkpath(const QString &path) const;
|
||||
QJSValue _stats;
|
||||
@@ -124,6 +127,7 @@ public:
|
||||
Q_INVOKABLE File* convert(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
||||
Q_INVOKABLE File* convertAsync(const QString &outpath, const QString &format, const QVariantMap ¶ms = QVariantMap());
|
||||
Q_INVOKABLE QJSValue stats();
|
||||
Q_INVOKABLE QJSValue calculatedBounds();
|
||||
#ifdef PLATESOLVER
|
||||
Q_INVOKABLE QJSValue solve(bool updateHeader = false);
|
||||
Q_INVOKABLE QJSValue extractStars(bool hfr);
|
||||
@@ -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->setCheckable(true);
|
||||
|
||||
QAction *showGridButton = addAction(QIcon(":/grid.svg"), tr("Draw equatorial grid"));
|
||||
showGridButton->setCheckable(true);
|
||||
connect(showGridButton, &QAction::toggled, this, &StretchToolbar::drawGrid);
|
||||
|
||||
QSettings settings;
|
||||
m_autoStretchOnLoad->setChecked(settings.value("stretchtoolbar/autostretch", false).toBool());
|
||||
}
|
||||
@@ -33,6 +33,7 @@ signals:
|
||||
void invert(bool enable);
|
||||
void superPixel(bool enable);
|
||||
void falseColor(bool enable);
|
||||
void drawGrid(bool enable);
|
||||
};
|
||||
|
||||
#endif // STRETCHTOOLBAR_H
|
||||
@@ -6,9 +6,9 @@ if(BUILD_THUMBNAILER)
|
||||
Dll.cpp
|
||||
loadimage.cpp
|
||||
TenmonThumbnailProvider.cpp
|
||||
../rawimage.h
|
||||
../rawimage.cpp
|
||||
../rawimage_sse.cpp)
|
||||
../src/rawimage.h
|
||||
../src/rawimage.cpp
|
||||
../src/rawimage_sse.cpp)
|
||||
set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "")
|
||||
|
||||
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
|
||||
@@ -19,8 +19,8 @@ if(BUILD_THUMBNAILER)
|
||||
qt_add_executable(tenmonthumbnailer
|
||||
main.cpp
|
||||
loadimage.cpp
|
||||
../rawimage.cpp
|
||||
../rawimage_sse.cpp)
|
||||
../src/rawimage.cpp
|
||||
../src/rawimage_sse.cpp)
|
||||
|
||||
target_link_libraries(tenmonthumbnailer PRIVATE ${FITS_LIB} XISF)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <thumbcache.h> // For IThumbnailProvider.
|
||||
#include <new>
|
||||
#include "libxisf.h"
|
||||
#include "../rawimage.h"
|
||||
#include "../src/rawimage.h"
|
||||
|
||||
bool loadXISF(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
||||
bool loadFITS(const LibXISF::ByteArray &data, std::shared_ptr<RawImage> &rawImage);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "genthumbnail.h"
|
||||
#include "../rawimage.h"
|
||||
#include "../loadimage.h"
|
||||
|
||||
#include "../src/rawimage.h"
|
||||
#include "../src/loadimage.h"
|
||||
|
||||
int generateThumbnail(const QString &input, const QString &output, uint32_t size)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "libxisf.h"
|
||||
#include "../rawimage.h"
|
||||
#include "../src/rawimage.h"
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "../rawimage.h"
|
||||
#include "../src/rawimage.h"
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
|
||||
Binary file not shown.
+366
-162
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+382
-178
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+370
-166
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+367
-163
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user