/* * Stellarium * Copyright (C) 2002 Fabien Chereau * * The big star catalogue extension to Stellarium: * Author and Copyright: Johannes Gajdosik, 2006 * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "StelProjector.hpp" #include "StarMgr.hpp" #include "StelObject.hpp" #include "StelTexture.hpp" #include "StelNavigator.hpp" #include "StelUtils.hpp" #include "StelToneReproducer.hpp" #include "StelTranslator.hpp" #include "StelGeodesicGrid.hpp" #include "StelTranslator.hpp" #include "StelApp.hpp" #include "StelTextureMgr.hpp" #include "StelObjectMgr.hpp" #include "StelLocaleMgr.hpp" #include "StelSkyCultureMgr.hpp" #include "StelFileMgr.hpp" #include "StelModuleMgr.hpp" #include "StelCore.hpp" #include "StelIniParser.hpp" #include "StelPainter.hpp" #include "StelJsonParser.hpp" #include "ZoneArray.hpp" #include #include using namespace BigStarCatalogExtension; static QStringList spectral_array; static QStringList component_array; // This number must be incremented each time the content or file format of the stars catalogs change // It can also be incremented when the defaultStarsConfig.json file change. // It should always matchs the version field of the defaultStarsConfig.json file static const int StarCatalogFormatVersion = 2; // Initialise statics bool StarMgr::flagSciNames = true; QHash StarMgr::commonNamesMap; QHash StarMgr::commonNamesMapI18n; QMap StarMgr::commonNamesIndexI18n; QHash StarMgr::sciNamesMapI18n; QMap StarMgr::sciNamesIndexI18n; QStringList initStringListFromFile(const QString& file_name) { QStringList list; QFile f(file_name); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { while (!f.atEnd()) { QString s = QString::fromUtf8(f.readLine()); s.chop(1); list << s; } f.close(); } return list; } QString StarMgr::convertToSpectralType(int index) { if (index < 0 || index >= spectral_array.size()) { qDebug() << "convertToSpectralType: bad index: " << index << ", max: " << spectral_array.size(); return ""; } return spectral_array.at(index); } QString StarMgr::convertToComponentIds(int index) { if (index < 0 || index >= component_array.size()) { qDebug() << "convertToComponentIds: bad index: " << index << ", max: " << component_array.size(); return ""; } return component_array.at(index); } void StarMgr::initTriangle(int lev,int index, const Vec3f &c0, const Vec3f &c1, const Vec3f &c2) { ZoneArrayMap::const_iterator it(zoneArrays.find(lev)); if (it!=zoneArrays.constEnd()) it.value()->initTriangle(index,c0,c1,c2); } StarMgr::StarMgr(void) : hipIndex(new HipIndexStruct[NR_OF_HIP+1]) { setObjectName("StarMgr"); if (hipIndex == 0) { qFatal("ERROR: StarMgr::StarMgr: no memory"); } maxGeodesicGridLevel = -1; lastMaxSearchLevel = -1; starFont.setPixelSize(13.); objectMgr = GETSTELMODULE(StelObjectMgr); Q_ASSERT(objectMgr); } /************************************************************************* Reimplementation of the getCallOrder method *************************************************************************/ double StarMgr::getCallOrder(StelModuleActionName actionName) const { if (actionName==StelModule::ActionDraw) return StelApp::getInstance().getModuleMgr().getModule("ConstellationMgr")->getCallOrder(actionName)+10; return 0; } StarMgr::~StarMgr(void) { ZoneArrayMap::iterator it(zoneArrays.end()); while (it!=zoneArrays.begin()) { --it; delete it.value(); it.value() = NULL; } zoneArrays.clear(); if (hipIndex) delete[] hipIndex; } QString StarMgr::getCommonName(int hip) { QHash::const_iterator it(commonNamesMapI18n.find(hip)); if (it!=commonNamesMapI18n.end()) return it.value(); return QString(); } QString StarMgr::getSciName(int hip) { QHash::const_iterator it(sciNamesMapI18n.find(hip)); if (it!=sciNamesMapI18n.end()) return it.value(); return QString(); } void StarMgr::copyDefaultConfigFile() { try { StelFileMgr::makeSureDirExistsAndIsWritable(StelFileMgr::getUserDir()+"/stars/default"); starConfigFileFullPath = StelFileMgr::getUserDir()+"/stars/default/starsConfig.json"; qDebug() << "Creates file " << starConfigFileFullPath; QFile::copy(StelFileMgr::findFile("stars/default/defaultStarsConfig.json"), starConfigFileFullPath); } catch (std::runtime_error& e) { qWarning() << e.what(); qFatal("Could not create configuration file stars/default/starsConfig.json"); } } void StarMgr::init() { QSettings* conf = StelApp::getInstance().getSettings(); Q_ASSERT(conf); try { starConfigFileFullPath = StelFileMgr::findFile("stars/default/starsConfig.json", StelFileMgr::Flags(StelFileMgr::Writable|StelFileMgr::File)); } catch (std::runtime_error& e) { qWarning() << "Could not find the starsConfig.json file: will copy the default one."; copyDefaultConfigFile(); } QFile fic(starConfigFileFullPath); fic.open(QIODevice::ReadOnly); starSettings = StelJsonParser::parse(&fic).toMap(); fic.close(); // Increment the 1 each time any star catalog file change if (starSettings.value("version").toInt()!=StarCatalogFormatVersion) { qWarning() << "Found an old starsConfig.json file, upgrade.."; fic.remove(); copyDefaultConfigFile(); QFile fic2(starConfigFileFullPath); fic2.open(QIODevice::ReadOnly); starSettings = StelJsonParser::parse(&fic2).toMap(); fic2.close(); } loadData(starSettings); starFont.setPixelSize(13); setFlagStars(conf->value("astro/flag_stars", true).toBool()); setFlagLabels(conf->value("astro/flag_star_name",true).toBool()); setLabelsAmount(conf->value("stars/labels_amount",3.f).toFloat()); objectMgr->registerStelObjectMgr(this); texPointer = StelApp::getInstance().getTextureManager().createTexture("textures/pointeur2.png"); // Load pointer texture StelApp::getInstance().getCore()->getGeodesicGrid(maxGeodesicGridLevel)->visitTriangles(maxGeodesicGridLevel,initTriangleFunc,this); for (ZoneArrayMap::const_iterator it(zoneArrays.begin()); it!=zoneArrays.end();it++) { it.value()->scaleAxis(); } } void StarMgr::drawPointer(StelPainter& sPainter, const StelNavigator * nav) { const QList newSelected = objectMgr->getSelectedObject("Star"); if (!newSelected.empty()) { const StelObjectP obj = newSelected[0]; Vec3d pos=obj->getJ2000EquatorialPos(nav); Vec3d screenpos; // Compute 2D pos and return if outside screen if (!sPainter.getProjector()->project(pos, screenpos)) return; const Vec3f& c(obj->getInfoColor()); sPainter.setColor(c[0],c[1],c[2]); texPointer->bind(); sPainter.enableTexture2d(true); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal transparency mode sPainter.drawSprite2dMode(screenpos[0], screenpos[1], 13.f, StelApp::getInstance().getTotalRunTime()*40.); } } void StarMgr::setStelStyle(const QString& section) { // Load colors from config file QSettings* conf = StelApp::getInstance().getSettings(); QString defaultColor = conf->value(section+"/default_color").toString(); setLabelColor(StelUtils::strToVec3f(conf->value(section+"/star_label_color", defaultColor).toString())); } bool StarMgr::checkAndLoadCatalog(QVariantMap catDesc) { const bool checked = catDesc.value("checked").toBool(); QString catalogFileName = catDesc.value("fileName").toString(); // See if it is an absolute path, else prepend default path if (!(StelFileMgr::isAbsolute(catalogFileName))) catalogFileName = "stars/default/"+catalogFileName; QString catalogFilePath; try { catalogFilePath = StelFileMgr::findFile(catalogFileName); } catch (std::runtime_error e) { // The file is supposed to be checked, but we can't find it if (checked) { qWarning() << QString("Warning: could not find star catalog %1").arg(catalogFileName); setCheckFlag(catDesc.value("id").toString(), false); } return false; } // Possibly fixes crash on Vista if (!StelFileMgr::isReadable(catalogFilePath)) { qWarning() << QString("Warning: User does not have permissions to read catalog %1").arg(catalogFilePath); return false; } if (!checked) { // The file is not checked but we found it, maybe from a previous download/version qWarning() << "Found file " << catalogFilePath << ", checking md5sum.."; QFile fic(catalogFilePath); fic.open(QIODevice::ReadOnly | QIODevice::Unbuffered); // Compute the MD5 sum QCryptographicHash md5Hash(QCryptographicHash::Md5); const qint64 cat_sz = fic.size(); qint64 maxStarBufMd5 = qMin(cat_sz, 9223372036854775807LL); uchar *cat = maxStarBufMd5 ? fic.map(0, maxStarBufMd5) : NULL; if (!cat) { // The OS was not able to map the file, revert to slower not mmap based method static const qint64 maxStarBufMd5 = 1024*1024*8; char* mmd5buf = (char*)malloc(maxStarBufMd5); while (!fic.atEnd()) { qint64 sz = fic.read(mmd5buf, maxStarBufMd5); md5Hash.addData(mmd5buf, sz); } free(mmd5buf); } else { md5Hash.addData((const char*)cat, cat_sz); fic.unmap(cat); } fic.close(); if (md5Hash.result().toHex()!=catDesc.value("checksum").toByteArray()) { qWarning() << "Error: File " << catalogFileName << " is corrupt, MD5 mismatch! Found " << md5Hash.result().toHex() << " expected " << catDesc.value("checksum").toByteArray(); fic.remove(); return false; } qWarning() << "MD5 sum correct!"; setCheckFlag(catDesc.value("id").toString(), true); } ZoneArray* const z = ZoneArray::create(catalogFilePath, true); if (z) { if (maxGeodesicGridLevel < z->level) { maxGeodesicGridLevel = z->level; } ZoneArray *&pos(zoneArrays[z->level]); if (pos) { qWarning() << catalogFileName << ", " << z->level << ": duplicate level"; delete z; } else { pos = z; } } return true; } void StarMgr::setCheckFlag(const QString& catId, bool b) { // Update the starConfigFileFullPath file to take into account that we now have a new catalog int idx=0; foreach (const QVariant& catV, catalogsDescription) { ++idx; QVariantMap m = catV.toMap(); if (m.value("id").toString()!=catId) continue; const bool checked = m.value("checked").toBool(); if (checked==b) return; m["checked"]=b; catalogsDescription[idx-1]=m; starSettings["catalogs"]=catalogsDescription; QFile tmp(starConfigFileFullPath); tmp.open(QIODevice::WriteOnly); StelJsonParser::write(starSettings, &tmp); tmp.close(); } } void StarMgr::loadData(QVariantMap starsConfig) { // Please do not init twice: Q_ASSERT(maxGeodesicGridLevel < 0); qDebug() << "Loading star data ..."; catalogsDescription = starsConfig.value("catalogs").toList(); foreach (const QVariant& catV, catalogsDescription) { QVariantMap m = catV.toMap(); checkAndLoadCatalog(m); } for (int i=0; i<=NR_OF_HIP; i++) { hipIndex[i].a = 0; hipIndex[i].z = 0; hipIndex[i].s = 0; } for (ZoneArrayMap::const_iterator it(zoneArrays.constBegin()); it != zoneArrays.constEnd();++it) { it.value()->updateHipIndex(hipIndex); } const QString cat_hip_sp_file_name = starsConfig.value("hipSpectralFile").toString(); if (cat_hip_sp_file_name.isEmpty()) { qWarning() << "ERROR: stars:cat_hip_sp_file_name not found"; } else { try { spectral_array = initStringListFromFile(StelFileMgr::findFile("stars/default/" + cat_hip_sp_file_name)); } catch (std::runtime_error& e) { qWarning() << "ERROR while loading data from " << ("stars/default/" + cat_hip_sp_file_name) << ": " << e.what(); } } const QString cat_hip_cids_file_name = starsConfig.value("hipComponentsIdsFile").toString(); if (cat_hip_cids_file_name.isEmpty()) { qWarning() << "ERROR: stars:cat_hip_cids_file_name not found"; } else { try { component_array = initStringListFromFile(StelFileMgr::findFile("stars/default/" + cat_hip_cids_file_name)); } catch (std::runtime_error& e) { qWarning() << "ERROR while loading data from " << ("stars/default/" + cat_hip_cids_file_name) << ": " << e.what(); } } lastMaxSearchLevel = maxGeodesicGridLevel; qDebug() << "Finished loading star catalogue data, max_geodesic_level: " << maxGeodesicGridLevel; } // Load common names from file int StarMgr::loadCommonNames(const QString& commonNameFile) { commonNamesMap.clear(); commonNamesMapI18n.clear(); commonNamesIndexI18n.clear(); qDebug() << "Loading star names from" << commonNameFile; QFile cnFile(commonNameFile); if (!cnFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "WARNING - could not open" << commonNameFile; return 0; } int readOk=0; int totalRecords=0; int lineNumber=0; QString record; QRegExp commentRx("^(\\s*#.*|\\s*)$"); // record structure is delimited with a | character. We will // use a QRegExp to extract the fields. with whitespace padding permitted // (i.e. it will be stripped automatically) Example record strings: // " 10819|c_And" // "113726|1_And" QRegExp recordRx("^\\s*(\\d+)\\s*\\|(.*)\\n"); while(!cnFile.atEnd()) { record = QString::fromUtf8(cnFile.readLine()); lineNumber++; if (commentRx.exactMatch(record)) continue; totalRecords++; if (!recordRx.exactMatch(record)) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << commonNameFile << " - record does not match record pattern"; continue; } else { // The record is the right format. Extract the fields bool ok; unsigned int hip = recordRx.capturedTexts().at(1).toUInt(&ok); if (!ok) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << commonNameFile << " - failed to convert " << recordRx.capturedTexts().at(1) << "to a number"; continue; } QString englishCommonName = recordRx.capturedTexts().at(2).trimmed(); if (englishCommonName.isEmpty()) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << commonNameFile << " - empty name field"; continue; } englishCommonName.replace('_', ' '); const QString commonNameI18n = q_(englishCommonName); QString commonNameI18n_cap = commonNameI18n.toUpper(); commonNamesMap[hip] = englishCommonName; commonNamesMapI18n[hip] = commonNameI18n; commonNamesIndexI18n[commonNameI18n_cap] = hip; readOk++; } } cnFile.close(); qDebug() << "Loaded" << readOk << "/" << totalRecords << "common star names"; return 1; } // Load scientific names from file void StarMgr::loadSciNames(const QString& sciNameFile) { sciNamesMapI18n.clear(); sciNamesIndexI18n.clear(); qDebug() << "Loading star names from" << sciNameFile; QFile snFile(sciNameFile); if (!snFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "WARNING - could not open" << sciNameFile; return; } const QStringList& allRecords = QString::fromUtf8(snFile.readAll()).split('\n'); snFile.close(); int readOk=0; int totalRecords=0; int lineNumber=0; // record structure is delimited with a | character. Example record strings: // " 10819|c_And" // "113726|1_And" foreach(const QString& record, allRecords) { ++lineNumber; if (record.isEmpty()) continue; ++totalRecords; const QStringList& fields = record.split('|'); if (fields.size()!=2) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << sciNameFile << " - record does not match record pattern"; continue; } else { // The record is the right format. Extract the fields bool ok; unsigned int hip = fields.at(0).toUInt(&ok); if (!ok) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << sciNameFile << " - failed to convert " << fields.at(0) << "to a number"; continue; } // Don't set the sci name if it's already set if (sciNamesMapI18n.find(hip)!=sciNamesMapI18n.end()) continue; QString sci_name_i18n = fields.at(1).trimmed(); if (sci_name_i18n.isEmpty()) { qWarning() << "WARNING - parse error at line" << lineNumber << "in" << sciNameFile << " - empty name field"; continue; } sci_name_i18n.replace('_',' '); sciNamesMapI18n[hip] = sci_name_i18n; sciNamesIndexI18n[sci_name_i18n.toUpper()] = hip; ++readOk; } } qDebug() << "Loaded" << readOk << "/" << totalRecords << "scientific star names"; } int StarMgr::getMaxSearchLevel() const { int rval = -1; for (ZoneArrayMap::const_iterator it(zoneArrays.constBegin());it!=zoneArrays.constEnd();++it) { const float mag_min = 0.001f*it.value()->mag_min; float rcmag[2]; if (StelApp::getInstance().getCore()->getSkyDrawer()->computeRCMag(mag_min,rcmag)==false) break; rval = it.key(); } return rval; } // Draw all the stars void StarMgr::draw(StelCore* core) { StelNavigator* nav = core->getNavigator(); const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000); StelSkyDrawer* skyDrawer = core->getSkyDrawer(); // If stars are turned off don't waste time below // projecting all stars just to draw disembodied labels if (!starsFader.getInterstate()) return; int maxSearchLevel = getMaxSearchLevel(); const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(maxSearchLevel)->search(prj->getViewportConvexPolygon()->getBoundingSphericalCaps(),maxSearchLevel); // Set temporary static variable for optimization const float names_brightness = labelsFader.getInterstate() * starsFader.getInterstate(); // Prepare openGL for drawing many stars StelPainter sPainter(prj); sPainter.setFont(starFont); skyDrawer->preDrawPointSource(&sPainter); // draw all the stars of all the selected zones float rcmag_table[2*256]; for (ZoneArrayMap::const_iterator it(zoneArrays.constBegin()); it!=zoneArrays.constEnd();++it) { const float mag_min = 0.001f*it.value()->mag_min; const float k = (0.001f*it.value()->mag_range)/it.value()->mag_steps; for (int i=it.value()->mag_steps-1;i>=0;--i) { const float mag = mag_min+k*i; if (skyDrawer->computeRCMag(mag,rcmag_table + 2*i)==false) { if (i==0) goto exit_loop; } if (skyDrawer->getFlagPointStar()) { rcmag_table[2*i+1] *= starsFader.getInterstate(); } else { rcmag_table[2*i] *= starsFader.getInterstate(); } } lastMaxSearchLevel = it.key(); unsigned int maxMagStarName = 0; if (labelsFader.getInterstate()>0.f) { // Adapt magnitude limit of the stars labels according to FOV and labelsAmount float maxMag = (skyDrawer->getLimitMagnitude()-6.5)*0.7+(labelsAmount*1.2f)-2.f; int x = (int)((maxMag-mag_min)/k); if (x > 0) maxMagStarName = x; } int zone; for (GeodesicSearchInsideIterator it1(*geodesic_search_result,it.key());(zone = it1.next()) >= 0;) it.value()->draw(&sPainter, zone, true, rcmag_table, core, maxMagStarName, names_brightness); for (GeodesicSearchBorderIterator it1(*geodesic_search_result,it.key());(zone = it1.next()) >= 0;) it.value()->draw(&sPainter, zone, false, rcmag_table, core, maxMagStarName,names_brightness); } exit_loop: // Finish drawing many stars skyDrawer->postDrawPointSource(&sPainter); if (objectMgr->getFlagSelectedObjectPointer()) drawPointer(sPainter, nav); } // Return a stl vector containing the stars located // inside the limFov circle around position v QList StarMgr::searchAround(const Vec3d& vv, double limFov, const StelCore* core) const { QList result; if (!getFlagStars()) return result; Vec3d v(vv); v.normalize(); // find any vectors h0 and h1 (length 1), so that h0*v=h1*v=h0*h1=0 int i; { const double a0 = fabs(v[0]); const double a1 = fabs(v[1]); const double a2 = fabs(v[2]); if (a0 <= a1) { if (a0 <= a2) i = 0; else i = 2; } else { if (a1 <= a2) i = 1; else i = 2; } } Vec3d h0(0.0,0.0,0.0); h0[i] = 1.0; Vec3d h1 = h0 ^ v; h1.normalize(); h0 = h1 ^ v; h0.normalize(); // Now we have h0*v=h1*v=h0*h1=0. // Construct a region with 4 corners e0,e1,e2,e3 inside which all desired stars must be: double f = 1.4142136 * tan(limFov * M_PI/180.0); h0 *= f; h1 *= f; Vec3d e0 = v + h0; Vec3d e1 = v + h1; Vec3d e2 = v - h0; Vec3d e3 = v - h1; f = 1.0/e0.length(); e0 *= f; e1 *= f; e2 *= f; e3 *= f; // Search the triangles SphericalConvexPolygon c(e3, e2, e2, e0); const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(lastMaxSearchLevel)->search(c.getBoundingSphericalCaps(),lastMaxSearchLevel); // Iterate over the stars inside the triangles f = cos(limFov * M_PI/180.); for (ZoneArrayMap::const_iterator it(zoneArrays.constBegin());it!=zoneArrays.constEnd();it++) { //qDebug() << "search inside(" << it->first << "):"; int zone; for (GeodesicSearchInsideIterator it1(*geodesic_search_result,it.key());(zone = it1.next()) >= 0;) { it.value()->searchAround(core->getNavigator(), zone,v,f,result); //qDebug() << " " << zone; } //qDebug() << endl << "search border(" << it->first << "):"; for (GeodesicSearchBorderIterator it1(*geodesic_search_result,it.key()); (zone = it1.next()) >= 0;) { it.value()->searchAround(core->getNavigator(), zone,v,f,result); //qDebug() << " " << zone; } } return result; } //! Update i18 names from english names according to passed translator. //! The translation is done using gettext with translated strings defined in translations.h void StarMgr::updateI18n() { StelTranslator trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator(); commonNamesMapI18n.clear(); commonNamesIndexI18n.clear(); for (QHash::ConstIterator it(commonNamesMap.constBegin());it!=commonNamesMap.constEnd();it++) { const int i = it.key(); const QString t(trans.qtranslate(it.value())); commonNamesMapI18n[i] = t; commonNamesIndexI18n[t.toUpper()] = i; } } // Search the star by HP number StelObjectP StarMgr::searchHP(int hp) const { if (0 < hp && hp <= NR_OF_HIP) { const Star1 *const s = hipIndex[hp].s; if (s) { const SpecialZoneArray *const a = hipIndex[hp].a; const SpecialZoneData *const z = hipIndex[hp].z; return s->createStelObject(a,z); } } return StelObjectP(); } StelObjectP StarMgr::searchByNameI18n(const QString& nameI18n) const { QString objw = nameI18n.toUpper(); // Search by HP number if it's an HP formated number QRegExp rx("^\\s*(HIP|HP)\\s*(\\d+)\\s*$", Qt::CaseInsensitive); if (rx.exactMatch(objw)) { return searchHP(rx.capturedTexts().at(2).toInt()); } // Search by I18n common name QMap::const_iterator it(commonNamesIndexI18n.find(objw)); if (it!=commonNamesIndexI18n.end()) { return searchHP(it.value()); } // Search by sci name QMap::const_iterator it2 = sciNamesIndexI18n.find(objw); if (it2!=sciNamesIndexI18n.end()) { return searchHP(it2.value()); } return StelObjectP(); } StelObjectP StarMgr::searchByName(const QString& name) const { QString objw = name.toUpper(); // Search by HP number if it's an HP formated number QRegExp rx("^\\s*(HP|HIP)\\s*(\\d+)\\s*$", Qt::CaseInsensitive); if (rx.exactMatch(objw)) { return searchHP(rx.capturedTexts().at(2).toInt()); } // Search by sci name QMap::const_iterator it = sciNamesIndexI18n.find(objw); if (it!=sciNamesIndexI18n.end()) { return searchHP(it.value()); } return StelObjectP(); } //! Find and return the list of at most maxNbItem objects auto-completing //! the passed object I18n name. QStringList StarMgr::listMatchingObjectsI18n(const QString& objPrefix, int maxNbItem) const { QStringList result; if (maxNbItem==0) return result; QString objw = objPrefix.toUpper(); // Search for common names for (QMap::const_iterator it(commonNamesIndexI18n.lowerBound(objw)); it!=commonNamesIndexI18n.end(); ++it) { if (it.key().startsWith(objw)) { if (maxNbItem==0) break; result << getCommonName(it.value()); --maxNbItem; } else break; } // Search for sci names QString bayerPattern = objw; QRegExp bayerRegEx(bayerPattern); // if the first character is a Greek letter, check if there's an index // after it, such as "alpha1 Cen". if (objw.at(0).unicode() >= 0x0391 && objw.at(0).unicode() <= 0x03A9) bayerRegEx.setPattern(bayerPattern.insert(1,"\\d?")); for (QMap::const_iterator it(sciNamesIndexI18n.lowerBound(objw)); it!=sciNamesIndexI18n.end(); ++it) { if (it.key().indexOf(bayerRegEx)==0) { if (maxNbItem==0) break; result << getSciName(it.value()); --maxNbItem; } else if (it.key().at(0) != objw.at(0)) break; } // Add exact Hp catalogue numbers QRegExp hpRx("^(HIP|HP)\\s*(\\d+)\\s*$"); hpRx.setCaseSensitivity(Qt::CaseInsensitive); if (hpRx.exactMatch(objw)) { bool ok; int hpNum = hpRx.capturedTexts().at(2).toInt(&ok); if (ok) { StelObjectP s = searchHP(hpNum); if (s && maxNbItem>0) { result << QString("HIP%1").arg(hpNum); maxNbItem--; } } } result.sort(); return result; } //! Define font file name and size to use for star names display void StarMgr::setFontSize(double newFontSize) { starFont.setPixelSize(newFontSize); } void StarMgr::updateSkyCulture(const QString& skyCultureDir) { // Load culture star names in english try { loadCommonNames(StelFileMgr::findFile("skycultures/" + skyCultureDir + "/star_names.fab")); } catch(std::runtime_error& e) { qDebug() << "Could not load star_names.fab for sky culture " << skyCultureDir << ": " << e.what(); } try { loadSciNames(StelFileMgr::findFile("stars/default/name.fab")); } catch (std::runtime_error& e) { qWarning() << "WARNING: could not load scientific star names file: " << e.what(); } // Turn on sci names/catalog names for western culture only setFlagSciNames(skyCultureDir.startsWith("western")); updateI18n(); }