diff --git a/datahandlers/catalogdb.cpp b/datahandlers/catalogdb.cpp index 1c9fbe2a5..17183423b 100644 --- a/datahandlers/catalogdb.cpp +++ b/datahandlers/catalogdb.cpp @@ -1,1006 +1,1042 @@ /*************************************************************************** catalogDB.cpp - K Desktop Planetarium ------------------- begin : 2012/03/08 copyright : (C) 2012 by Rishab Arora email : ra.rishab@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "catalogdb.h" #include "catalogdata.h" #include "catalogentrydata.h" #include "kstars/version.h" #include "../kstars/auxiliary/kspaths.h" #include "starobject.h" #include "deepskyobject.h" #include "skycomponent.h" -#include -#include +#include #include +#include +#include #include bool CatalogDB::Initialize() { skydb_ = QSqlDatabase::addDatabase("QSQLITE", "skydb"); QString dbfile = KSPaths::locate(QStandardPaths::GenericDataLocation, QString("skycomponents.sqlite")); if (dbfile.isEmpty()) dbfile = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString("skycomponents.sqlite"); QFile testdb(dbfile); bool first_run = false; if (!testdb.exists()) { qCWarning(KSTARS_CATALOG) << "DSO DB does not exist!"; first_run = true; } skydb_.setDatabaseName(dbfile); if (!skydb_.open()) { qCWarning(KSTARS_CATALOG) << "Unable to open DSO database file!"; qCWarning(KSTARS_CATALOG) << LastError(); } else { qCDebug(KSTARS_CATALOG) << "Opened the DSO Database. Ready!"; if (first_run == true) { FirstRun(); } } skydb_.close(); return true; } void CatalogDB::FirstRun() { qCWarning(KSTARS_CATALOG) << "Rebuilding Additional Sky Catalog Database"; QVector tables; tables.append("CREATE TABLE Version (" "Version CHAR DEFAULT NULL)"); tables.append("INSERT INTO Version VALUES (\"" KSTARS_VERSION "\")"); tables.append("CREATE TABLE ObjectDesignation (" "id INTEGER NOT NULL DEFAULT NULL PRIMARY KEY," "id_Catalog INTEGER DEFAULT NULL REFERENCES Catalog (id)," "UID_DSO INTEGER DEFAULT NULL REFERENCES DSO (UID)," "LongName MEDIUMTEXT DEFAULT NULL," "IDNumber INTEGER DEFAULT NULL," "Trixel INTEGER NULL)"); // TODO(kstar): `Trixel` int(11) NOT NULL COMMENT 'Trixel Number' // For Future safety tables.append("CREATE TABLE Catalog (" "id INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT," "Name CHAR NOT NULL DEFAULT 'NULL'," "Prefix CHAR DEFAULT 'NULL'," "Color CHAR DEFAULT '#CC0000'," "Epoch FLOAT DEFAULT 2000.0," "Author CHAR DEFAULT NULL," "License MEDIUMTEXT DEFAULT NULL," "FluxFreq CHAR DEFAULT 'NULL'," "FluxUnit CHAR DEFAULT 'NULL')"); tables.append("CREATE TABLE DSO (" "UID INTEGER DEFAULT NULL PRIMARY KEY AUTOINCREMENT," "RA DOUBLE NOT NULL DEFAULT 0.0," "Dec DOUBLE DEFAULT 0.0," //"RA CHAR NOT NULL DEFAULT 'NULL'," //"Dec CHAR NOT NULL DEFAULT 'NULL'," "Type INTEGER DEFAULT NULL," "Magnitude DECIMAL DEFAULT NULL," "PositionAngle INTEGER DEFAULT NULL," "MajorAxis FLOAT NOT NULL DEFAULT NULL," "MinorAxis FLOAT DEFAULT NULL," "Flux FLOAT DEFAULT NULL," "Add1 VARCHAR DEFAULT NULL," "Add2 INTEGER DEFAULT NULL," "Add3 INTEGER DEFAULT NULL," "Add4 INTEGER DEFAULT NULL)"); for (int i = 0; i < tables.count(); ++i) { QSqlQuery query(skydb_); if (!query.exec(tables[i])) { qCWarning(KSTARS_CATALOG) << query.lastError(); } } } CatalogDB::~CatalogDB() { skydb_.close(); } QSqlError CatalogDB::LastError() { // error description is in QSqlError::text() return skydb_.lastError(); } QStringList *CatalogDB::Catalogs() { RefreshCatalogList(); return &catalog_list_; } void CatalogDB::RefreshCatalogList() { catalog_list_.clear(); skydb_.open(); QSqlTableModel catalog(nullptr, skydb_); catalog.setTable("Catalog"); catalog.setSort(0, Qt::AscendingOrder); catalog.select(); for (int i = 0; i < catalog.rowCount(); ++i) { QSqlRecord record = catalog.record(i); QString name = record.value("Name").toString(); catalog_list_.append(name); // QString author = record.value("Author").toString(); // QString license = record.value("License").toString(); // QString compiled_by = record.value("CompiledBy").toString(); // QString prefix = record.value("Prefix").toString(); } catalog.clear(); skydb_.close(); } int CatalogDB::FindCatalog(const QString &catalog_name) { skydb_.open(); QSqlTableModel catalog(nullptr, skydb_); catalog.setTable("Catalog"); catalog.setFilter("Name LIKE \'" + catalog_name + "\'"); catalog.select(); int catalog_count = catalog.rowCount(); QSqlRecord record = catalog.record(0); int returnval = -1; if (catalog_count > 0) returnval = record.value("id").toInt(); catalog.clear(); skydb_.close(); return returnval; } void CatalogDB::AddCatalog(const CatalogData &catalog_data) { skydb_.open(); QSqlTableModel cat_entry(nullptr, skydb_); cat_entry.setTable("Catalog"); int row = 0; cat_entry.insertRows(row, 1); // row(0) is autoincerement ID cat_entry.setData(cat_entry.index(row, 1), catalog_data.catalog_name); cat_entry.setData(cat_entry.index(row, 2), catalog_data.prefix); cat_entry.setData(cat_entry.index(row, 3), catalog_data.color); cat_entry.setData(cat_entry.index(row, 4), catalog_data.epoch); cat_entry.setData(cat_entry.index(row, 5), catalog_data.author); cat_entry.setData(cat_entry.index(row, 6), catalog_data.license); cat_entry.setData(cat_entry.index(row, 7), catalog_data.fluxfreq); cat_entry.setData(cat_entry.index(row, 8), catalog_data.fluxunit); cat_entry.submitAll(); cat_entry.clear(); skydb_.close(); } void CatalogDB::RemoveCatalog(const QString &catalog_name) { // Part 1 Clear DSO Entries ClearDSOEntries(FindCatalog(catalog_name)); skydb_.open(); QSqlTableModel catalog(nullptr, skydb_); // Part 2 Clear Catalog Table catalog.setTable("Catalog"); catalog.setFilter("Name LIKE \'" + catalog_name + "\'"); catalog.select(); catalog.removeRows(0, catalog.rowCount()); catalog.submitAll(); catalog.clear(); skydb_.close(); RefreshCatalogList(); } void CatalogDB::ClearDSOEntries(int catalog_id) { skydb_.open(); QStringList del_query; // FIXME(spacetime): Only delete from DSO if removed from all Designations // del_query.append("DELETE FROM DSO WHERE UID IN (SELECT UID_DSO FROM " // "ObjectDesignation WHERE id_Catalog = " + // QString::number(catalog_id) + ")"); del_query.append("DELETE FROM ObjectDesignation WHERE id_Catalog = " + QString::number(catalog_id)); for (int i = 0; i < del_query.count(); ++i) { QSqlQuery query(skydb_); if (!query.exec(del_query[i])) { qCWarning(KSTARS_CATALOG) << query.lastError(); } } skydb_.close(); } int CatalogDB::FindFuzzyEntry(const double ra, const double dec, const double magnitude) { /* * FIXME (spacetime): Match the incoming entry with the ones from the db * with certain fuzz. If found, store it in rowuid * This Fuzz has not been established after due discussion */ //skydb_.open(); QSqlTableModel dsoentries(nullptr, skydb_); QString filter = "((RA - " + QString().setNum(ra) + ") between -0.0016 and 0.0016) and " "((Dec - " + QString().setNum(dec) + ") between -0.0016 and 0.0016) and" "((Magnitude - " + QString().setNum(magnitude) + ") between -0.1 and 0.1)"; // qDebug() << filter; dsoentries.setTable("DSO"); dsoentries.setFilter(filter); dsoentries.select(); int entry_count = dsoentries.rowCount(); QSqlRecord record = dsoentries.record(0); int returnval = -1; if (entry_count > 0) returnval = record.value("UID").toInt(); dsoentries.clear(); //skydb_.close(); // qDebug() << returnval; return returnval; } bool CatalogDB::AddEntry(const CatalogEntryData &catalog_entry, int catid) { if (!skydb_.open()) { qCWarning(KSTARS_CATALOG) << "Failed to open database to add catalog entry!"; qCWarning(KSTARS_CATALOG) << LastError(); return false; } bool retVal = _AddEntry(catalog_entry, catid); skydb_.close(); return retVal; } bool CatalogDB::_AddEntry(const CatalogEntryData &catalog_entry, int catid) { // Verification step // If RA, Dec are Null, it denotes an invalid object and should not be written if (catid < 0) { qCWarning(KSTARS_CATALOG) << "Catalog ID " << catid << " is invalid! Cannot add object."; return false; } if (catalog_entry.ra == KSParser::EBROKEN_DOUBLE || catalog_entry.ra == 0.0 || std::isnan(catalog_entry.ra) || catalog_entry.dec == KSParser::EBROKEN_DOUBLE || catalog_entry.dec == 0.0 || std::isnan(catalog_entry.dec)) { qCWarning(KSTARS_CATALOG) << "Attempt to add incorrect ra & dec with ID:" << catalog_entry.ID << " Long Name: " << catalog_entry.long_name; return false; } // Part 1: Adding in DSO table // I will not use QSQLTableModel as I need to execute a query to find // out the lastInsertId // Part 2: Fuzzy Match or Create New Entry int rowuid = FindFuzzyEntry(catalog_entry.ra, catalog_entry.dec, catalog_entry.magnitude); //skydb_.open(); if (rowuid == -1) //i.e. No fuzzy match found. Proceed to add new entry { QSqlQuery add_query(skydb_); add_query.prepare("INSERT INTO DSO (RA, Dec, Type, Magnitude, PositionAngle," " MajorAxis, MinorAxis, Flux) VALUES (:RA, :Dec, :Type," " :Magnitude, :PositionAngle, :MajorAxis, :MinorAxis," " :Flux)"); add_query.bindValue(":RA", catalog_entry.ra); add_query.bindValue(":Dec", catalog_entry.dec); add_query.bindValue(":Type", catalog_entry.type); add_query.bindValue(":Magnitude", catalog_entry.magnitude); add_query.bindValue(":PositionAngle", catalog_entry.position_angle); add_query.bindValue(":MajorAxis", catalog_entry.major_axis); add_query.bindValue(":MinorAxis", catalog_entry.minor_axis); add_query.bindValue(":Flux", catalog_entry.flux); if (!add_query.exec()) { qCWarning(KSTARS_CATALOG) << "Custom Catalog Insert Query FAILED!"; qCWarning(KSTARS_CATALOG) << add_query.lastQuery(); qCWarning(KSTARS_CATALOG) << add_query.lastError(); } // Find UID of the Row just added rowuid = add_query.lastInsertId().toInt(); add_query.clear(); } int ID = catalog_entry.ID; /* TODO(spacetime) * Possible Bugs in QSQL Db with SQLite * 1) Unless the db is closed and opened again, the next queries * fail. * 2) unless I clear the resources, db close fails. The doc says * this is to be rarely used. */ // Find ID of catalog //skydb_.close(); //catid = FindCatalog(catalog_entry.catalog_name); // Part 3: Add in Object Designation //skydb_.open(); QSqlQuery add_od(skydb_); if (ID >= 0) { add_od.prepare("INSERT INTO ObjectDesignation (id_Catalog, UID_DSO, LongName" ", IDNumber) VALUES (:catid, :rowuid, :longname, :id)"); add_od.bindValue(":id", ID); } else { //qWarning() << "FIXME: This query has not been tested!!!!"; add_od.prepare("INSERT INTO ObjectDesignation (id_Catalog, UID_DSO, LongName" ", IDNumber) VALUES (:catid, :rowuid, :longname," "(SELECT MAX(ISNULL(IDNumber,1))+1 FROM ObjectDesignation WHERE id_Catalog = :catid) )"); } add_od.bindValue(":catid", catid); add_od.bindValue(":rowuid", rowuid); add_od.bindValue(":longname", catalog_entry.long_name); bool retVal = true; if (!add_od.exec()) { qWarning() << "Query exec failed:"; qWarning() << add_od.lastQuery(); qWarning() << skydb_.lastError(); retVal = false; } add_od.clear(); //skydb_.close(); return retVal; } -bool CatalogDB::RemoveCustomEntry(const QString &entry_long_name) +bool CatalogDB::CheckCustomEntry(const QString &entry_long_name, int catid) +{ + if (!skydb_.open()) + { + qCWarning(KSTARS_CATALOG) << "Failed to open database to remove catalog entry!"; + qCWarning(KSTARS_CATALOG) << LastError(); + return false; + } + QSqlQuery query_od(skydb_); + + query_od.prepare("SELECT LongName FROM ObjectDesignation WHERE LongName = :long_name AND id_Catalog = :catid;"); + query_od.bindValue(":long_name", entry_long_name); + query_od.bindValue(":catid", catid); + if (!query_od.exec()) + { + qWarning() << "Query exec failed:"; + qWarning() << query_od.lastQuery(); + qWarning() << skydb_.lastError(); + skydb_.close(); + return false; + } + query_od.next(); + QSqlRecord result = query_od.record(); + + if (result.count() != 1 || result.value(0).toString().isEmpty()) + { + skydb_.close(); + return false; + } + skydb_.close(); + return true; +} + +bool CatalogDB::RemoveCustomEntry(const QString &entry_long_name, int catid) { if (!skydb_.open()) { qCWarning(KSTARS_CATALOG) << "Failed to open database to remove catalog entry!"; qCWarning(KSTARS_CATALOG) << LastError(); return false; } QSqlQuery remove_od(skydb_); - remove_od.prepare("DELETE FROM ObjectDesignation WHERE LongName = :long_name;"); + remove_od.prepare("DELETE FROM ObjectDesignation WHERE LongName = :long_name AND id_Catalog = :catid;"); remove_od.bindValue(":long_name", entry_long_name); + remove_od.bindValue(":catid", catid); if (!remove_od.exec()) { qWarning() << "Query exec failed:"; qWarning() << remove_od.lastQuery(); qWarning() << skydb_.lastError(); + skydb_.close(); return false; } skydb_.close(); return true; } QString CatalogDB::GetCatalogName(const QString &fname) { QDir::setCurrent(QDir::homePath()); // for files with relative path QString filename = fname; // If the filename begins with "~", replace the "~" with the user's home // directory (otherwise, the file will not successfully open) if (filename.at(0) == '~') filename = QDir::homePath() + filename.mid(1, filename.length()); QFile ccFile(filename); if (ccFile.open(QIODevice::ReadOnly)) { QString catalog_name; QTextStream stream(&ccFile); QString line; for (int times = 10; times >= 0 && !stream.atEnd(); --times) { line = stream.readLine(); int iname = line.indexOf("# Name: "); if (iname == 0) { // line contains catalog name iname = line.indexOf(":") + 2; catalog_name = line.mid(iname); return catalog_name; } } } return QString(); } bool CatalogDB::AddCatalogContents(const QString &fname) { QDir::setCurrent(QDir::homePath()); // for files with relative path QString filename = fname; // If the filename begins with "~", replace the "~" with the user's home // directory (otherwise, the file will not successfully open) if (filename.at(0) == '~') filename = QDir::homePath() + filename.mid(1, filename.length()); QFile ccFile(filename); if (ccFile.open(QIODevice::ReadOnly)) { QStringList columns; // list of data column descriptors in the header QString catalog_name; char delimiter; QTextStream stream(&ccFile); // TODO(spacetime) : Decide appropriate number of lines to be read QStringList lines; for (int times = 10; times >= 0 && !stream.atEnd(); --times) lines.append(stream.readLine()); /*WAS * = stream.readAll().split('\n', QString::SkipEmptyParts); * Memory Hog! */ if (lines.size() < 1 || !ParseCatalogInfoToDB(lines, columns, catalog_name, delimiter)) { qWarning() << "Issue in catalog file header: " << filename; ccFile.close(); return false; } ccFile.close(); // The entry in the Catalog table is now ready! /* * Now 'Columns' should be a StringList of the Header contents * Hence, we 1) Convert the Columns to a KSParser compatible format * 2) Use KSParser to read stuff and store in DB */ // Part 1) Conversion to KSParser compatible format QList> sequence = buildParserSequence(columns); // Part 2) Read file and store into DB KSParser catalog_text_parser(filename, '#', sequence, delimiter); int catid = FindCatalog(catalog_name); skydb_.open(); skydb_.transaction(); QHash row_content; while (catalog_text_parser.HasNextRow()) { row_content = catalog_text_parser.ReadNextRow(); CatalogEntryData catalog_entry; dms read_ra(row_content["RA"].toString(), false); dms read_dec(row_content["Dc"].toString(), true); //qDebug()< assume space if (delimiter == '\0') delimiter = ' '; if (!foundDataColumns) { if (showerrs) errs.append(i18n("Parsing header: no valid column descriptors found. Exiting")); return false; } if (i == lines.size()) { if (showerrs) errs.append(i18n("Parsing header: no data lines found after" " header. Exiting.")); return false; } else { // Make sure Name, Prefix, Color and Epoch were set if (catalog_name.isEmpty()) { if (showerrs) errs.append(i18n("Parsing header: no Catalog Name specified;" " setting to \"Custom\"")); catalog_name = i18n("Custom"); } if (catPrefix.isEmpty()) { if (showerrs) errs.append(i18n("Parsing header: no Catalog Prefix specified" "; setting to \"CC\"")); catPrefix = "CC"; } if (catColor.isEmpty()) { if (showerrs) errs.append(i18n("Parsing header: no Catalog Color specified" "; setting to Red")); catColor = "#CC0000"; } if (catEpoch == 0.) { if (showerrs) errs.append(i18n("Parsing header: no Catalog Epoch specified" "; assuming 2000")); catEpoch = 2000.; } #if !defined(ANDROID) // Detect a duplicate catalog name if (FindCatalog(catalog_name) != -1) { if (KMessageBox::warningYesNo(nullptr, i18n("A catalog of the same name already exists. " "Overwrite contents? If you press yes, the" " new catalog will erase the old one!"), i18n("Overwrite Existing Catalog")) == KMessageBox::No) { return false; } else { RemoveCatalog(catalog_name); } } #endif // Everything OK. Make a new Catalog entry in DB CatalogData new_catalog; new_catalog.catalog_name = catalog_name; new_catalog.prefix = catPrefix; new_catalog.color = catColor; new_catalog.epoch = catEpoch; new_catalog.fluxfreq = catFluxFreq; new_catalog.fluxunit = catFluxUnit; AddCatalog(new_catalog); return true; } } void CatalogDB::GetCatalogData(const QString &catalog_name, CatalogData &load_catalog) { skydb_.open(); QSqlTableModel catalog(nullptr, skydb_); catalog.setTable("Catalog"); catalog.setFilter("Name LIKE \'" + catalog_name + "\'"); catalog.select(); QSqlRecord record = catalog.record(0); load_catalog.prefix = record.value("Prefix").toString(); load_catalog.color = record.value("Color").toString(); load_catalog.fluxfreq = record.value("FluxFreq").toString(); load_catalog.fluxunit = record.value("FluxUnit").toString(); load_catalog.epoch = record.value("Epoch").toFloat(); catalog.clear(); skydb_.close(); } void CatalogDB::GetAllObjects(const QString &catalog, QList &sky_list, QList> &object_names, CatalogComponent *catalog_ptr, bool includeCatalogDesignation) { qDeleteAll(sky_list); sky_list.clear(); QString selected_catalog = QString::number(FindCatalog(catalog)); skydb_.open(); QSqlQuery get_query(skydb_); get_query.prepare("SELECT Epoch, Type, RA, Dec, Magnitude, Prefix, " "IDNumber, LongName, MajorAxis, MinorAxis, " "PositionAngle, Flux FROM ObjectDesignation JOIN DSO " "JOIN Catalog WHERE Catalog.id = :catID AND " "ObjectDesignation.id_Catalog = Catalog.id AND " "ObjectDesignation.UID_DSO = DSO.UID"); get_query.bindValue(":catID", selected_catalog); // qWarning() << get_query.lastQuery(); // qWarning() << get_query.lastError(); // qWarning() << FindCatalog(catalog); if (!get_query.exec()) { qWarning() << get_query.lastQuery(); qWarning() << get_query.lastError(); } while (get_query.next()) { int cat_epoch = get_query.value(0).toInt(); unsigned char iType = get_query.value(1).toInt(); dms RA(get_query.value(2).toDouble()); dms Dec(get_query.value(3).toDouble()); float mag = get_query.value(4).toFloat(); QString catPrefix = get_query.value(5).toString(); int id_number_in_catalog = get_query.value(6).toInt(); QString lname = get_query.value(7).toString(); float a = get_query.value(8).toFloat(); float b = get_query.value(9).toFloat(); float PA = get_query.value(10).toFloat(); float flux = get_query.value(11).toFloat(); QString name; if (!includeCatalogDesignation && !lname.isEmpty()) { name = lname; lname = QString(); } else name = catPrefix + ' ' + QString::number(id_number_in_catalog); SkyPoint t; t.set(RA, Dec); if (cat_epoch == 1950) { // Assume B1950 epoch t.B1950ToJ2000(); // t.ra() and t.dec() are now J2000.0 // coordinates } else if (cat_epoch == 2000) { // Do nothing { } } else { // FIXME: What should we do? // FIXME: This warning will be printed for each line in the // catalog rather than once for the entire catalog qWarning() << "Unknown epoch while dealing with custom " "catalog. Will ignore the epoch and assume" " J2000.0"; } RA = t.ra(); Dec = t.dec(); // FIXME: It is a bad idea to create objects in one class // (using new) and delete them in another! The objects created // here are usually deleted by CatalogComponent! See // CatalogComponent::loadData for more information! if (iType == 0) // Add a star { StarObject *o = new StarObject(RA, Dec, mag, lname); sky_list.append(o); } else // Add a deep-sky object { DeepSkyObject *o = new DeepSkyObject(iType, RA, Dec, mag, name, QString(), lname, catPrefix, a, b, -PA); o->setFlux(flux); o->setCustomCatalog(catalog_ptr); sky_list.append(o); // Add name to the list of object names if (!name.isEmpty()) { object_names.append(qMakePair(iType, name)); } } if (!lname.isEmpty() && lname != name) { object_names.append(qMakePair(iType, lname)); } } get_query.clear(); skydb_.close(); } QList> CatalogDB::buildParserSequence(const QStringList &Columns) { QList> sequence; QStringList::const_iterator iter = Columns.begin(); while (iter != Columns.end()) { // Available Types: ID RA Dc Tp Nm Mg Flux Mj Mn PA Ig KSParser::DataTypes current_type; if (*iter == QString("ID")) current_type = KSParser::D_QSTRING; else if (*iter == QString("RA")) current_type = KSParser::D_QSTRING; else if (*iter == QString("Dc")) current_type = KSParser::D_QSTRING; else if (*iter == QString("Tp")) current_type = KSParser::D_INT; else if (*iter == QString("Nm")) current_type = KSParser::D_QSTRING; else if (*iter == QString("Mg")) current_type = KSParser::D_FLOAT; else if (*iter == QString("Flux")) current_type = KSParser::D_FLOAT; else if (*iter == QString("Mj")) current_type = KSParser::D_FLOAT; else if (*iter == QString("Mn")) current_type = KSParser::D_FLOAT; else if (*iter == QString("PA")) current_type = KSParser::D_FLOAT; else if (*iter == QString("Ig")) current_type = KSParser::D_SKIP; sequence.append(qMakePair(*iter, current_type)); ++iter; } return sequence; } diff --git a/datahandlers/catalogdb.h b/datahandlers/catalogdb.h index b2d99bf1c..e7111d23a 100644 --- a/datahandlers/catalogdb.h +++ b/datahandlers/catalogdb.h @@ -1,248 +1,261 @@ /*************************************************************************** catalogDB.h - K Desktop Planetarium ------------------- begin : 2012/03/08 copyright : (C) 2012 by Rishab Arora email : ra.rishab@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include "ksparser.h" #include #if !defined(ANDROID) #include #endif #include #include class SkyObject; class CatalogComponent; class CatalogData; class CatalogEntryData; /* * Some notes about the database. (skycomponents.sqlite) * 1) The uid for Object Designation is the uid being used by objects in KStars * hence, the uid is a qint64 i.e. a 64 bit signed integer. Coincidentally, * this is the max limit of an int in Sqlite3. * Hence, the db is compatible with the uid, but doesn't use it as of now. */ class CatalogDB { public: /** * @brief Initializes the database and sets up pointers to Catalog DB * Performs the following actions: * 1. Checks if database file exists * 2. Checks if database can be opened * 3. If DB file is missing, creates new DB * 4. Sets up pointer to Catalog DB * * usage: Call QSqlDatabase::removeDatabase("skydb"); after the object * of this class is deallocated * @return bool **/ bool Initialize(); /** * @brief Attempt to close database and remove reference from the DB List * **/ ~CatalogDB(); /** * @brief Accessor for list of all available catalogs in db * * @return QStringList* **/ QStringList *Catalogs(); /** * @brief Rechecks the database and builds the Catalog listing. * New listing is directly updated into catalogs (accessed using Catalogs()) * * @return void **/ void RefreshCatalogList(); /** * @short Parses the catalog header and returns the catalog name * * @p filename the name of the file containing the data to be read * @return Catalog name or empty string if there is an error */ QString GetCatalogName(const QString &filename); /** * @short Add contents of custom catalog to the program database * * @p filename the name of the file containing the data to be read * @return true if catalog was successfully added */ bool AddCatalogContents(const QString &filename); /** * @brief returns the id of the row if it matches with certain fuzz. * Else return -1 if none found * * @param ra Right Ascension of new object to be added * @param dec Declination of new object to be added * @return int RowUID of the new row **/ int FindFuzzyEntry(const double ra, const double dec, const double magnitude); /** * @brief Removes the catalog from the database and refreshes the listing. * * @param catalog_name Name of the catalog * @return void **/ void RemoveCatalog(const QString &catalog_name); /** * @brief Creates objects of type SkyObject and assigns them to references * * @param catalog Name of the catalog whose objects are needed. * @param sky_list List of all skyobjects stored in database (assigns) * @param names List of named objects in database (assigns) * @param catalog_pointer pointer to the catalogcomponent objects * (needed for building skyobjects) * @param includeCatalogDesignation This is useful when using 'fake' catalogs to bundle up * catalogs. Imagine a "Misc" catalog with a bunch of miscellaneous objects. We don't want * the designations "Misc 1", "Misc 2" etc. So the only proper designations are the * long name. When this is the case, this flag is set to false, and the catalog designation * (cat_prefix + cat_id) will not be included in the object_names returned. * * @return void **/ void GetAllObjects(const QString &catalog_name, QList &sky_list, QList> &object_names, CatalogComponent *catalog_pointer, bool includeCatalogDesignation = true); /** * @brief Get information about the catalog like Prefix etc * * @param catalog_name Name of catalog whose details are required * @param catalog_data Data structure assigned with required data * @return void **/ void GetCatalogData(const QString &catalog_name, CatalogData &catalog_data); /** * @brief Used to add a cross referenced entry into the database * * @note This public method opens and closes the database. * * @param catalog_entry Data structure with entry details * @param catid Category ID in the database * @return false if adding was unsuccessful **/ bool AddEntry(const CatalogEntryData &catalog_entry, int catid); + /** + * @brief Check an entry in the database + * + * @note This public method opens and closes the database. + * + * @param entry_long_name Long name of the entry + * @param catid Category ID in the database + * @return True if successful otherwise false + **/ + bool CheckCustomEntry(const QString &entry_long_name, int catid); + /** * @brief Remove an entry from the database * * @note This public method opens and closes the database. * * @param entry_long_name Long name of the entry + * @param catid Category ID in the database + * @return True if successful otherwise false **/ - bool RemoveCustomEntry(const QString &entry_long_name); + bool RemoveCustomEntry(const QString &entry_long_name, int catid); /** * @brief Returns database ID of the required catalog. * Returns -1 if not found. * * @param name Name of the class being searched * @return int **/ int FindCatalog(const QString &catalog_name); /** * @brief Add the catalog with given details into the database * * @param catalog_data CatalogData object encompassing all catalog info * @return void **/ void AddCatalog(const CatalogData &catalog_data); private: /** * @brief Used to add a cross referenced entry into the database * * @note This private method is useful when calling the method * repeatedly on an already-opened DB. * * @param catalog_entry Data structure with entry details * @param catid Category ID in the database * @return false if adding was unsuccessful **/ bool _AddEntry(const CatalogEntryData &catalog_entry, int catid); /** * @brief Database object for the sky object. Assigned and Initialized by Initialize() **/ QSqlDatabase skydb_; /** * @brief Returns the last error the database encountered * * @return QSqlError **/ QSqlError LastError(); /** * @brief List of all the catalogs contained in the database. * This variable is accessed through Catalogs() **/ QStringList catalog_list_; /** * @short Add the catalog name and details to the db. * This does not store the contents. It only adds the catalog info * to the database. Hence, it is step 1 in AddCatalogContents * * @param lines List of lines to use for extraction of details * @param Columns Stores the read Columns in this list * @param catalog_name Name retrieved from file header * @param delimiter Delimeter retrieved from file header * @return bool **/ bool ParseCatalogInfoToDB(const QStringList &lines, QStringList &columns, QString &catalog_name, char &delimiter); /** * @brief Prepares the sequence required by KSParser according to header. * Information on the sequence is stored inside the header * * @param Columns List of the columns names as strings * @return QList of the format usable by KSParser **/ QList> buildParserSequence(const QStringList &Columns); /** * @brief Clears out the DSO table for the given catalog ID * * @param catalog_id DB generated catalog ID * @return void **/ void ClearDSOEntries(int catalog_id); /** * @brief Contains setup routines to intitialize a database for catalog storage * * @return void **/ void FirstRun(); }; diff --git a/datahandlers/catalogentrydata.h b/datahandlers/catalogentrydata.h index 41c6f48b7..9f60e361d 100644 --- a/datahandlers/catalogentrydata.h +++ b/datahandlers/catalogentrydata.h @@ -1,76 +1,73 @@ /*************************************************************************** CatalogEntryData.cpp - K Desktop Planetarium ------------------- begin : 2012/06/10 copyright : (C) 2012 by Rishab Arora email : ra.rishab@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ -#ifndef CATALOGENTRYDATA_H -#define CATALOGENTRYDATA_H +#pragma once #include /** * @brief Class to store details of a Catalog Entry * * catalog_name = Name of the Catalog (must exist prior to execution) * ID = The ID number from the catalog. eg. for M 31, ID is 31 * long_name = long name (if any) of the object * ra = Right Ascension of the object (in HH:MM:SS format) * dec = Declination of the object (in +/-DD:MM:SS format) * type = type of the object (from skyqpainter::drawDeepSkySymbol()) * 0: general star (not to be used for custom catalogs) * 1: Catalog star * 2: planet * 3: Open Cluster * 4: Globular Cluster * 5: Gaseous Nebula * 6: Planetary Nebula * 7: Supernova remnant * 8: Galaxy * 13: Asterism * 14: Galaxy cluster * 15: Dark Nebula * 16: Quasar * 17: Multiple Star * 18: Radio Source * 19: Satellite * 20: Supernova * 255: TYPE_UNKNOWN * * magnitude = Apparent Magnitude of the object * position_angle = Position Angle of the object * major_axis = Major Axis Length (arcmin) * minor_axis = Minor Axis Length (arcmin) * flux = Flux for the object **/ class CatalogEntryData { public: CatalogEntryData(); QString catalog_name; int ID; QString long_name; double ra; double dec; int type; float magnitude; int position_angle; float major_axis; float minor_axis; float flux; }; - -#endif // CATALOGENTRYDATA_H diff --git a/kstars/dialogs/finddialog.cpp b/kstars/dialogs/finddialog.cpp index 59d29170e..3229fd2e5 100644 --- a/kstars/dialogs/finddialog.cpp +++ b/kstars/dialogs/finddialog.cpp @@ -1,420 +1,420 @@ /*************************************************************************** finddialog.cpp - K Desktop Planetarium ------------------- begin : Wed Jul 4 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "finddialog.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "detaildialog.h" #include "skymap.h" #include "skyobjects/skyobject.h" #include "skyobjects/deepskyobject.h" #include "skycomponents/starcomponent.h" #include "skycomponents/syncedcatalogcomponent.h" #include "skycomponents/skymapcomposite.h" #include "tools/nameresolver.h" #include "skyobjectlistmodel.h" #include #include #include #include FindDialogUI::FindDialogUI(QWidget *parent) : QFrame(parent) { setupUi(this); FilterType->addItem(i18n("Any")); FilterType->addItem(i18n("Stars")); FilterType->addItem(i18n("Solar System")); FilterType->addItem(i18n("Open Clusters")); FilterType->addItem(i18n("Globular Clusters")); FilterType->addItem(i18n("Gaseous Nebulae")); FilterType->addItem(i18n("Planetary Nebulae")); FilterType->addItem(i18n("Galaxies")); FilterType->addItem(i18n("Comets")); FilterType->addItem(i18n("Asteroids")); FilterType->addItem(i18n("Constellations")); FilterType->addItem(i18n("Supernovae")); FilterType->addItem(i18n("Satellites")); SearchList->setMinimumWidth(256); SearchList->setMinimumHeight(320); } FindDialog::FindDialog(QWidget *parent) : QDialog(parent), timer(nullptr), m_targetObject(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new FindDialogUI(this); setWindowTitle(i18n("Find Object")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); okB = buttonBox->button(QDialogButtonBox::Ok); QPushButton *detailB = new QPushButton(i18n("Details...")); buttonBox->addButton(detailB, QDialogButtonBox::ActionRole); connect(detailB, SIGNAL(clicked()), this, SLOT(slotDetails())); ui->InternetSearchButton->setVisible(Options::resolveNamesOnline()); ui->InternetSearchButton->setEnabled(false); connect(ui->InternetSearchButton, SIGNAL(clicked()), this, SLOT(slotResolve())); ui->FilterType->setCurrentIndex(0); // show all types of objects fModel = new SkyObjectListModel(this); connect(KStars::Instance()->map(), &SkyMap::removeSkyObject, fModel, &SkyObjectListModel::removeSkyObject); sortModel = new QSortFilterProxyModel(ui->SearchList); sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); sortModel->setSourceModel(fModel); sortModel->setSortRole(Qt::DisplayRole); sortModel->setFilterRole(Qt::DisplayRole); sortModel->setDynamicSortFilter(true); sortModel->sort(0); ui->SearchList->setModel(sortModel); // Connect signals to slots connect(ui->SearchBox, SIGNAL(textChanged(QString)), SLOT(enqueueSearch())); connect(ui->SearchBox, SIGNAL(returnPressed()), SLOT(slotOk())); connect(ui->FilterType, SIGNAL(activated(int)), this, SLOT(enqueueSearch())); connect(ui->SearchList, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotOk())); // Set focus to object name edit ui->SearchBox->setFocus(); // First create and paint dialog and then load list QTimer::singleShot(0, this, SLOT(init())); listFiltered = false; } void FindDialog::init() { ui->SearchBox->clear(); filterByType(); sortModel->sort(0); initSelection(); m_targetObject = nullptr; } void FindDialog::initSelection() { if (sortModel->rowCount() <= 0) { okB->setEnabled(false); return; } if (ui->SearchBox->text().isEmpty()) { //Pre-select the first item QModelIndex selectItem = sortModel->index(0, sortModel->filterKeyColumn(), QModelIndex()); switch (ui->FilterType->currentIndex()) { case 0: //All objects, choose Andromeda galaxy { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Andromeda Galaxy"))); selectItem = sortModel->mapFromSource(qmi); break; } case 1: //Stars, choose Aldebaran { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aldebaran"))); selectItem = sortModel->mapFromSource(qmi); break; } case 2: //Solar system or Asteroids, choose Aaltje case 9: { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aaltje"))); selectItem = sortModel->mapFromSource(qmi); break; } case 8: //Comets, choose 'Aarseth-Brewington (1989 W1)' { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aarseth-Brewington (1989 W1)"))); selectItem = sortModel->mapFromSource(qmi); break; } } if (selectItem.isValid()) { ui->SearchList->selectionModel()->select(selectItem, QItemSelectionModel::ClearAndSelect); ui->SearchList->scrollTo(selectItem); ui->SearchList->setCurrentIndex(selectItem); okB->setEnabled(true); } } listFiltered = true; } void FindDialog::filterByType() { KStarsData *data = KStarsData::Instance(); switch (ui->FilterType->currentIndex()) { case 0: // All object types { QVector> allObjects; foreach (int type, data->skyComposite()->objectLists().keys()) { allObjects.append(data->skyComposite()->objectLists(SkyObject::TYPE(type))); } fModel->setSkyObjectsList(allObjects); break; } case 1: //Stars { QVector> starObjects; starObjects.append(data->skyComposite()->objectLists(SkyObject::STAR)); starObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR)); fModel->setSkyObjectsList(starObjects); break; } case 2: //Solar system { QVector> ssObjects; ssObjects.append(data->skyComposite()->objectLists(SkyObject::PLANET)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::COMET)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::ASTEROID)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::MOON)); fModel->setSkyObjectsList(ssObjects); break; } case 3: //Open Clusters fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::OPEN_CLUSTER)); break; case 4: //Globular Clusters fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GLOBULAR_CLUSTER)); break; case 5: //Gaseous nebulae fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GASEOUS_NEBULA)); break; case 6: //Planetary nebula fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::PLANETARY_NEBULA)); break; case 7: //Galaxies fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GALAXY)); break; case 8: //Comets fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::COMET)); break; case 9: //Asteroids fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::ASTEROID)); break; case 10: //Constellations fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::CONSTELLATION)); break; case 11: //Supernovae fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::SUPERNOVA)); break; case 12: //Satellites fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::SATELLITE)); break; } } void FindDialog::filterList() { QString SearchText = processSearchText(); sortModel->setFilterFixedString(SearchText); ui->InternetSearchButton->setText(i18n("or search the Internet for %1", SearchText)); filterByType(); initSelection(); //Select the first item in the list that begins with the filter string if (!SearchText.isEmpty()) { QStringList mItems = fModel->filter(QRegExp('^' + SearchText, Qt::CaseInsensitive)); mItems.sort(); if (mItems.size()) { QModelIndex qmi = fModel->index(fModel->indexOf(mItems[0])); QModelIndex selectItem = sortModel->mapFromSource(qmi); if (selectItem.isValid()) { ui->SearchList->selectionModel()->select(selectItem, QItemSelectionModel::ClearAndSelect); ui->SearchList->scrollTo(selectItem); ui->SearchList->setCurrentIndex(selectItem); okB->setEnabled(true); } } ui->InternetSearchButton->setEnabled(!mItems.contains( SearchText)); // Disable searching the internet when an exact match for SearchText exists in KStars } else ui->InternetSearchButton->setEnabled(false); listFiltered = true; } SkyObject *FindDialog::selectedObject() const { QModelIndex i = ui->SearchList->currentIndex(); QVariant sObj = sortModel->data(sortModel->index(i.row(), 0), SkyObjectListModel::SkyObjectRole); return reinterpret_cast(sObj.value()); } void FindDialog::enqueueSearch() { listFiltered = false; if (timer) { timer->stop(); } else { timer = new QTimer(this); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), this, SLOT(filterList())); } timer->start(500); } // Process the search box text to replace equivalent names like "m93" with "m 93" QString FindDialog::processSearchText() { QRegExp re; QString searchtext = ui->SearchBox->text(); re.setCaseSensitivity(Qt::CaseInsensitive); // If it is an NGC/IC/M catalog number, as in "M 76" or "NGC 5139", check for absence of the space re.setPattern("^(m|ngc|ic)\\s*\\d*$"); if (ui->SearchBox->text().contains(re)) { re.setPattern("\\s*(\\d+)"); searchtext.replace(re, " \\1"); re.setPattern("\\s*$"); searchtext.remove(re); re.setPattern("^\\s*"); searchtext.remove(re); } // TODO after KDE 4.1 release: // If it is a IAU standard three letter abbreviation for a constellation, then go to that constellation // Check for genetive names of stars. Example: alp CMa must go to alpha Canis Majoris return searchtext; } void FindDialog::slotOk() { //If no valid object selected, show a sorry-box. Otherwise, emit accept() SkyObject *selObj; if (!listFiltered) { filterList(); } selObj = selectedObject(); finishProcessing(selObj, Options::resolveNamesOnline()); } void FindDialog::slotResolve() { finishProcessing(nullptr, true); } void FindDialog::finishProcessing(SkyObject *selObj, bool resolve) { if (!selObj && resolve) { - CatalogEntryData cedata; - cedata = NameResolver::resolveName(processSearchText()); + CatalogEntryData cedata = NameResolver::resolveName(processSearchText()); DeepSkyObject *dso = nullptr; + if (!std::isnan(cedata.ra) && !std::isnan(cedata.dec)) { dso = KStarsData::Instance()->skyComposite()->internetResolvedComponent()->addObject(cedata); if (dso) qDebug() << dso->ra0().toHMSString() << ";" << dso->dec0().toDMSString(); selObj = dso; } } m_targetObject = selObj; if (selObj == nullptr) { QString message = i18n("No object named %1 found.", ui->SearchBox->text()); KMessageBox::sorry(nullptr, message, i18n("Bad object name")); } else { selObj->updateCoordsNow(KStarsData::Instance()->updateNum()); accept(); } } void FindDialog::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Escape: reject(); break; case Qt::Key_Up: { int currentRow = ui->SearchList->currentIndex().row(); if (currentRow > 0) { QModelIndex selectItem = sortModel->index(currentRow - 1, sortModel->filterKeyColumn(), QModelIndex()); ui->SearchList->selectionModel()->setCurrentIndex(selectItem, QItemSelectionModel::SelectCurrent); } break; } case Qt::Key_Down: { int currentRow = ui->SearchList->currentIndex().row(); if (currentRow < sortModel->rowCount() - 1) { QModelIndex selectItem = sortModel->index(currentRow + 1, sortModel->filterKeyColumn(), QModelIndex()); ui->SearchList->selectionModel()->setCurrentIndex(selectItem, QItemSelectionModel::SelectCurrent); } break; } } } void FindDialog::slotDetails() { if (selectedObject()) { QPointer dd = new DetailDialog(selectedObject(), KStarsData::Instance()->ut(), KStarsData::Instance()->geo(), KStars::Instance()); dd->exec(); delete dd; } } diff --git a/kstars/skycomponents/syncedcatalogcomponent.cpp b/kstars/skycomponents/syncedcatalogcomponent.cpp index e69b729d0..13821596e 100644 --- a/kstars/skycomponents/syncedcatalogcomponent.cpp +++ b/kstars/skycomponents/syncedcatalogcomponent.cpp @@ -1,167 +1,173 @@ /*************************************************************************** syncedcatalogcomponent.cpp - K Desktop Planetarium ------------------- begin : Tue 16 Aug 2016 04:19:00 CDT copyright : (c) 2016 by Akarsh Simha email : akarsh.simha@kdemail.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /* Project Includes */ #include "syncedcatalogcomponent.h" #include "catalogdata.h" #include "deepskyobject.h" #include "kstarsdata.h" #include "Options.h" #include "tools/nameresolver.h" /* KDE Includes */ /* Qt Includes */ SyncedCatalogComponent::SyncedCatalogComponent(SkyComposite *parent, const QString &catname, bool showerrs, int index) : CatalogComponent(parent, catname, showerrs, index, false) { // First check if the catalog exists CatalogDB *db = KStarsData::Instance()->catalogdb(); Q_ASSERT(db); m_catId = db->FindCatalog(catname); if (m_catId >= 0) loadData(); else { // Create the catalog qWarning() << "Creating new catalog " << catname; CatalogData catData; catData.color = "#ff0000"; // FIXME: Allow users to change the color of these catalogs (eg: for resolved objects) catData.epoch = 2000.0; catData.fluxfreq = "400 nm"; catData.fluxunit = "mag"; catData.author = "KStars"; catData.license = "Unknown"; catData.catalog_name = catname; catData.prefix = catname; db->AddCatalog(catData); m_catId = db->FindCatalog(catname); loadData(); Options::setShowCatalogNames(Options::showCatalogNames() << catname); } Q_ASSERT(m_catId >= 0); m_catColor = "#ff0000"; // FIXME: HARDCODED! m_catCount = m_ObjectList.count(); } /* SyncedCatalogComponent::~SyncedCatalogComponent() { } */ /* void SyncedCatalogComponent::draw( SkyPainter *skyp ) { qDebug() << "in SyncedCatalogComponent::draw()!"; CatalogComponent::draw( skyp ); } */ -DeepSkyObject *SyncedCatalogComponent::addObject(CatalogEntryData catalogEntry) +DeepSkyObject *SyncedCatalogComponent::addObject(CatalogEntryData &catalogEntry) { if (std::isnan(catalogEntry.major_axis)) catalogEntry.major_axis = 0.0; if (std::isnan(catalogEntry.minor_axis)) catalogEntry.minor_axis = 0.0; CatalogEntryData dbEntry = catalogEntry; if (dbEntry.catalog_name != m_catName) { qWarning() << "Trying to add object " << catalogEntry.catalog_name << catalogEntry.ID << " to catalog " << m_catName << " will over-write catalog name with " << m_catName << " in the database and assign an arbitrary ID"; dbEntry.catalog_name = m_catName; } dbEntry.ID = m_catCount; CatalogDB *db = KStarsData::Instance()->catalogdb(); if (!(db->AddEntry(dbEntry, m_catId))) return nullptr; m_catCount++; qDebug() << "Added object " << catalogEntry.long_name << " into database!"; DeepSkyObject *newObj = new DeepSkyObject( catalogEntry, this); // FIXME: What about stars? Are they treated as DeepSkyObjects, type CATALOG_STAR? -- asimha Q_ASSERT(newObj); qDebug() << "Created new DSO for " << catalogEntry.long_name << " - type: " << newObj->type(); if (newObj->hasLongName()) { // newObj->setName( newObj->longname() ); objectNames()[newObj->type()].append(newObj->longname()); objectLists()[newObj->type()].append(QPair(newObj->longname(), newObj)); } else { qWarning() << "Created object with name " << newObj->name() << " which is probably fake!"; objectNames()[newObj->type()].append(newObj->name()); objectLists()[newObj->type()].append(QPair(newObj->name(), newObj)); } m_ObjectList.append(newObj); qDebug() << "Added new SkyObject " << newObj->name() << " to synced catalog " << m_catName << " which now contains " << m_ObjectList.count() << " objects."; return newObj; } bool SyncedCatalogComponent::hasObject(SkyObject &object) { + QString name; + if (object.hasLongName()) { - if (objectNames()[object.type()].contains(object.longname())) - return true; - } else - if (objectNames()[object.type()].contains(object.name())) + name = object.longname(); + } else { + name = object.name(); + } + + CatalogDB *db = KStarsData::Instance()->catalogdb(); + + if (db->CheckCustomEntry(name, m_catId)) { return true; } return false; } bool SyncedCatalogComponent::removeObject(SkyObject &object) { QString name; if (object.hasLongName()) { name = object.longname(); } else { name = object.name(); } if (objectNames()[object.type()].contains(name)) { objectNames()[object.type()].removeAll(name); objectLists()[object.type()].removeAll(QPair(name, &object)); } else { qWarning() << "Can't find SkyObject " << name << " in the synced catalog " << m_catName; return false; } m_ObjectList.removeAll(&object); qDebug() << "Remove SkyObject " << name << " from synced catalog " << m_catName; // Remove the catalog entry CatalogEntryData cedata = NameResolver::resolveName(name); if (!std::isnan(cedata.ra) && !std::isnan(cedata.dec)) { CatalogDB *db = KStarsData::Instance()->catalogdb(); - if (!db->RemoveCustomEntry(name)) + if (!db->RemoveCustomEntry(name, m_catId)) { qWarning() << "Can't find SkyObject " << name << " in the CatalogDB"; return false; } } return true; } diff --git a/kstars/skycomponents/syncedcatalogcomponent.h b/kstars/skycomponents/syncedcatalogcomponent.h index 755d56343..f22522c0e 100644 --- a/kstars/skycomponents/syncedcatalogcomponent.h +++ b/kstars/skycomponents/syncedcatalogcomponent.h @@ -1,86 +1,86 @@ /*************************************************************************** syncedcatalogcomponent.h - K Desktop Planetarium ------------------- begin : Tue 16 Aug 2016 04:13:56 CDT copyright : (c) 2016 by Akarsh Simha email : akarsh.simha@kdemail.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include "catalogcomponent.h" #include "catalogentrydata.h" class DeepSkyObject; class SkyComposite; /** * @class SyncedCatalogComponent * * @short A subclass of CatalogComponent that supports run-time * insertion of data, which it keeps synced with the database. * * @author Akarsh Simha */ class SyncedCatalogComponent : public CatalogComponent { public: /** * @short Constructor * * @param parent Parent item * @param catname Catalog name * @param showerrs Show errors * @param index Index */ SyncedCatalogComponent(SkyComposite *parent, const QString &catname, bool showerrs, int index); // virtual void draw( SkyPainter *skyp ); /** * @short Create and insert a SkyObject from the given CatalogEntryData * * @return 0 upon failure, a valid SkyObject pointer otherwise. */ - DeepSkyObject *addObject(CatalogEntryData catalogEntry); + DeepSkyObject *addObject(CatalogEntryData &catalogEntry); /** * @short Whether an object is in the database * * @return True if the object is in this database otherwise false. */ bool hasObject(SkyObject &object); /** * @short Remove an object from the database * * @return True if the object is removed otherwise false. */ bool removeObject(SkyObject &object); /** * @short Edit an existing skyobject * * @note Will implement when required. * @note Question: How do we match the object? Probably by internal catalog id. */ // bool editObject( SkyObject *object ); void loadData() override { _loadData(false); } // virtual bool selected(); private: int m_catId { 0 }; int m_catCount { 0 }; };