1 /*
2 * Stellarium
3 * Copyright (C) 2002 Fabien Chereau
4 *
5 * The big star catalogue extension to Stellarium:
6 * Author and Copyright: Johannes Gajdosik, 2006
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
21 */
22
23
24 #include "StelProjector.hpp"
25 #include "StarMgr.hpp"
26 #include "StelObject.hpp"
27 #include "StelTexture.hpp"
28
29 #include "StelToneReproducer.hpp"
30 #include "StelTranslator.hpp"
31 #include "StelGeodesicGrid.hpp"
32 #include "StelApp.hpp"
33 #include "StelTextureMgr.hpp"
34 #include "StelObjectMgr.hpp"
35 #include "StelLocaleMgr.hpp"
36 #include "StelSkyCultureMgr.hpp"
37 #include "StelFileMgr.hpp"
38 #include "StelModuleMgr.hpp"
39 #include "StelCore.hpp"
40 #include "StelIniParser.hpp"
41 #include "StelPainter.hpp"
42 #include "StelJsonParser.hpp"
43 #include "ZoneArray.hpp"
44 #include "StelSkyDrawer.hpp"
45 #include "RefractionExtinction.hpp"
46 #include "StelModuleMgr.hpp"
47 #include "ConstellationMgr.hpp"
48 #include "Planet.hpp"
49 #include "StelUtils.hpp"
50
51 #include <QTextStream>
52 #include <QFile>
53 #include <QSettings>
54 #include <QString>
55 #include <QRegularExpression>
56 #include <QDebug>
57 #include <QFileInfo>
58 #include <QDir>
59 #include <QCryptographicHash>
60
61 #include <cstdlib>
62
63 static QStringList spectral_array;
64 static QStringList component_array;
65
66 // This number must be incremented each time the content or file format of the stars catalogs change
67 // It can also be incremented when the defaultStarsConfig.json file change.
68 // It should always matchs the version field of the defaultStarsConfig.json file
69 static const int StarCatalogFormatVersion = 12;
70
71 // Initialise statics
72 bool StarMgr::flagSciNames = true;
73 bool StarMgr::flagAdditionalStarNames = true;
74 bool StarMgr::flagDesignations = false;
75 bool StarMgr::flagDblStarsDesignation = false;
76 bool StarMgr::flagVarStarsDesignation = false;
77 bool StarMgr::flagHIPDesignation = false;
78 QHash<int,QString> StarMgr::commonNamesMap;
79 QHash<int,QString> StarMgr::commonNamesMapI18n;
80 QHash<int,QString> StarMgr::additionalNamesMap;
81 QHash<int,QString> StarMgr::additionalNamesMapI18n;
82 QMap<QString,int> StarMgr::commonNamesIndexI18n;
83 QMap<QString,int> StarMgr::commonNamesIndex;
84 QMap<QString,int> StarMgr::additionalNamesIndex;
85 QMap<QString,int> StarMgr::additionalNamesIndexI18n;
86 QHash<int,QString> StarMgr::sciDesignationsMapI18n;
87 QMap<QString,int> StarMgr::sciDesignationsIndexI18n;
88 QHash<int,QString> StarMgr::sciExtraDesignationsMapI18n;
89 QMap<QString,int> StarMgr::sciExtraDesignationsIndexI18n;
90 QHash<int, varstar> StarMgr::varStarsMapI18n;
91 QMap<QString, int> StarMgr::varStarsIndexI18n;
92 QHash<int, wds> StarMgr::wdsStarsMapI18n;
93 QMap<QString, int> StarMgr::wdsStarsIndexI18n;
94 QMap<QString, crossid> StarMgr::crossIdMap;
95 QMap<int, int> StarMgr::saoStarsIndex;
96 QMap<int, int> StarMgr::hdStarsIndex;
97 QMap<int, int> StarMgr::hrStarsIndex;
98 QHash<int, QString> StarMgr::referenceMap;
99 QHash<int, float> StarMgr::hipParallaxErrors;
100 QHash<int, PMData> StarMgr::hipPMData;
101
initStringListFromFile(const QString & file_name)102 QStringList initStringListFromFile(const QString& file_name)
103 {
104 QStringList list;
105 QFile f(file_name);
106 if (f.open(QIODevice::ReadOnly | QIODevice::Text))
107 {
108 while (!f.atEnd())
109 {
110 QString s = QString::fromUtf8(f.readLine());
111 s.chop(1);
112 list << s;
113 }
114 f.close();
115 }
116 return list;
117 }
118
convertToSpectralType(int index)119 QString StarMgr::convertToSpectralType(int index)
120 {
121 if (index < 0 || index >= spectral_array.size())
122 {
123 qDebug() << "convertToSpectralType: bad index: " << index << ", max: " << spectral_array.size();
124 return "";
125 }
126 return spectral_array.at(index);
127 }
128
convertToComponentIds(int index)129 QString StarMgr::convertToComponentIds(int index)
130 {
131 if (index < 0 || index >= component_array.size())
132 {
133 qDebug() << "convertToComponentIds: bad index: " << index << ", max: " << component_array.size();
134 return "";
135 }
136 return component_array.at(index);
137 }
138
139
initTriangle(int lev,int index,const Vec3f & c0,const Vec3f & c1,const Vec3f & c2)140 void StarMgr::initTriangle(int lev,int index, const Vec3f &c0, const Vec3f &c1, const Vec3f &c2)
141 {
142 gridLevels[lev]->initTriangle(index,c0,c1,c2);
143 }
144
145
StarMgr(void)146 StarMgr::StarMgr(void)
147 : StelObjectModule()
148 , flagStarName(false)
149 , labelsAmount(0.)
150 , gravityLabel(false)
151 , maxGeodesicGridLevel(-1)
152 , lastMaxSearchLevel(-1)
153 , hipIndex(new HipIndexStruct[NR_OF_HIP+1])
154 {
155 setObjectName("StarMgr");
156 objectMgr = GETSTELMODULE(StelObjectMgr);
157 Q_ASSERT(objectMgr);
158 }
159
160 /*************************************************************************
161 Reimplementation of the getCallOrder method
162 *************************************************************************/
getCallOrder(StelModuleActionName actionName) const163 double StarMgr::getCallOrder(StelModuleActionName actionName) const
164 {
165 if (actionName==StelModule::ActionDraw)
166 return StelApp::getInstance().getModuleMgr().getModule("ConstellationMgr")->getCallOrder(actionName)+10;
167 return 0;
168 }
169
170
~StarMgr(void)171 StarMgr::~StarMgr(void)
172 {
173 for (auto* z : gridLevels)
174 delete z;
175 gridLevels.clear();
176 if (hipIndex)
177 delete[] hipIndex;
178 }
179
180 // Allow untranslated name here if set in constellationMgr!
getCommonName(int hip)181 QString StarMgr::getCommonName(int hip)
182 {
183 ConstellationMgr* cmgr=GETSTELMODULE(ConstellationMgr);
184 if (cmgr->getConstellationDisplayStyle() == ConstellationMgr::constellationsNative)
185 return getCommonEnglishName(hip);
186
187 auto it = commonNamesMapI18n.find(hip);
188 if (it!=commonNamesMapI18n.end())
189 return it.value();
190 return QString();
191 }
192
getAdditionalNames(int hip)193 QString StarMgr::getAdditionalNames(int hip)
194 {
195 auto it = additionalNamesMapI18n.find(hip);
196 if (it!=additionalNamesMapI18n.end())
197 return it.value();
198 return QString();
199 }
200
getAdditionalEnglishNames(int hip)201 QString StarMgr::getAdditionalEnglishNames(int hip)
202 {
203 auto it = additionalNamesMap.find(hip);
204 if (it!=additionalNamesMap.end())
205 return it.value();
206 return QString();
207 }
208
getCommonEnglishName(int hip)209 QString StarMgr::getCommonEnglishName(int hip)
210 {
211 auto it = commonNamesMap.find(hip);
212 if (it!=commonNamesMap.end())
213 return it.value();
214 return QString();
215 }
216
217
getSciName(int hip)218 QString StarMgr::getSciName(int hip)
219 {
220 auto it = sciDesignationsMapI18n.find(hip);
221 if (it!=sciDesignationsMapI18n.end())
222 return it.value();
223 return QString();
224 }
225
getSciExtraName(int hip)226 QString StarMgr::getSciExtraName(int hip)
227 {
228 auto it = sciExtraDesignationsMapI18n.find(hip);
229 if (it!=sciExtraDesignationsMapI18n.end())
230 return it.value();
231 return QString();
232 }
233
getCrossIdentificationDesignations(QString hip)234 QString StarMgr::getCrossIdentificationDesignations(QString hip)
235 {
236 QStringList designations;
237 auto cr = crossIdMap.find(hip);
238 if (cr==crossIdMap.end() && hip.right(1).toUInt()==0)
239 cr = crossIdMap.find(hip.left(hip.size()-1));
240
241 if (cr!=crossIdMap.end())
242 {
243 crossid crossIdData = cr.value();
244 if (crossIdData.sao>0)
245 designations << QString("SAO %1").arg(crossIdData.sao);
246
247 if (crossIdData.hd>0)
248 designations << QString("HD %1").arg(crossIdData.hd);
249
250 if (crossIdData.hr>0)
251 designations << QString("HR %1").arg(crossIdData.hr);
252 }
253
254 return designations.join(" - ");
255 }
256
getWdsName(int hip)257 QString StarMgr::getWdsName(int hip)
258 {
259 auto it = wdsStarsMapI18n.find(hip);
260 if (it!=wdsStarsMapI18n.end())
261 return QString("WDS J%1").arg(it.value().designation);
262 return QString();
263 }
264
getWdsLastObservation(int hip)265 int StarMgr::getWdsLastObservation(int hip)
266 {
267 auto it = wdsStarsMapI18n.find(hip);
268 if (it!=wdsStarsMapI18n.end())
269 return it.value().observation;
270 return 0;
271 }
272
getWdsLastPositionAngle(int hip)273 float StarMgr::getWdsLastPositionAngle(int hip)
274 {
275 auto it = wdsStarsMapI18n.find(hip);
276 if (it!=wdsStarsMapI18n.end())
277 return it.value().positionAngle;
278 return 0;
279 }
280
getWdsLastSeparation(int hip)281 float StarMgr::getWdsLastSeparation(int hip)
282 {
283 auto it = wdsStarsMapI18n.find(hip);
284 if (it!=wdsStarsMapI18n.end())
285 return it.value().separation;
286 return 0.f;
287 }
288
getGcvsName(int hip)289 QString StarMgr::getGcvsName(int hip)
290 {
291 auto it = varStarsMapI18n.find(hip);
292 if (it!=varStarsMapI18n.end())
293 return it.value().designation;
294 return QString();
295 }
296
getGcvsVariabilityType(int hip)297 QString StarMgr::getGcvsVariabilityType(int hip)
298 {
299 auto it = varStarsMapI18n.find(hip);
300 if (it!=varStarsMapI18n.end())
301 return it.value().vtype;
302 return QString();
303 }
304
getGcvsMaxMagnitude(int hip)305 float StarMgr::getGcvsMaxMagnitude(int hip)
306 {
307 auto it = varStarsMapI18n.find(hip);
308 if (it!=varStarsMapI18n.end())
309 return it.value().maxmag;
310 return -99.f;
311 }
312
getGcvsMagnitudeFlag(int hip)313 int StarMgr::getGcvsMagnitudeFlag(int hip)
314 {
315 auto it = varStarsMapI18n.find(hip);
316 if (it!=varStarsMapI18n.end())
317 return it.value().mflag;
318 return 0;
319 }
320
321
getGcvsMinMagnitude(int hip,bool firstMinimumFlag)322 float StarMgr::getGcvsMinMagnitude(int hip, bool firstMinimumFlag)
323 {
324 auto it = varStarsMapI18n.find(hip);
325 if (it!=varStarsMapI18n.end())
326 {
327 if (firstMinimumFlag)
328 {
329 return it.value().min1mag;
330 }
331 else
332 {
333 return it.value().min2mag;
334 }
335 }
336 return -99.f;
337 }
338
getGcvsPhotometricSystem(int hip)339 QString StarMgr::getGcvsPhotometricSystem(int hip)
340 {
341 auto it = varStarsMapI18n.find(hip);
342 if (it!=varStarsMapI18n.end())
343 return it.value().photosys;
344 return QString();
345 }
346
getGcvsEpoch(int hip)347 double StarMgr::getGcvsEpoch(int hip)
348 {
349 auto it = varStarsMapI18n.find(hip);
350 if (it!=varStarsMapI18n.end())
351 return it.value().epoch;
352 return -99.;
353 }
354
getGcvsPeriod(int hip)355 double StarMgr::getGcvsPeriod(int hip)
356 {
357 auto it = varStarsMapI18n.find(hip);
358 if (it!=varStarsMapI18n.end())
359 return it.value().period;
360 return -99.;
361 }
362
getGcvsMM(int hip)363 int StarMgr::getGcvsMM(int hip)
364 {
365 auto it = varStarsMapI18n.find(hip);
366 if (it!=varStarsMapI18n.end())
367 return it.value().Mm;
368 return -99;
369 }
370
getPlxError(int hip)371 float StarMgr::getPlxError(int hip)
372 {
373 auto it = hipParallaxErrors.find(hip);
374 if (it!=hipParallaxErrors.end())
375 return it.value();
376 return 0.f;
377 }
378
getProperMotion(int hip)379 PMData StarMgr::getProperMotion(int hip)
380 {
381 auto it = hipPMData.find(hip);
382 if (it!=hipPMData.end())
383 return it.value();
384 return QPair<float, float>(NAN, NAN);
385 }
386
copyDefaultConfigFile()387 void StarMgr::copyDefaultConfigFile()
388 {
389 try
390 {
391 StelFileMgr::makeSureDirExistsAndIsWritable(StelFileMgr::getUserDir()+"/stars/default");
392 starConfigFileFullPath = StelFileMgr::getUserDir()+"/stars/default/starsConfig.json";
393 qDebug() << "Creates file " << QDir::toNativeSeparators(starConfigFileFullPath);
394 QFile::copy(StelFileMgr::getInstallationDir()+"/stars/default/defaultStarsConfig.json", starConfigFileFullPath);
395 QFile::setPermissions(starConfigFileFullPath, QFile::permissions(starConfigFileFullPath) | QFileDevice::WriteOwner);
396 }
397 catch (std::runtime_error& e)
398 {
399 qWarning() << e.what();
400 qFatal("Could not create configuration file stars/default/starsConfig.json");
401 }
402 }
403
init()404 void StarMgr::init()
405 {
406 QSettings* conf = StelApp::getInstance().getSettings();
407 Q_ASSERT(conf);
408
409 starConfigFileFullPath = StelFileMgr::findFile("stars/default/starsConfig.json", StelFileMgr::Flags(StelFileMgr::Writable|StelFileMgr::File));
410 if (starConfigFileFullPath.isEmpty())
411 {
412 qWarning() << "Could not find the starsConfig.json file: will copy the default one.";
413 copyDefaultConfigFile();
414 }
415
416 QFile fic(starConfigFileFullPath);
417 if(fic.open(QIODevice::ReadOnly))
418 {
419 starSettings = StelJsonParser::parse(&fic).toMap();
420 fic.close();
421 }
422
423 // Increment the 1 each time any star catalog file change
424 if (starSettings.value("version").toInt()!=StarCatalogFormatVersion)
425 {
426 qWarning() << "Found an old starsConfig.json file, upgrade..";
427 fic.remove();
428 copyDefaultConfigFile();
429 QFile fic2(starConfigFileFullPath);
430 if(fic2.open(QIODevice::ReadOnly))
431 {
432 starSettings = StelJsonParser::parse(&fic2).toMap();
433 fic2.close();
434 }
435 }
436
437 loadData(starSettings);
438
439 populateStarsDesignations();
440 populateHipparcosLists();
441
442 setFontSize(StelApp::getInstance().getScreenFontSize());
443 connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), this, SLOT(setFontSize(int)));
444
445 setFlagStars(conf->value("astro/flag_stars", true).toBool());
446 setFlagLabels(conf->value("astro/flag_star_name",true).toBool());
447 setFlagAdditionalNames(conf->value("astro/flag_star_additional_names",true).toBool());
448 setDesignationUsage(conf->value("astro/flag_star_designation_usage", false).toBool());
449 setFlagDblStarsDesignation(conf->value("astro/flag_star_designation_dbl", false).toBool());
450 setFlagVarStarsDesignation(conf->value("astro/flag_star_designation_var", false).toBool());
451 setFlagHIPDesignation(conf->value("astro/flag_star_designation_hip", false).toBool());
452 setLabelsAmount(conf->value("stars/labels_amount",3.).toDouble());
453
454 objectMgr->registerStelObjectMgr(this);
455 texPointer = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur2.png"); // Load pointer texture
456
457 StelApp::getInstance().getCore()->getGeodesicGrid(maxGeodesicGridLevel)->visitTriangles(maxGeodesicGridLevel,initTriangleFunc,this);
458 for (auto* z : gridLevels)
459 z->scaleAxis();
460 StelApp *app = &StelApp::getInstance();
461 connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
462 connect(&app->getSkyCultureMgr(), SIGNAL(currentSkyCultureChanged(QString)), this, SLOT(updateSkyCulture(const QString&)));
463
464 QString displayGroup = N_("Display Options");
465 addAction("actionShow_Stars", displayGroup, N_("Stars"), "flagStarsDisplayed", "S");
466 addAction("actionShow_Stars_Labels", displayGroup, N_("Stars labels"), "flagLabelsDisplayed", "Alt+S");
467 // Details: https://github.com/Stellarium/stellarium/issues/174
468 addAction("actionShow_Stars_MagnitudeLimitIncrease", displayGroup, N_("Increase the magnitude limit for stars"), "increaseStarsMagnitudeLimit()");
469 addAction("actionShow_Stars_MagnitudeLimitReduce", displayGroup, N_("Reduce the magnitude limit for stars"), "reduceStarsMagnitudeLimit()");
470 }
471
472
drawPointer(StelPainter & sPainter,const StelCore * core)473 void StarMgr::drawPointer(StelPainter& sPainter, const StelCore* core)
474 {
475 const QList<StelObjectP> newSelected = objectMgr->getSelectedObject("Star");
476 if (!newSelected.empty())
477 {
478 const StelObjectP obj = newSelected[0];
479 Vec3d pos=obj->getJ2000EquatorialPos(core);
480
481 Vec3f screenpos;
482 // Compute 2D pos and return if outside screen
483 if (!sPainter.getProjector()->project(pos, screenpos))
484 return;
485
486 sPainter.setColor(obj->getInfoColor());
487 texPointer->bind();
488 sPainter.setBlending(true);
489 sPainter.drawSprite2dMode(screenpos[0], screenpos[1], 13.f, static_cast<float>(StelApp::getInstance().getAnimationTime())*40.f);
490 }
491 }
492
checkAndLoadCatalog(const QVariantMap & catDesc)493 bool StarMgr::checkAndLoadCatalog(const QVariantMap& catDesc)
494 {
495 const bool checked = catDesc.value("checked").toBool();
496 QString catalogFileName = catDesc.value("fileName").toString();
497
498 // See if it is an absolute path, else prepend default path
499 if (!(StelFileMgr::isAbsolute(catalogFileName)))
500 catalogFileName = "stars/default/"+catalogFileName;
501
502 QString catalogFilePath = StelFileMgr::findFile(catalogFileName);
503 if (catalogFilePath.isEmpty())
504 {
505 // The file is supposed to be checked, but we can't find it
506 if (checked)
507 {
508 qWarning() << QString("Warning: could not find star catalog %1").arg(QDir::toNativeSeparators(catalogFileName));
509 setCheckFlag(catDesc.value("id").toString(), false);
510 }
511 return false;
512 }
513 // Possibly fixes crash on Vista
514 if (!StelFileMgr::isReadable(catalogFilePath))
515 {
516 qWarning() << QString("Warning: User does not have permissions to read catalog %1").arg(QDir::toNativeSeparators(catalogFilePath));
517 return false;
518 }
519
520 if (!checked)
521 {
522 // The file is not checked but we found it, maybe from a previous download/version
523 qWarning() << "Found file " << QDir::toNativeSeparators(catalogFilePath) << ", checking md5sum..";
524
525 QFile file(catalogFilePath);
526 if(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
527 {
528 // Compute the MD5 sum
529 QCryptographicHash md5Hash(QCryptographicHash::Md5);
530 const qint64 cat_sz = file.size();
531 qint64 maxStarBufMd5 = qMin(cat_sz, 9223372036854775807LL);
532 uchar *cat = maxStarBufMd5 ? file.map(0, maxStarBufMd5) : Q_NULLPTR;
533 if (!cat)
534 {
535 // The OS was not able to map the file, revert to slower not mmap based method
536 static const qint64 maxStarBufMd5 = 1024*1024*8;
537 char* mmd5buf = static_cast<char*>(malloc(maxStarBufMd5));
538 while (!file.atEnd())
539 {
540 qint64 sz = file.read(mmd5buf, maxStarBufMd5);
541 md5Hash.addData(mmd5buf, static_cast<int>(sz));
542 }
543 free(mmd5buf);
544 }
545 else
546 {
547 md5Hash.addData(reinterpret_cast<const char*>(cat), static_cast<int>(cat_sz));
548 file.unmap(cat);
549 }
550 file.close();
551 if (md5Hash.result().toHex()!=catDesc.value("checksum").toByteArray())
552 {
553 qWarning() << "Error: File " << QDir::toNativeSeparators(catalogFileName) << " is corrupt, MD5 mismatch! Found " << md5Hash.result().toHex() << " expected " << catDesc.value("checksum").toByteArray();
554 file.remove();
555 return false;
556 }
557 qWarning() << "MD5 sum correct!";
558 setCheckFlag(catDesc.value("id").toString(), true);
559 }
560 }
561
562 ZoneArray* z = ZoneArray::create(catalogFilePath, true);
563 if (z)
564 {
565 if (z->level<gridLevels.size())
566 {
567 qWarning() << QDir::toNativeSeparators(catalogFileName) << ", " << z->level << ": duplicate level";
568 delete z;
569 return true;
570 }
571 Q_ASSERT(z->level==maxGeodesicGridLevel+1);
572 Q_ASSERT(z->level==gridLevels.size());
573 ++maxGeodesicGridLevel;
574 gridLevels.append(z);
575 }
576 return true;
577 }
578
setCheckFlag(const QString & catId,bool b)579 void StarMgr::setCheckFlag(const QString& catId, bool b)
580 {
581 // Update the starConfigFileFullPath file to take into account that we now have a new catalog
582 int idx=0;
583 for (const auto& catV : catalogsDescription)
584 {
585 ++idx;
586 QVariantMap m = catV.toMap();
587 if (m.value("id").toString()!=catId)
588 continue;
589 const bool checked = m.value("checked").toBool();
590 if (checked==b)
591 return;
592 m["checked"]=b;
593 catalogsDescription[idx-1]=m;
594 starSettings["catalogs"]=catalogsDescription;
595 QFile tmp(starConfigFileFullPath);
596 if(tmp.open(QIODevice::WriteOnly))
597 {
598 StelJsonParser::write(starSettings, &tmp);
599 tmp.close();
600 }
601 }
602 }
603
loadData(QVariantMap starsConfig)604 void StarMgr::loadData(QVariantMap starsConfig)
605 {
606 // Please do not init twice:
607 Q_ASSERT(maxGeodesicGridLevel < 0);
608
609 qDebug() << "Loading star data ...";
610
611 catalogsDescription = starsConfig.value("catalogs").toList();
612 foreach (const QVariant& catV, catalogsDescription)
613 {
614 QVariantMap m = catV.toMap();
615 checkAndLoadCatalog(m);
616 }
617
618 for (int i=0; i<=NR_OF_HIP; i++)
619 {
620 hipIndex[i].a = Q_NULLPTR;
621 hipIndex[i].z = Q_NULLPTR;
622 hipIndex[i].s = Q_NULLPTR;
623 }
624 for (auto* z : gridLevels)
625 z->updateHipIndex(hipIndex);
626
627 const QString cat_hip_sp_file_name = starsConfig.value("hipSpectralFile").toString();
628 if (cat_hip_sp_file_name.isEmpty())
629 {
630 qWarning() << "ERROR: stars:cat_hip_sp_file_name not found";
631 }
632 else
633 {
634 QString tmpFic = StelFileMgr::findFile("stars/default/" + cat_hip_sp_file_name);
635 if (tmpFic.isEmpty())
636 qWarning() << "ERROR while loading data from " << QDir::toNativeSeparators(("stars/default/" + cat_hip_sp_file_name));
637 else
638 spectral_array = initStringListFromFile(tmpFic);
639 }
640
641 const QString cat_hip_cids_file_name = starsConfig.value("hipComponentsIdsFile").toString();
642 if (cat_hip_cids_file_name.isEmpty())
643 {
644 qWarning() << "ERROR: stars:cat_hip_cids_file_name not found";
645 }
646 else
647 {
648 QString tmpFic = StelFileMgr::findFile("stars/default/" + cat_hip_cids_file_name);
649 if (tmpFic.isEmpty())
650 qWarning() << "ERROR while loading data from " << QDir::toNativeSeparators(("stars/default/" + cat_hip_cids_file_name));
651 else
652 component_array = initStringListFromFile(tmpFic);
653 }
654
655 lastMaxSearchLevel = maxGeodesicGridLevel;
656 qDebug() << "Finished loading star catalogue data, max_geodesic_level: " << maxGeodesicGridLevel;
657 }
658
populateHipparcosLists()659 void StarMgr::populateHipparcosLists()
660 {
661 hipparcosStars.clear();
662 hipStarsHighPM.clear();
663 doubleHipStars.clear();
664 variableHipStars.clear();
665 algolTypeStars.clear();
666 classicalCepheidsTypeStars.clear();
667 carbonStars.clear();
668 bariumStars.clear();
669 const int pmLimit = 1; // arc-second per year!
670 for (int hip=0; hip<=NR_OF_HIP; hip++)
671 {
672 const Star1 *const s = hipIndex[hip].s;
673 if (s)
674 {
675 const SpecialZoneArray<Star1> *const a = hipIndex[hip].a;
676 const SpecialZoneData<Star1> *const z = hipIndex[hip].z;
677 StelObjectP so = s->createStelObject(a,z);
678 hipparcosStars.push_back(so);
679 QString spectrum = convertToSpectralType(s->getSpInt());
680 // Carbon stars have spectral type, which start with C letter
681 if (spectrum.startsWith("C", Qt::CaseInsensitive))
682 carbonStars.push_back(so);
683
684 // Barium stars have spectral class G to K and contains "Ba" string
685 if ((spectrum.startsWith("G", Qt::CaseInsensitive) || spectrum.startsWith("K", Qt::CaseInsensitive)) && spectrum.contains("Ba", Qt::CaseSensitive))
686 bariumStars.push_back(so);
687
688 if (!getGcvsVariabilityType(s->getHip()).isEmpty())
689 {
690 QMap<StelObjectP, float> sa;
691 sa[so] = static_cast<float>(getGcvsPeriod(s->getHip()));
692 variableHipStars.push_back(sa);
693
694 auto vartype = getGcvsVariabilityType(s->getHip());
695 if (vartype.contains("EA"))
696 {
697 QMap<StelObjectP, float> sal;
698 sal[so] = sa[so];
699 algolTypeStars.push_back(sal);
700 }
701 if (vartype.contains("DCEP") && !vartype.contains("DCEPS"))
702 {
703 QMap<StelObjectP, float> sacc;
704 sacc[so] = sa[so];
705 classicalCepheidsTypeStars.push_back(sacc);
706 }
707 }
708 if (!getWdsName(s->getHip()).isEmpty())
709 {
710 QMap<StelObjectP, float> sd;
711 sd[so] = getWdsLastSeparation(s->getHip());
712 doubleHipStars.push_back(sd);
713 }
714 // use separate variables for avoid the overflow (esp. for Barnard's star)
715 PMData properMotion = getProperMotion(s->getHip());
716 float pmX = properMotion.first;
717 float pmY = properMotion.second;
718 float pm = 0.001f * std::sqrt((pmX*pmX) + (pmY*pmY));
719 if (qAbs(pm)>=pmLimit)
720 {
721 QMap<StelObjectP, float> spm;
722 spm[so] = pm;
723 hipStarsHighPM.push_back(spm);
724 }
725 }
726 }
727 }
728
729 // Load common names from file
loadCommonNames(const QString & commonNameFile)730 int StarMgr::loadCommonNames(const QString& commonNameFile)
731 {
732 commonNamesMap.clear();
733 commonNamesMapI18n.clear();
734 additionalNamesMap.clear();
735 additionalNamesMapI18n.clear();
736 commonNamesIndexI18n.clear();
737 commonNamesIndex.clear();
738 additionalNamesIndex.clear();
739 additionalNamesIndexI18n.clear();
740
741 qDebug() << "Loading star names from" << QDir::toNativeSeparators(commonNameFile);
742 QFile cnFile(commonNameFile);
743 if (!cnFile.open(QIODevice::ReadOnly | QIODevice::Text))
744 {
745 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(commonNameFile);
746 return 0;
747 }
748
749 int readOk=0;
750 int totalRecords=0;
751 int lineNumber=0;
752 QString record;
753 // Allow empty and comment lines where first char (after optional blanks) is #
754 QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
755 // record structure is delimited with a | character. We will
756 // use a QRegularExpression to extract the fields. with white-space padding permitted
757 // (i.e. it will be stripped automatically) Example record strings:
758 // " 677|_("Alpheratz")"
759 // "113368|_("Fomalhaut")"
760 // Note: Stellarium doesn't support sky cultures made prior to version 0.10.6 now!
761 QRegularExpression recordRx("^\\s*(\\d+)\\s*\\|[_]*[(]\"(.*)\"[)]\\s*([\\,\\d\\s]*)\\n");
762
763 while(!cnFile.atEnd())
764 {
765 record = QString::fromUtf8(cnFile.readLine());
766 lineNumber++;
767 if (commentRx.match(record).hasMatch())
768 continue;
769
770 totalRecords++;
771 QRegularExpressionMatch recMatch=recordRx.match(record);
772 if (!recMatch.hasMatch())
773 {
774 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
775 << " - record does not match record pattern";
776 continue;
777 }
778 else
779 {
780 // The record is the right format. Extract the fields
781 bool ok;
782 int hip = recMatch.captured(1).toInt(&ok);
783 if (!ok)
784 {
785 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
786 << " - failed to convert " << recMatch.captured(1) << "to a number";
787 continue;
788 }
789 QString englishCommonName = recMatch.captured(2).trimmed();
790 if (englishCommonName.isEmpty())
791 {
792 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
793 << " - empty name field";
794 continue;
795 }
796
797 const QString englishNameCap = englishCommonName.toUpper();
798 if (commonNamesMap.find(hip)!=commonNamesMap.end())
799 {
800 if (additionalNamesMap.find(hip)!=additionalNamesMap.end())
801 {
802 QString sname = additionalNamesMap[hip].append(" - " + englishCommonName);
803 additionalNamesMap[hip] = sname;
804 additionalNamesMapI18n[hip] = sname;
805 additionalNamesIndex[englishNameCap] = hip;
806 additionalNamesIndexI18n[englishNameCap] = hip;
807 }
808 else
809 {
810 additionalNamesMap[hip] = englishCommonName;
811 additionalNamesMapI18n[hip] = englishCommonName;
812 additionalNamesIndex[englishNameCap] = hip;
813 additionalNamesIndexI18n[englishNameCap] = hip;
814 }
815 }
816 else
817 {
818 commonNamesMap[hip] = englishCommonName;
819 commonNamesMapI18n[hip] = englishCommonName;
820 commonNamesIndexI18n[englishNameCap] = hip;
821 commonNamesIndex[englishNameCap] = hip;
822 }
823
824 QString reference = recMatch.captured(3).trimmed();
825 if (!reference.isEmpty())
826 {
827 if (referenceMap.find(hip)!=referenceMap.end())
828 referenceMap[hip] = referenceMap[hip].append("," + reference);
829 else
830 referenceMap[hip] = reference;
831 }
832
833 readOk++;
834 }
835 }
836 cnFile.close();
837
838 qDebug() << "Loaded" << readOk << "/" << totalRecords << "common star names";
839 return 1;
840 }
841
842
843 // Load scientific names from file
loadSciNames(const QString & sciNameFile,const bool extraData)844 void StarMgr::loadSciNames(const QString& sciNameFile, const bool extraData)
845 {
846 if (extraData)
847 {
848 sciExtraDesignationsMapI18n.clear();
849 sciExtraDesignationsIndexI18n.clear();
850 qDebug() << "Loading scientific star extra names from" << QDir::toNativeSeparators(sciNameFile);
851 }
852 else
853 {
854 sciDesignationsMapI18n.clear();
855 sciDesignationsIndexI18n.clear();
856 qDebug() << "Loading scientific star names from" << QDir::toNativeSeparators(sciNameFile);
857 }
858
859 QFile snFile(sciNameFile);
860 if (!snFile.open(QIODevice::ReadOnly | QIODevice::Text))
861 {
862 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(sciNameFile);
863 return;
864 }
865 const QStringList& allRecords = QString::fromUtf8(snFile.readAll()).split('\n');
866 snFile.close();
867
868 int readOk=0;
869 int totalRecords=0;
870 int lineNumber=0;
871 // record structure is delimited with a | character. Example record strings:
872 // " 10819|c_And"
873 // "113726|1_And"
874 for (const auto& record : allRecords)
875 {
876 ++lineNumber;
877 // skip comments and empty lines
878 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
879 continue;
880
881 ++totalRecords;
882 const QStringList& fields = record.split('|');
883 if (fields.size()!=2)
884 {
885 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
886 << " - record does not match record pattern";
887 continue;
888 }
889 else
890 {
891 // The record is the right format. Extract the fields
892 bool ok;
893 int hip = fields.at(0).toInt(&ok);
894 if (!ok)
895 {
896 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
897 << " - failed to convert " << fields.at(0) << "to a number";
898 continue;
899 }
900
901 QString sci_name = fields.at(1).trimmed();
902 if (sci_name.isEmpty())
903 {
904 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
905 << " - empty name field";
906 continue;
907 }
908
909 sci_name.replace('_',' ');
910 if (extraData)
911 {
912 // Don't set the main sci name if it's already set - it's additional sci name
913 if (sciExtraDesignationsMapI18n.find(hip)!=sciExtraDesignationsMapI18n.end())
914 {
915 QString sname = sciExtraDesignationsMapI18n[hip].append(" - " + sci_name);
916 sciExtraDesignationsMapI18n[hip] = sname;
917 }
918 else
919 sciExtraDesignationsMapI18n[hip] = sci_name;
920 sciExtraDesignationsIndexI18n[sci_name] = hip;
921 }
922 else
923 {
924 // Don't set the main sci name if it's already set - it's additional sci name
925 if (sciDesignationsMapI18n.find(hip)!=sciDesignationsMapI18n.end())
926 {
927 QString sname = sciDesignationsMapI18n[hip].append(" - " + sci_name);
928 sciDesignationsMapI18n[hip] = sname;
929 }
930 else
931 sciDesignationsMapI18n[hip] = sci_name;
932 sciDesignationsIndexI18n[sci_name] = hip;
933 }
934 ++readOk;
935 }
936 }
937
938 if (extraData)
939 qDebug() << "Loaded" << readOk << "/" << totalRecords << "scientific star extra names";
940 else
941 qDebug() << "Loaded" << readOk << "/" << totalRecords << "scientific star names";
942 }
943
944 // Load GCVS from file
loadGcvs(const QString & GcvsFile)945 void StarMgr::loadGcvs(const QString& GcvsFile)
946 {
947 varStarsMapI18n.clear();
948 varStarsIndexI18n.clear();
949
950 qDebug() << "Loading variable stars from" << QDir::toNativeSeparators(GcvsFile);
951 QFile vsFile(GcvsFile);
952 if (!vsFile.open(QIODevice::ReadOnly | QIODevice::Text))
953 {
954 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(GcvsFile);
955 return;
956 }
957 const QStringList& allRecords = QString::fromUtf8(vsFile.readAll()).split('\n');
958 vsFile.close();
959
960 int readOk=0;
961 int totalRecords=0;
962 int lineNumber=0;
963
964 // record structure is delimited with a tab character.
965 for (const auto& record : allRecords)
966 {
967 ++lineNumber;
968 // skip comments and empty lines
969 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
970 continue;
971
972 ++totalRecords;
973 const QStringList& fields = record.split('\t');
974
975 bool ok;
976 int hip = fields.at(0).toInt(&ok);
977 if (!ok)
978 {
979 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(GcvsFile)
980 << " - failed to convert " << fields.at(0) << "to a number";
981 continue;
982 }
983
984 // Don't set the star if it's already set
985 if (varStarsMapI18n.find(hip)!=varStarsMapI18n.end())
986 continue;
987
988 varstar variableStar;
989
990 variableStar.designation = fields.at(1).trimmed();
991 variableStar.vtype = fields.at(2).trimmed();
992 if (fields.at(3).isEmpty())
993 variableStar.maxmag = 99.f;
994 else
995 variableStar.maxmag = fields.at(3).toFloat();
996 variableStar.mflag = fields.at(4).toInt();
997 if (fields.at(5).isEmpty())
998 variableStar.min1mag = 99.f;
999 else
1000 variableStar.min1mag = fields.at(5).toFloat();
1001 if (fields.at(6).isEmpty())
1002 variableStar.min2mag = 99.f;
1003 else
1004 variableStar.min2mag = fields.at(6).toFloat();
1005 variableStar.photosys = fields.at(7).trimmed();
1006 variableStar.epoch = fields.at(8).toDouble();
1007 variableStar.period = fields.at(9).toDouble();
1008 variableStar.Mm = fields.at(10).toInt();
1009 variableStar.stype = fields.at(11).trimmed();
1010
1011 varStarsMapI18n[hip] = variableStar;
1012 varStarsIndexI18n[variableStar.designation.toUpper()] = hip;
1013 ++readOk;
1014 }
1015
1016 qDebug() << "Loaded" << readOk << "/" << totalRecords << "variable stars";
1017 }
1018
1019 // Load WDS from file
loadWds(const QString & WdsFile)1020 void StarMgr::loadWds(const QString& WdsFile)
1021 {
1022 wdsStarsMapI18n.clear();
1023 wdsStarsIndexI18n.clear();
1024
1025 qDebug() << "Loading double stars from" << QDir::toNativeSeparators(WdsFile);
1026 QFile dsFile(WdsFile);
1027 if (!dsFile.open(QIODevice::ReadOnly | QIODevice::Text))
1028 {
1029 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(WdsFile);
1030 return;
1031 }
1032 const QStringList& allRecords = QString::fromUtf8(dsFile.readAll()).split('\n');
1033 dsFile.close();
1034
1035 int readOk=0;
1036 int totalRecords=0;
1037 int lineNumber=0;
1038
1039 // record structure is delimited with a tab character.
1040 for (const auto& record : allRecords)
1041 {
1042 ++lineNumber;
1043 // skip comments and empty lines
1044 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
1045 continue;
1046
1047 ++totalRecords;
1048 const QStringList& fields = record.split('\t');
1049
1050 bool ok;
1051 int hip = fields.at(0).toInt(&ok);
1052 if (!ok)
1053 {
1054 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(WdsFile)
1055 << " - failed to convert " << fields.at(0) << "to a number";
1056 continue;
1057 }
1058
1059 // Don't set the star if it's already set
1060 if (wdsStarsMapI18n.find(hip)!=wdsStarsMapI18n.end())
1061 continue;
1062
1063 wds doubleStar;
1064
1065 doubleStar.designation = fields.at(1).trimmed();
1066 doubleStar.observation = fields.at(2).toInt();
1067 doubleStar.positionAngle = fields.at(3).toFloat();
1068 doubleStar.separation = fields.at(4).toFloat();
1069
1070 wdsStarsMapI18n[hip] = doubleStar;
1071 wdsStarsIndexI18n[QString("WDS J%1").arg(doubleStar.designation.toUpper())] = hip;
1072 ++readOk;
1073 }
1074
1075 qDebug() << "Loaded" << readOk << "/" << totalRecords << "double stars";
1076 }
1077
1078 // Load cross-identification data from file
loadCrossIdentificationData(const QString & crossIdFile)1079 void StarMgr::loadCrossIdentificationData(const QString& crossIdFile)
1080 {
1081 crossIdMap.clear();
1082 saoStarsIndex.clear();
1083 hdStarsIndex.clear();
1084 hrStarsIndex.clear();
1085
1086 qDebug() << "Loading cross-identification data from" << QDir::toNativeSeparators(crossIdFile);
1087 QFile ciFile(crossIdFile);
1088 if (!ciFile.open(QIODevice::ReadOnly | QIODevice::Text))
1089 {
1090 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(crossIdFile);
1091 return;
1092 }
1093 const QStringList& allRecords = QString::fromUtf8(ciFile.readAll()).split('\n');
1094 ciFile.close();
1095
1096 crossid crossIdData;
1097
1098 int readOk=0;
1099 int totalRecords=0;
1100 int lineNumber=0;
1101 // record structure is delimited with a 'tab' character. Example record strings:
1102 // "1 128522 224700"
1103 // "2 165988 224690"
1104 for (const auto& record : allRecords)
1105 {
1106 ++lineNumber;
1107 // skip comments and empty lines
1108 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
1109 continue;
1110
1111 ++totalRecords;
1112 const QStringList& fields = record.split('\t');
1113 if (fields.size()!=5)
1114 {
1115 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(crossIdFile)
1116 << " - record does not match record pattern";
1117 continue;
1118 }
1119 else
1120 {
1121 // The record is the right format. Extract the fields
1122 bool ok;
1123 int hip = fields.at(0).toInt(&ok);
1124 if (!ok)
1125 {
1126 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(crossIdFile)
1127 << " - failed to convert " << fields.at(0) << "to a number";
1128 continue;
1129 }
1130
1131 QString hipstar = QString("%1%2").arg(hip).arg(fields.at(1).trimmed());
1132 crossIdData.sao = fields.at(2).toInt(&ok);
1133 crossIdData.hd = fields.at(3).toInt(&ok);
1134 crossIdData.hr = fields.at(4).toInt(&ok);
1135
1136 crossIdMap[hipstar] = crossIdData;
1137 if (crossIdData.sao>0)
1138 saoStarsIndex[crossIdData.sao] = hip;
1139 if (crossIdData.hd>0)
1140 hdStarsIndex[crossIdData.hd] = hip;
1141 if (crossIdData.hr>0)
1142 hrStarsIndex[crossIdData.hr] = hip;
1143
1144 ++readOk;
1145 }
1146 }
1147
1148 qDebug() << "Loaded" << readOk << "/" << totalRecords << "cross-identification data records for stars";
1149 }
1150
loadPlxErr(const QString & plxErrFile)1151 void StarMgr::loadPlxErr(const QString& plxErrFile)
1152 {
1153 // TODO: This is temporary solution for display parallax errors until format of stars catalogs will not be changed!
1154 hipParallaxErrors.clear();
1155
1156 qDebug() << "Loading parallax errors data from" << QDir::toNativeSeparators(plxErrFile);
1157 QFile ciFile(plxErrFile);
1158 if (!ciFile.open(QIODevice::ReadOnly | QIODevice::Text))
1159 {
1160 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(plxErrFile);
1161 return;
1162 }
1163 const QStringList& allRecords = QString::fromUtf8(ciFile.readAll()).split('\n');
1164 ciFile.close();
1165
1166 int readOk=0;
1167 int totalRecords=0;
1168 int lineNumber=0;
1169 // record structure is delimited with a 'tab' character. Example record strings:
1170 // "1 0.0606"
1171 // "2 0.3193"
1172 for (const auto& record : allRecords)
1173 {
1174 ++lineNumber;
1175 // skip comments and empty lines
1176 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
1177 continue;
1178
1179 ++totalRecords;
1180 const QStringList& fields = record.split('\t');
1181 if (fields.size()!=2)
1182 {
1183 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(plxErrFile)
1184 << " - record does not match record pattern";
1185 continue;
1186 }
1187 else
1188 {
1189 // The record is the right format. Extract the fields
1190 bool ok;
1191 int hip = fields.at(0).toInt(&ok);
1192 if (!ok)
1193 {
1194 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(plxErrFile)
1195 << " - failed to convert " << fields.at(0) << "to a number";
1196 continue;
1197 }
1198 hipParallaxErrors[hip] = fields.at(1).toFloat(&ok);
1199
1200 ++readOk;
1201 }
1202 }
1203
1204 qDebug() << "Loaded" << readOk << "/" << totalRecords << "parallax error data records for stars";
1205 }
1206
loadPMData(const QString & pmDataFile)1207 void StarMgr::loadPMData(const QString &pmDataFile)
1208 {
1209 // TODO: This is temporary solution for display parallax errors until format of stars catalogs will not be changed!
1210 hipPMData.clear();
1211
1212 qDebug() << "Loading proper motion data from" << QDir::toNativeSeparators(pmDataFile);
1213 QFile ciFile(pmDataFile);
1214 if (!ciFile.open(QIODevice::ReadOnly | QIODevice::Text))
1215 {
1216 qWarning() << "WARNING - could not open" << QDir::toNativeSeparators(pmDataFile);
1217 return;
1218 }
1219 const QStringList& allRecords = QString::fromUtf8(ciFile.readAll()).split('\n');
1220 ciFile.close();
1221
1222 int readOk=0;
1223 int totalRecords=0;
1224 int lineNumber=0;
1225 // record structure is delimited with a 'tab' character. Example record strings:
1226 // "1 -4.58 -1.61"
1227 // "2 179.70 1.40"
1228 for (const auto& record : allRecords)
1229 {
1230 ++lineNumber;
1231 // skip comments and empty lines
1232 if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
1233 continue;
1234
1235 ++totalRecords;
1236 const QStringList& fields = record.split('\t');
1237 if (fields.size()!=3)
1238 {
1239 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(pmDataFile)
1240 << " - record does not match record pattern";
1241 continue;
1242 }
1243 else
1244 {
1245 // The record is the right format. Extract the fields
1246 bool ok;
1247 int hip = fields.at(0).toInt(&ok);
1248 if (!ok)
1249 {
1250 qWarning() << "WARNING - parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(pmDataFile)
1251 << " - failed to convert " << fields.at(0) << "to a number";
1252 continue;
1253 }
1254 PMData properMotion;
1255 properMotion.first = fields.at(1).toFloat(&ok);
1256 properMotion.second = fields.at(2).toFloat(&ok);
1257 hipPMData[hip] = properMotion;
1258
1259 ++readOk;
1260 }
1261 }
1262
1263 qDebug() << "Loaded" << readOk << "/" << totalRecords << "proper motion data records for stars";
1264 }
1265
getMaxSearchLevel() const1266 int StarMgr::getMaxSearchLevel() const
1267 {
1268 int rval = -1;
1269 for (const auto* z : gridLevels)
1270 {
1271 const float mag_min = 0.001f*z->mag_min;
1272 RCMag rcmag;
1273 if (StelApp::getInstance().getCore()->getSkyDrawer()->computeRCMag(mag_min, &rcmag)==false)
1274 break;
1275 rval = z->level;
1276 }
1277 return rval;
1278 }
1279
1280
1281 // Draw all the stars
draw(StelCore * core)1282 void StarMgr::draw(StelCore* core)
1283 {
1284 const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
1285 StelSkyDrawer* skyDrawer = core->getSkyDrawer();
1286 // If stars are turned off don't waste time below
1287 // projecting all stars just to draw disembodied labels
1288 if (!static_cast<bool>(starsFader.getInterstate()))
1289 return;
1290
1291 int maxSearchLevel = getMaxSearchLevel();
1292 QVector<SphericalCap> viewportCaps = prj->getViewportConvexPolygon()->getBoundingSphericalCaps();
1293 viewportCaps.append(core->getVisibleSkyArea());
1294 const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(maxSearchLevel)->search(viewportCaps,maxSearchLevel);
1295
1296 // Set temporary static variable for optimization
1297 const float names_brightness = labelsFader.getInterstate() * starsFader.getInterstate();
1298
1299 // prepare for aberration: Explan. Suppl. 2013, (7.38)
1300 const bool withAberration=core->getUseAberration();
1301 Vec3d vel(0.);
1302 if (withAberration)
1303 {
1304 vel=core->getCurrentPlanet()->getHeliocentricEclipticVelocity();
1305 StelCore::matVsop87ToJ2000.transfo(vel);
1306 vel*=core->getAberrationFactor()*(AU/(86400.0*SPEED_OF_LIGHT));
1307 }
1308 const Vec3f velf=vel.toVec3f();
1309
1310 // Prepare openGL for drawing many stars
1311 StelPainter sPainter(prj);
1312 sPainter.setFont(starFont);
1313 skyDrawer->preDrawPointSource(&sPainter);
1314
1315 // Prepare a table for storing precomputed RCMag for all ZoneArrays
1316 RCMag rcmag_table[RCMAG_TABLE_SIZE];
1317
1318 // Draw all the stars of all the selected zones
1319 for (const auto* z : gridLevels)
1320 {
1321 int limitMagIndex=RCMAG_TABLE_SIZE;
1322 const float mag_min = 0.001f*z->mag_min;
1323 const float k = (0.001f*z->mag_range)/z->mag_steps; // MagStepIncrement
1324 for (int i=0;i<RCMAG_TABLE_SIZE;++i)
1325 {
1326 const float mag = mag_min+k*i;
1327 if (skyDrawer->computeRCMag(mag, &rcmag_table[i])==false)
1328 {
1329 if (i==0)
1330 goto exit_loop;
1331
1332 // The last magnitude at which the star is visible
1333 limitMagIndex = i-1;
1334
1335 // We reached the point where stars are not visible anymore
1336 // Fill the rest of the table with zero and leave.
1337 for (;i<RCMAG_TABLE_SIZE;++i)
1338 {
1339 rcmag_table[i].luminance=0;
1340 rcmag_table[i].radius=0;
1341 }
1342 break;
1343 }
1344 rcmag_table[i].radius *= starsFader.getInterstate();
1345 }
1346 lastMaxSearchLevel = z->level;
1347
1348 int maxMagStarName = 0;
1349 if (labelsFader.getInterstate()>0.f)
1350 {
1351 // Adapt magnitude limit of the stars labels according to FOV and labelsAmount
1352 float maxMag = (skyDrawer->getLimitMagnitude()-6.5f)*0.7f+(static_cast<float>(labelsAmount)*1.2f)-2.f;
1353 int x = static_cast<int>((maxMag-mag_min)/k);
1354 if (x > 0)
1355 maxMagStarName = x;
1356 }
1357 int zone;
1358
1359 for (GeodesicSearchInsideIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
1360 z->draw(&sPainter, zone, true, rcmag_table, limitMagIndex, core, maxMagStarName, names_brightness, viewportCaps, withAberration, velf);
1361 for (GeodesicSearchBorderIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
1362 z->draw(&sPainter, zone, false, rcmag_table, limitMagIndex, core, maxMagStarName,names_brightness, viewportCaps, withAberration, velf);
1363 }
1364 exit_loop:
1365
1366 // Finish drawing many stars
1367 skyDrawer->postDrawPointSource(&sPainter);
1368
1369 if (objectMgr->getFlagSelectedObjectPointer())
1370 drawPointer(sPainter, core);
1371 }
1372
1373
1374 // Return a QList containing the stars located
1375 // inside the limFov circle around position vv (in J2000 frame without aberration)
searchAround(const Vec3d & vv,double limFov,const StelCore * core) const1376 QList<StelObjectP > StarMgr::searchAround(const Vec3d& vv, double limFov, const StelCore* core) const
1377 {
1378 QList<StelObjectP > result;
1379 if (!getFlagStars())
1380 return result;
1381
1382 Vec3d v(vv);
1383 v.normalize();
1384
1385 // find any vectors h0 and h1 (length 1), so that h0*v=h1*v=h0*h1=0
1386 int i;
1387 {
1388 const double a0 = fabs(v[0]);
1389 const double a1 = fabs(v[1]);
1390 const double a2 = fabs(v[2]);
1391 if (a0 <= a1)
1392 {
1393 if (a0 <= a2) i = 0;
1394 else i = 2;
1395 } else
1396 {
1397 if (a1 <= a2) i = 1;
1398 else i = 2;
1399 }
1400 }
1401 Vec3d h0(0.0,0.0,0.0);
1402 h0[i] = 1.0;
1403 Vec3d h1 = h0 ^ v;
1404 h1.normalize();
1405 h0 = h1 ^ v;
1406 h0.normalize();
1407
1408 // Now we have h0*v=h1*v=h0*h1=0.
1409 // Construct a region with 4 corners e0,e1,e2,e3 inside which all desired stars must be:
1410 double f = 1.4142136 * tan(limFov * M_PI/180.0);
1411 h0 *= f;
1412 h1 *= f;
1413 Vec3d e0 = v + h0;
1414 Vec3d e1 = v + h1;
1415 Vec3d e2 = v - h0;
1416 Vec3d e3 = v - h1;
1417 f = 1.0/e0.length();
1418 e0 *= f;
1419 e1 *= f;
1420 e2 *= f;
1421 e3 *= f;
1422 // Search the triangles
1423 SphericalConvexPolygon c(e3, e2, e2, e0);
1424 const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(lastMaxSearchLevel)->search(c.getBoundingSphericalCaps(),lastMaxSearchLevel);
1425
1426 // Iterate over the stars inside the triangles
1427 f = cos(limFov * M_PI/180.);
1428 for (auto* z : gridLevels)
1429 {
1430 //qDebug() << "search inside(" << it->first << "):";
1431 int zone;
1432 for (GeodesicSearchInsideIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
1433 {
1434 z->searchAround(core, zone,v,f,result);
1435 //qDebug() << " " << zone;
1436 }
1437 //qDebug() << StelUtils::getEndLineChar() << "search border(" << it->first << "):";
1438 for (GeodesicSearchBorderIterator it1(*geodesic_search_result,z->level); (zone = it1.next()) >= 0;)
1439 {
1440 z->searchAround(core, zone,v,f,result);
1441 //qDebug() << " " << zone;
1442 }
1443 }
1444 return result;
1445 }
1446
1447
1448 //! Update i18 names from english names according to passed translator.
1449 //! The translation is done using gettext with translated strings defined in translations.h
updateI18n()1450 void StarMgr::updateI18n()
1451 {
1452 const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
1453 commonNamesMapI18n.clear();
1454 commonNamesIndexI18n.clear();
1455 additionalNamesMapI18n.clear();
1456 additionalNamesIndexI18n.clear();
1457 for (QHash<int,QString>::ConstIterator it(commonNamesMap.constBegin());it!=commonNamesMap.constEnd();it++)
1458 {
1459 const int i = it.key();
1460 const QString t(trans.qtranslate(it.value()));
1461 commonNamesMapI18n[i] = t;
1462 commonNamesIndexI18n[t.toUpper()] = i;
1463 }
1464 for (QHash<int,QString>::ConstIterator ita(additionalNamesMap.constBegin());ita!=additionalNamesMap.constEnd();ita++)
1465 {
1466 const int i = ita.key();
1467 QStringList a = ita.value().split(" - ");
1468 QStringList tn;
1469 for (const auto& str : a)
1470 {
1471 QString tns = trans.qtranslate(str);
1472 tn << tns;
1473 additionalNamesIndexI18n[tns.toUpper()] = i;
1474 }
1475 const QString r = tn.join(" - ");
1476 additionalNamesMapI18n[i] = r;
1477 }
1478 }
1479
1480 // Search the star by HP number
searchHP(int hp) const1481 StelObjectP StarMgr::searchHP(int hp) const
1482 {
1483 if (0 < hp && hp <= NR_OF_HIP)
1484 {
1485 const Star1 *const s = hipIndex[hp].s;
1486 if (s)
1487 {
1488 const SpecialZoneArray<Star1> *const a = hipIndex[hp].a;
1489 const SpecialZoneData<Star1> *const z = hipIndex[hp].z;
1490 return s->createStelObject(a,z);
1491 }
1492 }
1493 return StelObjectP();
1494 }
1495
searchByNameI18n(const QString & nameI18n) const1496 StelObjectP StarMgr::searchByNameI18n(const QString& nameI18n) const
1497 {
1498 QString objw = nameI18n.toUpper();
1499
1500 // Search by I18n common name
1501 auto it = commonNamesIndexI18n.find(objw);
1502 if (it!=commonNamesIndexI18n.end())
1503 return searchHP(it.value());
1504
1505 if (getFlagAdditionalNames())
1506 {
1507 // Search by I18n additional common names
1508 auto ita = additionalNamesIndexI18n.find(objw);
1509 if (ita!=additionalNamesIndexI18n.end())
1510 return searchHP(ita.value());
1511 }
1512
1513 return searchByName(nameI18n);
1514 }
1515
1516
searchByName(const QString & name) const1517 StelObjectP StarMgr::searchByName(const QString& name) const
1518 {
1519 QString objw = name.toUpper();
1520
1521 // Search by HP number if it's an HP formatted number
1522 QRegularExpression rx("^\\s*(HP|HIP)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1523 QRegularExpressionMatch match=rx.match(objw);
1524 if (match.hasMatch())
1525 return searchHP(match.captured(2).toInt());
1526
1527 // Search by SAO number if it's an SAO formatted number
1528 QRegularExpression rx2("^\\s*(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1529 match=rx2.match(objw);
1530 if (match.hasMatch())
1531 {
1532 auto sao = saoStarsIndex.find(match.captured(2).toInt());
1533 if (sao!=saoStarsIndex.end())
1534 return searchHP(sao.value());
1535 }
1536
1537 // Search by HD number if it's an HD formatted number
1538 QRegularExpression rx3("^\\s*(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1539 match=rx3.match(objw);
1540 if (match.hasMatch())
1541 {
1542 auto hd = hdStarsIndex.find(match.captured(2).toInt());
1543 if (hd!=hdStarsIndex.end())
1544 return searchHP(hd.value());
1545 }
1546
1547 // Search by HR number if it's an HR formatted number
1548 QRegularExpression rx4("^\\s*(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1549 match=rx4.match(objw);
1550 if (match.hasMatch())
1551 {
1552 auto hr = hrStarsIndex.find(match.captured(2).toInt());
1553 if (hr!=hrStarsIndex.end())
1554 return searchHP(hr.value());
1555 }
1556
1557 // Search by English common name
1558 auto it = commonNamesIndex.find(objw);
1559 if (it!=commonNamesIndex.end())
1560 return searchHP(it.value());
1561
1562 if (getFlagAdditionalNames())
1563 {
1564 // Search by English additional common names
1565 auto ita = additionalNamesIndex.find(objw);
1566 if (ita!=additionalNamesIndex.end())
1567 return searchHP(ita.value());
1568 }
1569
1570 // Search by scientific name
1571 auto itd = sciDesignationsIndexI18n.find(name); // case sensitive!
1572 if (itd!=sciDesignationsIndexI18n.end())
1573 return searchHP(itd.value());
1574 auto itdi = sciDesignationsIndexI18n.find(objw); // case insensitive!
1575 if (itdi!=sciDesignationsIndexI18n.end())
1576 return searchHP(itdi.value());
1577
1578 // Search by scientific name
1579 auto eitd = sciExtraDesignationsIndexI18n.find(name); // case sensitive!
1580 if (eitd!=sciExtraDesignationsIndexI18n.end())
1581 return searchHP(eitd.value());
1582 auto eitdi = sciExtraDesignationsIndexI18n.find(objw); // case insensitive!
1583 if (eitdi!=sciExtraDesignationsIndexI18n.end())
1584 return searchHP(eitdi.value());
1585
1586 // Search by GCVS name
1587 auto it4 = varStarsIndexI18n.find(objw);
1588 if (it4!=varStarsIndexI18n.end())
1589 return searchHP(it4.value());
1590
1591 // Search by WDS name
1592 auto wdsIt = wdsStarsIndexI18n.find(objw);
1593 if (wdsIt!=wdsStarsIndexI18n.end())
1594 return searchHP(wdsIt.value());
1595
1596 return StelObjectP();
1597 }
1598
searchByID(const QString & id) const1599 StelObjectP StarMgr::searchByID(const QString &id) const
1600 {
1601 return searchByName(id);
1602 }
1603
1604 //! Find and return the list of at most maxNbItem objects auto-completing the passed object name.
listMatchingObjects(const QString & objPrefix,int maxNbItem,bool useStartOfWords) const1605 QStringList StarMgr::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
1606 {
1607 QStringList result;
1608 if (maxNbItem <= 0 || !getFlagStars())
1609 return result;
1610
1611 QString objw = objPrefix.toUpper();
1612 bool found;
1613
1614 // Search for common names
1615 QMapIterator<QString, int> i(commonNamesIndexI18n);
1616 while (i.hasNext())
1617 {
1618 i.next();
1619 if (useStartOfWords && i.key().startsWith(objw))
1620 found = true;
1621 else if (!useStartOfWords && i.key().contains(objw))
1622 found = true;
1623 else
1624 found = false;
1625
1626 if (found)
1627 {
1628 if (maxNbItem<=0)
1629 break;
1630 result.append(getCommonName(i.value()));
1631 --maxNbItem;
1632 }
1633 }
1634
1635 QMapIterator<QString, int> j(commonNamesIndex);
1636 while (j.hasNext())
1637 {
1638 j.next();
1639 if (useStartOfWords && j.key().startsWith(objw))
1640 found = true;
1641 else if (!useStartOfWords && j.key().contains(objw))
1642 found = true;
1643 else
1644 found = false;
1645
1646 if (found)
1647 {
1648 if (maxNbItem<=0)
1649 break;
1650 result.append(getCommonEnglishName(j.value()));
1651 --maxNbItem;
1652 }
1653 }
1654
1655 if (getFlagAdditionalNames())
1656 {
1657 QMapIterator<QString, int> k(additionalNamesIndexI18n);
1658 while (k.hasNext())
1659 {
1660 k.next();
1661 QStringList names = getAdditionalNames(k.value()).split(" - ");
1662 for (const auto &name : qAsConst(names))
1663 {
1664 if (useStartOfWords && name.startsWith(objw, Qt::CaseInsensitive))
1665 found = true;
1666 else if (!useStartOfWords && name.contains(objw, Qt::CaseInsensitive))
1667 found = true;
1668 else
1669 found = false;
1670
1671 if (found)
1672 {
1673 if (maxNbItem<=0)
1674 break;
1675 result.append(name);
1676 --maxNbItem;
1677 }
1678 }
1679 }
1680
1681 QMapIterator<QString, int> l(additionalNamesIndex);
1682 while (l.hasNext())
1683 {
1684 l.next();
1685 QStringList names = getAdditionalEnglishNames(l.value()).split(" - ");
1686 for (const auto &name : qAsConst(names))
1687 {
1688 if (useStartOfWords && name.startsWith(objw, Qt::CaseInsensitive))
1689 found = true;
1690 else if (!useStartOfWords && name.contains(objw, Qt::CaseInsensitive))
1691 found = true;
1692 else
1693 found = false;
1694
1695 if (found)
1696 {
1697 if (maxNbItem<=0)
1698 break;
1699 result.append(name);
1700 --maxNbItem;
1701 }
1702 }
1703 }
1704 }
1705
1706 // Search for sci names
1707 QString bayerPattern = objPrefix;
1708 QRegularExpression bayerRegEx(bayerPattern);
1709 QString bayerPatternCI = objw;
1710 QRegularExpression bayerRegExCI(bayerPatternCI);
1711
1712 // if the first character is a Greek letter, check if there's an index
1713 // after it, such as "alpha1 Cen".
1714 if (objPrefix.at(0).unicode() >= 0x0391 && objPrefix.at(0).unicode() <= 0x03A9)
1715 bayerRegEx.setPattern(bayerPattern.insert(1,"\\d?"));
1716 if (objw.at(0).unicode() >= 0x0391 && objw.at(0).unicode() <= 0x03A9)
1717 bayerRegExCI.setPattern(bayerPatternCI.insert(1,"\\d?"));
1718
1719 for (auto it = sciDesignationsIndexI18n.lowerBound(objPrefix); it != sciDesignationsIndexI18n.end(); ++it)
1720 {
1721 if (it.key().indexOf(bayerRegEx)==0 || it.key().indexOf(objPrefix)==0)
1722 {
1723 if (maxNbItem<=0)
1724 break;
1725 QStringList names = getSciName(it.value()).split(" - ");
1726 for (const auto &name : qAsConst(names))
1727 {
1728 if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
1729 found = true;
1730 else if (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive))
1731 found = true;
1732 else
1733 found = false;
1734
1735 if (found)
1736 {
1737 if (maxNbItem<=0)
1738 break;
1739 result.append(name);
1740 --maxNbItem;
1741 }
1742 }
1743 }
1744 else if (it.key().at(0) != objPrefix.at(0))
1745 break;
1746 }
1747
1748 for (auto it = sciDesignationsIndexI18n.lowerBound(objw); it != sciDesignationsIndexI18n.end(); ++it)
1749 {
1750 if (it.key().indexOf(bayerRegExCI)==0 || it.key().indexOf(objw)==0)
1751 {
1752 if (maxNbItem<=0)
1753 break;
1754 QStringList names = getSciName(it.value()).split(" - ");
1755 for (const auto &name : qAsConst(names))
1756 {
1757 if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
1758 found = true;
1759 else if (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive))
1760 found = true;
1761 else
1762 found = false;
1763
1764 if (found)
1765 {
1766 if (maxNbItem<=0)
1767 break;
1768 result.append(name);
1769 --maxNbItem;
1770 }
1771 }
1772 }
1773 else if (it.key().at(0) != objw.at(0))
1774 break;
1775 }
1776
1777 for (auto it = sciExtraDesignationsIndexI18n.lowerBound(objPrefix); it != sciExtraDesignationsIndexI18n.end(); ++it)
1778 {
1779 if (it.key().indexOf(bayerRegEx)==0 || it.key().indexOf(objPrefix)==0)
1780 {
1781 if (maxNbItem<=0)
1782 break;
1783 QStringList names = getSciExtraName(it.value()).split(" - ");
1784 for (const auto &name : qAsConst(names))
1785 {
1786 if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
1787 found = true;
1788 else if (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive))
1789 found = true;
1790 else
1791 found = false;
1792
1793 if (found)
1794 {
1795 if (maxNbItem<=0)
1796 break;
1797 result.append(name);
1798 --maxNbItem;
1799 }
1800 }
1801 }
1802 else if (it.key().at(0) != objPrefix.at(0))
1803 break;
1804 }
1805
1806 for (auto it = sciExtraDesignationsIndexI18n.lowerBound(objw); it != sciExtraDesignationsIndexI18n.end(); ++it)
1807 {
1808 if (it.key().indexOf(bayerRegExCI)==0 || it.key().indexOf(objw)==0)
1809 {
1810 if (maxNbItem<=0)
1811 break;
1812 QStringList names = getSciExtraName(it.value()).split(" - ");
1813 for (const auto &name : qAsConst(names))
1814 {
1815 if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
1816 found = true;
1817 else if (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive))
1818 found = true;
1819 else
1820 found = false;
1821
1822 if (found)
1823 {
1824 if (maxNbItem<=0)
1825 break;
1826 result.append(name);
1827 --maxNbItem;
1828 }
1829 }
1830 }
1831 else if (it.key().at(0) != objw.at(0))
1832 break;
1833 }
1834
1835 // Search for sci names for var stars
1836 for (auto it = varStarsIndexI18n.lowerBound(objw); it != varStarsIndexI18n.end(); ++it)
1837 {
1838 if (it.key().startsWith(objw))
1839 {
1840 if (maxNbItem<=0)
1841 break;
1842 result << getGcvsName(it.value());
1843 --maxNbItem;
1844 }
1845 else
1846 break;
1847 }
1848
1849 // Add exact Hp catalogue numbers
1850 QRegularExpression hpRx("^(HIP|HP)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1851 QRegularExpressionMatch match=hpRx.match(objw);
1852 if (match.hasMatch())
1853 {
1854 bool ok;
1855 int hpNum = match.captured(2).toInt(&ok);
1856 if (ok)
1857 {
1858 StelObjectP s = searchHP(hpNum);
1859 if (s && maxNbItem>0)
1860 {
1861 result << QString("HIP%1").arg(hpNum);
1862 maxNbItem--;
1863 }
1864 }
1865 }
1866
1867 // Add exact SAO catalogue numbers
1868 QRegularExpression saoRx("^(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1869 match=saoRx.match(objw);
1870 if (match.hasMatch())
1871 {
1872 int saoNum = match.captured(2).toInt();
1873 auto sao = saoStarsIndex.find(saoNum);
1874 if (sao!=saoStarsIndex.end())
1875 {
1876 StelObjectP s = searchHP(sao.value());
1877 if (s && maxNbItem>0)
1878 {
1879 result << QString("SAO%1").arg(saoNum);
1880 maxNbItem--;
1881 }
1882 }
1883 }
1884
1885 // Add exact HD catalogue numbers
1886 QRegularExpression hdRx("^(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1887 match=hdRx.match(objw);
1888 if (match.hasMatch())
1889 {
1890 int hdNum = match.captured(2).toInt();
1891 auto hd = hdStarsIndex.find(hdNum);
1892 if (hd!=hdStarsIndex.end())
1893 {
1894 StelObjectP s = searchHP(hd.value());
1895 if (s && maxNbItem>0)
1896 {
1897 result << QString("HD%1").arg(hdNum);
1898 maxNbItem--;
1899 }
1900 }
1901 }
1902
1903 // Add exact HR catalogue numbers
1904 QRegularExpression hrRx("^(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1905 match=hrRx.match(objw);
1906 if (match.hasMatch())
1907 {
1908 int hrNum = match.captured(2).toInt();
1909 auto hr = hrStarsIndex.find(hrNum);
1910 if (hr!=hrStarsIndex.end())
1911 {
1912 StelObjectP s = searchHP(hr.value());
1913 if (s && maxNbItem>0)
1914 {
1915 result << QString("HR%1").arg(hrNum);
1916 maxNbItem--;
1917 }
1918 }
1919 }
1920
1921 // Add exact WDS catalogue numbers
1922 QRegularExpression wdsRx("^(WDS)\\s*(\\S+)\\s*$", QRegularExpression::CaseInsensitiveOption);
1923 if (wdsRx.match(objw).hasMatch())
1924 {
1925 for (auto wds = wdsStarsIndexI18n.lowerBound(objw); wds != wdsStarsIndexI18n.end(); ++wds)
1926 {
1927 if (wds.key().startsWith(objw))
1928 {
1929 if (maxNbItem==0)
1930 break;
1931 result << getWdsName(wds.value());
1932 --maxNbItem;
1933 }
1934 else
1935 break;
1936 }
1937 }
1938
1939 result.sort();
1940 return result;
1941 }
1942
1943 //! Define font file name and size to use for star names display
setFontSize(int newFontSize)1944 void StarMgr::setFontSize(int newFontSize)
1945 {
1946 starFont.setPixelSize(newFontSize);
1947 }
1948
updateSkyCulture(const QString & skyCultureDir)1949 void StarMgr::updateSkyCulture(const QString& skyCultureDir)
1950 {
1951 // Load culture star names in english
1952 QString fic = StelFileMgr::findFile("skycultures/" + skyCultureDir + "/star_names.fab");
1953 if (fic.isEmpty())
1954 qDebug() << "Could not load star_names.fab for sky culture " << QDir::toNativeSeparators(skyCultureDir);
1955 else
1956 loadCommonNames(fic);
1957
1958 // Turn on sci names/catalog names for western cultures only
1959 setFlagSciNames(skyCultureDir.contains("western", Qt::CaseInsensitive));
1960 updateI18n();
1961 }
1962
increaseStarsMagnitudeLimit()1963 void StarMgr::increaseStarsMagnitudeLimit()
1964 {
1965 StelCore* core = StelApp::getInstance().getCore();
1966 core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() + 0.1);
1967 }
1968
reduceStarsMagnitudeLimit()1969 void StarMgr::reduceStarsMagnitudeLimit()
1970 {
1971 StelCore* core = StelApp::getInstance().getCore();
1972 core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() - 0.1);
1973 }
1974
populateStarsDesignations()1975 void StarMgr::populateStarsDesignations()
1976 {
1977 QString fic;
1978 fic = StelFileMgr::findFile("stars/default/name.fab");
1979 if (fic.isEmpty())
1980 qWarning() << "WARNING: could not load scientific star names file: stars/default/name.fab";
1981 else
1982 loadSciNames(fic, false);
1983
1984 fic = StelFileMgr::findFile("stars/default/extra_name.fab");
1985 if (fic.isEmpty())
1986 qWarning() << "WARNING: could not load scientific star extra names file: stars/default/extra_name.fab";
1987 else
1988 loadSciNames(fic, true);
1989
1990 fic = StelFileMgr::findFile("stars/default/gcvs_hip_part.dat");
1991 if (fic.isEmpty())
1992 qWarning() << "WARNING: could not load variable stars file: stars/default/gcvs_hip_part.dat";
1993 else
1994 loadGcvs(fic);
1995
1996 fic = StelFileMgr::findFile("stars/default/wds_hip_part.dat");
1997 if (fic.isEmpty())
1998 qWarning() << "WARNING: could not load double stars file: stars/default/wds_hip_part.dat";
1999 else
2000 loadWds(fic);
2001
2002 fic = StelFileMgr::findFile("stars/default/cross-id.dat");
2003 if (fic.isEmpty())
2004 qWarning() << "WARNING: could not load cross-identification data file: stars/default/cross-id.dat";
2005 else
2006 loadCrossIdentificationData(fic);
2007
2008 fic = StelFileMgr::findFile("stars/default/hip_plx_err.dat");
2009 if (fic.isEmpty())
2010 qWarning() << "WARNING: could not load parallax errors data file: stars/default/hip_plx_err.dat";
2011 else
2012 loadPlxErr(fic);
2013
2014 fic = StelFileMgr::findFile("stars/default/hip_pm.dat");
2015 if (fic.isEmpty())
2016 qWarning() << "WARNING: could not load proper motion data file: stars/default/hip_pm.dat";
2017 else
2018 loadPMData(fic);
2019 }
2020
listAllObjects(bool inEnglish) const2021 QStringList StarMgr::listAllObjects(bool inEnglish) const
2022 {
2023 QStringList result;
2024 if (inEnglish)
2025 {
2026 QMapIterator<QString, int> i(commonNamesIndex);
2027 while (i.hasNext())
2028 {
2029 i.next();
2030 result << getCommonEnglishName(i.value());
2031 }
2032 }
2033 else
2034 {
2035 QMapIterator<QString, int> i(commonNamesIndexI18n);
2036 while (i.hasNext())
2037 {
2038 i.next();
2039 result << getCommonName(i.value());
2040 }
2041 }
2042 return result;
2043 }
2044
listAllObjectsByType(const QString & objType,bool inEnglish) const2045 QStringList StarMgr::listAllObjectsByType(const QString &objType, bool inEnglish) const
2046 {
2047 QStringList result;
2048 // type 1
2049 bool isStarT1 = false;
2050 QList<StelObjectP> starsT1;
2051 // type 2
2052 bool isStarT2 = false;
2053 QList<QMap<StelObjectP, float>> starsT2;
2054 int type = objType.toInt();
2055 // Use SkyTranslator for translation star names
2056 const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
2057 switch (type)
2058 {
2059 case 0: // Interesting double stars
2060 {
2061 static const QStringList doubleStars = {
2062 "Asterope", "Atlas", "77 Tau", "δ1 Tau", "V1016 Ori",
2063 "42 Ori", "ι Ori", "ζ Crv", "Mizar", "Zubenelgenubi",
2064 "ω1 Sco", "λ Sco", "μ1 Sco", "ζ1 Sco", "ε1 Lyr", "δ1 Lyr",
2065 "ν1 Sgr", "ο1 Cyg", "ο2 Cyg", "Algedi", "Albireo", "Rigel",
2066 "Almaak", "ξ Boo", "Rasalgethi", "T Dra", "Kuma", "70 Oph",
2067 "Castor", "ζ Her", "Keid", "Mesarthim", "Porrima", "Algieba",
2068 "β Mon", "Izar", "44 Boo", "Acrab", "Tegmine", "φ2 Cnc",
2069 "Regulus", "Cor Caroli", "ι Cas", "ε Ari", "Markeb", "γ1 Del",
2070 "Bessel's Star", "55 Aqr", "σ Cas", "Achird", "Polaris", "36 Oph",
2071 "65 UMa", "σ2 UMa", "55 Cnc", "16 Cyg", "HIP 28393", "HIP 84709"};
2072 if (inEnglish)
2073 {
2074 result = doubleStars;
2075 }
2076 else
2077 {
2078 for (const auto &star : doubleStars)
2079 {
2080 result << trans.qtranslate(star);
2081 }
2082 }
2083 break;
2084 }
2085 case 1: // Interesting variable stars
2086 {
2087 static const QStringList variableStars = {
2088 "δ Cep", "Algol", "Mira", "λ Tau", "Sheliak", "ζ Gem", "μ Cep",
2089 "Rasalgethi", "η Gem", "η Aql", "γ Cas", "Betelgeuse", "R And",
2090 "U Ant", "θ Aps", "R Aql", "V Aql", "R Aqr", "ε Aur", "R Aur",
2091 "AE Aur", "W Boo", "VZ Cam", "l Car", "γ Cas", "WZ Cas",
2092 "S Cen", "Proxima", "T Cep", "U Cep", "R CMa", "VY CMa",
2093 "S Cnc", "Alphekka", "R CrB", "T CrB", "U CrB", "R Cru",
2094 "SU Cyg", "EU Del", "β Dor", "R Gem", "30 Her", "68 Her",
2095 "R Hor", "Hind's Crimson Star", "R Leo", "RR Lyr", "U Mon",
2096 "Mintaka", "VV Ori", "κ Pav", "β Peg", "Enif", "ζ Phe", "R Sct",
2097 "U Sgr", "RY Sgr", "W UMa", "Polaris"};
2098 if (inEnglish)
2099 {
2100 result = variableStars;
2101 }
2102 else
2103 {
2104 for (const auto &star : variableStars)
2105 {
2106 result << trans.qtranslate(star);
2107 }
2108 }
2109 break;
2110 }
2111 case 2: // Bright double stars
2112 {
2113 starsT2 = doubleHipStars;
2114 isStarT2 = true;
2115 break;
2116 }
2117 case 3: // Bright variable stars
2118 {
2119 starsT2 = variableHipStars;
2120 isStarT2 = true;
2121 break;
2122 }
2123 case 4:
2124 {
2125 starsT2 = hipStarsHighPM;
2126 isStarT2 = true;
2127 break;
2128 }
2129 case 5: // Variable stars: Algol-type eclipsing systems
2130 {
2131 starsT2 = algolTypeStars;
2132 isStarT2 = true;
2133 break;
2134 }
2135 case 6: // Variable stars: the classical cepheids
2136 {
2137 starsT2 = classicalCepheidsTypeStars;
2138 isStarT2 = true;
2139 break;
2140 }
2141 case 7: // Bright carbon stars
2142 {
2143 starsT1 = carbonStars;
2144 isStarT1 = true;
2145 break;
2146 }
2147 case 8: // Bright barium stars
2148 {
2149 starsT1 = bariumStars;
2150 isStarT1 = true;
2151 break;
2152 }
2153 default:
2154 {
2155 // No stars yet?
2156 break;
2157 }
2158 }
2159
2160 QString starName;
2161 if (isStarT1)
2162 {
2163 for (const auto& star : qAsConst(starsT1))
2164 {
2165 starName = inEnglish ? star->getEnglishName() : star->getNameI18n();
2166 if (!starName.isEmpty())
2167 result << starName;
2168 else
2169 result << star->getID();
2170 }
2171 }
2172
2173 if (isStarT2)
2174 {
2175 for (const auto& star : qAsConst(starsT2))
2176 {
2177 starName = inEnglish ? star.firstKey()->getEnglishName() : star.firstKey()->getNameI18n();
2178 if (!starName.isEmpty())
2179 result << starName;
2180 else
2181 result << star.firstKey()->getID();
2182 }
2183 }
2184
2185 result.removeDuplicates();
2186 return result;
2187 }
2188
getStelObjectType() const2189 QString StarMgr::getStelObjectType() const
2190 {
2191 return STAR_TYPE;
2192 }
2193