1 /*
2 * Stellarium
3 * Copyright (C) 2008 Fabien Chereau
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 "StelSkyLayerMgr.hpp"
21 #include "StelApp.hpp"
22 #include "StelCore.hpp"
23 #include "StelFileMgr.hpp"
24 #include "StelProjector.hpp"
25 #include "StelSkyImageTile.hpp"
26 #include "StelModuleMgr.hpp"
27 #include "StelPainter.hpp"
28 #include "MilkyWay.hpp"
29 #include "StelGuiBase.hpp"
30 #include "StelSkyDrawer.hpp"
31 #include "StelTranslator.hpp"
32 #include "StelProgressController.hpp"
33
34 #include <QNetworkAccessManager>
35 #include <stdexcept>
36 #include <QDebug>
37 #include <QString>
38 #include <QVariantMap>
39 #include <QVariantList>
40 #include <QDir>
41 #include <QSettings>
42
StelSkyLayerMgr(void)43 StelSkyLayerMgr::StelSkyLayerMgr(void) : flagShow(true)
44 {
45 setObjectName("StelSkyLayerMgr");
46 }
47
~StelSkyLayerMgr()48 StelSkyLayerMgr::~StelSkyLayerMgr()
49 {
50 for (auto* s : allSkyLayers)
51 delete s;
52 }
53
54 /*************************************************************************
55 Reimplementation of the getCallOrder method
56 *************************************************************************/
getCallOrder(StelModuleActionName actionName) const57 double StelSkyLayerMgr::getCallOrder(StelModuleActionName actionName) const
58 {
59 if (actionName==StelModule::ActionDraw)
60 return GETSTELMODULE(MilkyWay)->getCallOrder(actionName)+5;
61 return 0;
62 }
63
64 // read from stream
init()65 void StelSkyLayerMgr::init()
66 {
67 loadCollection();
68
69 QSettings* conf = StelApp::getInstance().getSettings();
70 conf->beginGroup("skylayers");
71 for (const auto& key : conf->childKeys())
72 {
73 QString uri = conf->value(key, "").toString();
74 if (!uri.isEmpty())
75 {
76 if (key=="clilayer")
77 insertSkyImage(uri, "Command-line layer");
78 else
79 insertSkyImage(uri);
80 }
81 }
82 conf->endGroup();
83
84 setFlagShow(!conf->value("astro/flag_nebula_display_no_texture", false).toBool());
85 addAction("actionShow_DSO_Textures", N_("Display Options"), N_("Deep-sky objects background images"), "flagShow", "I");
86 addAction("actionShow_DSO_Textures_Reload", N_("Display Options"), N_("Reload the deep-sky objects background images"), "loadCollection()", "Ctrl+I");
87 }
88
loadCollection()89 void StelSkyLayerMgr::loadCollection()
90 {
91 if (!allSkyLayers.isEmpty())
92 allSkyLayers.clear();
93
94 QString path = StelFileMgr::findFile("nebulae/default/textures.json");
95 if (path.isEmpty())
96 qWarning() << "ERROR while loading nebula texture set default";
97 else
98 insertSkyImage(path);
99 }
100
insertSkyLayer(StelSkyLayerP tile,const QString & keyHint,bool ashow)101 QString StelSkyLayerMgr::insertSkyLayer(StelSkyLayerP tile, const QString& keyHint, bool ashow)
102 {
103 SkyLayerElem* bEl = new SkyLayerElem(tile, ashow);
104 QString key = tile->getKeyHint();
105 if (key.isEmpty() || key=="no name")
106 {
107 if (!keyHint.isEmpty())
108 {
109 key = keyHint;
110 }
111 else
112 {
113 key = "no name";
114 }
115 }
116 if (allSkyLayers.contains(key))
117 {
118 QString suffix = "_01";
119 int i=1;
120 while (allSkyLayers.contains(key+suffix))
121 {
122 suffix=QString("_%1").arg(i);
123 ++i;
124 }
125 key+=suffix;
126 }
127 allSkyLayers.insert(key,bEl);
128 connect(bEl->layer.data(), SIGNAL(loadingStateChanged(bool)), this, SLOT(loadingStateChanged(bool)));
129 connect(bEl->layer.data(), SIGNAL(percentLoadedChanged(int)), this, SLOT(percentLoadedChanged(int)));
130 return key;
131 }
132
133 // Add a new image from its URI (URL or local file name)
insertSkyImage(const QString & uri,const QString & keyHint,bool ashow)134 QString StelSkyLayerMgr::insertSkyImage(const QString& uri, const QString& keyHint, bool ashow)
135 {
136 return insertSkyLayer(StelSkyLayerP(new StelSkyImageTile(uri)), keyHint, ashow);
137 }
138
139 // Remove a sky image tile from the list of background images
removeSkyLayer(const QString & key)140 void StelSkyLayerMgr::removeSkyLayer(const QString& key)
141 {
142 //qDebug() << "StelSkyLayerMgr::removeSkyImage removing image:" << key;
143 if (allSkyLayers.contains(key))
144 {
145 SkyLayerElem* bEl = allSkyLayers[key];
146 disconnect(bEl->layer.data(), SIGNAL(loadingStateChanged(bool)), this, SLOT(loadingStateChanged(bool)));
147 disconnect(bEl->layer.data(), SIGNAL(percentLoadedChanged(int)), this, SLOT(percentLoadedChanged(int)));
148 delete bEl;
149 allSkyLayers.remove(key);
150 }
151 else
152 {
153 qDebug() << "StelSkyLayerMgr::removeSkyLayer there is no such key" << key << "nothing is removed";
154 }
155 }
156
157 // Remove a sky image tile from the list of background images
removeSkyLayer(StelSkyLayerP l)158 void StelSkyLayerMgr::removeSkyLayer(StelSkyLayerP l)
159 {
160 const QString k = keyForLayer(l.data());
161 if (!k.isEmpty())
162 removeSkyLayer(k);
163 }
164
165 // Draw all the multi-res images collection
draw(StelCore * core)166 void StelSkyLayerMgr::draw(StelCore* core)
167 {
168 if (!flagShow)
169 return;
170
171 StelPainter sPainter(core->getProjection(StelCore::FrameJ2000));
172 sPainter.setBlending(true, GL_ONE, GL_ONE); //additive blending
173 for (auto* s : allSkyLayers)
174 {
175 if (s->show)
176 {
177 sPainter.setProjector(core->getProjection(s->layer->getFrameType()));
178 s->layer->draw(core, sPainter, 1.);
179 }
180 }
181 }
182
noDelete(StelSkyLayer *)183 void noDelete(StelSkyLayer*) {;}
184
185 // Called when loading of data started or stopped for one collection
loadingStateChanged(bool b)186 void StelSkyLayerMgr::loadingStateChanged(bool b)
187 {
188 StelSkyLayer* tile = qobject_cast<StelSkyLayer*>(QObject::sender());
189 Q_ASSERT(tile!=Q_NULLPTR);
190 SkyLayerElem* elem = skyLayerElemForLayer(tile);
191 Q_ASSERT(elem!=Q_NULLPTR);
192 if (b)
193 {
194 Q_ASSERT(elem->progressBar==Q_NULLPTR);
195 elem->progressBar = StelApp::getInstance().addProgressBar();
196 QString serverStr = elem->layer->getShortServerCredits();
197 if (!serverStr.isEmpty())
198 serverStr = " from "+serverStr;
199 elem->progressBar->setFormat("Loading "+elem->layer->getShortName()+serverStr);
200 elem->progressBar->setRange(0,100);
201 }
202 else
203 {
204 Q_ASSERT(elem->progressBar!=Q_NULLPTR);
205 StelApp::getInstance().removeProgressBar(elem->progressBar);
206 elem->progressBar = Q_NULLPTR;
207 }
208 }
209
210 // Called when the percentage of loading tiles/tiles to be displayed changed for one collection
percentLoadedChanged(int percentage)211 void StelSkyLayerMgr::percentLoadedChanged(int percentage)
212 {
213 StelSkyLayer* tile = qobject_cast<StelSkyLayer*>(QObject::sender());
214 Q_ASSERT(tile!=Q_NULLPTR);
215 SkyLayerElem* elem = skyLayerElemForLayer(tile);
216 Q_ASSERT(elem!=Q_NULLPTR);
217 Q_ASSERT(elem->progressBar!=Q_NULLPTR);
218 elem->progressBar->setValue(percentage);
219 }
220
skyLayerElemForLayer(const StelSkyLayer * t)221 StelSkyLayerMgr::SkyLayerElem* StelSkyLayerMgr::skyLayerElemForLayer(const StelSkyLayer* t)
222 {
223 for (auto* e : allSkyLayers)
224 {
225 if (e->layer==t)
226 {
227 return e;
228 }
229 }
230 return Q_NULLPTR;
231 }
232
keyForLayer(const StelSkyLayer * t)233 QString StelSkyLayerMgr::keyForLayer(const StelSkyLayer* t)
234 {
235 return allSkyLayers.key(skyLayerElemForLayer(t));
236 }
237
SkyLayerElem(StelSkyLayerP t,bool ashow)238 StelSkyLayerMgr::SkyLayerElem::SkyLayerElem(StelSkyLayerP t, bool ashow) : layer(t), progressBar(Q_NULLPTR), show(ashow)
239 {;}
240
~SkyLayerElem()241 StelSkyLayerMgr::SkyLayerElem::~SkyLayerElem()
242 {
243 if (progressBar)
244 StelApp::getInstance().removeProgressBar(progressBar);
245 progressBar = Q_NULLPTR;
246 }
247
loadSkyImage(const QString & id,const QString & filename,double long0,double lat0,double long1,double lat1,double long2,double lat2,double long3,double lat3,double minRes,double maxBright,bool visible,StelCore::FrameType frameType,bool withAberration)248 bool StelSkyLayerMgr::loadSkyImage(const QString& id, const QString& filename,
249 double long0, double lat0,
250 double long1, double lat1,
251 double long2, double lat2,
252 double long3, double lat3,
253 double minRes, double maxBright, bool visible, StelCore::FrameType frameType, bool withAberration)
254 {
255 if (allSkyLayers.contains(id))
256 {
257 qWarning() << "Image ID" << id << "already exists, removing old image before loading";
258 removeSkyLayer(id);
259 }
260
261 QString path = StelFileMgr::findFile(filename);
262 if (path.isEmpty())
263 {
264 qWarning() << "Could not find image" << QDir::toNativeSeparators(filename);
265 return false;
266 }
267 QVariantMap vm;
268 QVariantList cl; // coordinates list for adding worldCoords and textureCoords
269 QVariantList c; // a list for a pair of coordinates
270 QVariantList ol; // outer list - we want a structure 3 levels deep...
271 vm["imageUrl"] = QVariant(path);
272 vm["maxBrightness"] = QVariant(maxBright);
273 vm["minResolution"] = QVariant(minRes);
274 vm["shortName"] = QVariant(id);
275 vm["withAberration"] = QVariant(withAberration);
276
277 // textureCoords (define the ordering of worldCoords)
278 cl.clear();
279 ol.clear();
280 c.clear(); c.append(0); c.append(0); cl.append(QVariant(c));
281 c.clear(); c.append(1); c.append(0); cl.append(QVariant(c));
282 c.clear(); c.append(1); c.append(1); cl.append(QVariant(c));
283 c.clear(); c.append(0); c.append(1); cl.append(QVariant(c));
284 ol.append(QVariant(cl));
285 vm["textureCoords"] = ol;
286
287 // world coordinates
288 cl.clear();
289 ol.clear();
290 c.clear(); c.append(long0); c.append(lat0); cl.append(QVariant(c));
291 c.clear(); c.append(long1); c.append(lat1); cl.append(QVariant(c));
292 c.clear(); c.append(long2); c.append(lat2); cl.append(QVariant(c));
293 c.clear(); c.append(long3); c.append(lat3); cl.append(QVariant(c));
294 ol.append(QVariant(cl));
295 vm["worldCoords"] = ol;
296
297 vm["alphaBlend"] = true; // new 2017-3: Make black correctly see-through.
298
299 StelSkyLayerP tile = StelSkyLayerP(new StelSkyImageTile(vm, Q_NULLPTR));
300 tile->setFrameType(frameType);
301
302 try
303 {
304 QString key = insertSkyLayer(tile, filename, visible);
305 if (key == id)
306 return true;
307 else
308 return false;
309 }
310 catch (std::runtime_error& e)
311 {
312 qWarning() << e.what();
313 return false;
314 }
315 }
316
showLayer(const QString & id,bool b)317 void StelSkyLayerMgr::showLayer(const QString& id, bool b)
318 {
319 if (allSkyLayers.contains(id))
320 {
321 if (allSkyLayers[id]!=Q_NULLPTR)
322 allSkyLayers[id]->show = b;
323 }
324 }
325
getShowLayer(const QString & id) const326 bool StelSkyLayerMgr::getShowLayer(const QString& id) const
327 {
328 if (allSkyLayers.contains(id))
329 {
330 if (allSkyLayers[id]!=Q_NULLPTR)
331 return allSkyLayers[id]->show;
332 }
333 return false;
334 }
335
336
337 //! Get the list of all the currently loaded layers.
getAllSkyLayers() const338 QMap<QString, StelSkyLayerP> StelSkyLayerMgr::getAllSkyLayers() const
339 {
340 QMap<QString, StelSkyLayerP> res;
341 for (QMap<QString, StelSkyLayerMgr::SkyLayerElem*>::ConstIterator iter=allSkyLayers.constBegin();iter!=allSkyLayers.constEnd();++iter)
342 {
343 //qDebug() << iter.key() << iter.value()->layer->getShortName();
344 res.insert(iter.key(), iter.value()->layer);
345 }
346 return res;
347 }
348
getSkyLayer(const QString & key) const349 StelSkyLayerP StelSkyLayerMgr::getSkyLayer(const QString& key) const
350 {
351 if (allSkyLayers.contains(key))
352 return allSkyLayers[key]->layer;
353 return StelSkyLayerP();
354 }
355