1 /*
2     SPDX-FileCopyrightText: 2001 Heiko Evermann <heiko@evermann.de>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "colorscheme.h"
10 #include "geolocation.h"
11 #include "ksnumbers.h"
12 #include "kstarsdatetime.h"
13 #include "ksuserdb.h"
14 #include "simclock.h"
15 #include "skyobjectuserdata.h"
16 #include <qobject.h>
17 #ifndef KSTARS_LITE
18 #include "oal/oal.h"
19 #include "oal/log.h"
20 #include "polyfills/qstring_hash.h"
21 #endif
22 
23 #include <QList>
24 #include <QMap>
25 #include <QKeySequence>
26 
27 #include <iostream>
28 #include <memory>
29 #include <unordered_map>
30 
31 #define MINZOOM     250.
32 #define MAXZOOM     5000000.
33 #define DEFAULTZOOM 2000.
34 #define DZOOM       1.189207115 // 2^(1/4)
35 #define AU_KM       1.49605e8   //km in one AU
36 
37 class QFile;
38 
39 class Execute;
40 class FOV;
41 class ImageExporter;
42 class SkyMap;
43 class SkyMapComposite;
44 class SkyObject;
45 class ObservingList;
46 class TimeZoneRule;
47 
48 #ifdef KSTARS_LITE
49 //Will go away when details window will be implemented in KStars Lite
50 struct ADVTreeData
51 {
52     QString Name;
53     QString Link;
54     int Type;
55 };
56 #else
57 struct ADVTreeData;
58 #endif
59 
60 /**
61  * @class KStarsData
62  * KStarsData is the backbone of KStars.  It contains all the data used by KStars,
63  * including the SkyMapComposite that contains all items in the skymap
64  * (stars, deep-sky objects, planets, constellations, etc).  Other kinds of data
65  * are stored here as well: the geographic locations, the timezone rules, etc.
66  *
67  * @author Heiko Evermann
68  * @version 1.0
69  */
70 class KStarsData : public QObject
71 {
72         Q_OBJECT
73 
74     protected:
75         /** Constructor. */
76         KStarsData();
77 
78     public:
79         // FIXME: It uses temporary trail. There must be way to
80         //        this better. And resumeKey in DBUS code
81         friend class KStars;
82         // FIXME: it uses temporary trail and resumeKey
83         friend class SkyMap;
84         // FIXME: uses geoList and changes it.
85         friend class LocationDialog;
86         friend class LocationDialogLite;
87 
88         static KStarsData *Create();
89 
Instance()90         static inline KStarsData *Instance()
91         {
92             return pinstance;
93         }
94 
95         /**
96          * Initialize KStarsData while running splash screen.
97          * @return true on success.
98          */
99         bool initialize();
100 
101         /** Destructor.  Delete data objects. */
102         ~KStarsData() override;
103 
104         /**
105          * Set the NextDSTChange member.
106          *  Need this accessor because I could not make KStars::privatedata a friend
107          *  class for some reason...:/
108          */
setNextDSTChange(const KStarsDateTime & dt)109         void setNextDSTChange(const KStarsDateTime &dt)
110         {
111             NextDSTChange = dt;
112         }
113 
114         /**
115          * Returns true if time is running forward else false. Used by KStars to prevent
116          *  double calculations of daylight saving change time.
117          */
isTimeRunningForward()118         bool isTimeRunningForward() const
119         {
120             return TimeRunsForward;
121         }
122 
123         /** @return pointer to the localization (KLocale) object */
124         //KLocale *getLocale() { return locale; }
125 
126         /**
127          * @short Find object by name.
128          * @param name Object name to find
129          * @return pointer to SkyObject matching this name
130          */
131         SkyObject *objectNamed(const QString &name);
132 
133         /**
134          * The Sky is updated more frequently than the moon, which is updated more frequently
135          * than the planets.  The date of the last update for each category is recorded so we
136          * know when we need to do it again (see KStars::updateTime()).
137          * Initializing these to -1000000.0 ensures they will be updated immediately
138          * on the first call to KStars::updateTime().
139          */
140         void setFullTimeUpdate();
141 
142         /**
143          * Change the current simulation date/time to the KStarsDateTime argument.
144          * Specified DateTime is always universal time.
145          * @param newDate the DateTime to set.
146          */
147         void changeDateTime(const KStarsDateTime &newDate);
148 
149         /** @return pointer to the current simulation local time */
lt()150         const KStarsDateTime &lt() const
151         {
152             return LTime;
153         }
154 
155         /** @return reference to the current simulation universal time */
ut()156         const KStarsDateTime &ut() const
157         {
158             return Clock.utc();
159         }
160 
161         /** Sync the LST with the simulation clock. */
162         void syncLST();
163 
164         /** @return pointer to SkyComposite */
skyComposite()165         SkyMapComposite *skyComposite()
166         {
167             return m_SkyComposite.get();
168         }
169 
170         /** @return pointer to the ColorScheme object */
colorScheme()171         ColorScheme *colorScheme()
172         {
173             return &CScheme;
174         }
175 
176         /** @return file name of current color scheme **/
colorSchemeFileName()177         Q_INVOKABLE QString colorSchemeFileName() { return CScheme.fileName(); }
178 
179         /** @return file name of the color scheme with the name \p name **/
colorSchemeFileName(const QString & name)180         QString colorSchemeFileName(const QString &name)
181         {
182             return m_color_schemes.count(name) > 0 ? m_color_schemes.at(name) : "";
183         }
184 
185         /** @return file name of the current color scheme **/
colorSchemeName()186         Q_INVOKABLE QString colorSchemeName()
187         {
188             return colorSchemeName(CScheme.fileName());
189         }
190 
191         /** @return the name of the color scheme with the name \p name **/
colorSchemeName(const QString & fileName)192         QString colorSchemeName(const QString &fileName)
193         {
194             return m_color_scheme_names.count(fileName) > 0 ? m_color_scheme_names.at(fileName) : "";
195         }
196 
197         /** @return if the color scheme with the name or filename \p scheme is loaded **/
hasColorScheme(const QString & scheme)198         bool hasColorScheme(const QString &scheme)
199         {
200             return m_color_scheme_names.count(scheme) || m_color_schemes.count(scheme);
201         }
202 
203         /** Register a color scheme with \p filename and \p name. */
add_color_scheme(const QString & filename,const QString & name)204         void add_color_scheme(const QString &filename, const QString &name)
205         {
206             m_color_schemes[name] = filename;
207             m_color_scheme_names[filename] = name;
208         };
209 
210         /** \return a map of color scheme names and filenames */
color_schemes()211         const std::map<QString, QString> color_schemes() { return m_color_schemes; };
212 
213         /** @return pointer to the KSUserDB object */
userdb()214         KSUserDB *userdb() { return &m_ksuserdb; }
215 
216         /** @return pointer to the simulation Clock object */
clock()217         Q_INVOKABLE SimClock *clock()
218         {
219             return &Clock;
220         }
221 
222         /** @return pointer to the local sidereal time: a dms object */
lst()223         CachingDms *lst()
224         {
225             return &LST;
226         }
227 
228         /** @return pointer to the GeoLocation object*/
geo()229         GeoLocation *geo()
230         {
231             return &m_Geo;
232         }
233 
234         /** @return list of all geographic locations */
getGeoList()235         QList<GeoLocation *> &getGeoList()
236         {
237             return geoList;
238         }
239 
240         GeoLocation *locationNamed(const QString &city, const QString &province = QString(),
241                                    const QString &country = QString());
242 
243         /**
244          * @brief nearestLocation Return nearest location to the given longitude and latitude coordinates
245          * @param longitude Longitude (-180 to +180)
246          * @param latitude Latitude (-90 to +90)
247          * @return nearest geographical location to the parameters above.
248          */
249         GeoLocation *nearestLocation(double longitude, double latitude);
250 
251         /**
252          * Set the GeoLocation according to the argument.
253          * @param l reference to the new GeoLocation
254          */
255         void setLocation(const GeoLocation &l);
256 
257         /** Set the GeoLocation according to the values stored in the configuration file. */
258         void setLocationFromOptions();
259 
260         /** Return map for daylight saving rules. */
getRulebook()261         const QMap<QString, TimeZoneRule> &getRulebook() const
262         {
263             return Rulebook;
264         }
265 
266         /** @return whether the next Focus change will omit the slewing animation. */
snapNextFocus()267         bool snapNextFocus() const
268         {
269             return snapToFocus;
270         }
271 
272         /**
273          * Disable or re-enable the slewing animation for the next Focus change.
274          * @note If the user has turned off all animated slewing, setSnapNextFocus(false)
275          * will *NOT* enable animation on the next slew.  A false argument would only
276          * be used if you have previously called setSnapNextFocus(true), but then decided
277          * you didn't want that after all.  In other words, it's extremely unlikely you'd
278          * ever want to use setSnapNextFocus(false).
279          * @param b when true (the default), the next Focus change will omit the slewing
280          * animation.
281          */
282         void setSnapNextFocus(bool b = true)
283         {
284             snapToFocus = b;
285         }
286 
287         /**
288          * Execute a script.  This function actually duplicates the DCOP functionality
289          * for those cases when invoking DCOP is not practical (i.e., when preparing
290          * a sky image in command-line dump mode).
291          * @param name the filename of the script to "execute".
292          * @param map pointer to the SkyMap object.
293          * @return true if the script was successfully parsed.
294          */
295         bool executeScript(const QString &name, SkyMap *map);
296 
297         /** Synchronize list of visible FOVs and list of selected FOVs in Options */
298 #ifndef KSTARS_LITE
299         void syncFOV();
300 #endif
301 
302         /**
303          * @return the list of visible FOVs
304          */
getVisibleFOVs()305         inline const QList<FOV *> getVisibleFOVs() const
306         {
307             return visibleFOVs;
308         }
309 
310         /**
311          * @return the list of available FOVs
312          */
getAvailableFOVs()313         inline const QList<FOV *> getAvailableFOVs() const
314         {
315             return availFOVs;
316         }
317 
318         /**
319          * @brief addTransientFOV Adds a new FOV to the list.
320          * @param newFOV pointer to FOV object.
321          */
addTransientFOV(std::shared_ptr<FOV> newFOV)322         inline void addTransientFOV(std::shared_ptr<FOV> newFOV)
323         {
324             transientFOVs.append(newFOV);
325         }
clearTransientFOVs()326         inline void clearTransientFOVs()
327         {
328             transientFOVs.clear();
329         }
330 
331         /**
332          * @return the list of transient FOVs
333          */
getTransientFOVs()334         inline const QList<std::shared_ptr<FOV>> getTransientFOVs() const
335         {
336             return transientFOVs;
337         }
338 #ifndef KSTARS_LITE
339         /** Return log object */
logObject()340         OAL::Log *logObject()
341         {
342             return m_LogObject.get();
343         }
344 
345         /** Return ADV Tree */
avdTree()346         QList<ADVTreeData *> avdTree()
347         {
348             return ADVtreeList;
349         }
350 
observingList()351         inline ObservingList *observingList() const
352         {
353             return m_ObservingList;
354         }
355 
356         ImageExporter *imageExporter();
357 
358         Execute *executeSession();
359 #endif
360         /*@short Increments the updateID, forcing a recomputation of star positions as well */
361         unsigned int incUpdateID();
362 
updateID()363         unsigned int updateID() const
364         {
365             return m_updateID;
366         }
updateNumID()367         unsigned int updateNumID() const
368         {
369             return m_updateNumID;
370         }
updateNum()371         KSNumbers *updateNum()
372         {
373             return &m_updateNum;
374         }
375         void syncUpdateIDs();
376 
377     signals:
378         /** Signal that specifies the text that should be drawn in the KStarsSplash window. */
379         void progressText(const QString &text);
380 
381         /** Should be used to refresh skymap. */
382         void skyUpdate(bool);
383 
384         /** If data changed, emit clearCache signal. */
385         void clearCache();
386 
387         /** Emitted when geo location changed */
388         void geoChanged();
389 
390     public slots:
391         /** @short send a message to the console*/
slotConsoleMessage(QString s)392         void slotConsoleMessage(QString s)
393         {
394             std::cout << (const char *)(s.toLocal8Bit()) << std::endl;
395         }
396 
397         /**
398          * Update the Simulation Clock.  Update positions of Planets.  Update
399          * Alt/Az coordinates of objects.  Update precession.
400          * emit the skyUpdate() signal so that SkyMap / whatever draws the sky can update itself
401          *
402          * This is ugly.
403          * It _will_ change!
404          * (JH:)hey, it's much less ugly now...can we lose the comment yet? :p
405          */
406         void updateTime(GeoLocation *geo, const bool automaticDSTchange = true);
407 
408         /**
409          * Sets the direction of time and stores it in bool TimeRunForwards. If scale >= 0
410          * time is running forward else time runs backward. We need this to calculate just
411          * one daylight saving change time (previous or next DST change).
412          */
413         void setTimeDirection(float scale);
414 
415         // What follows is mostly a port of Arkashs auxdata stuff to a
416         // more centralized approach that does not store the data in
417         // the skyobjects as they are ephemeral in the new DSO implementation
418         //
419         // I've tried to reuse as much code as possible and maintain
420         // compatibility with peoples data.
421         //
422         // -- Valentin Boettcher
423 
424         /**
425          * Get a reference to the user data of an object with the name \p name.
426          */
427         const SkyObjectUserdata::Data &getUserData(const QString &name);
428 
429         /**
430          * Adds a link \p data to the user data for the object with \p
431          * name, both in memory and on disk.
432          *
433          * @returns {success, error_message}
434          */
435         std::pair<bool, QString> addToUserData(const QString &name,
436                                                const SkyObjectUserdata::LinkData &data);
437 
438         /**
439          * Replace \p data in the user data at \p index for the object with \p
440          * name, both in memory and on disk.
441          *
442          * @returns {success, error_message}
443          */
444         std::pair<bool, QString> editUserData(const QString &name,
445                                               const unsigned int index,
446                                               const SkyObjectUserdata::LinkData &data);
447 
448         /**
449          * Remove data of \p type from the user data at \p index for
450          * the object with \p name, both in memory and on disk.
451          *
452          * @returns {success, error_message}
453          */
454         std::pair<bool, QString> deleteUserData(const QString &name,
455                                                 const unsigned int index,
456                                                 SkyObjectUserdata::Type type);
457         /**
458          * Update the user log of the object with the \p name to
459          * contain \p newLog (find and replace).
460          *
461          * @returns {success, error_message}
462          */
463         std::pair<bool, QString> updateUserLog(const QString &name,
464                                                const QString &newLog);
465 
466       private:
467         /**
468          * Populate list of geographic locations from "citydb.sqlite" database. Also check for custom
469          * locations file "mycitydb.sqlite" database, but don't require it.  Each line in the file
470          * provides the information required to create one GeoLocation object.
471          * @short Fill list of geographic locations from file(s)
472          * @return true if at least one city read successfully.
473          * @see KStarsData::processCity()
474          */
475         bool readCityData();
476 
477         /** Read the data file that contains daylight savings time rules. */
478         bool readTimeZoneRulebook();
479 
480         //TODO JM: ADV tree should use XML instead
481         /**
482          * Read Advanced interface structure to be used later to construct the list view in
483          * the advanced tab in the Detail Dialog.
484          * @li KSLABEL designates a top-level parent label
485          * @li KSINTERFACE designates a common URL interface for several objects
486          * @li END designates the end of a sub tree structure
487          * @short read online database lookup structure.
488          * @return true if data is successfully read.
489          */
490         bool readADVTreeData();
491 
492         /** Read INDI hosts from an XML file */
493         bool readINDIHosts();
494 
495         //TODO JM: Use XML instead; The logger should have more features
496         // that allow users to enter details about their observation logs
497         // objects observed, eye pieces, telescope, conditions, mag..etc
498         /**
499          * @short read user logs.
500          *
501          * Read user logs. The log file is formatted as following:
502          * @li KSLABEL designates the beginning of a log
503          * @li KSLogEnd designates the end of a log.
504          *
505          * @return true if data is successfully read.
506          */
507         bool readUserLog();
508 
509         /**
510          * Read in URLs to be attached to a named object's right-click popup menu.  At this
511          * point, there is no way to attach URLs to unnamed objects.  There are two
512          * kinds of URLs, each with its own data file: image links and webpage links.  In addition,
513          * there may be user-specific versions with custom URLs.  Each line contains 3 fields
514          * separated by colons (":").  Note that the last field is the URL, and as such it will
515          * generally contain a colon itself.  Only the first two colons encountered are treated
516          * as field separators.  The fields are:
517          *
518          * @li Object name.  This must be the "primary" name of the object (the name at the top of the popup menu).
519          * @li Menu text.  The string that should appear in the popup menu to activate the link.
520          * @li URL.
521          * @short Read in image and information URLs.
522          * @return true if data files were successfully read.
523          */
524         bool readURLData(const QString &url,
525                          SkyObjectUserdata::Type type = SkyObjectUserdata::Type::website);
526 
527         /**
528          * @short open a file containing URL links.
529          * @param urlfile string representation of the filename to open
530          * @param file reference to the QFile object which will be opened to this file.
531          * @return true if file successfully opened.
532          */
533         bool openUrlFile(const QString &urlfile, QFile &file);
534 
535         /**
536          * Reset local time to new daylight saving time. Use this function if DST has changed.
537          * Used by updateTime().
538          */
539         void resetToNewDST(GeoLocation *geo, const bool automaticDSTchange);
540 
541         /**
542          * As KStarsData::getUserData just non-const.
543          * @warning This method is not thread safe :) so take care of that when you use it.
544          */
545         SkyObjectUserdata::Data &findUserData(const QString &name);
546 
547         QList<ADVTreeData *> ADVtreeList;
548         std::unique_ptr<SkyMapComposite> m_SkyComposite;
549 
550         GeoLocation m_Geo;
551         SimClock Clock;
552         KStarsDateTime LTime;
553         KSUserDB m_ksuserdb;
554         ColorScheme CScheme;
555         std::map<QString, QString> m_color_schemes; // name: filename
556         std::map<QString, QString> m_color_scheme_names; // filename: name
557 
558 #ifndef KSTARS_LITE
559         ObservingList* m_ObservingList { nullptr };
560         std::unique_ptr<OAL::Log> m_LogObject;
561         std::unique_ptr<Execute> m_Execute;
562         std::unique_ptr<ImageExporter> m_ImageExporter;
563 #endif
564 
565         //EquipmentWriter *m_equipmentWriter;
566 
567         bool TimeRunsForward { false };
568         bool temporaryTrail { false };
569         // FIXME: Used in SkyMap only. Check!
570         bool snapToFocus { false };
571 
572         //KLocale *locale;
573 
574         CachingDms LST;
575 
576         QKeySequence resumeKey;
577 
578         QList<FOV *> availFOVs;         // List of all available FOVs
579         QList<FOV *> visibleFOVs;       // List of visible FOVs. Cached from Options::FOVNames
580         QList<std::shared_ptr<FOV>> transientFOVs;     // List of non-permenant transient FOVs.
581 
582         KStarsDateTime LastNumUpdate, LastSkyUpdate, LastPlanetUpdate, LastMoonUpdate;
583         KStarsDateTime NextDSTChange;
584         // FIXME: Used in kstarsdcop.cpp only
585         KStarsDateTime StoredDate;
586 
587         QList<GeoLocation *> geoList;
588         QMap<QString, TimeZoneRule> Rulebook;
589 
590         quint32 m_preUpdateID, m_updateID;
591         quint32 m_preUpdateNumID, m_updateNumID;
592         KSNumbers m_preUpdateNum, m_updateNum;
593 
594         static KStarsData *pinstance;
595 
596         std::unordered_map<QString, SkyObjectUserdata::Data> m_user_data;
597         QMutex m_user_data_mutex; // for m_user_data
598 };
599