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

Stellarium / stellarium / 15291801018

28 May 2025 04:52AM UTC coverage: 11.931% (-0.02%) from 11.951%
15291801018

push

github

alex-w
Added new set of navigational stars (XIX century)

0 of 6 new or added lines in 2 files covered. (0.0%)

14124 existing lines in 74 files now uncovered.

14635 of 122664 relevant lines covered (11.93%)

18291.42 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

UNCOV
64
Q_GLOBAL_STATIC(QStringList, spectral_array);
×
UNCOV
65
Q_GLOBAL_STATIC(QStringList, component_array);
×
UNCOV
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;
×
UNCOV
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());
×
UNCOV
114
                        s.chop(1);
×
115
                        list << s;
×
116
                }
×
UNCOV
117
                f.close();
×
118
        }
UNCOV
119
        return list;
×
120
}
×
121

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

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

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

152

UNCOV
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);
×
UNCOV
156
}
×
157

158

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

173
/*************************************************************************
174
 Reimplementation of the getCallOrder method
175
*************************************************************************/
176
double StarMgr::getCallOrder(StelModuleActionName actionName) const
×
177
{
UNCOV
178
        if (actionName==StelModule::ActionDraw)
×
UNCOV
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;
×
UNCOV
188
        gridLevels.clear();
×
UNCOV
189
        if (hipIndex)
×
190
                delete[] hipIndex;
×
UNCOV
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
UNCOV
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

UNCOV
208
        QList<StelObject::CulturalName>cNames=culturalNamesMap.values(hip);
×
UNCOV
209
        std::reverse(cNames.begin(), cNames.end());
×
210
        return cNames;
×
UNCOV
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
{
UNCOV
225
        return sciExtraDesignationsMap.value(hip, QString());
×
226
}
227

UNCOV
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

UNCOV
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

UNCOV
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

UNCOV
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

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

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

UNCOV
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
{
UNCOV
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
        {
UNCOV
297
                varstar var=varStarsMap.value(hip);
×
298
                return (firstMinimumFlag ? var.min1mag : var.min2mag);
×
UNCOV
299
        }
×
300
        return -99.f;
×
301
}
302

303
QString StarMgr::getGcvsPhotometricSystem(StarId hip)
×
304
{
UNCOV
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

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

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

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

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

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

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

UNCOV
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

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

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

383
        loadData(starSettings);
×
384

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

388
        setFontSize(StelApp::getInstance().getScreenFontSize());
×
UNCOV
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());
×
UNCOV
394
        setFlagAdditionalNames(conf->value("astro/flag_star_additional_names",true).toBool());
×
UNCOV
395
        setDesignationUsage(conf->value("astro/flag_star_designation_usage", false).toBool());
×
396
        setFlagDblStarsDesignation(conf->value("astro/flag_star_designation_dbl", false).toBool());
×
UNCOV
397
        setFlagVarStarsDesignation(conf->value("astro/flag_star_designation_var", false).toBool());
×
UNCOV
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();
×
UNCOV
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
UNCOV
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()");
×
UNCOV
414
}
×
415

416

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

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

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

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

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

446
        QString catalogFilePath = StelFileMgr::findFile(catalogFileName);
×
UNCOV
447
        if (catalogFilePath.isEmpty())
×
448
        {
449
                // The file is supposed to be checked, but we can't find it
UNCOV
450
                if (checked)
×
451
                {
452
                        qWarning().noquote() << "Could not find star catalog" << QDir::toNativeSeparators(catalogFileName);
×
UNCOV
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);
×
UNCOV
470
                if(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
×
471
                {
472
                        // Compute the MD5 sum
473
                        QCryptographicHash md5Hash(QCryptographicHash::Md5);
×
UNCOV
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;
UNCOV
481
                                char* mmd5buf = static_cast<char*>(malloc(maxStarBufMd5));
×
482
                                while (!file.atEnd())
×
483
                                {
UNCOV
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
                        {
UNCOV
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);
×
UNCOV
511
                }
×
UNCOV
512
        }
×
513

UNCOV
514
        if (load)
×
515
        {
516
                ZoneArray* z = ZoneArray::create(catalogFilePath, true);
×
UNCOV
517
                if (z)
×
518
                {
UNCOV
519
                        if (z->level<gridLevels.size())
×
520
                        {
521
                                qWarning().noquote() << QDir::toNativeSeparators(catalogFileName) << ", " << z->level << ": duplicate level";
×
UNCOV
522
                                delete z;
×
523
                                return true;
×
524
                        }
UNCOV
525
                        Q_ASSERT(z->level==maxGeodesicGridLevel+1);
×
UNCOV
526
                        Q_ASSERT(z->level==gridLevels.size());
×
527
                        ++maxGeodesicGridLevel;
×
UNCOV
528
                        gridLevels.append(z);
×
529
                }
530
                return true;
×
531
        }
532
        else
533
        {
UNCOV
534
                qWarning().noquote() << "Star catalog: " << QDir::toNativeSeparators(catalogFileName) << "is found but not loaded because at least one of the lower levels is missing!";
×
UNCOV
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
UNCOV
542
        int idx=0;
×
UNCOV
543
        for (const auto& catV : std::as_const(catalogsDescription))
×
544
        {
545
                ++idx;
×
UNCOV
546
                QVariantMap m = catV.toMap();
×
547
                if (m.value("id").toString()!=catId)
×
UNCOV
548
                        continue;
×
549
                const bool checked = m.value("checked").toBool();
×
UNCOV
550
                if (checked==b)
×
UNCOV
551
                        return;
×
UNCOV
552
                m["checked"]=b;
×
UNCOV
553
                catalogsDescription[idx-1]=m;                
×
554
        }
×
UNCOV
555
        starSettings["catalogs"]=catalogsDescription;
×
UNCOV
556
        QFile tmp(starConfigFileFullPath);
×
UNCOV
557
        if(tmp.open(QIODevice::WriteOnly))
×
558
        {
559
                StelJsonParser::write(starSettings, &tmp);
×
UNCOV
560
                tmp.close();
×
561
        }
UNCOV
562
}
×
563

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

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

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

579
        for (int i=0; i<=NR_OF_HIP; i++)
×
580
        {
UNCOV
581
                hipIndex[i].a = Q_NULLPTR;
×
582
                hipIndex[i].z = Q_NULLPTR;
×
UNCOV
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
        {
UNCOV
595
                QString tmpFic = StelFileMgr::findFile("stars/hip_gaia3/" + cat_hip_sp_file_name);
×
UNCOV
596
                if (tmpFic.isEmpty())
×
597
                        qWarning() << "ERROR while loading data from" << QDir::toNativeSeparators(("stars/hip_gaia3/" + cat_hip_sp_file_name));
×
598
                else
UNCOV
599
                        *spectral_array = initStringListFromFile(tmpFic);
×
600
        }
×
601

602
        const QString cat_objtype_file_name = starsConfig.value("objecttypesFile").toString();
×
UNCOV
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

UNCOV
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();
×
UNCOV
628
        hipStarsHighPM.clear();
×
UNCOV
629
        doubleHipStars.clear();
×
630
        variableHipStars.clear();
×
UNCOV
631
        algolTypeStars.clear();
×
632
        classicalCepheidsTypeStars.clear();
×
UNCOV
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
                {
UNCOV
641
                        const SpecialZoneArray<Star1> *const a = hipIndex[hip].a;
×
642
                        const SpecialZoneData<Star1> *const z = hipIndex[hip].z;
×
UNCOV
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
UNCOV
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
                        {
UNCOV
656
                                QPair<StelObjectP, float> sa(so, static_cast<float>(getGcvsPeriod(s->getHip())));
×
UNCOV
657
                                variableHipStars.push_back(sa);
×
658
                                
659
                                QString vartype = getGcvsVariabilityType(s->getHip());
×
660
                                if (vartype.contains("EA"))
×
UNCOV
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));
×
UNCOV
664
                        }
×
665
                        if (!getWdsDesignation(s->getHip()).isEmpty())
×
666
                        {
UNCOV
667
                                doubleHipStars.push_back(QPair<StelObjectP, float>(so, getWdsLastSeparation(s->getHip())));
×
668
                        }
UNCOV
669
                        float pm = s->getPMTotal();
×
UNCOV
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
                        }
×
UNCOV
675
                }
×
676
        }
677
        qInfo().nospace() << "Of " << hipparcosStars.count() << " HIP stars, " << doubleHipStars.count() << " are binaries, " <<
×
UNCOV
678
                   variableHipStars.count() << " variable (" << algolTypeStars.count() << " Algol-type, " << classicalCepheidsTypeStars.count() << " Cepheids), " <<
×
UNCOV
679
                   carbonStars.count() << " Carbon stars, " << bariumStars.count() << " Barium stars, and " << hipStarsHighPM.count() << " have PM>1000mas/yr.";
×
680

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1014
                varstar variableStar;
×
1015

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1112
        crossid crossIdData;
1113

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

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

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

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

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

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

UNCOV
1149
                ++readOk;
×
1150
        }
1151

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

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

1169
        binaryorbitstar orbitalData;
1170

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

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

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

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

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

1248

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

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

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

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

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

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

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

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

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

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

1355

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

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

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

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

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

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

1437

1438
//! Update i18 names from english names according to passed translator.
1439
//! The translation is done using gettext with translated strings defined in translations.h
1440
void StarMgr::updateI18n()
×
1441
{
1442
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
UNCOV
1443
        commonNamesI18nMap.clear();
×
UNCOV
1444
        commonNamesI18nUppercaseIndex.clear();
×
UNCOV
1445
        for (QHash<StarId,QString>::ConstIterator it(commonNamesMap.constBegin());it!=commonNamesMap.constEnd();it++)
×
1446
        {
1447
                const StarId i = it.key();
×
UNCOV
1448
                const QString t(trans.qTranslateStar(it.value()));
×
1449
                commonNamesI18nMap[i] = t;
×
1450
                commonNamesI18nUppercaseIndex[t.toUpper()] = i;
×
1451
        }
×
UNCOV
1452
        culturalNamesUppercaseIndex.clear();
×
1453
        for (QMultiHash<StarId,StelObject::CulturalName>::iterator it(culturalNamesMap.begin());it!=culturalNamesMap.end();++it)
×
1454
        {
UNCOV
1455
                StarId HIP=it.key();
×
UNCOV
1456
                StelObject::CulturalName &cName=it.value();
×
UNCOV
1457
                cName.pronounceI18n=trans.qTranslateStar(cName.pronounce);
×
UNCOV
1458
                cName.translatedI18n=trans.qTranslateStar(cName.translated);
×
1459
                it.value() = cName;
×
1460

1461
                // rebuild index
1462
                if (!cName.native.isEmpty())
×
UNCOV
1463
                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
1464
                if (!cName.pronounceI18n.isEmpty())
×
1465
                        culturalNamesUppercaseIndex.insert(cName.pronounceI18n.toUpper(), HIP);
×
UNCOV
1466
                if (!cName.transliteration.isEmpty())
×
UNCOV
1467
                        culturalNamesUppercaseIndex.insert(cName.transliteration.toUpper(), HIP);
×
1468
                if (!cName.translatedI18n.isEmpty())
×
1469
                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
1470
        }
UNCOV
1471
}
×
1472

1473
// Search the star by HP number
1474
StelObjectP StarMgr::searchHP(int hp) const
×
1475
{
1476
        if (0 < hp && hp <= NR_OF_HIP)
×
1477
        {
UNCOV
1478
                const Star1 *const s = hipIndex[hp].s;
×
UNCOV
1479
                if (s)
×
1480
                {
1481
                        const SpecialZoneArray<Star1> *const a = hipIndex[hp].a;
×
1482
                        const SpecialZoneData<Star1> *const z = hipIndex[hp].z;
×
1483
                        return s->createStelObject(a,z);
×
1484
                }
1485
        }
1486
        return StelObjectP();
×
1487
}
1488

1489
// Search the star by Gaia source_id
1490
StelObjectP StarMgr::searchGaia(StarId source_id) const
×
1491
{
1492
        int maxSearchLevel = getMaxSearchLevel();
×
UNCOV
1493
        int matched = 0;
×
1494
        int index = 0;
×
1495
        // get the level 12 HEALPix index of the source
UNCOV
1496
        int lv12_pix = source_id / 34359738368;
×
1497
        Vec3d v;
×
1498
        StelObjectP so;
×
1499
        healpix_pix2vec(pow(2, 12), lv12_pix, v.v);  // search which pixel the source is in and turn to coordinates
×
1500
        Vec3f vf = v.toVec3f();
×
1501

UNCOV
1502
        for (const auto* z : gridLevels)
×
1503
        {
1504
                // search the zone where the source is in
1505
                index = StelApp::getInstance().getCore()->getGeodesicGrid(maxSearchLevel)->getZoneNumberForPoint(vf, z->level);
×
UNCOV
1506
                so = z->searchGaiaID(index, source_id, matched);
×
UNCOV
1507
                if (matched)
×
UNCOV
1508
                        return so;
×
1509
                
1510
                // then search the global zone 
1511
                so = z->searchGaiaID((20<<(z->level<<1)), source_id, matched);
×
UNCOV
1512
                if (matched)
×
UNCOV
1513
                        return so;
×
1514
        }
1515
        return StelObjectP();
×
UNCOV
1516
}
×
1517

UNCOV
1518
StelObjectP StarMgr::searchByNameI18n(const QString& nameI18n) const
×
1519
{
UNCOV
1520
        QString nameI18nUpper = nameI18n.toUpper();
×
1521

1522
        // Search by I18n common name
1523
        if (commonNamesI18nUppercaseIndex.contains(nameI18nUpper))
×
1524
                return searchHP(commonNamesI18nUppercaseIndex.value(nameI18nUpper));
×
1525

1526
        // Search by cultural names? (Any names: native/pronounceI18n/translatedI18n/transliteration
UNCOV
1527
        if (getFlagAdditionalNames() && culturalNamesUppercaseIndex.contains(nameI18nUpper))
×
1528
        {
1529
                // This only returns the first-found number.
UNCOV
1530
                StarId starId=culturalNamesUppercaseIndex.value(nameI18nUpper);
×
1531
                return (starId <= NR_OF_HIP ? searchHP(starId) : searchGaia(starId));
×
1532
        }
1533

1534
        return searchByName(nameI18n);
×
1535
}
×
1536

UNCOV
1537
StelObjectP StarMgr::searchByName(const QString& name) const
×
1538
{
1539
        QString nameUpper = name.toUpper();
×
1540
        StarId sid;
1541

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

1548
        // Search by SAO number if it's an SAO formatted number
UNCOV
1549
        static const QRegularExpression rx2("^\\s*(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1550
        match=rx2.match(nameUpper);
×
1551
        if (match.hasMatch())
×
1552
        {
1553
                const int saoIndex=match.captured(2).toInt();
×
1554
                if (saoStarsIndex.contains(saoIndex))
×
1555
                {
1556
                        sid = saoStarsIndex.value(saoIndex);
×
1557
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1558
                }
1559
        }
1560

1561
        // Search by HD number if it's an HD formatted number
1562
        static const QRegularExpression rx3("^\\s*(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
UNCOV
1563
        match=rx3.match(nameUpper);
×
1564
        if (match.hasMatch())
×
1565
        {
UNCOV
1566
                const int hdIndex=match.captured(2).toInt();
×
1567
                if (hdStarsIndex.contains(hdIndex))
×
1568
                {
1569
                        sid = hdStarsIndex.value(hdIndex);
×
UNCOV
1570
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1571
                }
1572
        }
1573

1574
        // Search by HR number if it's an HR formatted number
UNCOV
1575
        static const QRegularExpression rx4("^\\s*(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1576
        match=rx4.match(nameUpper);
×
UNCOV
1577
        if (match.hasMatch())
×
1578
        {
1579
                const int hrIndex=match.captured(2).toInt();
×
1580
                if (hrStarsIndex.contains(hrIndex))
×
1581
                {
1582
                        sid = hrStarsIndex.value(hrIndex);
×
1583
                        return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1584
                }
1585
        }
1586

1587
        // Search by Gaia number if it's an Gaia formatted number.
1588
        static const QRegularExpression rx5("^\\s*(Gaia DR3)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
UNCOV
1589
        match=rx5.match(nameUpper);
×
UNCOV
1590
        if (match.hasMatch())
×
1591
                return searchGaia(match.captured(2).toLongLong());
×
1592

1593
        // Search by English common name
1594
        if (commonNamesUppercaseIndex.contains(nameUpper))
×
1595
        {
UNCOV
1596
                sid = commonNamesUppercaseIndex.value(nameUpper);
×
1597
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1598
        }
1599

UNCOV
1600
        if (getFlagAdditionalNames())
×
1601
        {
1602
                if (culturalNamesUppercaseIndex.contains(nameUpper))
×
1603
                {
1604
                        // This only returns the first-found number.
UNCOV
1605
                        sid=culturalNamesUppercaseIndex.value(nameUpper);
×
1606
                        return (sid <= NR_OF_HIP ? searchHP(sid) : searchGaia(sid));
×
1607
                }
1608
        }
1609

1610
        // Search by scientific name
1611
        if (sciDesignationsIndex.contains(name)) // case sensitive!
×
1612
        {
1613
                sid = sciDesignationsIndex.value(name);
×
UNCOV
1614
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1615
        }
1616
        if (sciDesignationsIndex.contains(nameUpper)) // case insensitive!
×
1617
        {
1618
                sid = sciDesignationsIndex.value(nameUpper);
×
UNCOV
1619
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1620
        }
1621

1622
        // Search by scientific name
UNCOV
1623
        if (sciExtraDesignationsIndex.contains(name)) // case sensitive!
×
1624
        {
1625
                sid = sciExtraDesignationsIndex.value(name);
×
UNCOV
1626
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1627
        }
UNCOV
1628
        if (sciExtraDesignationsIndex.contains(nameUpper)) // case insensitive!
×
1629
        {
UNCOV
1630
                sid = sciExtraDesignationsIndex.value(nameUpper);
×
1631
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1632
        }
1633

1634
        // Search by GCVS name
UNCOV
1635
        if (varStarsIndex.contains(nameUpper))
×
1636
        {
1637
                sid = varStarsIndex.value(nameUpper);
×
1638
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1639
        }
1640

1641
        // Search by WDS name
1642
        if (wdsStarsIndex.contains(nameUpper))
×
1643
        {
1644
                sid = wdsStarsIndex.value(nameUpper);
×
1645
                return (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1646
        }
1647

UNCOV
1648
        return StelObjectP();
×
UNCOV
1649
}
×
1650

1651
StelObjectP StarMgr::searchByID(const QString &id) const
×
1652
{
UNCOV
1653
        return searchByName(id);
×
1654
}
1655

1656
//! Find and return the list of at most maxNbItem objects auto-completing the passed object name.
1657
QStringList StarMgr::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
×
1658
{
UNCOV
1659
        QStringList result;
×
UNCOV
1660
        if (maxNbItem <= 0 || !getFlagStars())
×
UNCOV
1661
                return result;
×
1662

1663
        const QString objPrefixUpper = objPrefix.toUpper();
×
1664
        bool found;
1665

1666
        // Search for common names
1667
        QHashIterator<QString, StarId> i(commonNamesI18nUppercaseIndex);
×
1668
        while (i.hasNext())
×
1669
        {
1670
                i.next();
×
1671
                if (useStartOfWords && i.key().startsWith(objPrefixUpper))
×
UNCOV
1672
                        found = true;
×
UNCOV
1673
                else found = (!useStartOfWords && i.key().contains(objPrefixUpper));
×
1674

UNCOV
1675
                if (found)
×
1676
                {
1677
                        if (maxNbItem<=0)
×
1678
                                break;
×
1679
                        result.append(getCommonNameI18n(i.value()));
×
UNCOV
1680
                        --maxNbItem;
×
1681
                }
1682
        }
1683

UNCOV
1684
        QHashIterator<QString, StarId> j(commonNamesUppercaseIndex);
×
1685
        while (j.hasNext())
×
1686
        {
UNCOV
1687
                j.next();
×
UNCOV
1688
                if (useStartOfWords && j.key().startsWith(objPrefixUpper))
×
1689
                        found = true;
×
UNCOV
1690
                else found = (!useStartOfWords && j.key().contains(objPrefixUpper));
×
1691

1692
                if (found)
×
1693
                {
UNCOV
1694
                        if (maxNbItem<=0)
×
1695
                                break;
×
1696
                        result.append(getCommonEnglishName(j.value()));
×
UNCOV
1697
                        --maxNbItem;
×
1698
                }
1699
        }
1700

1701
        if (getFlagAdditionalNames())
×
1702
        {
1703
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1704
                QMapIterator<QString, StarId>it(culturalNamesUppercaseIndex);
1705
#else
UNCOV
1706
                QMultiMapIterator<QString, StarId>it(culturalNamesUppercaseIndex);
×
1707
#endif
1708
                while (it.hasNext())
×
1709
                {
1710
                        it.next();
×
1711
                        QString name=it.key();
×
1712

UNCOV
1713
                        if (useStartOfWords && name.startsWith(objPrefixUpper, Qt::CaseInsensitive))
×
UNCOV
1714
                                found = true;
×
1715
                        else found = (!useStartOfWords && name.contains(objPrefixUpper, Qt::CaseInsensitive));
×
1716

UNCOV
1717
                        if (found)
×
1718
                        {
1719
                                if (maxNbItem<=0)
×
UNCOV
1720
                                        break;
×
1721

1722
                                // We must retrieve the original mixed-case spelling
UNCOV
1723
                                QList<StelObject::CulturalName> cNames=getCulturalNames(it.value());
×
1724
                                QString finalName;
×
1725
                                for (const StelObject::CulturalName &cName: cNames)
×
1726
                                {
UNCOV
1727
                                        if (!cName.native.compare(name, Qt::CaseInsensitive))
×
UNCOV
1728
                                                finalName=cName.native;
×
1729
                                        else if (!cName.pronounceI18n.compare(name, Qt::CaseInsensitive))
×
1730
                                                finalName=cName.pronounceI18n;
×
UNCOV
1731
                                        else if (!cName.transliteration.compare(name, Qt::CaseInsensitive))
×
1732
                                                finalName=cName.transliteration;
×
1733
                                        else if (!cName.translatedI18n.compare(name, Qt::CaseInsensitive))
×
UNCOV
1734
                                                finalName=cName.translatedI18n;
×
1735
                                }
UNCOV
1736
                                if (finalName.isEmpty())
×
1737
                                {
1738
                                        qWarning() << "No original string found for " << name
×
UNCOV
1739
                                                   << "(This should not be possible...)";
×
1740
                                        finalName=name;
×
1741
                                }
1742

UNCOV
1743
                                result.append(finalName);
×
1744
                                --maxNbItem;
×
1745
                        }
×
UNCOV
1746
                }
×
1747
        }
×
1748

1749
        // Search for sci names
1750
        // need special character escape because many stars have name starting with "*"
UNCOV
1751
        QString bayerPattern = objPrefix;
×
UNCOV
1752
        QRegularExpression bayerRegEx(QRegularExpression::escape(bayerPattern));
×
1753
        QString bayerPatternCI = objPrefixUpper;
×
UNCOV
1754
        QRegularExpression bayerRegExCI(QRegularExpression::escape(bayerPatternCI));
×
1755

1756
        // if the first character is a Greek letter, check if there's an index
1757
        // after it, such as "alpha1 Cen".
UNCOV
1758
        if (objPrefix.at(0).unicode() >= 0x0391 && objPrefix.at(0).unicode() <= 0x03A9)
×
1759
                bayerRegEx.setPattern(bayerPattern.insert(1,"\\d?"));        
×
UNCOV
1760
        if (objPrefixUpper.at(0).unicode() >= 0x0391 && objPrefixUpper.at(0).unicode() <= 0x03A9)
×
UNCOV
1761
                bayerRegExCI.setPattern(bayerPatternCI.insert(1,"\\d?"));
×
1762

1763
        QHashIterator<QString,StarId>it(sciDesignationsIndex);
×
1764
        while (it.hasNext())
×
1765
        {
1766
                it.next();
×
1767
                if (it.key().indexOf(bayerRegEx)==0 || it.key().indexOf(objPrefix)==0)
×
1768
                {
1769
                        if (maxNbItem<=0)
×
1770
                                break;
×
UNCOV
1771
                        QStringList names = getSciDesignation(it.value()).split(" - ");
×
1772
                        for (const auto &name : std::as_const(names))
×
1773
                        {
1774
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
UNCOV
1775
                                        found = true;
×
1776
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1777

1778
                                if (found)
×
1779
                                {
UNCOV
1780
                                        if (maxNbItem<=0)
×
UNCOV
1781
                                                break;
×
UNCOV
1782
                                        result.append(name);
×
1783
                                        --maxNbItem;
×
1784
                                }
1785
                        }
1786
                }
×
1787
                else if (it.key().at(0) != objPrefix.at(0))
×
1788
                        break;
×
1789
        }
1790

UNCOV
1791
        it.toFront(); // reset
×
1792
        while (it.hasNext())
×
1793
        {
1794
                it.next();
×
UNCOV
1795
                if (it.key().indexOf(bayerRegExCI)==0 || it.key().indexOf(objPrefixUpper)==0)
×
1796
                {
1797
                        if (maxNbItem<=0)
×
1798
                                break;
×
1799
                        QStringList names = getSciDesignation(it.value()).split(" - ");
×
UNCOV
1800
                        for (const auto &name : std::as_const(names))
×
1801
                        {
UNCOV
1802
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1803
                                        found = true;
×
UNCOV
1804
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1805

1806
                                if (found)
×
1807
                                {
1808
                                        if (maxNbItem<=0)
×
1809
                                                break;
×
1810
                                        result.append(name);
×
UNCOV
1811
                                        --maxNbItem;
×
1812
                                }
1813
                        }
1814
                }
×
1815
                else if (it.key().at(0) != objPrefixUpper.at(0))
×
UNCOV
1816
                        break;
×
1817
        }
1818

1819
        QHashIterator<QString,StarId>ite(sciExtraDesignationsIndex);
×
UNCOV
1820
        while (ite.hasNext())
×
1821
        {
1822
                ite.next();
×
1823
                if (ite.key().indexOf(bayerRegEx)==0 || ite.key().indexOf(objPrefix)==0)
×
1824
                {
UNCOV
1825
                        if (maxNbItem<=0)
×
UNCOV
1826
                                break;
×
1827
                        QStringList names = getSciExtraDesignation(ite.value()).split(" - ");
×
UNCOV
1828
                        for (const auto &name : std::as_const(names))
×
1829
                        {
1830
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
UNCOV
1831
                                        found = true;
×
1832
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1833

1834
                                if (found)
×
1835
                                {
1836
                                        if (maxNbItem<=0)
×
1837
                                                break;
×
1838
                                        result.append(name);
×
1839
                                        --maxNbItem;
×
1840
                                }
1841
                        }
UNCOV
1842
                }
×
1843
                else if (ite.key().at(0) != objPrefix.at(0))
×
UNCOV
1844
                        break;
×
1845
        }
1846

1847
        ite.toFront(); // reset
×
1848
        while (ite.hasNext())
×
1849
        {
UNCOV
1850
                ite.next();
×
1851
                if (ite.key().indexOf(bayerRegExCI)==0 || ite.key().indexOf(objPrefixUpper)==0)
×
1852
                {
UNCOV
1853
                        if (maxNbItem<=0)
×
UNCOV
1854
                                break;
×
UNCOV
1855
                        QStringList names = getSciExtraDesignation(ite.value()).split(" - ");
×
1856
                        for (const auto &name : std::as_const(names))
×
1857
                        {
1858
                                if (useStartOfWords && name.startsWith(objPrefix, Qt::CaseInsensitive))
×
1859
                                        found = true;
×
UNCOV
1860
                                else found = (!useStartOfWords && name.contains(objPrefix, Qt::CaseInsensitive));
×
1861

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

1875
        // Search for sci names for var stars
UNCOV
1876
        QHashIterator<QString,StarId>itv(varStarsIndex);
×
1877
        while (itv.hasNext())
×
1878
        {
1879
                itv.next();
×
1880
                if (itv.key().startsWith(objPrefixUpper))
×
1881
                {
1882
                        if (maxNbItem<=0)
×
UNCOV
1883
                                break;
×
1884
                        result << getGcvsDesignation(itv.value());
×
UNCOV
1885
                        --maxNbItem;
×
1886
                }
1887
                else
1888
                        break;
×
1889
        }
1890

1891
        StarId sid;
1892
        // Add exact Hp catalogue numbers. The final part (A/B/...) is ignored
1893
        static const QRegularExpression hpRx("^(HIP|HP)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
1894
        QRegularExpressionMatch match=hpRx.match(objPrefixUpper);
×
UNCOV
1895
        if (match.hasMatch())
×
1896
        {
1897
                bool ok;
UNCOV
1898
                int hpNum = match.captured(2).toInt(&ok);
×
1899
                if (ok)
×
1900
                {
1901
                        StelObjectP s = searchHP(hpNum);
×
1902
                        if (s && maxNbItem>0)
×
1903
                        {
1904
                                result << QString("HIP%1").arg(hpNum);
×
UNCOV
1905
                                maxNbItem--;
×
1906
                        }
1907
                }
×
1908
        }
1909

1910
        // Add exact SAO catalogue numbers
1911
        static const QRegularExpression saoRx("^(SAO)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
UNCOV
1912
        match=saoRx.match(objPrefixUpper);
×
1913
        if (match.hasMatch())
×
1914
        {
1915
                int saoNum = match.captured(2).toInt();
×
1916
                if (saoStarsIndex.contains(saoNum))
×
1917
                {
1918
                        sid = saoStarsIndex.value(saoNum);
×
UNCOV
1919
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
UNCOV
1920
                        if (s && maxNbItem>0)
×
1921
                        {
1922
                                result << QString("SAO%1").arg(saoNum);
×
1923
                                maxNbItem--;
×
1924
                        }
UNCOV
1925
                }
×
1926
        }
1927

1928
        // Add exact HD catalogue numbers
UNCOV
1929
        static const QRegularExpression hdRx("^(HD)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1930
        match=hdRx.match(objPrefixUpper);
×
1931
        if (match.hasMatch())
×
1932
        {
1933
                int hdNum = match.captured(2).toInt();
×
UNCOV
1934
                if (hdStarsIndex.contains(hdNum))
×
1935
                {
1936
                        sid = hdStarsIndex.value(hdNum);
×
1937
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
1938
                        if (s && maxNbItem>0)
×
1939
                        {
1940
                                result << QString("HD%1").arg(hdNum);
×
UNCOV
1941
                                maxNbItem--;
×
1942
                        }
UNCOV
1943
                }
×
1944
        }
1945

1946
        // Add exact HR catalogue numbers
1947
        static const QRegularExpression hrRx("^(HR)\\s*(\\d+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
UNCOV
1948
        match=hrRx.match(objPrefixUpper);
×
UNCOV
1949
        if (match.hasMatch())
×
1950
        {
1951
                int hrNum = match.captured(2).toInt();
×
1952
                if (hrStarsIndex.contains(hrNum))
×
1953
                {
UNCOV
1954
                        sid = hrStarsIndex.value(hrNum);
×
1955
                        StelObjectP s =  (sid <= NR_OF_HIP) ? searchHP(sid) : searchGaia(sid);
×
UNCOV
1956
                        if (s && maxNbItem>0)
×
1957
                        {
UNCOV
1958
                                result << QString("HR%1").arg(hrNum);
×
1959
                                maxNbItem--;
×
1960
                        }
1961
                }
×
1962
        }
1963

1964
        // Add exact WDS catalogue numbers
1965
        static const QRegularExpression wdsRx("^(WDS)\\s*(\\S+)\\s*$", QRegularExpression::CaseInsensitiveOption);
×
1966
        if (wdsRx.match(objPrefixUpper).hasMatch())
×
1967
        {
UNCOV
1968
                QHashIterator<QString, StarId>wds(wdsStarsIndex);
×
1969
                while (wds.hasNext())
×
1970
                {
1971
                        wds.next();
×
UNCOV
1972
                        if (wds.key().startsWith(objPrefixUpper))
×
1973
                        {
1974
                                if (maxNbItem==0)
×
1975
                                        break;
×
1976
                                result << getWdsDesignation(wds.value());
×
UNCOV
1977
                                --maxNbItem;
×
1978
                        }
1979
                        else
1980
                                break;
×
1981
                }
UNCOV
1982
        }
×
1983

1984
        // Add exact Gaia DR3 catalogue numbers.
1985
        static const QRegularExpression gaiaRx("^\\s*(Gaia DR3)\\s*(\\d+)\\s*.*$", QRegularExpression::CaseInsensitiveOption);
×
UNCOV
1986
        match=gaiaRx.match(objPrefixUpper);
×
1987
        if (match.hasMatch())
×
1988
        {
1989
                bool ok;
1990
                StarId gaiaNum = match.captured(2).toLongLong(&ok);
×
1991
                if (ok)
×
1992
                {
UNCOV
1993
                        StelObjectP s = searchGaia(gaiaNum);
×
UNCOV
1994
                        if (s && maxNbItem>0)
×
1995
                        {
UNCOV
1996
                                result << QString("Gaia DR3 %1").arg(gaiaNum);
×
UNCOV
1997
                                maxNbItem--;
×
1998
                        }
UNCOV
1999
                }
×
2000
        }
2001

2002
        result.sort();        
×
UNCOV
2003
        return result;
×
UNCOV
2004
}
×
2005

2006
// Set display flag for Stars.
UNCOV
2007
void StarMgr::setFlagStars(bool b)
×
2008
{
2009
        starsFader=b;
×
UNCOV
2010
        StelApp::immediateSave("astro/flag_stars", b);
×
2011
        emit starsDisplayedChanged(b);
×
2012
}
×
2013
// Set display flag for Star names (labels).
2014
void StarMgr::setFlagLabels(bool b) {labelsFader=b; StelApp::immediateSave("astro/flag_star_name", b); emit starLabelsDisplayedChanged(b);}
×
2015
// Set the amount of star labels. The real amount is also proportional with FOV.
2016
// The limit is set in function of the stars magnitude
2017
// @param a the amount between 0 and 10. 0 is no labels, 10 is maximum of labels
2018
void StarMgr::setLabelsAmount(double a)
×
2019
{
2020
        if(!qFuzzyCompare(a,labelsAmount))
×
2021
        {
2022
                labelsAmount=a;
×
2023
                StelApp::immediateSave("stars/labels_amount", a);
×
2024
                emit labelsAmountChanged(a);
×
2025
        }
2026
}
×
2027

2028
// Define font file name and size to use for star names display
UNCOV
2029
void StarMgr::setFontSize(int newFontSize)
×
2030
{
2031
        starFont.setPixelSize(newFontSize);
×
UNCOV
2032
}
×
2033

2034
// Set flag for usage designations of stars for their labels instead common names.
UNCOV
2035
void StarMgr::setDesignationUsage(const bool flag)
×
2036
{
2037
        if(flagDesignations!=flag)
×
2038
        {
2039
                flagDesignations=flag;
×
UNCOV
2040
                StelApp::immediateSave("astro/flag_star_designation_usage", flag);
×
2041
                emit designationUsageChanged(flag);
×
2042
        }
2043
}
×
2044
// Set flag for usage traditional designations of double stars.
2045
void StarMgr::setFlagDblStarsDesignation(const bool flag)
×
2046
{
2047
        if(flagDblStarsDesignation!=flag)
×
2048
        {
2049
                flagDblStarsDesignation=flag;
×
2050
                StelApp::immediateSave("astro/flag_star_designation_dbl", flag);
×
UNCOV
2051
                emit flagDblStarsDesignationChanged(flag);
×
2052
        }
UNCOV
2053
}
×
2054
// Set flag for usage designations of variable stars.
UNCOV
2055
void StarMgr::setFlagVarStarsDesignation(const bool flag)
×
2056
{
2057
        if(flagVarStarsDesignation!=flag)
×
2058
        {
UNCOV
2059
                flagVarStarsDesignation=flag;
×
2060
                StelApp::immediateSave("astro/flag_star_designation_var", flag);
×
2061
                emit flagVarStarsDesignationChanged(flag);
×
2062
        }
UNCOV
2063
}
×
2064
// Set flag for usage Hipparcos catalog designations of stars.
2065
void StarMgr::setFlagHIPDesignation(const bool flag)
×
2066
{
UNCOV
2067
        if(flagHIPDesignation!=flag)
×
2068
        {
2069
                flagHIPDesignation=flag;
×
UNCOV
2070
                StelApp::immediateSave("astro/flag_star_designation_hip", flag);
×
2071
                emit flagHIPDesignationChanged(flag);
×
2072
        }
UNCOV
2073
}
×
2074
// Show additional star names.
2075
void StarMgr::setFlagAdditionalNames(bool flag)
×
2076
{
UNCOV
2077
        if (flagAdditionalStarNames!=flag)
×
2078
        {
UNCOV
2079
                flagAdditionalStarNames=flag;
×
2080
                StelApp::immediateSave("astro/flag_star_additional_names", flag);
×
UNCOV
2081
                emit flagAdditionalNamesDisplayedChanged(flag);
×
2082
        }
2083
}
×
2084

2085
void StarMgr::updateSkyCulture(const StelSkyCulture& skyCulture)
×
2086
{
UNCOV
2087
        const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
×
2088

UNCOV
2089
        const QString commonStarsFileName = StelFileMgr::findFile("skycultures/common_star_names.fab");
×
UNCOV
2090
        CommonNames commonNames;
×
UNCOV
2091
        if (commonStarsFileName.isEmpty())
×
UNCOV
2092
                qWarning() << "Could not load common_star_names.fab";
×
2093
        else
2094
                commonNames = loadCommonNames(commonStarsFileName);
×
2095

UNCOV
2096
        QMap<QString, int> commonNamesIndexToSearchWhileLoading = commonNames.hipByName;
×
UNCOV
2097
        commonNamesMap.clear();
×
2098
        commonNamesI18nMap.clear();
×
2099
        commonNamesI18nUppercaseIndex.clear();
×
UNCOV
2100
        commonNamesUppercaseIndex.clear();
×
2101
        culturalNamesMap.clear();
×
2102
        culturalNamesUppercaseIndex.clear();
×
2103

2104
        static QSettings* conf = StelApp::getInstance().getSettings();
×
2105
        Q_ASSERT(conf);
×
UNCOV
2106
        QString exclude=conf->value("SCExcludeReferences/"+skyCulture.id, QString()).toString();
×
2107
        QSet<int>excludeRefs;
×
UNCOV
2108
        if (!exclude.isEmpty())
×
2109
        {
2110
#if  (QT_VERSION<QT_VERSION_CHECK(5,14,0))
2111
                const QStringList excludeRefStrings = exclude.split(",", QString::SkipEmptyParts);
2112
#else
UNCOV
2113
                const QStringList excludeRefStrings = exclude.split(",", Qt::SkipEmptyParts);
×
2114
#endif
2115
                //qInfo() << "Skyculture" << skyCulture.id << "configured to exclude references" << excludeRefStrings;
UNCOV
2116
                for (const QString &s: excludeRefStrings)
×
2117
                {
2118
                        bool ok;
2119
                        int numRef=s.toInt(&ok); // ok=false for strings e.g. from asterisms
×
2120
                        if (ok)
×
UNCOV
2121
                                excludeRefs.insert(numRef);
×
2122
                }
UNCOV
2123
                qInfo() << "Skyculture" << skyCulture.id << "configured to exclude references" << excludeRefs;
×
UNCOV
2124
        }
×
2125

2126
        if (!skyCulture.names.isEmpty())
×
UNCOV
2127
                loadCultureSpecificNames(skyCulture.names, commonNamesIndexToSearchWhileLoading, excludeRefs);
×
2128

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

2137
                const QString& i18nName = trans.qTranslateStar(commonNames.byHIP[HIP]);
×
UNCOV
2138
                const QString i18nNameUpper = i18nName.toUpper();
×
2139

2140
                // TODO: Why do we have to reload the commonname* structures? Just load once at program start? Translate if needed, OK.
UNCOV
2141
                if (!commonNamesMap.contains(HIP))
×
2142
                {
2143
                        commonNamesMap[HIP] = englishName;
×
UNCOV
2144
                        commonNamesUppercaseIndex[englishNameUpper] = HIP;
×
2145
                        commonNamesI18nMap[HIP] = i18nName;
×
UNCOV
2146
                        commonNamesI18nUppercaseIndex[i18nNameUpper] = HIP;
×
2147
                }
2148

2149
                if (skyCulture.fallbackToInternationalNames)
×
2150
                {
2151
                        // Add name from commonNames, but only if not already in list.
UNCOV
2152
                        auto starIds=culturalNamesUppercaseIndex.values(englishNameUpper);
×
2153
                        if (!starIds.contains(HIP))
×
2154
                        {
2155
                                StelObject::CulturalName cName{englishName, QString(), QString(),
×
UNCOV
2156
                                                        englishName, englishName, i18nName, QString()};
×
2157
                                //if (culturalNamesMap.contains(HIP))
2158
                                //        qInfo() << "Adding additional cultural name for HIP" << HIP << ":" <<  cName.native << "/" << cName.pronounceI18n << "/" << cName.translated << "/" << cName.translatedI18n;
2159
                                culturalNamesMap.insert(HIP, cName); // add as possibly multiple entry to HIP.
×
UNCOV
2160
                                if (!cName.native.isEmpty())
×
2161
                                        culturalNamesUppercaseIndex.insert(cName.native.toUpper(), HIP);
×
UNCOV
2162
                                if (!cName.translatedI18n.isEmpty())
×
2163
                                        culturalNamesUppercaseIndex.insert(cName.translatedI18n.toUpper(), HIP);
×
UNCOV
2164
                        }
×
2165
                }
×
UNCOV
2166
        }
×
2167

2168
        // Turn on sci names/catalog names for modern cultures only
2169
        setFlagSciNames(skyCulture.englishName.contains("modern", Qt::CaseInsensitive));
×
UNCOV
2170
        updateI18n();
×
2171
}
×
2172

2173
void StarMgr::increaseStarsMagnitudeLimit()
×
2174
{
2175
        static StelCore* core = StelApp::getInstance().getCore();
×
UNCOV
2176
        core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() + 0.1);
×
2177
}
×
2178

2179
void StarMgr::reduceStarsMagnitudeLimit()
×
2180
{
2181
        static StelCore* core = StelApp::getInstance().getCore();
×
UNCOV
2182
        core->getSkyDrawer()->setCustomStarMagnitudeLimit(core->getSkyDrawer()->getCustomStarMagnitudeLimit() - 0.1);
×
2183
}
×
2184

2185
void StarMgr::populateStarsDesignations()
×
2186
{
2187
        QString filePath;
×
2188
        filePath = StelFileMgr::findFile("stars/hip_gaia3/name.fab");
×
2189
        if (filePath.isEmpty())
×
UNCOV
2190
                qWarning() << "Could not load scientific star names file: stars/hip_gaia3/name.fab";
×
2191
        else
UNCOV
2192
                loadSciDesignations(filePath, sciDesignationsMap, sciDesignationsIndex);
×
2193

UNCOV
2194
        filePath = StelFileMgr::findFile("stars/hip_gaia3/extra_name.fab");
×
2195
        if (filePath.isEmpty())
×
2196
                qWarning() << "Could not load scientific star extra names file: stars/hip_gaia3/extra_name.fab";
×
2197
        else
2198
                loadSciDesignations(filePath, sciExtraDesignationsMap, sciExtraDesignationsIndex);
×
2199

2200
        filePath = StelFileMgr::findFile("stars/hip_gaia3/gcvs.cat");
×
UNCOV
2201
        if (filePath.isEmpty())
×
2202
                qWarning() << "Could not load variable stars file: stars/hip_gaia3/gcvs.cat";
×
2203
        else
2204
                loadGcvs(filePath);
×
2205

2206
        filePath = StelFileMgr::findFile("stars/hip_gaia3/wds_hip_part.dat");
×
2207
        if (filePath.isEmpty())
×
2208
                qWarning() << "Could not load double stars file: stars/hip_gaia3/wds_hip_part.dat";
×
2209
        else
2210
                loadWds(filePath);
×
2211

2212
        filePath = StelFileMgr::findFile("stars/hip_gaia3/cross-id.cat");
×
2213
        if (filePath.isEmpty())
×
UNCOV
2214
                qWarning() << "Could not load cross-identification data file: stars/hip_gaia3/cross-id.cat";
×
2215
        else
UNCOV
2216
                loadCrossIdentificationData(filePath);
×
2217
        
UNCOV
2218
        filePath = StelFileMgr::findFile("stars/hip_gaia3/binary_orbitparam.dat");
×
2219
        if (filePath.isEmpty())
×
2220
                qWarning() << "Could not load binary orbital parameters data file: stars/hip_gaia3/binary_orbitparam.dat";
×
2221
        else
2222
                loadBinaryOrbitalData(filePath);
×
UNCOV
2223
}
×
2224

2225
QStringList StarMgr::listAllObjects(bool inEnglish) const
×
2226
{
2227
        QStringList result;
×
UNCOV
2228
        if (inEnglish)
×
2229
        {
UNCOV
2230
                result = commonNamesMap.values();
×
2231

2232
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
2233
                QHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
2234
#else
UNCOV
2235
                QMultiHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
×
2236
#endif
UNCOV
2237
                while (ci.hasNext())
×
2238
                {
2239
                        ci.next();
×
2240
                        result << ci.value().native;
×
2241
                        result << ci.value().translated;
×
2242
                        result << ci.value().pronounce;
×
2243
                        result << ci.value().transliteration;
×
2244
                }
UNCOV
2245
        }
×
2246
        else
2247
        {
2248
                result=commonNamesI18nMap.values();
×
2249

2250
#if  (QT_VERSION<QT_VERSION_CHECK(6,0,0))
2251
                QHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
2252
#else
UNCOV
2253
                QMultiHashIterator<StarId, StelObject::CulturalName> ci(culturalNamesMap);
×
2254
#endif
2255
                while (ci.hasNext())
×
2256
                {
UNCOV
2257
                        ci.next();
×
2258
                        result << ci.value().native;
×
UNCOV
2259
                        result << ci.value().translatedI18n;
×
2260
                        result << ci.value().pronounceI18n;
×
2261
                        result << ci.value().transliteration;
×
2262
                }
UNCOV
2263
        }
×
2264
        result.removeDuplicates();
×
UNCOV
2265
        result.removeAll(QString(""));
×
2266
        result.removeAll(QString());
×
2267
        return result;
×
2268
}
×
2269

UNCOV
2270
QStringList StarMgr::listAllObjectsByType(const QString &objType, bool inEnglish) const
×
2271
{
UNCOV
2272
        QStringList result;        
×
2273
        // type 1
2274
        bool isStarT1 = false;
×
2275
        QList<StelObjectP> starsT1;
×
2276
        // type 2
2277
        bool isStarT2 = false;
×
UNCOV
2278
        QList<StelACStarData> starsT2;
×
2279
        int type = objType.toInt();        
×
2280
        switch (type)
×
2281
        {
UNCOV
2282
                case 0: // Interesting double stars
×
2283
                {
2284
                        static const QStringList doubleStars = {
2285
                                "21 Tau", "27 Tau", "77 Tau", "δ1 Tau", "V1016 Ori",
2286
                                "42 Ori", "ι Ori", "ζ Crv", "ζ UMa", "α2 Lib", "α1 Cru",
2287
                                "ω1 Sco", "λ Sco", "μ1 Sco", "ζ1 Sco", "ε1 Lyr", "ε2 Lyr",
2288
                                "δ1 Lyr",         "ν1 Sgr", "ο1 Cyg", "ο2 Cyg", "α2 Cap", "β1 Cyg",
2289
                                "β Ori", "γ1 And", "ξ Boo", "α1 Her", "T Dra", "ν1 Dra",
2290
                                "70 Oph", "α Gem", "ζ Her", "ο2 Eri", "γ1 Ari", "γ Vir",
2291
                                "γ1 Leo", "β Mon", "ε Boo", "44 Boo", "β1 Sco", "ζ1 Cnc",
2292
                                "φ2 Cnc", "α Leo", "α2 CVn", "ι Cas", "ε Ari", "κ Vel", "γ1 Del",
2293
                                "61 Cyg B", "55 Aqr", "σ Cas", "η Cas", "α UMi", "36 Oph",
2294
                                "α1 Cen",  "65 UMa", "σ2 UMa", "55 Cnc", "16 Cyg A",
2295
                                "HIP 28393", "HIP 84709"};
×
UNCOV
2296
                        result = doubleStars;
×
2297
                        break;
×
2298
                }
2299
                case 1: // Interesting variable stars
×
2300
                {
2301
                        static const QStringList variableStars = {
2302
                                "δ Cep", "β Per", "ο Cet", "λ Tau", "β Lyr", "ζ Gem", "μ Cep",
2303
                                "α1 Her", "η Gem", "η Aql", "γ Cas", "α Ori", "R And",
2304
                                "U Ant", "θ Aps", "R Aql", "V Aql", "R Aqr", "ε Aur", "R Aur",
2305
                                "AE Aur", "W Boo", "VZ Cam", "l Car", "WZ Cas",        "S Cen",
2306
                                "α Cen C", "T Cep", "U Cep", "R CMa", "VY CMa",
2307
                                "S Cnc", "α CrB", "R CrB", "T CrB", "U CrB", "R Cru",
2308
                                "SU Cyg", "EU Del", "β Dor", "R Gem", "30 Her", "68 Her",
2309
                                "R Hor", "R Lep", "R Leo", "RR Lyr", "U Mon", "R Hya", "χ Cyg",
2310
                                "δ Ori", "VV Ori", "κ Pav", "β Peg", "ε Peg", "ζ Phe", "R Sct",
UNCOV
2311
                                "U Sgr", "RY Sgr", "W UMa", "α UMi"};
×
2312
                        result = variableStars;
×
2313
                        break;
×
2314
                }
2315
                case 2: // Bright double stars
×
2316
                {
UNCOV
2317
                        starsT2 = doubleHipStars;
×
2318
                        isStarT2 = true;
×
2319
                        break;
×
2320
                }
2321
                case 3: // Bright variable stars
×
2322
                {
UNCOV
2323
                        starsT2 = variableHipStars;
×
2324
                        isStarT2 = true;
×
2325
                        break;
×
2326
                }
UNCOV
2327
                case 4:
×
2328
                {
UNCOV
2329
                        starsT2 = hipStarsHighPM;
×
2330
                        isStarT2 = true;
×
UNCOV
2331
                        break;
×
2332
                }
2333
                case 5: // Variable stars: Algol-type eclipsing systems
×
2334
                {
2335
                        starsT2 = algolTypeStars;
×
2336
                        isStarT2 = true;
×
2337
                        break;
×
2338
                }
UNCOV
2339
                case 6: // Variable stars: the classical cepheids
×
2340
                {
UNCOV
2341
                        starsT2 = classicalCepheidsTypeStars;
×
UNCOV
2342
                        isStarT2 = true;
×
UNCOV
2343
                        break;
×
2344
                }
UNCOV
2345
                case 7: // Bright carbon stars
×
2346
                {
UNCOV
2347
                        starsT1 = carbonStars;
×
UNCOV
2348
                        isStarT1 = true;
×
UNCOV
2349
                        break;
×
2350
                }
UNCOV
2351
                case 8: // Bright barium stars
×
2352
                {
2353
                        starsT1 = bariumStars;
×
2354
                        isStarT1 = true;
×
2355
                        break;
×
2356
                }
2357
                default:
×
2358
                {
2359
                        // No stars yet?
UNCOV
2360
                        break;
×
2361
                }
2362
        }
2363

UNCOV
2364
        QString starName;
×
UNCOV
2365
        if (isStarT1)
×
2366
        {
UNCOV
2367
                for (const auto& star : std::as_const(starsT1))
×
2368
                {
2369
                        starName = inEnglish ? star->getEnglishName() : star->getNameI18n();
×
2370
                        if (!starName.isEmpty())
×
2371
                                result << starName;
×
2372
                        else
2373
                                result << star->getID();
×
2374
                }
2375
        }
2376

2377
        if (isStarT2)
×
2378
        {
2379
                for (const auto& star : std::as_const(starsT2))
×
2380
                {
2381
                        starName = inEnglish ? star.first->getEnglishName() : star.first->getNameI18n();
×
2382
                        if (!starName.isEmpty())
×
2383
                                result << starName;
×
2384
                        else
2385
                                result << star.first->getID();
×
2386
                }
2387
        }
2388

2389
        result.removeDuplicates();
×
UNCOV
2390
        return result;
×
2391
}
×
2392

2393
QString StarMgr::getStelObjectType() const
×
2394
{
2395
        return STAR_TYPE;
×
2396
}
2397

2398
//! cultural names
2399
//! Return screen label (to be used in the sky display. Most users will use some short label)
2400
QString StarMgr::getCulturalScreenLabel(StarId hip)
×
2401
{
UNCOV
2402
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2403
        QStringList list=getCultureLabels(hip, scMgr->getScreenLabelStyle());
×
2404
        //qDebug() << "culturalScreenLabel: " << list;
2405
        return list.isEmpty() ? QString() : list.constFirst();
×
2406
}
×
2407

2408
//! Return InfoString label (to be used in the InfoString).
2409
//! When dealing with foreign skycultures, many users will want this to be longer, with more name components.
UNCOV
2410
QString StarMgr::getCulturalInfoLabel(StarId hip)
×
2411
{
2412
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2413
        QStringList list=getCultureLabels(hip, scMgr->getInfoLabelStyle());
×
UNCOV
2414
        return list.isEmpty() ? "" : list.join(" - ");
×
2415
}
×
2416

UNCOV
2417
QStringList StarMgr::getCultureLabels(StarId hip, StelObject::CulturalDisplayStyle style)
×
2418
{
UNCOV
2419
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
2420
        // Retrieve list in order as read from JSON
UNCOV
2421
        QList<StelObject::CulturalName>culturalNames=getCulturalNames(hip);
×
2422
        if (culturalNames.isEmpty())
×
2423
        {
UNCOV
2424
                return QStringList();
×
2425
        }
2426

2427
        QStringList labels;
×
2428
        for (auto &cName: culturalNames)
×
2429
                {
UNCOV
2430
                        QString commonNamei18=getCommonNameI18n(hip);
×
2431
                        if (commonNamei18.isEmpty())
×
2432
                        {
UNCOV
2433
                                QString sciDes=getSciDesignation(hip);
×
UNCOV
2434
                                if (sciDes.isEmpty())
×
2435
                                        sciDes=getSciExtraDesignation(hip);
×
2436

2437
                                commonNamei18=sciDes.split(" - ").first();
×
UNCOV
2438
                        }
×
2439
                        QString label=scMgr->createCulturalLabel(cName, style, commonNamei18);
×
2440
                        labels << label;
×
2441
                }
×
UNCOV
2442
        labels.removeDuplicates();
×
2443
        labels.removeAll(QString(""));
×
UNCOV
2444
        labels.removeAll(QString());
×
UNCOV
2445
        return labels;
×
UNCOV
2446
}
×
2447

2448

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