File indexing completed on 2025-01-05 03:59:22

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2008, 2009, 2010 Jens-Michael Hoffmann <jmho@c-xx.com>
0006 // SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de>
0007 // SPDX-FileCopyrightText: 2010-2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0008 // SPDX-FileCopyrightText: 2014 Abhinav Gangwar <abhgang@gmail.com>
0009 //
0010 
0011 #include "MarbleModel.h"
0012 
0013 #include <cmath>
0014 
0015 #include <QAtomicInt>
0016 #include <QPointer>
0017 #include <QAbstractItemModel>
0018 #include <QItemSelectionModel>
0019 #include <QSortFilterProxyModel>
0020 #include <QTextDocument>
0021 
0022 #include "kdescendantsproxymodel.h"
0023 #include "MapThemeManager.h"
0024 #include "GeoSceneDocument.h"
0025 #include "GeoSceneGeodata.h"
0026 #include "GeoSceneHead.h"
0027 #include "GeoSceneLayer.h"
0028 #include "GeoSceneMap.h"
0029 #include "GeoScenePalette.h"
0030 #include "GeoSceneTileDataset.h"
0031 #include "GeoSceneVector.h"
0032 #include "GeoDataDocument.h"
0033 #include "GeoDataFeature.h"
0034 #include "GeoDataPlacemark.h"
0035 #include "GeoDataPoint.h"
0036 #include "GeoDataStyle.h"
0037 #include "GeoDataStyleMap.h"
0038 #include "GeoDataTrack.h"
0039 #include "GeoDataLineStyle.h"
0040 #include "GeoDataPolyStyle.h"
0041 #include "GeoDataTypes.h"
0042 #include "DgmlAuxillaryDictionary.h"
0043 #include "MarbleClock.h"
0044 #include "FileStoragePolicy.h"
0045 #include "FileStorageWatcher.h"
0046 #include "HttpDownloadManager.h"
0047 #include "MarbleDirs.h"
0048 #include "FileManager.h"
0049 #include "GeoDataTreeModel.h"
0050 #include "Planet.h"
0051 #include "PlanetFactory.h"
0052 #include "PluginManager.h"
0053 #include "StoragePolicy.h"
0054 #include "SunLocator.h"
0055 #include "TileCreator.h"
0056 #include "TileCreatorDialog.h"
0057 #include "TileLoader.h"
0058 #include "ElevationModel.h"
0059 
0060 #include "digikam_debug.h"
0061 
0062 namespace Marble
0063 {
0064 
0065 class MarbleModelPrivate
0066 {
0067  public:
0068     MarbleModelPrivate()
0069         : m_clock(),
0070           m_planet(PlanetFactory::construct(QStringLiteral("earth"))),
0071           m_sunLocator( &m_clock, &m_planet ),
0072           m_pluginManager(),
0073           m_homePoint( -9.4, 54.8, 0.0, GeoDataCoordinates::Degree ),  // Some point that tackat defined. :-)
0074           m_homeZoom( 1050 ),
0075           m_mapTheme( nullptr ),
0076           m_storagePolicy( MarbleDirs::localPath() ),
0077           m_downloadManager( &m_storagePolicy ),
0078           m_storageWatcher( MarbleDirs::localPath() ),
0079           m_treeModel(),
0080           m_descendantProxy(),
0081           m_placemarkProxyModel(),
0082           m_placemarkSelectionModel( nullptr ),
0083           m_fileManager( &m_treeModel, &m_pluginManager ),
0084           m_trackedPlacemark( nullptr ),
0085           m_legend( nullptr ),
0086           m_workOffline( false ),
0087           m_elevationModel( &m_downloadManager, &m_pluginManager )
0088     {
0089         m_descendantProxy.setSourceModel( &m_treeModel );
0090 
0091         m_placemarkProxyModel.setFilterFixedString( QString::fromUtf8(GeoDataTypes::GeoDataPlacemarkType) );
0092         m_placemarkProxyModel.setFilterKeyColumn( 1 );
0093         m_placemarkProxyModel.setSourceModel( &m_descendantProxy );
0094         m_placemarkSelectionModel.setModel(&m_placemarkProxyModel);
0095 
0096         m_groundOverlayProxyModel.setFilterFixedString( QString::fromUtf8(GeoDataTypes::GeoDataGroundOverlayType) );
0097         m_groundOverlayProxyModel.setFilterKeyColumn( 1 );
0098         m_groundOverlayProxyModel.setSourceModel( &m_descendantProxy );
0099     }
0100 
0101     ~MarbleModelPrivate()
0102     {
0103         delete m_mapTheme;
0104         delete m_legend;
0105     }
0106     /**
0107      * @brief Assigns each placemark an inline
0108      * style based on the color values specified
0109      * by colorMap attribute under <brush> element
0110      * in theme file.
0111      */
0112     void assignFillColors( const QString &filePath );
0113     void assignFillColors(GeoDataDocument *doc, const GeoSceneGeodata &data) const;
0114 
0115     void addHighlightStyle(GeoDataDocument *doc) const;
0116 
0117     // Misc stuff.
0118     MarbleClock              m_clock;
0119     Planet                   m_planet;
0120     SunLocator               m_sunLocator;
0121 
0122     PluginManager            m_pluginManager;
0123 
0124     // The home position
0125     GeoDataCoordinates       m_homePoint;
0126     int                      m_homeZoom;
0127 
0128     // View and paint stuff
0129     GeoSceneDocument        *m_mapTheme;
0130 
0131     FileStoragePolicy        m_storagePolicy;
0132     HttpDownloadManager      m_downloadManager;
0133 
0134     // Cache related
0135     FileStorageWatcher       m_storageWatcher;
0136 
0137     // Places on the map
0138     GeoDataTreeModel         m_treeModel;
0139     KDescendantsProxyModel   m_descendantProxy;
0140     QSortFilterProxyModel    m_placemarkProxyModel;
0141     QSortFilterProxyModel    m_groundOverlayProxyModel;
0142 
0143     // Selection handling
0144     QItemSelectionModel      m_placemarkSelectionModel;
0145 
0146     FileManager              m_fileManager;
0147 
0148     const GeoDataPlacemark  *m_trackedPlacemark;
0149 
0150     QTextDocument           *m_legend;
0151 
0152     bool                     m_workOffline;
0153 
0154     ElevationModel           m_elevationModel;
0155 };
0156 
0157 MarbleModel::MarbleModel( QObject *parent )
0158     : QObject( parent ),
0159       d( new MarbleModelPrivate() )
0160 {
0161     // connect the StoragePolicy used by the download manager to the FileStorageWatcher
0162     connect( &d->m_storagePolicy, SIGNAL(cleared()),
0163              &d->m_storageWatcher, SLOT(resetCurrentSize()) );
0164     connect( &d->m_storagePolicy, SIGNAL(sizeChanged(qint64)),
0165              &d->m_storageWatcher, SLOT(addToCurrentSize(qint64)) );
0166 
0167     connect( &d->m_fileManager, SIGNAL(fileAdded(QString)),
0168              this, SLOT(assignFillColors(QString)) );
0169 
0170     connect(&d->m_clock,   SIGNAL(timeChanged()),
0171             &d->m_sunLocator, SLOT(update()) );
0172 }
0173 
0174 MarbleModel::~MarbleModel()
0175 {
0176     delete d;
0177 
0178     qCDebug(DIGIKAM_MARBLE_LOG) << "Model deleted:" << this;
0179 }
0180 
0181 QString MarbleModel::mapThemeId() const
0182 {
0183     QString mapThemeId;
0184 
0185     if (d->m_mapTheme)
0186         mapThemeId = d->m_mapTheme->head()->mapThemeId();
0187 
0188     return mapThemeId;
0189 }
0190 
0191 GeoSceneDocument *MarbleModel::mapTheme()
0192 {
0193     return d->m_mapTheme;
0194 }
0195 
0196 const GeoSceneDocument *MarbleModel::mapTheme() const
0197 {
0198     return d->m_mapTheme;
0199 }
0200 
0201 // Set a particular theme for the map and load the appropriate tile level.
0202 // If the tiles (for the lowest tile level) haven't been created already
0203 // then create them here and now.
0204 //
0205 // FIXME: Move the tile creation dialogs out of this function.  Change
0206 //        them into signals instead.
0207 // FIXME: Get rid of 'currentProjection' here.  It's totally misplaced.
0208 //
0209 
0210 void MarbleModel::setMapThemeId( const QString &mapThemeId )
0211 {
0212     if ( !mapThemeId.isEmpty() && mapThemeId == this->mapThemeId() )
0213         return;
0214 
0215     GeoSceneDocument *mapTheme = MapThemeManager::loadMapTheme( mapThemeId );
0216     setMapTheme( mapTheme );
0217 }
0218 
0219 void MarbleModel::setMapTheme( GeoSceneDocument *document )
0220 {
0221     GeoSceneDocument *mapTheme = document;
0222     if ( !mapTheme ) {
0223         // Check whether the previous theme works
0224         if ( d->m_mapTheme ){
0225             qCWarning(DIGIKAM_MARBLE_LOG) << "Selected theme doesn't work, so we stick to the previous one";
0226             return;
0227         }
0228 
0229         // Fall back to default theme
0230         QString defaultTheme = QString::fromUtf8("earth/srtm/srtm.dgml");
0231         qCWarning(DIGIKAM_MARBLE_LOG) << "Falling back to default theme:" << defaultTheme;
0232         mapTheme = MapThemeManager::loadMapTheme( defaultTheme );
0233     }
0234 
0235     // If this last resort doesn't work either shed a tear and exit
0236     if ( !mapTheme ) {
0237         qCWarning(DIGIKAM_MARBLE_LOG) << "Couldn't find a valid DGML map.";
0238         return;
0239     }
0240 
0241     // find the list of previous theme's geodata
0242     QList<GeoSceneGeodata> currentDatasets;
0243     if ( d->m_mapTheme ) {
0244         for ( GeoSceneLayer *layer: d->m_mapTheme->map()->layers() ) {
0245             if ( layer->backend() != QString::fromUtf8(dgml::dgmlValue_geodata)
0246                  && layer->backend() != QString::fromUtf8(dgml::dgmlValue_vector) )
0247                 continue;
0248 
0249             // look for documents
0250             for ( GeoSceneAbstractDataset *dataset: layer->datasets() ) {
0251                 GeoSceneGeodata *data = dynamic_cast<GeoSceneGeodata*>( dataset );
0252                 Q_ASSERT( data );
0253                 currentDatasets << *data;
0254             }
0255         }
0256     }
0257 
0258     delete d->m_mapTheme;
0259     d->m_mapTheme = mapTheme;
0260 
0261     addDownloadPolicies( d->m_mapTheme );
0262 
0263     // Some output to show how to use this stuff ...
0264     qCDebug(DIGIKAM_MARBLE_LOG) << "DGML2 Name       : " << d->m_mapTheme->head()->name();
0265 /*
0266     qCDebug(DIGIKAM_MARBLE_LOG) << "DGML2 Description: " << d->m_mapTheme->head()->description();
0267 
0268     if ( d->m_mapTheme->map()->hasTextureLayers() )
0269         qCDebug(DIGIKAM_MARBLE_LOG) << "Contains texture layers! ";
0270     else
0271         qCDebug(DIGIKAM_MARBLE_LOG) << "Does not contain any texture layers! ";
0272 
0273     qCDebug(DIGIKAM_MARBLE_LOG) << "Number of SRTM textures: " << d->m_mapTheme->map()->layer("srtm")->datasets().count();
0274 
0275     if ( d->m_mapTheme->map()->hasVectorLayers() )
0276         qCDebug(DIGIKAM_MARBLE_LOG) << "Contains vector layers! ";
0277     else
0278         qCDebug(DIGIKAM_MARBLE_LOG) << "Does not contain any vector layers! ";
0279 */
0280     //Don't change the planet unless we have to...
0281     qreal const radiusAttributeValue = d->m_mapTheme->head()->radius();
0282     if( d->m_mapTheme->head()->target().toLower() != d->m_planet.id() || radiusAttributeValue != d->m_planet.radius() ) {
0283         qCDebug(DIGIKAM_MARBLE_LOG) << "Changing Planet";
0284         d->m_planet = PlanetFactory::construct(d->m_mapTheme->head()->target().toLower());
0285         if ( radiusAttributeValue > 0.0 ) {
0286             d->m_planet.setRadius( radiusAttributeValue );
0287         }
0288         sunLocator()->setPlanet( &d->m_planet );
0289     }
0290 
0291     QStringList fileList;
0292     QStringList propertyList;
0293     QList<GeoDataStyle::Ptr> styleList;
0294     QList<int> renderOrderList;
0295 
0296     for ( GeoSceneLayer *layer: d->m_mapTheme->map()->layers() ) {
0297         if ( layer->backend() != QString::fromUtf8(dgml::dgmlValue_geodata)
0298              && layer->backend() != QString::fromUtf8(dgml::dgmlValue_vector) )
0299             continue;
0300 
0301         // look for datasets which are different from currentDatasets
0302         for ( const GeoSceneAbstractDataset *dataset: layer->datasets() ) {
0303             const GeoSceneGeodata *data = dynamic_cast<const GeoSceneGeodata*>( dataset );
0304             Q_ASSERT( data );
0305             bool skip = false;
0306             GeoDataDocument *doc = nullptr;
0307             for ( int i = 0; i < currentDatasets.size(); ++i ) {
0308                 if ( currentDatasets[i] == *data ) {
0309                     currentDatasets.removeAt( i );
0310                     skip = true;
0311                     break;
0312                 }
0313                 /*
0314                  * If the sourcefile of data matches any in the currentDatasets then there
0315                  * is no need to parse the file again just update the style
0316                  * i.e. <brush> and <pen> values of already parsed file. assignNewStyle() does that
0317                  */
0318                 if ( currentDatasets[i].sourceFile() == data->sourceFile() ) {
0319                     doc = d->m_fileManager.at(data->sourceFile());
0320                     currentDatasets.removeAt(i);
0321                 }
0322             }
0323             if ( skip ) {
0324                 continue;
0325             }
0326 
0327             if (doc) {
0328                 d->assignFillColors(doc, *data);
0329             }
0330             else {
0331                 const QString filename = data->sourceFile();
0332                 const QString property = data->property();
0333                 const QPen pen = data->pen();
0334                 const QBrush brush = data->brush();
0335                 GeoDataStyle::Ptr style;
0336                 const int renderOrder = data->renderOrder();
0337 
0338                 /*
0339                  * data->colors() are the colorMap values from dgml file. If this is not
0340                  * empty then we are supposed to assign every placemark a different style
0341                  * by giving it a color from colorMap values based on color index
0342                  * of that placemark. See assignFillColors() for details. So, we need to
0343                  * send an empty style to fileManeger otherwise the FileLoader::createFilterProperties()
0344                  * will overwrite the parsed value of color index ( GeoDataPolyStyle::d->m_colorIndex ).
0345                  */
0346                 if ( data->colors().isEmpty() ) {
0347                     GeoDataLineStyle lineStyle( pen.color() );
0348                     lineStyle.setPenStyle( pen.style() );
0349                     lineStyle.setWidth( pen.width() );
0350                     GeoDataPolyStyle polyStyle( brush.color() );
0351                     polyStyle.setFill( true );
0352                     style = GeoDataStyle::Ptr(new GeoDataStyle);
0353                     style->setLineStyle( lineStyle );
0354                     style->setPolyStyle( polyStyle );
0355                     style->setId(QStringLiteral("default"));
0356                 }
0357 
0358                 fileList << filename;
0359                 propertyList << property;
0360                 styleList << style;
0361                 renderOrderList << renderOrder;
0362             }
0363         }
0364     }
0365     // unload old currentDatasets which are not part of the new map
0366     for(const GeoSceneGeodata &data: currentDatasets) {
0367         d->m_fileManager.removeFile( data.sourceFile() );
0368     }
0369     // load new datasets
0370     for ( int i = 0 ; i < fileList.size(); ++i ) {
0371         d->m_fileManager.addFile( fileList.at(i), propertyList.at(i), styleList.at(i), MapDocument, renderOrderList.at(i) );
0372     }
0373 
0374     qCDebug(DIGIKAM_MARBLE_LOG) << "THEME CHANGED: ***" << mapTheme->head()->mapThemeId();
0375     Q_EMIT themeChanged( mapTheme->head()->mapThemeId() );
0376 }
0377 
0378 void MarbleModelPrivate::addHighlightStyle(GeoDataDocument *doc) const
0379 {
0380     if ( doc ) {
0381         /*
0382          * Add a highlight style to GeoDataDocument if
0383          *the theme file specifies any highlight color.
0384          */
0385         QColor highlightBrushColor = m_mapTheme->map()->highlightBrushColor();
0386         QColor highlightPenColor = m_mapTheme->map()->highlightPenColor();
0387 
0388         GeoDataStyle::Ptr highlightStyle(new GeoDataStyle);
0389         highlightStyle->setId(QStringLiteral("highlight"));
0390 
0391         if ( highlightBrushColor.isValid() ) {
0392             GeoDataPolyStyle highlightPolyStyle;
0393             highlightPolyStyle.setColor( highlightBrushColor );
0394             highlightPolyStyle.setFill( true );
0395             highlightStyle->setPolyStyle( highlightPolyStyle );
0396         }
0397         if ( highlightPenColor.isValid() ) {
0398             GeoDataLineStyle highlightLineStyle( highlightPenColor );
0399             highlightStyle->setLineStyle( highlightLineStyle );
0400         }
0401         if ( highlightBrushColor.isValid()
0402             || highlightPenColor.isValid() )
0403         {
0404             GeoDataStyleMap styleMap = doc->styleMap(QStringLiteral("default-map"));
0405             styleMap.insert(QStringLiteral("highlight"), QLatin1Char('#') + highlightStyle->id());
0406             doc->addStyle( highlightStyle );
0407             doc->addStyleMap( styleMap );
0408         }
0409     }
0410 }
0411 
0412 void MarbleModel::home( qreal &lon, qreal &lat, int& zoom ) const
0413 {
0414     d->m_homePoint.geoCoordinates( lon, lat, GeoDataCoordinates::Degree );
0415     zoom = d->m_homeZoom;
0416 }
0417 
0418 void MarbleModel::setHome( qreal lon, qreal lat, int zoom )
0419 {
0420     d->m_homePoint = GeoDataCoordinates( lon, lat, 0, GeoDataCoordinates::Degree );
0421     d->m_homeZoom = zoom;
0422     Q_EMIT homeChanged( d->m_homePoint );
0423 }
0424 
0425 void MarbleModel::setHome( const GeoDataCoordinates& homePoint, int zoom )
0426 {
0427     d->m_homePoint = homePoint;
0428     d->m_homeZoom = zoom;
0429     Q_EMIT homeChanged( d->m_homePoint );
0430 }
0431 
0432 HttpDownloadManager *MarbleModel::downloadManager()
0433 {
0434     return &d->m_downloadManager;
0435 }
0436 
0437 const HttpDownloadManager *MarbleModel::downloadManager() const
0438 {
0439     return &d->m_downloadManager;
0440 }
0441 
0442 
0443 GeoDataTreeModel *MarbleModel::treeModel()
0444 {
0445     return &d->m_treeModel;
0446 }
0447 
0448 const GeoDataTreeModel *MarbleModel::treeModel() const
0449 {
0450     return &d->m_treeModel;
0451 }
0452 
0453 QAbstractItemModel *MarbleModel::placemarkModel()
0454 {
0455     return &d->m_placemarkProxyModel;
0456 }
0457 
0458 const QAbstractItemModel *MarbleModel::placemarkModel() const
0459 {
0460     return &d->m_placemarkProxyModel;
0461 }
0462 
0463 QAbstractItemModel *MarbleModel::groundOverlayModel()
0464 {
0465     return &d->m_groundOverlayProxyModel;
0466 }
0467 
0468 const QAbstractItemModel *MarbleModel::groundOverlayModel() const
0469 {
0470     return &d->m_groundOverlayProxyModel;
0471 }
0472 
0473 QItemSelectionModel *MarbleModel::placemarkSelectionModel()
0474 {
0475     return &d->m_placemarkSelectionModel;
0476 }
0477 
0478 FileManager *MarbleModel::fileManager()
0479 {
0480     return &d->m_fileManager;
0481 }
0482 
0483 qreal MarbleModel::planetRadius()   const
0484 {
0485     return d->m_planet.radius();
0486 }
0487 
0488 QString MarbleModel::planetName()   const
0489 {
0490     return d->m_planet.name();
0491 }
0492 
0493 QString MarbleModel::planetId() const
0494 {
0495     return d->m_planet.id();
0496 }
0497 
0498 MarbleClock *MarbleModel::clock()
0499 {
0500     return &d->m_clock;
0501 }
0502 
0503 const MarbleClock *MarbleModel::clock() const
0504 {
0505     return &d->m_clock;
0506 }
0507 
0508 SunLocator *MarbleModel::sunLocator()
0509 {
0510     return &d->m_sunLocator;
0511 }
0512 
0513 const SunLocator *MarbleModel::sunLocator() const
0514 {
0515     return &d->m_sunLocator;
0516 }
0517 
0518 quint64 MarbleModel::persistentTileCacheLimit() const
0519 {
0520     return d->m_storageWatcher.cacheLimit() / 1024;
0521 }
0522 
0523 void MarbleModel::clearPersistentTileCache()
0524 {
0525     d->m_storagePolicy.clearCache();
0526 
0527     // Now create base tiles again if needed
0528     if ( d->m_mapTheme->map()->hasTextureLayers() || d->m_mapTheme->map()->hasVectorLayers() ) {
0529         // If the tiles aren't already there, put up a progress dialog
0530         // while creating them.
0531 
0532         // As long as we don't have an Layer Management Class we just lookup
0533         // the name of the layer that has the same name as the theme ID
0534         QString themeID = d->m_mapTheme->head()->theme();
0535 
0536         const GeoSceneLayer *layer =
0537             static_cast<const GeoSceneLayer*>( d->m_mapTheme->map()->layer( themeID ) );
0538         const GeoSceneTileDataset *texture =
0539             static_cast<const GeoSceneTileDataset*>( layer->groundDataset() );
0540 
0541         QString sourceDir = texture->sourceDir();
0542         QString installMap = texture->installMap();
0543         QString role = d->m_mapTheme->map()->layer( themeID )->role();
0544 
0545         if ( !TileLoader::baseTilesAvailable( *texture )
0546             && !installMap.isEmpty() )
0547         {
0548             qCDebug(DIGIKAM_MARBLE_LOG) << "Base tiles not available. Creating Tiles ... \n"
0549                      << "SourceDir: " << sourceDir << "InstallMap:" << installMap;
0550             MarbleDirs::debug();
0551 
0552             TileCreator *tileCreator = new TileCreator(
0553                                      sourceDir,
0554                                      installMap,
0555                                      (role == QLatin1String("dem")) ? QString::fromUtf8("true") : QString::fromUtf8("false") );
0556             tileCreator->setTileFormat( texture->fileFormat().toLower() );
0557 
0558             QPointer<TileCreatorDialog> tileCreatorDlg = new TileCreatorDialog( tileCreator, nullptr );
0559             tileCreatorDlg->setSummary( d->m_mapTheme->head()->name(),
0560                                         d->m_mapTheme->head()->description() );
0561             tileCreatorDlg->exec();
0562             qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8("Tile creation completed");
0563             delete tileCreatorDlg;
0564         }
0565     }
0566 }
0567 
0568 void MarbleModel::setPersistentTileCacheLimit(quint64 kiloBytes)
0569 {
0570     d->m_storageWatcher.setCacheLimit( kiloBytes * 1024 );
0571 
0572     if( kiloBytes != 0 )
0573     {
0574         if( !d->m_storageWatcher.isRunning() )
0575             d->m_storageWatcher.start( QThread::IdlePriority );
0576     }
0577     else
0578     {
0579         d->m_storageWatcher.quit();
0580     }
0581     // TODO: trigger update
0582 }
0583 
0584 void MarbleModel::setTrackedPlacemark( const GeoDataPlacemark *placemark )
0585 {
0586     d->m_trackedPlacemark = placemark;
0587     Q_EMIT trackedPlacemarkChanged( placemark );
0588 }
0589 
0590 const GeoDataPlacemark* MarbleModel::trackedPlacemark() const
0591 {
0592     return d->m_trackedPlacemark;
0593 }
0594 
0595 const PluginManager* MarbleModel::pluginManager() const
0596 {
0597     return &d->m_pluginManager;
0598 }
0599 
0600 PluginManager* MarbleModel::pluginManager()
0601 {
0602     return &d->m_pluginManager;
0603 }
0604 
0605 const Planet *MarbleModel::planet() const
0606 {
0607     return &d->m_planet;
0608 }
0609 
0610 void MarbleModel::addDownloadPolicies( const GeoSceneDocument *mapTheme )
0611 {
0612     if ( !mapTheme )
0613         return;
0614     if ( !mapTheme->map()->hasTextureLayers() && !mapTheme->map()->hasVectorLayers() )
0615         return;
0616 
0617     // As long as we don't have an Layer Management Class we just lookup
0618     // the name of the layer that has the same name as the theme ID
0619     const QString themeId = mapTheme->head()->theme();
0620     const GeoSceneLayer * const layer = static_cast<const GeoSceneLayer*>( mapTheme->map()->layer( themeId ));
0621     if ( !layer )
0622         return;
0623 
0624     const GeoSceneTileDataset * const texture = static_cast<const GeoSceneTileDataset*>( layer->groundDataset() );
0625     if ( !texture )
0626         return;
0627 
0628     QList<const DownloadPolicy *> policies = texture->downloadPolicies();
0629     QList<const DownloadPolicy *>::const_iterator pos = policies.constBegin();
0630     QList<const DownloadPolicy *>::const_iterator const end = policies.constEnd();
0631     for (; pos != end; ++pos ) {
0632         d->m_downloadManager.addDownloadPolicy( **pos );
0633     }
0634 }
0635 
0636 void MarbleModel::setClockDateTime( const QDateTime& datetime )
0637 {
0638     d->m_clock.setDateTime( datetime );
0639 }
0640 
0641 QDateTime MarbleModel::clockDateTime() const
0642 {
0643     return d->m_clock.dateTime();
0644 }
0645 
0646 int MarbleModel::clockSpeed() const
0647 {
0648     return d->m_clock.speed();
0649 }
0650 
0651 void MarbleModel::setClockSpeed( int speed )
0652 {
0653     d->m_clock.setSpeed( speed );
0654 }
0655 
0656 void MarbleModel::setClockTimezone( int timeInSec )
0657 {
0658     d->m_clock.setTimezone( timeInSec );
0659 }
0660 
0661 int MarbleModel::clockTimezone() const
0662 {
0663     return d->m_clock.timezone();
0664 }
0665 
0666 QTextDocument * MarbleModel::legend()
0667 {
0668     return d->m_legend;
0669 }
0670 
0671 void MarbleModel::setLegend( QTextDocument * legend )
0672 {
0673     delete d->m_legend;
0674     d->m_legend = legend;
0675 }
0676 
0677 void MarbleModel::addGeoDataFile( const QString& filename )
0678 {
0679     d->m_fileManager.addFile( filename, filename, GeoDataStyle::Ptr(), UserDocument, 0, true );
0680 }
0681 
0682 void MarbleModel::addGeoDataString( const QString& data, const QString& key )
0683 {
0684     d->m_fileManager.addData( key, data, UserDocument );
0685 }
0686 
0687 void MarbleModel::removeGeoData( const QString& fileName )
0688 {
0689     d->m_fileManager.removeFile( fileName );
0690 }
0691 
0692 void MarbleModel::updateProperty( const QString &property, bool value )
0693 {
0694     for( GeoDataFeature *feature: d->m_treeModel.rootDocument()->featureList()) {
0695         if (auto document = geodata_cast<GeoDataDocument>(feature)) {
0696             if( document->property() == property ){
0697                 document->setVisible( value );
0698                 d->m_treeModel.updateFeature( document );
0699             }
0700         }
0701     }
0702 }
0703 
0704 void MarbleModelPrivate::assignFillColors(const QString &filePath)
0705 {
0706     const GeoSceneGeodata *data = nullptr;
0707 
0708     for (auto layer : m_mapTheme->map()->layers()) {
0709         if (layer->backend() != QString::fromUtf8(dgml::dgmlValue_geodata)
0710             && layer->backend() != QString::fromUtf8(dgml::dgmlValue_vector)) {
0711             continue;
0712         }
0713 
0714         for (auto dataset: layer->datasets()) {
0715             auto sceneData = dynamic_cast<const GeoSceneGeodata *>(dataset);
0716             if (sceneData != nullptr && sceneData->sourceFile() == filePath) {
0717                 data = sceneData;
0718                 break;
0719             }
0720         }
0721 
0722         if (data) {
0723             break;
0724         }
0725     }
0726 
0727     if (data == nullptr) {
0728         return;
0729     }
0730 
0731     GeoDataDocument *doc = m_fileManager.at(filePath);
0732     Q_ASSERT( doc );
0733 
0734     assignFillColors(doc, *data);
0735 }
0736 
0737 void MarbleModelPrivate::assignFillColors(GeoDataDocument *doc, const GeoSceneGeodata &data) const
0738 {
0739     addHighlightStyle( doc );
0740 
0741     const QPen pen = data.pen();
0742     const QBrush brush = data.brush();
0743     const QVector<QColor> colors = data.colors();
0744     GeoDataLineStyle lineStyle( pen.color() );
0745     lineStyle.setPenStyle( pen.style() );
0746     lineStyle.setWidth( pen.width() );
0747 
0748     if (!colors.isEmpty()) {
0749         const qreal alpha = data.alpha();
0750         for (auto feature : *doc) {
0751             auto placemark = geodata_cast<GeoDataPlacemark>(feature);
0752             if (placemark == nullptr) {
0753                 continue;
0754             }
0755 
0756             GeoDataStyle::Ptr style(new GeoDataStyle);
0757             style->setId(QStringLiteral("normal"));
0758             style->setLineStyle( lineStyle );
0759             quint8 colorIndex = placemark->style()->polyStyle().colorIndex();
0760             GeoDataPolyStyle polyStyle;
0761             // Set the colorIndex so that it's not lost after setting new style.
0762             polyStyle.setColorIndex( colorIndex );
0763             QColor color;
0764             // color index having value 99 is undefined
0765             Q_ASSERT( colors.size() );
0766             if (colorIndex > colors.size() || (colorIndex - 1) < 0) {
0767                 color = colors[0];      // Assign the first color as default
0768             }
0769             else {
0770                 color = colors[colorIndex-1];
0771             }
0772             color.setAlphaF( alpha );
0773             polyStyle.setColor( color );
0774             polyStyle.setFill( true );
0775             style->setPolyStyle( polyStyle );
0776             placemark->setStyle( style );
0777         }
0778     }
0779     else {
0780         GeoDataStyle::Ptr style(new GeoDataStyle);
0781         GeoDataPolyStyle polyStyle( brush.color() );
0782         polyStyle.setFill( true );
0783         style->setLineStyle( lineStyle );
0784         style->setPolyStyle( polyStyle );
0785         style->setId(QStringLiteral("default"));
0786         GeoDataStyleMap styleMap;
0787         styleMap.setId(QStringLiteral("default-map"));
0788         styleMap.insert(QStringLiteral("normal"), QLatin1Char('#') + style->id());
0789         doc->addStyle( style );
0790         doc->addStyleMap( styleMap );
0791 
0792         const QString styleUrl = QLatin1Char('#') + styleMap.id();
0793 
0794         for (auto feature : *doc) {
0795             auto placemark = geodata_cast<GeoDataPlacemark>(feature);
0796             if (placemark == nullptr) {
0797                 continue;
0798             }
0799 
0800             if (geodata_cast<GeoDataTrack>(placemark->geometry()) ||
0801                 geodata_cast<GeoDataPoint>(placemark->geometry())) {
0802                 continue;
0803             }
0804 
0805             placemark->setStyleUrl(styleUrl);
0806         }
0807     }
0808 }
0809 
0810 bool MarbleModel::workOffline() const
0811 {
0812     return d->m_workOffline;
0813 }
0814 
0815 void MarbleModel::setWorkOffline( bool workOffline )
0816 {
0817     if ( d->m_workOffline != workOffline ) {
0818         downloadManager()->setDownloadEnabled( !workOffline );
0819 
0820         d->m_workOffline = workOffline;
0821         Q_EMIT workOfflineChanged();
0822     }
0823 }
0824 
0825 ElevationModel* MarbleModel::elevationModel()
0826 {
0827     return &d->m_elevationModel;
0828 }
0829 
0830 const ElevationModel* MarbleModel::elevationModel() const
0831 {
0832     return &d->m_elevationModel;
0833 }
0834 
0835 }
0836 
0837 #include "moc_MarbleModel.cpp"