File indexing completed on 2025-01-05 03:59:19
0001 /* 0002 SPDX-License-Identifier: LGPL-2.1-or-later 0003 0004 SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de> 0005 SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr> 0006 SPDX-FileCopyrightText: 2012 Ander Pijoan <ander.pijoan@deusto.es> 0007 SPDX-FileCopyrightText: 2013 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0008 */ 0009 0010 #include "VectorTileLayer.h" 0011 0012 #include <qmath.h> 0013 #include <QThreadPool> 0014 0015 #include "VectorTileModel.h" 0016 #include "GeoPainter.h" 0017 #include "GeoSceneGroup.h" 0018 #include "GeoSceneTypes.h" 0019 #include "GeoSceneVectorTileDataset.h" 0020 #include "GeoSceneAbstractTileProjection.h" 0021 #include "TileLoader.h" 0022 #include "ViewportParams.h" 0023 #include "RenderState.h" 0024 #include "GeoDataDocument.h" 0025 #include "GeoDataLatLonAltBox.h" 0026 #include "HttpDownloadManager.h" 0027 #include "TileLoaderHelper.h" 0028 0029 #include "digikam_debug.h" 0030 0031 namespace Marble 0032 { 0033 0034 class Q_DECL_HIDDEN VectorTileLayer::Private 0035 { 0036 public: 0037 Private(HttpDownloadManager *downloadManager, 0038 const PluginManager *pluginManager, 0039 VectorTileLayer *parent, 0040 GeoDataTreeModel *treeModel); 0041 0042 ~Private(); 0043 0044 void updateTile(const TileId &tileId, GeoDataDocument* document); 0045 void updateLayerSettings(); 0046 0047 QVector<const GeoSceneVectorTileDataset *> findRelevantVectorLayers( const TileId &stackedTileId ) const; 0048 0049 public: 0050 VectorTileLayer *const m_parent; 0051 TileLoader m_loader; 0052 QVector<VectorTileModel *> m_tileModels; 0053 QVector<VectorTileModel *> m_activeTileModels; 0054 const GeoSceneGroup *m_layerSettings; 0055 0056 // TreeModel for displaying GeoDataDocuments 0057 GeoDataTreeModel *const m_treeModel; 0058 0059 QThreadPool m_threadPool; // a shared thread pool for all layers to keep CPU usage sane 0060 }; 0061 0062 VectorTileLayer::Private::Private(HttpDownloadManager *downloadManager, 0063 const PluginManager *pluginManager, 0064 VectorTileLayer *parent, 0065 GeoDataTreeModel *treeModel) : 0066 m_parent(parent), 0067 m_loader(downloadManager, pluginManager), 0068 m_tileModels(), 0069 m_activeTileModels(), 0070 m_layerSettings(nullptr), 0071 m_treeModel(treeModel) 0072 { 0073 m_threadPool.setMaxThreadCount(1); 0074 } 0075 0076 VectorTileLayer::Private::~Private() 0077 { 0078 qDeleteAll(m_activeTileModels); 0079 } 0080 0081 void VectorTileLayer::Private::updateTile(const TileId &tileId, GeoDataDocument* document) 0082 { 0083 for (VectorTileModel *mapper: m_activeTileModels) { 0084 mapper->updateTile(tileId, document); 0085 } 0086 } 0087 0088 void VectorTileLayer::Private::updateLayerSettings() 0089 { 0090 m_activeTileModels.clear(); 0091 0092 for (VectorTileModel *candidate: m_tileModels) { 0093 bool enabled = true; 0094 if (m_layerSettings) { 0095 const bool propertyExists = m_layerSettings->propertyValue(candidate->name(), enabled); 0096 enabled |= !propertyExists; // if property doesn't exist, enable layer nevertheless 0097 } 0098 if (enabled) { 0099 m_activeTileModels.append(candidate); 0100 qCDebug(DIGIKAM_MARBLE_LOG) << "enabling vector layer" << candidate->name(); 0101 } else { 0102 candidate->clear(); 0103 qCDebug(DIGIKAM_MARBLE_LOG) << "disabling vector layer" << candidate->name(); 0104 } 0105 } 0106 } 0107 0108 VectorTileLayer::VectorTileLayer(HttpDownloadManager *downloadManager, 0109 const PluginManager *pluginManager, 0110 GeoDataTreeModel *treeModel) 0111 : TileLayer() 0112 , d(new Private(downloadManager, pluginManager, this, treeModel)) 0113 { 0114 qRegisterMetaType<TileId>("TileId"); 0115 qRegisterMetaType<GeoDataDocument*>("GeoDataDocument*"); 0116 0117 connect(&d->m_loader, SIGNAL(tileCompleted(TileId,GeoDataDocument*)), this, SLOT(updateTile(TileId,GeoDataDocument*))); 0118 } 0119 0120 VectorTileLayer::~VectorTileLayer() 0121 { 0122 delete d; 0123 } 0124 0125 RenderState VectorTileLayer::renderState() const 0126 { 0127 return RenderState(QStringLiteral("Vector Tiles")); 0128 } 0129 0130 int VectorTileLayer::tileZoomLevel() const 0131 { 0132 int level = -1; 0133 for (const auto *mapper: d->m_activeTileModels) { 0134 level = qMax(level, mapper->tileZoomLevel()); 0135 } 0136 return level; 0137 } 0138 0139 QString VectorTileLayer::runtimeTrace() const 0140 { 0141 int tiles = 0; 0142 for (const auto *mapper: d->m_activeTileModels) { 0143 tiles += mapper->cachedDocuments(); 0144 } 0145 int const layers = d->m_activeTileModels.size(); 0146 return QStringLiteral("Vector Tiles: %1 tiles in %2 layers").arg(tiles).arg(layers); 0147 } 0148 0149 bool VectorTileLayer::render(GeoPainter *painter, ViewportParams *viewport, 0150 const QString &renderPos, GeoSceneLayer *layer) 0151 { 0152 Q_UNUSED(painter); 0153 Q_UNUSED(renderPos); 0154 Q_UNUSED(layer); 0155 0156 int const oldLevel = tileZoomLevel(); 0157 int level = 0; 0158 for (VectorTileModel *mapper: d->m_activeTileModels) { 0159 mapper->setViewport(viewport->viewLatLonAltBox()); 0160 level = qMax(level, mapper->tileZoomLevel()); 0161 } 0162 if (oldLevel != level && level >= 0) { 0163 Q_EMIT tileLevelChanged(level); 0164 } 0165 0166 return true; 0167 } 0168 0169 void VectorTileLayer::reload() 0170 { 0171 for (auto mapper : d->m_activeTileModels) { 0172 mapper->reload(); 0173 } 0174 } 0175 0176 QSize VectorTileLayer::tileSize() const 0177 { 0178 return QSize(256, 256); 0179 } 0180 0181 const GeoSceneAbstractTileProjection *VectorTileLayer::tileProjection() const 0182 { 0183 if (!d->m_activeTileModels.isEmpty()) 0184 return d->m_activeTileModels.first()->layer()->tileProjection(); 0185 return 0; 0186 } 0187 0188 int VectorTileLayer::tileColumnCount(int level) const 0189 { 0190 // So far we only support Vector tiles with a single level zero tile 0191 return TileLoaderHelper::levelToColumn( 1, level ); 0192 } 0193 0194 int VectorTileLayer::tileRowCount(int level) const 0195 { 0196 // So far we only support Vector tiles with a single level zero tile 0197 return TileLoaderHelper::levelToRow( 1, level ); 0198 } 0199 0200 int VectorTileLayer::layerCount() const 0201 { 0202 // So far we only support one sublayer of vector tiles 0203 return 1; 0204 } 0205 0206 void VectorTileLayer::downloadTile(const TileId &id) 0207 { 0208 const QVector<const GeoSceneVectorTileDataset *> vectorLayers = d->findRelevantVectorLayers( id ); 0209 0210 for ( const GeoSceneVectorTileDataset *vectorLayer: vectorLayers ) { 0211 if (vectorLayer->tileLevels().isEmpty() || vectorLayer->tileLevels().contains(id.zoomLevel())) { 0212 if ( TileLoader::tileStatus( vectorLayer, id ) != TileLoader::Available ) { 0213 d->m_loader.downloadTile( vectorLayer, id, DownloadBulk ); 0214 } 0215 } 0216 } 0217 } 0218 0219 void VectorTileLayer::reset() 0220 { 0221 for (VectorTileModel *mapper: d->m_tileModels) { 0222 mapper->clear(); 0223 } 0224 } 0225 0226 void VectorTileLayer::setMapTheme(const QVector<const GeoSceneVectorTileDataset *> &textures, const GeoSceneGroup *textureLayerSettings) 0227 { 0228 qDeleteAll(d->m_tileModels); 0229 d->m_tileModels.clear(); 0230 d->m_activeTileModels.clear(); 0231 0232 for (const GeoSceneVectorTileDataset *layer: textures) { 0233 d->m_tileModels << new VectorTileModel(&d->m_loader, layer, d->m_treeModel, &d->m_threadPool); 0234 } 0235 0236 d->m_layerSettings = textureLayerSettings; 0237 0238 if (d->m_layerSettings) { 0239 connect(d->m_layerSettings, SIGNAL(valueChanged(QString,bool)), 0240 this, SLOT(updateLayerSettings())); 0241 } 0242 0243 d->updateLayerSettings(); 0244 auto const level = tileZoomLevel(); 0245 if (level >= 0) { 0246 Q_EMIT tileLevelChanged(level); 0247 } 0248 } 0249 0250 QVector<const GeoSceneVectorTileDataset *> VectorTileLayer::Private::findRelevantVectorLayers( const TileId &tileId ) const 0251 { 0252 QVector<const GeoSceneVectorTileDataset *> result; 0253 0254 for (VectorTileModel * candidate: m_activeTileModels) { 0255 Q_ASSERT( candidate ); 0256 const GeoSceneVectorTileDataset * vectorTileDataset = candidate->layer(); 0257 // check, if layer provides tiles for the current level 0258 if ( !vectorTileDataset->hasMaximumTileLevel() || 0259 vectorTileDataset->maximumTileLevel() >= tileId.zoomLevel() ) { 0260 //check if the tile intersects with texture bounds 0261 if (vectorTileDataset->latLonBox().isNull()) { 0262 result.append(vectorTileDataset); 0263 } 0264 else { 0265 const GeoDataLatLonBox bbox = vectorTileDataset->tileProjection()->geoCoordinates(tileId); 0266 0267 if (vectorTileDataset->latLonBox().intersects(bbox)) { 0268 result.append( vectorTileDataset ); 0269 } 0270 } 0271 } 0272 } 0273 0274 return result; 0275 } 0276 0277 0278 } 0279 0280 #include "moc_VectorTileLayer.cpp"