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