File indexing completed on 2024-04-21 03:49:33
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2008 Patrick Spendrin <ps_ml@gmx.de> 0004 // 0005 0006 #include "FileLoader.h" 0007 0008 #include <QBuffer> 0009 #include <QDataStream> 0010 #include <QFile> 0011 0012 #include "GeoDataParser.h" 0013 #include "GeoDataFolder.h" 0014 #include "GeoDataGroundOverlay.h" 0015 #include "GeoDataPlacemark.h" 0016 #include "GeoDataData.h" 0017 #include "GeoDataExtendedData.h" 0018 #include "GeoDataStyle.h" 0019 #include "GeoDataStyleMap.h" 0020 #include "GeoDataPhotoOverlay.h" 0021 #include "GeoDataPoint.h" 0022 #include "GeoDataPolyStyle.h" 0023 #include "GeoDataLineStyle.h" 0024 #include "GeoDataPolygon.h" 0025 #include "GeoDataRelation.h" 0026 #include "GeoDataScreenOverlay.h" 0027 #include "GeoDataTour.h" 0028 #include "GeoDataTrack.h" 0029 #include "MarbleDirs.h" 0030 #include "MarbleDebug.h" 0031 #include "MarbleModel.h" 0032 #include "ParsingRunnerManager.h" 0033 0034 namespace Marble 0035 { 0036 0037 class FileLoaderPrivate 0038 { 0039 public: 0040 FileLoaderPrivate( FileLoader* parent, const PluginManager *pluginManager, bool recenter, 0041 const QString &file, const QString &property, const GeoDataStyle::Ptr &style, DocumentRole role, int renderOrder) : 0042 q(parent), 0043 m_runner(pluginManager), 0044 m_filepath (file), 0045 m_property(property), 0046 m_style(style), 0047 m_styleMap(new GeoDataStyleMap), 0048 m_document(nullptr), 0049 m_renderOrder(renderOrder), 0050 m_documentRole(role), 0051 m_recenter(recenter) 0052 { 0053 if( m_style ) { 0054 m_styleMap->setId(QStringLiteral("default-map")); 0055 m_styleMap->insert(QStringLiteral("normal"), QLatin1Char('#') + m_style->id()); 0056 } 0057 } 0058 0059 FileLoaderPrivate( FileLoader* parent, const PluginManager *pluginManager, 0060 const QString &contents, const QString &file, DocumentRole role) : 0061 q(parent), 0062 m_runner(pluginManager), 0063 m_filepath(file), 0064 m_contents(contents), 0065 m_styleMap(nullptr), 0066 m_document(nullptr), 0067 m_documentRole(role), 0068 m_recenter(false) 0069 { 0070 } 0071 0072 ~FileLoaderPrivate() 0073 { 0074 delete m_styleMap; 0075 } 0076 0077 void createFilterProperties( GeoDataContainer *container ); 0078 static int cityPopIdx( qint64 population ); 0079 static int spacePopIdx( qint64 population ); 0080 static int areaPopIdx( qreal area ); 0081 0082 void documentParsed( GeoDataDocument *doc, const QString& error); 0083 0084 FileLoader *q; 0085 ParsingRunnerManager m_runner; 0086 QString m_filepath; 0087 QString m_contents; 0088 QString m_property; 0089 GeoDataStyle::Ptr m_style; 0090 GeoDataStyleMap* m_styleMap; 0091 GeoDataDocument *m_document; 0092 QString m_error; 0093 int m_renderOrder; 0094 DocumentRole m_documentRole; 0095 bool m_recenter; 0096 }; 0097 0098 FileLoader::FileLoader( QObject* parent, const PluginManager *pluginManager, bool recenter, const QString& file, 0099 const QString& property, const GeoDataStyle::Ptr &style, DocumentRole role, int renderOrder ) 0100 : QThread( parent ), 0101 d( new FileLoaderPrivate( this, pluginManager, recenter, file, property, style, role, renderOrder ) ) 0102 { 0103 } 0104 0105 FileLoader::FileLoader( QObject* parent, const PluginManager *pluginManager, 0106 const QString& contents, const QString& file, DocumentRole role ) 0107 : QThread( parent ), 0108 d( new FileLoaderPrivate( this, pluginManager, contents, file, role ) ) 0109 { 0110 } 0111 0112 FileLoader::~FileLoader() 0113 { 0114 delete d; 0115 } 0116 0117 QString FileLoader::path() const 0118 { 0119 return d->m_filepath; 0120 } 0121 0122 GeoDataDocument* FileLoader::document() 0123 { 0124 return d->m_document; 0125 } 0126 0127 QString FileLoader::error() const 0128 { 0129 return d->m_error; 0130 } 0131 0132 void FileLoader::run() 0133 { 0134 if ( d->m_contents.isEmpty() ) { 0135 QString defaultSourceName; 0136 0137 mDebug() << "starting parser for" << d->m_filepath; 0138 0139 QFileInfo fileinfo( d->m_filepath ); 0140 QString path = fileinfo.path(); 0141 if (path == QLatin1String(".")) path.clear(); 0142 QString name = fileinfo.completeBaseName(); 0143 QString suffix = fileinfo.suffix(); 0144 0145 // determine source, cache names 0146 if ( fileinfo.isAbsolute() ) { 0147 // We got an _absolute_ path now: e.g. "/patrick.kml" 0148 defaultSourceName = path + QLatin1Char('/') + name + QLatin1Char('.') + suffix; 0149 } 0150 else if ( d->m_filepath.contains( '/' ) ) { 0151 // _relative_ path: "maps/mars/viking/patrick.kml" 0152 defaultSourceName = MarbleDirs::path(path + QLatin1Char('/') + name + QLatin1Char('.') + suffix); 0153 if ( !QFile::exists( defaultSourceName ) ) { 0154 defaultSourceName = MarbleDirs::path(path + QLatin1Char('/') + name + QLatin1String(".cache")); 0155 } 0156 } 0157 else { 0158 // _standard_ shared placemarks: "placemarks/patrick.kml" 0159 defaultSourceName = MarbleDirs::path(QLatin1String("placemarks/") + path + name + QLatin1Char('.') + suffix); 0160 if ( !QFile::exists( defaultSourceName ) ) { 0161 defaultSourceName = MarbleDirs::path(QLatin1String("placemarks/") + path + name + QLatin1String(".cache")); 0162 } 0163 } 0164 0165 if ( QFile::exists( defaultSourceName ) ) { 0166 mDebug() << "No recent Default Placemark Cache File available!"; 0167 0168 // use runners: pnt, gpx, osm 0169 connect( &d->m_runner, SIGNAL(parsingFinished(GeoDataDocument*,QString)), 0170 this, SLOT(documentParsed(GeoDataDocument*,QString)) ); 0171 d->m_runner.parseFile( defaultSourceName, d->m_documentRole ); 0172 } 0173 else { 0174 mDebug() << "No Default Placemark Source File for " << name; 0175 } 0176 // content is not empty, we load from data 0177 } else { 0178 // Read the KML Data 0179 GeoDataParser parser( GeoData_KML ); 0180 0181 QByteArray ba( d->m_contents.toUtf8() ); 0182 QBuffer buffer( &ba ); 0183 buffer.open( QIODevice::ReadOnly ); 0184 0185 if ( !parser.read( &buffer ) ) { 0186 qWarning( "Could not import kml buffer!" ); 0187 emit loaderFinished( this ); 0188 return; 0189 } 0190 0191 GeoDocument* document = parser.releaseDocument(); 0192 Q_ASSERT( document ); 0193 0194 d->m_document = static_cast<GeoDataDocument*>( document ); 0195 d->m_document->setProperty( d->m_property ); 0196 d->m_document->setDocumentRole( d->m_documentRole ); 0197 d->createFilterProperties( d->m_document ); 0198 buffer.close(); 0199 0200 mDebug() << "newGeoDataDocumentAdded" << d->m_filepath; 0201 0202 emit newGeoDataDocumentAdded( d->m_document ); 0203 emit loaderFinished( this ); 0204 } 0205 0206 } 0207 0208 bool FileLoader::recenter() const 0209 { 0210 return d->m_recenter; 0211 } 0212 0213 void FileLoaderPrivate::documentParsed( GeoDataDocument* doc, const QString& error ) 0214 { 0215 m_error = error; 0216 if ( doc ) { 0217 m_document = doc; 0218 doc->setProperty( m_property ); 0219 if( m_style ) { 0220 doc->addStyleMap( *m_styleMap ); 0221 doc->addStyle( m_style ); 0222 } 0223 0224 if (m_renderOrder != 0) { 0225 for (GeoDataPlacemark* placemark: doc->placemarkList()) { 0226 if (GeoDataPolygon *polygon = geodata_cast<GeoDataPolygon>(placemark->geometry())) { 0227 polygon->setRenderOrder(m_renderOrder); 0228 } 0229 } 0230 } 0231 0232 createFilterProperties( doc ); 0233 emit q->newGeoDataDocumentAdded( m_document ); 0234 } 0235 emit q->loaderFinished( q ); 0236 } 0237 0238 void FileLoaderPrivate::createFilterProperties( GeoDataContainer *container ) 0239 { 0240 const QString styleUrl = QLatin1Char('#') + m_styleMap->id(); 0241 0242 QVector<GeoDataFeature*>::Iterator i = container->begin(); 0243 QVector<GeoDataFeature*>::Iterator const end = container->end(); 0244 for (; i != end; ++i ) { 0245 if (auto child = dynamic_cast<GeoDataContainer *>(*i)) { 0246 createFilterProperties( child ); 0247 } else if (geodata_cast<GeoDataTour>(*i) 0248 || geodata_cast<GeoDataRelation>(*i) 0249 || geodata_cast<GeoDataGroundOverlay>(*i) 0250 || geodata_cast<GeoDataPhotoOverlay>(*i) 0251 || geodata_cast<GeoDataScreenOverlay>(*i)) { 0252 /** @todo: How to handle this ? */ 0253 } else if (auto placemark = geodata_cast<GeoDataPlacemark>(*i)) { 0254 const QString placemarkRole = placemark->role(); 0255 Q_ASSERT( placemark->geometry() ); 0256 0257 bool hasPopularity = false; 0258 0259 if (!geodata_cast<GeoDataTrack>(placemark->geometry()) && 0260 !geodata_cast<GeoDataPoint>(placemark->geometry()) 0261 && m_documentRole == MapDocument 0262 && m_style ) { 0263 placemark->setStyleUrl(styleUrl); 0264 } 0265 0266 // Mountain (H), Volcano (V), Shipwreck (W) 0267 if (placemarkRole == QLatin1String("H") || 0268 placemarkRole == QLatin1String("V") || 0269 placemarkRole == QLatin1String("W")) 0270 { 0271 qreal altitude = placemark->coordinate().altitude(); 0272 if ( altitude != 0.0 ) 0273 { 0274 hasPopularity = true; 0275 placemark->setPopularity( (qint64)(altitude * 1000.0) ); 0276 placemark->setZoomLevel( cityPopIdx( qAbs( (qint64)(altitude * 1000.0) ) ) ); 0277 } 0278 } 0279 // Continent (K), Ocean (O), Nation (S) 0280 else if (placemarkRole == QLatin1String("K") || 0281 placemarkRole == QLatin1String("O") || 0282 placemarkRole == QLatin1String("S")) 0283 { 0284 qreal area = placemark->area(); 0285 if ( area >= 0.0 ) 0286 { 0287 hasPopularity = true; 0288 // mDebug() << placemark->name() << " " << (qint64)(area); 0289 placemark->setPopularity( (qint64)(area * 100) ); 0290 placemark->setZoomLevel( areaPopIdx( area ) ); 0291 } 0292 } 0293 // Pole (P) 0294 else if (placemarkRole == QLatin1String("P") ) 0295 { 0296 placemark->setPopularity( 1000000000 ); 0297 placemark->setZoomLevel( 1 ); 0298 } 0299 // Magnetic Pole (M) 0300 else if (placemarkRole == QLatin1String("M")) 0301 { 0302 placemark->setPopularity( 10000000 ); 0303 placemark->setZoomLevel( 3 ); 0304 } 0305 // MannedLandingSite (h) 0306 else if (placemarkRole == QLatin1String("h")) 0307 { 0308 placemark->setPopularity( 1000000000 ); 0309 placemark->setZoomLevel( 1 ); 0310 } 0311 // RoboticRover (r) 0312 else if (placemarkRole == QLatin1String("r")) 0313 { 0314 placemark->setPopularity( 10000000 ); 0315 placemark->setZoomLevel( 2 ); 0316 } 0317 // UnmannedSoftLandingSite (u) 0318 else if (placemarkRole == QLatin1String("u")) 0319 { 0320 placemark->setPopularity( 1000000 ); 0321 placemark->setZoomLevel( 3 ); 0322 } 0323 // UnmannedSoftLandingSite (i) 0324 else if (placemarkRole == QLatin1String("i")) 0325 { 0326 placemark->setPopularity( 1000000 ); 0327 placemark->setZoomLevel( 3 ); 0328 } 0329 // Space Terrain: Craters, Maria, Montes, Valleys, etc. 0330 else if (placemarkRole == QLatin1String("m") || 0331 placemarkRole == QLatin1String("v") || 0332 placemarkRole == QLatin1String("o") || 0333 placemarkRole == QLatin1String("c") || 0334 placemarkRole == QLatin1String("a")) 0335 { 0336 qint64 diameter = placemark->population(); 0337 if ( diameter >= 0 ) 0338 { 0339 hasPopularity = true; 0340 placemark->setPopularity( diameter ); 0341 if (placemarkRole == QLatin1String("c")) { 0342 placemark->setZoomLevel( spacePopIdx( diameter ) ); 0343 if (placemark->name() == QLatin1String("Tycho") || 0344 placemark->name() == QLatin1String("Copernicus")) { 0345 placemark->setZoomLevel( 1 ); 0346 } 0347 } 0348 else { 0349 placemark->setZoomLevel( spacePopIdx( diameter ) ); 0350 } 0351 0352 if (placemarkRole == QLatin1String("a") && diameter == 0) { 0353 placemark->setPopularity( 1000000000 ); 0354 placemark->setZoomLevel( 1 ); 0355 } 0356 } 0357 } 0358 else 0359 { 0360 qint64 population = placemark->population(); 0361 if ( population >= 0 ) 0362 { 0363 hasPopularity = true; 0364 placemark->setPopularity( population ); 0365 placemark->setZoomLevel( cityPopIdx( population ) ); 0366 } 0367 } 0368 0369 // Then we set the visual category: 0370 0371 if (placemarkRole == QLatin1String("H")) placemark->setVisualCategory( GeoDataPlacemark::Mountain ); 0372 else if (placemarkRole == QLatin1String("V")) placemark->setVisualCategory( GeoDataPlacemark::Volcano ); 0373 0374 else if (placemarkRole == QLatin1String("m")) placemark->setVisualCategory( GeoDataPlacemark::Mons ); 0375 else if (placemarkRole == QLatin1String("v")) placemark->setVisualCategory( GeoDataPlacemark::Valley ); 0376 else if (placemarkRole == QLatin1String("o")) placemark->setVisualCategory( GeoDataPlacemark::OtherTerrain ); 0377 else if (placemarkRole == QLatin1String("c")) placemark->setVisualCategory( GeoDataPlacemark::Crater ); 0378 else if (placemarkRole == QLatin1String("a")) placemark->setVisualCategory( GeoDataPlacemark::Mare ); 0379 0380 else if (placemarkRole == QLatin1String("P")) placemark->setVisualCategory( GeoDataPlacemark::GeographicPole ); 0381 else if (placemarkRole == QLatin1String("M")) placemark->setVisualCategory( GeoDataPlacemark::MagneticPole ); 0382 else if (placemarkRole == QLatin1String("W")) placemark->setVisualCategory( GeoDataPlacemark::ShipWreck ); 0383 else if (placemarkRole == QLatin1String("F")) placemark->setVisualCategory( GeoDataPlacemark::AirPort ); 0384 else if (placemarkRole == QLatin1String("A")) placemark->setVisualCategory( GeoDataPlacemark::Observatory ); 0385 else if (placemarkRole == QLatin1String("K")) placemark->setVisualCategory( GeoDataPlacemark::Continent ); 0386 else if (placemarkRole == QLatin1String("O")) placemark->setVisualCategory( GeoDataPlacemark::Ocean ); 0387 else if (placemarkRole == QLatin1String("S")) placemark->setVisualCategory( GeoDataPlacemark::Nation ); 0388 else if (placemarkRole == QLatin1String("PPL") || 0389 placemarkRole == QLatin1String("PPLF") || 0390 placemarkRole == QLatin1String("PPLG") || 0391 placemarkRole == QLatin1String("PPLL") || 0392 placemarkRole == QLatin1String("PPLQ") || 0393 placemarkRole == QLatin1String("PPLR") || 0394 placemarkRole == QLatin1String("PPLS") || 0395 placemarkRole == QLatin1String("PPLW")) { 0396 switch (placemark->zoomLevel()) { 0397 case 3: 0398 case 4: 0399 placemark->setVisualCategory(GeoDataPlacemark::LargeCity); 0400 break; 0401 case 5: 0402 case 6: 0403 placemark->setVisualCategory(GeoDataPlacemark::BigCity); 0404 break; 0405 case 7: 0406 case 8: 0407 placemark->setVisualCategory(GeoDataPlacemark::MediumCity); 0408 break; 0409 default: 0410 placemark->setVisualCategory(GeoDataPlacemark::SmallCity); 0411 break; 0412 } 0413 } 0414 else if (placemarkRole == QLatin1String("PPLA")) { 0415 switch (placemark->zoomLevel()) { 0416 case 3: 0417 case 4: 0418 placemark->setVisualCategory(GeoDataPlacemark::LargeStateCapital); 0419 break; 0420 case 5: 0421 case 6: 0422 placemark->setVisualCategory(GeoDataPlacemark::BigStateCapital); 0423 break; 0424 case 7: 0425 case 8: 0426 placemark->setVisualCategory(GeoDataPlacemark::MediumStateCapital); 0427 break; 0428 default: 0429 placemark->setVisualCategory(GeoDataPlacemark::SmallStateCapital); 0430 break; 0431 } 0432 } 0433 else if (placemarkRole == QLatin1String("PPLC")) { 0434 switch (placemark->zoomLevel()) { 0435 case 3: 0436 case 4: 0437 placemark->setVisualCategory(GeoDataPlacemark::LargeNationCapital); 0438 break; 0439 case 5: 0440 case 6: 0441 placemark->setVisualCategory(GeoDataPlacemark::BigNationCapital); 0442 break; 0443 case 7: 0444 case 8: 0445 placemark->setVisualCategory(GeoDataPlacemark::MediumNationCapital); 0446 break; 0447 default: 0448 placemark->setVisualCategory(GeoDataPlacemark::SmallNationCapital); 0449 break; 0450 } 0451 } 0452 else if (placemarkRole == QLatin1String("PPLA2") || 0453 placemarkRole == QLatin1String("PPLA3") || 0454 placemarkRole == QLatin1String("PPLA4")) { 0455 switch (placemark->zoomLevel()) { 0456 case 3: 0457 case 4: 0458 placemark->setVisualCategory(GeoDataPlacemark::LargeCountyCapital); 0459 break; 0460 case 5: 0461 case 6: 0462 placemark->setVisualCategory(GeoDataPlacemark::BigCountyCapital); 0463 break; 0464 case 7: 0465 case 8: 0466 placemark->setVisualCategory(GeoDataPlacemark::MediumCountyCapital); 0467 break; 0468 default: 0469 placemark->setVisualCategory(GeoDataPlacemark::SmallCountyCapital); 0470 break; 0471 } 0472 } 0473 else if (placemarkRole == QLatin1String(" ") && !hasPopularity && placemark->visualCategory() == GeoDataPlacemark::Unknown) { 0474 placemark->setVisualCategory( GeoDataPlacemark::Unknown ); // default location 0475 placemark->setZoomLevel(0); 0476 } 0477 else if (placemarkRole == QLatin1String("h")) { 0478 placemark->setVisualCategory( GeoDataPlacemark::MannedLandingSite ); 0479 } 0480 else if (placemarkRole == QLatin1String("r")) { 0481 placemark->setVisualCategory( GeoDataPlacemark::RoboticRover ); 0482 } 0483 else if (placemarkRole == QLatin1String("u")) { 0484 placemark->setVisualCategory( GeoDataPlacemark::UnmannedSoftLandingSite ); 0485 } 0486 else if (placemarkRole == QLatin1String("i")) { 0487 placemark->setVisualCategory( GeoDataPlacemark::UnmannedHardLandingSite ); 0488 } 0489 0490 // At last fine-tune zoomlevel: 0491 if (!placemark->isVisible()) { 0492 placemark->setZoomLevel( 18 ); 0493 } 0494 // Workaround: Emulate missing "setVisible" serialization by allowing for population 0495 // values smaller than -1 which are considered invisible. 0496 else if (placemark->population() < -1) { 0497 placemark->setZoomLevel( 18 ); 0498 } 0499 else if (placemarkRole == QLatin1String("W")) { 0500 if (placemark->zoomLevel() < 4) { 0501 placemark->setZoomLevel( 4 ); 0502 } 0503 } 0504 else if (placemarkRole == QLatin1String("O")) { 0505 placemark->setZoomLevel( 2 ); 0506 } 0507 else if (placemarkRole == QLatin1String("K")) { 0508 placemark->setZoomLevel( 0 ); 0509 } 0510 } else { 0511 qWarning() << "Unknown feature" << (*i)->nodeType() << ". Skipping."; 0512 } 0513 } 0514 } 0515 0516 int FileLoaderPrivate::cityPopIdx( qint64 population ) 0517 { 0518 int popidx = 3; 0519 0520 if ( population < 2500 ) popidx=10; 0521 else if ( population < 5000) popidx=9; 0522 else if ( population < 25000) popidx=8; 0523 else if ( population < 75000) popidx=7; 0524 else if ( population < 250000) popidx=6; 0525 else if ( population < 750000) popidx=5; 0526 else if ( population < 2500000) popidx=4; 0527 0528 return popidx; 0529 } 0530 0531 int FileLoaderPrivate::spacePopIdx( qint64 population ) 0532 { 0533 int popidx = 1; 0534 0535 if ( population < 1000 ) popidx=10; 0536 else if ( population < 2000) popidx=9; 0537 else if ( population < 8000) popidx=8; 0538 else if ( population < 20000) popidx=7; 0539 else if ( population < 60000) popidx=6; 0540 else if ( population < 100000) popidx=5; 0541 else if ( population < 200000 ) popidx=4; 0542 else if ( population < 400000 ) popidx=2; 0543 else if ( population < 600000 ) popidx=1; 0544 0545 return popidx; 0546 } 0547 0548 int FileLoaderPrivate::areaPopIdx( qreal area ) 0549 { 0550 int popidx = 1; 0551 if ( area < 200000 ) popidx=5; 0552 else if ( area < 1000000 ) popidx=4; 0553 else if ( area < 2500000 ) popidx=3; 0554 else if ( area < 5000000 ) popidx=2; 0555 0556 return popidx; 0557 } 0558 0559 0560 0561 #include "moc_FileLoader.cpp" 0562 } // namespace Marble