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

Stellarium / stellarium / 15670918640

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

push

github

alex-w
Updated data

14700 of 124846 relevant lines covered (11.77%)

18324.52 hits per line

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

0.0
/src/core/modules/Asterism.cpp
1
/*
2
 * Stellarium
3
 * Copyright (C) 2017 Alexander Wolf
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18
 */
19

20
#include "StelProjector.hpp"
21
#include "Asterism.hpp"
22
#include "StelModuleMgr.hpp"
23
#include "StarMgr.hpp"
24
#include "NebulaMgr.hpp"
25

26
#include "StelPainter.hpp"
27
#include "StelApp.hpp"
28
#include "StelCore.hpp"
29
#include "StelUtils.hpp"
30
#include "ZoneArray.hpp"
31
#include "StelModuleMgr.hpp"
32
#include "StelSkyCultureMgr.hpp"
33
#include <QMessageBox>
34

35
#include <QString>
36
#include <QTextStream>
37
#include <QDebug>
38
#include <QJsonArray>
39
#include <QJsonObject>
40
#include <QFontMetrics>
41
#include <QIODevice>
42

43
Vec3f Asterism::lineColor = Vec3f(0.4f,0.4f,0.8f);
44
Vec3f Asterism::rayHelperColor = Vec3f(1.0f,1.0f,0.0f);
45
Vec3f Asterism::labelColor = Vec3f(0.4f,0.4f,0.8f);
46
const QString Asterism::ASTERISM_TYPE = QStringLiteral("Asterism");
47

48
Asterism::Asterism()
×
49
        : flagAsterism(true)
×
50
        , singleStarAsterismRadius(cos(M_PI/360.)) // default radius of 1/2 degrees
×
51
{
52
}
×
53

54
Asterism::~Asterism()
×
55
{
56
}
×
57

58
bool Asterism::read(const QJsonObject& data, StarMgr *starMgr, const QSet<int> &excludeRefs)
×
59
{
60
        static NebulaMgr *nebulaMgr=GETSTELMODULE(NebulaMgr);
×
61
        static QSettings *conf=StelApp::getInstance().getSettings();
×
62

63
        const QString id = data["id"].toString();
×
64
        const QStringList idParts = id.split(" ");
×
65
        QString scName;
×
66
        if (idParts.size() == 3 && idParts[0] == "AST")
×
67
        {
68
                abbreviation = idParts[2].trimmed();
×
69
                scName = idParts[1].trimmed();
×
70
                if (scName != GETSTELMODULE(StelSkyCultureMgr)->getCurrentSkyCultureID())
×
71
                {
72
                        qWarning().nospace() << "Skyculture definition error (skyculture name mismatch) in asterism " << id
×
73
                                             << " of skyculture " << GETSTELMODULE(StelSkyCultureMgr)->getCurrentSkyCultureID()
×
74
                                             << ". Skipping this asterism.";
×
75
                        return false;
×
76
                }
77
        }
78
        else
79
        {
80
                qWarning().nospace() << "Bad asterism id: expected \"AST cultureName Abbrev\", got " << id;
×
81
                return false;
×
82
        }
83

84
        // Allow exclusion by user configuration!
85
        QString exclude=conf->value(QString("SCExcludeReferences/%1").arg(scName), QString()).toString();
×
86
        if (!exclude.isEmpty())
×
87
        {
88
#if  (QT_VERSION<QT_VERSION_CHECK(5,14,0))
89
                QStringList excludeRefStrings=exclude.split(',', QString::SkipEmptyParts);
90
#else
91
                QStringList excludeRefStrings=exclude.split(',', Qt::SkipEmptyParts);
×
92
#endif
93
                // The list has potentially both, unwanted references (which we just take out) and additional asterism abbrevations.
94
                QMutableListIterator<QString> it(excludeRefStrings);
×
95
                while (it.hasNext())
×
96
                {
97
                        bool ok;
98
                        int numRef=it.next().toInt(&ok); // ok=false for strings e.g. from asterisms
×
99
                        if (ok)
×
100
                        {
101
                                Q_UNUSED(numRef);
102
                                it.remove();
×
103
                        }
104
                }
105
                //qInfo() << "Skyculture" << scName << "configured to exclude asterisms referenced from" << excludeRefs;
106

107
                QVariantList refsVariants=data["references"].toArray().toVariantList();
×
108
                if (!refsVariants.isEmpty())
×
109
                {
110
                        QSet<int> refs;
×
111
                        foreach(const QVariant &v, refsVariants) {
×
112
                            refs << v.value<int>();
×
113
                        }
×
114
                        //qInfo() << "Asterism" << id << "has refs" << refs;
115
                        if (refs.subtract(excludeRefs).isEmpty())
×
116
                        {
117
                                //qInfo() << "Asterism" << id << "has lost reference support. Skipping.";
118
                                return false;
×
119
                        }
120
                }
×
121

122
                // The remaining elements are (hopefully) unique asterism abbreviations.
123
                if (excludeRefStrings.contains(abbreviation))
×
124
                {
125
                        qDebug().nospace() << "Asterism " << id << " excluded by user config. Skipping.";
×
126
                        return false;
×
127
                }
128
        }
×
129

130
        const QJsonValue names = data["common_name"];
×
131
        if (names.isObject())
×
132
        {
133
                culturalName.translated = names["english"].toString().trimmed();
×
134
                culturalName.native = names["native"].toString().trimmed();
×
135
                culturalName.pronounce = names["pronounce"].toString().trimmed();
×
136
                culturalName.IPA = names["IPA"].toString().trimmed();
×
137
                culturalName.transliteration = names["transliteration"].toString().trimmed();
×
138
        }
139
        const QJsonArray polylines = data["lines"].toArray();
×
140
        asterism.clear();
×
141

142
        flagAsterism = !data["is_ray_helper"].toBool();
×
143
        typeOfAsterism = flagAsterism ? Type::Asterism : Type::RayHelper;
×
144

145
        // TODO: Apparently ray helpers have no name. This could act to autodetect all this.
146
        if(names.isObject()) Q_ASSERT(flagAsterism);
×
147

148
        if (polylines.isEmpty())
×
149
        {
150
                qWarning().nospace() << "Empty asterism lines array found for asterism " << id << " (" << culturalName.native << ")";
×
151
                return false;
×
152
        }
153

154
        if (!polylines[0].toArray().isEmpty() && polylines[0].toArray()[0].isArray())
×
155
        {
156
                qWarning().nospace() << "Coordinate array detected for asterism" << id << ". This is obsolete. Reformulate with Gaia stars. Skipping.";
×
157
                //if (polylines[0].toArray()[0].toArray().size() != 2)
158
                //{
159
                //        qWarning().nospace() << "Bad asterism point entry for asterism " << id
160
                //                             << " (" << culturalName.native << "): expected size 2, got " << polylines[0].toArray()[0].toArray().size();
161
                //        return false;
162
                //}
163
                //if (typeOfAsterism == Type::RayHelper)
164
                //{
165
                //        qWarning() << "Mismatch between asterism type and line data: got a ray helper, "
166
                //                      "but the line points contain two entries instead of one";
167
                //        return false;
168
                //}
169
                // The entry contains RA and dec instead of HIP catalog number
170
                typeOfAsterism = Type::TelescopicAsterism;
×
171
        }
172

173
        StelCore *core = StelApp::getInstance().getCore();
×
174
        for (int lineIndex = 0; lineIndex < polylines.size(); ++lineIndex)
×
175
        {
176
                if (!polylines[lineIndex].isArray())
×
177
                {
178
                        qWarning().nospace() << "Bad asterism line #" << lineIndex << ": is not an array";
×
179
                        return false;
×
180
                }
181
                const auto polyline = polylines[lineIndex].toArray();
×
182
                for (int pointIndex = 0; pointIndex < polyline.size(); ++pointIndex)
×
183
                {
184
                        const auto point = polyline[pointIndex];
×
185
                        StelObjectP newObj;
×
186
                        switch (typeOfAsterism)
×
187
                        {
188
                        case Type::RayHelper:
×
189
                        case Type::Asterism:
190
                        {
191
                                if (point.isString() && point.toString().startsWith("DSO:"))
×
192
                                {
193
                                        QString DSOname=point.toString().remove(0,4);
×
194
                                        newObj = nebulaMgr->searchByID(DSOname);
×
195
                                        if (!newObj)
×
196
                                        {
197
                                                qWarning().nospace() << "Error in asterism " << abbreviation << ": can't find DSO " << DSOname << "... skipping asterism";
×
198
                                                return false;
×
199
                                        }
200
                                }
×
201
                                else if (!point.isDouble() && !point.isString())
×
202
                                {
203
                                        qWarning().nospace() << "Error in asterism " << abbreviation << ": bad point at line #"
×
204
                                                             << lineIndex << ": isn't a number";
×
205
                                        return false;
×
206
                                }
207
                                else
208
                                {
209
                                        const StarId HIP = StelUtils::getLongLong(point);
×
210
                                        if (HIP>0)
×
211
                                        {
212
                                                newObj = HIP <= NR_OF_HIP ? starMgr->searchHP(HIP)
×
213
                                                                          : starMgr->searchGaia(HIP);
×
214

215
                                                if (!newObj)
×
216
                                                {
217
                                                        qWarning().nospace() << "Error in asterism " << abbreviation << ": can't find star HIP " << HIP << "... skipping asterism";
×
218
                                                        return false;
×
219
                                                }
220
                                        }
221
                                        else
222
                                        {
223
                                                qWarning().nospace() << "Error in asterism " << abbreviation << ": bad element: " << point.toString() << "... skipping asterism";
×
224
                                                return false;
×
225
                                        }
226
                                }
227
                                asterism.push_back(newObj);
×
228

229
                                if (!asterism.back())
×
230
                                {
231
                                        asterism.pop_back();
×
232
                                        qWarning().nospace() << "Error in asterism " << abbreviation <<
×
233
                                                                " (Skipping asterism. Install more catalogs?)";
×
234
                                        return false;
×
235
                                }
236
                                break;
×
237
                        }
238
                        case Type::TelescopicAsterism:
×
239
                        {
240
                                qWarning() << "Asterisms with coord list no longer supported. Skipping " << id;
×
241
                                /*
242
                                if (!point.isArray())
243
                                {
244
                                        qWarning().nospace() << "Error in asterism " << id << ": bad point at line #"
245
                                                             << lineIndex << ": isn't an array of two numbers (RA and dec)";
246
                                        return false;
247
                                }
248
                                const QJsonArray arr = point.toArray();
249
                                if (arr.size() != 2 || !arr[0].isDouble() || !arr[1].isDouble())
250
                                {
251
                                        qWarning().nospace() << "Error in asterism " << id << ": bad point at line #"
252
                                                             << lineIndex << ": isn't an array of two numbers (RA and dec)";
253
                                        return false;
254
                                }
255

256
                                const double RA = arr[0].toDouble();
257
                                const double DE = arr[1].toDouble();
258

259
                                Vec3d coords;
260
                                StelUtils::spheToRect(RA*M_PI/12., DE*M_PI/180., coords);
261
                                const QList<StelObjectP> stars = starMgr->searchAround(coords, 0.25, core);
262
                                // Find star closest to coordinates
263
                                StelObjectP s = nullptr;
264
                                double d = 10.;
265
                                for (const auto& p : stars)
266
                                {
267
                                        double a = coords.angle(p->getJ2000EquatorialPos(core));
268
                                        if (a<d)
269
                                        {
270
                                                d = a;
271
                                                s = p;
272
                                        }
273
                                }
274
                                asterism.push_back(s);
275
                                if (!asterism.back())
276
                                {
277
                                        qWarning() << "Error in asterism" << id << "- can't find star with coordinates" << RA << "/" << DE;
278
                                        return false;
279
                                }
280
                                */
281
                                break;
×
282
                        }
283
                        }
284
                        // Expand the polyline into a sequence of segments
285
                        if (pointIndex != 0 && pointIndex != polyline.size() - 1)
×
286
                                asterism.push_back(asterism.back());
×
287
                }
×
288
        }
×
289

290
        if (data.contains("single_star_radius"))
×
291
        {
292
                double rd = data["single_star_radius"].toDouble(0.5);
×
293
                singleStarAsterismRadius = cos(rd*M_PI/180.);
×
294
        }
295

296
        if (typeOfAsterism != Type::RayHelper)
×
297
        {
298
                Vec3d XYZname1(0.);
×
299
                for(const auto& point : asterism)
×
300
                        XYZname1 += point->getJ2000EquatorialPos(core);
×
301
                XYZname1.normalize();
×
302
                XYZname.append(XYZname1);
×
303

304
                // Sometimes label placement is suboptimal. Allow a correction from the automatic solution in label_offset:[dRA_deg, dDec_deg]
305
                if (data.contains("label_offset"))
×
306
                {
307
                        QJsonArray offset=data["label_offset"].toArray();
×
308
                        if (offset.size()!=2)
×
309
                                qWarning() << "Bad constellation label offset given for " << id << ", ignoring";
×
310
                        else
311
                        {
312
                                double ra, dec;
313
                                StelUtils::rectToSphe(&ra, &dec, XYZname[0]);
×
314
                                ra  += offset[0].toDouble()*M_PI_180;
×
315
                                dec += offset[1].toDouble()*M_PI_180;
×
316
                                StelUtils::spheToRect(ra, dec, XYZname[0]);
×
317
                        }
318
                }
×
319
                // Asterism label placement: Manual position can have more than one.
320
                if (data.contains("label_positions"))
×
321
                {
322
                        XYZname.clear();
×
323
                        const QJsonArray &labelPosArray=data["label_positions"].toArray();
×
324
                        for (const auto& labelPos : labelPosArray)
×
325
                        {
326
                                const auto& labelArray=labelPos.toArray();
×
327
                                if (labelArray.size() != 2)
×
328
                                {
329
                                        qWarning() << "Bad label position given for asterism" << abbreviation << "... skipping";
×
330
                                        continue;
×
331
                                }
332
                                const double RA = labelArray[0].toDouble() * (M_PI_180*15.);
×
333
                                const double DE = labelArray[1].toDouble() * M_PI_180;
×
334
                                Vec3d newPoint;
×
335
                                StelUtils::spheToRect(RA, DE, newPoint);
×
336
                                XYZname.append(newPoint);
×
337
                        }
×
338
                }
×
339
        }
340
        return true;
×
341
}
×
342

343
QString Asterism::getScreenLabel() const
×
344
{
345
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
346
        return getCultureLabel(scMgr->getScreenLabelStyle());
×
347
}
348
QString Asterism::getInfoLabel() const
×
349
{
350
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
351
        return getCultureLabel(scMgr->getInfoLabelStyle());
×
352
}
353

354
QString Asterism::getCultureLabel(StelObject::CulturalDisplayStyle style) const
×
355
{
356
        static StelSkyCultureMgr *scMgr=GETSTELMODULE(StelSkyCultureMgr);
×
357
        return scMgr->createCulturalLabel(culturalName, style, getNameI18n(), abbreviationI18n);
×
358
}
359

360
void Asterism::drawOptim(StelPainter& sPainter, const StelCore* core, const SphericalCap& viewportHalfspace) const
×
361
{
362
        if (flagAsterism)
×
363
        {
364
                if (lineFader.getInterstate()<=0.0001f)
×
365
                        return;
×
366

367
                sPainter.setColor(lineColor, lineFader.getInterstate());
×
368
        }
369
        else
370
        {
371
                if (rayHelperFader.getInterstate()<=0.0001f)
×
372
                        return;
×
373

374
                sPainter.setColor(rayHelperColor, rayHelperFader.getInterstate());
×
375
        }
376

377
        Vec3d star1;
×
378
        Vec3d star2;
×
379
        for (unsigned int i = 0; i < asterism.size() / 2; ++i)
×
380
        {
381
                star1=asterism[2*i]->getJ2000EquatorialPos(core);
×
382
                star2=asterism[2*i+1]->getJ2000EquatorialPos(core);
×
383
                star1.normalize();
×
384
                star2.normalize();
×
385
                if (star1.fuzzyEquals(star2))
×
386
                {
387
                        // draw single-star segment as circle
388
                        SphericalCap saCircle(star1, singleStarAsterismRadius);
×
389
                        sPainter.drawSphericalRegion(&saCircle, StelPainter::SphericalPolygonDrawModeBoundary);
×
390
                }
×
391
                else
392
                        sPainter.drawGreatCircleArc(star1, star2, &viewportHalfspace);
×
393
        }
394
}
395

396
// observer centered J2000 coordinates.
397
// These are either automatically computed from all stars forming the lines,
398
// or from the manually defined label point(s).
399
Vec3d Asterism::getJ2000EquatorialPos(const StelCore*) const
×
400
{
401
        if (XYZname.length() ==1)
×
402
                return XYZname.first();
×
403
        else
404
        {
405
                Vec3d point(0.0);
×
406
                for (Vec3d namePoint: XYZname)
×
407
                {
408
                        point += namePoint;
×
409
                }
410
                point.normalize();
×
411
                return point;
×
412
        }
413
}
414

415
void Asterism::drawName(const Vec3d &xyName, StelPainter& sPainter) const
×
416
{
417
        if ((nameFader.getInterstate()==0.0f) || !flagAsterism)
×
418
                return;
×
419

420
        if (typeOfAsterism==Type::TelescopicAsterism && sPainter.getProjector()->getFov()>60.f)
×
421
                return;
×
422

423
        QString name = getScreenLabel();
×
424
        sPainter.setColor(labelColor, nameFader.getInterstate());
×
425
        sPainter.drawText(static_cast<float>(xyName[0]), static_cast<float>(xyName[1]), name, 0., -sPainter.getFontMetrics().boundingRect(name).width()/2, 0, false);
×
426
}
×
427

428
void Asterism::update(int deltaTime)
×
429
{
430
        lineFader.update(deltaTime);
×
431
        rayHelperFader.update(deltaTime);
×
432
        nameFader.update(deltaTime);
×
433
}
×
434

435
QString Asterism::getInfoString(const StelCore *core, const InfoStringGroup &flags) const
×
436
{
437
        Q_UNUSED(core)
438
        QString str;
×
439
        QTextStream oss(&str);
×
440

441
        if (flags&Name)
×
442
                oss << "<h2>" << getInfoLabel() << "</h2>";
×
443

444
        if (flags&ObjectType)
×
445
                oss << QString("%1: <b>%2</b>").arg(q_("Type"), getObjectTypeI18n()) << "<br />";
×
446

447
        oss << getSolarLunarInfoString(core, flags);
×
448
        postProcessInfoString(str, flags);
×
449

450
        return str;
×
451
}
×
452

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