• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 15670918640

16 Jun 2025 02:08AM UTC coverage: 11.775% (-0.2%) from 11.931%
15670918640

push

github

alex-w
Updated data

14700 of 124846 relevant lines covered (11.77%)

18324.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/core/modules/StarMgr.cpp
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 "StelTranslator.hpp"
30
#include "StelGeodesicGrid.hpp"
31
#include "StelApp.hpp"
32
#include "StelTextureMgr.hpp"
33
#include "StelObjectMgr.hpp"
34
#include "StelLocaleMgr.hpp"
35
#include "StelSkyCultureMgr.hpp"
36
#include "StelFileMgr.hpp"
37
#include "StelModuleMgr.hpp"
38
#include "StelCore.hpp"
39
#include "StelPainter.hpp"
40
#include "StelJsonParser.hpp"
41
#include "ZoneArray.hpp"
42
#include "StelSkyDrawer.hpp"
43
#include "StelModuleMgr.hpp"
44
#include "ConstellationMgr.hpp"
45
#include "Planet.hpp"
46
#include "StelUtils.hpp"
47
#include "StelHealpix.hpp"
48
#include "StelObject.hpp"
49

50
#include <QGlobalStatic>
51
#include <QTextStream>
52
#include <QFile>
53
#include <QBuffer>
54
#include <QSettings>
55
#include <QString>
56
#include <QRegularExpression>
57
#include <QDebug>
58
#include <QFileInfo>
59
#include <QDir>
60
#include <QCryptographicHash>
61

62
#include <cstdlib>
63

64
Q_GLOBAL_STATIC(QStringList, spectral_array);
×
65
Q_GLOBAL_STATIC(QStringList, component_array);
×
66
Q_GLOBAL_STATIC(QStringList, objtype_array);
×
67

68
// This number must be incremented each time the content or file format of the stars catalogs change
69
// It can also be incremented when the defaultStarsConfig.json file change.
70
// It should always match the version field of the defaultStarsConfig.json file
71
static const int StarCatalogFormatVersion = 23;
72

73
// Initialise statics
74
bool StarMgr::flagSciNames = true;
75
bool StarMgr::flagAdditionalStarNames = true;
76
bool StarMgr::flagDesignations = false;
77
bool StarMgr::flagDblStarsDesignation = false;
78
bool StarMgr::flagVarStarsDesignation = false;
79
bool StarMgr::flagHIPDesignation = false;
80

81
QHash<StarId,QString> StarMgr::commonNamesMap;
82
QHash<StarId,QString> StarMgr::commonNamesI18nMap;
83
QHash<QString,StarId> StarMgr::commonNamesI18nUppercaseIndex;
84
QHash<QString,StarId> StarMgr::commonNamesUppercaseIndex;
85

86
// Cultural names: We must store all data here, and have one common index to native names&spelling, pronunciation, english and user-language spelling
87
QMultiHash<StarId, StelObject::CulturalName> StarMgr::culturalNamesMap; // cultural names
88
QMultiMap<QString, StarId> StarMgr::culturalNamesUppercaseIndex; // reverse mappings of uppercased names. For names, unfortunately multiple results are possible!
89

90
QHash<StarId,QString> StarMgr::sciDesignationsMap;      // Bayer/Flamsteed. TODO: Convert map target to QStringList?
91
QHash<QString,StarId> StarMgr::sciDesignationsIndex;
92
QHash<StarId,QString> StarMgr::sciExtraDesignationsMap; // Other sci designations. TODO: Convert map target to QStringList?
93
QHash<QString,StarId> StarMgr::sciExtraDesignationsIndex;
94
QHash<StarId, varstar> StarMgr::varStarsMap;
95
QHash<QString, StarId> StarMgr::varStarsIndex;
96
QHash<StarId, wds> StarMgr::wdsStarsMap;
97
QHash<QString, StarId> StarMgr::wdsStarsIndex;
98
QMap<QString, crossid> StarMgr::crossIdMap;
99
QHash<int, StarId> StarMgr::saoStarsIndex;
100
QHash<int, StarId> StarMgr::hdStarsIndex;
101
QHash<int, StarId> StarMgr::hrStarsIndex;
102
QHash<StarId, QString> StarMgr::referenceMap;
103
QHash<StarId, binaryorbitstar> StarMgr::binaryOrbitStarMap;
104

105
QStringList initStringListFromFile(const QString& file_name)
×
106
{
107
        QStringList list;
×
108
        QFile f(file_name);
×
109
        if (f.open(QIODevice::ReadOnly | QIODevice::Text))
×
110
        {
111
                while (!f.atEnd())
×
112
                {
113
                        QString s = QString::fromUtf8(f.readLine());
×
114
                        s.chop(1);
×
115
                        list << s;
×
116
                }
×
117
                f.close();
×
118
        }
119
        return list;
×
120
}
×
121

122
QString StarMgr::convertToSpectralType(int index)
×
123
{
124
        if (index < 0 || index >= spectral_array->size())
×
125
        {
126
                qDebug() << "convertToSpectralType: bad index: " << index << ", max: " << spectral_array->size();
×
127
                return "";
×
128
        }
129
        return spectral_array->at(index);
×
130
}
131

132
QString StarMgr::convertToComponentIds(int index)
×
133
{
134
        if (index < 0 || index >= component_array->size())
×
135
        {
136
                qDebug() << "convertToComponentIds: bad index: " << index << ", max: " << component_array->size();
×
137
                return "";
×
138
        }
139
        return component_array->at(index);
×
140
}
141

142
QString StarMgr::convertToOjectTypes(int index)
×
143
{
144
        if (index < 0 || index >= objtype_array->size())
×
145
        {
146
                qDebug() << "convertToObjTypeIds: bad index: " << index << ", max: " << objtype_array->size();
×
147
                return "";
×
148
        }
149
        return objtype_array->at(index).trimmed();
×
150
}
151

152

153
void StarMgr::initTriangle(int lev,int index, const Vec3f &c0, const Vec3f &c1, const Vec3f &c2)
×
154
{
155
        gridLevels[lev]->initTriangle(index,c0,c1,c2);
×
156
}
×
157

158

159
StarMgr::StarMgr(void)
×
160
        : StelObjectModule()
161
        , flagStarName(false)
×
162
        , labelsAmount(0.)
×
163
        , gravityLabel(false)
×
164
        , maxGeodesicGridLevel(-1)
×
165
        , lastMaxSearchLevel(-1)
×
166
        , hipIndex(new HipIndexStruct[NR_OF_HIP+1])
×
167
{
168
        setObjectName("StarMgr");
×
169
        objectMgr = GETSTELMODULE(StelObjectMgr);
×
170
        Q_ASSERT(objectMgr);
×
171
}
×
172

173
/*************************************************************************
174
 Reimplementation of the getCallOrder method
175
*************************************************************************/
176
double StarMgr::getCallOrder(StelModuleActionName actionName) const
×
177
{
178
        if (actionName==StelModule::ActionDraw)
×
179
                return StelApp::getInstance().getModuleMgr().getModule("ConstellationMgr")->getCallOrder(actionName)+10;
×
180
        return 0;
×
181
}
182

183

184
StarMgr::~StarMgr(void)
×
185
{
186
        for (auto* z : std::as_const(gridLevels))
×
187
                delete z;
×
188
        gridLevels.clear();
×
189
        if (hipIndex)
×
190
                delete[] hipIndex;
×
191
}
×
192

193
// Allow untranslated name here if set in constellationMgr!
194
QString StarMgr::getCommonNameI18n(StarId hip)
×
195
{
196
        return commonNamesI18nMap.value(hip, QString());
×
197
}
198

199
// Get the cultural names for a star with a specified
200
// Hipparcos or Gaia catalogue number.
201
// @param hip The Hipparcos/Gaia number of star
202
// @return cultural names of star
203
QList<StelObject::CulturalName> StarMgr::getCulturalNames(StarId hip)
×
204
{
205
        // As returned, the latest added comes first!
206
        // Our SC JSONs are typically human generated, with most common first, so reverse the results.
207

208
        QList<StelObject::CulturalName>cNames=culturalNamesMap.values(hip);
×
209
        std::reverse(cNames.begin(), cNames.end());
×
210
        return cNames;
×
211
}
×
212

213
QString StarMgr::getCommonEnglishName(StarId hip)
×
214
{
215
        return commonNamesMap.value(hip, QString());
×
216
}
217

218
QString StarMgr::getSciDesignation(StarId hip)
×
219
{
220
        return sciDesignationsMap.value(hip, QString());
×
221
}
222

223
QString StarMgr::getSciExtraDesignation(StarId hip)
×
224
{
225
        return sciExtraDesignationsMap.value(hip, QString());
×
226
}
227

228
QString StarMgr::getCrossIdentificationDesignations(const QString &hip)
×
229
{
230
        Q_ASSERT(hip==hip.trimmed());
×
231
        QStringList designations;
×
232
        //auto cr = crossIdMap.find(hip);
233
        //if (cr==crossIdMap.end() && hip.right(1).toUInt()==0) // What was the purpose of this? check for 0 (bug) or space (just trim!)?
234
        //        cr = crossIdMap.find(hip.left(hip.size()-1));
235

236
        if (crossIdMap.contains(hip))
×
237
        {
238
                crossid crossIdData = crossIdMap.value(hip);
×
239
                if (crossIdData.hr>0)
×
240
                        designations << QString("HR %1").arg(crossIdData.hr);
×
241

242
                if (crossIdData.hd>0)
×
243
                        designations << QString("HD %1").arg(crossIdData.hd);
×
244

245
                if (crossIdData.sao>0)
×
246
                        designations << QString("SAO %1").arg(crossIdData.sao);
×
247
        }
248

249
        return designations.join(" - ");
×
250
}
×
251

252
QString StarMgr::getWdsDesignation(StarId hip)
×
253
{
254
        return (wdsStarsMap.contains(hip) ? QString("WDS J%1").arg(wdsStarsMap.value(hip).designation) : QString());
×
255
}
256

257
int StarMgr::getWdsLastObservation(StarId hip)
×
258
{
259
        return (wdsStarsMap.contains(hip) ? wdsStarsMap.value(hip).observation : 0);
×
260
}
261

262
float StarMgr::getWdsLastPositionAngle(StarId hip)
×
263
{
264
        return (wdsStarsMap.contains(hip) ? wdsStarsMap.value(hip).positionAngle : 0);
×
265
}
266

267
float StarMgr::getWdsLastSeparation(StarId hip)
×
268
{
269
        return (wdsStarsMap.contains(hip) ? wdsStarsMap.value(hip).separation : 0);
×
270
}
271

272
QString StarMgr::getGcvsDesignation(StarId hip)
×
273
{
274
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).designation : QString());
×
275
}
276

277
QString StarMgr::getGcvsVariabilityType(StarId hip)
×
278
{
279
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).vtype : QString());
×
280
}
281

282
float StarMgr::getGcvsMaxMagnitude(StarId hip)
×
283
{
284
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).maxmag : -99.f);
×
285
}
286

287
int StarMgr::getGcvsMagnitudeFlag(StarId hip)
×
288
{
289
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).mflag : 0);
×
290
}
291

292

293
float StarMgr::getGcvsMinMagnitude(StarId hip, bool firstMinimumFlag)
×
294
{
295
        if (varStarsMap.contains(hip))
×
296
        {
297
                varstar var=varStarsMap.value(hip);
×
298
                return (firstMinimumFlag ? var.min1mag : var.min2mag);
×
299
        }
×
300
        return -99.f;
×
301
}
302

303
QString StarMgr::getGcvsPhotometricSystem(StarId hip)
×
304
{
305
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).photosys : QString());
×
306
}
307

308
double StarMgr::getGcvsEpoch(StarId hip)
×
309
{
310
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).epoch : -99.);
×
311
}
312

313
double StarMgr::getGcvsPeriod(StarId hip)
×
314
{
315
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).period : -99.);
×
316
}
317

318
int StarMgr::getGcvsMM(StarId hip)
×
319
{
320
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).Mm : -99);
×
321
}
322

323
QString StarMgr::getGcvsSpectralType(StarId hip)
×
324
{
325
        return (varStarsMap.contains(hip) ? varStarsMap.value(hip).stype : QString());
×
326
}
327

328
binaryorbitstar StarMgr::getBinaryOrbitData(StarId hip)
×
329
{
330
        return binaryOrbitStarMap.value(hip, binaryorbitstar());
×
331
}
332

333
void StarMgr::copyDefaultConfigFile()
×
334
{
335
        try
336
        {
337
                StelFileMgr::makeSureDirExistsAndIsWritable(StelFileMgr::getUserDir()+"/stars/hip_gaia3");
×
338
                starConfigFileFullPath = StelFileMgr::getUserDir()+"/stars/hip_gaia3/starsConfig.json";
×
339
                qDebug() << "Creates file " << QDir::toNativeSeparators(starConfigFileFullPath);
×
340
                QFile::copy(StelFileMgr::getInstallationDir()+"/stars/hip_gaia3/defaultStarsConfig.json", starConfigFileFullPath);
×
341
                QFile::setPermissions(starConfigFileFullPath, QFile::permissions(starConfigFileFullPath) | QFileDevice::WriteOwner);
×
342
        }
343
        catch (std::runtime_error& e)
×
344
        {
345
                qWarning() << e.what();
×
346
                qFatal("Could not create configuration file stars/hip_gaia3/starsConfig.json");
×
347
        }
×
348
}
×
349

350
void StarMgr::init()
×
351
{
352
        QSettings* conf = StelApp::getInstance().getSettings();
×
353
        Q_ASSERT(conf);
×
354

355
        starConfigFileFullPath = StelFileMgr::findFile("stars/hip_gaia3/starsConfig.json", StelFileMgr::Flags(StelFileMgr::Writable|StelFileMgr::File));
×
356
        if (starConfigFileFullPath.isEmpty())
×
357
        {
358
                qWarning() << "Could not find the starsConfig.json file: will copy the default one.";
×
359
                copyDefaultConfigFile();
×
360
        }
361

362
        QFile fic(starConfigFileFullPath);
×
363
        if(fic.open(QIODevice::ReadOnly))
×
364
        {
365
                starSettings = StelJsonParser::parse(&fic).toMap();
×
366
                fic.close();
×
367
        }
368

369
        // Increment the 1 each time any star catalog file change
370
        if (starSettings.value("version").toInt()!=StarCatalogFormatVersion)
×
371
        {
372
                qWarning() << "Found an old starsConfig.json file, upgrade..";
×
373
                fic.remove();
×
374
                copyDefaultConfigFile();
×
375
                QFile fic2(starConfigFileFullPath);
×
376
                if(fic2.open(QIODevice::ReadOnly))
×
377
                {
378
                        starSettings = StelJsonParser::parse(&fic2).toMap();
×
379
                        fic2.close();
×
380
                }
381
        }
×
382

383
        loadData(starSettings);
×
384

385
        populateStarsDesignations();
×
386
        populateHipparcosLists();
×
387

388
        setFontSize(StelApp::getInstance().getScreenFontSize());
×
389
        connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), this, SLOT(setFontSize(int)));
×
390

391
        setFlagStars(conf->value("astro/flag_stars", true).toBool());
×
392
        setFlagLabels(conf->value("astro/flag_star_name",true).toBool());
×
393
        setLabelsAmount(conf->value("stars/labels_amount",3.).toDouble());
×
394
        setFlagAdditionalNames(conf->value("astro/flag_star_additional_names",true).toBool());
×
395
        setDesignationUsage(conf->value("astro/flag_star_designation_usage", false).toBool());
×
396
        setFlagDblStarsDesignation(conf->value("astro/flag_star_designation_dbl", false).toBool());
×
397
        setFlagVarStarsDesignation(conf->value("astro/flag_star_designation_var", false).toBool());
×
398
        setFlagHIPDesignation(conf->value("astro/flag_star_designation_hip", false).toBool());
×
399

400
        objectMgr->registerStelObjectMgr(this);
×
401
        texPointer = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur2.png");   // Load pointer texture
×
402

403
        StelApp::getInstance().getCore()->getGeodesicGrid(maxGeodesicGridLevel)->visitTriangles(maxGeodesicGridLevel,initTriangleFunc,this);
×
404
        StelApp *app = &StelApp::getInstance();
×
405
        connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
×
406
        connect(&app->getSkyCultureMgr(), &StelSkyCultureMgr::currentSkyCultureChanged, this, &StarMgr::updateSkyCulture);
×
407

408
        QString displayGroup = N_("Display Options");
×
409
        addAction("actionShow_Stars", displayGroup, N_("Stars"), "flagStarsDisplayed", "S");
×
410
        addAction("actionShow_Stars_Labels", displayGroup, N_("Stars labels"), "flagLabelsDisplayed", "Alt+S");
×
411
        // Details: https://github.com/Stellarium/stellarium/issues/174
412
        addAction("actionShow_Stars_MagnitudeLimitIncrease", displayGroup, N_("Increase the magnitude limit for stars"), "increaseStarsMagnitudeLimit()");
×
413
        addAction("actionShow_Stars_MagnitudeLimitReduce", displayGroup, N_("Reduce the magnitude limit for stars"), "reduceStarsMagnitudeLimit()");
×
414
}
×
415

416

417
void StarMgr::drawPointer(StelPainter& sPainter, const StelCore* core)
×
418
{
419
        const QList<StelObjectP> newSelected = objectMgr->getSelectedObject("Star");
×
420
        if (!newSelected.empty())
×
421
        {
422
                const StelObjectP obj = newSelected[0];
×
423
                Vec3d pos=obj->getJ2000EquatorialPos(core);
×
424

425
                Vec3f screenpos;
×
426
                // Compute 2D pos and return if outside screen
427
                if (!sPainter.getProjector()->project(pos, screenpos))
×
428
                        return;
×
429

430
                sPainter.setColor(obj->getInfoColor());
×
431
                texPointer->bind();
×
432
                sPainter.setBlending(true);
×
433
                sPainter.drawSprite2dMode(screenpos[0], screenpos[1], 13.f, static_cast<float>(StelApp::getInstance().getAnimationTime())*40.f);
×
434
        }
×
435
}
×
436

437
bool StarMgr::checkAndLoadCatalog(const QVariantMap& catDesc, const bool load)
×
438
{
439
        const bool checked = catDesc.value("checked").toBool();
×
440
        QString catalogFileName = catDesc.value("fileName").toString();
×
441

442
        // See if it is an absolute path, else prepend default path
443
        if (!(StelFileMgr::isAbsolute(catalogFileName)))
×
444
                catalogFileName = "stars/hip_gaia3/"+catalogFileName;
×
445

446
        QString catalogFilePath = StelFileMgr::findFile(catalogFileName);
×
447
        if (catalogFilePath.isEmpty())
×
448
        {
449
                // The file is supposed to be checked, but we can't find it
450
                if (checked)
×
451
                {
452
                        qWarning().noquote() << "Could not find star catalog" << QDir::toNativeSeparators(catalogFileName);
×
453
                        setCheckFlag(catDesc.value("id").toString(), false);
×
454
                }
455
                return false;
×
456
        }
457
        // Possibly fixes crash on Vista
458
        if (!StelFileMgr::isReadable(catalogFilePath))
×
459
        {
460
                qWarning().noquote() << "User does not have permissions to read catalog" << QDir::toNativeSeparators(catalogFilePath);
×
461
                return false;
×
462
        }
463

464
        if (!checked)
×
465
        {
466
                // The file is not checked but we found it, maybe from a previous download/version
467
                qInfo().noquote() << "Found file" << QDir::toNativeSeparators(catalogFilePath) << ", checking md5sum...";
×
468

469
                QFile file(catalogFilePath);
×
470
                if(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
×
471
                {
472
                        // Compute the MD5 sum
473
                        QCryptographicHash md5Hash(QCryptographicHash::Md5);
×
474
                        const qint64 cat_sz = file.size();
×
475
                        qint64 maxStarBufMd5 = qMin(cat_sz, 9223372036854775807LL);
×
476
                        uchar *cat = maxStarBufMd5 ? file.map(0, maxStarBufMd5) : Q_NULLPTR;
×
477
                        if (!cat)
×
478
                        {
479
                                // The OS was not able to map the file, revert to slower not mmap based method
480
                                static const qint64 maxStarBufMd5 = 1024*1024*8;
481
                                char* mmd5buf = static_cast<char*>(malloc(maxStarBufMd5));
×
482
                                while (!file.atEnd())
×
483
                                {
484
                                        qint64 sz = file.read(mmd5buf, maxStarBufMd5);
×
485
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
486
                                        md5Hash.addData(QByteArrayView(mmd5buf, static_cast<int>(sz)));
×
487
#else
488
                                        md5Hash.addData(mmd5buf, static_cast<int>(sz));
489
#endif
490
                                }
491
                                free(mmd5buf);
×
492
                        }
493
                        else
494
                        {
495
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
496
                                md5Hash.addData(QByteArrayView(reinterpret_cast<const char*>(cat), static_cast<int>(cat_sz)));
×
497
#else
498
                                md5Hash.addData(reinterpret_cast<const char*>(cat), static_cast<int>(cat_sz));
499
#endif
500
                                file.unmap(cat);
×
501
                        }
502
                        file.close();
×
503
                        if (md5Hash.result().toHex()!=catDesc.value("checksum").toByteArray())
×
504
                        {
505
                                qWarning().noquote() << "Error: File" << QDir::toNativeSeparators(catalogFileName) << "is corrupt, MD5 mismatch! Found" << md5Hash.result().toHex() << "expected" << catDesc.value("checksum").toByteArray();
×
506
                                file.remove();
×
507
                                return false;
×
508
                        }
509
                        qInfo() << "MD5 sum correct!";
×
510
                        setCheckFlag(catDesc.value("id").toString(), true);
×
511
                }
×
512
        }
×
513

514
        if (load)
×
515
        {
516
                ZoneArray* z = ZoneArray::create(catalogFilePath, true);
×
517
                if (z)
×
518
                {
519
                        if (z->level<gridLevels.size())
×
520
                        {
521
                                qWarning().noquote() << QDir::toNativeSeparators(catalogFileName) << ", " << z->level << ": duplicate level";
×
522
                                delete z;
×
523
                                return true;
×
524
                        }
525
                        Q_ASSERT(z->level==maxGeodesicGridLevel+1);
×
526
                        Q_ASSERT(z->level==gridLevels.size());
×
527
                        ++maxGeodesicGridLevel;
×
528
                        gridLevels.append(z);
×
529
                }
530
                return true;
×
531
        }
532
        else
533
        {
534
                qWarning().noquote() << "Star catalog: " << QDir::toNativeSeparators(catalogFileName) << "is found but not loaded because at least one of the lower levels is missing!";
×
535
                return false;
×
536
        }
537
}
×
538

539
void StarMgr::setCheckFlag(const QString& catId, bool b)
×
540
{
541
        // Update the starConfigFileFullPath file to take into account that we now have a new catalog
542
        int idx=0;
×
543
        for (const auto& catV : std::as_const(catalogsDescription))
×
544
        {
545
                ++idx;
×
546
                QVariantMap m = catV.toMap();
×
547
                if (m.value("id").toString()!=catId)
×
548
                        continue;
×
549
                const bool checked = m.value("checked").toBool();
×
550
                if (checked==b)
×
551
                        return;
×
552
                m["checked"]=b;
×
553
                catalogsDescription[idx-1]=m;                
×
554
        }
×
555
        starSettings["catalogs"]=catalogsDescription;
×
556
        QFile tmp(starConfigFileFullPath);
×
557
        if(tmp.open(QIODevice::WriteOnly))
×
558
        {
559
                StelJsonParser::write(starSettings, &tmp);
×
560
                tmp.close();
×
561
        }
562
}
×
563

564
void StarMgr::loadData(const QVariantMap &starsConfig)
×
565
{
566
        // Please do not init twice:
567
        Q_ASSERT(maxGeodesicGridLevel < 0);
×
568

569
        qInfo() << "Loading star data ...";
×
570

571
        catalogsDescription = starsConfig.value("catalogs").toList();
×
572
        bool isSuccessing = true;
×
573
        foreach (const QVariant& catV, catalogsDescription)
×
574
        {
575
                QVariantMap m = catV.toMap();
×
576
                isSuccessing = checkAndLoadCatalog(m, isSuccessing);
×
577
        }
×
578

579
        for (int i=0; i<=NR_OF_HIP; i++)
×
580
        {
581
                hipIndex[i].a = Q_NULLPTR;
×
582
                hipIndex[i].z = Q_NULLPTR;
×
583
                hipIndex[i].s = Q_NULLPTR;
×
584
        }
585
        for (auto* z : std::as_const(gridLevels))
×
586
                z->updateHipIndex(hipIndex);
×
587

588
        const QString cat_hip_sp_file_name = starsConfig.value("hipSpectralFile").toString();
×
589
        if (cat_hip_sp_file_name.isEmpty())
×
590
        {
591
                qWarning() << "ERROR: stars:cat_hip_sp_file_name not found";
×
592
        }
593
        else
594
        {
595
                QString tmpFic = StelFileMgr::findFile("stars/hip_gaia3/" + cat_hip_sp_file_name);
×
596
                if (tmpFic.isEmpty())
×
597
                        qWarning() << "ERROR while loading data from" << QDir::toNativeSeparators(("stars/hip_gaia3/" + cat_hip_sp_file_name));
×
598
                else
599
                        *spectral_array = initStringListFromFile(tmpFic);
×
600
        }
×
601

602
        const QString cat_objtype_file_name = starsConfig.value("objecttypesFile").toString();
×
603
        if (cat_objtype_file_name.isEmpty())
×
604
        {
605
                qWarning() << "ERROR: stars:cat_objtype_file_name not found";
×
606
        }
607
        else
608
        {
609
                QString tmpFic = StelFileMgr::findFile("stars/hip_gaia3/" + cat_objtype_file_name);
×
610
                if (tmpFic.isEmpty())
×
611
                        qWarning() << "ERROR while loading data from" << QDir::toNativeSeparators(("stars/hip_gaia3/" + cat_objtype_file_name));
×
612
                else
613
                        *objtype_array = initStringListFromFile(tmpFic);
×
614
        }
×
615

616
        // create an array with the first element being an empty string, and then contain strings A,B,C,...,Z
617
        component_array->append("");
×
618
        for (int i=0; i<26; i++)
×
619
                component_array->append(QString(QChar('A'+i)));
×
620

621
        lastMaxSearchLevel = maxGeodesicGridLevel;
×
622
        qInfo().noquote() << "Finished loading star catalogue data, max_geodesic_level:" << maxGeodesicGridLevel;
×
623
}
×
624

625
void StarMgr::populateHipparcosLists()
×
626
{
627
        hipparcosStars.clear();
×
628
        hipStarsHighPM.clear();
×
629
        doubleHipStars.clear();
×
630
        variableHipStars.clear();
×
631
        algolTypeStars.clear();
×
632
        classicalCepheidsTypeStars.clear();
×
633
        carbonStars.clear();
×
634
        bariumStars.clear();
×
635
        const int pmLimit = 1000; // arc-second per year!
×
636
        for (int hip=0; hip<=NR_OF_HIP; hip++)
×
637
        {
638
                const Star1 *const s = hipIndex[hip].s;
×
639
                if (s)
×
640
                {
641
                        const SpecialZoneArray<Star1> *const a = hipIndex[hip].a;
×
642
                        const SpecialZoneData<Star1> *const z = hipIndex[hip].z;
×
643
                        StelObjectP so = s->createStelObject(a,z);
×
644
                        hipparcosStars.push_back(so);
×
645
                        QString spectrum = convertToSpectralType(s->getSpInt());
×
646
                        // Carbon stars have spectral type, which start with C letter
647
                        if (spectrum.startsWith("C", Qt::CaseInsensitive))
×
648
                                carbonStars.push_back(so);
×
649

650
                        // Barium stars have spectral class G to K and contains "Ba" string
651
                        if ((spectrum.startsWith("G", Qt::CaseInsensitive) || spectrum.startsWith("K", Qt::CaseInsensitive)) && spectrum.contains("Ba", Qt::CaseSensitive))
×
652
                                bariumStars.push_back(so);
×
653

654
                        if (!getGcvsVariabilityType(s->getHip()).isEmpty())
×
655
                        {
656
                                QPair<StelObjectP, float> sa(so, static_cast<float>(getGcvsPeriod(s->getHip())));
×
657
                                variableHipStars.push_back(sa);
×
658
                                
659
                                QString vartype = getGcvsVariabilityType(s->getHip());
×
660
                                if (vartype.contains("EA"))
×
661
                                        algolTypeStars.push_back(QPair<StelObjectP, float>(so, sa.second));
×
662
                                else if (vartype.contains("DCEP") && !vartype.contains("DCEPS"))
×
663
                                        classicalCepheidsTypeStars.push_back(QPair<StelObjectP, float>(so, sa.second));
×
664
                        }
×
665
                        if (!getWdsDesignation(s->getHip()).isEmpty())
×
666
                        {
667
                                doubleHipStars.push_back(QPair<StelObjectP, float>(so, getWdsLastSeparation(s->getHip())));
×
668
                        }
669
                        float pm = s->getPMTotal();
×
670
                        if (qAbs(pm)>=pmLimit)
×
671
                        {
672
                                QPair<StelObjectP, float> spm(so, pm / 1000.f);  // in arc-seconds per year
×
673
                                hipStarsHighPM.push_back(spm);
×
674
                        }
×
675
                }
×
676
        }
677
        qInfo().nospace() << "Of " << hipparcosStars.count() << " HIP stars, " << doubleHipStars.count() << " are binaries, " <<
×
678
                   variableHipStars.count() << " variable (" << algolTypeStars.count() << " Algol-type, " << classicalCepheidsTypeStars.count() << " Cepheids), " <<
×
679
                   carbonStars.count() << " Carbon stars, " << bariumStars.count() << " Barium stars, and " << hipStarsHighPM.count() << " have PM>1000mas/yr.";
×
680

681
        //qInfo() << classicalCepheidsTypeStars;
682
}
×
683

684
// Load common names from file
685
StarMgr::CommonNames StarMgr::loadCommonNames(const QString& commonNameFile) const
×
686
{
687
        CommonNames commonNames;
×
688

689
        if (commonNameFile.isEmpty()) return commonNames;
×
690

691
        qInfo().noquote() << "Loading star names from" << QDir::toNativeSeparators(commonNameFile);
×
692
        QFile cnFile(commonNameFile);
×
693
        if (!cnFile.open(QIODevice::ReadOnly | QIODevice::Text))
×
694
        {
695
                qWarning().noquote() << "Could not open" << QDir::toNativeSeparators(commonNameFile);
×
696
                return commonNames;
×
697
        }
698

699
        int readOk=0;
×
700
        int totalRecords=0;
×
701
        int lineNumber=0;
×
702
        QString record;
×
703
        // Allow empty and comment lines where first char (after optional blanks) is #
704
        static const QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
×
705
        // record structure is delimited with a | character.  We will
706
        // use a QRegularExpression to extract the fields. with white-space padding permitted
707
        // (i.e. it will be stripped automatically) Example record strings:
708
        // "   677|_("Alpheratz")"
709
        // "113368|_("Fomalhaut")"
710
        // Note: Stellarium doesn't support sky cultures made prior to version 25.1 now!
711
        static const QRegularExpression recordRx("^\\s*(\\d+)\\s*\\|[_]*[(]\"(.*)\"[)]\\s*([\\,\\d\\s]*)");
×
712

713
        while(!cnFile.atEnd())
×
714
        {
715
                record = QString::fromUtf8(cnFile.readLine()).trimmed();
×
716
                lineNumber++;
×
717
                if (commentRx.match(record).hasMatch())
×
718
                        continue;
×
719

720
                totalRecords++;
×
721
                QRegularExpressionMatch recMatch=recordRx.match(record);
×
722
                if (!recMatch.hasMatch())
×
723
                {
724
                        qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
×
725
                                             << " - record does not match record pattern";
×
726
                        qWarning().noquote() << "Problematic record:" << record;
×
727
                        continue;
×
728
                }
729
                else
730
                {
731
                        // The record is the right format.  Extract the fields
732
                        bool ok;
733
                        StarId hip = recMatch.captured(1).toULongLong(&ok);
×
734
                        if (!ok)
×
735
                        {
736
                                qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
×
737
                                                     << " - failed to convert " << recMatch.captured(1) << "to a number";
×
738
                                continue;
×
739
                        }
740
                        QString englishCommonName = recMatch.captured(2).trimmed();
×
741
                        if (englishCommonName.isEmpty())
×
742
                        {
743
                                qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(commonNameFile)
×
744
                                                     << " - empty name field";
×
745
                                continue;
×
746
                        }
747

748
                        const QString englishNameCap = englishCommonName.toUpper();
×
749
                        commonNames.byHIP[hip] = englishCommonName;
×
750
                        commonNames.hipByName[englishNameCap] = hip;
×
751

752
                        QString reference = recMatch.captured(3).trimmed();
×
753
                        if (!reference.isEmpty())
×
754
                        {
755
                                if (referenceMap.contains(hip))
×
756
                                        referenceMap[hip] = referenceMap[hip].append("," + reference);
×
757
                                else
758
                                        referenceMap[hip] = reference;
×
759
                        }
760

761
                        readOk++;
×
762
                }
×
763
        }
×
764
        cnFile.close();
×
765

766
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "common star names";
×
767
        return commonNames;
×
768
}
×
769

770
// Identify a star HIP/Gaia StarId from its common name and the commonnames list.
771
// @param data the JSON array assigned to the "NAME commonName" entry of the current skyculture's index.json
772
// @param commonName the name given in the common_names entry in the current skyculture's index.json
773
// @param commonNamesIndexToSearchWhileLoading the part of CommonNames that provides HIPO for a given name.
774
void StarMgr::loadCultureSpecificNameForNamedObject(const QJsonArray& data, const QString& commonName,
×
775
                                                    const QMap<QString, int>& commonNamesIndexToSearchWhileLoading,
776
                                                    const QSet<int> &excludedRefs)
777
{
778
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
779

780
        const QString commonNameUpper=commonName.toUpper();
×
781
        if (!commonNamesIndexToSearchWhileLoading.contains(commonNameUpper))
×
782
        {
783
                // This may actually not even be a star, so we shouldn't emit any warning, just return
784
                return;
×
785
        }
786
        const int HIP = commonNamesIndexToSearchWhileLoading.value(commonNameUpper);
×
787

788
        for (const QJsonValue& entry : data)
×
789
        {
790
                // See if we have configured unwanted references. We can only attempt to exclude entries which actually have references.
791
                QVariantList refsVariants=entry["references"].toArray().toVariantList();
×
792
                if (!refsVariants.isEmpty())
×
793
                {
794
                        QSet<int> refs;
×
795
                        foreach(const QVariant &v, refsVariants) {
×
796
                            refs << v.value<int>();
×
797
                        }
×
798
                        if (refs.subtract(excludedRefs).isEmpty())
×
799
                                continue;
×
800
                }
×
801

802
                StelObject::CulturalName cName{entry["native"].toString(), entry["pronounce"].toString(), trans.qTranslateStar(entry["pronounce"].toString()),
×
803
                                        entry["transliteration"].toString(), entry["english"].toString(), trans.qTranslateStar(entry["english"].toString()), entry["IPA"].toString(), StelObject::CulturalNameSpecial::None};
×
804

805
                //if (culturalNamesMap.contains(HIP))
806
                //        qInfo() << "Adding additional cultural name for HIP" << HIP << ":" <<  cName.native << "/" << cName.pronounceI18n << "/" << cName.translated;
807
                culturalNamesMap.insert(HIP, cName); // add as possibly multiple entry to HIP.
×
808
                if (!cName.native.isEmpty())
×
809
                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
810
                if (!cName.pronounceI18n.isEmpty())
×
811
                        culturalNamesUppercaseIndex.insert(cName.pronounceI18n.toUpper(), HIP);
×
812
                if (!cName.transliteration.isEmpty())
×
813
                        culturalNamesUppercaseIndex.insert(cName.transliteration.toUpper(), HIP);
×
814
                if (!cName.translatedI18n.isEmpty())
×
815
                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
816
        }
×
817
}
×
818

819
// data: the array containing one or more name dicts
820
void StarMgr::loadCultureSpecificNameForStar(const QJsonArray& data, const StarId HIP, const QSet<int> &excludedRefs)
×
821
{
822
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
823

824
        for (const QJsonValue& entry : data)
×
825
        {
826
                // See if we have configured unwanted references. We can only attempt to exclude entries which actually have references.
827
                QVariantList refsVariants=entry["references"].toArray().toVariantList();
×
828
                if (!refsVariants.isEmpty())
×
829
                {
830
                        QSet<int> refs;
×
831
                        foreach(const QVariant &v, refsVariants) {
×
832
                            refs << v.value<int>();
×
833
                        }
×
834
                        //qInfo() << "Star" << entry["native"].toString() << "has refs" << refs;
835
                        if (refs.subtract(excludedRefs).isEmpty())
×
836
                        {
837
                                //qInfo() << "Star name" << entry["native"].toString() << "has lost support. Skipping.";
838
                                continue;
×
839
                        }
840
                        //else
841
                        //        qInfo() << "Star" << entry["native"].toString() << "still has refs" << refs.subtract(excludedRefs);
842
                }
×
843

844
                StelObject::CulturalName cName{entry["native"].toString(), entry["pronounce"].toString(), trans.qTranslateStar(entry["pronounce"].toString()),
×
845
                                        entry["transliteration"].toString(), entry["english"].toString(), trans.qTranslateStar(entry["english"].toString()), entry["IPA"].toString()};
×
846
                //if (culturalNamesMap.contains(HIP))
847
                //        qInfo() << "Adding additional cultural name for HIP" << HIP << ":" <<  cName.native << "/" << cName.pronounceI18n << "/" << cName.translated << "/" << cName.translatedI18n;
848
                culturalNamesMap.insert(HIP, cName); // add as possibly multiple entry to HIP.
×
849
                if (!cName.native.isEmpty())
×
850
                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
851
                if (!cName.pronounceI18n.isEmpty())
×
852
                        culturalNamesUppercaseIndex.insert(cName.pronounceI18n.toUpper(), HIP);
×
853
                if (!cName.transliteration.isEmpty())
×
854
                        culturalNamesUppercaseIndex.insert(cName.transliteration.toUpper(), HIP);
×
855
                if (!cName.translatedI18n.isEmpty())
×
856
                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
857
        }
×
858
        //qInfo() << "Skyculture has " << culturalNamesMap.size() << "entries, index has" << culturalNamesIndex.size();
859
}
×
860

861
void StarMgr::loadCultureSpecificNames(const QJsonObject& data, const QMap<QString, int>& commonNamesIndexToSearchWhileLoading, const QSet<int> &excludedRefs)
×
862
{
863
        for (auto it = data.begin(); it != data.end(); ++it)
×
864
        {
865
                const auto key = it.key();
×
866
                // Let's allow Hipparcos and Gaia designations only
867
                if (key.startsWith("HIP "))
×
868
                {
869
                        loadCultureSpecificNameForStar(it.value().toArray(), key.mid(4).toUInt(), excludedRefs);
×
870
                }
871
                else if (key.startsWith("Gaia DR3 "))
×
872
                {
873
                        loadCultureSpecificNameForStar(it.value().toArray(), key.mid(9).toULongLong(), excludedRefs);
×
874
                }
875
                else if (key.startsWith("NAME "))
×
876
                {
877
                        loadCultureSpecificNameForNamedObject(it.value().toArray(), key.mid(5), commonNamesIndexToSearchWhileLoading, excludedRefs);
×
878
                }
879
        }
×
880
}
×
881

882
// Load scientific designations from file
883
void StarMgr::loadSciDesignations(const QString& sciNameFile, QHash<StarId, QString>&map, QHash<QString, StarId>&index)
×
884
{
885
        map.clear();
×
886
        index.clear();
×
887
        qInfo().noquote() << "Loading scientific star designations from" << QDir::toNativeSeparators(sciNameFile);
×
888

889
        QFile snFile(sciNameFile);
×
890
        if (!snFile.open(QIODevice::ReadOnly | QIODevice::Text))
×
891
        {
892
                qWarning().noquote() << "Could not open" << QDir::toNativeSeparators(sciNameFile);
×
893
                return;
×
894
        }
895
        const QStringList& allRecords = QString::fromUtf8(snFile.readAll()).split('\n');
×
896
        snFile.close();
×
897

898
        int readOk=0;
×
899
        int totalRecords=0;
×
900
        int lineNumber=0;
×
901
        // record structure is delimited with a | character. Example record strings:
902
        // " 10819|c_And"
903
        // "113726|1_And"
904
        for (const QString& record : allRecords)
×
905
        {
906
                ++lineNumber;
×
907
                // skip comments and empty lines
908
                if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
×
909
                        continue;
×
910

911
                ++totalRecords;
×
912
                const QStringList& fields = record.split('|');
×
913
                if (fields.size()!=2)
×
914
                {
915
                        qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
×
916
                                             << " - record does not match record pattern. Skipping line.";
×
917
                        continue;
×
918
                }
919
                else
920
                {
921
                        // The record is the right format.  Extract the fields
922
                        bool ok;
923
                        StarId hip = fields.at(0).toLongLong(&ok);
×
924
                        if (!ok)
×
925
                        {
926
                                qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
×
927
                                                     << " - failed to convert " << fields.at(0) << "to a number";
×
928
                                continue;
×
929
                        }
930

931
                        QString sci_name = fields.at(1).trimmed();
×
932
                        if (sci_name.isEmpty())
×
933
                        {
934
                                qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(sciNameFile)
×
935
                                                     << " - empty name field";
×
936
                                continue;
×
937
                        }
938

939
                        sci_name.replace('_',' ');
×
940
                        // Don't set the main sci name if it's already set - it's additional sci name
941
                        if (map.contains(hip))
×
942
                        {
943
                                map[hip].append(" - " + sci_name);
×
944
                        }
945
                        else
946
                                map.insert(hip, sci_name);
×
947
                        index[sci_name] = hip;
×
948

949
                        ++readOk;
×
950
                }
×
951
        }
×
952
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "scientific designations";
×
953
}
×
954

955
// Load GCVS from file
956
void StarMgr::loadGcvs(const QString& GcvsFileName)
×
957
{
958
        varStarsMap.clear();
×
959
        varStarsIndex.clear();
×
960

961
        qInfo().noquote() << "Loading variable stars data from" << QDir::toNativeSeparators(GcvsFileName);
×
962

963
        QFile vsFile(GcvsFileName);
×
964
        if (!vsFile.open(QIODevice::ReadOnly))
×
965
        {
966
                qDebug().noquote() << "Cannot open file" << QDir::toNativeSeparators(GcvsFileName);
×
967
                return;
×
968
        }
969
        QByteArray data = StelUtils::uncompress(vsFile);
×
970
        vsFile.close();
×
971
        //check if decompressing was successful
972
        if(data.isEmpty())
×
973
        {
974
                qDebug().noquote() << "Could not decompress file" << QDir::toNativeSeparators(GcvsFileName);
×
975
                return;
×
976
        }
977
        //create and open a QBuffer for reading
978
        QBuffer buf(&data);
×
979
        buf.open(QIODevice::ReadOnly);
×
980

981
        int readOk=0;
×
982
        int totalRecords=0;
×
983
        int lineNumber=0;
×
984
        // Version of GCVS catalog
985
        static const QRegularExpression versionRx("\\s*Version:\\s*([\\d\\-\\.]+)\\s*");
×
986

987
        // record structure is delimited with a tab character.
988
        while (!buf.atEnd())
×
989
        {
990
                QString record = QString::fromUtf8(buf.readLine());
×
991
                lineNumber++;
×
992

993
                // skip comments and empty lines
994
                if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
×
995
                {
996
                        QRegularExpressionMatch versionMatch=versionRx.match(record);
×
997
                        if (versionMatch.hasMatch())
×
998
                                qInfo().noquote() << "[...]" << QString("GCVS %1").arg(versionMatch.captured(1).trimmed());
×
999
                        continue;
×
1000
                }
×
1001

1002
                totalRecords++;
×
1003
                const QStringList& fields = record.split('\t');
×
1004

1005
                bool ok;
1006
                StarId hip = fields.at(0).toLongLong(&ok);
×
1007
                if (!ok)
×
1008
                        continue;
×
1009

1010
                // Don't set the star if it's already set
1011
                if (varStarsMap.contains(hip))
×
1012
                        continue;
×
1013

1014
                varstar variableStar;
×
1015

1016
                variableStar.designation = fields.at(1).trimmed();
×
1017
                variableStar.vtype = fields.at(2).trimmed();
×
1018
                variableStar.maxmag = fields.at(3).isEmpty() ? 99.f : fields.at(3).toFloat();
×
1019
                variableStar.mflag = fields.at(4).toInt();
×
1020
                variableStar.min1mag = fields.at(5).isEmpty() ? 99.f : fields.at(5).toFloat();
×
1021
                variableStar.min2mag = fields.at(6).isEmpty() ? 99.f : fields.at(6).toFloat();
×
1022
                variableStar.photosys = fields.at(7).trimmed();
×
1023
                variableStar.epoch = fields.at(8).toDouble();
×
1024
                variableStar.period = fields.at(9).toDouble();
×
1025
                variableStar.Mm = fields.at(10).toInt();
×
1026
                variableStar.stype = fields.at(11).trimmed();
×
1027

1028
                varStarsMap[hip] = variableStar;
×
1029
                varStarsIndex[variableStar.designation.toUpper()] = hip;
×
1030
                ++readOk;
×
1031
        }
×
1032

1033
        buf.close();
×
1034
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "variable stars";
×
1035
}
×
1036

1037
// Load WDS from file
1038
void StarMgr::loadWds(const QString& WdsFileName)
×
1039
{
1040
        wdsStarsMap.clear();
×
1041
        wdsStarsIndex.clear();
×
1042

1043
        qInfo().noquote() << "Loading double stars from" << QDir::toNativeSeparators(WdsFileName);
×
1044
        QFile dsFile(WdsFileName);
×
1045
        if (!dsFile.open(QIODevice::ReadOnly | QIODevice::Text))
×
1046
        {
1047
                qWarning().noquote() << "Could not open" << QDir::toNativeSeparators(WdsFileName);
×
1048
                return;
×
1049
        }
1050
        const QStringList& allRecords = QString::fromUtf8(dsFile.readAll()).split('\n');
×
1051
        dsFile.close();
×
1052

1053
        int readOk=0;
×
1054
        int totalRecords=0;
×
1055
        int lineNumber=0;
×
1056

1057
        // record structure is delimited with a tab character.
1058
        for (const auto& record : allRecords)
×
1059
        {
1060
                ++lineNumber;
×
1061
                // skip comments and empty lines
1062
                if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
×
1063
                        continue;
×
1064

1065
                ++totalRecords;
×
1066
                const QStringList& fields = record.split('\t');
×
1067

1068
                bool ok;
1069
                StarId hip = fields.at(0).toLongLong(&ok);
×
1070
                if (!ok)
×
1071
                {
1072
                        qWarning() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(WdsFileName)
×
1073
                                   << " - failed to convert " << fields.at(0) << "to a number";
×
1074
                        continue;
×
1075
                }
1076

1077
                // Don't set the star if it's already set
1078
                if (wdsStarsMap.contains(hip))
×
1079
                {
1080
                        qWarning() << "HIP" << hip << "already processed. Ignoring record:" << record;
×
1081
                        continue;
×
1082
                }
1083

1084
                wds doubleStar = {fields.at(1).trimmed(), fields.at(2).toInt(), fields.at(3).toFloat(),fields.at(4).toFloat()};
×
1085
                wdsStarsMap[hip] = doubleStar;
×
1086
                wdsStarsIndex[QString("WDS J%1").arg(doubleStar.designation.toUpper())] = hip;
×
1087
                ++readOk;
×
1088
        }
×
1089

1090
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "double stars";
×
1091
}
×
1092

1093
// Load cross-identification data from file
1094
void StarMgr::loadCrossIdentificationData(const QString& crossIdFile)
×
1095
{
1096
        crossIdMap.clear();
×
1097
        saoStarsIndex.clear();        
×
1098
        hdStarsIndex.clear();        
×
1099
        hrStarsIndex.clear();
×
1100

1101
        qInfo().noquote() << "Loading cross-identification data from" << QDir::toNativeSeparators(crossIdFile);
×
1102
        QFile ciFile(crossIdFile);
×
1103
        if (!ciFile.open(QIODevice::ReadOnly))
×
1104
        {
1105
                qWarning().noquote() << "Could not open" << QDir::toNativeSeparators(crossIdFile);
×
1106
                return;
×
1107
        }
1108

1109
        QDataStream in(&ciFile);
×
1110
        in.setByteOrder(QDataStream::LittleEndian);
×
1111

1112
        crossid crossIdData;
1113

1114
        int readOk = 0;
×
1115
        int totalRecords = 0;
×
1116
        StarId hip;
1117
        unsigned char component;
1118
        int sao, hd, hr;
1119
        QString hipstar;
×
1120
        quint64 hipTemp;
1121

1122
        while (!in.atEnd())
×
1123
        {
1124
                ++totalRecords;
×
1125

1126
                in >> hipTemp >> component >> sao >> hd >> hr;
×
1127
                hip = static_cast<StarId>(hipTemp);
×
1128

1129
                if (in.status() != QDataStream::Ok)
×
1130
                {
1131
                        qWarning().noquote() << "Parse error in" << QDir::toNativeSeparators(crossIdFile)
×
1132
                                                 << " - failed to read data";
×
1133
                        break;
×
1134
                }
1135

1136
                hipstar = QString("%1%2").arg(hip).arg(component == 0 ? QString() : QString(QChar('A' + component - 1)));
×
1137
                crossIdData.sao = sao;
×
1138
                crossIdData.hd = hd;
×
1139
                crossIdData.hr = hr;
×
1140

1141
                crossIdMap[hipstar] = crossIdData;
×
1142
                if (crossIdData.sao > 0)
×
1143
                        saoStarsIndex[crossIdData.sao] = hip;
×
1144
                if (crossIdData.hd > 0)
×
1145
                        hdStarsIndex[crossIdData.hd] = hip;
×
1146
                if (crossIdData.hr > 0)
×
1147
                        hrStarsIndex[crossIdData.hr] = hip;
×
1148

1149
                ++readOk;
×
1150
        }
1151

1152
        ciFile.close();
×
1153
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "cross-identification data records for stars";
×
1154
}
×
1155

1156
// load binary system orbital parameters data
1157
void StarMgr::loadBinaryOrbitalData(const QString& orbitalParamFile)
×
1158
{
1159
        qInfo().noquote() << "Loading orbital parameters data for binary system from" << QDir::toNativeSeparators(orbitalParamFile);
×
1160
        QFile opFile(orbitalParamFile);
×
1161
        if (!opFile.open(QIODevice::ReadOnly | QIODevice::Text))
×
1162
        {
1163
                qWarning().noquote() << "Could not open" << QDir::toNativeSeparators(orbitalParamFile);
×
1164
                return;
×
1165
        }
1166
        const QStringList& allRecords = QString::fromUtf8(opFile.readAll()).split('\n');
×
1167
        opFile.close();
×
1168

1169
        binaryorbitstar orbitalData;
1170

1171
        int readOk=0;
×
1172
        int totalRecords=0;
×
1173
        int lineNumber=0;
×
1174
        StarId hip;
1175
        for (const auto& record : allRecords)
×
1176
        {
1177
                ++lineNumber;
×
1178
                // skip comments and empty lines
1179
                if (record.startsWith("//") || record.startsWith("#") || record.isEmpty())
×
1180
                        continue;
×
1181

1182
                ++totalRecords;
×
1183
                const QStringList& fields = record.split('\t');
×
1184
                if (fields.size()!=18)
×
1185
                {
1186
                        qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(orbitalParamFile)
×
1187
                                             << " - record does not match record pattern";
×
1188
                        continue;
×
1189
                }
1190
                else
1191
                {
1192
                        // The record is the right format.  Extract the fields
1193
                        bool ok, allOK=true;
×
1194
                        orbitalData.binary_period    = fields.at(2).toDouble(&ok);  allOK &= ok;
×
1195
                        orbitalData.eccentricity     = fields.at(3).toFloat(&ok);   allOK &= ok;
×
1196
                        orbitalData.inclination      = fields.at(4).toFloat(&ok);   allOK &= ok;
×
1197
                        orbitalData.big_omega        = fields.at(5).toFloat(&ok);   allOK &= ok;
×
1198
                        orbitalData.small_omega      = fields.at(6).toFloat(&ok);   allOK &= ok;
×
1199
                        orbitalData.periastron_epoch = fields.at(7).toDouble(&ok);  allOK &= ok;
×
1200
                        orbitalData.semi_major       = fields.at(8).toDouble(&ok);  allOK &= ok;
×
1201
                        orbitalData.bary_distance    = fields.at(9).toDouble(&ok);  allOK &= ok;
×
1202
                        orbitalData.data_epoch       = fields.at(10).toDouble(&ok); allOK &= ok;
×
1203
                        orbitalData.bary_ra          = fields.at(11).toDouble(&ok); allOK &= ok;
×
1204
                        orbitalData.bary_dec         = fields.at(12).toDouble(&ok); allOK &= ok;
×
1205
                        orbitalData.bary_rv          = fields.at(13).toDouble(&ok); allOK &= ok;
×
1206
                        orbitalData.bary_pmra        = fields.at(14).toDouble(&ok); allOK &= ok;
×
1207
                        orbitalData.bary_pmdec       = fields.at(15).toDouble(&ok); allOK &= ok;
×
1208
                        orbitalData.primary_mass     = fields.at(16).toFloat(&ok);  allOK &= ok;
×
1209
                        orbitalData.secondary_mass   = fields.at(17).toFloat(&ok);  allOK &= ok;
×
1210
                        // do primary star
1211
                        hip = fields.at(0).toLongLong(&ok); allOK &= ok;
×
1212
                        orbitalData.hip = hip;
×
1213
                        orbitalData.primary = true;
×
1214
                        binaryOrbitStarMap[hip] = orbitalData;
×
1215
                        // do secondary star
1216
                        hip = fields.at(1).toLongLong(&ok); allOK &= ok;
×
1217
                        orbitalData.hip = hip;
×
1218
                        orbitalData.primary = false;
×
1219
                        binaryOrbitStarMap[hip] = orbitalData;
×
1220

1221
                        if (!allOK)
×
1222
                        {
1223
                                qWarning().noquote() << "Parse error at line" << lineNumber << "in" << QDir::toNativeSeparators(orbitalParamFile)
×
1224
                                                     << " - failed to convert " << fields.at(0) << "to a number";
×
1225
                                continue;
×
1226
                        }
1227
                        ++readOk;
×
1228
                }
1229
        }
×
1230

1231
        qInfo().noquote() << "Loaded" << readOk << "/" << totalRecords << "orbital parameters data for binary system";
×
1232
}
×
1233

1234
int StarMgr::getMaxSearchLevel() const
×
1235
{
1236
        int rval = -1;
×
1237
        for (const auto* z : gridLevels)
×
1238
        {
1239
                const float mag_min = 0.001f*z->mag_min;
×
1240
                RCMag rcmag;
1241
                if (StelApp::getInstance().getCore()->getSkyDrawer()->computeRCMag(mag_min, &rcmag)==false)
×
1242
                        break;
×
1243
                rval = z->level;
×
1244
        }
1245
        return rval;
×
1246
}
1247

1248

1249
// Draw all the stars
1250
void StarMgr::draw(StelCore* core)
×
1251
{
1252
        const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
×
1253
        StelSkyDrawer* skyDrawer = core->getSkyDrawer();
×
1254
        // If stars are turned off don't waste time below
1255
        // projecting all stars just to draw disembodied labels
1256
        if (!static_cast<bool>(starsFader.getInterstate()))
×
1257
                return;
×
1258

1259
        static StelSkyCultureMgr* scMgr = GETSTELMODULE(StelSkyCultureMgr);
×
1260
        const bool currentSkycultureUsesCommonStarnames=scMgr->getFlagOverrideUseCommonNames() ||
×
1261
                                                        scMgr->currentSkycultureUsesCommonNames();
×
1262

1263
        int maxSearchLevel = getMaxSearchLevel();
×
1264
        double margin = 0.;  // pixel margin to be applied to viewport convex polygon
×
1265
        if (core->getSkyDrawer()->getFlagHasAtmosphere())
×
1266
        {
1267
                // Saemundsson's inversion formula for atmospheric refraction is not exact, so need some padding in terms of arcseconds
1268
                margin = 30000. * MAS2RAD * prj->getPixelPerRadAtCenter();  // 0.5 arcmin
×
1269
        }
1270
        
1271
        QVector<SphericalCap> viewportCaps = prj->getViewportConvexPolygon(margin, margin)->getBoundingSphericalCaps();
×
1272
        viewportCaps.append(core->getVisibleSkyArea());
×
1273
        const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(maxSearchLevel)->search(viewportCaps,maxSearchLevel);
×
1274

1275
        // Set temporary static variable for optimization
1276
        const float names_brightness = labelsFader.getInterstate() * starsFader.getInterstate();
×
1277

1278
        // prepare for aberration: Explan. Suppl. 2013, (7.38)
1279
        const bool withAberration=core->getUseAberration();
×
1280
        Vec3d vel(0.);
×
1281
        if (withAberration)
×
1282
        {
1283
                vel = core->getAberrationVec(core->getJDE());
×
1284
        }
1285

1286
        // Prepare openGL for drawing many stars
1287
        StelPainter sPainter(prj);
×
1288
        sPainter.setFont(starFont);
×
1289
        skyDrawer->preDrawPointSource(&sPainter);
×
1290

1291
        // Prepare a table for storing precomputed RCMag for all ZoneArrays
1292
        RCMag rcmag_table[RCMAG_TABLE_SIZE];
1293
        
1294
        // Draw all the stars of all the selected zones
1295
        for (const auto* z : std::as_const(gridLevels))
×
1296
        {
1297
                int limitMagIndex=RCMAG_TABLE_SIZE;
×
1298
                // overshoot by 7 mag because some stars (like HIP 16757) can get brighter than its catalogs min magnitude
1299
                const float mag_min = 0.001f*z->mag_min - 7.f;
×
1300
                for (int i=0;i<RCMAG_TABLE_SIZE;++i)
×
1301
                {
1302
                        const float mag = mag_min+0.05*i;  // 0.05 mag MagStepIncrement
×
1303
                        if (skyDrawer->computeRCMag(mag, &rcmag_table[i])==false)
×
1304
                        {
1305
                                if (i==0)
×
1306
                                        goto exit_loop;
×
1307
                                
1308
                                // The last magnitude at which the star is visible
1309
                                limitMagIndex = i-1;
×
1310
                                
1311
                                // We reached the point where stars are not visible anymore
1312
                                // Fill the rest of the table with zero and leave.
1313
                                for (;i<RCMAG_TABLE_SIZE;++i)
×
1314
                                {
1315
                                        rcmag_table[i].luminance=0;
×
1316
                                        rcmag_table[i].radius=0;
×
1317
                                }
1318
                                break;
×
1319
                        }
1320
                        rcmag_table[i].radius *= starsFader.getInterstate();
×
1321
                }
1322
                lastMaxSearchLevel = z->level;
×
1323

1324
                int maxMagStarName = 0;
×
1325
                if (labelsFader.getInterstate()>0.f)
×
1326
                {
1327
                        // Adapt magnitude limit of the stars labels according to FOV and labelsAmount
1328
                        float maxMag = (skyDrawer->getLimitMagnitude()-6.5f)*0.7f+(static_cast<float>(labelsAmount)*1.2f)-2.f;
×
1329
                        int x = static_cast<int>((maxMag-mag_min)/0.05);  // 0.05 mag MagStepIncrement
×
1330
                        if (x > 0)
×
1331
                                maxMagStarName = x;
×
1332
                }
1333
                int zone;
1334
                double withParallax = core->getUseParallax() * core->getParallaxFactor();
×
1335
                Vec3d diffPos(0., 0., 0.);
×
1336
                if (withParallax) {
×
1337
                        diffPos = core->getParallaxDiff(core->getJDE());
×
1338
                }
1339
                for (GeodesicSearchInsideIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
×
1340
                        z->draw(&sPainter, zone, true, rcmag_table, limitMagIndex, core, maxMagStarName, names_brightness, viewportCaps, withAberration, vel, withParallax, diffPos, currentSkycultureUsesCommonStarnames);
×
1341
                for (GeodesicSearchBorderIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
×
1342
                        z->draw(&sPainter, zone, false, rcmag_table, limitMagIndex, core, maxMagStarName,names_brightness, viewportCaps, withAberration, vel, withParallax, diffPos, currentSkycultureUsesCommonStarnames);
×
1343
                // always check the last zone because it is a global zone
1344
                z->draw(&sPainter, (20<<(z->level<<1)), false, rcmag_table, limitMagIndex, core, maxMagStarName, names_brightness, viewportCaps, withAberration, vel, withParallax, diffPos, currentSkycultureUsesCommonStarnames);
×
1345
        }
1346
        exit_loop:
×
1347

1348
        // Finish drawing many stars
1349
        skyDrawer->postDrawPointSource(&sPainter);
×
1350

1351
        if (objectMgr->getFlagSelectedObjectPointer())
×
1352
                drawPointer(sPainter, core);
×
1353
}
×
1354

1355

1356
// Return a QList containing the stars located
1357
// inside the limFov circle around position vv (in J2000 frame without aberration)
1358
QList<StelObjectP > StarMgr::searchAround(const Vec3d& vv, double limFov, const StelCore* core) const
×
1359
{
1360
        QList<StelObjectP > result;
×
1361
        if (!getFlagStars())
×
1362
                return result;
×
1363

1364
        Vec3d v(vv);
×
1365
        v.normalize();
×
1366

1367
        // find any vectors h0 and h1 (length 1), so that h0*v=h1*v=h0*h1=0
1368
        int i;
1369
        {
1370
                const double a0 = fabs(v[0]);
×
1371
                const double a1 = fabs(v[1]);
×
1372
                const double a2 = fabs(v[2]);
×
1373
                if (a0 <= a1)
×
1374
                {
1375
                        if (a0 <= a2) i = 0;
×
1376
                        else i = 2;
×
1377
                } else
1378
                {
1379
                        if (a1 <= a2) i = 1;
×
1380
                        else i = 2;
×
1381
                }
1382
        }
1383
        Vec3d h0(0.0,0.0,0.0);
×
1384
        h0[i] = 1.0;
×
1385
        Vec3d h1 = h0 ^ v;
×
1386
        h1.normalize();
×
1387
        h0 = h1 ^ v;
×
1388
        h0.normalize();
×
1389

1390
        // Now we have h0*v=h1*v=h0*h1=0.
1391
        // Construct a region with 4 corners e0,e1,e2,e3 inside which all desired stars must be:
1392
        double f = 1.4142136 * tan(limFov * M_PI_180);
×
1393
        h0 *= f;
×
1394
        h1 *= f;
×
1395
        Vec3d e0 = v + h0;
×
1396
        Vec3d e1 = v + h1;
×
1397
        Vec3d e2 = v - h0;
×
1398
        Vec3d e3 = v - h1;
×
1399
        f = 1.0/e0.norm();
×
1400
        e0 *= f;
×
1401
        e1 *= f;
×
1402
        e2 *= f;
×
1403
        e3 *= f;
×
1404
        // Search the triangles
1405
        SphericalConvexPolygon c(e3, e2, e2, e0);
×
1406
        const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(lastMaxSearchLevel)->search(c.getBoundingSphericalCaps(),lastMaxSearchLevel);
×
1407

1408
        double withParallax = core->getUseParallax() * core->getParallaxFactor();
×
1409
        Vec3d diffPos(0., 0., 0.);
×
1410
        if (withParallax) {
×
1411
                diffPos = core->getParallaxDiff(core->getJDE());
×
1412
        }
1413

1414
        // Iterate over the stars inside the triangles
1415
        f = cos(limFov * M_PI/180.);
×
1416
        for (auto* z : gridLevels)
×
1417
        {
1418
                //qDebug() << "search inside(" << it->first << "):";
1419
                int zone;
1420
                for (GeodesicSearchInsideIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
×
1421
                {
1422
                        z->searchAround(core, zone, v, withParallax, diffPos, f, result);
×
1423
                        //qDebug() << " " << zone;
1424
                }
1425
                //qDebug() << StelUtils::getEndLineChar() << "search border(" << it->first << "):";
1426
                for (GeodesicSearchBorderIterator it1(*geodesic_search_result,z->level); (zone = it1.next()) >= 0;)
×
1427
                {
1428
                        z->searchAround(core, zone, v, withParallax, diffPos, f, result);
×
1429
                        //qDebug() << " " << zone;
1430
                }
1431
                // always search the last zone because it is a global zone
1432
                z->searchAround(core, (20<<(z->level<<1)), v, withParallax, diffPos, f, result);
×
1433
        }
1434
        return result;
×
1435
}
×
1436

1437
// Return a QList containing the stars located within region (in J2000 frame without aberration)
1438
// Debugging info is available in a debug build. The reason for the minimum opening angle in StelCore::getGeodesicGrid() is still a bit unclear.
1439
QList<StelObjectP > StarMgr::searchWithin(const SphericalRegionP region, const StelCore* core, const bool hipOnly, const float maxMag) const
×
1440
{
1441
        QList<StelObjectP> result;
×
1442
        if (!getFlagStars())
×
1443
                return result;
×
1444

1445
        // For unidentified reasons, the geodesic search result is empty when the cap used in search (below) has d>0.83.
1446
        QVector<SphericalCap> caps=region->getBoundingSphericalCaps();
×
1447
        QVector<SphericalCap> largerCaps;
×
1448
        foreach (auto &cap, caps)
×
1449
        {
1450
#ifndef NDEBUG
1451
                qDebug() << "Cap: " << cap.n << cap.d;
×
1452
#endif
1453
                largerCaps.append(SphericalCap(cap.n, qMin(cap.d, 0.75))); // 0.83 seemed still too small, unclear why. 0.75 seems to work.
×
1454
        }
×
1455
        const GeodesicSearchResult* geodesic_search_result = core->getGeodesicGrid(maxGeodesicGridLevel)->search(largerCaps,maxGeodesicGridLevel);
×
1456

1457
#ifndef NDEBUG
1458
        // Just some temporary debug output.
1459
        geodesic_search_result->print();
×
1460
#endif
1461
        // prepare for aberration: Explan. Suppl. 2013, (7.38)
1462
        const bool withAberration=core->getUseAberration();
×
1463
        Vec3d vel(0.);
×
1464
        if (withAberration)
×
1465
        {
1466
                vel = core->getAberrationVec(core->getJDE());
×
1467
        }
1468

1469
#ifndef NDEBUG
1470
        qDebug() << "We have" << gridLevels.count() << " ZoneArrays in gridLevels at maxGeodesicGridLevel:" << maxGeodesicGridLevel;
×
1471
#endif
1472
        // Draw all the stars of all the selected zones
1473
        for (const  auto* z : std::as_const(gridLevels))
×
1474
        {
1475
                if (hipOnly && z->level>3) // There are no hip numbers after level 3.
×
1476
                {
1477
#ifndef NDEBUG
1478
                        qDebug() << "StarMgr::searchWithin(): Skip ZoneArray with level" << z->level << "(" << z->fname << ")";
×
1479
#endif
1480
                        continue;
×
1481
                }
1482
#ifndef NDEBUG
1483
                qDebug() << "Z level=" << z->level << "mag_min=" << z->mag_min;
×
1484
#endif
1485
                int zone;
1486
                double withParallax = core->getUseParallax() * core->getParallaxFactor();
×
1487
                Vec3d diffPos(0., 0., 0.);
×
1488
                if (withParallax) {
×
1489
                        diffPos = core->getParallaxDiff(core->getJDE());
×
1490
                }
1491

1492
                for (GeodesicSearchInsideIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
×
1493
                {
1494
#ifndef NDEBUG
1495
                        qDebug() << "Inside: Zone z->fname:" << z->fname << "Level z=" << z->level << "zone=" << zone;
×
1496
#endif
1497
                        z->searchWithin(core, zone, region, withParallax, diffPos, hipOnly, maxMag, result);
×
1498
                }
1499
                for (GeodesicSearchBorderIterator it1(*geodesic_search_result,z->level);(zone = it1.next()) >= 0;)
×
1500
                {
1501
#ifndef NDEBUG
1502
                        qDebug() << "Border: Zone z->fname:" << z->fname << "Level z=" << z->level << "zone=" << zone;
×
1503
#endif
1504
                        z->searchWithin(core, zone, region, withParallax, diffPos, hipOnly, maxMag, result);
×
1505
                }
1506
                // always check the last zone because it is a global zone
1507
#ifndef NDEBUG
1508
                qDebug() << "Global 20<<(z->level<<1)=" << (20<<(z->level<<1));
×
1509
#endif
1510
                z->searchWithin(core, (20<<(z->level<<1)), region, withParallax, diffPos, hipOnly, maxMag, result);
×
1511
        }
1512

1513
#ifndef NDEBUG
1514
        qInfo() << "Region contains" << result.length() << "entries";
×
1515
#endif
1516
        return result;
×
1517
}
×
1518

1519

1520

1521
//! Update i18 names from english names according to passed translator.
1522
//! The translation is done using gettext with translated strings defined in translations.h
1523
void StarMgr::updateI18n()
×
1524
{
1525
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
1526
        commonNamesI18nMap.clear();
×
1527
        commonNamesI18nUppercaseIndex.clear();
×
1528
        for (QHash<StarId,QString>::ConstIterator it(commonNamesMap.constBegin());it!=commonNamesMap.constEnd();it++)
×
1529
        {
1530
                const StarId i = it.key();
×
1531
                const QString t(trans.qTranslateStar(it.value()));
×
1532
                commonNamesI18nMap[i] = t;
×
1533
                commonNamesI18nUppercaseIndex[t.toUpper()] = i;
×
1534
        }
×
1535
        culturalNamesUppercaseIndex.clear();
×
1536
        for (QMultiHash<StarId,StelObject::CulturalName>::iterator it(culturalNamesMap.begin());it!=culturalNamesMap.end();++it)
×
1537
        {
1538
                StarId HIP=it.key();
×
1539
                StelObject::CulturalName &cName=it.value();
×
1540
                cName.pronounceI18n=trans.qTranslateStar(cName.pronounce);
×
1541
                cName.translatedI18n=trans.qTranslateStar(cName.translated);
×
1542
                it.value() = cName;
×
1543

1544
                // rebuild index
1545
                if (!cName.native.isEmpty())
×
1546
                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
1547
                if (!cName.pronounceI18n.isEmpty())
×
1548
                        culturalNamesUppercaseIndex.insert(cName.pronounceI18n.toUpper(), HIP);
×
1549
                if (!cName.transliteration.isEmpty())
×
1550
                        culturalNamesUppercaseIndex.insert(cName.transliteration.toUpper(), HIP);
×
1551
                if (!cName.translatedI18n.isEmpty())
×
1552
                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
1553
        }
1554
}
×
1555

1556
// Search the star by HP number
1557
StelObjectP StarMgr::searchHP(int hp) const
×
1558
{
1559
        if (0 < hp && hp <= NR_OF_HIP)
×
1560
        {
1561
                const Star1 *const s = hipIndex[hp].s;
×
1562
                if (s)
×
1563
                {
1564
                        const SpecialZoneArray<Star1> *const a = hipIndex[hp].a;
×
1565
                        const SpecialZoneData<Star1> *const z = hipIndex[hp].z;
×
1566
                        return s->createStelObject(a,z);
×
1567
                }
1568
        }
1569
        return StelObjectP();
×
1570
}
1571

1572
// Search the star by Gaia source_id
1573
StelObjectP StarMgr::searchGaia(StarId source_id) const
×
1574
{
1575
        int maxSearchLevel = getMaxSearchLevel();
×
1576
        int matched = 0;
×
1577
        int index = 0;
×
1578
        // get the level 12 HEALPix index of the source
1579
        int lv12_pix = source_id / 34359738368;
×
1580
        Vec3d v;
×
1581
        StelObjectP so;
×
1582
        healpix_pix2vec(pow(2, 12), lv12_pix, v.v);  // search which pixel the source is in and turn to coordinates
×
1583
        Vec3f vf = v.toVec3f();
×
1584

1585
        for (const auto* z : gridLevels)
×
1586
        {
1587
                // search the zone where the source is in
1588
                index = StelApp::getInstance().getCore()->getGeodesicGrid(maxSearchLevel)->getZoneNumberForPoint(vf, z->level);
×
1589
                so = z->searchGaiaID(index, source_id, matched);
×
1590
                if (matched)
×
1591
                        return so;
×
1592
                
1593
                // then search the global zone 
1594
                so = z->searchGaiaID((20<<(z->level<<1)), source_id, matched);
×
1595
                if (matched)
×
1596
                        return so;
×
1597
        }
1598
        return StelObjectP();
×
1599
}
×
1600

1601
StelObjectP StarMgr::searchByNameI18n(const QString& nameI18n) const
×
1602
{
1603
        QString nameI18nUpper = nameI18n.toUpper();
×
1604

1605
        // Search by I18n common name
1606
        if (commonNamesI18nUppercaseIndex.contains(nameI18nUpper))
×
1607
                return searchHP(commonNamesI18nUppercaseIndex.value(nameI18nUpper));
×
1608

1609
        // Search by cultural names? (Any names: native/pronounceI18n/translatedI18n/transliteration
1610
        if (getFlagAdditionalNames() && culturalNamesUppercaseIndex.contains(nameI18nUpper))
×
1611
        {
1612
                // This only returns the first-found number.
1613
                StarId starId=culturalNamesUppercaseIndex.value(nameI18nUpper);
×
1614
                return (starId <= NR_OF_HIP ? searchHP(starId) : searchGaia(starId));
×
1615
        }
1616

1617
        return searchByName(nameI18n);
×
1618
}
×
1619

1620
StelObjectP StarMgr::searchByName(const QString& name) const
×
1621
{
1622
        QString nameUpper = name.toUpper();
×
1623
        StarId sid;
1624

1625
        // Search by HP number if it's an HP formatted number. The final part (A/B/...) is ignored
1626
        static const QRegularExpression rx("^\\s*(HP|HIP)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
1627
        QRegularExpressionMatch match=rx.match(nameUpper);
×
1628
        if (match.hasMatch())
×
1629
                return searchHP(match.captured(2).toInt());
×
1630

1631
        // Search by SAO number if it's an SAO formatted number
1632
        static const QRegularExpression rx2("^\\s*(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1633
        match=rx2.match(nameUpper);
×
1634
        if (match.hasMatch())
×
1635
        {
1636
                const int saoIndex=match.captured(2).toInt();
×
1637
                if (saoStarsIndex.contains(saoIndex))
×
1638
                {
1639
                        sid = saoStarsIndex.value(saoIndex);
×
1640
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1641
                }
1642
        }
1643

1644
        // Search by HD number if it's an HD formatted number
1645
        static const QRegularExpression rx3("^\\s*(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1646
        match=rx3.match(nameUpper);
×
1647
        if (match.hasMatch())
×
1648
        {
1649
                const int hdIndex=match.captured(2).toInt();
×
1650
                if (hdStarsIndex.contains(hdIndex))
×
1651
                {
1652
                        sid = hdStarsIndex.value(hdIndex);
×
1653
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1654
                }
1655
        }
1656

1657
        // Search by HR number if it's an HR formatted number
1658
        static const QRegularExpression rx4("^\\s*(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1659
        match=rx4.match(nameUpper);
×
1660
        if (match.hasMatch())
×
1661
        {
1662
                const int hrIndex=match.captured(2).toInt();
×
1663
                if (hrStarsIndex.contains(hrIndex))
×
1664
                {
1665
                        sid = hrStarsIndex.value(hrIndex);
×
1666
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1667
                }
1668
        }
1669

1670
        // Search by Gaia number if it's an Gaia formatted number.
1671
        static const QRegularExpression rx5("^\\s*(Gaia DR3)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
1672
        match=rx5.match(nameUpper);
×
1673
        if (match.hasMatch())
×
1674
                return searchGaia(match.captured(2).toLongLong());
×
1675

1676
        // Search by English common name
1677
        if (commonNamesUppercaseIndex.contains(nameUpper))
×
1678
        {
1679
                sid = commonNamesUppercaseIndex.value(nameUpper);
×
1680
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1681
        }
1682

1683
        if (getFlagAdditionalNames())
×
1684
        {
1685
                if (culturalNamesUppercaseIndex.contains(nameUpper))
×
1686
                {
1687
                        // This only returns the first-found number.
1688
                        sid=culturalNamesUppercaseIndex.value(nameUpper);
×
1689
                        return (sid <= NR_OF_HIP ? searchHP(sid) : searchGaia(sid));
×
1690
                }
1691
        }
1692

1693
        // Search by scientific name
1694
        if (sciDesignationsIndex.contains(name)) // case sensitive!
×
1695
        {
1696
                sid = sciDesignationsIndex.value(name);
×
1697
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1698
        }
1699
        if (sciDesignationsIndex.contains(nameUpper)) // case insensitive!
×
1700
        {
1701
                sid = sciDesignationsIndex.value(nameUpper);
×
1702
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1703
        }
1704

1705
        // Search by scientific name
1706
        if (sciExtraDesignationsIndex.contains(name)) // case sensitive!
×
1707
        {
1708
                sid = sciExtraDesignationsIndex.value(name);
×
1709
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1710
        }
1711
        if (sciExtraDesignationsIndex.contains(nameUpper)) // case insensitive!
×
1712
        {
1713
                sid = sciExtraDesignationsIndex.value(nameUpper);
×
1714
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1715
        }
1716

1717
        // Search by GCVS name
1718
        if (varStarsIndex.contains(nameUpper))
×
1719
        {
1720
                sid = varStarsIndex.value(nameUpper);
×
1721
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1722
        }
1723

1724
        // Search by WDS name
1725
        if (wdsStarsIndex.contains(nameUpper))
×
1726
        {
1727
                sid = wdsStarsIndex.value(nameUpper);
×
1728
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1729
        }
1730

1731
        return StelObjectP();
×
1732
}
×
1733

1734
StelObjectP StarMgr::searchByID(const QString &id) const
×
1735
{
1736
        return searchByName(id);
×
1737
}
1738

1739
//! Find and return the list of at most maxNbItem objects auto-completing the passed object name.
1740
QStringList StarMgr::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
×
1741
{
1742
        QStringList result;
×
1743
        if (maxNbItem <= 0 || !getFlagStars())
×
1744
                return result;
×
1745

1746
        const QString objPrefixUpper = objPrefix.toUpper();
×
1747
        bool found;
1748

1749
        // Search for common names
1750
        QHashIterator<QString, StarId> i(commonNamesI18nUppercaseIndex);
×
1751
        while (i.hasNext())
×
1752
        {
1753
                i.next();
×
1754
                if (useStartOfWords && i.key().startsWith(objPrefixUpper))
×
1755
                        found = true;
×
1756
                else found = (!useStartOfWords && i.key().contains(objPrefixUpper));
×
1757

1758
                if (found)
×
1759
                {
1760
                        if (maxNbItem<=0)
×
1761
                                break;
×
1762
                        result.append(getCommonNameI18n(i.value()));
×
1763
                        --maxNbItem;
×
1764
                }
1765
        }
1766

1767
        QHashIterator<QString, StarId> j(commonNamesUppercaseIndex);
×
1768
        while (j.hasNext())
×
1769
        {
1770
                j.next();
×
1771
                if (useStartOfWords && j.key().startsWith(objPrefixUpper))
×
1772
                        found = true;
×
1773
                else found = (!useStartOfWords && j.key().contains(objPrefixUpper));
×
1774

1775
                if (found)
×
1776
                {
1777
                        if (maxNbItem<=0)
×
1778
                                break;
×
1779
                        result.append(getCommonEnglishName(j.value()));
×
1780
                        --maxNbItem;
×
1781
                }
1782
        }
1783

1784
        if (getFlagAdditionalNames())
×
1785
        {
1786
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1787
                QMapIterator<QString, StarId>it(culturalNamesUppercaseIndex);
1788
#else
1789
                QMultiMapIterator<QString, StarId>it(culturalNamesUppercaseIndex);
×
1790
#endif
1791
                while (it.hasNext())
×
1792
                {
1793
                        it.next();
×
1794
                        QString name=it.key();
×
1795

1796
                        if (useStartOfWords && name.startsWith(objPrefixUpper, Qt::CaseInsensitive))
×
1797
                                found = true;
×
1798
                        else found = (!useStartOfWords && name.contains(objPrefixUpper, Qt::CaseInsensitive));
×
1799

1800
                        if (found)
×
1801
                        {
1802
                                if (maxNbItem<=0)
×
1803
                                        break;
×
1804

1805
                                // We must retrieve the original mixed-case spelling
1806
                                const QList<StelObject::CulturalName> cNames=getCulturalNames(it.value());
×
1807
                                QString finalName;
×
1808
                                for (const StelObject::CulturalName &cName: cNames)
×
1809
                                {
1810
                                        if (!cName.native.compare(name, Qt::CaseInsensitive))
×
1811
                                                finalName=cName.native;
×
1812
                                        else if (!cName.pronounceI18n.compare(name, Qt::CaseInsensitive))
×
1813
                                                finalName=cName.pronounceI18n;
×
1814
                                        else if (!cName.transliteration.compare(name, Qt::CaseInsensitive))
×
1815
                                                finalName=cName.transliteration;
×
1816
                                        else if (!cName.translatedI18n.compare(name, Qt::CaseInsensitive))
×
1817
                                                finalName=cName.translatedI18n;
×
1818
                                }
1819
                                if (finalName.isEmpty())
×
1820
                                {
1821
                                        qWarning() << "No original string found for " << name
×
1822
                                                   << "(This should not be possible...)";
×
1823
                                        finalName=name;
×
1824
                                }
1825

1826
                                result.append(finalName);
×
1827
                                --maxNbItem;
×
1828
                        }
×
1829
                }
×
1830
        }
×
1831

1832
        // Search for sci names
1833
        // need special character escape because many stars have name starting with "*"
1834
        QString bayerPattern = objPrefix;
×
1835
        QRegularExpression bayerRegEx(QRegularExpression::escape(bayerPattern));
×
1836
        QString bayerPatternCI = objPrefixUpper;
×
1837
        QRegularExpression bayerRegExCI(QRegularExpression::escape(bayerPatternCI));
×
1838

1839
        // if the first character is a Greek letter, check if there's an index
1840
        // after it, such as "alpha1 Cen".
1841
        if (objPrefix.at(0).unicode() >= 0x0391 && objPrefix.at(0).unicode() <= 0x03A9)
×
1842
                bayerRegEx.setPattern(bayerPattern.insert(1,"\\d?"));        
×
1843
        if (objPrefixUpper.at(0).unicode() >= 0x0391 && objPrefixUpper.at(0).unicode() <= 0x03A9)
×
1844
                bayerRegExCI.setPattern(bayerPatternCI.insert(1,"\\d?"));
×
1845

1846
        QHashIterator<QString,StarId>it(sciDesignationsIndex);
×
1847
        while (it.hasNext())
×
1848
        {
1849
                it.next();
×
1850
                if (it.key().indexOf(bayerRegEx)==0 || it.key().indexOf(objPrefix)==0)
×
1851
                {
1852
                        if (maxNbItem<=0)
×
1853
                                break;
×
1854
                        QStringList names = getSciDesignation(it.value()).split(" - ");
×
1855
                        for (const auto &name : std::as_const(names))
×
1856
                        {
1857
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1858
                                        found = true;
×
1859
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1860

1861
                                if (found)
×
1862
                                {
1863
                                        if (maxNbItem<=0)
×
1864
                                                break;
×
1865
                                        result.append(name);
×
1866
                                        --maxNbItem;
×
1867
                                }
1868
                        }
1869
                }
×
1870
                else if (it.key().at(0) != objPrefix.at(0))
×
1871
                        break;
×
1872
        }
1873

1874
        it.toFront(); // reset
×
1875
        while (it.hasNext())
×
1876
        {
1877
                it.next();
×
1878
                if (it.key().indexOf(bayerRegExCI)==0 || it.key().indexOf(objPrefixUpper)==0)
×
1879
                {
1880
                        if (maxNbItem<=0)
×
1881
                                break;
×
1882
                        QStringList names = getSciDesignation(it.value()).split(" - ");
×
1883
                        for (const auto &name : std::as_const(names))
×
1884
                        {
1885
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1886
                                        found = true;
×
1887
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1888

1889
                                if (found)
×
1890
                                {
1891
                                        if (maxNbItem<=0)
×
1892
                                                break;
×
1893
                                        result.append(name);
×
1894
                                        --maxNbItem;
×
1895
                                }
1896
                        }
1897
                }
×
1898
                else if (it.key().at(0) != objPrefixUpper.at(0))
×
1899
                        break;
×
1900
        }
1901

1902
        QHashIterator<QString,StarId>ite(sciExtraDesignationsIndex);
×
1903
        while (ite.hasNext())
×
1904
        {
1905
                ite.next();
×
1906
                if (ite.key().indexOf(bayerRegEx)==0 || ite.key().indexOf(objPrefix)==0)
×
1907
                {
1908
                        if (maxNbItem<=0)
×
1909
                                break;
×
1910
                        QStringList names = getSciExtraDesignation(ite.value()).split(" - ");
×
1911
                        for (const auto &name : std::as_const(names))
×
1912
                        {
1913
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1914
                                        found = true;
×
1915
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1916

1917
                                if (found)
×
1918
                                {
1919
                                        if (maxNbItem<=0)
×
1920
                                                break;
×
1921
                                        result.append(name);
×
1922
                                        --maxNbItem;
×
1923
                                }
1924
                        }
1925
                }
×
1926
                else if (ite.key().at(0) != objPrefix.at(0))
×
1927
                        break;
×
1928
        }
1929

1930
        ite.toFront(); // reset
×
1931
        while (ite.hasNext())
×
1932
        {
1933
                ite.next();
×
1934
                if (ite.key().indexOf(bayerRegExCI)==0 || ite.key().indexOf(objPrefixUpper)==0)
×
1935
                {
1936
                        if (maxNbItem<=0)
×
1937
                                break;
×
1938
                        QStringList names = getSciExtraDesignation(ite.value()).split(" - ");
×
1939
                        for (const auto &name : std::as_const(names))
×
1940
                        {
1941
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1942
                                        found = true;
×
1943
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1944

1945
                                if (found)
×
1946
                                {
1947
                                        if (maxNbItem<=0)
×
1948
                                                break;
×
1949
                                        result.append(name);
×
1950
                                        --maxNbItem;
×
1951
                                }
1952
                        }
1953
                }
×
1954
                else if (ite.key().at(0) != objPrefixUpper.at(0))
×
1955
                        break;
×
1956
        }
1957

1958
        // Search for sci names for var stars
1959
        QHashIterator<QString,StarId>itv(varStarsIndex);
×
1960
        while (itv.hasNext())
×
1961
        {
1962
                itv.next();
×
1963
                if (itv.key().startsWith(objPrefixUpper))
×
1964
                {
1965
                        if (maxNbItem<=0)
×
1966
                                break;
×
1967
                        result << getGcvsDesignation(itv.value());
×
1968
                        --maxNbItem;
×
1969
                }
1970
                else
1971
                        break;
×
1972
        }
1973

1974
        StarId sid;
1975
        // Add exact Hp catalogue numbers. The final part (A/B/...) is ignored
1976
        static const QRegularExpression hpRx("^(HIP|HP)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
1977
        QRegularExpressionMatch match=hpRx.match(objPrefixUpper);
×
1978
        if (match.hasMatch())
×
1979
        {
1980
                bool ok;
1981
                int hpNum = match.captured(2).toInt(&ok);
×
1982
                if (ok)
×
1983
                {
1984
                        StelObjectP s = searchHP(hpNum);
×
1985
                        if (s && maxNbItem>0)
×
1986
                        {
1987
                                result << QString("HIP%1").arg(hpNum);
×
1988
                                maxNbItem--;
×
1989
                        }
1990
                }
×
1991
        }
1992

1993
        // Add exact SAO catalogue numbers
1994
        static const QRegularExpression saoRx("^(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1995
        match=saoRx.match(objPrefixUpper);
×
1996
        if (match.hasMatch())
×
1997
        {
1998
                int saoNum = match.captured(2).toInt();
×
1999
                if (saoStarsIndex.contains(saoNum))
×
2000
                {
2001
                        sid = saoStarsIndex.value(saoNum);
×
2002
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
2003
                        if (s && maxNbItem>0)
×
2004
                        {
2005
                                result << QString("SAO%1").arg(saoNum);
×
2006
                                maxNbItem--;
×
2007
                        }
2008
                }
×
2009
        }
2010

2011
        // Add exact HD catalogue numbers
2012
        static const QRegularExpression hdRx("^(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
2013
        match=hdRx.match(objPrefixUpper);
×
2014
        if (match.hasMatch())
×
2015
        {
2016
                int hdNum = match.captured(2).toInt();
×
2017
                if (hdStarsIndex.contains(hdNum))
×
2018
                {
2019
                        sid = hdStarsIndex.value(hdNum);
×
2020
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
2021
                        if (s && maxNbItem>0)
×
2022
                        {
2023
                                result << QString("HD%1").arg(hdNum);
×
2024
                                maxNbItem--;
×
2025
                        }
2026
                }
×
2027
        }
2028

2029
        // Add exact HR catalogue numbers
2030
        static const QRegularExpression hrRx("^(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
2031
        match=hrRx.match(objPrefixUpper);
×
2032
        if (match.hasMatch())
×
2033
        {
2034
                int hrNum = match.captured(2).toInt();
×
2035
                if (hrStarsIndex.contains(hrNum))
×
2036
                {
2037
                        sid = hrStarsIndex.value(hrNum);
×
2038
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
2039
                        if (s && maxNbItem>0)
×
2040
                        {
2041
                                result << QString("HR%1").arg(hrNum);
×
2042
                                maxNbItem--;
×
2043
                        }
2044
                }
×
2045
        }
2046

2047
        // Add exact WDS catalogue numbers
2048
        static const QRegularExpression wdsRx("^(WDS)\\s*(\\S+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
2049
        if (wdsRx.match(objPrefixUpper).hasMatch())
×
2050
        {
2051
                QHashIterator<QString, StarId>wds(wdsStarsIndex);
×
2052
                while (wds.hasNext())
×
2053
                {
2054
                        wds.next();
×
2055
                        if (wds.key().startsWith(objPrefixUpper))
×
2056
                        {
2057
                                if (maxNbItem==0)
×
2058
                                        break;
×
2059
                                result << getWdsDesignation(wds.value());
×
2060
                                --maxNbItem;
×
2061
                        }
2062
                        else
2063
                                break;
×
2064
                }
2065
        }
×
2066

2067
        // Add exact Gaia DR3 catalogue numbers.
2068
        static const QRegularExpression gaiaRx("^\\s*(Gaia DR3)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
2069
        match=gaiaRx.match(objPrefixUpper);
×
2070
        if (match.hasMatch())
×
2071
        {
2072
                bool ok;
2073
                StarId gaiaNum = match.captured(2).toLongLong(&ok);
×
2074
                if (ok)
×
2075
                {
2076
                        StelObjectP s = searchGaia(gaiaNum);
×
2077
                        if (s && maxNbItem>0)
×
2078
                        {
2079
                                result << QString("Gaia DR3 %1").arg(gaiaNum);
×
2080
                                maxNbItem--;
×
2081
                        }
2082
                }
×
2083
        }
2084

2085
        result.sort();        
×
2086
        return result;
×
2087
}
×
2088

2089
// Set display flag for Stars.
2090
void StarMgr::setFlagStars(bool b)
×
2091
{
2092
        starsFader=b;
×
2093
        StelApp::immediateSave("astro/flag_stars", b);
×
2094
        emit starsDisplayedChanged(b);
×
2095
}
×
2096
// Set display flag for Star names (labels).
2097
void StarMgr::setFlagLabels(bool b) {labelsFader=b; StelApp::immediateSave("astro/flag_star_name", b); emit starLabelsDisplayedChanged(b);}
×
2098
// Set the amount of star labels. The real amount is also proportional with FOV.
2099
// The limit is set in function of the stars magnitude
2100
// @param a the amount between 0 and 10. 0 is no labels, 10 is maximum of labels
2101
void StarMgr::setLabelsAmount(double a)
×
2102
{
2103
        if(!qFuzzyCompare(a,labelsAmount))
×
2104
        {
2105
                labelsAmount=a;
×
2106
                StelApp::immediateSave("stars/labels_amount", a);
×
2107
                emit labelsAmountChanged(a);
×
2108
        }
2109
}
×
2110

2111
// Define font file name and size to use for star names display
2112
void StarMgr::setFontSize(int newFontSize)
×
2113
{
2114
        starFont.setPixelSize(newFontSize);
×
2115
}
×
2116

2117
// Set flag for usage designations of stars for their labels instead common names.
2118
void StarMgr::setDesignationUsage(const bool flag)
×
2119
{
2120
        if(flagDesignations!=flag)
×
2121
        {
2122
                flagDesignations=flag;
×
2123
                StelApp::immediateSave("astro/flag_star_designation_usage", flag);
×
2124
                emit designationUsageChanged(flag);
×
2125
        }
2126
}
×
2127
// Set flag for usage traditional designations of double stars.
2128
void StarMgr::setFlagDblStarsDesignation(const bool flag)
×
2129
{
2130
        if(flagDblStarsDesignation!=flag)
×
2131
        {
2132
                flagDblStarsDesignation=flag;
×
2133
                StelApp::immediateSave("astro/flag_star_designation_dbl", flag);
×
2134
                emit flagDblStarsDesignationChanged(flag);
×
2135
        }
2136
}
×
2137
// Set flag for usage designations of variable stars.
2138
void StarMgr::setFlagVarStarsDesignation(const bool flag)
×
2139
{
2140
        if(flagVarStarsDesignation!=flag)
×
2141
        {
2142
                flagVarStarsDesignation=flag;
×
2143
                StelApp::immediateSave("astro/flag_star_designation_var", flag);
×
2144
                emit flagVarStarsDesignationChanged(flag);
×
2145
        }
2146
}
×
2147
// Set flag for usage Hipparcos catalog designations of stars.
2148
void StarMgr::setFlagHIPDesignation(const bool flag)
×
2149
{
2150
        if(flagHIPDesignation!=flag)
×
2151
        {
2152
                flagHIPDesignation=flag;
×
2153
                StelApp::immediateSave("astro/flag_star_designation_hip", flag);
×
2154
                emit flagHIPDesignationChanged(flag);
×
2155
        }
2156
}
×
2157
// Show additional star names.
2158
void StarMgr::setFlagAdditionalNames(bool flag)
×
2159
{
2160
        if (flagAdditionalStarNames!=flag)
×
2161
        {
2162
                flagAdditionalStarNames=flag;
×
2163
                StelApp::immediateSave("astro/flag_star_additional_names", flag);
×
2164
                emit flagAdditionalNamesDisplayedChanged(flag);
×
2165
        }
2166
}
×
2167

2168
void StarMgr::updateSkyCulture(const StelSkyCulture& skyCulture)
×
2169
{
2170
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
2171

2172
        const QString commonStarsFileName = StelFileMgr::findFile("skycultures/common_star_names.fab");
×
2173
        CommonNames commonNames;
×
2174
        if (commonStarsFileName.isEmpty())
×
2175
                qWarning() << "Could not load common_star_names.fab";
×
2176
        else
2177
                commonNames = loadCommonNames(commonStarsFileName);
×
2178

2179
        QMap<QString, int> commonNamesIndexToSearchWhileLoading = commonNames.hipByName;
×
2180
        commonNamesMap.clear();
×
2181
        commonNamesI18nMap.clear();
×
2182
        commonNamesI18nUppercaseIndex.clear();
×
2183
        commonNamesUppercaseIndex.clear();
×
2184
        culturalNamesMap.clear();
×
2185
        culturalNamesUppercaseIndex.clear();
×
2186

2187
        static QSettings* conf = StelApp::getInstance().getSettings();
×
2188
        Q_ASSERT(conf);
×
2189
        QString exclude=conf->value("SCExcludeReferences/"+skyCulture.id, QString()).toString();
×
2190
        QSet<int>excludeRefs;
×
2191
        if (!exclude.isEmpty())
×
2192
        {
2193
#if  (QT_VERSION<QT_VERSION_CHECK(5,14,0))
2194
                const QStringList excludeRefStrings = exclude.split(",", QString::SkipEmptyParts);
2195
#else
2196
                const QStringList excludeRefStrings = exclude.split(",", Qt::SkipEmptyParts);
×
2197
#endif
2198
                //qInfo() << "Skyculture" << skyCulture.id << "configured to exclude references" << excludeRefStrings;
2199
                for (const QString &s: excludeRefStrings)
×
2200
                {
2201
                        bool ok;
2202
                        int numRef=s.toInt(&ok); // ok=false for strings e.g. from asterisms
×
2203
                        if (ok)
×
2204
                                excludeRefs.insert(numRef);
×
2205
                }
2206
                qInfo() << "Skyculture" << skyCulture.id << "configured to exclude references" << excludeRefs;
×
2207
        }
×
2208

2209
        if (!skyCulture.names.isEmpty())
×
2210
                loadCultureSpecificNames(skyCulture.names, commonNamesIndexToSearchWhileLoading, excludeRefs);
×
2211

2212
        // Load common names (i.e., standardized IAU approved star names). These are used for reference if user includes "Modern" names in the display settings.
2213
        // If a skyculture has set fallbackToInternationalNames, it means the names will be incorporated in the actual skyculture.
2214
        for (auto it = commonNames.hipByName.begin(); it != commonNames.hipByName.end(); ++it)
×
2215
        {
2216
                const StarId HIP = it.value();
×
2217
                const QString& englishName = commonNames.byHIP[HIP];
×
2218
                const QString englishNameUpper = englishName.toUpper();
×
2219

2220
                const QString& i18nName = trans.qTranslateStar(commonNames.byHIP[HIP]);
×
2221
                const QString i18nNameUpper = i18nName.toUpper();
×
2222

2223
                // TODO: Why do we have to reload the commonname* structures? Just load once at program start? Translate if needed, OK.
2224
                if (!commonNamesMap.contains(HIP))
×
2225
                {
2226
                        commonNamesMap[HIP] = englishName;
×
2227
                        commonNamesUppercaseIndex[englishNameUpper] = HIP;
×
2228
                        commonNamesI18nMap[HIP] = i18nName;
×
2229
                        commonNamesI18nUppercaseIndex[i18nNameUpper] = HIP;
×
2230
                }
2231

2232
                if (skyCulture.fallbackToInternationalNames)
×
2233
                {
2234
                        // Add name from commonNames, but only if not already in list.
2235
                        auto starIds=culturalNamesUppercaseIndex.values(englishNameUpper);
×
2236
                        if (!starIds.contains(HIP))
×
2237
                        {
2238
                                StelObject::CulturalName cName{englishName, QString(), QString(),
×
2239
                                                        englishName, englishName, i18nName, QString()};
×
2240
                                //if (culturalNamesMap.contains(HIP))
2241
                                //        qInfo() << "Adding additional cultural name for HIP" << HIP << ":" <<  cName.native << "/" << cName.pronounceI18n << "/" << cName.translated << "/" << cName.translatedI18n;
2242
                                culturalNamesMap.insert(HIP, cName); // add as possibly multiple entry to HIP.
×
2243
                                if (!cName.native.isEmpty())
×
2244
                                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
2245
                                if (!cName.translatedI18n.isEmpty())
×
2246
                                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
2247
                        }
×
2248
                }
×
2249
        }
×
2250

2251
        // Turn on sci names/catalog names for modern cultures only
2252
        setFlagSciNames(skyCulture.englishName.contains("modern", Qt::CaseInsensitive));
×
2253
        updateI18n();
×
2254
}
×
2255

2256
void StarMgr::increaseStarsMagnitudeLimit()
×
2257
{
2258
        static StelCore* core = StelApp::getInstance().getCore();
×
2259
        core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() + 0.1);
×
2260
}
×
2261

2262
void StarMgr::reduceStarsMagnitudeLimit()
×
2263
{
2264
        static StelCore* core = StelApp::getInstance().getCore();
×
2265
        core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() - 0.1);
×
2266
}
×
2267

2268
void StarMgr::populateStarsDesignations()
×
2269
{
2270
        QString filePath;
×
2271
        filePath = StelFileMgr::findFile("stars/hip_gaia3/name.fab");
×
2272
        if (filePath.isEmpty())
×
2273
                qWarning() << "Could not load scientific star names file: stars/hip_gaia3/name.fab";
×
2274
        else
2275
                loadSciDesignations(filePath, sciDesignationsMap, sciDesignationsIndex);
×
2276

2277
        filePath = StelFileMgr::findFile("stars/hip_gaia3/extra_name.fab");
×
2278
        if (filePath.isEmpty())
×
2279
                qWarning() << "Could not load scientific star extra names file: stars/hip_gaia3/extra_name.fab";
×
2280
        else
2281
                loadSciDesignations(filePath, sciExtraDesignationsMap, sciExtraDesignationsIndex);
×
2282

2283
        filePath = StelFileMgr::findFile("stars/hip_gaia3/gcvs.cat");
×
2284
        if (filePath.isEmpty())
×
2285
                qWarning() << "Could not load variable stars file: stars/hip_gaia3/gcvs.cat";
×
2286
        else
2287
                loadGcvs(filePath);
×
2288

2289
        filePath = StelFileMgr::findFile("stars/hip_gaia3/wds_hip_part.dat");
×
2290
        if (filePath.isEmpty())
×
2291
                qWarning() << "Could not load double stars file: stars/hip_gaia3/wds_hip_part.dat";
×
2292
        else
2293
                loadWds(filePath);
×
2294

2295
        filePath = StelFileMgr::findFile("stars/hip_gaia3/cross-id.cat");
×
2296
        if (filePath.isEmpty())
×
2297
                qWarning() << "Could not load cross-identification data file: stars/hip_gaia3/cross-id.cat";
×
2298
        else
2299
                loadCrossIdentificationData(filePath);
×
2300
        
2301
        filePath = StelFileMgr::findFile("stars/hip_gaia3/binary_orbitparam.dat");
×
2302
        if (filePath.isEmpty())
×
2303
                qWarning() << "Could not load binary orbital parameters data file: stars/hip_gaia3/binary_orbitparam.dat";
×
2304
        else
2305
                loadBinaryOrbitalData(filePath);
×
2306
}
×
2307

2308
QStringList StarMgr::listAllObjects(bool inEnglish) const
×
2309
{
2310
        QStringList result;
×
2311
        if (inEnglish)
×
2312
        {
2313
                result = commonNamesMap.values();
×
2314

2315
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
2316
                QHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
2317
#else
2318
                QMultiHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
×
2319
#endif
2320
                while (ci.hasNext())
×
2321
                {
2322
                        ci.next();
×
2323
                        result << ci.value().native;
×
2324
                        result << ci.value().translated;
×
2325
                        result << ci.value().pronounce;
×
2326
                        result << ci.value().transliteration;
×
2327
                }
2328
        }
×
2329
        else
2330
        {
2331
                result=commonNamesI18nMap.values();
×
2332

2333
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
2334
                QHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
2335
#else
2336
                QMultiHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
×
2337
#endif
2338
                while (ci.hasNext())
×
2339
                {
2340
                        ci.next();
×
2341
                        result << ci.value().native;
×
2342
                        result << ci.value().translatedI18n;
×
2343
                        result << ci.value().pronounceI18n;
×
2344
                        result << ci.value().transliteration;
×
2345
                }
2346
        }
×
2347
        result.removeDuplicates();
×
2348
        result.removeAll(QString(""));
×
2349
        result.removeAll(QString());
×
2350
        return result;
×
2351
}
×
2352

2353
QStringList StarMgr::listAllObjectsByType(const QString &objType, bool inEnglish) const
×
2354
{
2355
        QStringList result;        
×
2356
        // type 1
2357
        bool isStarT1 = false;
×
2358
        QList<StelObjectP> starsT1;
×
2359
        // type 2
2360
        bool isStarT2 = false;
×
2361
        QList<StelACStarData> starsT2;
×
2362
        int type = objType.toInt();        
×
2363
        switch (type)
×
2364
        {
2365
                case 0: // Interesting double stars
×
2366
                {
2367
                        static const QStringList doubleStars = {
2368
                                "21 Tau", "27 Tau", "77 Tau", "δ1 Tau", "V1016 Ori",
2369
                                "42 Ori", "ι Ori", "ζ Crv", "ζ UMa", "α2 Lib", "α1 Cru",
2370
                                "ω1 Sco", "λ Sco", "μ1 Sco", "ζ1 Sco", "ε1 Lyr", "ε2 Lyr",
2371
                                "δ1 Lyr",         "ν1 Sgr", "ο1 Cyg", "ο2 Cyg", "α2 Cap", "β1 Cyg",
2372
                                "β Ori", "γ1 And", "ξ Boo", "α1 Her", "T Dra", "ν1 Dra",
2373
                                "70 Oph", "α Gem", "ζ Her", "ο2 Eri", "γ1 Ari", "γ Vir",
2374
                                "γ1 Leo", "β Mon", "ε Boo", "44 Boo", "β1 Sco", "ζ1 Cnc",
2375
                                "φ2 Cnc", "α Leo", "α2 CVn", "ι Cas", "ε Ari", "κ Vel", "γ1 Del",
2376
                                "61 Cyg B", "55 Aqr", "σ Cas", "η Cas", "α UMi", "36 Oph",
2377
                                "α1 Cen",  "65 UMa", "σ2 UMa", "55 Cnc", "16 Cyg A",
2378
                                "HIP 28393", "HIP 84709"};
×
2379
                        result = doubleStars;
×
2380
                        break;
×
2381
                }
2382
                case 1: // Interesting variable stars
×
2383
                {
2384
                        static const QStringList variableStars = {
2385
                                "δ Cep", "β Per", "ο Cet", "λ Tau", "β Lyr", "ζ Gem", "μ Cep",
2386
                                "α1 Her", "η Gem", "η Aql", "γ Cas", "α Ori", "R And",
2387
                                "U Ant", "θ Aps", "R Aql", "V Aql", "R Aqr", "ε Aur", "R Aur",
2388
                                "AE Aur", "W Boo", "VZ Cam", "l Car", "WZ Cas",        "S Cen",
2389
                                "α Cen C", "T Cep", "U Cep", "R CMa", "VY CMa",
2390
                                "S Cnc", "α CrB", "R CrB", "T CrB", "U CrB", "R Cru",
2391
                                "SU Cyg", "EU Del", "β Dor", "R Gem", "30 Her", "68 Her",
2392
                                "R Hor", "R Lep", "R Leo", "RR Lyr", "U Mon", "R Hya", "χ Cyg",
2393
                                "δ Ori", "VV Ori", "κ Pav", "β Peg", "ε Peg", "ζ Phe", "R Sct",
2394
                                "U Sgr", "RY Sgr", "W UMa", "α UMi"};
×
2395
                        result = variableStars;
×
2396
                        break;
×
2397
                }
2398
                case 2: // Bright double stars
×
2399
                {
2400
                        starsT2 = doubleHipStars;
×
2401
                        isStarT2 = true;
×
2402
                        break;
×
2403
                }
2404
                case 3: // Bright variable stars
×
2405
                {
2406
                        starsT2 = variableHipStars;
×
2407
                        isStarT2 = true;
×
2408
                        break;
×
2409
                }
2410
                case 4:
×
2411
                {
2412
                        starsT2 = hipStarsHighPM;
×
2413
                        isStarT2 = true;
×
2414
                        break;
×
2415
                }
2416
                case 5: // Variable stars: Algol-type eclipsing systems
×
2417
                {
2418
                        starsT2 = algolTypeStars;
×
2419
                        isStarT2 = true;
×
2420
                        break;
×
2421
                }
2422
                case 6: // Variable stars: the classical cepheids
×
2423
                {
2424
                        starsT2 = classicalCepheidsTypeStars;
×
2425
                        isStarT2 = true;
×
2426
                        break;
×
2427
                }
2428
                case 7: // Bright carbon stars
×
2429
                {
2430
                        starsT1 = carbonStars;
×
2431
                        isStarT1 = true;
×
2432
                        break;
×
2433
                }
2434
                case 8: // Bright barium stars
×
2435
                {
2436
                        starsT1 = bariumStars;
×
2437
                        isStarT1 = true;
×
2438
                        break;
×
2439
                }
2440
                default:
×
2441
                {
2442
                        // No stars yet?
2443
                        break;
×
2444
                }
2445
        }
2446

2447
        QString starName;
×
2448
        if (isStarT1)
×
2449
        {
2450
                for (const auto& star : std::as_const(starsT1))
×
2451
                {
2452
                        starName = inEnglish ? star->getEnglishName() : star->getNameI18n();
×
2453
                        if (!starName.isEmpty())
×
2454
                                result << starName;
×
2455
                        else
2456
                                result << star->getID();
×
2457
                }
2458
        }
2459

2460
        if (isStarT2)
×
2461
        {
2462
                for (const auto& star : std::as_const(starsT2))
×
2463
                {
2464
                        starName = inEnglish ? star.first->getEnglishName() : star.first->getNameI18n();
×
2465
                        if (!starName.isEmpty())
×
2466
                                result << starName;
×
2467
                        else
2468
                                result << star.first->getID();
×
2469
                }
2470
        }
2471

2472
        result.removeDuplicates();
×
2473
        return result;
×
2474
}
×
2475

2476
QString StarMgr::getStelObjectType() const
×
2477
{
2478
        return STAR_TYPE;
×
2479
}
2480

2481
//! cultural names
2482
//! Return screen label (to be used in the sky display. Most users will use some short label)
2483
QString StarMgr::getCulturalScreenLabel(StarId hip)
×
2484
{
2485
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2486
        QStringList list=getCultureLabels(hip, scMgr->getScreenLabelStyle());
×
2487
        //qDebug() << "culturalScreenLabel: " << list;
2488
        return list.isEmpty() ? QString() : list.constFirst();
×
2489
}
×
2490

2491
//! Return InfoString label (to be used in the InfoString).
2492
//! When dealing with foreign skycultures, many users will want this to be longer, with more name components.
2493
QString StarMgr::getCulturalInfoLabel(StarId hip)
×
2494
{
2495
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2496
        QStringList list=getCultureLabels(hip, scMgr->getInfoLabelStyle());
×
2497
        return list.isEmpty() ? "" : list.join(" - ");
×
2498
}
×
2499

2500
QStringList StarMgr::getCultureLabels(StarId hip, StelObject::CulturalDisplayStyle style)
×
2501
{
2502
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2503
        // Retrieve list in order as read from JSON
2504
        QList<StelObject::CulturalName>culturalNames=getCulturalNames(hip);
×
2505
        if (culturalNames.isEmpty())
×
2506
        {
2507
                return QStringList();
×
2508
        }
2509

2510
        QStringList labels;
×
2511
        for (auto &cName: culturalNames)
×
2512
                {
2513
                        QString commonNamei18=getCommonNameI18n(hip);
×
2514
                        if (commonNamei18.isEmpty())
×
2515
                        {
2516
                                QString sciDes=getSciDesignation(hip);
×
2517
                                if (sciDes.isEmpty())
×
2518
                                        sciDes=getSciExtraDesignation(hip);
×
2519

2520
                                commonNamei18=sciDes.split(" - ").first();
×
2521
                        }
×
2522
                        QString label=scMgr->createCulturalLabel(cName, style, commonNamei18);
×
2523
                        labels << label;
×
2524
                }
×
2525
        labels.removeDuplicates();
×
2526
        labels.removeAll(QString(""));
×
2527
        labels.removeAll(QString());
×
2528
        return labels;
×
2529
}
×
2530

2531

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc