From 8e01dce85e562db5b9f2f7b54f6dc59759172d80 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Sun, 7 Apr 2013 15:58:45 +0200 Subject: [PATCH] -use absolute x/y position and scale in MapPosition - scale calculations look much nicer now, better always use 'double' unless you are sure about precision required - finally got rid of zoomLevel relative coordinates - cleanup MapPosition and MercatorProjection API functions --- src/org/oscim/core/MapPosition.java | 91 +++++---- src/org/oscim/core/MercatorProjection.java | 177 +++-------------- src/org/oscim/core/Tile.java | 11 -- .../oscim/database/mapfile/MapDatabase.java | 18 +- .../oscim/database/mapfile/Projection.java | 108 +++++++++++ .../mapfile/header/SubFileParameter.java | 11 +- src/org/oscim/generator/TileGenerator.java | 10 +- src/org/oscim/overlay/BuildingOverlay.java | 18 +- src/org/oscim/overlay/ItemizedOverlay.java | 42 ++-- src/org/oscim/overlay/PathOverlay.java | 26 ++- src/org/oscim/renderer/GLRenderer.java | 31 ++- src/org/oscim/renderer/LineRenderer.java | 25 +-- src/org/oscim/renderer/LineTexRenderer.java | 6 +- src/org/oscim/renderer/MapTile.java | 5 + src/org/oscim/renderer/PolygonRenderer.java | 10 +- src/org/oscim/renderer/TextureRenderer.java | 2 +- src/org/oscim/renderer/TileManager.java | 65 ++++--- src/org/oscim/renderer/TileRenderer.java | 37 ++-- .../oscim/renderer/overlays/BasicOverlay.java | 17 +- .../renderer/overlays/ExtrusionOverlay.java | 35 ++-- .../oscim/renderer/overlays/GridOverlay.java | 31 ++- .../renderer/overlays/RenderOverlay.java | 55 ++---- .../oscim/renderer/overlays/TextOverlay.java | 67 +++++-- src/org/oscim/view/Compass.java | 8 +- src/org/oscim/view/MapActivity.java | 33 ++-- src/org/oscim/view/MapScaleBar.java | 71 ++++--- src/org/oscim/view/MapView.java | 181 +++++++----------- src/org/oscim/view/MapViewPosition.java | 175 ++++++----------- src/org/oscim/view/TouchHandler.java | 2 +- 29 files changed, 658 insertions(+), 710 deletions(-) create mode 100644 src/org/oscim/database/mapfile/Projection.java diff --git a/src/org/oscim/core/MapPosition.java b/src/org/oscim/core/MapPosition.java index 7bf9ab82..11ca50f8 100644 --- a/src/org/oscim/core/MapPosition.java +++ b/src/org/oscim/core/MapPosition.java @@ -15,78 +15,73 @@ */ package org.oscim.core; +import org.oscim.utils.FastMath; + /** A MapPosition Container. */ public class MapPosition { + public final static int MAX_ZOOMLEVEL = 20; + public final static int MIN_ZOOMLEVEL = 2; - public double lon; - public double lat; + /** projected position x 0..1 */ + public double x; + /** projected position y 0..1 */ + public double y; + /** absolute scale */ + public double scale; - public double absX; - public double absY; - public double absScale; - - public float scale; + /** rotation angle */ public float angle; + /** perspective tile */ public float tilt; - // map center in tile coordinates of current zoom-level - public double x; - public double y; + // to be removed + // FastMath.log2((int) scale) public int zoomLevel; public MapPosition() { - this.zoomLevel = (byte) 1; this.scale = 1; - this.lat = 0; - this.lon = 0; + this.x = 0.5; + this.y = 0.5; + this.zoomLevel = 1; this.angle = 0; - this.x = MercatorProjection.longitudeToPixelX(this.lon, zoomLevel); - this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel); } - /** - * @param geoPoint - * the map position. - * @param zoomLevel - * the zoom level. - * @param scale - * ... - */ - public MapPosition(GeoPoint geoPoint, byte zoomLevel, float scale) { + public void setZoomLevel(int zoomLevel) { this.zoomLevel = zoomLevel; - this.scale = scale; - this.lat = geoPoint.getLatitude(); - this.lon = geoPoint.getLongitude(); - this.angle = 0; - this.x = MercatorProjection.longitudeToPixelX(this.lon, zoomLevel); - this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel); + this.scale = 1 << zoomLevel; } - public MapPosition(double latitude, double longitude, int zoomLevel, float scale, - float angle) { - this.zoomLevel = zoomLevel; + public void setScale(double scale) { + this.zoomLevel = FastMath.log2((int) scale); this.scale = scale; - this.lat = latitude; - this.lon = longitude; - this.angle = angle; - this.x = MercatorProjection.longitudeToPixelX(longitude, zoomLevel); - this.y = MercatorProjection.latitudeToPixelY(latitude, zoomLevel); + } + + public void setPosition(GeoPoint geoPoint){ + setPosition(geoPoint.getLatitude(), geoPoint.getLongitude()); + } + + public void setPosition(double latitude, double longitude) { + latitude = MercatorProjection.limitLatitude(latitude); + longitude = MercatorProjection.limitLongitude(longitude); + this.x = MercatorProjection.longitudeToX(longitude); + this.y = MercatorProjection.latitudeToY(latitude); } public void copy(MapPosition other) { this.zoomLevel = other.zoomLevel; - this.scale = other.scale; - this.lat = other.lat; - this.lon = other.lon; this.angle = other.angle; + this.scale = other.scale; this.x = other.x; this.y = other.y; } - public void setFromLatLon(double latitude, double longitude, int zoomLevel){ - this.zoomLevel = zoomLevel; - this.x = MercatorProjection.longitudeToPixelX(longitude, zoomLevel); - this.y = MercatorProjection.latitudeToPixelY(latitude, zoomLevel); + public double getZoomScale() { + return scale / (1 << zoomLevel); + } + + public GeoPoint getGeoPoint() { + return new GeoPoint(MercatorProjection.toLatitude(y), + MercatorProjection.toLongitude(x)); } @Override @@ -94,11 +89,11 @@ public class MapPosition { StringBuilder builder = new StringBuilder(); builder.append("MapPosition ["); builder.append("lat="); - builder.append(this.lat); + builder.append(MercatorProjection.toLatitude(y)); builder.append(", lon="); - builder.append(this.lon); + builder.append(MercatorProjection.toLongitude(x)); builder.append(", zoomLevel="); - builder.append(this.zoomLevel); + builder.append(zoomLevel); builder.append("]"); return builder.toString(); } diff --git a/src/org/oscim/core/MercatorProjection.java b/src/org/oscim/core/MercatorProjection.java index 50a3add1..6ea825fc 100644 --- a/src/org/oscim/core/MercatorProjection.java +++ b/src/org/oscim/core/MercatorProjection.java @@ -15,7 +15,6 @@ */ package org.oscim.core; - /** * An implementation of the spherical Mercator projection. */ @@ -61,28 +60,6 @@ public final class MercatorProjection { / ((long) Tile.TILE_SIZE << zoomLevel); } - /** - * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a - * certain zoom level. - * - * @param latitude - * the latitude coordinate that should be converted. - * @param zoomLevel - * the zoom level at which the coordinate should be converted. - * @return the pixel Y coordinate of the latitude value. - */ - public static double latitudeToPixelY(double latitude, int zoomLevel) { - double sinLatitude = Math.sin(latitude * (Math.PI / 180)); - return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) - * ((long) Tile.TILE_SIZE << zoomLevel); - } - - public static double latitudeToPixelY(MapPosition mapPosition) { - double sinLatitude = Math.sin(mapPosition.lat * (Math.PI / 180)); - return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) - * ((long) Tile.TILE_SIZE << mapPosition.zoomLevel); - } - /** * Projects a longitude coordinate (in degrees) to the range [0.0,1.0] * @@ -95,18 +72,23 @@ public final class MercatorProjection { return 0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI); } + public static double toLatitude(double y) { + return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI; + } + /** - * Converts a latitude coordinate (in degrees) to a tile Y number at a - * certain zoom level. + * Projects a longitude coordinate (in degrees) to the range [0.0,1.0] * - * @param latitude - * the latitude coordinate that should be converted. - * @param zoomLevel - * the zoom level at which the coordinate should be converted. - * @return the tile Y number of the latitude value. + * @param longitude + * the longitude coordinate that should be converted. + * @return the position . */ - public static long latitudeToTileY(double latitude, int zoomLevel) { - return pixelYToTileY(latitudeToPixelY(latitude, zoomLevel), zoomLevel); + public static double longitudeToX(double longitude) { + return (longitude + 180) / 360; + } + + public static double toLongitude(double x) { + return 360 * (x - 0.5); } /** @@ -135,51 +117,6 @@ public final class MercatorProjection { return Math.max(Math.min(longitude - 360, LONGITUDE_MAX), LONGITUDE_MIN); return longitude; - - } - - /** - * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a - * certain zoom level. - * - * @param longitude - * the longitude coordinate that should be converted. - * @param zoomLevel - * the zoom level at which the coordinate should be converted. - * @return the pixel X coordinate of the longitude value. - */ - public static double longitudeToPixelX(double longitude,int zoomLevel) { - return (longitude + 180) / 360 * ((long) Tile.TILE_SIZE << zoomLevel); - } - - public static double longitudeToPixelX(MapPosition mapPosition) { - return (mapPosition.lon + 180) / 360 - * ((long) Tile.TILE_SIZE << mapPosition.zoomLevel); - } - - /** - * Projects a longitude coordinate (in degrees) to the range [0.0,1.0] - * - * @param longitude - * the longitude coordinate that should be converted. - * @return the position . - */ - public static double longitudeToX(double longitude) { - return (longitude + 180) / 360; - } - - /** - * Converts a longitude coordinate (in degrees) to the tile X number at a - * certain zoom level. - * - * @param longitude - * the longitude coordinate that should be converted. - * @param zoomLevel - * the zoom level at which the coordinate should be converted. - * @return the tile X number of the longitude value. - */ - public static long longitudeToTileX(double longitude, int zoomLevel) { - return pixelXToTileX(longitudeToPixelX(longitude, zoomLevel), zoomLevel); } /** @@ -196,25 +133,18 @@ public final class MercatorProjection { return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5); } - public static double toLongitude(double pixelX) { - return 360 * (pixelX - 0.5); - } - - public static double toLongitude(double pixelX, double scale) { - return 360 * ((pixelX / scale) - 0.5); - } /** - * Converts a pixel X coordinate to the tile X number. + * Converts a longitude coordinate (in degrees) to a pixel X coordinate at a + * certain zoom level. * - * @param pixelX - * the pixel X coordinate that should be converted. + * @param longitude + * the longitude coordinate that should be converted. * @param zoomLevel * the zoom level at which the coordinate should be converted. - * @return the tile X number. + * @return the pixel X coordinate of the longitude value. */ - public static int pixelXToTileX(double pixelX, int zoomLevel) { - return (int) Math.min(Math.max(pixelX / Tile.TILE_SIZE, 0), - Math.pow(2, zoomLevel) - 1); + public static double longitudeToPixelX(double longitude, int zoomLevel) { + return (longitude + 180) / 360 * ((long) Tile.TILE_SIZE << zoomLevel); } /** @@ -232,69 +162,22 @@ public final class MercatorProjection { return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; } - public static double toLatitude(double pixelY) { - double y = 0.5 - pixelY; - return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; - } - - public static double toLatitude(double pixelY, double scale) { - double y = 0.5 - pixelY / scale; - return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; - } /** - * Converts a pixel Y coordinate to the tile Y number. + * Converts a latitude coordinate (in degrees) to a pixel Y coordinate at a + * certain zoom level. * - * @param pixelY - * the pixel Y coordinate that should be converted. + * @param latitude + * the latitude coordinate that should be converted. * @param zoomLevel * the zoom level at which the coordinate should be converted. - * @return the tile Y number. + * @return the pixel Y coordinate of the latitude value. */ - public static int pixelYToTileY(double pixelY, int zoomLevel) { - return (int) Math.min(Math.max(pixelY / Tile.TILE_SIZE, 0), - Math.pow(2, zoomLevel) - 1); - } - - /** - * Converts a tile X number at a certain zoom level to a longitude - * coordinate. - * - * @param tileX - * the tile X number that should be converted. - * @param zoomLevel - * the zoom level at which the number should be converted. - * @return the longitude value of the tile X number. - */ - public static double tileXToLongitude(long tileX, int zoomLevel) { - return pixelXToLongitude(tileX * Tile.TILE_SIZE, zoomLevel); - } - - /** - * Converts a tile Y number at a certain zoom level to a latitude - * coordinate. - * - * @param tileY - * the tile Y number that should be converted. - * @param zoomLevel - * the zoom level at which the number should be converted. - * @return the latitude value of the tile Y number. - */ - public static double tileYToLatitude(long tileY, int zoomLevel) { - return pixelYToLatitude(tileY * Tile.TILE_SIZE, zoomLevel); + public static double latitudeToPixelY(double latitude, int zoomLevel) { + double sinLatitude = Math.sin(latitude * (Math.PI / 180)); + return (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) + * ((long) Tile.TILE_SIZE << zoomLevel); } private MercatorProjection() { - throw new IllegalStateException(); } - -// public static Point projectPoint(GeoPoint geopoint, int z, Point reuse) { -// Point out = reuse == null ? new Point() : reuse; -// -// out.x = (int) MercatorProjection.longitudeToPixelX(geopoint.getLongitude(), z); -// out.y = (int) MercatorProjection.latitudeToPixelY(geopoint.getLatitude(), z); -// -// return out; -// } - - } diff --git a/src/org/oscim/core/Tile.java b/src/org/oscim/core/Tile.java index 95a2b321..d79ffe7f 100644 --- a/src/org/oscim/core/Tile.java +++ b/src/org/oscim/core/Tile.java @@ -41,15 +41,6 @@ public class Tile { */ public final byte zoomLevel; - /** - * the pixel X coordinate of the upper left corner of this tile. - */ - public final long pixelX; - /** - * the pixel Y coordinate of the upper left corner of this tile. - */ - public final long pixelY; - /** * @param tileX * the X number of the tile. @@ -61,8 +52,6 @@ public class Tile { public Tile(int tileX, int tileY, byte zoomLevel) { this.tileX = tileX; this.tileY = tileY; - this.pixelX = this.tileX * TILE_SIZE; - this.pixelY = this.tileY * TILE_SIZE; this.zoomLevel = zoomLevel; } diff --git a/src/org/oscim/database/mapfile/MapDatabase.java b/src/org/oscim/database/mapfile/MapDatabase.java index 19c999c5..c10888a9 100644 --- a/src/org/oscim/database/mapfile/MapDatabase.java +++ b/src/org/oscim/database/mapfile/MapDatabase.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.io.RandomAccessFile; import org.oscim.core.GeometryBuffer; -import org.oscim.core.MercatorProjection; import org.oscim.core.Tag; import org.oscim.core.Tile; import org.oscim.database.IMapDatabase; @@ -443,6 +442,8 @@ public class MapDatabase implements IMapDatabase { } + + private void processBlocks(IMapDatabaseCallback mapDatabaseCallback, QueryParameters queryParameters, SubFileParameter subFileParameter) throws IOException { @@ -527,10 +528,10 @@ public class MapDatabase implements IMapDatabase { } // calculate the top-left coordinates of the underlying tile - double tileLatitudeDeg = MercatorProjection.tileYToLatitude( + double tileLatitudeDeg = Projection.tileYToLatitude( subFileParameter.boundaryTileTop + row, subFileParameter.baseZoomLevel); - double tileLongitudeDeg = MercatorProjection.tileXToLongitude( + double tileLongitudeDeg = Projection.tileXToLongitude( subFileParameter.boundaryTileLeft + column, subFileParameter.baseZoomLevel); mTileLatitude = (int) (tileLatitudeDeg * 1000000); @@ -590,9 +591,8 @@ public class MapDatabase implements IMapDatabase { Tag[] tags = null; Tag[] curTags; - - long x = mTile.pixelX; - long y = mTile.pixelY + Tile.TILE_SIZE; + long x = mTile.tileX * Tile.TILE_SIZE; + long y = mTile.tileY * Tile.TILE_SIZE + Tile.TILE_SIZE; long z = Tile.TILE_SIZE << mTile.zoomLevel; long dx = (x - (z >> 1)); @@ -917,7 +917,7 @@ public class MapDatabase implements IMapDatabase { int add = (hasName ? 1 : 0) + (hasHouseNr ? 1 : 0) + (hasRef ? 1 : 0); int addTag = tags.length; - if (add > 0){ + if (add > 0) { curTags = new Tag[tags.length + add]; System.arraycopy(tags, 0, curTags, 0, tags.length); } @@ -1055,8 +1055,8 @@ public class MapDatabase implements IMapDatabase { private boolean projectToTile(float[] coords, short[] indices) { - long x = mTile.pixelX; - long y = mTile.pixelY + Tile.TILE_SIZE; + long x = mTile.tileX * Tile.TILE_SIZE; + long y = mTile.tileY * Tile.TILE_SIZE + Tile.TILE_SIZE; long z = Tile.TILE_SIZE << mTile.zoomLevel; double divx, divy = 0; diff --git a/src/org/oscim/database/mapfile/Projection.java b/src/org/oscim/database/mapfile/Projection.java new file mode 100644 index 00000000..553f66d3 --- /dev/null +++ b/src/org/oscim/database/mapfile/Projection.java @@ -0,0 +1,108 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.org + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.database.mapfile; + +import org.oscim.core.MercatorProjection; +import org.oscim.core.Tile; + +public class Projection { + + + /** + * Converts a tile X number at a certain zoom level to a longitude + * coordinate. + * + * @param tileX + * the tile X number that should be converted. + * @param zoomLevel + * the zoom level at which the number should be converted. + * @return the longitude value of the tile X number. + */ + public static double tileXToLongitude(long tileX, int zoomLevel) { + return MercatorProjection.pixelXToLongitude(tileX * Tile.TILE_SIZE, zoomLevel); + } + + /** + * Converts a tile Y number at a certain zoom level to a latitude + * coordinate. + * + * @param tileY + * the tile Y number that should be converted. + * @param zoomLevel + * the zoom level at which the number should be converted. + * @return the latitude value of the tile Y number. + */ + public static double tileYToLatitude(long tileY, int zoomLevel) { + return MercatorProjection.pixelYToLatitude(tileY * Tile.TILE_SIZE, zoomLevel); + } + + /** + * Converts a latitude coordinate (in degrees) to a tile Y number at a + * certain zoom level. + * + * @param latitude + * the latitude coordinate that should be converted. + * @param zoomLevel + * the zoom level at which the coordinate should be converted. + * @return the tile Y number of the latitude value. + */ + public static long latitudeToTileY(double latitude, int zoomLevel) { + return pixelYToTileY(MercatorProjection.latitudeToPixelY(latitude, zoomLevel), zoomLevel); + } + + /** + * Converts a longitude coordinate (in degrees) to the tile X number at a + * certain zoom level. + * + * @param longitude + * the longitude coordinate that should be converted. + * @param zoomLevel + * the zoom level at which the coordinate should be converted. + * @return the tile X number of the longitude value. + */ + public static long longitudeToTileX(double longitude, int zoomLevel) { + return pixelXToTileX(MercatorProjection.longitudeToPixelX(longitude, zoomLevel), zoomLevel); + } + + /** + * Converts a pixel X coordinate to the tile X number. + * + * @param pixelX + * the pixel X coordinate that should be converted. + * @param zoomLevel + * the zoom level at which the coordinate should be converted. + * @return the tile X number. + */ + public static int pixelXToTileX(double pixelX, int zoomLevel) { + return (int) Math.min(Math.max(pixelX / Tile.TILE_SIZE, 0), + Math.pow(2, zoomLevel) - 1); + } + /** + * Converts a pixel Y coordinate to the tile Y number. + * + * @param pixelY + * the pixel Y coordinate that should be converted. + * @param zoomLevel + * the zoom level at which the coordinate should be converted. + * @return the tile Y number. + */ + public static int pixelYToTileY(double pixelY, int zoomLevel) { + return (int) Math.min(Math.max(pixelY / Tile.TILE_SIZE, 0), + Math.pow(2, zoomLevel) - 1); + } + private Projection(){ + + } +} diff --git a/src/org/oscim/database/mapfile/header/SubFileParameter.java b/src/org/oscim/database/mapfile/header/SubFileParameter.java index 575fc796..4204f5e2 100644 --- a/src/org/oscim/database/mapfile/header/SubFileParameter.java +++ b/src/org/oscim/database/mapfile/header/SubFileParameter.java @@ -14,7 +14,7 @@ */ package org.oscim.database.mapfile.header; -import org.oscim.core.MercatorProjection; +import org.oscim.database.mapfile.Projection; /** * Holds all parameters of a sub-file. @@ -110,6 +110,7 @@ public class SubFileParameter { */ private final int hashCodeValue; + SubFileParameter(SubFileParameterBuilder subFileParameterBuilder) { this.startAddress = subFileParameterBuilder.startAddress; this.indexStartAddress = subFileParameterBuilder.indexStartAddress; @@ -120,16 +121,16 @@ public class SubFileParameter { this.hashCodeValue = calculateHashCode(); // calculate the XY numbers of the boundary tiles in this sub-file - this.boundaryTileBottom = MercatorProjection.latitudeToTileY( + this.boundaryTileBottom = Projection.latitudeToTileY( subFileParameterBuilder.boundingBox.minLatitudeE6 / COORDINATES_DIVISOR, this.baseZoomLevel); - this.boundaryTileLeft = MercatorProjection.longitudeToTileX( + this.boundaryTileLeft = Projection.longitudeToTileX( subFileParameterBuilder.boundingBox.minLongitudeE6 / COORDINATES_DIVISOR, this.baseZoomLevel); - this.boundaryTileTop = MercatorProjection.latitudeToTileY( + this.boundaryTileTop = Projection.latitudeToTileY( subFileParameterBuilder.boundingBox.maxLatitudeE6 / COORDINATES_DIVISOR, this.baseZoomLevel); - this.boundaryTileRight = MercatorProjection.longitudeToTileX( + this.boundaryTileRight = Projection.longitudeToTileX( subFileParameterBuilder.boundingBox.maxLongitudeE6 / COORDINATES_DIVISOR, this.baseZoomLevel); diff --git a/src/org/oscim/generator/TileGenerator.java b/src/org/oscim/generator/TileGenerator.java index 74e58623..c44b81b2 100644 --- a/src/org/oscim/generator/TileGenerator.java +++ b/src/org/oscim/generator/TileGenerator.java @@ -41,7 +41,6 @@ import org.oscim.theme.renderinstruction.RenderInstruction; import org.oscim.theme.renderinstruction.Text; import org.oscim.utils.LineClipper; import org.oscim.view.DebugSettings; -import org.oscim.view.MapView; import android.graphics.Bitmap; import android.graphics.Paint; @@ -80,7 +79,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { new float[] { 0, 0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0, 0, 0 }, - new short[1]), + new short[] {10}), new Tag[] { new Tag("debug", "box") } ); @@ -151,16 +150,14 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { setScaleStrokeWidth(mTile.zoomLevel); // account for area changes with latitude - double latitude = MercatorProjection.pixelYToLatitude(mTile.pixelY + (Tile.TILE_SIZE >> 1), - mTile.zoomLevel); + double latitude = MercatorProjection.toLatitude(mTile.y); + mLatScaleFactor = 0.5f + 0.5f * ((float) Math.sin(Math.abs(latitude) * (Math.PI / 180))); mGroundResolution = (float) (Math.cos(latitude * (Math.PI / 180)) * MercatorProjection.EARTH_CIRCUMFERENCE / ((long) Tile.TILE_SIZE << mTile.zoomLevel)); - Log.d(TAG, mTile + " " + mGroundResolution); - mTile.layers = new Layers(); // query database, which calls renderWay and renderPOI @@ -188,7 +185,6 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { // draw tile box mWay = mDebugWay; - mWay.geom.index[0] = (short) (MapView.enableClosePolygons ? 8 : 10); mDrawingLayer = 100 * renderLevels; ri = renderTheme.matchWay(mDebugWay.tags, (byte) 0, false); renderWay(ri, mDebugWay.tags); diff --git a/src/org/oscim/overlay/BuildingOverlay.java b/src/org/oscim/overlay/BuildingOverlay.java index b76f3de9..dbc013e0 100644 --- a/src/org/oscim/overlay/BuildingOverlay.java +++ b/src/org/oscim/overlay/BuildingOverlay.java @@ -41,6 +41,8 @@ public class BuildingOverlay extends Overlay { private final float mFadeTime = 300; private float mAlpha = 1; + private final static int MIN_ZOOM = 17; + @Override public boolean onTouchEvent(MotionEvent e) { int action = e.getAction() & MotionEvent.ACTION_MASK; @@ -48,7 +50,7 @@ public class BuildingOverlay extends Overlay { multi++; } else if (action == MotionEvent.ACTION_POINTER_UP) { multi--; - if (mPrevZoom != 17 && mAlpha > 0) { + if (!mActive && mAlpha > 0) { // finish hiding //Log.d(TAG, "add multi hide timer " + mAlpha); addShowTimer(mFadeTime * mAlpha, false); @@ -65,19 +67,20 @@ public class BuildingOverlay extends Overlay { return false; } - private int mPrevZoom = 0; + private boolean mActive = false; @Override public void onUpdate(MapPosition mapPosition, boolean changed) { - int z = mapPosition.zoomLevel; - if (z == mPrevZoom) + boolean show = mapPosition.scale >= (1 << MIN_ZOOM); + + if (show && mActive) return; - if (z == 17) { + if (show) { // start showing //Log.d(TAG, "add show timer " + mAlpha); addShowTimer(mFadeTime * (1 - mAlpha), true); - } else if (mPrevZoom == 17) { + } else if (mActive) { // indicate hiding if (multi > 0) { //Log.d(TAG, "add fade timer " + mAlpha); @@ -87,8 +90,7 @@ public class BuildingOverlay extends Overlay { addShowTimer(mFadeTime * mAlpha, false); } } - - mPrevZoom = z; + mActive = show; } void fade(float duration, long tick, boolean dir, float max) { diff --git a/src/org/oscim/overlay/ItemizedOverlay.java b/src/org/oscim/overlay/ItemizedOverlay.java index 47aaccba..7679d913 100644 --- a/src/org/oscim/overlay/ItemizedOverlay.java +++ b/src/org/oscim/overlay/ItemizedOverlay.java @@ -24,10 +24,12 @@ package org.oscim.overlay; import org.oscim.core.GeoPoint; import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; +import org.oscim.core.Tile; import org.oscim.overlay.OverlayItem.HotspotPlace; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.layer.SymbolLayer; import org.oscim.renderer.overlays.BasicOverlay; +import org.oscim.utils.FastMath; import org.oscim.view.MapView; import android.content.res.Resources; @@ -71,8 +73,8 @@ public abstract class ItemizedOverlay extends Overlay private int mSize; - // pre-projected points to zoomlovel 20 - private static final byte MAX_ZOOM = 20; + // pre-projected points to zoomlovel 18 + private static final byte MAX_ZOOM = 18; class ItemOverlay extends BasicOverlay { @@ -95,9 +97,16 @@ public abstract class ItemizedOverlay extends Overlay mUpdate = false; - int diff = MAX_ZOOM - curPos.zoomLevel; - int mx = (int) curPos.x; - int my = (int) curPos.y; + //int mx = (int) curPos.x; + //int my = (int) curPos.y; + + //int z = curPos.zoomLevel; + + int z = FastMath.log2((int) curPos.scale); + int diff = MAX_ZOOM - z; + + int mx = (int) (curPos.x * (Tile.TILE_SIZE << z)); + int my = (int) (curPos.y * (Tile.TILE_SIZE << z)); // limit could be 1 if we update on every position change float limit = 1.5f; @@ -122,6 +131,8 @@ public abstract class ItemizedOverlay extends Overlay for (InternalItem it = mItems; it != null; it = it.next) { it.x = (it.px >> diff) - mx; it.y = (it.py >> diff) - my; + //it.x = it.px - mx; + //it.y = it.py - my; if (it.x > max || it.x < -max || it.y > max || it.y < -max) { if (it.visible) { @@ -159,21 +170,22 @@ public abstract class ItemizedOverlay extends Overlay // only update when zoomlevel changed, new items are visible // or more than 10 of the current items became invisible - if (((curPos.zoomLevel == mMapPosition.zoomLevel || numVisible == 0)) && - (changedVisible == 0 && changesInvisible < 10)) + if ((numVisible == 0) && (changedVisible == 0 && changesInvisible < 10)) return; // keep position for current state // updateMapPosition(); - // TODO add copy utility function - mMapPosition.x = curPos.x; - mMapPosition.y = curPos.y; - mMapPosition.zoomLevel = curPos.zoomLevel; - mMapPosition.scale = curPos.scale; - mMapPosition.angle = curPos.angle; + mMapPosition.copy(curPos); + + //mMapPosition.x = curPos.x; + //mMapPosition.y = curPos.y; + //mMapPosition.zoomLevel = curPos.zoomLevel; + //mMapPosition.scale = curPos.scale; + //mMapPosition.angle = curPos.angle; // items are placed relative to scale == 1 - mMapPosition.scale = 1; + // mMapPosition.scale = 1; + mMapPosition.scale = 1 << z; layers.clear(); @@ -423,7 +435,7 @@ public abstract class ItemizedOverlay extends Overlay return marker; } - public static Drawable makeMarker(Resources res, int id, HotspotPlace place){ + public static Drawable makeMarker(Resources res, int id, HotspotPlace place) { Drawable marker = res.getDrawable(id); if (place == null) boundToHotspot(marker, HotspotPlace.CENTER); diff --git a/src/org/oscim/overlay/PathOverlay.java b/src/org/oscim/overlay/PathOverlay.java index 9d462257..b6d5f73c 100644 --- a/src/org/oscim/overlay/PathOverlay.java +++ b/src/org/oscim/overlay/PathOverlay.java @@ -22,6 +22,7 @@ import java.util.List; import org.oscim.core.GeoPoint; import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; +import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.LineLayer; @@ -108,12 +109,6 @@ public class PathOverlay extends Overlay { int size = mSize; - // keep position to render relative to current state - mMapPosition.copy(curPos); - - // items are placed relative to scale == 1 - mMapPosition.scale = 1; - // layers.clear(); LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE); // reset verticesCnt to reuse layer @@ -124,15 +119,22 @@ public class PathOverlay extends Overlay { int x, y, px = 0, py = 0; int i = 0; - int diff = MAX_ZOOM - mMapPosition.zoomLevel; - int mx = (int) mMapPosition.x; - int my = (int) mMapPosition.y; + //int mx = (int) mMapPosition.x; + //int my = (int) mMapPosition.y; + //int z = curPos.zoomLevel; + int z = FastMath.log2((int) curPos.scale); + int diff = MAX_ZOOM - z; + int mx = (int) (curPos.x * (Tile.TILE_SIZE << z)); + int my = (int) (curPos.y * (Tile.TILE_SIZE << z)); for (int j = 0; j < size; j += 2) { // TODO translate mapPosition and do this after clipping x = (mPreprojected[j + 0] >> diff) - mx; y = (mPreprojected[j + 1] >> diff) - my; + //x = (mPreprojected[j + 0]) - mx; + //y = (mPreprojected[j + 1]) - my; + // TODO use line clipping, this doesnt work with 'GreatCircle' // TODO clip to view bounding box if (x > max || x < -max || y > max || y < -max) { @@ -157,6 +159,12 @@ public class PathOverlay extends Overlay { mIndex[0] = (short) i; ll.addLine(projected, mIndex, false); + // keep position to render relative to current state + mMapPosition.copy(curPos); + + // items are placed relative to scale 1 + mMapPosition.scale = 1 << z; + newData = true; } diff --git a/src/org/oscim/renderer/GLRenderer.java b/src/org/oscim/renderer/GLRenderer.java index a22a7aad..c5c3320e 100644 --- a/src/org/oscim/renderer/GLRenderer.java +++ b/src/org/oscim/renderer/GLRenderer.java @@ -35,7 +35,6 @@ import org.oscim.core.Tile; import org.oscim.renderer.layer.Layers; import org.oscim.renderer.overlays.RenderOverlay; import org.oscim.theme.RenderTheme; -import org.oscim.utils.FastMath; import org.oscim.utils.GlUtils; import org.oscim.utils.Matrix4; import org.oscim.view.MapView; @@ -317,31 +316,21 @@ public class GLRenderer implements GLSurfaceView.Renderer { for (int i = 0; i < mDrawTiles.cnt; i++) tiles[i].isVisible = false; - // relative zoom-level, 'tiles' could not have been updated after - // zoom-level changed. - float div = FastMath.pow(pos.zoomLevel - tiles[0].zoomLevel); + int z = tiles[0].zoomLevel; - // draw additional tiles on max zoom-level: - // to make sure buildings that are half visible but - // the not ground tile are still drawn. - float scale = pos.scale; - if (scale > 2) - scale = 2; + double curScale = Tile.TILE_SIZE * pos.scale; + double tileScale = Tile.TILE_SIZE * (pos.scale / (1 << z)); - // transform screen coordinates to tile coordinates - float tileScale = scale * div * Tile.TILE_SIZE; - double px = pos.x * scale; - double py = pos.y * scale; for (int i = 0; i < 8; i += 2) { - coords[i + 0] = (float) (px + coords[i + 0]) / tileScale; - coords[i + 1] = (float) (py + coords[i + 1]) / tileScale; + coords[i + 0] = (float) ((pos.x * curScale + coords[i + 0]) / tileScale); + coords[i + 1] = (float) ((pos.y * curScale + coords[i + 1]) / tileScale); } // count placeholder tiles mNumTileHolder = 0; // check visibile tiles - mScanBox.scan(coords, tiles[0].zoomLevel); + mScanBox.scan(coords, z); } } @@ -451,21 +440,23 @@ public class GLRenderer implements GLSurfaceView.Renderer { return; boolean tilesChanged = false; + boolean positionChanged = false; + // check if the tiles have changed... if (serial != mDrawTiles.serial) { - mMapPosition.zoomLevel = -1; tilesChanged = true; + // FIXME needed? + positionChanged = true; } // get current MapPosition, set mTileCoords (mapping of screen to model // coordinates) MapPosition pos = mMapPosition; - boolean positionChanged; synchronized (mMapViewPosition) { mMapViewPosition.updateAnimation(); - positionChanged = mMapViewPosition.getMapPosition(pos); + positionChanged |= mMapViewPosition.getMapPosition(pos); if (positionChanged) mMapViewPosition.getMapViewProjection(mTileCoords); diff --git a/src/org/oscim/renderer/LineRenderer.java b/src/org/oscim/renderer/LineRenderer.java index c73d9ca1..8f7f038a 100644 --- a/src/org/oscim/renderer/LineRenderer.java +++ b/src/org/oscim/renderer/LineRenderer.java @@ -23,7 +23,6 @@ import static android.opengl.GLES20.glUniform1f; import static android.opengl.GLES20.glVertexAttribPointer; import org.oscim.core.MapPosition; -import org.oscim.generator.TileGenerator; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layers; @@ -130,15 +129,17 @@ public final class LineRenderer { //glUniformMatrix4fv(hLineMatrix[mode], 1, false, matrix, 0); m.mvp.setAsUniform(hLineMatrix[mode]); + //int zoom = FastMath.log2((int) pos.absScale); int zoom = pos.zoomLevel; - float scale = pos.scale; + + double scale = pos.getZoomScale(); // Line scale factor for non fixed lines: Within a zoom- // level lines would be scaled by the factor 2 by view-matrix. // Though lines should only scale by sqrt(2). This is achieved // by inverting scaling of extrusion vector with: width/sqrt(s). // within one zoom-level: 1 <= s <= 2 - float s = scale / div; + double s = scale / div; float lineScale = (float) Math.sqrt(s * 2 / 2.2); // scale factor to map one pixel on tile to one pixel on screen: @@ -146,7 +147,7 @@ public final class LineRenderer { float pixel = 0; if (mode == 1) - pixel = 1.5f / s; + pixel = (float) (1.5 / s); glUniform1f(uLineScale, pixel); int lineMode = 0; @@ -154,7 +155,7 @@ public final class LineRenderer { boolean blur = false; // dont increase scale when max is reached - boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; + //boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; Layer l = curLayer; for (; l != null && l.type == Layer.LINE; l = l.next) { @@ -167,7 +168,7 @@ public final class LineRenderer { } else if (line.fade > zoom) { continue; } else { - float alpha = (scale > 1.2f ? scale : 1.2f) - 1f; + float alpha = (float) (scale > 1.2 ? scale : 1.2) - 1; GlUtils.setColor(uLineColor, line.color, alpha); } @@ -180,10 +181,10 @@ public final class LineRenderer { // draw linelayers references by this outline for (LineLayer o = ll.outlines; o != null; o = o.outlines) { - if (o.line.fixed || strokeMaxZoom) { - width = (ll.width + o.width) / s; + if (o.line.fixed /* || strokeMaxZoom */) { + width = (float) ((ll.width + o.width) / s); } else { - width = ll.width / s + o.width / lineScale; + width = (float) (ll.width / s + o.width / lineScale); // check min-size for outline if (o.line.min > 0 && o.width * lineScale < o.line.min * 2) @@ -193,7 +194,7 @@ public final class LineRenderer { glUniform1f(uLineWidth, width * COORD_SCALE_BY_DIR_SCALE); if (line.blur != 0) { - glUniform1f(uLineScale, 1f - (line.blur / s)); + glUniform1f(uLineScale, (float) (1 - (line.blur / s))); blur = true; } else if (mode == 1) { glUniform1f(uLineScale, pixel / width); @@ -212,10 +213,10 @@ public final class LineRenderer { } } else { - if (line.fixed || strokeMaxZoom) { + if (line.fixed /* || strokeMaxZoom */) { // invert scaling of extrusion vectors so that line // width stays the same. - width = ll.width / s; + width = (float) (ll.width / s); } else { // reduce linear scaling of extrusion vectors so that // line width increases by sqrt(2.2). diff --git a/src/org/oscim/renderer/LineTexRenderer.java b/src/org/oscim/renderer/LineTexRenderer.java index 66619688..cfa0e702 100644 --- a/src/org/oscim/renderer/LineTexRenderer.java +++ b/src/org/oscim/renderer/LineTexRenderer.java @@ -130,7 +130,9 @@ public class LineTexRenderer { GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); - float s = pos.scale / div; + float scale = (float)pos.getZoomScale(); + + float s = scale / div; //GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID[0]); @@ -146,7 +148,7 @@ public class LineTexRenderer { GLES20.glUniform1f(hPatternScale, (GLRenderer.COORD_SCALE * line.stipple) / ps); GLES20.glUniform1f(hPatternWidth, line.stippleWidth); - GLES20.glUniform1f(hScale, pos.scale); + GLES20.glUniform1f(hScale, scale); // keep line width fixed GLES20.glUniform1f(hWidth, ll.width / s * COORD_SCALE_BY_DIR_SCALE); diff --git a/src/org/oscim/renderer/MapTile.java b/src/org/oscim/renderer/MapTile.java index 4ca462ea..429efe59 100644 --- a/src/org/oscim/renderer/MapTile.java +++ b/src/org/oscim/renderer/MapTile.java @@ -24,6 +24,9 @@ import org.oscim.renderer.layer.TextItem; */ public final class MapTile extends JobTile { + public double x; + public double y; + /** * Tile data set by TileGenerator. */ @@ -73,6 +76,8 @@ public final class MapTile extends JobTile { MapTile(int tileX, int tileY, byte zoomLevel) { super(tileX, tileY, zoomLevel); + this.x = (double)tileX / (1 << zoomLevel); + this.y = (double)tileY / (1 << zoomLevel); } /** diff --git a/src/org/oscim/renderer/PolygonRenderer.java b/src/org/oscim/renderer/PolygonRenderer.java index fdb611d1..29040075 100644 --- a/src/org/oscim/renderer/PolygonRenderer.java +++ b/src/org/oscim/renderer/PolygonRenderer.java @@ -179,6 +179,8 @@ public final class PolygonRenderer { int start = cur; + float scale = (float)pos.getZoomScale(); + Layer l = layer; for (; l != null && l.type == Layer.POLYGON; l = l.next) { PolygonLayer pl = (PolygonLayer) l; @@ -204,13 +206,13 @@ public final class PolygonRenderer { // draw up to 7 layers into stencil buffer if (cur == STENCIL_BITS - 1) { - fillPolygons(start, cur, zoom, pos.scale); + fillPolygons(start, cur, zoom, scale); start = cur = 0; } } if (cur > 0) - fillPolygons(start, cur, zoom, pos.scale); + fillPolygons(start, cur, zoom, scale); if (clip) { if (first) { @@ -318,7 +320,9 @@ public final class PolygonRenderer { glColorMask(false, false, false, false); } // always pass stencil test: - glStencilFunc(GL_ALWAYS, 0x00, 0x00); + //glStencilFunc(GL_ALWAYS, 0x00, 0x00); + glStencilFunc(GL_EQUAL, CLIP_BIT, CLIP_BIT); + // write to all bits glStencilMask(0xFF); // zero out area to draw to diff --git a/src/org/oscim/renderer/TextureRenderer.java b/src/org/oscim/renderer/TextureRenderer.java index 986aea64..c2e27854 100644 --- a/src/org/oscim/renderer/TextureRenderer.java +++ b/src/org/oscim/renderer/TextureRenderer.java @@ -122,7 +122,7 @@ public final class TextureRenderer { + " pos = u_proj * (u_mv * vec4(vertex.xy + dir * u_scale, 0.0, 1.0));" + " } else {" // place as billboard + " vec4 center = u_mv * vec4(vertex.xy, 0.0, 1.0);" - + " pos = u_proj * (center + vec4(dir * (coord_scale * u_swidth), 0.1, 0.0));" + + " pos = u_proj * (center + vec4(dir * (coord_scale * u_swidth), 0.0, 0.0));" + " }" + " gl_Position = pos;" + " tex_c = tex_coord * div;" diff --git a/src/org/oscim/renderer/TileManager.java b/src/org/oscim/renderer/TileManager.java index ae0c08db..11123eff 100644 --- a/src/org/oscim/renderer/TileManager.java +++ b/src/org/oscim/renderer/TileManager.java @@ -28,6 +28,7 @@ import org.oscim.generator.JobTile; import org.oscim.generator.TileDistanceSort; import org.oscim.renderer.layer.TextItem; import org.oscim.renderer.layer.VertexPool; +import org.oscim.utils.FastMath; import org.oscim.view.MapView; import org.oscim.view.MapViewPosition; @@ -42,6 +43,8 @@ import android.util.Log; */ public class TileManager { static final String TAG = TileManager.class.getSimpleName(); + private final static int MAX_ZOOMLEVEL = 17; + private final static int MIN_ZOOMLEVEL = 2; // limit number tiles with new data not uploaded to GL // TODO this should depend on the number of tiles displayed @@ -148,10 +151,10 @@ public class TileManager { * 2. Add not yet loaded (or loading) tiles to JobQueue. * 3. Manage cache * - * @param mapPosition + * @param pos * current MapPosition */ - public synchronized void update(MapPosition mapPosition) { + public synchronized void update(MapPosition pos) { // clear JobQueue and set tiles to state == NONE. // one could also append new tiles and sort in JobQueue // but this has the nice side-effect that MapWorkers dont @@ -162,22 +165,38 @@ public class TileManager { // scale and translate projection to tile coordinates // load some tiles more than currently visible (* 0.75) - float scale = mapPosition.scale * 0.75f; - float tileScale = scale * Tile.TILE_SIZE; - double px = mapPosition.x * scale; - double py = mapPosition.y * scale; + // float scale = mapPosition.scale * 0.75f; + // float tileScale = scale * Tile.TILE_SIZE; + // double px = mapPosition.x * scale; + // double py = mapPosition.y * scale; + // + // float[] coords = mTileCoords; + // mMapViewPosition.getMapViewProjection(coords); + // for (int i = 0; i < 8; i += 2) { + // coords[i + 0] = (float)(px + coords[i + 0]) / tileScale; + // coords[i + 1] = (float)(py + coords[i + 1]) / tileScale; + // } + + // scale and translate projection to tile coordinates + // load some tiles more than currently visible (* 0.75) + double scale = pos.scale * 0.75f; + double curScale = Tile.TILE_SIZE * scale; + int zoomLevel = FastMath.clamp(pos.zoomLevel, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL); + + double tileScale = Tile.TILE_SIZE * (scale / (1 << zoomLevel)); float[] coords = mTileCoords; mMapViewPosition.getMapViewProjection(coords); + for (int i = 0; i < 8; i += 2) { - coords[i + 0] = (float)(px + coords[i + 0]) / tileScale; - coords[i + 1] = (float)(py + coords[i + 1]) / tileScale; + coords[i + 0] = (float) ((pos.x * curScale + coords[i + 0]) / tileScale); + coords[i + 1] = (float) ((pos.y * curScale + coords[i + 1]) / tileScale); } // scan visible tiles. callback function calls 'addTile' // which sets mNewTiles mNewTiles.cnt = 0; - mScanBox.scan(coords, mapPosition.zoomLevel); + mScanBox.scan(coords, zoomLevel); MapTile[] newTiles = mNewTiles.tiles; MapTile[] curTiles = mCurrentTiles.tiles; @@ -225,7 +244,7 @@ public class TileManager { JobTile[] jobs = new JobTile[mJobs.size()]; jobs = mJobs.toArray(jobs); - updateTileDistances(jobs, jobs.length, mapPosition); + updateTileDistances(jobs, jobs.length, pos); // sets tiles to state == LOADING mMapView.addJobs(jobs); @@ -237,7 +256,7 @@ public class TileManager { if (remove > CACHE_THRESHOLD || mTilesForUpload > MAX_TILES_IN_QUEUE) - limitCache(mapPosition, remove); + limitCache(pos, remove); } // need to keep track of TileSets to clear on reset... @@ -295,7 +314,7 @@ public class TileManager { tile = QuadTree.getTile(x, y, zoomLevel); if (tile == null) { - tile = new MapTile(x, y, (byte)zoomLevel); + tile = new MapTile(x, y, (byte) zoomLevel); QuadTree.add(tile); mJobs.add(tile); addToCache(tile); @@ -389,11 +408,13 @@ public class TileManager { private static void updateTileDistances(Object[] tiles, int size, MapPosition mapPosition) { // TODO there is probably a better quad-tree distance function - double x = mapPosition.x; - double y = mapPosition.y; + int zoom = mapPosition.zoomLevel; - int h = Tile.TILE_SIZE >> 1; - long center = h << zoom; + double x = mapPosition.x * (1 << zoom); + double y = mapPosition.y * (1 << zoom); + + final float h = 0.5f; + long center = (long)(h * (1 << zoom)); for (int i = 0; i < size; i++) { JobTile t = (JobTile) tiles[i]; @@ -405,16 +426,16 @@ public class TileManager { dz = 0; if (diff == 0) { - dx = (t.pixelX + h) - x; - dy = (t.pixelY + h) - y; + dx = (t.tileX + h) - x; + dy = (t.tileY + h) - y; scale = 0.5f; } else if (diff > 0) { // tile zoom level is greater than current // NB: distance increase by the factor 2 // with each zoom-level, so that children // will be kept less likely than parent tiles. - dx = (t.pixelX + h) - x * (1 << diff); - dy = (t.pixelY + h) - y * (1 << diff); + dx = (t.tileX + h) - x * (1 << diff); + dy = (t.tileY + h) - y * (1 << diff); // add tilesize/2 with each zoom-level // so that children near the current // map position but a few levels above @@ -424,8 +445,8 @@ public class TileManager { } else { diff = -diff; // tile zoom level is smaller than current - dx = (t.pixelX + h) - x / (1 << diff); - dy = (t.pixelY + h) - y / (1 << diff); + dx = (t.tileX + h) - x / (1 << diff); + dy = (t.tileY + h) - y / (1 << diff); dz = diff * h; scale = 0.5f * (1 << diff); } diff --git a/src/org/oscim/renderer/TileRenderer.java b/src/org/oscim/renderer/TileRenderer.java index 1ed30337..338be13d 100644 --- a/src/org/oscim/renderer/TileRenderer.java +++ b/src/org/oscim/renderer/TileRenderer.java @@ -19,6 +19,7 @@ import static android.opengl.GLES20.glStencilMask; import static org.oscim.generator.JobTile.STATE_READY; import org.oscim.core.MapPosition; +import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer.Matrices; import org.oscim.renderer.layer.Layer; import org.oscim.utils.FastMath; @@ -68,21 +69,25 @@ public class TileRenderer { drawTile(t, pos); } + double scale = pos.getZoomScale(); + // Draw parent or children as proxy for visibile tiles that dont // have data yet. Proxies are clipped to the region where nothing // was drawn to depth buffer. // TODO draw proxies for placeholder for (int i = 0; i < tileCnt; i++) { MapTile t = tiles[i]; - if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)) - drawProxyTile(t, pos, true); + if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)){ + boolean preferParent = (scale > 1.5) || (pos.zoomLevel - t.zoomLevel < 0); + drawProxyTile(t, pos, true, preferParent); + } } // Draw grandparents for (int i = 0; i < tileCnt; i++) { MapTile t = tiles[i]; if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)) - drawProxyTile(t, pos, false); + drawProxyTile(t, pos, false, false); } // make sure stencil buffer write is disabled @@ -116,13 +121,18 @@ public class TileRenderer { GLES20.glBindBuffer(GL_ARRAY_BUFFER, t.layers.vbo.id); // place tile relative to map position - float div = FastMath.pow(tile.zoomLevel - pos.zoomLevel); - float x = (float) (tile.pixelX - pos.x * div); - float y = (float) (tile.pixelY - pos.y * div); - float scale = pos.scale / div; + int z = tile.zoomLevel; + + float div = FastMath.pow(z - pos.zoomLevel); + + double curScale = Tile.TILE_SIZE * pos.scale; + double scale = (pos.scale / (1 << z)); + + float x = (float) ((tile.x - pos.x) * curScale); + float y = (float) ((tile.y - pos.y) * curScale); Matrices m = mMatrices; - m.mvp.setTransScale(x * scale, y * scale, scale / GLRenderer.COORD_SCALE); + m.mvp.setTransScale(x, y, (float)(scale / GLRenderer.COORD_SCALE)); m.mvp.multiplyMM(mProjMatrix, m.mvp); @@ -168,7 +178,8 @@ public class TileRenderer { } // clear clip-region and could also draw 'fade-effect' - PolygonRenderer.drawOver(m, true, 0x22000000); + //PolygonRenderer.drawOver(m, true, 0x22000000); + PolygonRenderer.drawOver(m, false, 0); } private static int drawProxyChild(MapTile tile, MapPosition pos) { @@ -187,12 +198,14 @@ public class TileRenderer { return drawn; } - private static void drawProxyTile(MapTile tile, MapPosition pos, boolean parent) { - int diff = pos.zoomLevel - tile.zoomLevel; + // just FIXME! + private static void drawProxyTile(MapTile tile, MapPosition pos, boolean parent, boolean preferParent) { + //int diff = pos.zoomLevel - tile.zoomLevel; QuadTree r = tile.rel; MapTile proxy; - if (pos.scale > 1.5f || diff < 0) { + + if (!preferParent) { // prefer drawing children if (drawProxyChild(tile, pos) == 4) return; diff --git a/src/org/oscim/renderer/overlays/BasicOverlay.java b/src/org/oscim/renderer/overlays/BasicOverlay.java index 6c914ee0..d8c39558 100644 --- a/src/org/oscim/renderer/overlays/BasicOverlay.java +++ b/src/org/oscim/renderer/overlays/BasicOverlay.java @@ -46,35 +46,36 @@ public abstract class BasicOverlay extends RenderOverlay { * use synchronized when modifying layers */ @Override - public synchronized void render(MapPosition pos, Matrices m) { + public synchronized void render(MapPosition curPos, Matrices m) { + MapPosition pos = mMapPosition; - float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); + float div = FastMath.pow(pos.zoomLevel - curPos.zoomLevel); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); GLState.test(false, false); if (layers.baseLayers != null) { - setMatrix(pos, m, true); + setMatrix(curPos, m, true); for (Layer l = layers.baseLayers; l != null;) { switch (l.type) { case Layer.POLYGON: - l = PolygonRenderer.draw(pos, l, m, true, false); + l = PolygonRenderer.draw(curPos, l, m, true, false); break; case Layer.LINE: - l = LineRenderer.draw(layers, l, pos, m, div, 0); + l = LineRenderer.draw(layers, l, curPos, m, div, 0); break; case Layer.TEXLINE: - l = LineTexRenderer.draw(layers, l, pos, m, div); + l = LineTexRenderer.draw(layers, l, curPos, m, div); break; } } } if (layers.textureLayers != null) { - setMatrix(pos, m, false); + setMatrix(curPos, m, false); - float scale = (mMapPosition.scale / pos.scale) * div; + float scale = (float)(pos.scale / curPos.scale); for (Layer l = layers.textureLayers; l != null;) { l = TextureRenderer.draw(l, scale, m); diff --git a/src/org/oscim/renderer/overlays/ExtrusionOverlay.java b/src/org/oscim/renderer/overlays/ExtrusionOverlay.java index 84a788a9..db73fda0 100644 --- a/src/org/oscim/renderer/overlays/ExtrusionOverlay.java +++ b/src/org/oscim/renderer/overlays/ExtrusionOverlay.java @@ -19,6 +19,7 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import org.oscim.core.MapPosition; +import org.oscim.core.Tile; import org.oscim.generator.JobTile; import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer.Matrices; @@ -26,7 +27,6 @@ import org.oscim.renderer.GLState; import org.oscim.renderer.MapTile; import org.oscim.renderer.TileSet; import org.oscim.renderer.layer.ExtrusionLayer; -import org.oscim.utils.FastMath; import org.oscim.utils.GlUtils; import org.oscim.view.MapView; @@ -173,7 +173,7 @@ public class ExtrusionOverlay extends RenderOverlay { MapTile[] tiles = mTiles; - float div = FastMath.pow(tiles[0].zoomLevel - pos.zoomLevel); + //float div = FastMath.pow(tiles[0].zoomLevel - pos.zoomLevel); int shaderMode = 1; int uExtAlpha = hAlpha[shaderMode]; @@ -195,7 +195,7 @@ public class ExtrusionOverlay extends RenderOverlay { for (int i = 0; i < mTileCnt; i++) { ExtrusionLayer el = (ExtrusionLayer) tiles[i].layers.extrusionLayers; - setMatrix(pos, m, tiles[i], div, 0); + setMatrix(pos, m, tiles[i], 0); m.mvp.setAsUniform(uExtMatrix); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID); @@ -230,7 +230,7 @@ public class ExtrusionOverlay extends RenderOverlay { GLState.useProgram(shaderProgram[shaderMode]); GLState.enableVertexArrays(uExtVertexPosition, -1); - if (pos.scale < 2) { + if (pos.scale < (1 << 18)) { // chances are high that one moves through a building // with scale > 2 also draw back sides in this case. GLES20.glEnable(GLES20.GL_CULL_FACE); @@ -247,7 +247,7 @@ public class ExtrusionOverlay extends RenderOverlay { MapTile t = tiles[i]; ExtrusionLayer el = (ExtrusionLayer) t.layers.extrusionLayers; int d = GLRenderer.depthOffset(t) * 10; - setMatrix(pos, m, t, div, d); + setMatrix(pos, m, t, d); m.mvp.setAsUniform(uExtMatrix); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID); @@ -273,7 +273,7 @@ public class ExtrusionOverlay extends RenderOverlay { GLES20.glDepthFunc(GLES20.GL_EQUAL); int d = GLRenderer.depthOffset(t) * 10; - setMatrix(pos, m, t, div, d); + setMatrix(pos, m, t, d); m.mvp.setAsUniform(uExtMatrix); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, el.mIndicesBufferID); @@ -316,19 +316,28 @@ public class ExtrusionOverlay extends RenderOverlay { tiles[i] = null; } - if (pos.scale < 2) + if (pos.scale < (1 << 18)) GLES20.glDisable(GLES20.GL_CULL_FACE); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); } - private static void setMatrix(MapPosition mapPosition, Matrices m, - MapTile tile, float div, int delta) { + private static void setMatrix(MapPosition pos, Matrices m, + MapTile tile, int delta) { - float x = (float) (tile.pixelX - mapPosition.x * div); - float y = (float) (tile.pixelY - mapPosition.y * div); - float scale = mapPosition.scale / div; + int z = tile.zoomLevel; + double curScale = Tile.TILE_SIZE * pos.scale; + float scale = (float)(pos.scale / (1 << z)); - m.mvp.setTransScale(x * scale, y * scale, scale / GLRenderer.COORD_SCALE); + float x = (float) ((tile.x - pos.x) * curScale); + float y = (float) ((tile.y - pos.y) * curScale); + m.mvp.setTransScale(x, y, scale / GLRenderer.COORD_SCALE); + +// float x = (float) (tile.pixelX - mapPosition.x * div); +// float y = (float) (tile.pixelY - mapPosition.y * div); +// float scale = mapPosition.scale / div; +// +// m.mvp.setTransScale(x * scale, y * scale, scale / GLRenderer.COORD_SCALE); // scale height m.mvp.setValue(10, scale / 10); diff --git a/src/org/oscim/renderer/overlays/GridOverlay.java b/src/org/oscim/renderer/overlays/GridOverlay.java index 8b14f98d..fad9e882 100644 --- a/src/org/oscim/renderer/overlays/GridOverlay.java +++ b/src/org/oscim/renderer/overlays/GridOverlay.java @@ -93,7 +93,6 @@ public class GridOverlay extends BasicOverlay { } } tl.prepare(); - layers.textureLayers = tl; } @@ -101,30 +100,28 @@ public class GridOverlay extends BasicOverlay { private int mCurY = -1; private int mCurZ = -1; - private boolean finished; - - @Override public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged, Matrices matrices) { - mMapPosition.copy(curPos); + int z = 1 << curPos.zoomLevel; - // fix map position to tile coordinates - float size = Tile.TILE_SIZE; - int x = (int) (mMapPosition.x / size); - int y = (int) (mMapPosition.y / size); - mMapPosition.x = x * size; - mMapPosition.y = y * size; - - if (!finished) - mMapPosition.scale = 1; + int x = (int) (curPos.x * z); + int y = (int) (curPos.y * z); // update layers when map moved by at least one tile - if (x != mCurX || y != mCurY || mMapPosition.zoomLevel != mCurZ) { + if (x != mCurX || y != mCurY || z != mCurZ) { + + MapPosition pos = mMapPosition; + + pos.copy(curPos); + pos.x = (double) x / z; + pos.y = (double) y / z; + pos.scale = z; + mCurX = x; mCurY = y; - mCurZ = mMapPosition.zoomLevel; + mCurZ = z; layers.clear(); @@ -134,9 +131,7 @@ public class GridOverlay extends BasicOverlay { ll.addLine(mPoints, mIndex, false); addLabels(x, y, mCurZ); - newData = true; - finished = false; } } } diff --git a/src/org/oscim/renderer/overlays/RenderOverlay.java b/src/org/oscim/renderer/overlays/RenderOverlay.java index f27a65b2..ed07f28a 100644 --- a/src/org/oscim/renderer/overlays/RenderOverlay.java +++ b/src/org/oscim/renderer/overlays/RenderOverlay.java @@ -18,7 +18,6 @@ import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.renderer.GLRenderer; import org.oscim.renderer.GLRenderer.Matrices; -import org.oscim.utils.FastMath; import org.oscim.view.MapView; public abstract class RenderOverlay { @@ -51,7 +50,8 @@ public abstract class RenderOverlay { * true when current tiles changed * @param matrices TODO */ - public abstract void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged, Matrices matrices); + public abstract void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged, + Matrices matrices); /** * called 2. compile everything for drawing @@ -70,44 +70,40 @@ public abstract class RenderOverlay { public abstract void render(MapPosition pos, Matrices m); /** - * Utility: set m.mvp matrix relative to the difference of current MapPosition + * Utility: set m.mvp matrix relative to the difference of current + * MapPosition * and the last updated Overlay MapPosition * - * @param curPos ... - * @param m ... + * @param curPos + * current MapPosition + * @param m + * current Matrices * @param project - * apply view and projection, or just view otherwise + * apply view and projection, or just view otherwise */ protected void setMatrix(MapPosition curPos, Matrices m, boolean project) { MapPosition oPos = mMapPosition; - float div = FastMath.pow(oPos.zoomLevel - curPos.zoomLevel); + double tileScale = Tile.TILE_SIZE * curPos.scale; - // translate relative to map center - float x = (float) (oPos.x - curPos.x * div); - float y = (float) (oPos.y - curPos.y * div); + double x = oPos.x - curPos.x; + double y = oPos.y - curPos.y; - // flip around date-line - float max = (Tile.TILE_SIZE << oPos.zoomLevel); - if (x < -max / 2) - x = max + x; - else if (x > max / 2) - x = x - max; + // wrap around date-line + while (x < 0) + x += 1.0; + while (x > 1) + x -= 1.0; - // scale to current tile world coordinates - float scale = curPos.scale / div; - - // set scale to be relative to current scale - float s = (curPos.scale / oPos.scale) / div; - - m.mvp.setTransScale(x * scale, y * scale, - s / GLRenderer.COORD_SCALE); + m.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), + (float) ((curPos.scale / oPos.scale) / GLRenderer.COORD_SCALE)); m.mvp.multiplyMM(project ? m.viewproj : m.view, m.mvp); } /** - * Utility: set m.mvp matrix relative to the difference of current MapPosition + * Utility: set m.mvp matrix relative to the difference of current + * MapPosition * and the last updated Overlay MapPosition and add m.viewproj * * @param curPos ... @@ -116,13 +112,4 @@ public abstract class RenderOverlay { protected void setMatrix(MapPosition curPos, Matrices m) { setMatrix(curPos, m, true); } - -// /** -// * Utility: update mMapPosition -// * -// * @return true if position has changed -// */ -// protected boolean updateMapPosition() { -// return mMapView.getMapViewPosition().getMapPosition(mMapPosition); -// } } diff --git a/src/org/oscim/renderer/overlays/TextOverlay.java b/src/org/oscim/renderer/overlays/TextOverlay.java index 38246d95..a4f6ff26 100644 --- a/src/org/oscim/renderer/overlays/TextOverlay.java +++ b/src/org/oscim/renderer/overlays/TextOverlay.java @@ -246,6 +246,7 @@ public class TextOverlay extends BasicOverlay { private Layers mDebugLayer; private final static float[] mDebugPoints = new float[4]; + //private final Matrix4 mMVP = new Matrix4(); //void addTile(MapTile t) { @@ -313,9 +314,15 @@ public class TextOverlay extends BasicOverlay { // this scales MapPosition to the zoomlevel of mTiles... // TODO create a helper function in MapPosition MapTile[] tiles = mTileSet.tiles; + + //int zoom = tiles[0].zoomLevel; + int diff = tiles[0].zoomLevel - pos.zoomLevel; float div = FastMath.pow(diff); - float scale = pos.scale * div; + + double scale = pos.getZoomScale() * div; + + //float scale = (float)(pos.absScale / (1 << zoom) ); double angle = Math.toRadians(pos.angle); float cos = (float) Math.cos(angle); @@ -330,11 +337,17 @@ public class TextOverlay extends BasicOverlay { mRelabelCnt++; + double tileX = (pos.x * (Tile.TILE_SIZE << pos.zoomLevel)); + double tileY = (pos.y * (Tile.TILE_SIZE << pos.zoomLevel)); + for (l = mPrevLabels; l != null;) { // transform screen coordinates to tile coordinates float s = FastMath.pow(l.tile.zoomLevel - pos.zoomLevel); - float sscale = pos.scale / s; + + //float sscale = (float)((1 << l.tile.zoomLevel) / pos.absScale); + + float sscale = (float) (pos.getZoomScale() / s); if (l.width > l.length * sscale) { //Log.d(TAG, "- scale " + lp + " " + s + " " + sscale + " " + lp.length + " " + lp.width); @@ -342,8 +355,8 @@ public class TextOverlay extends BasicOverlay { continue; } - float dx = (float) (l.tile.pixelX - pos.x * s); - float dy = (float) (l.tile.pixelY - pos.y * s); + float dx = (float) (l.tile.tileX * Tile.TILE_SIZE - tileX * s); + float dy = (float) (l.tile.tileY * Tile.TILE_SIZE - tileY * s); // flip around date-line if (dx > maxx) @@ -399,8 +412,8 @@ public class TextOverlay extends BasicOverlay { if (t.state != JobTile.STATE_READY) continue; - float dx = (float) (t.pixelX - pos.x); - float dy = (float) (t.pixelY - pos.y); + float dx = (float) (t.tileX * Tile.TILE_SIZE - tileX); + float dy = (float) (t.tileY * Tile.TILE_SIZE - tileY); // flip around date-line if (dx > maxx) @@ -422,7 +435,7 @@ public class TextOverlay extends BasicOverlay { continue; l.clone(ti); - l.move(ti, dx, dy, scale); + l.move(ti, dx, dy, (float) scale); // set line endpoints relative to view to be able to // check intersections with label from other tiles @@ -448,7 +461,7 @@ public class TextOverlay extends BasicOverlay { overlaps = checkOverlap(tl, l); if (dbg != null) - addDebugBox(dbg, l, ti, overlaps, false, scale); + addDebugBox(dbg, l, ti, overlaps, false, (float) scale); if (overlaps == 0) { tl.addText(l); @@ -466,8 +479,8 @@ public class TextOverlay extends BasicOverlay { if (t.state != JobTile.STATE_READY) continue; - float dx = (float) (t.pixelX - pos.x); - float dy = (float) (t.pixelY - pos.y); + float dx = (float) (t.tileX * Tile.TILE_SIZE - tileX); + float dy = (float) (t.tileY * Tile.TILE_SIZE - tileY); // flip around date-line if (dx > maxx) @@ -484,7 +497,7 @@ public class TextOverlay extends BasicOverlay { l = getLabel(); l.clone(ti); - l.move(ti, dx, dy, scale); + l.move(ti, dx, dy, (float) scale); if (!nodeIsVisible(l)) continue; @@ -678,7 +691,7 @@ public class TextOverlay extends BasicOverlay { @Override public synchronized void render(MapPosition pos, Matrices m) { - float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); + //float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, layers.vbo.id); GLState.test(false, false); @@ -690,7 +703,10 @@ public class TextOverlay extends BasicOverlay { if (l.type == Layer.POLYGON) { l = PolygonRenderer.draw(pos, l, m, true, false); } else { - float scale = pos.scale * div; + //float scale = pos.getScale() * div; + + float scale = (float) ((1 << mMapPosition.zoomLevel) / pos.scale); + l = LineRenderer.draw(layers, l, pos, m, scale, 0); } } @@ -698,7 +714,8 @@ public class TextOverlay extends BasicOverlay { setMatrix(pos, m); for (Layer l = layers.textureLayers; l != null;) { - float scale = (mMapPosition.scale / pos.scale) * div; + float scale = (float) (mMapPosition.scale / pos.scale); + //float scale = (mMapPosition.getScale() / pos.getScale()) * div; l = TextureRenderer.draw(l, scale, m); } @@ -709,14 +726,22 @@ public class TextOverlay extends BasicOverlay { protected void setMatrix(MapPosition curPos, Matrices m) { MapPosition oPos = mMapPosition; - float div = FastMath.pow(oPos.zoomLevel - curPos.zoomLevel); - float x = (float) (oPos.x - curPos.x * div); - float y = (float) (oPos.y - curPos.y * div); + double tileScale = Tile.TILE_SIZE * curPos.scale; + double scale = (curPos.scale / oPos.scale); - float scale = (curPos.scale / mMapPosition.scale) / div; - float s = curPos.scale / div; - m.mvp.setTransScale(x * s, y * s, - scale / GLRenderer.COORD_SCALE); + float x = (float) ((oPos.x - curPos.x) * tileScale); + float y = (float) ((oPos.y - curPos.y) * tileScale); + + m.mvp.setTransScale(x, y, (float) (scale / GLRenderer.COORD_SCALE)); + + // float div = FastMath.pow(oPos.zoomLevel - curPos.zoomLevel); + // float x = (float) (oPos.x - curPos.x * div); + // float y = (float) (oPos.y - curPos.y * div); + // + // float scale = (curPos.getScale() / oPos.getScale()) / div; + // float s = curPos.getScale() / div; + // m.mvp.setTransScale(x * s, y * s, + // scale / GLRenderer.COORD_SCALE); m.mvp.multiplyMM(m.view, m.mvp); } diff --git a/src/org/oscim/view/Compass.java b/src/org/oscim/view/Compass.java index 61a6b38e..64675f1f 100644 --- a/src/org/oscim/view/Compass.java +++ b/src/org/oscim/view/Compass.java @@ -29,7 +29,7 @@ public class Compass { mAngle = event.values[0]; if (mMapView != null) { - mMapView.getMapPosition().setRotation(-mAngle); + mMapView.getMapViewPosition().setRotation(-mAngle); mMapView.redrawMap(true); } } @@ -43,8 +43,8 @@ public class Compass { /* package */float mAngle = 0; /* package */MapView mMapView; - private SensorManager mSensorManager; - private Sensor mSensor; + private final SensorManager mSensorManager; + private final Sensor mSensor; public Compass(MapActivity mapActivity, MapView mapView) { mMapView = mapView; @@ -61,6 +61,6 @@ public class Compass { void disable() { mSensorManager.unregisterListener(mListener); - mMapView.getMapPosition().setRotation(0); + mMapView.getMapViewPosition().setRotation(0); } } diff --git a/src/org/oscim/view/MapActivity.java b/src/org/oscim/view/MapActivity.java index 871820b4..c5e57eae 100644 --- a/src/org/oscim/view/MapActivity.java +++ b/src/org/oscim/view/MapActivity.java @@ -40,14 +40,15 @@ import android.content.SharedPreferences.Editor; public abstract class MapActivity extends Activity { private static final String KEY_LATITUDE = "latitude"; private static final String KEY_LONGITUDE = "longitude"; - private static final String KEY_ZOOM_LEVEL = "zoomLevel"; + private static final String KEY_MAP_SCALE = "map_scale"; + private static final String PREFERENCES_FILE = "MapActivity"; private static final String KEY_THEME = "Theme"; private static boolean containsMapViewPosition(SharedPreferences sharedPreferences) { return sharedPreferences.contains(KEY_LATITUDE) && sharedPreferences.contains(KEY_LONGITUDE) - && sharedPreferences.contains(KEY_ZOOM_LEVEL); + && sharedPreferences.contains(KEY_MAP_SCALE); } protected MapView mMapView; @@ -66,14 +67,16 @@ public abstract class MapActivity extends Activity { Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit(); editor.clear(); - // save the map position and zoom level - MapPosition mapPosition = mMapView.getMapPosition().getMapPosition(); - if (mapPosition != null) { - GeoPoint geoPoint = new GeoPoint(mapPosition.lat, mapPosition.lon); - editor.putInt(KEY_LATITUDE, geoPoint.latitudeE6); - editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6); - editor.putInt(KEY_ZOOM_LEVEL, mapPosition.zoomLevel); - } + // save the map position + MapPosition mapPosition = new MapPosition(); + + mMapView.getMapViewPosition().getMapPosition(mapPosition); + + GeoPoint geoPoint = mapPosition.getGeoPoint(); + + editor.putInt(KEY_LATITUDE, geoPoint.latitudeE6); + editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6); + editor.putFloat(KEY_MAP_SCALE, (float)mapPosition.scale); editor.putString(KEY_THEME, mMapView.getRenderTheme()); @@ -108,12 +111,14 @@ public abstract class MapActivity extends Activity { // get and set the map position and zoom level int latitudeE6 = sharedPreferences.getInt(KEY_LATITUDE, 0); int longitudeE6 = sharedPreferences.getInt(KEY_LONGITUDE, 0); - int zoomLevel = sharedPreferences.getInt(KEY_ZOOM_LEVEL, -1); + float scale = sharedPreferences.getFloat(KEY_MAP_SCALE, 1); - GeoPoint geoPoint = new GeoPoint(latitudeE6, longitudeE6); - MapPosition mapPosition = new MapPosition(geoPoint, (byte) zoomLevel, 1); - mMapView.getMapViewPosition().setMapCenter(mapPosition); + MapPosition mapPosition = new MapPosition(); + mapPosition.setPosition(latitudeE6 / 1E6, longitudeE6 / 1E6); + mapPosition.setScale(scale); + + mMapView.getMapViewPosition().setMapPosition(mapPosition); } String theme = sharedPreferences.getString(KEY_THEME, diff --git a/src/org/oscim/view/MapScaleBar.java b/src/org/oscim/view/MapScaleBar.java index 6eacf3be..3d93c52d 100644 --- a/src/org/oscim/view/MapScaleBar.java +++ b/src/org/oscim/view/MapScaleBar.java @@ -18,7 +18,6 @@ import java.util.HashMap; import java.util.Map; import org.oscim.core.MapPosition; -import org.oscim.core.MercatorProjection; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -173,17 +172,17 @@ public class MapScaleBar { return true; } - MapPosition currentMapPosition = mMapView.getMapPosition().getMapPosition(); +// MapPosition mapPosition = mMapView.getMapPosition().getMapPosition(); +// +// if (mapPosition.zoomLevel != mMapPosition.zoomLevel) { +// return true; +// } - if (currentMapPosition.zoomLevel != mMapPosition.zoomLevel) { - return true; - } - - double latitudeDiff = Math.abs(currentMapPosition.lat - - mMapPosition.lat); - if (latitudeDiff > LATITUDE_REDRAW_THRESHOLD) { - return true; - } +// double latitudeDiff = Math.abs(mapPosition.lat +// - mMapPosition.lat); +// if (latitudeDiff > LATITUDE_REDRAW_THRESHOLD) { +// return true; +// } return false; } @@ -250,31 +249,31 @@ public class MapScaleBar { return; } - mMapPosition = mMapView.getMapPosition().getMapPosition(); - double groundResolution = MercatorProjection.calculateGroundResolution( - mMapPosition.lat, - mMapPosition.zoomLevel); - - int[] scaleBarValues; - if (mImperialUnits) { - groundResolution = groundResolution / METER_FOOT_RATIO; - scaleBarValues = SCALE_BAR_VALUES_IMPERIAL; - } else { - scaleBarValues = SCALE_BAR_VALUES_METRIC; - } - - float scaleBarLength = 0; - int mapScaleValue = 0; - - for (int i = 0; i < scaleBarValues.length; ++i) { - mapScaleValue = scaleBarValues[i]; - scaleBarLength = mapScaleValue / (float) groundResolution; - if (scaleBarLength < (BITMAP_WIDTH - 10)) { - break; - } - } - - redrawMapScaleBitmap(scaleBarLength, mapScaleValue); +// mMapPosition = mMapView.getMapPosition().getMapPosition(); +// double groundResolution = MercatorProjection.calculateGroundResolution( +// mMapPosition.lat, +// mMapPosition.zoomLevel); +// +// int[] scaleBarValues; +// if (mImperialUnits) { +// groundResolution = groundResolution / METER_FOOT_RATIO; +// scaleBarValues = SCALE_BAR_VALUES_IMPERIAL; +// } else { +// scaleBarValues = SCALE_BAR_VALUES_METRIC; +// } +// +// float scaleBarLength = 0; +// int mapScaleValue = 0; +// +// for (int i = 0; i < scaleBarValues.length; ++i) { +// mapScaleValue = scaleBarValues[i]; +// scaleBarLength = mapScaleValue / (float) groundResolution; +// if (scaleBarLength < (BITMAP_WIDTH - 10)) { +// break; +// } +// } +// +// redrawMapScaleBitmap(scaleBarLength, mapScaleValue); mRedrawNeeded = false; } } diff --git a/src/org/oscim/view/MapView.java b/src/org/oscim/view/MapView.java index 8383d7ba..900c29fc 100644 --- a/src/org/oscim/view/MapView.java +++ b/src/org/oscim/view/MapView.java @@ -39,7 +39,6 @@ import org.oscim.generator.MapWorker; import org.oscim.generator.TileGenerator; import org.oscim.overlay.BuildingOverlay; import org.oscim.overlay.LabelingOverlay; -import org.oscim.overlay.MapLensOverlay; import org.oscim.overlay.Overlay; import org.oscim.overlay.OverlayManager; import org.oscim.renderer.GLRenderer; @@ -175,11 +174,6 @@ public class MapView extends RelativeLayout { mapActivity.registerMapView(this); - if (!mMapViewPosition.isValid()) { - Log.d(TAG, "set default start position"); - setMapCenter(new MapPosition(new GeoPoint(0, 0), (byte) 2, 1)); - } - LayoutParams params = new LayoutParams( android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT); @@ -191,20 +185,63 @@ public class MapView extends RelativeLayout { mRotationEnabled = true; //mOverlayManager.add(new GenericOverlay(this, new GridOverlay(this))); - mOverlayManager.add(new BuildingOverlay(this)); + mOverlayManager.add(new BuildingOverlay(this)); mOverlayManager.add(new LabelingOverlay(this)); - //mOverlayManager.add(new GenericOverlay(this, new TileOverlay(this))); - mOverlayManager.add(new MapLensOverlay(this)); - //mOverlayManager.add(new GenericOverlay(this, new CustomOverlay(this))); + //mOverlayManager.add(new MapLensOverlay(this)); - // if (testRegionZoom) - // mRegionLookup = new RegionLookup(this); clearMap(); + } + void destroy() { + mTileManager.destroy(); + + for (MapWorker mapWorker : mMapWorkers) { + mapWorker.pause(); + mapWorker.interrupt(); + + mapWorker.getTileGenerator().getMapDatabase().close(); + + try { + mapWorker.join(10000); + } catch (InterruptedException e) { + // restore the interrupted status + Thread.currentThread().interrupt(); + } + + } + } + + private boolean mPausing = false; + + void onPause() { + mPausing = true; + + Log.d(TAG, "onPause"); + mJobQueue.clear(); + mapWorkersPause(true); + + if (this.mCompassEnabled) + mCompass.disable(); + + } + + void onResume() { + Log.d(TAG, "onResume"); + mapWorkersProceed(); + + if (this.mCompassEnabled) + mCompass.enable(); + + mPausing = false; + } + + public void onStop() { + Log.d(TAG, "onStop"); + //mTileManager.destroy(); } @Override @@ -243,13 +280,6 @@ public class MapView extends RelativeLayout { mGLView.requestRender(); } - /** - * @return the current position and zoom level of this MapView. - */ - public MapViewPosition getMapPosition() { - return mMapViewPosition; - } - public void enableRotation(boolean enable) { mRotationEnabled = enable; @@ -356,10 +386,19 @@ public class MapView extends RelativeLayout { if (startPos == null) startPos = new GeoPoint(0, 0); - if (mapInfo.startZoomLevel != null) - return new MapPosition(startPos, (mapInfo.startZoomLevel).byteValue(), 1); + MapPosition mapPosition = new MapPosition(); + mapPosition.setPosition(startPos); - return new MapPosition(startPos, (byte) 12, 1); + if (mapInfo.startZoomLevel == null) + mapPosition.setZoomLevel(12); + else + mapPosition.setZoomLevel((mapInfo.startZoomLevel).byteValue()); + + return mapPosition; + } + + public void setMapPosition(MapPosition mapPosition) { + mMapViewPosition.setMapPosition(mapPosition); } /** @@ -505,95 +544,6 @@ public class MapView extends RelativeLayout { return false; } - void destroy() { - for (MapWorker mapWorker : mMapWorkers) { - mapWorker.pause(); - mapWorker.interrupt(); - - mapWorker.getTileGenerator().getMapDatabase().close(); - - try { - mapWorker.join(10000); - } catch (InterruptedException e) { - // restore the interrupted status - Thread.currentThread().interrupt(); - } - - } - } - - private boolean mPausing = false; - - void onPause() { - mPausing = true; - - Log.d(TAG, "onPause"); - mJobQueue.clear(); - mapWorkersPause(true); - - if (this.mCompassEnabled) - mCompass.disable(); - - } - - void onResume() { - Log.d(TAG, "onResume"); - mapWorkersProceed(); - - if (this.mCompassEnabled) - mCompass.enable(); - - mPausing = false; - } - - public void onStop() { - Log.d(TAG, "onStop"); - mTileManager.destroy(); - } - - /** - * @return the maximum possible zoom level. - */ - byte getMaximumPossibleZoomLevel() { - return (byte) MapViewPosition.MAX_ZOOMLEVEL; - // Math.min(mMapZoomControls.getZoomLevelMax(), - // mMapGenerator.getZoomLevelMax()); - } - - /** - * @return true if the current center position of this MapView is valid, - * false otherwise. - */ - boolean hasValidCenter() { - MapInfo mapInfo; - - if (!mMapViewPosition.isValid()) - return false; - - if ((mapInfo = mMapDatabase.getMapInfo()) == null) - return false; - - if (!mapInfo.boundingBox.contains(getMapPosition().getMapCenter())) - return false; - - return true; - } - - /** - * Sets the center and zoom level of this MapView and triggers a redraw. - * - * @param mapPosition - * the new map position of this MapView. - */ - public void setMapCenter(MapPosition mapPosition) { - Log.d(TAG, "setMapCenter " - + " lat: " + mapPosition.lat - + " lon: " + mapPosition.lon); - - mMapViewPosition.setMapCenter(mapPosition); - redrawMap(true); - } - /** * Sets the center of the MapView and triggers a redraw. * @@ -607,7 +557,7 @@ public class MapView extends RelativeLayout { } /** - * @return MapPosition + * @return MapViewPosition */ public MapViewPosition getMapViewPosition() { return mMapViewPosition; @@ -671,11 +621,10 @@ public class MapView extends RelativeLayout { return mTileManager; } + /** + * @return estimated visible axis aligned bounding box + */ public BoundingBox getBoundingBox() { return mMapViewPosition.getViewBox(); } - - public GeoPoint getCenter() { - return new GeoPoint(mMapPosition.lat, mMapPosition.lon); - } } diff --git a/src/org/oscim/view/MapViewPosition.java b/src/org/oscim/view/MapViewPosition.java index ea66e355..1cffaabb 100644 --- a/src/org/oscim/view/MapViewPosition.java +++ b/src/org/oscim/view/MapViewPosition.java @@ -34,30 +34,22 @@ import android.os.SystemClock; import android.util.Log; import android.view.animation.AccelerateDecelerateInterpolator; -/** - * A MapPosition stores the latitude and longitude coordinate of a MapView - * together with its zoom level, rotation and tilt - */ - public class MapViewPosition { private static final String TAG = MapViewPosition.class.getName(); - public final static int MAX_ZOOMLEVEL = 17; + // needs to fit for int: 2 * 20 * Tile.TILE_SIZE + public final static int MAX_ZOOMLEVEL = 20; public final static int MIN_ZOOMLEVEL = 2; - public final static int MAX_END_SCALE = 8; - public final static double MAX_SCALE = ((1 << MAX_ZOOMLEVEL) * MAX_END_SCALE); + + public final static double MAX_SCALE = (1 << MAX_ZOOMLEVEL); public final static double MIN_SCALE = (1 << MIN_ZOOMLEVEL); - // needs to fit for int: 2 * 20 * Tile.TILE_SIZE public final static int ABS_ZOOMLEVEL = 20; private final static float MAX_ANGLE = 65; private final MapView mMapView; - private double mLatitude; - private double mLongitude; - private double mAbsScale; private double mAbsX; private double mAbsY; @@ -73,20 +65,7 @@ public class MapViewPosition { private double mCurY; private float mRotation; - public float mTilt; - - private final AnimationHandler mHandler; - - MapViewPosition(MapView mapView) { - mMapView = mapView; - mLatitude = Double.NaN; - mLongitude = Double.NaN; - mRotation = 0.0f; - mTilt = 0; - mAbsScale = 1; - - mHandler = new AnimationHandler(this); - } + private float mTilt; private final Matrix4 mProjMatrix = new Matrix4(); private final Matrix4 mProjMatrixI = new Matrix4(); @@ -110,6 +89,29 @@ public class MapViewPosition { // scale map plane at VIEW_DISTANCE to near plane public final static float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f; + private final AnimationHandler mHandler; + + MapViewPosition(MapView mapView) { + mMapView = mapView; + + mAbsScale = 4; + mAbsX = 0.5; + mAbsY = 0.5; + + mRotation = 0; + mTilt = 0; + + updatePosition(); + + mHandler = new AnimationHandler(this); + } + + private void updatePosition() { + mCurScale = mAbsScale * Tile.TILE_SIZE; + mCurX = mAbsX * mCurScale; + mCurY = mAbsY * mCurScale; + } + void setViewport(int width, int height) { float s = VIEW_SCALE; float aspect = height / (float) width; @@ -141,34 +143,27 @@ public class MapViewPosition { public synchronized boolean getMapPosition(MapPosition pos) { int z = FastMath.log2((int) mAbsScale); - z = FastMath.clamp(z, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL); - float scale = (float) (mAbsScale / (1 << z)); + //z = FastMath.clamp(z, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL); + //float scale = (float) (mAbsScale / (1 << z)); - if (pos.lat == mLatitude - && pos.lon == mLongitude - && pos.zoomLevel == z - && pos.scale == scale - && pos.angle == mRotation - && pos.tilt == mTilt) - return false; + boolean changed = (pos.zoomLevel != z + || pos.x != mAbsX + || pos.y != mAbsY + || pos.scale != mAbsScale + || pos.angle != mRotation + || pos.tilt != mTilt); - pos.lat = mLatitude; - pos.lon = mLongitude; pos.angle = mRotation; pos.tilt = mTilt; - pos.absX = mAbsX; - pos.absY = mAbsY; - pos.absScale = mAbsScale; + pos.x = mAbsX; + pos.y = mAbsY; + pos.scale = mAbsScale; // for tiling - pos.scale = scale; - pos.zoomLevel = (byte) z; + pos.zoomLevel = z; - pos.x = mAbsX * (Tile.TILE_SIZE << z); - pos.y = mAbsY * (Tile.TILE_SIZE << z); - - return true; + return changed; } /** @@ -250,23 +245,8 @@ public class MapViewPosition { /** @return the current center point of the MapView. */ public synchronized GeoPoint getMapCenter() { - return new GeoPoint(mLatitude, mLongitude); - } - - /** - * @return a MapPosition or null, if this map position is not valid. - * @see #isValid() - */ - public synchronized MapPosition getMapPosition() { - if (!isValid()) { - return null; - } - - int z = FastMath.log2((int) mAbsScale); - z = FastMath.clamp(z, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL); - float scale = (float) (mAbsScale / (1 << z)); - - return new MapPosition(mLatitude, mLongitude, (byte) z, scale, mRotation); + return new GeoPoint(MercatorProjection.toLatitude(mAbsY), + MercatorProjection.toLongitude(mAbsX)); } /** @@ -303,10 +283,10 @@ public class MapViewPosition { maxY = Math.max(maxY, dy); } - minX = MercatorProjection.toLongitude(minX, mCurScale); - maxX = MercatorProjection.toLongitude(maxX, mCurScale); - minY = MercatorProjection.toLatitude(minY, mCurScale); - maxY = MercatorProjection.toLatitude(maxY, mCurScale); + minX = MercatorProjection.toLongitude(minX / mCurScale); + maxX = MercatorProjection.toLongitude(maxX / mCurScale); + minY = MercatorProjection.toLatitude(minY / mCurScale); + maxY = MercatorProjection.toLatitude(maxY / mCurScale); // yea, this is upside down.. BoundingBox bbox = new BoundingBox(maxY, minX, minY, maxX); @@ -446,27 +426,6 @@ public class MapViewPosition { mUnprojMatrix.multiplyMM(mTmpMatrix, mProjMatrixI); } - /** @return true if this MapViewPosition is valid, false otherwise. */ - public synchronized boolean isValid() { - if (Double.isNaN(mLatitude)) { - return false; - } else if (mLatitude < MercatorProjection.LATITUDE_MIN) { - return false; - } else if (mLatitude > MercatorProjection.LATITUDE_MAX) { - return false; - } - - if (Double.isNaN(mLongitude)) { - return false; - } else if (mLongitude < MercatorProjection.LONGITUDE_MIN) { - return false; - } else if (mLongitude > MercatorProjection.LONGITUDE_MAX) { - return false; - } - - return true; - } - /** * Moves this MapViewPosition by the given amount of pixels. * @@ -495,9 +454,6 @@ public class MapViewPosition { while (mAbsX < 0) mAbsX += 1; - mLongitude = MercatorProjection.toLongitude(mAbsX); - mLatitude = MercatorProjection.toLatitude(mAbsY); - updatePosition(); } @@ -516,9 +472,6 @@ public class MapViewPosition { while (mAbsX < 0) mAbsX += 1; - mLongitude = MercatorProjection.toLongitude(mAbsX); - mLatitude = MercatorProjection.toLatitude(mAbsY); - updatePosition(); } @@ -615,25 +568,26 @@ public class MapViewPosition { updateMatrix(); } + public synchronized float getTilt() { + return mTilt; + } + private void setMapCenter(double latitude, double longitude) { - mLatitude = MercatorProjection.limitLatitude(latitude); - mLongitude = MercatorProjection.limitLongitude(longitude); - mAbsX = MercatorProjection.longitudeToX(mLongitude); - mAbsY = MercatorProjection.latitudeToY(mLatitude); + latitude = MercatorProjection.limitLatitude(latitude); + longitude = MercatorProjection.limitLongitude(longitude); + mAbsX = MercatorProjection.longitudeToX(longitude); + mAbsY = MercatorProjection.latitudeToY(latitude); + } + + synchronized void setMapPosition(MapPosition mapPosition) { + setZoomLevelLimit(mapPosition.zoomLevel); + mAbsX = mapPosition.x; + mAbsY = mapPosition.y; updatePosition(); } synchronized void setMapCenter(GeoPoint geoPoint) { setMapCenter(geoPoint.getLatitude(), geoPoint.getLongitude()); - } - - synchronized void setMapCenter(MapPosition mapPosition) { - setZoomLevelLimit(mapPosition.zoomLevel); - setMapCenter(mapPosition.lat, mapPosition.lon); - } - - synchronized void setZoomLevel(byte zoomLevel) { - setZoomLevelLimit(zoomLevel); updatePosition(); } @@ -641,13 +595,6 @@ public class MapViewPosition { mAbsScale = FastMath.clamp(1 << zoomLevel, MIN_SCALE, MAX_SCALE); } - private void updatePosition() { - mCurScale = mAbsScale * Tile.TILE_SIZE; - - mCurX = mAbsX * mCurScale; - mCurY = mAbsY * mCurScale; - } - /************************************************************************/ // TODO move to MapAnimator: diff --git a/src/org/oscim/view/TouchHandler.java b/src/org/oscim/view/TouchHandler.java index 90b8937a..0ad79bd9 100644 --- a/src/org/oscim/view/TouchHandler.java +++ b/src/org/oscim/view/TouchHandler.java @@ -78,7 +78,7 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener { */ public TouchHandler(Context context, MapView mapView) { mMapView = mapView; - mMapPosition = mapView.getMapPosition(); + mMapPosition = mapView.getMapViewPosition(); mOverlayManager = mapView.getOverlayManager(); mGestureDetector = new GestureDetector(context, this); mGestureDetector.setOnDoubleTapListener(this);