summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFriedrich W. H. Kossebau <kossebau@kde.org>2016-10-07 05:41:28 (GMT)
committerFriedrich W. H. Kossebau <kossebau@kde.org>2016-10-07 05:46:56 (GMT)
commit5c1022d13a3b68fd3251cf0b57d03087957dc9eb (patch)
tree4b92792b982466452194d991bb043ed7114cd384
parentc85227a4ccda5f7a6595102f67cf73de0e6791b1 (diff)
Introducing GeoSceneAbstractTileProjection for tile x/y <-> lonlat
Summary: In the current code in some places hard assumptions are made about the projection used by the tile material. Also are calculations duplicated. The new abstract class GeoSceneAbstractTileProjection and its currently two concrete subclasses GeoSceneEquirectTileProjection & GeoSceneMercatorTileProjection should allow to make most code agnostic of the tile projection details and also remove the code duplication, also makes unit testing of the projection easy. GeoSceneAbstractTileProjection follows the concepts of AbstractProjection and has conversion methods in the interface, with the output vars as non-const references at the end of the argument list (for consistency, I personally prefer yielded stuff as return parameter, or have output vars at least being first in the argument list and as pointers for improved markup in the calling code). As the current two implementations need to know about the variables levelZeroColumns and levelZeroRows, I made these properties of the classes themselves, to avoid having the classes depend on GeoSceneTileDataset. Comes at the cost of data duplication and thus more complicated setup, but also avoids an indirection in the class methods using these values. For simplicity I put these properties onto GeoSceneAbstractTileProjection itself, to avoid another intermediate abstract subclass for the concept of levelZeroColumns and levelZeroRows. The projection methods do not do any out-of-bounds handling, but expect the calling code to pass proper value. Which should be valid with the current callees. Any methods tileProjection() have been renamed to tileProjectionType(), to make clear those just return the type, not a projection object. The patch also makes TileId more dump again and just a property container, especially no longer knowing about GeoSceneTileDataset. FUTURE WORK: * Look into all remaining places which have "if tileProjectionType() == x" to see how they can be made tile projection type agnostic as well Reviewers: rahn, #marble Reviewed By: rahn, #marble Subscribers: rahn Differential Revision: https://phabricator.kde.org/D2780
-rw-r--r--src/lib/marble/DownloadRegion.cpp6
-rw-r--r--src/lib/marble/MapThemeManager.cpp2
-rw-r--r--src/lib/marble/MapWizard.cpp6
-rw-r--r--src/lib/marble/MergedLayerDecorator.cpp24
-rw-r--r--src/lib/marble/MergedLayerDecorator.h2
-rw-r--r--src/lib/marble/ScanlineTextureMapperContext.cpp2
-rw-r--r--src/lib/marble/ScanlineTextureMapperContext.h6
-rw-r--r--src/lib/marble/ServerLayout.cpp12
-rw-r--r--src/lib/marble/StackedTileLoader.cpp4
-rw-r--r--src/lib/marble/StackedTileLoader.h2
-rw-r--r--src/lib/marble/TileId.cpp60
-rw-r--r--src/lib/marble/TileId.h8
-rw-r--r--src/lib/marble/VectorTileModel.cpp21
-rw-r--r--src/lib/marble/geodata/CMakeLists.txt3
-rw-r--r--src/lib/marble/geodata/handlers/dgml/DgmlProjectionTagHandler.cpp15
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp72
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h145
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp161
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h85
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp182
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h89
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp34
-rw-r--r--src/lib/marble/geodata/scene/GeoSceneTileDataset.h10
-rw-r--r--src/lib/marble/geodata/writers/dgml/DgmlTextureTagWriter.cpp5
-rw-r--r--src/lib/marble/layers/TextureLayer.cpp6
-rw-r--r--src/lib/marble/layers/TextureLayer.h2
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/TestGeoSceneWriter.cpp2
-rw-r--r--tests/TestTileProjection.cpp540
-rw-r--r--tools/vectorosm-tilecreator/TileDirectory.cpp25
-rw-r--r--tools/vectorosm-tilecreator/TileDirectory.h2
-rw-r--r--tools/vectorosm-tilecreator/TileIterator.cpp19
-rw-r--r--tools/vectorosm-tilecreator/VectorClipper.cpp7
-rw-r--r--tools/vectorosm-tilecreator/VectorClipper.h2
34 files changed, 1410 insertions, 152 deletions
diff --git a/src/lib/marble/DownloadRegion.cpp b/src/lib/marble/DownloadRegion.cpp
index d36075f..ad8305c 100644
--- a/src/lib/marble/DownloadRegion.cpp
+++ b/src/lib/marble/DownloadRegion.cpp
@@ -57,10 +57,10 @@ int DownloadRegionPrivate::rad2PixelY( qreal const lat, const TextureLayer *text
qreal const globalHeight = textureLayer->tileSize().height()
* textureLayer->tileRowCount( m_visibleTileLevel );
qreal const normGlobalHeight = globalHeight / M_PI;
- switch ( textureLayer->tileProjection() ) {
- case GeoSceneTileDataset::Equirectangular:
+ switch (textureLayer->tileProjectionType()) {
+ case GeoSceneAbstractTileProjection::Equirectangular:
return static_cast<int>( globalHeight * 0.5 - lat * normGlobalHeight );
- case GeoSceneTileDataset::Mercator:
+ case GeoSceneAbstractTileProjection::Mercator:
if ( fabs( lat ) < 1.4835 )
return static_cast<int>( globalHeight * 0.5 - gdInv( lat ) * 0.5 * normGlobalHeight );
if ( lat >= +1.4835 )
diff --git a/src/lib/marble/MapThemeManager.cpp b/src/lib/marble/MapThemeManager.cpp
index 512abb6..c2ab592 100644
--- a/src/lib/marble/MapThemeManager.cpp
+++ b/src/lib/marble/MapThemeManager.cpp
@@ -563,7 +563,7 @@ GeoSceneDocument *MapThemeManager::createMapThemeFromOverlay( const GeoDataPhoto
texture->setSourceDir( sourceDir );
texture->setFileFormat( extension );
texture->setInstallMap( fileName );
- texture->setProjection(GeoSceneTileDataset::Equirectangular);
+ texture->setTileProjection(GeoSceneAbstractTileProjection::Equirectangular);
layer->addDataset(texture);
diff --git a/src/lib/marble/MapWizard.cpp b/src/lib/marble/MapWizard.cpp
index 83727e9..549c511 100644
--- a/src/lib/marble/MapWizard.cpp
+++ b/src/lib/marble/MapWizard.cpp
@@ -789,7 +789,7 @@ GeoSceneDocument* MapWizard::createDocument()
texture->setLevelZeroRows( 1 );
texture->setLevelZeroColumns( 1 );
texture->setServerLayout( new WmsServerLayout( texture ) );
- texture->setProjection( GeoSceneTileDataset::Equirectangular );
+ texture->setTileProjection(GeoSceneAbstractTileProjection::Equirectangular);
}
else if( d->mapProviderType == MapWizardPrivate::StaticUrlMap )
@@ -803,7 +803,7 @@ GeoSceneDocument* MapWizard::createDocument()
texture->setLevelZeroRows( 1 );
texture->setLevelZeroColumns( 1 );
texture->setServerLayout( new CustomServerLayout( texture ) );
- texture->setProjection( GeoSceneTileDataset::Mercator );
+ texture->setTileProjection(GeoSceneAbstractTileProjection::Mercator);
}
else if( d->mapProviderType == MapWizardPrivate::StaticImageMap )
@@ -813,7 +813,7 @@ GeoSceneDocument* MapWizard::createDocument()
texture->setFileFormat( d->format.toUpper() );
texture->setInstallMap(document->head()->theme() + QLatin1Char('.') + d->format);
texture->setServerLayout( new MarbleServerLayout( texture ) );
- texture->setProjection( GeoSceneTileDataset::Equirectangular );
+ texture->setTileProjection(GeoSceneAbstractTileProjection::Equirectangular);
int imageWidth = QImage( image ).width();
int tileSize = c_defaultTileSize;
diff --git a/src/lib/marble/MergedLayerDecorator.cpp b/src/lib/marble/MergedLayerDecorator.cpp
index 46d4619..f6daf8b 100644
--- a/src/lib/marble/MergedLayerDecorator.cpp
+++ b/src/lib/marble/MergedLayerDecorator.cpp
@@ -146,11 +146,11 @@ int MergedLayerDecorator::tileRowCount( int level ) const
return TileLoaderHelper::levelToRow( levelZeroRows, level );
}
-GeoSceneTileDataset::Projection MergedLayerDecorator::tileProjection() const
+GeoSceneAbstractTileProjection::Type MergedLayerDecorator::tileProjectionType() const
{
Q_ASSERT( !d->m_textureLayers.isEmpty() );
- return d->m_textureLayers.at( 0 )->projection();
+ return d->m_textureLayers.at( 0 )->tileProjectionType();
}
QSize MergedLayerDecorator::tileSize() const
@@ -218,7 +218,8 @@ void MergedLayerDecorator::Private::renderGroundOverlays( QImage *tileImage, con
/* All tiles are covering the same area. Pick one. */
const TileId tileId = tiles.first()->id();
- GeoDataLatLonBox tileLatLonBox = tileId.toLatLonBox( findRelevantTextureLayers( tileId ).first() );
+ GeoDataLatLonBox tileLatLonBox;
+ findRelevantTextureLayers(tileId).first()->tileProjection()->geoCoordinates(tileId, tileLatLonBox);
/* Map the ground overlay to the image. */
for ( int i = 0; i < m_groundOverlays.size(); ++i ) {
@@ -246,13 +247,14 @@ void MergedLayerDecorator::Private::renderGroundOverlays( QImage *tileImage, con
const qreal rad2Pixel = global_height / M_PI;
qreal latPixelPosition = rad2Pixel/2 * gdInv(tileLatLonBox.north());
+ const bool isMercatorTileProjection = (m_textureLayers.at( 0 )->tileProjectionType() == GeoSceneAbstractTileProjection::Mercator);
for ( int y = 0; y < tileImage->height(); ++y ) {
QRgb *scanLine = ( QRgb* ) ( tileImage->scanLine( y ) );
qreal lat = 0;
- if (m_textureLayers.at( 0 )->projection() == GeoSceneTileDataset::Mercator) {
+ if (isMercatorTileProjection) {
lat = gd(2 * (latPixelPosition - y) * pixel2Rad );
}
else {
@@ -584,10 +586,16 @@ QVector<const GeoSceneTextureTileDataset *> MergedLayerDecorator::Private::findR
if ( !candidate->hasMaximumTileLevel() ||
candidate->maximumTileLevel() >= stackedTileId.zoomLevel() ) {
//check if the tile intersects with texture bounds
- if ( candidate->latLonBox().isNull()
- || candidate->latLonBox().intersects( stackedTileId.toLatLonBox( candidate ) ) )
- {
- result.append( candidate );
+ if (candidate->latLonBox().isNull()) {
+ result.append(candidate);
+ }
+ else {
+ GeoDataLatLonBox bbox;
+ candidate->tileProjection()->geoCoordinates(stackedTileId, bbox);
+
+ if (candidate->latLonBox().intersects(bbox)) {
+ result.append( candidate );
+ }
}
}
}
diff --git a/src/lib/marble/MergedLayerDecorator.h b/src/lib/marble/MergedLayerDecorator.h
index b4c9c61..5825557 100644
--- a/src/lib/marble/MergedLayerDecorator.h
+++ b/src/lib/marble/MergedLayerDecorator.h
@@ -57,7 +57,7 @@ class MergedLayerDecorator
int tileRowCount( int level ) const;
- GeoSceneTextureTileDataset::Projection tileProjection() const;
+ GeoSceneAbstractTileProjection::Type tileProjectionType() const;
QSize tileSize() const;
diff --git a/src/lib/marble/ScanlineTextureMapperContext.cpp b/src/lib/marble/ScanlineTextureMapperContext.cpp
index eeaae47..8ab9e1f 100644
--- a/src/lib/marble/ScanlineTextureMapperContext.cpp
+++ b/src/lib/marble/ScanlineTextureMapperContext.cpp
@@ -24,7 +24,7 @@ using namespace Marble;
ScanlineTextureMapperContext::ScanlineTextureMapperContext( StackedTileLoader * const tileLoader, int tileLevel )
: m_tileLoader( tileLoader ),
- m_textureProjection( tileLoader->tileProjection() ), // cache texture projection
+ m_textureProjection(tileLoader->tileProjectionType()), // cache texture projection
m_tileSize( tileLoader->tileSize() ), // cache tile size
m_tileLevel( tileLevel ),
m_globalWidth( m_tileSize.width() * m_tileLoader->tileColumnCount( m_tileLevel ) ),
diff --git a/src/lib/marble/ScanlineTextureMapperContext.h b/src/lib/marble/ScanlineTextureMapperContext.h
index e187b61..bf35749 100644
--- a/src/lib/marble/ScanlineTextureMapperContext.h
+++ b/src/lib/marble/ScanlineTextureMapperContext.h
@@ -73,7 +73,7 @@ private:
private:
StackedTileLoader *const m_tileLoader;
- GeoSceneTileDataset::Projection const m_textureProjection;
+ GeoSceneAbstractTileProjection::Type const m_textureProjection;
/// size of the tiles of of the current texture layer
QSize const m_tileSize;
@@ -123,9 +123,9 @@ inline qreal ScanlineTextureMapperContext::rad2PixelX( const qreal lon ) const
inline qreal ScanlineTextureMapperContext::rad2PixelY( const qreal lat ) const
{
switch ( m_textureProjection ) {
- case GeoSceneTileDataset::Equirectangular:
+ case GeoSceneAbstractTileProjection::Equirectangular:
return -lat * m_normGlobalHeight;
- case GeoSceneTileDataset::Mercator:
+ case GeoSceneAbstractTileProjection::Mercator:
if ( fabs( lat ) < 1.4835 ) {
// We develop the inverse Gudermannian into a MacLaurin Series:
// In spite of the many elements needed to get decent
diff --git a/src/lib/marble/ServerLayout.cpp b/src/lib/marble/ServerLayout.cpp
index fa6c2ec..91b0988 100644
--- a/src/lib/marble/ServerLayout.cpp
+++ b/src/lib/marble/ServerLayout.cpp
@@ -95,7 +95,8 @@ CustomServerLayout::CustomServerLayout( GeoSceneTileDataset *texture )
QUrl CustomServerLayout::downloadUrl( const QUrl &prototypeUrl, const TileId &id ) const
{
- const GeoDataLatLonBox bbox = id.toLatLonBox( m_textureLayer );
+ GeoDataLatLonBox bbox;
+ m_textureLayer->tileProjection()->geoCoordinates(id, bbox);
QString urlStr = prototypeUrl.toString( QUrl::DecodeReserved );
@@ -123,7 +124,8 @@ WmsServerLayout::WmsServerLayout( GeoSceneTileDataset *texture )
QUrl WmsServerLayout::downloadUrl( const QUrl &prototypeUrl, const Marble::TileId &tileId ) const
{
- GeoDataLatLonBox box = tileId.toLatLonBox( m_textureLayer );
+ GeoDataLatLonBox box;
+ m_textureLayer->tileProjection()->geoCoordinates(tileId, box);
QUrlQuery url(prototypeUrl.query());
url.addQueryItem( "service", "WMS" );
@@ -160,10 +162,10 @@ QString WmsServerLayout::name() const
QString WmsServerLayout::epsgCode() const
{
- switch ( m_textureLayer->projection() ) {
- case GeoSceneTileDataset::Equirectangular:
+ switch (m_textureLayer->tileProjectionType()) {
+ case GeoSceneAbstractTileProjection::Equirectangular:
return "EPSG:4326";
- case GeoSceneTileDataset::Mercator:
+ case GeoSceneAbstractTileProjection::Mercator:
return "EPSG:3785";
}
diff --git a/src/lib/marble/StackedTileLoader.cpp b/src/lib/marble/StackedTileLoader.cpp
index 2446aef..847dfa3 100644
--- a/src/lib/marble/StackedTileLoader.cpp
+++ b/src/lib/marble/StackedTileLoader.cpp
@@ -77,9 +77,9 @@ int StackedTileLoader::tileRowCount( int level ) const
return d->m_layerDecorator->tileRowCount( level );
}
-GeoSceneTileDataset::Projection StackedTileLoader::tileProjection() const
+GeoSceneAbstractTileProjection::Type StackedTileLoader::tileProjectionType() const
{
- return d->m_layerDecorator->tileProjection();
+ return d->m_layerDecorator->tileProjectionType();
}
QSize StackedTileLoader::tileSize() const
diff --git a/src/lib/marble/StackedTileLoader.h b/src/lib/marble/StackedTileLoader.h
index f5b1e14..ddef3a4 100644
--- a/src/lib/marble/StackedTileLoader.h
+++ b/src/lib/marble/StackedTileLoader.h
@@ -74,7 +74,7 @@ class StackedTileLoader : public QObject
int tileRowCount( int level ) const;
- GeoSceneTextureTileDataset::Projection tileProjection() const;
+ GeoSceneAbstractTileProjection::Type tileProjectionType() const;
QSize tileSize() const;
diff --git a/src/lib/marble/TileId.cpp b/src/lib/marble/TileId.cpp
index a494288..228a1f0 100644
--- a/src/lib/marble/TileId.cpp
+++ b/src/lib/marble/TileId.cpp
@@ -12,9 +12,7 @@
// Own
#include "TileId.h"
-#include "MarbleMath.h"
-#include "GeoDataLatLonBox.h"
-#include "GeoSceneTileDataset.h"
+#include "GeoDataCoordinates.h"
#include <QDebug>
@@ -36,32 +34,6 @@ TileId::TileId()
{
}
-GeoDataLatLonBox TileId::toLatLonBox( const GeoSceneTileDataset *textureLayer ) const
-{
-
- qreal radius = ( 1 << zoomLevel() ) * textureLayer->levelZeroColumns() / 2.0;
-
- qreal lonLeft = ( x() - radius ) / radius * M_PI;
- qreal lonRight = ( x() - radius + 1 ) / radius * M_PI;
-
- radius = ( 1 << zoomLevel() ) * textureLayer->levelZeroRows() / 2.0;
- qreal latTop = 0;
- qreal latBottom = 0;
-
- switch ( textureLayer->projection() ) {
- case GeoSceneTileDataset::Equirectangular:
- latTop = ( radius - y() ) / radius * M_PI / 2.0;
- latBottom = ( radius - y() - 1 ) / radius * M_PI / 2.0;
- break;
- case GeoSceneTileDataset::Mercator:
- latTop = atan( sinh( ( radius - y() ) / radius * M_PI ) );
- latBottom = atan( sinh( ( radius - y() - 1 ) / radius * M_PI ) );
- break;
- }
-
- return GeoDataLatLonBox( latTop, latBottom, lonRight, lonLeft );
-}
-
TileId TileId::fromCoordinates(const GeoDataCoordinates &coords, int zoomLevel)
{
if ( zoomLevel < 0 ) {
@@ -89,36 +61,6 @@ TileId TileId::fromCoordinates(const GeoDataCoordinates &coords, int zoomLevel)
return TileId(0, zoomLevel, x, y);
}
-unsigned int TileId::lon2tileX( qreal lon, unsigned int maxTileX )
-{
- return (unsigned int)floor(0.5 * (lon / M_PI + 1.0) * maxTileX);
-}
-
-unsigned int TileId::lat2tileY( qreal latitude, unsigned int maxTileY )
-{
- // We need to calculate the tile position from the latitude
- // projected using the Mercator projection. This requires the inverse Gudermannian
- // function which is only defined between -85°S and 85°N. Therefore in order to
- // prevent undefined results we need to restrict our calculation:
- qreal maxAbsLat = 85.0 * DEG2RAD;
- qreal lat = (qAbs(latitude) > maxAbsLat) ? latitude/qAbs(latitude) * maxAbsLat : latitude;
- return (unsigned int)floor(0.5 * (1.0 - gdInv(lat) / M_PI) * maxTileY);
-}
-
-
-qreal TileId::tileX2lon( unsigned int x, unsigned int maxTileX )
-{
- return ( (2*M_PI * x) / maxTileX - M_PI );
-}
-
-qreal TileId::tileY2lat( unsigned int y, unsigned int maxTileY )
-{
- return gd(M_PI * (1.0 - (2.0 * y) / maxTileY));
-}
-
-
-
-
}
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/lib/marble/TileId.h b/src/lib/marble/TileId.h
index 04fcab0..2e2721c 100644
--- a/src/lib/marble/TileId.h
+++ b/src/lib/marble/TileId.h
@@ -21,8 +21,6 @@ class QString;
namespace Marble
{
class GeoDataCoordinates;
-class GeoSceneTileDataset;
-class GeoDataLatLonBox;
class MARBLE_EXPORT TileId
{
@@ -39,14 +37,8 @@ class MARBLE_EXPORT TileId
bool operator==( TileId const& rhs ) const;
bool operator<( TileId const& rhs ) const;
- GeoDataLatLonBox toLatLonBox( const GeoSceneTileDataset *textureLayer ) const;
static TileId fromCoordinates( const GeoDataCoordinates& coords, int zoomLevel );
- static unsigned int lon2tileX( qreal lon, unsigned int maxTileX );
- static unsigned int lat2tileY( qreal lat, unsigned int maxTileY );
- static qreal tileX2lon( unsigned int x, unsigned int maxTileX );
- static qreal tileY2lat( unsigned int y, unsigned int maxTileY );
-
private:
uint m_mapThemeIdHash;
int m_zoomLevel;
diff --git a/src/lib/marble/VectorTileModel.cpp b/src/lib/marble/VectorTileModel.cpp
index 4e4c7b4..5ef5d98 100644
--- a/src/lib/marble/VectorTileModel.cpp
+++ b/src/lib/marble/VectorTileModel.cpp
@@ -115,27 +115,29 @@ void VectorTileModel::setViewport( const GeoDataLatLonBox &latLonBox, int radius
m_deleteDocumentsLater = true;
}
- const unsigned int maxTileX = ( 1 << tileZoomLevel ) * m_layer->levelZeroColumns();
- const unsigned int maxTileY = ( 1 << tileZoomLevel ) * m_layer->levelZeroRows();
-
/** LOGIC FOR DOWNLOADING ALL THE TILES THAT ARE INSIDE THE SCREEN AT THE CURRENT ZOOM LEVEL **/
// New tiles X and Y for moved screen coordinates
// More info: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Subtiles
// More info: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#C.2FC.2B.2B
- // Sometimes the formula returns wrong huge values, x and y have to be between 0 and 2^ZoomLevel
- unsigned int westX = qBound<unsigned int>( 0, TileId::lon2tileX( latLonBox.west(), maxTileX ), maxTileX);
- unsigned int northY = qBound<unsigned int>( 0, TileId::lat2tileY( latLonBox.north(), maxTileY ), maxTileY);
- unsigned int eastX = qBound<unsigned int>( 0, TileId::lon2tileX( latLonBox.east(), maxTileX ), maxTileX);
- unsigned int southY = qBound<unsigned int>( 0, TileId::lat2tileY( latLonBox.south(), maxTileY ), maxTileY );
+ int westX;
+ int northY;
+ int eastX;
+ int southY;
+ m_layer->tileProjection()->tileIndexes(latLonBox, tileZoomLevel, westX, northY, eastX, southY);
// Download tiles and send them to VectorTileLayer
// When changing zoom, download everything inside the screen
+ // TODO: hardcodes assumption about tiles indexing also ends at dateline
+ // TODO: what about crossing things in y direction?
if ( !latLonBox.crossesDateLine() ) {
queryTiles( tileZoomLevel, westX, northY, eastX, southY );
}
// When only moving screen, just download the new tiles
else {
+ // TODO: maxTileX (calculation knowledge) should be a property of tileProjection or m_layer
+ const unsigned int maxTileX = (1 << tileZoomLevel) * m_layer->levelZeroColumns() - 1;
+
queryTiles( tileZoomLevel, 0, northY, eastX, southY );
queryTiles( tileZoomLevel, westX, northY, maxTileX, southY );
}
@@ -197,7 +199,8 @@ void VectorTileModel::updateTile( const TileId &id, GeoDataDocument *document )
m_deleteDocumentsLater = false;
m_documents.clear();
}
- GeoDataLatLonBox const boundingBox = id.toLatLonBox(m_layer);
+ GeoDataLatLonBox boundingBox;
+ m_layer->tileProjection()->geoCoordinates(id, boundingBox);
m_documents[id] = QSharedPointer<CacheDocument>(new CacheDocument(document, this, boundingBox));
emit tileAdded(document);
}
diff --git a/src/lib/marble/geodata/CMakeLists.txt b/src/lib/marble/geodata/CMakeLists.txt
index 187d548..add79b2 100644
--- a/src/lib/marble/geodata/CMakeLists.txt
+++ b/src/lib/marble/geodata/CMakeLists.txt
@@ -77,6 +77,9 @@ SET ( geodata_data_SRCS
)
SET ( geodata_scene_SRCS
+ geodata/scene/GeoSceneAbstractTileProjection.cpp
+ geodata/scene/GeoSceneMercatorTileProjection.cpp
+ geodata/scene/GeoSceneEquirectTileProjection.cpp
geodata/scene/GeoSceneIcon.cpp
geodata/scene/GeoSceneTileDataset.cpp
geodata/scene/GeoSceneVectorTileDataset.cpp
diff --git a/src/lib/marble/geodata/handlers/dgml/DgmlProjectionTagHandler.cpp b/src/lib/marble/geodata/handlers/dgml/DgmlProjectionTagHandler.cpp
index 7aef842..e38bd7d 100644
--- a/src/lib/marble/geodata/handlers/dgml/DgmlProjectionTagHandler.cpp
+++ b/src/lib/marble/geodata/handlers/dgml/DgmlProjectionTagHandler.cpp
@@ -47,15 +47,16 @@ GeoNode* DgmlProjectionTagHandler::parse( GeoParser& parser ) const
// Attribute name, default to "Equirectangular"
const QString nameStr = parser.attribute( dgmlAttr_name ).trimmed();
if ( !nameStr.isEmpty() ) {
- GeoSceneTileDataset::Projection projection = GeoSceneTileDataset::Equirectangular;
- if (nameStr == QLatin1String("Equirectangular"))
- projection = GeoSceneTileDataset::Equirectangular;
- else if (nameStr == QLatin1String("Mercator"))
- projection = GeoSceneTileDataset::Mercator;
- else
+ GeoSceneAbstractTileProjection::Type tileProjectionType = GeoSceneAbstractTileProjection::Equirectangular;
+ if (nameStr == QLatin1String("Equirectangular")) {
+ tileProjectionType = GeoSceneAbstractTileProjection::Equirectangular;
+ } else if (nameStr == QLatin1String("Mercator")) {
+ tileProjectionType = GeoSceneAbstractTileProjection::Mercator;
+ } else {
parser.raiseWarning( QString( "Value not allowed for attribute name: %1" ).arg( nameStr ));
+ }
- parentItem.nodeAs<GeoSceneTileDataset>()->setProjection( projection );
+ parentItem.nodeAs<GeoSceneTileDataset>()->setTileProjection(tileProjectionType);
}
return 0;
}
diff --git a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp
new file mode 100644
index 0000000..48f6e30
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp
@@ -0,0 +1,72 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "GeoSceneAbstractTileProjection.h"
+
+namespace Marble
+{
+
+class GeoSceneAbstractTileProjectionPrivate
+{
+public:
+ GeoSceneAbstractTileProjectionPrivate();
+
+public:
+ int levelZeroColumns;
+ int levelZeroRows;
+};
+
+GeoSceneAbstractTileProjectionPrivate::GeoSceneAbstractTileProjectionPrivate()
+ : levelZeroColumns(1)
+ , levelZeroRows(1)
+{
+}
+
+GeoSceneAbstractTileProjection::GeoSceneAbstractTileProjection()
+ : d_ptr(new GeoSceneAbstractTileProjectionPrivate())
+{
+}
+
+GeoSceneAbstractTileProjection::~GeoSceneAbstractTileProjection()
+{
+}
+
+int GeoSceneAbstractTileProjection::levelZeroColumns() const
+{
+ return d_ptr->levelZeroColumns;
+}
+
+void GeoSceneAbstractTileProjection::setLevelZeroColumns(int levelZeroColumns)
+{
+ d_ptr->levelZeroColumns = levelZeroColumns;
+}
+
+int GeoSceneAbstractTileProjection::levelZeroRows() const
+{
+ return d_ptr->levelZeroRows;
+}
+
+void GeoSceneAbstractTileProjection::setLevelZeroRows(int levelZeroRows)
+{
+ d_ptr->levelZeroRows = levelZeroRows;
+}
+
+}
diff --git a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h
new file mode 100644
index 0000000..d909b47
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h
@@ -0,0 +1,145 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef MARBLE_GEOSCENEABSTRACTTILEPROJECTION_H
+#define MARBLE_GEOSCENEABSTRACTTILEPROJECTION_H
+
+#include "geodata_export.h"
+#include <TileId.h>
+
+#include <QScopedPointer>
+
+namespace Marble
+{
+
+class GeoSceneAbstractTileProjectionPrivate;
+
+class GeoDataLatLonBox;
+
+/**
+ * @short A base class for projections between tile indizes and geo coordinates in Marble.
+ *
+ * For map tiling with indizes in x and y dimensions and 1 or multiple zoomlevels.
+ * The lowest zoomlevel is 0.
+ */
+class GEODATA_EXPORT GeoSceneAbstractTileProjection
+{
+public:
+ enum Type { Equirectangular, Mercator };
+
+ /**
+ * @brief Construct a new GeoSceneAbstractTileProjection.
+ */
+ GeoSceneAbstractTileProjection();
+
+ virtual ~GeoSceneAbstractTileProjection();
+
+public:
+ virtual GeoSceneAbstractTileProjection::Type type() const = 0;
+
+ /**
+ * @return the number of tiles on level 0 in x dimension
+ */
+ int levelZeroColumns() const;
+ /**
+ * @brief Sets the number of tiles on level 0 in x dimension
+ *
+ * @param levelZeroColumns new number of tiles on level 0 in x dimension
+ *
+ * Default value of the levelZeroColumns property is 1.
+ */
+ void setLevelZeroColumns(int levelZeroColumns);
+
+ /**
+ * @return the number of tiles on level 0 in y dimension
+ */
+ int levelZeroRows() const;
+ /**
+ * @brief Sets the number of tiles on level 0 in y dimension
+ *
+ * @param levelZeroColumns new number of tiles on level 0 in y dimension
+ *
+ * Default value of the levelZeroRows property is 1.
+ */
+ void setLevelZeroRows(int levelZeroRows);
+
+ /**
+ * @brief Get the tile indexes which cover the given geographical box.
+ * If @p latLonBox or @p zoomLevel have values out-of-bounds, the behaviour is undefined.
+ *
+ * @param latLonBox the geo coordinates of the requested tiles
+ * @param zoomLevel the zoomlevel of the requested tiles
+ * @param westX the x index of the tiles covering the western boundary is returned through this parameter
+ * @param northY the y index of the tiles covering the northern boundary is returned through this parameter
+ * @param eastX the x index of the tiles covering the eastern boundary is returned through this parameter
+ * @param southY the y index of the tiles covering the southern boundary is returned through this parameter
+ */
+ virtual void tileIndexes(const GeoDataLatLonBox& latLonBox, int zoomLevel,
+ int& westX, int& northY, int& eastX, int& southY) const = 0;
+
+ /**
+ * @brief Get the north-west geo coordinates corresponding to a tile.
+ * If @p x, @p y or @p zoomLevel have values out-of-bounds, the behaviour is undefined.
+ *
+ * @param zoomLevel the zoomlevel of the tile
+ * @param x the x index of the tile
+ * @param y the y index of the tile
+ * @param westernTileEdgeLon the longitude angle in radians of the western tile edge tis returned through this parameter
+ * @param northernTileEdgeLat the latitude angle in radians of the northern tile edge is returned through this parameter
+ */
+ virtual void geoCoordinates(int zoomLevel,
+ int x, int y,
+ qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const = 0;
+
+ /**
+ * @brief Get the boundary geo coordinates corresponding to a tile.
+ * If @p x, @p y or @p zoomLevel have values out-of-bounds, the behaviour is undefined.
+ *
+ * @param zoomLevel the zoomlevel of the tile
+ * @param x the x index of the tile
+ * @param y the y index of the tile
+ * @param latLonBox the boundary geo coordinates are set to this GeoDataLatLonBox
+ */
+ virtual void geoCoordinates(int zoomLevel,
+ int x, int y,
+ GeoDataLatLonBox& latLonBox) const = 0;
+
+ /**
+ * @brief Get the boundary geo coordinates corresponding to a tile.
+ * If @p tildId has values out-of-bounds, the behaviour is undefined.
+ *
+ * @param tileId the id of the tile
+ * @param latLonBox the boundary geo coordinates are set to this GeoDataLatLonBox
+ */
+ void geoCoordinates(const TileId& tileId,
+ GeoDataLatLonBox& latLonBox) const
+ {
+ geoCoordinates(tileId.zoomLevel(), tileId.x(), tileId.y(), latLonBox);
+ }
+
+ private:
+ Q_DISABLE_COPY(GeoSceneAbstractTileProjection)
+ const QScopedPointer<GeoSceneAbstractTileProjectionPrivate> d_ptr;
+};
+
+}
+
+#endif
diff --git a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp
new file mode 100644
index 0000000..182b1fc
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp
@@ -0,0 +1,161 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "GeoSceneEquirectTileProjection.h"
+
+#include <GeoDataLatLonBox.h>
+#include <MarbleMath.h>
+
+namespace Marble
+{
+
+GeoSceneEquirectTileProjection::GeoSceneEquirectTileProjection()
+{
+}
+
+
+GeoSceneEquirectTileProjection::~GeoSceneEquirectTileProjection()
+{
+}
+
+GeoSceneAbstractTileProjection::Type GeoSceneEquirectTileProjection::type() const
+{
+ return Equirectangular;
+}
+
+
+static inline
+unsigned int lowerBoundTileIndex(qreal baseTileIndex)
+{
+ const qreal floorBaseTileIndex = floor(baseTileIndex);
+ unsigned int tileIndex = static_cast<unsigned int>(floorBaseTileIndex);
+ return (baseTileIndex == floorBaseTileIndex) ? tileIndex-1 : tileIndex;
+}
+
+static inline
+unsigned int upperBoundTileIndex(qreal baseTileIndex)
+{
+ return (unsigned int)floor(baseTileIndex);
+}
+
+static inline
+qreal baseTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ return 0.5 * (lon / M_PI + 1.0) * tileCount;
+}
+
+static inline
+qreal baseTileYFromLat(qreal lat, unsigned int tileCount)
+{
+ return (0.5 - lat / M_PI) * tileCount;
+}
+
+
+// on tile borders selects the tile to the east
+static inline
+unsigned int eastBoundTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lon == M_PI) {
+ return 0;
+ }
+ return upperBoundTileIndex(baseTileXFromLon(lon, tileCount));
+}
+
+// on tile borders selects the tile to the west
+static inline
+unsigned int westBoundTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lon == -M_PI) {
+ return tileCount-1;
+ }
+ return lowerBoundTileIndex(baseTileXFromLon(lon, tileCount));
+}
+
+// on tile borders selects the tile to the south
+static inline
+unsigned int southBoundTileYFromLat(qreal lat, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lat == -M_PI*0.5) {
+ return 0;
+ }
+ return upperBoundTileIndex(baseTileYFromLat(lat, tileCount));
+}
+
+// on tile borders selects the tile to the north
+static inline
+unsigned int northBoundTileYFromLat(qreal lat, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lat == M_PI*0.5) {
+ return tileCount-1;
+ }
+ return lowerBoundTileIndex(baseTileYFromLat(lat, tileCount));
+}
+
+
+void GeoSceneEquirectTileProjection::tileIndexes(const GeoDataLatLonBox& latLonBox, int zoomLevel,
+ int& westX, int& northY, int& eastX, int& southY) const
+{
+ const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
+
+ westX = eastBoundTileXFromLon(latLonBox.west(), xTileCount);
+ eastX = westBoundTileXFromLon(latLonBox.east(), xTileCount);
+
+ const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
+
+ northY = southBoundTileYFromLat(latLonBox.north(), yTileCount);
+ southY = northBoundTileYFromLat(latLonBox.south(), yTileCount);
+}
+
+void GeoSceneEquirectTileProjection::geoCoordinates(int zoomLevel,
+ int x, int y,
+ qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const
+{
+ qreal radius = (1 << zoomLevel) * levelZeroColumns() / 2.0;
+
+ westernTileEdgeLon = (x - radius ) / radius * M_PI;
+
+ radius = (1 << zoomLevel) * levelZeroRows() / 2.0;
+
+ northernTileEdgeLat = (radius - y) / radius * M_PI / 2.0;
+}
+
+void GeoSceneEquirectTileProjection::geoCoordinates(int zoomLevel,
+ int x, int y,
+ GeoDataLatLonBox& latLonBox) const
+{
+ qreal radius = (1 << zoomLevel) * levelZeroColumns() / 2.0;
+
+ qreal lonLeft = (x - radius ) / radius * M_PI;
+ qreal lonRight = (x - radius + 1 ) / radius * M_PI;
+
+ radius = (1 << zoomLevel) * levelZeroRows() / 2.0;
+
+ qreal latTop = (radius - y) / radius * M_PI / 2.0;
+ qreal latBottom = (radius - y - 1) / radius * M_PI / 2.0;
+
+ latLonBox.setBoundaries(latTop, latBottom, lonRight, lonLeft);
+}
+
+}
diff --git a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h
new file mode 100644
index 0000000..cbd2bbd
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h
@@ -0,0 +1,85 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef MARBLE_GEOSCENEEQUIRECTTILEPROJECTION_H
+#define MARBLE_GEOSCENEEQUIRECTTILEPROJECTION_H
+
+#include "GeoSceneAbstractTileProjection.h"
+
+namespace Marble
+{
+
+/**
+ * Convertes the x and y indizes of tiles to and from geo coordinates.
+ * For tiles of maps in Equirectangular projection.
+ *
+ * Tiles do have the same width and the same height per zoomlevel.
+ * The number of tiles per dimension is twice that of the previous lower zoomlevel.
+ * The indexing is done in x dimension eastwards, with the first tiles beginning at -180 degree
+ * and an x value of 0 and the last tiles ending at +180 degree,
+ * in y dimension southwards with the first tiles beginning at +90 degree and a y value of 0
+ * and the last tiles ending at -90 degree.
+ */
+class GEODATA_EXPORT GeoSceneEquirectTileProjection : public GeoSceneAbstractTileProjection
+{
+public:
+ /**
+ * @brief Construct a new GeoSceneEquirectTileProjection.
+ */
+ GeoSceneEquirectTileProjection();
+
+ ~GeoSceneEquirectTileProjection() override;
+
+public:
+ /**
+ * @copydoc
+ */
+ GeoSceneAbstractTileProjection::Type type() const override;
+
+ /**
+ * @copydoc
+ */
+ void tileIndexes(const GeoDataLatLonBox& latLonBox, int zoomLevel,
+ int& westX, int& northY, int& eastX, int& southY) const override;
+
+ /**
+ * @copydoc
+ */
+ void geoCoordinates(int zoomLevel,
+ int x, int y,
+ qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const override;
+
+ /**
+ * @copydoc
+ */
+ void geoCoordinates(int zoomLevel,
+ int x, int y,
+ GeoDataLatLonBox& latLonBox) const override;
+
+ using GeoSceneAbstractTileProjection::geoCoordinates;
+
+private:
+ Q_DISABLE_COPY(GeoSceneEquirectTileProjection)
+};
+
+}
+
+#endif
diff --git a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp
new file mode 100644
index 0000000..2a107b7
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp
@@ -0,0 +1,182 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "GeoSceneMercatorTileProjection.h"
+
+#include <GeoDataLatLonBox.h>
+#include <MarbleMath.h>
+
+
+namespace Marble
+{
+
+GeoSceneMercatorTileProjection::GeoSceneMercatorTileProjection()
+{
+}
+
+
+GeoSceneMercatorTileProjection::~GeoSceneMercatorTileProjection()
+{
+}
+
+GeoSceneAbstractTileProjection::Type GeoSceneMercatorTileProjection::type() const
+{
+ return Mercator;
+}
+
+
+static inline
+unsigned int lowerBoundTileIndex(qreal baseTileIndex)
+{
+ const qreal floorBaseTileIndex = floor(baseTileIndex);
+ unsigned int tileIndex = static_cast<unsigned int>(floorBaseTileIndex);
+ return (baseTileIndex == floorBaseTileIndex) ? tileIndex-1 : tileIndex;
+}
+
+static inline
+unsigned int upperBoundTileIndex(qreal baseTileIndex)
+{
+ return (unsigned int)floor(baseTileIndex);
+}
+
+static inline
+qreal baseTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ return 0.5 * (lon / M_PI + 1.0) * tileCount;
+}
+
+static inline
+qreal baseTileYFromLat(qreal latitude, unsigned int tileCount)
+{
+ // We need to calculate the tile position from the latitude
+ // projected using the Mercator projection. This requires the inverse Gudermannian
+ // function which is only defined between -85°S and 85°N. Therefore in order to
+ // prevent undefined results we need to restrict our calculation.
+ // Using 85.0 instead of some more correct 85.05113, to avoid running into NaN issues.
+ qreal maxAbsLat = 85.0 * DEG2RAD;
+ qreal lat = (qAbs(latitude) > maxAbsLat) ? latitude/qAbs(latitude) * maxAbsLat : latitude;
+ return (0.5 * (1.0 - gdInv(lat) / M_PI) * tileCount);
+}
+
+// on tile borders selects the tile to the east
+static inline
+unsigned int eastBoundTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lon == M_PI) {
+ return 0;
+ }
+ return upperBoundTileIndex(baseTileXFromLon(lon, tileCount));
+}
+
+// on tile borders selects the tile to the west
+static inline
+unsigned int westBoundTileXFromLon(qreal lon, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lon == -M_PI) {
+ return tileCount-1;
+ }
+ return lowerBoundTileIndex(baseTileXFromLon(lon, tileCount));
+}
+
+// on tile borders selects the tile to the south
+static inline
+unsigned int southBoundTileYFromLat(qreal lat, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lat == -M_PI*0.5) {
+ // calculate with normal lat value
+ lat = M_PI * 0.5;
+ }
+ return upperBoundTileIndex(baseTileYFromLat(lat, tileCount));
+}
+
+// on tile borders selects the tile to the north
+static inline
+unsigned int northBoundTileYFromLat(qreal lat, unsigned int tileCount)
+{
+ // special casing tile-map end
+ if (lat == M_PI*0.5) {
+ // calculate with normal lat value
+ lat = - M_PI * 0.5;
+ }
+ return lowerBoundTileIndex(baseTileYFromLat(lat, tileCount));
+}
+
+
+static inline
+qreal lonFromTileX(unsigned int x, unsigned int tileCount)
+{
+ return ( (2*M_PI * x) / tileCount - M_PI );
+}
+
+static inline
+qreal latFromTileY(unsigned int y, unsigned int tileCount)
+{
+ return gd(M_PI * (1.0 - (2.0 * y) / tileCount));
+}
+
+
+void GeoSceneMercatorTileProjection::tileIndexes(const GeoDataLatLonBox& latLonBox, int zoomLevel,
+ int& westX, int& northY, int& eastX, int& southY) const
+{
+ const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
+
+ westX = eastBoundTileXFromLon(latLonBox.west(), xTileCount);
+ eastX = westBoundTileXFromLon(latLonBox.east(), xTileCount);
+
+ const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
+
+ northY = southBoundTileYFromLat(latLonBox.north(), yTileCount);
+ southY = northBoundTileYFromLat(latLonBox.south(), yTileCount);
+}
+
+void GeoSceneMercatorTileProjection::geoCoordinates(int zoomLevel,
+ int x, int y,
+ qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const
+{
+ const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
+ westernTileEdgeLon = lonFromTileX(x, xTileCount);
+
+ const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
+ northernTileEdgeLat = latFromTileY(y, yTileCount);
+}
+
+
+void GeoSceneMercatorTileProjection::geoCoordinates(int zoomLevel,
+ int x, int y,
+ GeoDataLatLonBox& latLonBox) const
+{
+ const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
+
+ const qreal west = lonFromTileX(x, xTileCount);
+ const qreal east = lonFromTileX(x + 1, xTileCount);
+
+ const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
+
+ const qreal north = latFromTileY(y, yTileCount);
+ const qreal south = latFromTileY(y + 1, yTileCount);
+
+ latLonBox.setBoundaries(north, south, east, west);
+}
+
+}
diff --git a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h
new file mode 100644
index 0000000..8dfe4ce
--- /dev/null
+++ b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h
@@ -0,0 +1,89 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef MARBLE_GEOSCENEMERCATORTILEPROJECTION_H
+#define MARBLE_GEOSCENEMERCATORTILEPROJECTION_H
+
+#include "GeoSceneAbstractTileProjection.h"
+
+namespace Marble
+{
+
+/**
+ * Convertes the x and y indizes of tiles to and from geo coordinates.
+ * For tiles of maps in Mercator projection.
+ *
+ * Tiles do have the same width and the same height per zoomlevel.
+ * The number of tiles per dimension is twice that of the previous lower zoomlevel.
+ * The indexing is done in x dimension eastwards, with the first tiles beginning at -180 degree
+ * and an x value of 0 and the last tiles ending at +180 degree,
+ * in y dimension southwards with the first tiles beginning at +85.05113 degree and a y value of 0
+ * and the last tiles ending at -85.05113 degree.
+ *
+ * NOTE: The method @c tileIndexes() handles any latitude value >= +85.0 degree as
+ * exactly +85.0 degree and any latitude value <= -85.0 as exactly -85.0 degree.
+ * So for higher zoomlevels the outermost tiles will be masked by that and not included in any results.
+ */
+class GEODATA_EXPORT GeoSceneMercatorTileProjection : public GeoSceneAbstractTileProjection
+{
+public:
+ /**
+ * @brief Construct a new GeoSceneMercatorTileProjection.
+ */
+ GeoSceneMercatorTileProjection();
+
+ ~GeoSceneMercatorTileProjection() override;
+
+public:
+ /**
+ * @copydoc
+ */
+ GeoSceneAbstractTileProjection::Type type() const override;
+
+ /**
+ * @copydoc
+ */
+ void tileIndexes(const GeoDataLatLonBox& latLonBox, int zoomLevel,
+ int& westX, int& northY, int& eastX, int& southY) const override;
+
+ /**
+ * @copydoc
+ */
+ void geoCoordinates(int zoomLevel,
+ int x, int y,
+ qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const override;
+
+ /**
+ * @copydoc
+ */
+ void geoCoordinates(int zoomLevel,
+ int x, int y,
+ GeoDataLatLonBox& latLonBox) const override;
+
+ using GeoSceneAbstractTileProjection::geoCoordinates;
+
+private:
+ Q_DISABLE_COPY(GeoSceneMercatorTileProjection)
+};
+
+}
+
+#endif
diff --git a/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp b/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp
index 8b1767b..4edd31b 100644
--- a/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp
+++ b/src/lib/marble/geodata/scene/GeoSceneTileDataset.cpp
@@ -14,6 +14,8 @@
#include "GeoSceneTileDataset.h"
#include "GeoSceneTypes.h"
+#include "GeoSceneEquirectTileProjection.h"
+#include "GeoSceneMercatorTileProjection.h"
#include "DownloadPolicy.h"
#include "MarbleDebug.h"
@@ -37,17 +39,20 @@ GeoSceneTileDataset::GeoSceneTileDataset( const QString& name )
m_levelZeroRows( defaultLevelZeroRows ),
m_minimumTileLevel(0),
m_maximumTileLevel( -1 ),
- m_projection( Equirectangular ),
+ m_tileProjection(new GeoSceneEquirectTileProjection()),
m_blending(),
m_downloadUrls(),
m_nextUrl( m_downloadUrls.constEnd() )
{
+ m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
+ m_tileProjection->setLevelZeroRows(m_levelZeroRows);
}
GeoSceneTileDataset::~GeoSceneTileDataset()
{
qDeleteAll( m_downloadPolicies );
delete m_serverLayout;
+ delete m_tileProjection;
}
const char* GeoSceneTileDataset::nodeType() const
@@ -104,6 +109,7 @@ int GeoSceneTileDataset::levelZeroColumns() const
void GeoSceneTileDataset::setLevelZeroColumns( const int columns )
{
m_levelZeroColumns = columns;
+ m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
}
int GeoSceneTileDataset::levelZeroRows() const
@@ -114,6 +120,7 @@ int GeoSceneTileDataset::levelZeroRows() const
void GeoSceneTileDataset::setLevelZeroRows( const int rows )
{
m_levelZeroRows = rows;
+ m_tileProjection->setLevelZeroRows(m_levelZeroRows);
}
int GeoSceneTileDataset::maximumTileLevel() const
@@ -208,14 +215,31 @@ void GeoSceneTileDataset::setTileSize( const QSize &tileSize )
}
}
-GeoSceneTileDataset::Projection GeoSceneTileDataset::projection() const
+void GeoSceneTileDataset::setTileProjection(GeoSceneAbstractTileProjection::Type projectionType)
{
- return m_projection;
+ if (m_tileProjection->type() == projectionType) {
+ return;
+ }
+
+ delete m_tileProjection;
+ if (projectionType == GeoSceneAbstractTileProjection::Mercator) {
+ m_tileProjection = new GeoSceneMercatorTileProjection();
+ } else {
+ m_tileProjection = new GeoSceneEquirectTileProjection();
+ }
+
+ m_tileProjection->setLevelZeroColumns(m_levelZeroColumns);
+ m_tileProjection->setLevelZeroRows(m_levelZeroRows);
+}
+
+const GeoSceneAbstractTileProjection * GeoSceneTileDataset::tileProjection() const
+{
+ return m_tileProjection;
}
-void GeoSceneTileDataset::setProjection( const Projection projection )
+GeoSceneAbstractTileProjection::Type GeoSceneTileDataset::tileProjectionType() const
{
- m_projection = projection;
+ return m_tileProjection->type();
}
// Even though this method changes the internal state, it may be const
diff --git a/src/lib/marble/geodata/scene/GeoSceneTileDataset.h b/src/lib/marble/geodata/scene/GeoSceneTileDataset.h
index 586d806..d838c50 100644
--- a/src/lib/marble/geodata/scene/GeoSceneTileDataset.h
+++ b/src/lib/marble/geodata/scene/GeoSceneTileDataset.h
@@ -20,6 +20,7 @@
#include <QSize>
#include "GeoSceneAbstractDataset.h"
+#include "GeoSceneAbstractTileProjection.h"
#include "MarbleGlobal.h"
class QStringList;
@@ -44,7 +45,6 @@ class GEODATA_EXPORT GeoSceneTileDataset : public GeoSceneAbstractDataset
{
public:
enum StorageLayout { Marble, OpenStreetMap, TileMapService };
- enum Projection { Equirectangular, Mercator };
explicit GeoSceneTileDataset( const QString& name );
~GeoSceneTileDataset();
@@ -83,8 +83,10 @@ class GEODATA_EXPORT GeoSceneTileDataset : public GeoSceneAbstractDataset
const QSize tileSize() const;
void setTileSize( const QSize &tileSize );
- Projection projection() const;
- void setProjection( const Projection );
+ void setTileProjection(GeoSceneAbstractTileProjection::Type projectionType);
+
+ const GeoSceneAbstractTileProjection * tileProjection() const;
+ GeoSceneAbstractTileProjection::Type tileProjectionType() const;
QString blending() const;
void setBlending( const QString &name );
@@ -119,7 +121,7 @@ class GEODATA_EXPORT GeoSceneTileDataset : public GeoSceneAbstractDataset
int m_maximumTileLevel;
QVector<int> m_tileLevels;
mutable QSize m_tileSize;
- Projection m_projection;
+ GeoSceneAbstractTileProjection *m_tileProjection;
QString m_blending;
/// List of Urls which are used in a round robin fashion
diff --git a/src/lib/marble/geodata/writers/dgml/DgmlTextureTagWriter.cpp b/src/lib/marble/geodata/writers/dgml/DgmlTextureTagWriter.cpp
index 8bc786c..3e82a57 100644
--- a/src/lib/marble/geodata/writers/dgml/DgmlTextureTagWriter.cpp
+++ b/src/lib/marble/geodata/writers/dgml/DgmlTextureTagWriter.cpp
@@ -89,9 +89,10 @@ bool DgmlTextureTagWriter::write(const GeoNode *node, GeoWriter& writer) const
}
writer.writeStartElement( dgml::dgmlTag_Projection );
- if( texture->projection() == GeoSceneTileDataset::Mercator ) {
+ const GeoSceneAbstractTileProjection::Type tileProjectionType = texture->tileProjectionType();
+ if (tileProjectionType == GeoSceneAbstractTileProjection::Mercator) {
writer.writeAttribute( "name", "Mercator" );
- } else if ( texture->projection() == GeoSceneTileDataset::Equirectangular ) {
+ } else if (tileProjectionType == GeoSceneAbstractTileProjection::Equirectangular) {
writer.writeAttribute( "name", "Equirectangular" );
}
writer.writeEndElement();
diff --git a/src/lib/marble/layers/TextureLayer.cpp b/src/lib/marble/layers/TextureLayer.cpp
index d038b1d..1b83f36 100644
--- a/src/lib/marble/layers/TextureLayer.cpp
+++ b/src/lib/marble/layers/TextureLayer.cpp
@@ -412,7 +412,7 @@ void TextureLayer::setProjection( Projection projection )
d->m_texmapper = new EquirectScanlineTextureMapper( &d->m_tileLoader );
break;
case Mercator:
- if ( d->m_textures.at(0)->projection() == GeoSceneTileDataset::Mercator ) {
+ if (d->m_textures.at(0)->tileProjectionType() == GeoSceneAbstractTileProjection::Mercator) {
d->m_texmapper = new TileScalingTextureMapper( &d->m_tileLoader );
} else {
d->m_texmapper = new MercatorScanlineTextureMapper( &d->m_tileLoader );
@@ -499,9 +499,9 @@ QSize TextureLayer::tileSize() const
return d->m_layerDecorator.tileSize();
}
-GeoSceneTileDataset::Projection TextureLayer::tileProjection() const
+GeoSceneAbstractTileProjection::Type TextureLayer::tileProjectionType() const
{
- return d->m_layerDecorator.tileProjection();
+ return d->m_layerDecorator.tileProjectionType();
}
int TextureLayer::tileColumnCount( int level ) const
diff --git a/src/lib/marble/layers/TextureLayer.h b/src/lib/marble/layers/TextureLayer.h
index 6912868..caba1c3 100644
--- a/src/lib/marble/layers/TextureLayer.h
+++ b/src/lib/marble/layers/TextureLayer.h
@@ -79,7 +79,7 @@ class MARBLE_EXPORT TextureLayer : public QObject, public LayerInterface
QSize tileSize() const;
- GeoSceneTileDataset::Projection tileProjection() const;
+ GeoSceneAbstractTileProjection::Type tileProjectionType() const;
int tileColumnCount( int level ) const;
int tileRowCount( int level ) const;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d0db4d6..32c034c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -109,6 +109,7 @@ marble_add_test( TestTimeSpan )
marble_add_test( TestEquality )
marble_add_test( TestFeatureDetach )
marble_add_test( TestGeometryDetach )
+marble_add_test( TestTileProjection )
qt_add_resources(TestGeoDataCopy_SRCS TestGeoDataCopy.qrc) # Check copy operations on CoW classes
marble_add_test( TestGeoDataCopy ${TestGeoDataCopy_SRCS} )
diff --git a/tests/TestGeoSceneWriter.cpp b/tests/TestGeoSceneWriter.cpp
index 4649107..d161d5b 100644
--- a/tests/TestGeoSceneWriter.cpp
+++ b/tests/TestGeoSceneWriter.cpp
@@ -219,7 +219,7 @@ void TestGeoSceneWriter::writeHeadTag()
GeoSceneTileDataset* texture = new GeoSceneTileDataset( "map" );
texture->setSourceDir( "earth/testmap" );
texture->setFileFormat( "png" );
- texture->setProjection( GeoSceneTileDataset::Equirectangular );
+ texture->setTileProjection(GeoSceneAbstractTileProjection::Equirectangular);
texture->addDownloadUrl( QUrl( "http://download.kde.org/marble/map/{x}/{y}/{zoomLevel}" ) );
texture->addDownloadUrl( QUrl( "http://download.google.com/marble/map/{x}/{y}/{zoomLevel}" ) );
texture->addDownloadPolicy( DownloadBrowse, 20 );
diff --git a/tests/TestTileProjection.cpp b/tests/TestTileProjection.cpp
new file mode 100644
index 0000000..19f27bd
--- /dev/null
+++ b/tests/TestTileProjection.cpp
@@ -0,0 +1,540 @@
+/*
+ Copyright 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
+
+ This file is part of the KDE project
+
+ This library is free software you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "TestUtils.h"
+
+#include <GeoSceneEquirectTileProjection.h>
+#include <GeoSceneMercatorTileProjection.h>
+#include <GeoDataLatLonBox.h>
+#include <TileId.h>
+
+
+namespace Marble
+{
+
+class TileProjectionTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testTypeEquirect();
+ void testTypeMercator();
+
+ void testLevelZeroColumnsRowsEquirect();
+ void testLevelZeroColumnsRowsMercator();
+
+ void testTileIndexesEquirect_data();
+ void testTileIndexesEquirect();
+ void testTileIndexesMercator_data();
+ void testTileIndexesMercator();
+
+ void testGeoCoordinatesEquirect_data();
+ void testGeoCoordinatesEquirect();
+ void testGeoCoordinatesMercator_data();
+ void testGeoCoordinatesMercator();
+
+private:
+ void testLevelZeroColumnsRows(GeoSceneAbstractTileProjection& projection);
+};
+
+
+void TileProjectionTest::testLevelZeroColumnsRows(GeoSceneAbstractTileProjection& projection)
+{
+ // test default
+ QCOMPARE(projection.levelZeroColumns(), 1);
+ QCOMPARE(projection.levelZeroRows(), 1);
+
+ // test setting a different value
+ const int levelZeroColumns = 4;
+ const int levelZeroRows = 6;
+
+ projection.setLevelZeroColumns(levelZeroColumns);
+ projection.setLevelZeroRows(levelZeroRows);
+
+ QCOMPARE(projection.levelZeroColumns(), levelZeroColumns);
+ QCOMPARE(projection.levelZeroRows(), levelZeroRows);
+}
+
+void TileProjectionTest::testLevelZeroColumnsRowsEquirect()
+{
+ GeoSceneEquirectTileProjection projection;
+ testLevelZeroColumnsRows(projection);
+}
+
+void TileProjectionTest::testLevelZeroColumnsRowsMercator()
+{
+ GeoSceneMercatorTileProjection projection;
+ testLevelZeroColumnsRows(projection);
+}
+
+void TileProjectionTest::testTypeEquirect()
+{
+ GeoSceneEquirectTileProjection projection;
+ QCOMPARE(projection.type(), GeoSceneAbstractTileProjection::Equirectangular);
+}
+
+void TileProjectionTest::testTypeMercator()
+{
+ GeoSceneMercatorTileProjection projection;
+ QCOMPARE(projection.type(), GeoSceneAbstractTileProjection::Mercator);
+}
+
+
+void TileProjectionTest::testTileIndexesEquirect_data()
+{
+ QTest::addColumn<qreal>("westLon");
+ QTest::addColumn<qreal>("northLat");
+ QTest::addColumn<qreal>("eastLon");
+ QTest::addColumn<qreal>("southLat");
+ QTest::addColumn<int>("zoomLevel");
+ QTest::addColumn<int>("expectedTileXWest");
+ QTest::addColumn<int>("expectedTileYNorth");
+ QTest::addColumn<int>("expectedTileXEast");
+ QTest::addColumn<int>("expectedTileYSouth");
+
+ // zoomlevel zero: 1 tile
+ // bounds matching the tile map
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds inside the 1 tile
+ addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds west and north on tile map borders, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds west and north on tile map borders, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+
+ // zoomlevel 1: 2 tiles per dimension
+ // bounds matching the tile map
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 1
+ << 0 << 0 << 1 << 1;
+ // bounds inside the 4 tiles
+ addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 1
+ << 0 << 0 << 1 << 1;
+ // bounds matching the most north-west tile, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(0) << qreal(0)
+ << 1
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most north-west tile, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(0) << qreal(0)
+ << 1
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most south-east tile, with normal border values
+ addRow() << qreal(0) << qreal(0)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 1
+ << 1 << 1 << 1 << 1;
+ // bounds matching the most south-east tile, with border values from other map border sides
+ addRow() << qreal(0) << qreal(0)
+ << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << 1
+ << 1 << 1 << 1 << 1;
+
+ // zoomlevel 9: 2^8==512 tiles per dimension
+ // bounds matching the tile map
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 9
+ << 0 << 0 << 511 << 511;
+ // bounds inside the outer tiles
+ addRow() << qreal(-M_PI*(511/512.0)) << qreal(+M_PI * 0.5 * (511/512.0))
+ << qreal(+M_PI*(511/512.0)) << qreal(-M_PI * 0.5 * (511/512.0))
+ << 9
+ << 0 << 0 << 511 << 511;
+ // bounds matching the most north-west tile, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0))
+ << 9
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most north-west tile, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0))
+ << 9
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most south-east tile, with normal border values
+ addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0))
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 9
+ << 511 << 511 << 511 << 511;
+ // bounds matching the most south-east tile, with border values from other map border sides
+ addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0))
+ << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << 9
+ << 511 << 511 << 511 << 511;
+}
+
+
+void TileProjectionTest::testTileIndexesEquirect()
+{
+ QFETCH(qreal, westLon);
+ QFETCH(qreal, northLat);
+ QFETCH(qreal, eastLon);
+ QFETCH(qreal, southLat);
+ QFETCH(int, zoomLevel);
+ QFETCH(int, expectedTileXWest);
+ QFETCH(int, expectedTileYNorth);
+ QFETCH(int, expectedTileXEast);
+ QFETCH(int, expectedTileYSouth);
+
+ GeoDataLatLonBox latLonBox(northLat, southLat, eastLon, westLon);
+
+ GeoSceneEquirectTileProjection projection;
+
+ int tileXWest;
+ int tileYNorth;
+ int tileXEast;
+ int tileYSouth;
+
+ projection.tileIndexes(latLonBox, zoomLevel, tileXWest, tileYNorth, tileXEast, tileYSouth);
+
+ QCOMPARE(tileXWest, expectedTileXWest);
+ QCOMPARE(tileYNorth, expectedTileYNorth);
+ QCOMPARE(tileXEast, expectedTileXEast);
+ QCOMPARE(tileYSouth, expectedTileYSouth);
+}
+
+
+void TileProjectionTest::testTileIndexesMercator_data()
+{
+ QTest::addColumn<qreal>("westLon");
+ QTest::addColumn<qreal>("northLat");
+ QTest::addColumn<qreal>("eastLon");
+ QTest::addColumn<qreal>("southLat");
+ QTest::addColumn<int>("zoomLevel");
+ QTest::addColumn<int>("expectedTileXWest");
+ QTest::addColumn<int>("expectedTileYNorth");
+ QTest::addColumn<int>("expectedTileXEast");
+ QTest::addColumn<int>("expectedTileYSouth");
+
+ // zoomlevel zero: 1 tile
+ // bounds matching the tile map up to 90 degree latitude
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds matching the tile map with 85 degree latitude limit
+ addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD)
+ << qreal(+M_PI) << qreal(-85.0 * DEG2RAD)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds inside the 1 tile
+ addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds west and north on tile map borders, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+ // bounds west and north on tile map borders, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 0
+ << 0 << 0 << 0 << 0;
+
+ // zoomlevel 1: 2 tiles per dimension
+ // bounds matching the tile map up to 90 degree latitude
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 1
+ << 0 << 0 << 1 << 1;
+ // bounds matching the tile map with 85 degree latitude limit
+ addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD)
+ << qreal(+M_PI) << qreal(-85.0 * DEG2RAD)
+ << 1
+ << 0 << 0 << 1 << 1;
+ // bounds inside the 4 tiles
+ addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25)
+ << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25)
+ << 1
+ << 0 << 0 << 1 << 1;
+ // bounds matching the most north-west tile, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(0) << qreal(0)
+ << 1
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most north-west tile, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(0) << qreal(0)
+ << 1
+ << 0 << 0 << 0 << 0;
+ // bounds matching the most south-east tile, with normal border values
+ addRow() << qreal(0) << qreal(0)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 1
+ << 1 << 1 << 1 << 1;
+ // bounds matching the most south-east tile, with border values from other map border sides
+ addRow() << qreal(0) << qreal(0)
+ << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << 1
+ << 1 << 1 << 1 << 1;
+
+ // zoomlevel 9: 2^8==512 tiles per dimension
+ // GeoSceneMercatorTileProjection bounds latitude value at +/- 85.0 degree (so not at 85.05113),
+ // which results in some tiles missed at the outer sides.
+ // bounds matching the tile map up to 90 degree latitude
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 9
+ << 0 << 5 << 511 << 506;
+ // bounds matching the tile map with 85 degree latitude limit
+ addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD)
+ << qreal(+M_PI) << qreal(-85.0 * DEG2RAD)
+ << 9
+ << 0 << 5 << 511 << 506;
+ // bounds inside the outer tiles
+ addRow() << qreal(-M_PI*(511/512.0)) << qreal(+M_PI * 0.5 * (511/512.0))
+ << qreal(+M_PI*(511/512.0)) << qreal(-M_PI * 0.5 * (511/512.0))
+ << 9
+ << 0 << 5 << 511 << 506;
+ // bounds matching the most north-west tile, with normal border values
+ addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0))
+ << 9
+ << 0 << 5 << 0 << 5;
+ // bounds matching the most north-west tile, with border values from other map border sides
+ addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0))
+ << 9
+ << 0 << 5 << 0 << 5;
+ // bounds matching the most south-east tile, with normal border values
+ addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0))
+ << qreal(+M_PI) << qreal(-M_PI * 0.5)
+ << 9
+ << 511 << 506 << 511 << 506;
+ // bounds matching the most south-east tile, with border values from other map border sides
+ addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0))
+ << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << 9
+ << 511 << 506 << 511 << 506;
+}
+
+
+void TileProjectionTest::testTileIndexesMercator()
+{
+ QFETCH(qreal, westLon);
+ QFETCH(qreal, northLat);
+ QFETCH(qreal, eastLon);
+ QFETCH(qreal, southLat);
+ QFETCH(int, zoomLevel);
+ QFETCH(int, expectedTileXWest);
+ QFETCH(int, expectedTileYNorth);
+ QFETCH(int, expectedTileXEast);
+ QFETCH(int, expectedTileYSouth);
+
+ GeoDataLatLonBox latLonBox(northLat, southLat, eastLon, westLon);
+
+ GeoSceneMercatorTileProjection projection;
+
+ int tileXWest;
+ int tileYNorth;
+ int tileXEast;
+ int tileYSouth;
+
+ projection.tileIndexes(latLonBox, zoomLevel, tileXWest, tileYNorth, tileXEast, tileYSouth);
+
+ QCOMPARE(tileXWest, expectedTileXWest);
+ QCOMPARE(tileYNorth, expectedTileYNorth);
+ QCOMPARE(tileXEast, expectedTileXEast);
+ QCOMPARE(tileYSouth, expectedTileYSouth);
+}
+
+
+void TileProjectionTest::testGeoCoordinatesEquirect_data()
+{
+ QTest::addColumn<int>("tileX");
+ QTest::addColumn<int>("tileY");
+ QTest::addColumn<int>("zoomLevel");
+ QTest::addColumn<qreal>("expectedWesternTileEdgeLon");
+ QTest::addColumn<qreal>("expectedNorthernTileEdgeLat");
+ QTest::addColumn<qreal>("expectedEasternTileEdgeLon");
+ QTest::addColumn<qreal>("expectedSouthernTileEdgeLat");
+
+ // zoomlevel zero: 1 tile
+ addRow() << 0 << 0 << 0 << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5);
+
+ // zoomlevel 1: 2 tiles per dimension
+ addRow() << 0 << 0 << 1 << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(0) << qreal(0);
+ addRow() << 0 << 1 << 1 << qreal(-M_PI) << qreal(0)
+ << qreal(0) << qreal(-M_PI * 0.5);
+ addRow() << 1 << 0 << 1 << qreal(0) << qreal(+M_PI * 0.5)
+ << qreal(+M_PI) << qreal(0);
+ addRow() << 1 << 1 << 1 << qreal(0) << qreal(0)
+ << qreal(+M_PI) << qreal(-M_PI * 0.5);
+
+ // zoomlevel 9: 2^8==512 tiles per dimension
+ addRow() << 0 << 0 << 9 << qreal(-M_PI) << qreal(+M_PI * 0.5)
+ << qreal(-M_PI * (255/256.0)) << qreal(+M_PI * 0.5 * (255/256.0));
+ addRow() << 0 << 256 << 9 << qreal(-M_PI) << qreal(0)
+ << qreal(-M_PI * (255/256.0)) << qreal(-M_PI * 0.5 * (1/256.0));
+ addRow() << 256 << 0 << 9 << qreal(0) << qreal(+M_PI * 0.5)
+ << qreal(M_PI * (1/256.0)) << qreal(+M_PI * 0.5 * (255/256.0));
+ addRow() << 511 << 511 << 9 << qreal(M_PI * (255/256.0)) << qreal(-M_PI * 0.5 * (255/256.0))
+ << qreal(+M_PI) << qreal(-M_PI * 0.5);
+}
+
+
+void TileProjectionTest::testGeoCoordinatesEquirect()
+{
+ QFETCH(int, tileX);
+ QFETCH(int, tileY);
+ QFETCH(int, zoomLevel);
+ QFETCH(qreal, expectedWesternTileEdgeLon);
+ QFETCH(qreal, expectedNorthernTileEdgeLat);
+ QFETCH(qreal, expectedEasternTileEdgeLon);
+ QFETCH(qreal, expectedSouthernTileEdgeLat);
+
+ GeoSceneEquirectTileProjection projection;
+
+ qreal westernTileEdgeLon;
+ qreal northernTileEdgeLat;
+
+ // method variant with zoomLevel, tileX, tileY
+ projection.geoCoordinates(zoomLevel, tileX, tileY, westernTileEdgeLon, northernTileEdgeLat);
+
+ QCOMPARE(westernTileEdgeLon, expectedWesternTileEdgeLon);
+ QCOMPARE(northernTileEdgeLat, expectedNorthernTileEdgeLat);
+
+ // method variants with GeoDataLatLonBox
+ GeoDataLatLonBox latLonBox;
+
+ projection.geoCoordinates(zoomLevel, tileX, tileY, latLonBox);
+
+ QCOMPARE(latLonBox.west(), expectedWesternTileEdgeLon);
+ QCOMPARE(latLonBox.north(), expectedNorthernTileEdgeLat);
+ QCOMPARE(latLonBox.east(), expectedEasternTileEdgeLon);
+ QCOMPARE(latLonBox.south(), expectedSouthernTileEdgeLat);
+
+ TileId tileId(QStringLiteral("testmap"), zoomLevel, tileX, tileY);
+ GeoDataLatLonBox latLonBox2;
+
+ projection.geoCoordinates(tileId, latLonBox2);
+
+ QCOMPARE(latLonBox2.west(), expectedWesternTileEdgeLon);
+ QCOMPARE(latLonBox2.north(), expectedNorthernTileEdgeLat);
+ QCOMPARE(latLonBox2.east(), expectedEasternTileEdgeLon);
+ QCOMPARE(latLonBox2.south(), expectedSouthernTileEdgeLat);
+}
+
+void TileProjectionTest::testGeoCoordinatesMercator_data()
+{
+ QTest::addColumn<int>("tileX");
+ QTest::addColumn<int>("tileY");
+ QTest::addColumn<int>("zoomLevel");
+ QTest::addColumn<qreal>("expectedWesternTileEdgeLon");
+ QTest::addColumn<qreal>("expectedNorthernTileEdgeLat");
+ QTest::addColumn<qreal>("expectedEasternTileEdgeLon");
+ QTest::addColumn<qreal>("expectedSouthernTileEdgeLat");
+
+ const qreal absMaxLat = DEG2RAD * 85.05113;
+
+ // zoomlevel zero: 1 tile
+ addRow() << 0 << 0 << 0 << qreal(-M_PI) << qreal(+absMaxLat)
+ << qreal(+M_PI) << qreal(-absMaxLat);
+
+ // zoomlevel 1: 2 tiles per dimension
+ addRow() << 0 << 0 << 1 << qreal(-M_PI) << qreal(+absMaxLat)
+ << qreal(0) << qreal(0);
+ addRow() << 0 << 1 << 1 << qreal(-M_PI) << qreal(0)
+ << qreal(0) << qreal(-absMaxLat);
+ addRow() << 1 << 0 << 1 << qreal(0) << qreal(+absMaxLat)
+ << qreal(+M_PI) << qreal(0);
+ addRow() << 1 << 1 << 1 << qreal(0) << qreal(0)
+ << qreal(+M_PI) << qreal(-absMaxLat);
+
+ // zoomlevel 9: 2^8==512 tiles per dimension
+ addRow() << 0 << 0 << 9 << qreal(-M_PI) << qreal(+absMaxLat)
+ << qreal(-M_PI * (255/256.0)) << qreal(+1.48336);
+ addRow() << 0 << 256 << 9 << qreal(-M_PI) << qreal(0)
+ << qreal(-M_PI * (255/256.0)) << qreal(-0.0122715);
+ addRow() << 256 << 0 << 9 << qreal(0) << qreal(+absMaxLat)
+ << qreal(M_PI * (1/256.0)) << qreal(+1.48336);
+ addRow() << 511 << 511 << 9 << qreal(M_PI * (255/256.0)) << qreal(-1.48336)
+ << qreal(+M_PI) << qreal(-absMaxLat);
+}
+
+
+void TileProjectionTest::testGeoCoordinatesMercator()
+{
+ QFETCH(int, tileX);
+ QFETCH(int, tileY);
+ QFETCH(int, zoomLevel);
+ QFETCH(qreal, expectedWesternTileEdgeLon);
+ QFETCH(qreal, expectedNorthernTileEdgeLat);
+ QFETCH(qreal, expectedEasternTileEdgeLon);
+ QFETCH(qreal, expectedSouthernTileEdgeLat);
+
+ GeoSceneMercatorTileProjection projection;
+
+ qreal westernTileEdgeLon;
+ qreal northernTileEdgeLat;
+
+ // method variant with zoomLevel, tileX, tileY
+ projection.geoCoordinates(zoomLevel, tileX, tileY, westernTileEdgeLon, northernTileEdgeLat);
+
+ QCOMPARE(westernTileEdgeLon, expectedWesternTileEdgeLon);
+ QFUZZYCOMPARE(northernTileEdgeLat, expectedNorthernTileEdgeLat, 0.00001);
+
+ // method variants with GeoDataLatLonBox
+ GeoDataLatLonBox latLonBox;
+
+ projection.geoCoordinates(zoomLevel, tileX, tileY, latLonBox);
+
+ QCOMPARE(latLonBox.west(), expectedWesternTileEdgeLon);
+ QFUZZYCOMPARE(latLonBox.north(), expectedNorthernTileEdgeLat, 0.00001);
+ QCOMPARE(latLonBox.east(), expectedEasternTileEdgeLon);
+ QFUZZYCOMPARE(latLonBox.south(), expectedSouthernTileEdgeLat, 0.00001);
+
+ TileId tileId(QStringLiteral("testmap"), zoomLevel, tileX, tileY);
+ GeoDataLatLonBox latLonBox2;
+
+ projection.geoCoordinates(tileId, latLonBox2);
+
+ QCOMPARE(latLonBox2.west(), expectedWesternTileEdgeLon);
+ QFUZZYCOMPARE(latLonBox2.north(), expectedNorthernTileEdgeLat, 0.00001);
+ QCOMPARE(latLonBox2.east(), expectedEasternTileEdgeLon);
+ QFUZZYCOMPARE(latLonBox2.south(), expectedSouthernTileEdgeLat, 0.00001);
+}
+
+} // namespace Marble
+
+QTEST_MAIN(Marble::TileProjectionTest)
+
+#include "TestTileProjection.moc"
diff --git a/tools/vectorosm-tilecreator/TileDirectory.cpp b/tools/vectorosm-tilecreator/TileDirectory.cpp
index 1edcf5e..c7141db 100644
--- a/tools/vectorosm-tilecreator/TileDirectory.cpp
+++ b/tools/vectorosm-tilecreator/TileDirectory.cpp
@@ -356,11 +356,14 @@ void TileDirectory::createTiles() const
QDir().mkpath(outputDir);
if (m_tileType == OpenStreetMap) {
QString const output = QString("-o=%1").arg(outputFile);
- int const N = pow(2, m_zoomLevel);
- double const minLon = TileId::tileX2lon(tileId.x(), N) * RAD2DEG;
- double const maxLon = TileId::tileX2lon(tileId.x()+1, N) * RAD2DEG;
- double const maxLat = TileId::tileY2lat(tileId.y(), N) * RAD2DEG;
- double const minLat = TileId::tileY2lat(tileId.y()+1, N) * RAD2DEG;
+
+ GeoDataLatLonBox tileBoundary;
+ m_tileProjection.geoCoordinates(m_zoomLevel, tileId.x(), tileId.y(), tileBoundary);
+
+ double const minLon = tileBoundary.west(GeoDataCoordinates::Degree);
+ double const maxLon = tileBoundary.east(GeoDataCoordinates::Degree);
+ double const maxLat = tileBoundary.north(GeoDataCoordinates::Degree);
+ double const minLat = tileBoundary.south(GeoDataCoordinates::Degree);
QString const bbox = QString("-b=%1,%2,%3,%4").arg(minLon).arg(minLat).arg(maxLon).arg(maxLat);
QProcess osmconvert;
osmconvert.start("osmconvert", QStringList() << "--drop-author" << "--drop-version"
@@ -387,11 +390,13 @@ void TileDirectory::createTiles() const
bool TileDirectory::contains(const TileId &tile) const
{
- int const N = pow(2, tile.zoomLevel());
- double const west = TileId::tileX2lon(tile.x(), N);
- double const east = TileId::tileX2lon(tile.x()+1, N);
- double const north = TileId::tileY2lat(tile.y(), N);
- double const south = TileId::tileY2lat(tile.y()+1, N);
+ GeoDataLatLonBox tileBoundary;
+ m_tileProjection.geoCoordinates(m_zoomLevel, tile.x(), tile.y(), tileBoundary);
+
+ double const west = tileBoundary.west();
+ double const east = tileBoundary.east();
+ double const north = tileBoundary.north();
+ double const south = tileBoundary.south();
QVector<GeoDataCoordinates> bounds;
bounds << GeoDataCoordinates(west, north);
bounds << GeoDataCoordinates(east, north);
diff --git a/tools/vectorosm-tilecreator/TileDirectory.h b/tools/vectorosm-tilecreator/TileDirectory.h
index 9f985e1..52de4d5 100644
--- a/tools/vectorosm-tilecreator/TileDirectory.h
+++ b/tools/vectorosm-tilecreator/TileDirectory.h
@@ -16,6 +16,7 @@
#include <TileId.h>
#include <GeoDataLinearRing.h>
#include <ParsingRunnerManager.h>
+#include <GeoSceneMercatorTileProjection.h>
#include <QNetworkAccessManager>
#include <QSharedPointer>
@@ -99,6 +100,7 @@ private:
GeoDataLatLonBox m_boundingBox;
QVector<GeoDataLinearRing> m_boundingPolygon;
QNetworkAccessManager m_downloadManager;
+ GeoSceneMercatorTileProjection m_tileProjection;
QString m_landmassFile;
QSharedPointer<Download> m_download;
int m_maxZoomLevel;
diff --git a/tools/vectorosm-tilecreator/TileIterator.cpp b/tools/vectorosm-tilecreator/TileIterator.cpp
index d3b9900..51bc857 100644
--- a/tools/vectorosm-tilecreator/TileIterator.cpp
+++ b/tools/vectorosm-tilecreator/TileIterator.cpp
@@ -9,8 +9,8 @@
//
#include "TileIterator.h"
-#include "TileId.h"
-#include "VectorTileModel.h"
+
+#include "GeoSceneMercatorTileProjection.h"
#include <QDebug>
@@ -45,13 +45,14 @@ TileIterator::const_iterator &TileIterator::operator++()
TileIterator::TileIterator(const GeoDataLatLonBox &latLonBox, int zoomLevel)
{
- qreal north, west, south, east;
- latLonBox.boundaries(north, south, east, west);
- unsigned int N = pow(2, zoomLevel);
- m_bounds.setLeft(TileId::lon2tileX(west, N));
- m_bounds.setTop(TileId::lat2tileY(north, N));
- m_bounds.setRight(qMin(N-1, TileId::lon2tileX(east, N)));
- m_bounds.setBottom(TileId::lat2tileY(south, N));
+ int westX, northY, eastX, southY;
+ GeoSceneMercatorTileProjection tileProjection;
+ tileProjection.tileIndexes(latLonBox, zoomLevel, westX, northY, eastX, southY);
+
+ m_bounds.setLeft(westX);
+ m_bounds.setTop(northY);
+ m_bounds.setRight(eastX);
+ m_bounds.setBottom(southY);
}
TileIterator::const_iterator TileIterator::begin() const
diff --git a/tools/vectorosm-tilecreator/VectorClipper.cpp b/tools/vectorosm-tilecreator/VectorClipper.cpp
index 3298297..da290b8 100644
--- a/tools/vectorosm-tilecreator/VectorClipper.cpp
+++ b/tools/vectorosm-tilecreator/VectorClipper.cpp
@@ -239,13 +239,8 @@ QVector<GeoDataPlacemark *> VectorClipper::potentialIntersections(const GeoDataL
GeoDataDocument *VectorClipper::clipTo(unsigned int zoomLevel, unsigned int tileX, unsigned int tileY)
{
- unsigned int N = pow(2, zoomLevel);
GeoDataLatLonBox tileBoundary;
- qreal north = TileId::tileY2lat(tileY, N);
- qreal south = TileId::tileY2lat(tileY+1, N);
- qreal west = TileId::tileX2lon(tileX, N);
- qreal east = TileId::tileX2lon(tileX+1, N);
- tileBoundary.setBoundaries(north, south, east, west);
+ m_tileProjection.geoCoordinates(zoomLevel, tileX, tileY, tileBoundary);
GeoDataDocument *tile = clipTo(tileBoundary);
QString tileName = QString("%1/%2/%3").arg(zoomLevel).arg(tileX).arg(tileY);
diff --git a/tools/vectorosm-tilecreator/VectorClipper.h b/tools/vectorosm-tilecreator/VectorClipper.h
index cd91614..fe7c6e3 100644
--- a/tools/vectorosm-tilecreator/VectorClipper.h
+++ b/tools/vectorosm-tilecreator/VectorClipper.h
@@ -17,6 +17,7 @@
#include <GeoDataLatLonBox.h>
#include "GeoDataPlacemark.h"
#include <TileId.h>
+#include <GeoSceneMercatorTileProjection.h>
#include "clipper/clipper.hpp"
@@ -82,6 +83,7 @@ private:
QMap<TileId, QVector<GeoDataPlacemark*> > m_items;
int m_maxZoomLevel;
+ GeoSceneMercatorTileProjection m_tileProjection;
};
}