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

Stellarium / stellarium / 15793453064

21 Jun 2025 06:37AM UTC coverage: 11.767% (-0.002%) from 11.769%
15793453064

push

github

10110111
Add support for touch clicks and moves

0 of 30 new or added lines in 1 file covered. (0.0%)

1336 existing lines in 5 files now uncovered.

14700 of 124924 relevant lines covered (11.77%)

18313.08 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

678
        qInfo().nospace() << "Lists of stars: " << hipparcosStars.count() << " HIP stars, " << doubleHipStars.count() << " are binaries, " << variableHipStars.count()
×
679
                          << " variable (" << algolTypeStars.count() << " Algol-type, " << classicalCepheidsTypeStars.count() << " Cepheids), " << carbonStars.count()
×
UNCOV
680
                          << " Carbon stars, " << bariumStars.count() << " Barium stars, and " << hipStarsHighPM.count() << " have PM>1000mas/yr.";
×
UNCOV
681
}
×
682

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

UNCOV
688
        if (commonNameFile.isEmpty()) return commonNames;
×
689

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1013
                varstar variableStar;
×
1014

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

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

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

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

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

UNCOV
1052
        int readOk=0;
×
1053
        int totalRecords=0;
×
1054
        int lineNumber=0;
×
1055

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

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

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

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

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

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

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

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

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

1111
        crossid crossIdData;
1112

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

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

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

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

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

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

UNCOV
1148
                ++readOk;
×
1149
        }
1150

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

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

1168
        binaryorbitstar orbitalData;
1169

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

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

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

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

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

1247

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

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

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

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

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

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

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

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

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

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

1354

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

UNCOV
1363
        Vec3d v(vv);
×
1364
        v.normalize();
×
1365

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

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

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

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

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

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

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

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

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

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

1518

1519

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

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

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

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

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

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

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

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

UNCOV
1616
        return searchByName(nameI18n);
×
1617
}
×
1618

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1730
        return StelObjectP();
×
1731
}
×
1732

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2530

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