Kstars

nameresolver.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Akarsh Simha <akarsh.simha@kdemail.net>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7/* Project Includes */
8#include "nameresolver.h"
9#include "catalogobject.h"
10
11/* KDE Includes */
12#ifndef KSTARS_LITE
13#include <kio/filecopyjob.h>
14#else
15#include "kstarslite.h"
16#endif
17
18/* Qt Includes */
19#include <QUrl>
20#include <QTemporaryFile>
21#include <QString>
22#include <QXmlStreamReader>
23#include <QNetworkRequest>
24#include <QNetworkReply>
25#include <QNetworkAccessManager>
26#include <QEventLoop>
27
28#include <kstars_debug.h>
29
30std::pair<bool, CatalogObject> NameResolver::resolveName(const QString &name)
31{
32 const auto &found_sesame{ NameResolverInternals::sesameResolver(name) };
33 if (!found_sesame.first)
34 {
35 QString msg =
36 xi18n("Error: sesameResolver failed. Could not resolve name on CDS Sesame.");
37 qCDebug(KSTARS) << msg;
38
39#ifdef KSTARS_LITE
40 KStarsLite::Instance()->notificationMessage(msg);
41#endif
42 }
43 // More to be done here if the resolved name is SIMBAD
44 return found_sesame;
45}
46
47std::pair<bool, CatalogObject>
48NameResolver::NameResolverInternals::sesameResolver(const QString &name)
49{
51 QString("http://cdsweb.u-strasbg.fr/cgi-bin/nph-sesame/-oxpFI/SNV?%1").arg(name));
52
53 QString msg = xi18n("Attempting to resolve object %1 using CDS Sesame.", name);
54 qCDebug(KSTARS) << msg;
55
56#ifdef KSTARS_LITE
57 KStarsLite::Instance()->notificationMessage(msg);
58#endif
59
63
64 // Wait synchronously
65 QEventLoop event;
66 QObject::connect(response, SIGNAL(finished()), &event, SLOT(quit()));
67 event.exec();
68
69 if (response->error() != QNetworkReply::NoError)
70 {
71 msg = xi18n("Error trying to get XML response from CDS Sesame server: %1",
72 response->errorString());
73 qWarning() << msg;
74
75#ifdef KSTARS_LITE
76 KStarsLite::Instance()->notificationMessage(msg);
77#endif
78 return { false, {} };
79 }
80
81 QXmlStreamReader xml(response->readAll());
82 response->deleteLater();
83 if (xml.atEnd())
84 {
85 // file is empty
86 msg = xi18n("Empty result instead of expected XML from CDS Sesame. Maybe bad "
87 "Internet connection?");
88 qCDebug(KSTARS) << msg;
89
90#ifdef KSTARS_LITE
91 KStarsLite::Instance()->notificationMessage(msg);
92#endif
93 return { false, {} };
94 }
95
96 CatalogObject data{
97 CatalogObject::oid{}, SkyObject::STAR, dms{ 0 }, dms{ 0 }, 0, name, name
98 };
99
100 bool found{ false };
101 while (!xml.atEnd() && !xml.hasError())
102 {
103 QXmlStreamReader::TokenType token = xml.readNext();
104
105 if (xml.isStartDocument())
106 continue;
107
109 {
110 qCDebug(KSTARS) << "Parsing token with name " << xml.name();
111
112 if (xml.name() == "Resolver")
113 {
114 found = true;
115 // This is the section we want
116 char resolver = 0;
117 QXmlStreamAttributes attributes = xml.attributes();
118 if (attributes.hasAttribute("name"))
119 resolver =
120 attributes.value("name")
121 .at(0)
122 .toLatin1(); // Expected to be S (Simbad), V (VizieR), or N (NED)
123 else
124 {
125 resolver = 0; // NUL character for unknown resolver
126 qWarning() << "Warning: Unknown resolver "
127 << attributes.value("name ")
128 << " while reading output from CDS Sesame";
129 }
130
131 qCDebug(KSTARS)
132 << "Resolved by " << resolver << attributes.value("name") << "!";
133
134 // Start reading the data to pick out the relevant ones
135 while (xml.readNextStartElement())
136 {
137 if (xml.name() == "otype")
138 {
139 const QString typeString = xml.readElementText();
140 data.setType(interpretObjectType(typeString));
141 }
142 else if (xml.name() == "jradeg")
143 {
144 data.setRA0(dms{ xml.readElementText().toDouble() });
145 }
146 else if (xml.name() == "jdedeg")
147 {
148 data.setDec0(dms{ xml.readElementText().toDouble() });
149 }
150 else if (xml.name() == "mag")
151 {
152 attributes = xml.attributes();
153 char band;
154 if (attributes.hasAttribute("band"))
155 {
156 band = attributes.value("band").at(0).toLatin1();
157 }
158 else
159 {
160 qWarning() << "Warning: Magnitude of unknown band found "
161 "while reading output from CDS Sesame";
162 band = 0;
163 }
164
165 float mag = NaN::f;
166 xml.readNext();
167 if (xml.isCharacters())
168 {
169 qCDebug(KSTARS) << "characters: " << xml.tokenString();
170 mag = xml.tokenString().toFloat();
171 }
172 else if (xml.isStartElement())
173 {
174 while (xml.name() != "v")
175 {
176 qCDebug(KSTARS) << "element: " << xml.name();
177 xml.readNextStartElement();
178 }
179 mag = xml.readElementText().toFloat();
180 qCDebug(KSTARS)
181 << "Got " << xml.tokenString() << " mag = " << mag;
182 while (!xml.atEnd() && xml.readNext() && xml.name() != "mag")
183 ; // finish reading the <mag> tag all the way to </mag>
184 }
185 else
186 qWarning()
187 << "Failed to parse Xml token in magnitude element: "
188 << xml.tokenString();
189
190 if (band == 'V')
191 {
192 data.setMag(mag);
193 }
194 else if (band == 'B')
195 {
196 data.setFlux(mag); // FIXME: This is bad
197 if (std::isnan(data.mag()))
198 data.setMag(mag); // FIXME: This is bad too
199 }
200 // Don't know what to do with other magnitudes, until we have a magnitude hash
201 }
202 else if (xml.name() == "oname") // Primary identifier
203 {
204 QString contents = xml.readElementText();
205 data.setCatalogIdentifier(contents);
206 }
207 else
208 xml.skipCurrentElement();
209 // TODO: Parse aliases for common names
210 }
211 break;
212 }
213 else
214 continue;
215 }
216 }
217 if (xml.hasError())
218 {
219 msg = xi18n("Error parsing XML from CDS Sesame: %1 on line %2 @ col = %3",
220 xml.errorString(), xml.lineNumber(), xml.columnNumber());
221 qCDebug(KSTARS) << msg;
222
223#ifdef KSTARS_LITE
224 KStarsLite::Instance()->notificationMessage(msg);
225#endif
226 return { false, {} };
227 }
228
229 if (!found)
230 return { false, {} };
231
232 msg = xi18n("Resolved %1 successfully.", name);
233 qCDebug(KSTARS) << msg;
234
235#ifdef KSTARS_LITE
236 KStarsLite::Instance()->notificationMessage(msg);
237#endif
238 qCDebug(KSTARS) << "Object type: " << SkyObject::typeName(data.type())
239 << "; Coordinates: " << data.ra0().Degrees() << ";"
240 << data.dec().Degrees();
241 return { true, data };
242}
243
244// bool NameResolver::NameResolverInternals::getDataFromSimbad( class CatalogObject &data ) {
245// // TODO: Implement
246// // QUrl( QString( "http://simbad.u-strasbg.fr/simbad/sim-script?script=output%20console=off%20script=off%0Aformat%20object%20%22%25DIM%22%0A" ) + data.name );
247// }
248
249SkyObject::TYPE NameResolver::NameResolverInternals::interpretObjectType(const QString &typeString, bool caseSensitive)
250{
251 // FIXME: Due to the quirks of Sesame (SIMBAD vs NED etc), it
252 // might be very difficult to discern the type in all cases. The
253 // best way TODO things might be to first run the query with NED,
254 // and if it is extragalactic, then trust NED and
255 // accept. Otherwise, or if NED did not return a result, re-run
256 // the query on SIMBAD and VizieR and use that result, if any.
257
258 // See https://simbad.u-strasbg.fr/simbad/sim-display?data=otypes for Object Classification in SIMBAD
259
260 // Highest likelihood is a galaxy of some form, so we process that first
261 const QString &s = typeString; // To make the code compact
263
268 galaxyTypes << "G"
269 << "LIN"
270 << "AGN"
271 << "GiG"
272 << "GiC"
273 << "H2G"
274 << "BiC"
275 << "GiP"
276 << "HzG"
277 << "rG"
278 << "AG?"
279 << "EmG"
280 << "LSB"
281 << "SBG"
282 << "bCG"
283 << "SyG"
284 << "Sy1"
285 << "Sy2"
286 << "Gxy";
287
288 galaxyGroupTypes << "GClstr"
289 << "GGroup"
290 << "GPair"
291 << "ClG"
292 << "CGG"
293 << "PaG"
294 << "IG"
295 << "GrG"
296 << "SCG"; // NOTE (FIXME?): Compact groups and pairs of galaxies ar treated like galaxy clusters
297
298 openClusterTypes << "*Cl"
299 << "Cl*"
300 << "OpC"
301 << "As*"
302 << "St*"
303 << "OCl";
304
305 radioSourceTypes << "Rad"
306 << "mR"
307 << "cm"
308 << "mm"
309 << "smm"
310 << "HI"
311 << "rB"
312 << "Mas";
313
314 if (galaxyTypes.contains(s, cs))
315 return SkyObject::GALAXY;
316
317 if (galaxyGroupTypes.contains(s, cs))
318 return SkyObject::GALAXY_CLUSTER;
319
320 if (openClusterTypes.contains(s, cs))
321 return SkyObject::OPEN_CLUSTER; // FIXME: NED doesn't distinguish between globular clusters and open clusters!!
322
323 auto check = [typeString, cs](const QString &type) { return (!QString::compare(typeString, type, cs)); };
324
325 if (check("GlC") || check("GlCl"))
326 return SkyObject::GLOBULAR_CLUSTER;
327 if (check("Neb") || check("HII") || check("HH")) // FIXME: The last one is Herbig-Haro object
328 return SkyObject::GASEOUS_NEBULA;
329 if (check("SNR")) // FIXME: Simbad returns "ISM" for Veil Nebula (Interstellar Medium??)
330 return SkyObject::SUPERNOVA_REMNANT;
331 if (check("PN") || check("PNeb") || check("PNe") || check("pA*")) // FIXME: The latter is actually Proto PN
332 return SkyObject::PLANETARY_NEBULA;
333 if (typeString == "*")
334 return SkyObject::CATALOG_STAR;
335 if (check("QSO"))
336 return SkyObject::QUASAR;
337 if (check("DN") || check("DNe") || check("DNeb") || check("glb")) // The latter is Bok globule
338 return SkyObject::DARK_NEBULA;
339 if (radioSourceTypes.contains(s, cs))
340 return SkyObject::RADIO_SOURCE;
341 if (typeString == "**")
342 return SkyObject::MULT_STAR;
343 if (typeString.contains('*') && !check("C?*"))
344 return SkyObject::CATALOG_STAR;
345
346 return SkyObject::TYPE_UNKNOWN;
347 // FIXME: complete this method
348}
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
static KStarsLite * Instance()
Definition kstarslite.h:77
QString typeName() const
TYPE
The type classification of the SkyObject.
Definition skyobject.h:112
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
QString xi18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
QString name(StandardShortcut id)
std::pair< bool, CatalogObject > resolveName(const QString &name)
Resolve the name of the given DSO and extract data from various sources.
char toLatin1() const const
QNetworkReply * get(const QNetworkRequest &request)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QChar at(qsizetype n) const const
CaseSensitivity
bool hasAttribute(QAnyStringView namespaceUri, QAnyStringView name) const const
QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.