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 }