File indexing completed on 2025-01-05 03:59:11
0001 /* 0002 SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org> 0003 SPDX-FileCopyrightText: 2008 Jens-Michael Hoffmann <jensmh@gmx.de> 0004 SPDX-FileCopyrightText: 2012 Ander Pijoan <ander.pijoan@deusto.es> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "GeoSceneTileDataset.h" 0010 0011 #include <QImage> 0012 #include <QUrl> 0013 0014 #include "GeoSceneTypes.h" 0015 #include "GeoSceneEquirectTileProjection.h" 0016 #include "GeoSceneMercatorTileProjection.h" 0017 #include "DownloadPolicy.h" 0018 #include "MarbleDirs.h" 0019 #include "ServerLayout.h" 0020 #include "TileId.h" 0021 0022 #include "digikam_debug.h" 0023 0024 namespace Marble 0025 { 0026 0027 GeoSceneTileDataset::GeoSceneTileDataset( const QString& name ) 0028 : GeoSceneAbstractDataset( name ), 0029 m_sourceDir(), 0030 m_installMap(), 0031 m_storageLayoutMode(Marble), 0032 m_serverLayout( new MarbleServerLayout( this ) ), 0033 m_levelZeroColumns( defaultLevelZeroColumns ), 0034 m_levelZeroRows( defaultLevelZeroRows ), 0035 m_minimumTileLevel(0), 0036 m_maximumTileLevel( -1 ), 0037 m_tileProjection(new GeoSceneEquirectTileProjection()), 0038 m_blending(), 0039 m_downloadUrls(), 0040 m_nextUrl( m_downloadUrls.constEnd() ) 0041 { 0042 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns); 0043 m_tileProjection->setLevelZeroRows(m_levelZeroRows); 0044 } 0045 0046 GeoSceneTileDataset::~GeoSceneTileDataset() 0047 { 0048 qDeleteAll( m_downloadPolicies ); 0049 delete m_serverLayout; 0050 delete m_tileProjection; 0051 } 0052 0053 const char* GeoSceneTileDataset::nodeType() const 0054 { 0055 return GeoSceneTypes::GeoSceneTileDatasetType; 0056 } 0057 0058 QString GeoSceneTileDataset::sourceDir() const 0059 { 0060 return m_sourceDir; 0061 } 0062 0063 void GeoSceneTileDataset::setSourceDir( const QString& sourceDir ) 0064 { 0065 m_sourceDir = sourceDir; 0066 } 0067 0068 QString GeoSceneTileDataset::installMap() const 0069 { 0070 return m_installMap; 0071 } 0072 0073 void GeoSceneTileDataset::setInstallMap( const QString& installMap ) 0074 { 0075 m_installMap = installMap; 0076 } 0077 0078 GeoSceneTileDataset::StorageLayout GeoSceneTileDataset::storageLayout() const 0079 { 0080 return m_storageLayoutMode; 0081 } 0082 0083 void GeoSceneTileDataset::setStorageLayout( const StorageLayout layout ) 0084 { 0085 m_storageLayoutMode = layout; 0086 } 0087 0088 void GeoSceneTileDataset::setServerLayout( const ServerLayout *layout ) 0089 { 0090 delete m_serverLayout; 0091 m_serverLayout = layout; 0092 } 0093 0094 const ServerLayout* GeoSceneTileDataset::serverLayout() const 0095 { 0096 return m_serverLayout; 0097 } 0098 0099 int GeoSceneTileDataset::levelZeroColumns() const 0100 { 0101 return m_levelZeroColumns; 0102 } 0103 0104 void GeoSceneTileDataset::setLevelZeroColumns( const int columns ) 0105 { 0106 m_levelZeroColumns = columns; 0107 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns); 0108 } 0109 0110 int GeoSceneTileDataset::levelZeroRows() const 0111 { 0112 return m_levelZeroRows; 0113 } 0114 0115 void GeoSceneTileDataset::setLevelZeroRows( const int rows ) 0116 { 0117 m_levelZeroRows = rows; 0118 m_tileProjection->setLevelZeroRows(m_levelZeroRows); 0119 } 0120 0121 int GeoSceneTileDataset::maximumTileLevel() const 0122 { 0123 return m_maximumTileLevel; 0124 } 0125 0126 void GeoSceneTileDataset::setMaximumTileLevel( const int maximumTileLevel ) 0127 { 0128 m_maximumTileLevel = maximumTileLevel; 0129 } 0130 0131 int GeoSceneTileDataset::minimumTileLevel() const 0132 { 0133 return m_minimumTileLevel; 0134 } 0135 0136 void GeoSceneTileDataset::setMinimumTileLevel(int level) 0137 { 0138 m_minimumTileLevel = level; 0139 } 0140 0141 void GeoSceneTileDataset::setTileLevels(const QString &tileLevels) 0142 { 0143 if (tileLevels.isEmpty()) { 0144 m_tileLevels.clear(); 0145 return; 0146 } 0147 0148 const QStringList values = tileLevels.split(QLatin1Char(',')); 0149 for(const QString &value: values) { 0150 bool canParse(false); 0151 int const tileLevel = value.trimmed().toInt(&canParse); 0152 if (canParse && tileLevel >= 0 && tileLevel < 100) { 0153 m_tileLevels << tileLevel; 0154 } else { 0155 qCDebug(DIGIKAM_MARBLE_LOG) << "Cannot parse tile level part " << value << " in " << tileLevels << ", ignoring it."; 0156 } 0157 } 0158 0159 if (!m_tileLevels.isEmpty()) { 0160 std::sort(m_tileLevels.begin(), m_tileLevels.end()); 0161 m_minimumTileLevel = m_tileLevels.first(); 0162 m_maximumTileLevel = m_tileLevels.last(); 0163 } 0164 } 0165 0166 QVector<int> GeoSceneTileDataset::tileLevels() const 0167 { 0168 return m_tileLevels; 0169 } 0170 0171 QVector<QUrl> GeoSceneTileDataset::downloadUrls() const 0172 { 0173 return m_downloadUrls; 0174 } 0175 0176 const QSize GeoSceneTileDataset::tileSize() const 0177 { 0178 if ( m_tileSize.isEmpty() ) { 0179 const TileId id( 0, 0, 0, 0 ); 0180 QString const fileName = relativeTileFileName( id ); 0181 QFileInfo const dirInfo( fileName ); 0182 QString const path = dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName ); 0183 0184 QImage testTile( path ); 0185 0186 if ( testTile.isNull() ) { 0187 qCDebug(DIGIKAM_MARBLE_LOG) << "Tile size is missing in dgml and no base tile found in " << themeStr(); 0188 qCDebug(DIGIKAM_MARBLE_LOG) << "Using default tile size " << c_defaultTileSize; 0189 m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize ); 0190 } else { 0191 m_tileSize = testTile.size(); 0192 } 0193 0194 if ( m_tileSize.isEmpty() ) { 0195 qCDebug(DIGIKAM_MARBLE_LOG) << "Tile width or height cannot be 0. Falling back to default tile size."; 0196 m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize ); 0197 } 0198 } 0199 0200 Q_ASSERT( !m_tileSize.isEmpty() ); 0201 return m_tileSize; 0202 } 0203 0204 GeoDataLatLonBox GeoSceneTileDataset::latLonBox() const 0205 { 0206 return m_latLonBox; 0207 } 0208 0209 void GeoSceneTileDataset::setLatLonBox( const GeoDataLatLonBox &box ) 0210 { 0211 m_latLonBox = box; 0212 } 0213 0214 void GeoSceneTileDataset::setTileSize( const QSize &tileSize ) 0215 { 0216 if ( tileSize.isEmpty() ) { 0217 qCDebug(DIGIKAM_MARBLE_LOG) << "Ignoring invalid tile size " << tileSize; 0218 } else { 0219 m_tileSize = tileSize; 0220 } 0221 } 0222 0223 void GeoSceneTileDataset::setTileProjection(GeoSceneAbstractTileProjection::Type projectionType) 0224 { 0225 if (m_tileProjection->type() == projectionType) { 0226 return; 0227 } 0228 0229 delete m_tileProjection; 0230 if (projectionType == GeoSceneAbstractTileProjection::Mercator) { 0231 m_tileProjection = new GeoSceneMercatorTileProjection(); 0232 } else { 0233 m_tileProjection = new GeoSceneEquirectTileProjection(); 0234 } 0235 0236 m_tileProjection->setLevelZeroColumns(m_levelZeroColumns); 0237 m_tileProjection->setLevelZeroRows(m_levelZeroRows); 0238 } 0239 0240 const GeoSceneAbstractTileProjection * GeoSceneTileDataset::tileProjection() const 0241 { 0242 return m_tileProjection; 0243 } 0244 0245 GeoSceneAbstractTileProjection::Type GeoSceneTileDataset::tileProjectionType() const 0246 { 0247 return m_tileProjection->type(); 0248 } 0249 0250 // Even though this method changes the internal state, it may be const 0251 // because the compiler is forced to invoke this method for different TileIds. 0252 QUrl GeoSceneTileDataset::downloadUrl( const TileId &id ) const 0253 { 0254 // default download url 0255 if ( m_downloadUrls.empty() ) { 0256 QUrl const defaultUrl = QUrl(QLatin1String("https://maps.kde.org/") + m_serverLayout->sourceDir()); 0257 qCDebug(DIGIKAM_MARBLE_LOG) << "No download URL specified for tiles stored in " 0258 << m_sourceDir << ", falling back to " << defaultUrl.toString(); 0259 return m_serverLayout->downloadUrl(defaultUrl, id); 0260 } else if (m_downloadUrls.size() == 1) { 0261 return m_serverLayout->downloadUrl(*m_nextUrl, id); 0262 } else { 0263 if (m_nextUrl == m_downloadUrls.constEnd()) { 0264 m_nextUrl = m_downloadUrls.constBegin(); 0265 } 0266 const QUrl url = m_serverLayout->downloadUrl(*m_nextUrl, id); 0267 ++m_nextUrl; 0268 return url; 0269 } 0270 } 0271 0272 void GeoSceneTileDataset::addDownloadUrl( const QUrl & url ) 0273 { 0274 m_downloadUrls.append( url ); 0275 // FIXME: this could be done only once 0276 m_nextUrl = m_downloadUrls.constBegin(); 0277 } 0278 0279 QString GeoSceneTileDataset::relativeTileFileName( const TileId &id ) const 0280 { 0281 const QString suffix = fileFormat().toLower(); 0282 0283 QString relFileName; 0284 0285 switch ( m_storageLayoutMode ) { 0286 case GeoSceneTileDataset::Marble: 0287 relFileName = QStringLiteral( "%1/%2/%3/%3_%4.%5" ) 0288 .arg( themeStr() ) 0289 .arg( id.zoomLevel() ) 0290 .arg(id.y(), tileDigits, 10, QLatin1Char('0')) 0291 .arg(id.x(), tileDigits, 10, QLatin1Char('0')) 0292 .arg( suffix ); 0293 break; 0294 case GeoSceneTileDataset::OpenStreetMap: 0295 relFileName = QStringLiteral( "%1/%2/%3/%4.%5" ) 0296 .arg( themeStr() ) 0297 .arg( id.zoomLevel() ) 0298 .arg( id.x() ) 0299 .arg( id.y() ) 0300 .arg( suffix ); 0301 break; 0302 case GeoSceneTileDataset::TileMapService: 0303 relFileName = QStringLiteral( "%1/%2/%3/%4.%5" ) 0304 .arg( themeStr() ) 0305 .arg( id.zoomLevel() ) 0306 .arg( id.x() ) 0307 .arg( ( 1<<id.zoomLevel() ) - id.y() - 1 ) //Y coord in TMS runs from bottom to top 0308 .arg( suffix ); 0309 break; 0310 } 0311 0312 return relFileName; 0313 } 0314 0315 QString GeoSceneTileDataset::themeStr() const 0316 { 0317 QFileInfo const dirInfo( sourceDir() ); 0318 return dirInfo.isAbsolute() ? sourceDir() : QLatin1String("maps/") + sourceDir(); 0319 } 0320 0321 QList<const DownloadPolicy *> GeoSceneTileDataset::downloadPolicies() const 0322 { 0323 return m_downloadPolicies; 0324 } 0325 0326 void GeoSceneTileDataset::addDownloadPolicy( const DownloadUsage usage, const int maximumConnections ) 0327 { 0328 DownloadPolicy * const policy = new DownloadPolicy( DownloadPolicyKey( hostNames(), usage )); 0329 policy->setMaximumConnections( maximumConnections ); 0330 m_downloadPolicies.append( policy ); 0331 qCDebug(DIGIKAM_MARBLE_LOG) << "added download policy" << hostNames() << usage << maximumConnections; 0332 } 0333 0334 QStringList GeoSceneTileDataset::hostNames() const 0335 { 0336 QStringList result; 0337 result.reserve(m_downloadUrls.size()); 0338 0339 QVector<QUrl>::const_iterator pos = m_downloadUrls.constBegin(); 0340 QVector<QUrl>::const_iterator const end = m_downloadUrls.constEnd(); 0341 for (; pos != end; ++pos ) 0342 result.append( (*pos).host() ); 0343 return result; 0344 } 0345 0346 }