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

KDE's Doxygen guidelines are available online.