diff --git a/database.cpp b/database.cpp index 99309d4..7ccfcfc 100644 --- a/database.cpp +++ b/database.cpp @@ -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 Database::getObjects(double minRa, double maxRa, double minDec, double maxDec) +{ + QVector 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").toDouble(), + {0, 0}, + }); + } + } + return objects; +} + bool Database::indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs) { if(scannedDirs.contains(dir.canonicalPath()))return true; diff --git a/database.h b/database.h index ff4e435..84bfc5e 100644 --- a/database.h +++ b/database.h @@ -6,11 +6,13 @@ #include #include #include +#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 getObjects(double minRa, double maxRa, double minDec, double maxDec); protected: bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs); bool indexFile(const QFileInfo &file); diff --git a/imageinfodata.cpp b/imageinfodata.cpp index 224780c..e004b1e 100644 --- a/imageinfodata.cpp +++ b/imageinfodata.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "database.h" #include "libxisf.h" static const QVector 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) @@ -327,6 +329,14 @@ QString SkyPoint::toDMS(double deg) 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 ret; @@ -416,7 +426,18 @@ SkyPointScale ImageInfoData::getCenterRaDec() const return ret; } -SkyGrid WCSDataT::prepareGrid(uint32_t w, uint32_t h) +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; @@ -424,6 +445,27 @@ SkyGrid WCSDataT::prepareGrid(uint32_t w, uint32_t h) 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; @@ -526,4 +568,5 @@ void SkyGrid::clear() empty = true; grid.clear(); text.clear(); + objects.clear(); } diff --git a/imageinfodata.h b/imageinfodata.h index 08406c8..09ed2ff 100644 --- a/imageinfodata.h +++ b/imageinfodata.h @@ -12,6 +12,8 @@ namespace LibXISF { struct FITSKeyword; struct Property; } +class Database; + struct FITSRecord { QByteArray key; @@ -43,6 +45,7 @@ public: static double fromDMS(const QString &dms); static QString toHMS(double decHour); static QString toDMS(double deg); + SkyPoint operator+(const SkyPoint &p); }; struct SkyPointScale @@ -54,11 +57,25 @@ struct SkyPointScale double scaleHigh = 10000.0; }; +struct SkyObject +{ + QString name; + QString name2; + SkyPoint skyPoint; + double maj_ax; + double min_ax; + double pos_ang; + double vmag; + QPointF pixel; +}; + struct SkyGrid { bool empty = true; QPainterPath grid; QVector> text; + QVector objects; + double rot_ang = 0; void clear(); }; @@ -76,10 +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); + SkyGrid prepareGrid(uint32_t w, uint32_t h, Database *database); }; struct ImageInfoData diff --git a/imagewidget.cpp b/imagewidget.cpp index f2e949f..3bd7d03 100644 --- a/imagewidget.cpp +++ b/imagewidget.cpp @@ -182,8 +182,8 @@ void ImageWidgetGL::setWCS(std::shared_ptr wcs) m_wcs = wcs; m_grid.clear(); - if(m_drawGrid) - m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight); + if(m_drawGrid && m_wcs) + m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database); } void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos) @@ -502,8 +502,8 @@ void swPaint(std::shared_ptr &rawImage, float dx, float dy, float scal void ImageWidgetGL::drawGrid(bool enable) { - if(m_grid.empty) - m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight); + if(m_grid.empty && m_wcs) + m_grid = m_wcs->prepareGrid(m_imgWidth, m_imgHeight, m_database); if(enable != m_drawGrid) { @@ -644,13 +644,28 @@ void ImageWidgetGL::paintGL() painter.setClipRect(0, 0, m_imgWidth, m_imgHeight); painter.drawPath(m_grid.grid); painter.setPen(Qt::yellow); - painter.setFont(QFont({"Arial", "Sans"}, 12 / m_scale)); + 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) diff --git a/resources/ngc.db b/resources/ngc.db new file mode 100644 index 0000000..c329dd4 Binary files /dev/null and b/resources/ngc.db differ diff --git a/resources/resources.qrc b/resources/resources.qrc index 14d7c63..e1ca5e1 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -19,6 +19,7 @@ ../translations/tenmon_pt_BR.qm ../about/help_en colormap.png + ngc.db ../about/help_en diff --git a/scriptengine.cpp b/scriptengine.cpp index b1b95b5..ab6b65c 100644 --- a/scriptengine.cpp +++ b/scriptengine.cpp @@ -92,6 +92,28 @@ bool ScriptEngine::isMarked(const File *file) return ret; } +QJSValue ScriptEngine::getObjects(double ra, double dec, double distance) +{ + QVector 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("vmag", object.vmag); + ret.setProperty(i++, jsObj); + } + + return ret; +} + void ScriptEngine::setMaxThread(int maxthread) { int newval = std::max(std::min(QThread::idealThreadCount(), maxthread), 1); diff --git a/scriptengine.h b/scriptengine.h index e923876..45e5c7b 100644 --- a/scriptengine.h +++ b/scriptengine.h @@ -41,6 +41,7 @@ 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 void setMaxThread(int maxthread); Q_INVOKABLE void sync(); Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;