diff --git a/data/maps/earth/srtm2/srtm2.dgml b/data/maps/earth/srtm2/srtm2.dgml index 8ce7deb78..e2ff45502 100644 --- a/data/maps/earth/srtm2/srtm2.dgml +++ b/data/maps/earth/srtm2/srtm2.dgml @@ -1,46 +1,45 @@ SRTM Data earth srtm2 false 900 21000 false earth/srtm2 - diff --git a/src/lib/marble/ServerLayout.cpp b/src/lib/marble/ServerLayout.cpp index eff081dae..63a8a24d3 100644 --- a/src/lib/marble/ServerLayout.cpp +++ b/src/lib/marble/ServerLayout.cpp @@ -1,232 +1,236 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010,2011 Bernhard Beschow // // Own #include "ServerLayout.h" #include "GeoSceneTileDataset.h" #include "MarbleGlobal.h" #include "TileId.h" #include #include namespace Marble { ServerLayout::ServerLayout( GeoSceneTileDataset *textureLayer ) : m_textureLayer( textureLayer ) { } ServerLayout::~ServerLayout() { } MarbleServerLayout::MarbleServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl MarbleServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { - const QString path = QString( "%1maps/%2/%3/%4/%4_%5.%6" ) + const QString path = QString( "%1/%2/%3/%3_%4.%5" ) .arg( prototypeUrl.path() ) - .arg( m_textureLayer->sourceDir() ) .arg( id.zoomLevel() ) .arg( id.y(), tileDigits, 10, QChar('0') ) .arg( id.x(), tileDigits, 10, QChar('0') ) .arg( m_textureLayer->fileFormat().toLower() ); QUrl url = prototypeUrl; url.setPath( path ); return url; } QString MarbleServerLayout::name() const { return "Marble"; } +QString ServerLayout::sourceDir() const +{ + return m_textureLayer ? m_textureLayer->sourceDir() : QString(); +} + OsmServerLayout::OsmServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl OsmServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const QString suffix = m_textureLayer->fileFormat().toLower(); const QString path = QString( "%1/%2/%3.%4" ).arg( id.zoomLevel() ) .arg( id.x() ) .arg( id.y() ) .arg( suffix ); QUrl url = prototypeUrl; url.setPath( url.path() + path ); return url; } QString OsmServerLayout::name() const { return "OpenStreetMap"; } CustomServerLayout::CustomServerLayout( GeoSceneTileDataset *texture ) : ServerLayout( texture ) { } QUrl CustomServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const GeoDataLatLonBox bbox = id.toLatLonBox( m_textureLayer ); QString urlStr = prototypeUrl.toString( QUrl::DecodeReserved ); urlStr.replace( "{zoomLevel}", QString::number( id.zoomLevel() ) ); urlStr.replace( "{x}", QString::number( id.x() ) ); urlStr.replace( "{y}", QString::number( id.y() ) ); urlStr.replace( "{west}", QString::number( bbox.west( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{south}", QString::number( bbox.south( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{east}", QString::number( bbox.east( GeoDataCoordinates::Degree ), 'f', 12 ) ); urlStr.replace( "{north}", QString::number( bbox.north( GeoDataCoordinates::Degree ), 'f', 12 ) ); return QUrl( urlStr ); } QString CustomServerLayout::name() const { return "Custom"; } WmsServerLayout::WmsServerLayout( GeoSceneTileDataset *texture ) : ServerLayout( texture ) { } QUrl WmsServerLayout::downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &tileId ) const { GeoDataLatLonBox box = tileId.toLatLonBox( m_textureLayer ); QUrlQuery url(prototypeUrl.query()); url.addQueryItem( "service", "WMS" ); url.addQueryItem( "request", "GetMap" ); url.addQueryItem( "version", "1.1.1" ); if ( !url.hasQueryItem( "styles" ) ) url.addQueryItem( "styles", "" ); if ( !url.hasQueryItem( "format" ) ) { if ( m_textureLayer->fileFormat().toLower() == "jpg" ) url.addQueryItem( "format", "image/jpeg" ); else url.addQueryItem( "format", "image/" + m_textureLayer->fileFormat().toLower() ); } if ( !url.hasQueryItem( "srs" ) ) { url.addQueryItem( "srs", epsgCode() ); } if ( !url.hasQueryItem( "layers" ) ) url.addQueryItem( "layers", m_textureLayer->name() ); url.addQueryItem( "width", QString::number( m_textureLayer->tileSize().width() ) ); url.addQueryItem( "height", QString::number( m_textureLayer->tileSize().height() ) ); url.addQueryItem( "bbox", QString( "%1,%2,%3,%4" ).arg( QString::number( box.west( GeoDataCoordinates::Degree ), 'f', 12 ) ) .arg( QString::number( box.south( GeoDataCoordinates::Degree ), 'f', 12 ) ) .arg( QString::number( box.east( GeoDataCoordinates::Degree ), 'f', 12 ) ) .arg( QString::number( box.north( GeoDataCoordinates::Degree ), 'f', 12 ) ) ); QUrl finalUrl = prototypeUrl; finalUrl.setQuery(url); return finalUrl; } QString WmsServerLayout::name() const { return "WebMapService"; } QString WmsServerLayout::epsgCode() const { switch ( m_textureLayer->projection() ) { case GeoSceneTileDataset::Equirectangular: return "EPSG:4326"; case GeoSceneTileDataset::Mercator: return "EPSG:3785"; } Q_ASSERT( false ); // not reached return QString(); } QuadTreeServerLayout::QuadTreeServerLayout( GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl QuadTreeServerLayout::downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &id ) const { QString urlStr = prototypeUrl.toString( QUrl::DecodeReserved ); urlStr.replace( "{quadIndex}", encodeQuadTree( id ) ); return QUrl( urlStr ); } QString QuadTreeServerLayout::name() const { return "QuadTree"; } QString QuadTreeServerLayout::encodeQuadTree( const Marble::TileId &id ) { QString tileNum; for ( int i = id.zoomLevel(); i >= 0; i-- ) { const int tileX = (id.x() >> i) % 2; const int tileY = (id.y() >> i) % 2; const int num = ( 2 * tileY ) + tileX; tileNum += QString::number( num ); } return tileNum; } TmsServerLayout::TmsServerLayout(GeoSceneTileDataset *textureLayer ) : ServerLayout( textureLayer ) { } QUrl TmsServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const { const QString suffix = m_textureLayer->fileFormat().toLower(); // y coordinate in TMS start at the bottom of the map (South) and go upwards, // opposed to OSM which start at the top. // // http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification int y_frombottom = ( 1< // #ifndef MARBLE_SERVERLAYOUT_H #define MARBLE_SERVERLAYOUT_H #include namespace Marble { class GeoSceneTileDataset; class TileId; class ServerLayout { public: explicit ServerLayout( GeoSceneTileDataset *textureLayer ); virtual ~ServerLayout(); /** * Translates given tile @p id using a @p prototypeUrl into an URL * that can be used for downloading. * * @param prototypeUrl prototype URL, to be completed by this method * @param id Marble-specific ID of requested tile * @return completed URL for requested tile id */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const = 0; /** * Returns the name of the server layout to be used as the value in the * mode attribute in the DGML file. */ virtual QString name() const = 0; + /** + * Returns the sourceDir of the texture layer, or an empty string if the texture layer is 0 + */ + QString sourceDir() const; + protected: GeoSceneTileDataset *const m_textureLayer; }; class MarbleServerLayout : public ServerLayout { public: explicit MarbleServerLayout( GeoSceneTileDataset *textureLayer ); /** * Completes the path of the @p prototypeUrl and returns it. */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const TileId & ) const; virtual QString name() const; }; class OsmServerLayout : public ServerLayout { public: explicit OsmServerLayout( GeoSceneTileDataset *textureLayer ); /** * Appends %zoomLevel/%x/%y.%suffix to the path of the @p prototypeUrl and returns * the result. */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const TileId & ) const; virtual QString name() const; }; class CustomServerLayout : public ServerLayout { public: explicit CustomServerLayout( GeoSceneTileDataset *texture ); /** * Replaces escape sequences in the @p prototypeUrl by the values in @p id * and returns the result. * * Escape sequences are: {zoomLevel}, {x}, and {y}. */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const; virtual QString name() const; }; class WmsServerLayout : public ServerLayout { public: explicit WmsServerLayout( GeoSceneTileDataset *texture ); /** * Adds WMS query items to the @p prototypeUrl and returns the result. * * The following items are added: service, request, version, width, height, bbox. * * The following items are only added if they are not already specified in the dgml file: * styles, format, srs, layers. */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &tileId ) const; virtual QString name() const; QString epsgCode() const; }; class QuadTreeServerLayout : public ServerLayout { public: explicit QuadTreeServerLayout( GeoSceneTileDataset* textureLayer ); virtual QUrl downloadUrl( const QUrl &, const Marble::TileId & ) const; virtual QString name() const; private: static QString encodeQuadTree( const Marble::TileId & ); }; class TmsServerLayout : public ServerLayout { public: explicit TmsServerLayout( GeoSceneTileDataset *textureLayer ); /** * Appends %zoomLevel/%x/2^%zoomLevel-%y-1.%suffix to the path of the @p prototypeUrl and returns * the result. * TMS (TileMapService) maps take the origin for y coordinate at the bottom of the map, * as opposed to what Marble and OpenStreepMap (SlippyTiles) do. */ virtual QUrl downloadUrl( const QUrl &prototypeUrl, const TileId & ) const; virtual QString name() const; }; } #endif diff --git a/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp b/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp index 6ddc0817e..18a351222 100644 --- a/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp +++ b/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp @@ -1,312 +1,318 @@ /* This file is part of the Marble Virtual Globe. This program is free software licensed under the GNU LGPL. You can find a copy of this license in LICENSE.txt in the top directory of the source code. Copyright (C) 2008 Torsten Rahn Copyright (C) 2008 Jens-Michael Hoffmann Copyright 2012 Ander Pijoan */ #include "GeoSceneTileDataset.h" #include "GeoSceneTypes.h" #include "DownloadPolicy.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "ServerLayout.h" #include "TileId.h" #include namespace Marble { GeoSceneTileDataset::GeoSceneTileDataset( const QString& name ) : GeoSceneAbstractDataset( name ), m_sourceDir(), m_installMap(), m_storageLayoutMode(Marble), m_serverLayout( new MarbleServerLayout( this ) ), m_levelZeroColumns( defaultLevelZeroColumns ), m_levelZeroRows( defaultLevelZeroRows ), m_minimumTileLevel(0), m_maximumTileLevel( -1 ), m_projection( Equirectangular ), m_blending(), m_downloadUrls(), m_nextUrl( m_downloadUrls.constEnd() ) { } GeoSceneTileDataset::~GeoSceneTileDataset() { qDeleteAll( m_downloadPolicies ); delete m_serverLayout; } const char* GeoSceneTileDataset::nodeType() const { return GeoSceneTypes::GeoSceneTileDatasetType; } QString GeoSceneTileDataset::sourceDir() const { return m_sourceDir; } void GeoSceneTileDataset::setSourceDir( const QString& sourceDir ) { m_sourceDir = sourceDir; } QString GeoSceneTileDataset::installMap() const { return m_installMap; } void GeoSceneTileDataset::setInstallMap( const QString& installMap ) { m_installMap = installMap; } GeoSceneTileDataset::StorageLayout GeoSceneTileDataset::storageLayout() const { return m_storageLayoutMode; } void GeoSceneTileDataset::setStorageLayout( const StorageLayout layout ) { m_storageLayoutMode = layout; } void GeoSceneTileDataset::setServerLayout( const ServerLayout *layout ) { delete m_serverLayout; m_serverLayout = layout; } const ServerLayout* GeoSceneTileDataset::serverLayout() const { return m_serverLayout; } int GeoSceneTileDataset::levelZeroColumns() const { return m_levelZeroColumns; } void GeoSceneTileDataset::setLevelZeroColumns( const int columns ) { m_levelZeroColumns = columns; } int GeoSceneTileDataset::levelZeroRows() const { return m_levelZeroRows; } void GeoSceneTileDataset::setLevelZeroRows( const int rows ) { m_levelZeroRows = rows; } int GeoSceneTileDataset::maximumTileLevel() const { return m_maximumTileLevel; } void GeoSceneTileDataset::setMaximumTileLevel( const int maximumTileLevel ) { m_maximumTileLevel = maximumTileLevel; } int GeoSceneTileDataset::minimumTileLevel() const { return m_minimumTileLevel; } void GeoSceneTileDataset::setMinimumTileLevel(int level) { m_minimumTileLevel = level; } void GeoSceneTileDataset::setTileLevels(const QString &tileLevels) { if (tileLevels.isEmpty()) { m_tileLevels.clear(); return; } QStringList values = tileLevels.split(','); foreach(const QString &value, values) { bool canParse(false); int const tileLevel = value.trimmed().toInt(&canParse); if (canParse && tileLevel >= 0 && tileLevel < 100) { m_tileLevels << tileLevel; } else { mDebug() << "Cannot parse tile level part " << value << " in " << tileLevels << ", ignoring it."; } } if (!m_tileLevels.isEmpty()) { qSort(m_tileLevels); m_minimumTileLevel = m_tileLevels.first(); m_maximumTileLevel = m_tileLevels.last(); } } QVector GeoSceneTileDataset::tileLevels() const { return m_tileLevels; } QVector GeoSceneTileDataset::downloadUrls() const { return m_downloadUrls; } const QSize GeoSceneTileDataset::tileSize() const { if ( m_tileSize.isEmpty() ) { const TileId id( 0, 0, 0, 0 ); QString const fileName = relativeTileFileName( id ); QFileInfo const dirInfo( fileName ); QString const path = dirInfo.isAbsolute() ? fileName : MarbleDirs::path( fileName ); QImage testTile( path ); if ( testTile.isNull() ) { mDebug() << "Tile size is missing in dgml and no base tile found in " << themeStr(); mDebug() << "Using default tile size " << c_defaultTileSize; m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize ); } else { m_tileSize = testTile.size(); } if ( m_tileSize.isEmpty() ) { mDebug() << "Tile width or height cannot be 0. Falling back to default tile size."; m_tileSize = QSize( c_defaultTileSize, c_defaultTileSize ); } } Q_ASSERT( !m_tileSize.isEmpty() ); return m_tileSize; } void GeoSceneTileDataset::setTileSize( const QSize &tileSize ) { if ( tileSize.isEmpty() ) { mDebug() << "Ignoring invalid tile size " << tileSize; } else { m_tileSize = tileSize; } } GeoSceneTileDataset::Projection GeoSceneTileDataset::projection() const { return m_projection; } void GeoSceneTileDataset::setProjection( const Projection projection ) { m_projection = projection; } // Even though this method changes the internal state, it may be const // because the compiler is forced to invoke this method for different TileIds. QUrl GeoSceneTileDataset::downloadUrl( const TileId &id ) const { // default download url - if ( m_downloadUrls.empty() ) - return m_serverLayout->downloadUrl( QUrl( "http://files.kde.org/marble/" ), id ); + if ( m_downloadUrls.empty() ) { + QUrl const defaultUrl = QUrl(QString("%1/%2") + .arg("https://maps.kde.org") + .arg(m_serverLayout->sourceDir())); + mDebug() << "No download URL specified for tiles stored in " + << m_sourceDir << ", falling back to " << defaultUrl.toString(); + return m_serverLayout->downloadUrl(defaultUrl, id); + } if ( m_nextUrl == m_downloadUrls.constEnd() ) m_nextUrl = m_downloadUrls.constBegin(); const QUrl url = m_serverLayout->downloadUrl( *m_nextUrl, id ); ++m_nextUrl; return url; } void GeoSceneTileDataset::addDownloadUrl( const QUrl & url ) { m_downloadUrls.append( url ); // FIXME: this could be done only once m_nextUrl = m_downloadUrls.constBegin(); } QString GeoSceneTileDataset::relativeTileFileName( const TileId &id ) const { const QString suffix = fileFormat().toLower(); QString relFileName; switch ( m_storageLayoutMode ) { default: mDebug() << Q_FUNC_INFO << "Invalid storage layout mode! Falling back to default."; case GeoSceneTileDataset::Marble: relFileName = QString( "%1/%2/%3/%3_%4.%5" ) .arg( themeStr() ) .arg( id.zoomLevel() ) .arg( id.y(), tileDigits, 10, QChar('0') ) .arg( id.x(), tileDigits, 10, QChar('0') ) .arg( suffix ); break; case GeoSceneTileDataset::OpenStreetMap: relFileName = QString( "%1/%2/%3/%4.%5" ) .arg( themeStr() ) .arg( id.zoomLevel() ) .arg( id.x() ) .arg( id.y() ) .arg( suffix ); break; case GeoSceneTileDataset::TileMapService: relFileName = QString( "%1/%2/%3/%4.%5" ) .arg( themeStr() ) .arg( id.zoomLevel() ) .arg( id.x() ) .arg( ( 1< GeoSceneTileDataset::downloadPolicies() const { return m_downloadPolicies; } void GeoSceneTileDataset::addDownloadPolicy( const DownloadUsage usage, const int maximumConnections ) { DownloadPolicy * const policy = new DownloadPolicy( DownloadPolicyKey( hostNames(), usage )); policy->setMaximumConnections( maximumConnections ); m_downloadPolicies.append( policy ); mDebug() << "added download policy" << hostNames() << usage << maximumConnections; } QStringList GeoSceneTileDataset::hostNames() const { QStringList result; QVector::const_iterator pos = m_downloadUrls.constBegin(); QVector::const_iterator const end = m_downloadUrls.constEnd(); for (; pos != end; ++pos ) result.append( (*pos).host() ); return result; } }