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