summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Rahn <[email protected]>2016-06-14 22:40:00 +0200
committerTorsten Rahn <[email protected]>2016-06-14 22:42:40 +0200
commitea6e117329992b2f3b22d7736ea91b32e8a2f87d (patch)
treeb7141fe32614363c411b3ead67cf3ffee969a4db
parent0b65421af6f405607cb02b05c4d04bd2e8f140c0 (diff)
Fix VectorTileModel:
- Correct and simplify dateline handling - Simplify math - Correct tile loading behavior around level 0/1 - Prevent infinite/undefined results returned by the inverse Gudermannian method. - Better namings and more meaningful code documentation.
-rw-r--r--src/lib/marble/VectorTileModel.cpp73
-rw-r--r--src/lib/marble/VectorTileModel.h2
-rw-r--r--src/lib/marble/geodata/data/GeoDataLatLonBox.cpp10
3 files changed, 37 insertions, 48 deletions
diff --git a/src/lib/marble/VectorTileModel.cpp b/src/lib/marble/VectorTileModel.cpp
index 3d137e4..263e163 100644
--- a/src/lib/marble/VectorTileModel.cpp
+++ b/src/lib/marble/VectorTileModel.cpp
@@ -19,6 +19,7 @@
#include "MarbleGlobal.h"
#include "MarbleDebug.h"
#include "MathHelper.h"
+#include "MarbleMath.h"
#include "TileId.h"
#include "TileLoader.h"
@@ -67,7 +68,7 @@ VectorTileModel::VectorTileModel( TileLoader *loader, const GeoSceneVectorTileDa
connect(treeModel, SIGNAL(removed(GeoDataObject*)), this, SLOT(cleanupTile(GeoDataObject*)) );
}
-void VectorTileModel::setViewport( const GeoDataLatLonBox &bbox, int radius )
+void VectorTileModel::setViewport( const GeoDataLatLonBox &latLonBox, int radius )
{
// choose the smaller dimension for selecting the tile level, leading to higher-resolution results
const int levelZeroWidth = m_layer->tileSize().width() * m_layer->levelZeroColumns();
@@ -89,8 +90,12 @@ void VectorTileModel::setViewport( const GeoDataLatLonBox &bbox, int radius )
// roughly equals the global texture width
m_tileZoomLevel = tileZoomLevel;
+ // Determine available tile levels in the layer and thereby
+ // select the tileZoomLevel that is actually used:
QVector<int> tileLevels = m_layer->tileLevels();
- if (tileLevels.isEmpty() || tileZoomLevel < tileLevels.first()) {
+ if (tileLevels.isEmpty() /* || tileZoomLevel < tileLevels.first() */) {
+ // if there is no (matching) tile level then show nothing
+ // and bail out.
m_documents.clear();
return;
}
@@ -118,50 +123,22 @@ void VectorTileModel::setViewport( const GeoDataLatLonBox &bbox, int radius )
// 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 minX = qMin<unsigned int>( maxTileX,
- qMax<unsigned int>( lon2tileX( bbox.west(GeoDataCoordinates::Degree), maxTileX ),
- 0 ) );
-
- unsigned int minY = qMin<unsigned int>( maxTileY,
- qMax<unsigned int>( lat2tileY( bbox.north(GeoDataCoordinates::Degree), maxTileY ),
- 0 ) );
-
- unsigned int maxX = qMax<unsigned int>( 0,
- qMin<unsigned int>( lon2tileX( bbox.east(GeoDataCoordinates::Degree), maxTileX ),
- maxTileX ) );
-
- unsigned int maxY = qMax<unsigned int>( 0,
- qMin<unsigned int>( lat2tileY( bbox.south(GeoDataCoordinates::Degree), maxTileY ),
- maxTileY ) );
-
- bool left = minX < maxTileX;
- bool right = maxX > 0;
- bool up = minY < maxTileY;
- bool down = maxY > 0 ;
+ unsigned int westX = qBound<unsigned int>( 0, lon2tileX( latLonBox.west(), maxTileX ), maxTileX);
+ unsigned int northY = qBound<unsigned int>( 0, lat2tileY( latLonBox.north(), maxTileY ), maxTileY);
+ unsigned int eastX = qBound<unsigned int>( 0, lon2tileX( latLonBox.east(), maxTileX ), maxTileX);
+ unsigned int southY = qBound<unsigned int>( 0, lat2tileY( latLonBox.south(), maxTileY ), maxTileY );
// Download tiles and send them to VectorTileLayer
// When changing zoom, download everything inside the screen
- if ( left && right && up && down )
-
- setViewport( tileZoomLevel, minX, minY, maxX, maxY );
-
+ if ( !latLonBox.crossesDateLine() ) {
+ queryTiles( tileZoomLevel, westX, northY, eastX, southY );
+ }
// When only moving screen, just download the new tiles
- else if ( left || right || up || down ){
-
- if ( left )
- setViewport( tileZoomLevel, minX, maxTileY, maxTileX, 0 );
- if ( right )
- setViewport( tileZoomLevel, 0, maxTileY, maxX, 0 );
- if ( up )
- setViewport( tileZoomLevel, maxTileX, minY, 0, maxTileY );
- if ( down )
- setViewport( tileZoomLevel, maxTileX, 0, 0, maxY );
-
- // During testing discovered that this code above does not request the "corner" tiles
-
+ else {
+ queryTiles( tileZoomLevel, 0, northY, eastX, southY );
+ queryTiles( tileZoomLevel, westX, northY, maxTileX, southY );
}
-
- removeTilesOutOfView(bbox);
+ removeTilesOutOfView(latLonBox);
}
void VectorTileModel::removeTilesOutOfView(const GeoDataLatLonBox &boundingBox)
@@ -226,7 +203,7 @@ void VectorTileModel::clear()
m_documents.clear();
}
-void VectorTileModel::setViewport( int tileZoomLevel,
+void VectorTileModel::queryTiles( int tileZoomLevel,
unsigned int minTileX, unsigned int minTileY, unsigned int maxTileX, unsigned int maxTileY )
{
// Download all the tiles inside the given indexes
@@ -256,12 +233,18 @@ void VectorTileModel::cleanupTile(GeoDataObject *object)
unsigned int VectorTileModel::lon2tileX( qreal lon, unsigned int maxTileX )
{
- return (unsigned int)floor((lon + 180.0) / 360.0 * maxTileX);
+ return (unsigned int)floor(0.5 * (lon / M_PI + 1.0) * maxTileX);
}
-unsigned int VectorTileModel::lat2tileY( qreal lat, unsigned int maxTileY )
+unsigned int VectorTileModel::lat2tileY( qreal latitude, unsigned int maxTileY )
{
- return (unsigned int)floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * 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);
}
#include "moc_VectorTileModel.cpp"
diff --git a/src/lib/marble/VectorTileModel.h b/src/lib/marble/VectorTileModel.h
index de4dc06..7bf99db 100644
--- a/src/lib/marble/VectorTileModel.h
+++ b/src/lib/marble/VectorTileModel.h
@@ -80,7 +80,7 @@ private Q_SLOTS:
private:
void removeTilesOutOfView(const GeoDataLatLonBox &boundingBox);
- void setViewport( int tileZoomLevel, unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY );
+ void queryTiles( int tileZoomLevel, unsigned int minX, unsigned int minY, unsigned int maxX, unsigned int maxY );
static unsigned int lon2tileX( qreal lon, unsigned int maxTileX );
static unsigned int lat2tileY( qreal lat, unsigned int maxTileY );
diff --git a/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp b/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp
index f5a8471..ec57c5c 100644
--- a/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp
+++ b/src/lib/marble/geodata/data/GeoDataLatLonBox.cpp
@@ -242,8 +242,14 @@ void GeoDataLatLonBox::scale(qreal verticalFactor, qreal horizontalFactor) const
qreal const deltaX = 0.5 * width() * horizontalFactor;
d->m_north = GeoDataCoordinates::normalizeLat(middle.latitude() + deltaY);
d->m_south = GeoDataCoordinates::normalizeLat(middle.latitude() - deltaY);
- d->m_east = GeoDataCoordinates::normalizeLon(middle.longitude() + deltaX);
- d->m_west = GeoDataCoordinates::normalizeLon(middle.longitude() - deltaX);
+ if (deltaX > 180 * DEG2RAD) {
+ d->m_east = M_PI;
+ d->m_west = -M_PI;
+ }
+ else {
+ d->m_east = GeoDataCoordinates::normalizeLon(middle.longitude() + deltaX);
+ d->m_west = GeoDataCoordinates::normalizeLon(middle.longitude() - deltaX);
+ }
}
GeoDataLatLonBox GeoDataLatLonBox::scaled(qreal verticalFactor, qreal horizontalFactor) const