Marble

FileLoader.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008 Patrick Spendrin <[email protected]>
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 
34 namespace Marble
35 {
36 
37 class FileLoaderPrivate
38 {
39 public:
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 
98 FileLoader::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 
105 FileLoader::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 
112 FileLoader::~FileLoader()
113 {
114  delete d;
115 }
116 
117 QString FileLoader::path() const
118 {
119  return d->m_filepath;
120 }
121 
122 GeoDataDocument* FileLoader::document()
123 {
124  return d->m_document;
125 }
126 
127 QString FileLoader::error() const
128 {
129  return d->m_error;
130 }
131 
132 void 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 
208 bool FileLoader::recenter() const
209 {
210  return d->m_recenter;
211 }
212 
213 void 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 
238 void FileLoaderPrivate::createFilterProperties( GeoDataContainer *container )
239 {
240  const QString styleUrl = QLatin1Char('#') + m_styleMap->id();
241 
242  QVector<GeoDataFeature*>::Iterator i = container->begin();
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() << Q_FUNC_INFO << "Unknown feature" << (*i)->nodeType() << ". Skipping.";
512  }
513  }
514 }
515 
516 int 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 
531 int 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 
548 int 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
QVector::iterator begin()
void clear()
bool exists() const const
Binds a QML item to a specific geodetic location in screen coordinates.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString path(const QString &relativePath)
QString name(StandardShortcut id)
const QList< QKeySequence > & end()
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.