Marble

FileLoader.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2008 Patrick Spendrin <ps_ml@gmx.de>
4//
5
6#include "FileLoader.h"
7
8#include <QBuffer>
9#include <QDataStream>
10#include <QFile>
11
12#include "GeoDataData.h"
13#include "GeoDataExtendedData.h"
14#include "GeoDataFolder.h"
15#include "GeoDataGroundOverlay.h"
16#include "GeoDataLineStyle.h"
17#include "GeoDataParser.h"
18#include "GeoDataPhotoOverlay.h"
19#include "GeoDataPlacemark.h"
20#include "GeoDataPoint.h"
21#include "GeoDataPolyStyle.h"
22#include "GeoDataPolygon.h"
23#include "GeoDataRelation.h"
24#include "GeoDataScreenOverlay.h"
25#include "GeoDataStyle.h"
26#include "GeoDataStyleMap.h"
27#include "GeoDataTour.h"
28#include "GeoDataTrack.h"
29#include "MarbleDebug.h"
30#include "MarbleDirs.h"
31#include "MarbleModel.h"
32#include "ParsingRunnerManager.h"
33
34namespace Marble
35{
36
37class FileLoaderPrivate
38{
39public:
40 FileLoaderPrivate(FileLoader *parent,
41 const PluginManager *pluginManager,
42 bool recenter,
43 const QString &file,
44 const QString &property,
45 const GeoDataStyle::Ptr &style,
46 DocumentRole role,
47 int renderOrder)
48 : q(parent)
49 , m_runner(pluginManager)
50 , m_filepath(file)
51 , m_property(property)
52 , m_style(style)
53 , m_styleMap(new GeoDataStyleMap)
54 , m_document(nullptr)
55 , m_renderOrder(renderOrder)
56 , m_documentRole(role)
57 , m_recenter(recenter)
58 {
59 if (m_style) {
60 m_styleMap->setId(QStringLiteral("default-map"));
61 m_styleMap->insert(QStringLiteral("normal"), QLatin1Char('#') + m_style->id());
62 }
63 }
64
65 FileLoaderPrivate(FileLoader *parent, const PluginManager *pluginManager, const QString &contents, const QString &file, DocumentRole role)
66 : q(parent)
67 , m_runner(pluginManager)
68 , m_filepath(file)
69 , m_contents(contents)
70 , m_styleMap(nullptr)
71 , m_document(nullptr)
72 , m_documentRole(role)
73 , m_recenter(false)
74 {
75 }
76
77 ~FileLoaderPrivate()
78 {
79 delete m_styleMap;
80 }
81
82 void createFilterProperties(GeoDataContainer *container);
83 static int cityPopIdx(qint64 population);
84 static int spacePopIdx(qint64 population);
85 static int areaPopIdx(qreal area);
86
87 void documentParsed(GeoDataDocument *doc, const QString &error);
88
89 FileLoader *q;
90 ParsingRunnerManager m_runner;
91 QString m_filepath;
92 QString m_contents;
93 QString m_property;
94 GeoDataStyle::Ptr m_style;
95 GeoDataStyleMap *m_styleMap;
96 GeoDataDocument *m_document;
97 QString m_error;
98 int m_renderOrder;
99 DocumentRole m_documentRole;
100 bool m_recenter;
101};
102
103FileLoader::FileLoader(QObject *parent,
104 const PluginManager *pluginManager,
105 bool recenter,
106 const QString &file,
107 const QString &property,
108 const GeoDataStyle::Ptr &style,
109 DocumentRole role,
110 int renderOrder)
111 : QThread(parent)
112 , d(new FileLoaderPrivate(this, pluginManager, recenter, file, property, style, role, renderOrder))
113{
114}
115
116FileLoader::FileLoader(QObject *parent, const PluginManager *pluginManager, const QString &contents, const QString &file, DocumentRole role)
117 : QThread(parent)
118 , d(new FileLoaderPrivate(this, pluginManager, contents, file, role))
119{
120}
121
122FileLoader::~FileLoader()
123{
124 delete d;
125}
126
127QString FileLoader::path() const
128{
129 return d->m_filepath;
130}
131
132GeoDataDocument *FileLoader::document()
133{
134 return d->m_document;
135}
136
137QString FileLoader::error() const
138{
139 return d->m_error;
140}
141
142void FileLoader::run()
143{
144 if (d->m_contents.isEmpty()) {
145 QString defaultSourceName;
146
147 mDebug() << "starting parser for" << d->m_filepath;
148
149 QFileInfo fileinfo(d->m_filepath);
150 QString path = fileinfo.path();
151 if (path == QLatin1StringView("."))
152 path.clear();
153 QString name = fileinfo.completeBaseName();
154 QString suffix = fileinfo.suffix();
155
156 // determine source, cache names
157 if (fileinfo.isAbsolute()) {
158 // We got an _absolute_ path now: e.g. "/patrick.kml"
159 defaultSourceName = path + QLatin1Char('/') + name + QLatin1Char('.') + suffix;
160 } else if (d->m_filepath.contains(QLatin1Char('/'))) {
161 // _relative_ path: "maps/mars/viking/patrick.kml"
162 defaultSourceName = MarbleDirs::path(path + QLatin1Char('/') + name + QLatin1Char('.') + suffix);
163 if (!QFile::exists(defaultSourceName)) {
164 defaultSourceName = MarbleDirs::path(path + QLatin1Char('/') + name + QLatin1StringView(".cache"));
165 }
166 } else {
167 // _standard_ shared placemarks: "placemarks/patrick.kml"
168 defaultSourceName = MarbleDirs::path(QLatin1StringView("placemarks/") + path + name + QLatin1Char('.') + suffix);
169 if (!QFile::exists(defaultSourceName)) {
170 defaultSourceName = MarbleDirs::path(QLatin1StringView("placemarks/") + path + name + QLatin1StringView(".cache"));
171 }
172 }
173
174 if (QFile::exists(defaultSourceName)) {
175 mDebug() << "No recent Default Placemark Cache File available!";
176
177 // use runners: pnt, gpx, osm
178 connect(&d->m_runner, SIGNAL(parsingFinished(GeoDataDocument *, QString)), this, SLOT(documentParsed(GeoDataDocument *, QString)));
179 d->m_runner.parseFile(defaultSourceName, d->m_documentRole);
180 } else {
181 mDebug() << "No Default Placemark Source File for " << name;
182 }
183 // content is not empty, we load from data
184 } else {
185 // Read the KML Data
186 GeoDataParser parser(GeoData_KML);
187
188 QByteArray ba(d->m_contents.toUtf8());
189 QBuffer buffer(&ba);
190 buffer.open(QIODevice::ReadOnly);
191
192 if (!parser.read(&buffer)) {
193 qWarning("Could not import kml buffer!");
194 Q_EMIT loaderFinished(this);
195 return;
196 }
197
198 GeoDocument *document = parser.releaseDocument();
199 Q_ASSERT(document);
200
201 d->m_document = static_cast<GeoDataDocument *>(document);
202 d->m_document->setProperty(d->m_property);
203 d->m_document->setDocumentRole(d->m_documentRole);
204 d->createFilterProperties(d->m_document);
205 buffer.close();
206
207 mDebug() << "newGeoDataDocumentAdded" << d->m_filepath;
208
209 Q_EMIT newGeoDataDocumentAdded(d->m_document);
210 Q_EMIT loaderFinished(this);
211 }
212}
213
214bool FileLoader::recenter() const
215{
216 return d->m_recenter;
217}
218
219void FileLoaderPrivate::documentParsed(GeoDataDocument *doc, const QString &error)
220{
221 m_error = error;
222 if (doc) {
223 m_document = doc;
224 doc->setProperty(m_property);
225 if (m_style) {
226 doc->addStyleMap(*m_styleMap);
227 doc->addStyle(m_style);
228 }
229
230 if (m_renderOrder != 0) {
231 for (GeoDataPlacemark *placemark : doc->placemarkList()) {
232 if (auto polygon = geodata_cast<GeoDataPolygon>(placemark->geometry())) {
233 polygon->setRenderOrder(m_renderOrder);
234 }
235 }
236 }
237
238 createFilterProperties(doc);
239 Q_EMIT q->newGeoDataDocumentAdded(m_document);
240 }
241 Q_EMIT q->loaderFinished(q);
242}
243
244void FileLoaderPrivate::createFilterProperties(GeoDataContainer *container)
245{
246 const QString styleUrl = QLatin1Char('#') + m_styleMap->id();
247
248 QList<GeoDataFeature *>::Iterator i = container->begin();
249 QList<GeoDataFeature *>::Iterator const end = container->end();
250 for (; i != end; ++i) {
251 if (auto child = dynamic_cast<GeoDataContainer *>(*i)) {
252 createFilterProperties(child);
253 } else if (geodata_cast<GeoDataTour>(*i) || geodata_cast<GeoDataRelation>(*i) || geodata_cast<GeoDataGroundOverlay>(*i)
254 || geodata_cast<GeoDataPhotoOverlay>(*i) || geodata_cast<GeoDataScreenOverlay>(*i)) {
255 /** @todo: How to handle this ? */
256 } else if (auto placemark = geodata_cast<GeoDataPlacemark>(*i)) {
257 const QString placemarkRole = placemark->role();
258 Q_ASSERT(placemark->geometry());
259
260 bool hasPopularity = false;
261
262 if (!geodata_cast<GeoDataTrack>(placemark->geometry()) && !geodata_cast<GeoDataPoint>(placemark->geometry()) && m_documentRole == MapDocument
263 && m_style) {
264 placemark->setStyleUrl(styleUrl);
265 }
266
267 // Mountain (H), Volcano (V), Shipwreck (W)
268 if (placemarkRole == QLatin1StringView("H") || placemarkRole == QLatin1StringView("V") || placemarkRole == QLatin1StringView("W")) {
269 qreal altitude = placemark->coordinate().altitude();
270 if (altitude != 0.0) {
271 hasPopularity = true;
272 placemark->setPopularity((qint64)(altitude * 1000.0));
273 placemark->setZoomLevel(cityPopIdx(qAbs((qint64)(altitude * 1000.0))));
274 }
275 }
276 // Continent (K), Ocean (O), Nation (S)
277 else if (placemarkRole == QLatin1StringView("K") || placemarkRole == QLatin1StringView("O") || placemarkRole == QLatin1StringView("S")) {
278 qreal area = placemark->area();
279 if (area >= 0.0) {
280 hasPopularity = true;
281 // mDebug() << placemark->name() << " " << (qint64)(area);
282 placemark->setPopularity((qint64)(area * 100));
283 placemark->setZoomLevel(areaPopIdx(area));
284 }
285 }
286 // Pole (P)
287 else if (placemarkRole == QLatin1StringView("P")) {
288 placemark->setPopularity(1000000000);
289 placemark->setZoomLevel(1);
290 }
291 // Magnetic Pole (M)
292 else if (placemarkRole == QLatin1StringView("M")) {
293 placemark->setPopularity(10000000);
294 placemark->setZoomLevel(3);
295 }
296 // MannedLandingSite (h)
297 else if (placemarkRole == QLatin1StringView("h")) {
298 placemark->setPopularity(1000000000);
299 placemark->setZoomLevel(1);
300 }
301 // RoboticRover (r)
302 else if (placemarkRole == QLatin1StringView("r")) {
303 placemark->setPopularity(10000000);
304 placemark->setZoomLevel(2);
305 }
306 // UnmannedSoftLandingSite (u)
307 else if (placemarkRole == QLatin1StringView("u")) {
308 placemark->setPopularity(1000000);
309 placemark->setZoomLevel(3);
310 }
311 // UnmannedSoftLandingSite (i)
312 else if (placemarkRole == QLatin1StringView("i")) {
313 placemark->setPopularity(1000000);
314 placemark->setZoomLevel(3);
315 }
316 // Space Terrain: Craters, Maria, Montes, Valleys, etc.
317 else if (placemarkRole == QLatin1StringView("m") || placemarkRole == QLatin1StringView("v") || placemarkRole == QLatin1StringView("o")
318 || placemarkRole == QLatin1StringView("c") || placemarkRole == QLatin1StringView("a")) {
319 qint64 diameter = placemark->population();
320 if (diameter >= 0) {
321 hasPopularity = true;
322 placemark->setPopularity(diameter);
323 if (placemarkRole == QLatin1StringView("c")) {
324 placemark->setZoomLevel(spacePopIdx(diameter));
325 if (placemark->name() == QLatin1StringView("Tycho") || placemark->name() == QLatin1StringView("Copernicus")) {
326 placemark->setZoomLevel(1);
327 }
328 } else {
329 placemark->setZoomLevel(spacePopIdx(diameter));
330 }
331
332 if (placemarkRole == QLatin1StringView("a") && diameter == 0) {
333 placemark->setPopularity(1000000000);
334 placemark->setZoomLevel(1);
335 }
336 }
337 } else {
338 qint64 population = placemark->population();
339 if (population >= 0) {
340 hasPopularity = true;
341 placemark->setPopularity(population);
342 placemark->setZoomLevel(cityPopIdx(population));
343 }
344 }
345
346 // Then we set the visual category:
347
348 if (placemarkRole == QLatin1StringView("H"))
349 placemark->setVisualCategory(GeoDataPlacemark::Mountain);
350 else if (placemarkRole == QLatin1StringView("V"))
351 placemark->setVisualCategory(GeoDataPlacemark::Volcano);
352
353 else if (placemarkRole == QLatin1StringView("m"))
354 placemark->setVisualCategory(GeoDataPlacemark::Mons);
355 else if (placemarkRole == QLatin1StringView("v"))
356 placemark->setVisualCategory(GeoDataPlacemark::Valley);
357 else if (placemarkRole == QLatin1StringView("o"))
358 placemark->setVisualCategory(GeoDataPlacemark::OtherTerrain);
359 else if (placemarkRole == QLatin1StringView("c"))
360 placemark->setVisualCategory(GeoDataPlacemark::Crater);
361 else if (placemarkRole == QLatin1StringView("a"))
362 placemark->setVisualCategory(GeoDataPlacemark::Mare);
363
364 else if (placemarkRole == QLatin1StringView("P"))
365 placemark->setVisualCategory(GeoDataPlacemark::GeographicPole);
366 else if (placemarkRole == QLatin1StringView("M"))
367 placemark->setVisualCategory(GeoDataPlacemark::MagneticPole);
368 else if (placemarkRole == QLatin1StringView("W"))
369 placemark->setVisualCategory(GeoDataPlacemark::ShipWreck);
370 else if (placemarkRole == QLatin1StringView("F"))
371 placemark->setVisualCategory(GeoDataPlacemark::AirPort);
372 else if (placemarkRole == QLatin1StringView("A"))
373 placemark->setVisualCategory(GeoDataPlacemark::Observatory);
374 else if (placemarkRole == QLatin1StringView("K"))
375 placemark->setVisualCategory(GeoDataPlacemark::Continent);
376 else if (placemarkRole == QLatin1StringView("O"))
377 placemark->setVisualCategory(GeoDataPlacemark::Ocean);
378 else if (placemarkRole == QLatin1StringView("S"))
379 placemark->setVisualCategory(GeoDataPlacemark::Nation);
380 else if (placemarkRole == QLatin1StringView("PPL") || placemarkRole == QLatin1StringView("PPLF") || placemarkRole == QLatin1StringView("PPLG")
381 || placemarkRole == QLatin1StringView("PPLL") || placemarkRole == QLatin1StringView("PPLQ") || placemarkRole == QLatin1StringView("PPLR")
382 || placemarkRole == QLatin1StringView("PPLS") || placemarkRole == QLatin1StringView("PPLW")) {
383 switch (placemark->zoomLevel()) {
384 case 3:
385 case 4:
386 placemark->setVisualCategory(GeoDataPlacemark::LargeCity);
387 break;
388 case 5:
389 case 6:
390 placemark->setVisualCategory(GeoDataPlacemark::BigCity);
391 break;
392 case 7:
393 case 8:
394 placemark->setVisualCategory(GeoDataPlacemark::MediumCity);
395 break;
396 default:
397 placemark->setVisualCategory(GeoDataPlacemark::SmallCity);
398 break;
399 }
400 } else if (placemarkRole == QLatin1StringView("PPLA")) {
401 switch (placemark->zoomLevel()) {
402 case 3:
403 case 4:
404 placemark->setVisualCategory(GeoDataPlacemark::LargeStateCapital);
405 break;
406 case 5:
407 case 6:
408 placemark->setVisualCategory(GeoDataPlacemark::BigStateCapital);
409 break;
410 case 7:
411 case 8:
412 placemark->setVisualCategory(GeoDataPlacemark::MediumStateCapital);
413 break;
414 default:
415 placemark->setVisualCategory(GeoDataPlacemark::SmallStateCapital);
416 break;
417 }
418 } else if (placemarkRole == QLatin1StringView("PPLC")) {
419 switch (placemark->zoomLevel()) {
420 case 3:
421 case 4:
422 placemark->setVisualCategory(GeoDataPlacemark::LargeNationCapital);
423 break;
424 case 5:
425 case 6:
426 placemark->setVisualCategory(GeoDataPlacemark::BigNationCapital);
427 break;
428 case 7:
429 case 8:
430 placemark->setVisualCategory(GeoDataPlacemark::MediumNationCapital);
431 break;
432 default:
433 placemark->setVisualCategory(GeoDataPlacemark::SmallNationCapital);
434 break;
435 }
436 } else if (placemarkRole == QLatin1StringView("PPLA2") || placemarkRole == QLatin1StringView("PPLA3")
437 || placemarkRole == QLatin1StringView("PPLA4")) {
438 switch (placemark->zoomLevel()) {
439 case 3:
440 case 4:
441 placemark->setVisualCategory(GeoDataPlacemark::LargeCountyCapital);
442 break;
443 case 5:
444 case 6:
445 placemark->setVisualCategory(GeoDataPlacemark::BigCountyCapital);
446 break;
447 case 7:
448 case 8:
449 placemark->setVisualCategory(GeoDataPlacemark::MediumCountyCapital);
450 break;
451 default:
452 placemark->setVisualCategory(GeoDataPlacemark::SmallCountyCapital);
453 break;
454 }
455 } else if (placemarkRole == QLatin1StringView(" ") && !hasPopularity && placemark->visualCategory() == GeoDataPlacemark::Unknown) {
456 placemark->setVisualCategory(GeoDataPlacemark::Unknown); // default location
457 placemark->setZoomLevel(0);
458 } else if (placemarkRole == QLatin1StringView("h")) {
459 placemark->setVisualCategory(GeoDataPlacemark::MannedLandingSite);
460 } else if (placemarkRole == QLatin1StringView("r")) {
461 placemark->setVisualCategory(GeoDataPlacemark::RoboticRover);
462 } else if (placemarkRole == QLatin1StringView("u")) {
463 placemark->setVisualCategory(GeoDataPlacemark::UnmannedSoftLandingSite);
464 } else if (placemarkRole == QLatin1StringView("i")) {
465 placemark->setVisualCategory(GeoDataPlacemark::UnmannedHardLandingSite);
466 }
467
468 // At last fine-tune zoomlevel:
469 if (!placemark->isVisible()) {
470 placemark->setZoomLevel(18);
471 }
472 // Workaround: Emulate missing "setVisible" serialization by allowing for population
473 // values smaller than -1 which are considered invisible.
474 else if (placemark->population() < -1) {
475 placemark->setZoomLevel(18);
476 } else if (placemarkRole == QLatin1StringView("W")) {
477 if (placemark->zoomLevel() < 4) {
478 placemark->setZoomLevel(4);
479 }
480 } else if (placemarkRole == QLatin1StringView("O")) {
481 placemark->setZoomLevel(2);
482 } else if (placemarkRole == QLatin1StringView("K")) {
483 placemark->setZoomLevel(0);
484 }
485 } else {
486 qWarning() << "Unknown feature" << (*i)->nodeType() << ". Skipping.";
487 }
488 }
489}
490
491int FileLoaderPrivate::cityPopIdx(qint64 population)
492{
493 int popidx = 3;
494
495 if (population < 2500)
496 popidx = 10;
497 else if (population < 5000)
498 popidx = 9;
499 else if (population < 25000)
500 popidx = 8;
501 else if (population < 75000)
502 popidx = 7;
503 else if (population < 250000)
504 popidx = 6;
505 else if (population < 750000)
506 popidx = 5;
507 else if (population < 2500000)
508 popidx = 4;
509
510 return popidx;
511}
512
513int FileLoaderPrivate::spacePopIdx(qint64 population)
514{
515 int popidx = 1;
516
517 if (population < 1000)
518 popidx = 10;
519 else if (population < 2000)
520 popidx = 9;
521 else if (population < 8000)
522 popidx = 8;
523 else if (population < 20000)
524 popidx = 7;
525 else if (population < 60000)
526 popidx = 6;
527 else if (population < 100000)
528 popidx = 5;
529 else if (population < 200000)
530 popidx = 4;
531 else if (population < 400000)
532 popidx = 2;
533 else if (population < 600000)
534 popidx = 1;
535
536 return popidx;
537}
538
539int FileLoaderPrivate::areaPopIdx(qreal area)
540{
541 int popidx = 1;
542 if (area < 200000)
543 popidx = 5;
544 else if (area < 1000000)
545 popidx = 4;
546 else if (area < 2500000)
547 popidx = 3;
548 else if (area < 5000000)
549 popidx = 2;
550
551 return popidx;
552}
553
554} // namespace Marble
555
556#include "moc_FileLoader.cpp"
This file contains the headers for MarbleModel.
void setId(const QString &value)
Set the id of the object.
QString name(GameStandardAction id)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Binds a QML item to a specific geodetic location in screen coordinates.
bool exists() const const
iterator insert(const Key &key, const T &value)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void clear()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 29 2024 11:55:53 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.