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