1 /*
2     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3     SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #pragma once
9 
10 #include "skycomponent.h"
11 #include "catalogsdb.h"
12 #include "catalogobject.h"
13 #include "skymesh.h"
14 #include "trixelcache.h"
15 #include "Options.h"
16 
17 #include "polyfills/qstring_hash.h"
18 #include <unordered_map>
19 
20 class SkyMesh;
21 class SkyMap;
22 
23 /**
24  * \brief Represents objects loaded from an sqlite backed, trixel
25  * indexed catalog.
26  *
27  * The component doesn't follow the traditional list approach and
28  * loads it's skyobjects into an LRU cache (`TrixelCache`). For
29  * puproses of compatiblility with object search etc. some of the
30  * brightest objects are loaded into `m_static_objects` and registered
31  * within the component system. Furthermore, if some part of the code
32  * demands a pointer to a CatalogObject, it will be allocated into
33  * `m_static_objects` on demand.
34  *
35  * If you want to access DSOs in _new_ code you should use a local
36  * instance of `CatalogsDB::DBManager` instead and call `dropCache` if
37  * necessary.
38  *
39  * \sa CatalogsDB::DBManager
40  */
41 class CatalogsComponent : public SkyComponent
42 {
43     public:
44         using ObjectList = std::vector<CatalogObject>;
45 
46         /**
47          * Constructs the Catalogscomponent with a \p parent and a
48          * database file under the path \p db_filename. If \p load_ngc is
49          * specified, an attempt is made to load the default catalog from
50          * the default location into the db.
51          *
52          * The lru cache for the objects will be initialized to a capacity
53          * configurable by Options::dSOCachePercentage.
54          */
55         explicit CatalogsComponent(SkyComposite *parent, const QString &db_filename,
56                                    bool load_default = false);
57 
58         ~CatalogsComponent() override = default;
59 
60         /**
61          * Draws the objects in the currently visible trixels by
62          * dynamically loading them from the database.
63          */
64         void draw(SkyPainter *skyp) override;
65 
66         /**
67          * Set the cache size to the new \p percentage.
68          *
69          * The cache stores the objects of a certain \p percentage of all
70          * trixels. Setting `percentage = 100` short circuits the cache and loads
71          * all the objects into memory. This is reasonable for catalog sizes up
72          * to `10_000` objects.
73          */
resizeCache(const int percentage)74         void resizeCache(const int percentage)
75         {
76             m_cache.set_size(calculateCacheSize(percentage));
77         };
78 
79         /**
80          * \short Search the underlying database for an object with the \p
81          * name. \sa `CatalogsDB::DBManager::find_object_by_name` for
82          * details.
83          *
84          * If multiple objects match, the one with the hightest magnitude is
85          * returned.
86          *
87          * \return a pointer to the SkyObject whose name matches the argument, or
88          * a nullptr pointer if no match was found. (Due to way KStars works)
89          */
90         SkyObject *findByName(const QString &name) override;
91 
92         void objectsInArea(QList<SkyObject *> &list, const SkyRegion &region) override;
93 
94         SkyObject *objectNearest(SkyPoint *p, double &maxrad) override;
95 
96         /**
97          * Insert an object \p obj into `m_static_objects` and return a
98          * reference to the newly inserted object. If the object is
99          * already present in the list, return a reference to
100          * that. Furthermore the object will be updated
101          * (`CatalogObject::JITupdate`) and inserted into the parent's
102          * `objectLists`.
103          */
104         CatalogObject &insertStaticObject(const CatalogObject &obj);
105 
106         /**
107          * Clear the internal cache and effectively reload all objects
108          * from the database.
109          */
dropCache()110         void dropCache()
111         {
112             m_cache.clear();
113             m_catalog_colors = m_db_manager.get_catalog_colors();
114         };
115 
116         /**
117          * Wether to show the DSOs.
118          */
selected()119         bool selected() override
120         {
121             return Options::showDeepSky();
122         };
123 
124     private:
125         /**
126          * The database interface for the catalog.
127          */
128         CatalogsDB::DBManager m_db_manager;
129 
130         /**
131          * A pointer to a SkyMesh of the appropriate level.
132          *
133          * @note The use of a pointer here is a legacy from the
134          * SkyComponent implementation.
135          */
136         SkyMesh *m_skyMesh;
137 
138         /**
139          * The main container for the currently loaded objects.
140          */
141         ObjectList m_objects;
142 
143         /**
144          * The cache holding the DSOs
145          */
146         TrixelCache<ObjectList> m_cache;
147 
148         /**
149          * A trixel indexed map of lists containing manually loaded
150          * `CatalogObject`s.
151          *
152          * Because some `KStars` internal code requires pointers to SkyObjects
153          * and this component doesn't hold its objects, we have create a space to
154          * to own the objets that we create in methods like `findByName`. Thus it
155          * is expected that this list won't hold many objects and doesn't have to
156          * be emptied at runtime. The objects in this map are not drawn and have
157          * to be updated manually.
158          *
159          * __No objects should ever be removed from this list, as references and
160          * pointers to list members are required to remain valid.__
161          *
162          * __In new code, a local instance of `CatalogsDB::DBManager` should be
163          * used when access to CatalogObjects is required. Call `dropCache` if
164          * required.__
165          */
166         std::unordered_map<Trixel, CatalogsDB::CatalogObjectList> m_static_objects;
167 
168         /**
169          * A cache for catalog colors.
170          */
171         CatalogsDB::ColorMap m_catalog_colors;
172 
173         //@{
174         /** Helpers */
175 
176         void updateSkyMesh(SkyMap &map, MeshBufNum_t buf = DRAW_BUF);
calculateCacheSize(const unsigned int percentage)177         size_t calculateCacheSize(const unsigned int percentage)
178         {
179             return m_skyMesh->size() * percentage / 100;
180         }
181 
182         /**
183          * Try importing the old skycomponents database.
184          */
185         void tryImportSkyComponents();
186 
187         //@}
188 };
189