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