avoid zoom-level relative calculation

This commit is contained in:
Hannes Janetzek 2013-03-12 16:07:19 +01:00
parent c14d101aef
commit 5230aee091
10 changed files with 906 additions and 853 deletions

View File

@ -16,6 +16,7 @@
*/ */
package org.oscim.core; package org.oscim.core;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@ -74,6 +75,11 @@ public class GeoPoint implements Parcelable, Comparable<GeoPoint> {
this(latitudeE6 / CONVERSION_FACTOR, longitudeE6 / CONVERSION_FACTOR); this(latitudeE6 / CONVERSION_FACTOR, longitudeE6 / CONVERSION_FACTOR);
} }
public void project(Point2D out) {
out.x = MercatorProjection.longitudeToX(this.longitudeE6 / CONVERSION_FACTOR);
out.y = MercatorProjection.latitudeToY(this.latitudeE6 / CONVERSION_FACTOR);
}
@Override @Override
public int compareTo(GeoPoint geoPoint) { public int compareTo(GeoPoint geoPoint) {
if (this.longitudeE6 > geoPoint.longitudeE6) { if (this.longitudeE6 > geoPoint.longitudeE6) {

View File

@ -27,6 +27,7 @@ public class MapPosition {
public float angle; public float angle;
public float tilt; public float tilt;
// map center in tile coordinates of current zoom-level
public double x; public double x;
public double y; public double y;
@ -40,21 +41,6 @@ public class MapPosition {
this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel); this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel);
} }
// public Point geopointToMap(GeoPoint in, Point reuse) {
// Point out = reuse == null ? new Point() : reuse;
// out.x = (int) (MercatorProjection.longitudeToPixelX(in.getLongitude(), zoomLevel) - x);
// out.y = (int) (MercatorProjection.latitudeToPixelY(in.getLatitude(), zoomLevel) - y);
//
// return out;
// }
// public void geopointToMap(GeoPoint in, float[] out, int pos) {
// out[pos * 2 + 0] =
// (float) (MercatorProjection.longitudeToPixelX(in.getLongitude(), zoomLevel) - x);
// out[pos * 2 + 1] =
// (float) (MercatorProjection.latitudeToPixelY(in.getLatitude(), zoomLevel) - y);
// }
/** /**
* @param geoPoint * @param geoPoint
* the map position. * the map position.

View File

@ -15,7 +15,6 @@
*/ */
package org.oscim.core; package org.oscim.core;
import android.graphics.Point;
/** /**
* An implementation of the spherical Mercator projection. * An implementation of the spherical Mercator projection.
@ -197,6 +196,13 @@ public final class MercatorProjection {
return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5); 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 pixel X coordinate to the tile X number.
* *
@ -226,6 +232,15 @@ public final class MercatorProjection {
return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI; 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 pixel Y coordinate to the tile Y number.
* *
@ -272,12 +287,14 @@ public final class MercatorProjection {
throw new IllegalStateException(); throw new IllegalStateException();
} }
public static Point projectPoint(GeoPoint geopoint, byte z, Point reuse) { // public static Point projectPoint(GeoPoint geopoint, byte z, Point reuse) {
Point out = reuse == null ? new 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;
// }
out.x = (int) MercatorProjection.longitudeToPixelX(geopoint.getLongitude(), z);
out.y = (int) MercatorProjection.latitudeToPixelY(geopoint.getLatitude(), z);
return out;
}
} }

View File

@ -18,7 +18,6 @@ package org.oscim.overlay;
import java.util.List; import java.util.List;
import org.oscim.app.R; import org.oscim.app.R;
import org.oscim.core.MercatorProjection;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
@ -34,7 +33,6 @@ public class ItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverl
protected final List<Item> mItemList; protected final List<Item> mItemList;
protected OnItemGestureListener<Item> mOnItemGestureListener; protected OnItemGestureListener<Item> mOnItemGestureListener;
private int mDrawnItemsLimit = Integer.MAX_VALUE; private int mDrawnItemsLimit = Integer.MAX_VALUE;
private final Point mTouchScreenPoint = new Point();
private final Point mItemPoint = new Point(); private final Point mItemPoint = new Point();
@ -181,32 +179,29 @@ public class ItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverl
* .. * ..
* @return true if event is handled false otherwise * @return true if event is handled false otherwise
*/ */
private boolean activateSelectedItems(final MotionEvent event, private boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
final ActiveItem task) { int eventX = (int) event.getX() - mMapView.getWidth()/2;
final int eventX = (int) event.getX(); int eventY = (int) event.getY() - mMapView.getHeight()/2;
final int eventY = (int) event.getY();
MapViewPosition mapViewPosition = mMapView.getMapViewPosition(); MapViewPosition mapViewPosition = mMapView.getMapViewPosition();
byte z = mapViewPosition.getMapPosition().zoomLevel;
mapViewPosition.getScreenPointOnMap(eventX, eventY, mTouchScreenPoint);
int nearest = -1; int nearest = -1;
double dist = Double.MAX_VALUE; double dist = Double.MAX_VALUE;
// TODO use intermediate projection and bounding box test // TODO use intermediate projection and bounding box test
for (int i = 0; i < this.mItemList.size(); ++i) { for (int i = 0; i < this.mItemList.size(); i++) {
final Item item = getItem(i); Item item = getItem(i);
// final Drawable marker = (item.getMarker(0) == null) ? this.mDefaultMarker : item // final Drawable marker = (item.getMarker(0) == null) ? this.mDefaultMarker : item
// .getMarker(0); // .getMarker(0);
MercatorProjection.projectPoint(item.getPoint(), z, mItemPoint);
float dx = mItemPoint.x - mTouchScreenPoint.x; mapViewPosition.project(item.getPoint(), mItemPoint);
float dy = mItemPoint.y - mTouchScreenPoint.y;
double d = Math.sqrt(dx * dx + dy * dy);
if (d < 50) { int dx = mItemPoint.x - eventX;
int dy = mItemPoint.y - eventY;
double d = dx * dx + dy * dy;
// squared dist: 50*50 pixel
if (d < 2500) {
if (d < dist) { if (d < dist) {
dist = d; dist = d;
nearest = i; nearest = i;

View File

@ -180,12 +180,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mMapViewPosition = mapView.getMapViewPosition(); mMapViewPosition = mapView.getMapViewPosition();
mMapPosition = new MapPosition(); mMapPosition = new MapPosition();
//Matrix.setIdentityM(mMVPMatrix, 0);
mMatrices = new Matrices(); mMatrices = new Matrices();
// add half pixel to tile clip/fill coordinates to avoid rounding issues short min = 0;
short min = -4; short max = (short) ((Tile.TILE_SIZE * COORD_SCALE));
short max = (short) ((Tile.TILE_SIZE << 3) + 4);
mFillCoords = new short[8]; mFillCoords = new short[8];
mFillCoords[0] = min; mFillCoords[0] = min;
mFillCoords[1] = max; mFillCoords[1] = max;
@ -329,7 +327,40 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static Object tilelock = new Object(); private static Object tilelock = new Object();
static void draw() { private static void updateTileVisibility() {
float[] coords = mTileCoords;
MapPosition pos = mMapPosition;
MapTile[] tiles = mDrawTiles.tiles;
// lock tiles while updating isVisible state
synchronized (GLRenderer.tilelock) {
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.
byte z = tiles[0].zoomLevel;
float div = FastMath.pow(z - pos.zoomLevel);
// transform screen coordinates to tile coordinates
float scale = pos.scale / div;
float px = (float) pos.x * div;
float py = (float) pos.y * div;
for (int i = 0; i < 8; i += 2) {
coords[i + 0] = (px + coords[i + 0] / scale) / Tile.TILE_SIZE;
coords[i + 1] = (py + coords[i + 1] / scale) / Tile.TILE_SIZE;
}
// count placeholder tiles
mHolderCount = 0;
// check visibile tiles
mScanBox.scan(coords, z);
}
}
private static void draw() {
long start = 0; long start = 0;
if (MapView.debugFrameTime) if (MapView.debugFrameTime)
@ -374,46 +405,25 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// get current MapPosition, set mTileCoords (mapping of screen to model // get current MapPosition, set mTileCoords (mapping of screen to model
// coordinates) // coordinates)
MapPosition pos = mMapPosition; MapPosition pos = mMapPosition;
float[] coords = mTileCoords; boolean positionChanged;
boolean changed;
synchronized (mMapViewPosition) { synchronized (mMapViewPosition) {
changed = mMapViewPosition.getMapPosition(pos); mMapViewPosition.updateAnimation();
mMapViewPosition.getMapViewProjection(coords);
positionChanged = mMapViewPosition.getMapPosition(pos);
if (positionChanged)
mMapViewPosition.getMapViewProjection(mTileCoords);
mMapViewPosition.getMatrix(mMatrices.view, null, mMatrices.viewproj); mMapViewPosition.getMatrix(mMatrices.view, null, mMatrices.viewproj);
} }
int tileCnt = mDrawTiles.cnt; int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles; MapTile[] tiles = mDrawTiles.tiles;
if (changed) { if (positionChanged)
// lock tiles while updating isVisible state updateTileVisibility();
synchronized (GLRenderer.tilelock) {
for (int i = 0; i < tileCnt; i++)
tiles[i].isVisible = false;
// relative zoom-level, 'tiles' could not have been updated after
// zoom-level changed.
byte z = tiles[0].zoomLevel;
float div = FastMath.pow(z - pos.zoomLevel);
// transform screen coordinates to tile coordinates
float scale = pos.scale / div;
float px = (float) pos.x * div;
float py = (float) pos.y * div;
for (int i = 0; i < 8; i += 2) {
coords[i + 0] = (px + coords[i + 0] / scale) / Tile.TILE_SIZE;
coords[i + 1] = (py + coords[i + 1] / scale) / Tile.TILE_SIZE;
}
// count placeholder tiles
mHolderCount = 0;
// check visibile tiles
mScanBox.scan(coords, z);
}
}
tileCnt += mHolderCount; tileCnt += mHolderCount;
@ -466,7 +476,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
List<RenderOverlay> overlays = mMapView.getOverlayManager().getRenderLayers(); List<RenderOverlay> overlays = mMapView.getOverlayManager().getRenderLayers();
for (int i = 0, n = overlays.size(); i < n; i++) for (int i = 0, n = overlays.size(); i < n; i++)
overlays.get(i).update(mMapPosition, changed, tilesChanged); overlays.get(i).update(mMapPosition, positionChanged, tilesChanged);
/* draw base layer */ /* draw base layer */
BaseMap.draw(tiles, tileCnt, pos, mMatrices); BaseMap.draw(tiles, tileCnt, pos, mMatrices);

View File

@ -14,6 +14,8 @@
*/ */
package org.oscim.utils; package org.oscim.utils;
import org.oscim.core.Point2D;
import android.graphics.Point; import android.graphics.Point;
/** /**
@ -169,18 +171,13 @@ public final class GeometryUtils {
return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength; return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength;
} }
public static final class Point2D {
public double x;
public double y;
}
// from libosmscout-render // from libosmscout-render
public static byte calcLinesIntersect( public static byte calcLinesIntersect(
double ax1, double ay1, double ax1, double ay1,
double ax2, double ay2, double ax2, double ay2,
double bx1, double by1, double bx1, double by1,
double bx2, double by2, double bx2, double by2,
GeometryUtils.Point2D point) Point2D point)
{ {
double ua_numr = (bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1); double ua_numr = (bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1);
double ub_numr = (ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1); double ub_numr = (ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1);

View File

@ -78,7 +78,7 @@ public class MapView extends RelativeLayout {
private final MapViewPosition mMapViewPosition; private final MapViewPosition mMapViewPosition;
private final MapPosition mMapPosition; private final MapPosition mMapPosition;
private final MapZoomControls mMapZoomControls; //private final MapZoomControls mMapZoomControls;
private final TouchHandler mTouchEventHandler; private final TouchHandler mTouchEventHandler;
private final Compass mCompass; private final Compass mCompass;
@ -86,7 +86,7 @@ public class MapView extends RelativeLayout {
private final TileManager mTileManager; private final TileManager mTileManager;
private final OverlayManager mOverlayManager; private final OverlayManager mOverlayManager;
private final GLView mGLView; final GLView mGLView;
private final JobQueue mJobQueue; private final JobQueue mJobQueue;
// TODO use 1 download and 1 generator thread instead // TODO use 1 download and 1 generator thread instead
@ -161,7 +161,6 @@ public class MapView extends RelativeLayout {
mTileManager = new TileManager(this); mTileManager = new TileManager(this);
mGLView = new GLView(context, this); mGLView = new GLView(context, this);
mMapWorkers = new MapWorker[mNumMapWorkers]; mMapWorkers = new MapWorker[mNumMapWorkers];
mDebugSettings = new DebugSettings(); mDebugSettings = new DebugSettings();
@ -186,8 +185,8 @@ public class MapView extends RelativeLayout {
addView(mGLView, params); addView(mGLView, params);
mMapZoomControls = new MapZoomControls(mapActivity, this); //mMapZoomControls = new MapZoomControls(mapActivity, this);
mMapZoomControls.setShowMapZoomControls(true); //mMapZoomControls.setShowMapZoomControls(true);
mRotationEnabled = true; mRotationEnabled = true;
//mOverlayManager.add(new GenericOverlay(this, new GridOverlay(this))); //mOverlayManager.add(new GenericOverlay(this, new GridOverlay(this)));
@ -277,8 +276,6 @@ public class MapView extends RelativeLayout {
return mRotationEnabled; return mRotationEnabled;
} }
/** /**
* Calculates all necessary tiles and adds jobs accordingly. * Calculates all necessary tiles and adds jobs accordingly.
* *
@ -303,8 +300,6 @@ public class MapView extends RelativeLayout {
boolean changed = mMapViewPosition.getMapPosition(mMapPosition); boolean changed = mMapViewPosition.getMapPosition(mMapPosition);
//Log.d(TAG, "redraw " + changed + " " + forceRedraw);
// required when not changed? // required when not changed?
if (AndroidUtils.currentThreadIsUiThread()) if (AndroidUtils.currentThreadIsUiThread())
mOverlayManager.onUpdate(mMapPosition, changed); mOverlayManager.onUpdate(mMapPosition, changed);
@ -505,7 +500,6 @@ public class MapView extends RelativeLayout {
return false; return false;
} }
void destroy() { void destroy() {
for (MapWorker mapWorker : mMapWorkers) { for (MapWorker mapWorker : mMapWorkers) {
mapWorker.pause(); mapWorker.pause();
@ -580,14 +574,6 @@ public class MapView extends RelativeLayout {
return true; return true;
} }
// byte limitZoomLevel(byte zoom) {
// if (mMapZoomControls == null)
// return zoom;
//
// return (byte) Math.max(Math.min(zoom, getMaximumPossibleZoomLevel()),
// mMapZoomControls.getZoomLevelMin());
// }
/** /**
* Sets the center and zoom level of this MapView and triggers a redraw. * Sets the center and zoom level of this MapView and triggers a redraw.
* *
@ -598,6 +584,7 @@ public class MapView extends RelativeLayout {
Log.d(TAG, "setMapCenter " Log.d(TAG, "setMapCenter "
+ " lat: " + mapPosition.lat + " lat: " + mapPosition.lat
+ " lon: " + mapPosition.lon); + " lon: " + mapPosition.lon);
mMapViewPosition.setMapCenter(mapPosition); mMapViewPosition.setMapCenter(mapPosition);
redrawMap(true); redrawMap(true);
} }
@ -609,10 +596,9 @@ public class MapView extends RelativeLayout {
* the new center point of the map. * the new center point of the map.
*/ */
public void setCenter(GeoPoint geoPoint) { public void setCenter(GeoPoint geoPoint) {
MapPosition mapPosition = new MapPosition(geoPoint,
mMapViewPosition.getZoomLevel(), 1);
setMapCenter(mapPosition); mMapViewPosition.setMapCenter(geoPoint);
redrawMap(true);
} }
/** /**
@ -687,27 +673,4 @@ public class MapView extends RelativeLayout {
public GeoPoint getCenter() { public GeoPoint getCenter() {
return new GeoPoint(mMapPosition.lat, mMapPosition.lon); return new GeoPoint(mMapPosition.lat, mMapPosition.lon);
} }
// /**
// * Sets the visibility of the zoom controls.
// *
// * @param showZoomControls
// * true if the zoom controls should be visible, false otherwise.
// */
// public void setBuiltInZoomControls(boolean showZoomControls) {
// mMapZoomControls.setShowMapZoomControls(showZoomControls);
//
// }
// /**
// * Sets the text scale for the map rendering. Has no effect in downloading
// mode.
// *
// * @param textScale
// * the new text scale for the map rendering.
// */
// public void setTextScale(float textScale) {
// mJobParameters = new JobParameters(mJobParameters.theme, textScale);
// clearAndRedrawMapView();
// }
} }

View File

@ -21,9 +21,9 @@ import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint; import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection; import org.oscim.core.MercatorProjection;
import org.oscim.core.Point2D;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.utils.FastMath; import org.oscim.utils.FastMath;
import org.oscim.utils.GeometryUtils.Point2D;
import org.oscim.utils.GlUtils; import org.oscim.utils.GlUtils;
import android.graphics.Point; import android.graphics.Point;
@ -32,6 +32,7 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.widget.Scroller;
/** /**
* A MapPosition stores the latitude and longitude coordinate of a MapView * A MapPosition stores the latitude and longitude coordinate of a MapView
@ -39,11 +40,13 @@ import android.util.Log;
*/ */
public class MapViewPosition { public class MapViewPosition {
//private static final String TAG = MapViewPosition.class.getName(); private static final String TAG = MapViewPosition.class.getName();
public final static int MAX_ZOOMLEVEL = 17; public final static int MAX_ZOOMLEVEL = 17;
public final static int MIN_ZOOMLEVEL = 2; public final static int MIN_ZOOMLEVEL = 2;
public final static int MAX_END_SCALE = 8; public final static int MAX_END_SCALE = 8;
public final static double MAX_SCALE = ((1 << MAX_ZOOMLEVEL) * MAX_END_SCALE);
public final static double MIN_SCALE = (1 << MIN_ZOOMLEVEL);
private final static float MAX_ANGLE = 65; private final static float MAX_ANGLE = 65;
@ -51,31 +54,39 @@ public class MapViewPosition {
private double mLatitude; private double mLatitude;
private double mLongitude; private double mLongitude;
private byte mZoomLevel;
// 1.0 - 2.0 scale per level private double mMapScale;
private float mScale;
// 2^mZoomLevel * mScale; // mMapScale * Tile.TILE_SIZE
private float mMapScale; // i.e. size of tile 0/0/0 at current scale in pixel
private double mTileScale;
private float mRotation; private float mRotation;
public float mTilt; public float mTilt;
// private static final int REF_ZOOM = 20; // map center in tile coordinates of current zoom-level
private double mPosX; //private double mPosX;
private double mPosY; //private double mPosY;
// NB: mMapScale == 2^mZoomLevel * mScale;
private byte mZoomLevel;
// 1.0 - 2.0 scale per level
private float mScale;
private final AnimationHandler mHandler; private final AnimationHandler mHandler;
private final Scroller mScroller;
MapViewPosition(MapView mapView) { MapViewPosition(MapView mapView) {
mMapView = mapView; mMapView = mapView;
mLatitude = Double.NaN; mLatitude = Double.NaN;
mLongitude = Double.NaN; mLongitude = Double.NaN;
mZoomLevel = -1;
mScale = 1;
mRotation = 0.0f; mRotation = 0.0f;
mTilt = 0; mTilt = 0;
mMapScale = 1; mMapScale = 1;
mHandler = new AnimationHandler(this); mHandler = new AnimationHandler(this);
mScroller = new Scroller(mapView.getContext());
} }
private final float[] mProjMatrix = new float[16]; private final float[] mProjMatrix = new float[16];
@ -88,8 +99,8 @@ public class MapViewPosition {
// temporary vars: only use in synchronized functions! // temporary vars: only use in synchronized functions!
private final Point2D mMovePoint = new Point2D(); private final Point2D mMovePoint = new Point2D();
private final float[] mv = { 0, 0, 0, 1 }; private final float[] mv = new float[4];
private final float[] mu = { 0, 0, 0, 1 }; private final float[] mu = new float[4];
private final float[] mBBoxCoords = new float[8]; private final float[] mBBoxCoords = new float[8];
private float mHeight, mWidth; private float mHeight, mWidth;
@ -118,38 +129,46 @@ public class MapViewPosition {
updateMatrix(); updateMatrix();
} }
public synchronized boolean getMapPosition(final MapPosition mapPosition) { /**
// if (!isValid()) * Get the current MapPosition
// return false; *
* @param pos MapPosition object to be updated
* @return true if current position is different from 'pos'.
*/
public synchronized boolean getMapPosition(MapPosition pos) {
if (mapPosition.lat == mLatitude updateTileScale();
&& mapPosition.lon == mLongitude
&& mapPosition.zoomLevel == mZoomLevel if (pos.lat == mLatitude
&& mapPosition.scale == mScale && pos.lon == mLongitude
&& mapPosition.angle == mRotation && pos.zoomLevel == mZoomLevel
&& mapPosition.tilt == mTilt) && pos.scale == mScale
&& pos.angle == mRotation
&& pos.tilt == mTilt)
return false; return false;
byte z = mZoomLevel; byte z = mZoomLevel;
mapPosition.lat = mLatitude; pos.lat = mLatitude;
mapPosition.lon = mLongitude; pos.lon = mLongitude;
mapPosition.angle = mRotation; pos.angle = mRotation;
mapPosition.tilt = mTilt; pos.tilt = mTilt;
mapPosition.scale = mScale;
mapPosition.zoomLevel = z;
mapPosition.x = mPosX; // for tiling
mapPosition.y = mPosY; pos.scale = mScale;
pos.zoomLevel = z;
pos.x = mAbsX * (Tile.TILE_SIZE << mZoomLevel);
pos.y = mAbsY * (Tile.TILE_SIZE << mZoomLevel);
return true; return true;
} }
/** /**
* get a copy of current matrices * Get a copy of current matrices
* *
* @param view ... * @param view view Matrix
* @param proj ... * @param proj projection Matrix
* @param vp view and projection * @param vp view and projection
*/ */
public synchronized void getMatrix(float[] view, float[] proj, float[] vp) { public synchronized void getMatrix(float[] view, float[] proj, float[] vp) {
@ -163,21 +182,33 @@ public class MapViewPosition {
System.arraycopy(mVPMatrix, 0, vp, 0, 16); System.arraycopy(mVPMatrix, 0, vp, 0, 16);
} }
/**
* Get the inverse projection of the viewport, i.e. the
* coordinates with z==0 that will be projected exactly
* to screen corners by current view-projection-matrix.
*
* @param box float[8] will be set.
*/
public synchronized void getMapViewProjection(float[] box) { public synchronized void getMapViewProjection(float[] box) {
float t = getZ(1); float t = getZ(1);
float t2 = getZ(-1); float t2 = getZ(-1);
unproject(1, -1, t, box, 0); // top-right // top-right
unproject(-1, -1, t, box, 2); // top-left unproject(1, -1, t, box, 0);
unproject(-1, 1, t2, box, 4); // bottom-left // top-left
unproject(1, 1, t2, box, 6); // bottom-right unproject(-1, -1, t, box, 2);
// bottom-left
unproject(-1, 1, t2, box, 4);
// bottom-right
unproject(1, 1, t2, box, 6);
} }
// get the z-value of the map-plane for a point on screen /*
* Get Z-value of the map-plane for a point on screen -
* calculate the intersection of a ray from camera origin
* and the map plane
*/
private float getZ(float y) { private float getZ(float y) {
// calculate the intersection of a ray from
// camera origin and the map plane
// origin is moved by VIEW_DISTANCE // origin is moved by VIEW_DISTANCE
double cx = VIEW_DISTANCE; double cx = VIEW_DISTANCE;
// 'height' of the ray // 'height' of the ray
@ -208,11 +239,9 @@ public class MapViewPosition {
Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0); Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0);
if (mv[3] != 0) {
coords[position + 0] = mv[0] / mv[3]; coords[position + 0] = mv[0] / mv[3];
coords[position + 1] = mv[1] / mv[3]; coords[position + 1] = mv[1] / mv[3];
} }
}
/** @return the current center point of the MapView. */ /** @return the current center point of the MapView. */
public synchronized GeoPoint getMapCenter() { public synchronized GeoPoint getMapCenter() {
@ -227,20 +256,11 @@ public class MapViewPosition {
if (!isValid()) { if (!isValid()) {
return null; return null;
} }
updateTileScale();
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation); return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
} }
/** @return the current zoom level of the MapView. */
public synchronized byte getZoomLevel() {
return mZoomLevel;
}
/** @return the current scale of the MapView. */
public synchronized float getScale() {
return mScale;
}
/** /**
* ... * ...
* *
@ -253,96 +273,106 @@ public class MapViewPosition {
float t = getZ(1); float t = getZ(1);
float t2 = getZ(-1); float t2 = getZ(-1);
unproject(1, -1, t, coords, 0); // top-right unproject(1, -1, t, coords, 0);
unproject(-1, -1, t, coords, 2); // top-left unproject(-1, -1, t, coords, 2);
unproject(-1, 1, t2, coords, 4); // bottom-left unproject(-1, 1, t2, coords, 4);
unproject(1, 1, t2, coords, 6); // bottom-right unproject(1, 1, t2, coords, 6);
byte z = mZoomLevel;
double dx, dy; double dx, dy;
double minLat = 0, minLon = 0, maxLat = 0, maxLon = 0, lon, lat; double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double maxY = Double.MIN_VALUE;
for (int i = 0; i < 8; i += 2) { for (int i = 0; i < 8; i += 2) {
dx = mRealX - coords[i + 0];
dy = mRealY - coords[i + 1];
dx = mPosX - coords[i + 0] / mScale; minX = Math.min(minX, dx);
dy = mPosY - coords[i + 1] / mScale; maxX = Math.max(maxX, dx);
minY = Math.min(minY, dy);
lon = MercatorProjection.pixelXToLongitude(dx, z); maxY = Math.max(maxY, dy);
lat = MercatorProjection.pixelYToLatitude(dy, z);
if (i == 0) {
minLon = maxLon = lon;
minLat = maxLat = lat;
} else {
if (lat > maxLat)
maxLat = lat;
else if (lat < minLat)
minLat = lat;
if (lon > maxLon)
maxLon = lon;
else if (lon < minLon)
minLon = lon;
}
} }
BoundingBox bbox = new BoundingBox(minLat, minLon, maxLat, maxLon); minX = MercatorProjection.toLongitude(minX, mTileScale);
maxX = MercatorProjection.toLongitude(maxX, mTileScale);
minY = MercatorProjection.toLatitude(minY, mTileScale);
maxY = MercatorProjection.toLatitude(maxY, mTileScale);
Log.d(">>>", "getScreenBoundingBox " + bbox); // yea, this is upside down..
BoundingBox bbox = new BoundingBox(maxY, minX, minY, maxX);
Log.d(TAG, "getScreenBoundingBox " + bbox);
return bbox; return bbox;
} }
/** /**
* for x,y in screen coordinates get the point on the map in map-tile * For x, y in screen coordinates set Point to map-tile
* coordinates * coordinates at returned scale.
* *
* @param x ... * @param x screen coordinate
* @param y ... * @param y screen coordinate
* @param reuse ... * @param out Point coords will be set
* @return ... * @return current map scale
*/ */
public synchronized Point getScreenPointOnMap(float x, float y, Point reuse) { public synchronized float getScreenPointOnMap(float x, float y, Point2D out) {
Point out = reuse == null ? new Point() : reuse;
float mx = ((mWidth / 2) - x) / (mWidth / 2); // scale to -1..1
float my = ((mHeight / 2) - y) / (mHeight / 2); float mx = 1 - (x / mWidth * 2);
float my = 1 - (y / mHeight * 2);
unproject(-mx, my, getZ(-my), mu, 0); unproject(-mx, my, getZ(-my), mu, 0);
out.x = (int) (mPosX + mu[0] / mScale); out.x = mRealX + mu[0];
out.y = (int) (mPosY + mu[1] / mScale); out.y = mRealY + mu[1];
//Log.d(TAG, "getScreenPointOnMap " + reuse);
return out; return (float) mMapScale;
} }
/** /**
* get the GeoPoint for x,y in screen coordinates * Get the GeoPoint for x,y in screen coordinates.
* (only used by MapEventsOverlay currently)
* *
* @param x screen pixel x * @param x screen coordinate
* @param y screen pixel y * @param y screen coordinate
* @return the corresponding GeoPoint * @return the corresponding GeoPoint
*/ */
public synchronized GeoPoint fromScreenPixels(float x, float y) { public synchronized GeoPoint fromScreenPixels(float x, float y) {
float mx = ((mWidth / 2) - x) / (mWidth / 2); // scale to -1..1
float my = ((mHeight / 2) - y) / (mHeight / 2); float mx = 1 - (x / mWidth * 2);
float my = 1 - (y / mHeight * 2);
unproject(-mx, my, getZ(-my), mu, 0); unproject(-mx, my, getZ(-my), mu, 0);
double dx = mPosX + mu[0] / mScale; double dx = mRealX + mu[0];
double dy = mPosY + mu[1] / mScale; double dy = mRealY + mu[1];
dx /= mMapScale * Tile.TILE_SIZE;
dy /= mMapScale * Tile.TILE_SIZE;
if (dx > 1) {
while (dx > 1)
dx -= 1;
} else {
while (dx < 0)
dx += 1;
}
if (dy > 1)
dy = 1;
else if (dy < 0)
dy = 0;
GeoPoint p = new GeoPoint( GeoPoint p = new GeoPoint(
MercatorProjection.pixelYToLatitude(dy, mZoomLevel), MercatorProjection.toLatitude(dy),
MercatorProjection.pixelXToLongitude(dx, mZoomLevel)); MercatorProjection.toLongitude(dx));
//Log.d(TAG, "fromScreenPixels " + p);
return p; return p;
} }
/** /**
* get the screen pixel for a GeoPoint * Get the screen pixel for a GeoPoint
* *
* @param geoPoint ... * @param geoPoint ...
* @param reuse ... * @param reuse ...
@ -351,40 +381,24 @@ public class MapViewPosition {
public synchronized Point project(GeoPoint geoPoint, Point reuse) { public synchronized Point project(GeoPoint geoPoint, Point reuse) {
Point out = reuse == null ? new Point() : reuse; Point out = reuse == null ? new Point() : reuse;
double x = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), double x = MercatorProjection.longitudeToX(geoPoint.getLongitude()) * mTileScale;
mZoomLevel); double y = MercatorProjection.latitudeToY(geoPoint.getLatitude()) * mTileScale;
double y = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(),
mZoomLevel); mv[0] = (float) (x - mRealX);
mv[1] = (float) (y - mRealY);
mv[0] = (float) (x - mPosX) * mScale;
mv[1] = (float) (y - mPosY) * mScale;
mv[2] = 0; mv[2] = 0;
mv[3] = 1; mv[3] = 1;
Matrix.multiplyMV(mv, 0, mVPMatrix, 0, mv, 0); Matrix.multiplyMV(mv, 0, mVPMatrix, 0, mv, 0);
out.x = (int) (mv[0] / mv[3] * mWidth / 2); // positive direction is down and right;
out.y = (int) (mv[1] / mv[3] * mHeight / 2); out.x = (int) ((mv[0] / mv[3]) * (mWidth / 2));
out.y = (int) -((mv[1] / mv[3]) * (mHeight / 2));
return out; return out;
} }
// public static Point project(float x, float y, float[] matrix, float[] tmpVec, Point reuse) {
// Point out = reuse == null ? new Point() : reuse;
//
// tmpVec[0] = x;
// tmpVec[1] = y;
// tmpVec[2] = 0;
// tmpVec[3] = 1;
//
// Matrix.multiplyMV(tmpVec, 0, matrix, 0, tmpVec, 0);
//
// out.x = (int) (tmpVec[0] / tmpVec[3] * mWidth / 2);
// out.y = (int) (tmpVec[1] / tmpVec[3] * mHeight / 2);
//
// return out;
// }
private void updateMatrix() { private void updateMatrix() {
// --- view matrix // --- view matrix
// 1. scale to window coordinates // 1. scale to window coordinates
@ -449,6 +463,10 @@ public class MapViewPosition {
return true; return true;
} }
//private double mMaxLat = MercatorProjection.latitudeToY(MercatorProjection.LATITUDE_MAX);
private double mAbsX;
private double mAbsY;
/** /**
* Moves this MapViewPosition by the given amount of pixels. * Moves this MapViewPosition by the given amount of pixels.
* *
@ -456,75 +474,88 @@ public class MapViewPosition {
* @param my the amount of pixels to move the map vertically. * @param my the amount of pixels to move the map vertically.
*/ */
public synchronized void moveMap(float mx, float my) { public synchronized void moveMap(float mx, float my) {
Point2D p = getMove(mx, my); // stop animation
mHandler.cancel();
mLatitude = MercatorProjection.pixelYToLatitude(mPosY - p.y, mZoomLevel); Point2D p = applyRotation(mx, my);
mLatitude = MercatorProjection.limitLatitude(mLatitude);
mLongitude = MercatorProjection.pixelXToLongitude(mPosX - p.x, mZoomLevel); move(p.x, p.y);
mLongitude = MercatorProjection.wrapLongitude(mLongitude); }
private synchronized void move(double mx, double my) {
mAbsX = (mRealX - mx) / mTileScale;
mAbsY = (mRealY - my) / mTileScale;
// clamp latitude
mAbsY = FastMath.clamp(mAbsY, 0, 1);
// wrap longitude
while (mAbsX > 1)
mAbsX -= 1;
while (mAbsX < 0)
mAbsX += 1;
mLongitude = MercatorProjection.toLongitude(mAbsX);
mLatitude = MercatorProjection.toLatitude(mAbsY);
updatePosition(); updatePosition();
} }
private Point2D getMove(float mx, float my) { private Point2D applyRotation(float mx, float my) {
double dx = mx / mScale;
double dy = my / mScale;
if (mMapView.mRotationEnabled || mMapView.mCompassEnabled) { if (mMapView.mRotationEnabled || mMapView.mCompassEnabled) {
double rad = Math.toRadians(mRotation); double rad = Math.toRadians(mRotation);
double rcos = Math.cos(rad); double rcos = Math.cos(rad);
double rsin = Math.sin(rad); double rsin = Math.sin(rad);
double x = dx * rcos + dy * rsin; float x = (float) (mx * rcos + my * rsin);
double y = dx * -rsin + dy * rcos; float y = (float) (mx * -rsin + my * rcos);
dx = x; mx = x;
dy = y; my = y;
} }
mMovePoint.x = dx; mMovePoint.x = mx;
mMovePoint.y = dy; mMovePoint.y = my;
return mMovePoint; return mMovePoint;
} }
private void updateTileScale() {
int z = FastMath.log2((int) mMapScale);
z = FastMath.clamp(z, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL);
mZoomLevel = (byte) z;
mScale = (float) (mMapScale / (1 << z));
//Log.d(TAG, "updateTileScale " + mZoomLevel + " " + mScale);
}
/** /**
* - * @param scale map by this factor
*
* @param scale ...
* @param pivotX ... * @param pivotX ...
* @param pivotY ... * @param pivotY ...
* @return true if scale was changed * @return true if scale was changed
*/ */
public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) { public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) {
// stop animation
mHandler.cancel();
// sanitize input // just sanitize input
scale = FastMath.clamp(scale, 0.5f, 2); scale = FastMath.clamp(scale, 0.5f, 2);
float newScale = mMapScale * scale; double newScale = mMapScale * scale;
int z = FastMath.log2((int) newScale); newScale = FastMath.clamp(newScale, MIN_SCALE, MAX_SCALE);
if (z < MIN_ZOOMLEVEL || (z >= MAX_ZOOMLEVEL && mScale >= 8)) if (newScale == mMapScale)
return false; return false;
if (z > MAX_ZOOMLEVEL) { scale = (float) (newScale / mMapScale);
// z17 shows everything, just increase scaling
// need to fix this for ScanBox
if (mScale * scale > 4)
return false;
mScale *= scale;
mMapScale = newScale; mMapScale = newScale;
} else {
mZoomLevel = (byte) z;
updatePosition();
mScale = newScale / (1 << z);
mMapScale = newScale;
}
if (pivotX != 0 || pivotY != 0) if (pivotX != 0 || pivotY != 0)
moveMap(pivotX * (1.0f - scale), moveMap(pivotX * (1.0f - scale),
pivotY * (1.0f - scale)); pivotY * (1.0f - scale));
else
updatePosition();
return true; return true;
} }
@ -532,14 +563,21 @@ public class MapViewPosition {
/** /**
* rotate map around pivot cx,cy * rotate map around pivot cx,cy
* *
* @param angle ... * @param radians ...
* @param cx ... * @param cx ...
* @param cy ... * @param cy ...
*/ */
public synchronized void rotateMap(float angle, float cx, float cy) { public synchronized void rotateMap(double radians, float cx, float cy) {
moveMap(cx, cy);
mRotation += angle; double rsin = Math.sin(radians);
double rcos = Math.cos(radians);
float x = (float) (cx * rcos + cy * -rsin - cx);
float y = (float) (cx * rsin + cy * rcos - cy);
moveMap(x, y);
mRotation += Math.toDegrees(radians);
updateMatrix(); updateMatrix();
} }
@ -567,6 +605,9 @@ public class MapViewPosition {
private void setMapCenter(double latitude, double longitude) { private void setMapCenter(double latitude, double longitude) {
mLatitude = MercatorProjection.limitLatitude(latitude); mLatitude = MercatorProjection.limitLatitude(latitude);
mLongitude = MercatorProjection.limitLongitude(longitude); mLongitude = MercatorProjection.limitLongitude(longitude);
mAbsX = MercatorProjection.longitudeToX(mLongitude);
mAbsY = MercatorProjection.latitudeToY(mLatitude);
updatePosition(); updatePosition();
} }
@ -575,33 +616,42 @@ public class MapViewPosition {
} }
synchronized void setMapCenter(MapPosition mapPosition) { synchronized void setMapCenter(MapPosition mapPosition) {
//mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
setZoomLevelLimit(mapPosition.zoomLevel); setZoomLevelLimit(mapPosition.zoomLevel);
mMapScale = 1 << mZoomLevel; //mMapScale = 1 << mZoomLevel;
setMapCenter(mapPosition.lat, mapPosition.lon); setMapCenter(mapPosition.lat, mapPosition.lon);
} }
synchronized void setZoomLevel(byte zoomLevel) { synchronized void setZoomLevel(byte zoomLevel) {
//mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
setZoomLevelLimit(zoomLevel); setZoomLevelLimit(zoomLevel);
mMapScale = 1 << mZoomLevel; //mMapScale = 1 << mZoomLevel;
mScale = 1; //mScale = 1;
updatePosition(); updatePosition();
} }
private void setZoomLevelLimit(byte zoomLevel) { private void setZoomLevelLimit(byte zoomLevel) {
mZoomLevel = (byte)FastMath.clamp(zoomLevel, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL); mMapScale = FastMath.clamp(1 << zoomLevel, MIN_SCALE, MAX_SCALE);
//mMapScale = 1 << zoomLevel;
//mZoomLevel = (byte) FastMath.clamp(zoomLevel, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL);
} }
private double mRealX;
private double mRealY;
private void updatePosition() { private void updatePosition() {
mPosX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel); mTileScale = mMapScale * Tile.TILE_SIZE;
mPosY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
mRealX = mAbsX * mTileScale;
mRealY = mAbsY * mTileScale;
} }
private double mStartX; private int mScrollX;
private double mStartY; private int mScrollY;
private double mEndX;
private double mEndY; private double mStartScale;
private double mEndScale;
private float mDuration = 500; private float mDuration = 500;
public synchronized void animateTo(BoundingBox bbox) { public synchronized void animateTo(BoundingBox bbox) {
@ -610,6 +660,7 @@ public class MapViewPosition {
double dy = MercatorProjection.latitudeToY(bbox.getMinLatitude()) double dy = MercatorProjection.latitudeToY(bbox.getMinLatitude())
- MercatorProjection.latitudeToY(bbox.getMaxLatitude()); - MercatorProjection.latitudeToY(bbox.getMaxLatitude());
double log4 = Math.log(4); double log4 = Math.log(4);
double zx = -log4 * Math.log(dx) + (mWidth / Tile.TILE_SIZE); double zx = -log4 * Math.log(dx) + (mWidth / Tile.TILE_SIZE);
@ -617,80 +668,160 @@ public class MapViewPosition {
double z = Math.min(zx, zy); double z = Math.min(zx, zy);
setZoomLevelLimit((byte) Math.floor(z)); double newScale = Math.pow(2, z);
mScale = FastMath.clamp((float) (1 + (z - mZoomLevel)), 1, MAX_END_SCALE); //double newScale = (1 << (int)Math.floor(z));
mMapScale = (1 << mZoomLevel) * mScale; newScale = FastMath.clamp(newScale, MIN_SCALE, (1 << 15));
//Log.d(TAG, "zoom: " + bbox + " " + zx + " " + zy + " / " + mScale + " " + mZoomLevel);
setMapCenter(bbox.getCenterPoint());
float scale = (float) (newScale / mMapScale);
Log.d(TAG, "scale to " + bbox + " "+ z + " " + newScale + " " + mMapScale
+ " " + FastMath.log2((int) newScale) + " " + scale);
mEndScale = mMapScale * scale;
mStartScale = mMapScale;
//mMapScale = newScale;
//updatePosition(); //updatePosition();
//
// // reset rotation/tilt // ----- mScale = FastMath.clamp((float) (1 + (z - mZoomLevel)), 1, MAX_END_SCALE);
// mTilt = 0;
// mRotation = 0; //mMapScale = (1 << mZoomLevel) * mScale;
// updateMatrix(); //Log.d(TAG, "zoom: " + bbox + " " + zx + " " + zy + " / " + mScale + " " + mZoomLevel);
// //updatePosition();
// GeoPoint geoPoint = bbox.getCenterPoint();
// mEndX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mZoomLevel); // reset rotation/tilt
// mEndY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mZoomLevel); mTilt = 0;
// mStartX = mPosX; mRotation = 0;
// mStartY = mPosY; updateMatrix();
//
// mDuration = 300; GeoPoint geoPoint = bbox.getCenterPoint();
// mHandler.start((int) mDuration);
//mStartScale = mMapScale;
double mx = MercatorProjection.longitudeToX(geoPoint.getLongitude()) * mTileScale;
double my = MercatorProjection.latitudeToY(geoPoint.getLatitude()) * mTileScale;
mx = mRealX - mx;
my = mRealY - my;
mScrollX = 0;
mScrollY = 0;
mDuration = 500;
mScroller.startScroll(0, 0, (int) mx, (int) my, (int)mDuration);
mHandler.start((int) mDuration);
} }
public synchronized void animateTo(GeoPoint geoPoint) { public synchronized void animateTo(GeoPoint geoPoint) {
//MercatorProjection.projectPoint(geoPoint, mZoomLevel, mTmpPoint); //MercatorProjection.projectPoint(geoPoint, mZoomLevel, mTmpPoint);
mEndX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mZoomLevel); //mEndX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mZoomLevel);
mEndY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mZoomLevel); //mEndY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mZoomLevel);
mStartX = mPosX; //mStartX = mPosX;
mStartY = mPosY; //mStartY = mPosY;
mDuration = 300;
mHandler.start((int) mDuration);
}
public synchronized void animateFling(int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
mScrollX = 0;
mScrollY = 0;
mScroller.fling(0, 0, velocityX, velocityY, minX, maxX, minY, maxY);
//mMapView.mGLView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
mDuration = 300; mDuration = 300;
mHandler.start((int) mDuration); mHandler.start((int) mDuration);
} }
public synchronized void animateZoom(float scale) {
mStartScale = mMapScale;
mEndScale = mMapScale * scale;
//mScroller.fling(0, 0, velocityX, velocityY, minX, maxX, minY, maxY);
//mMapView.mGLView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
mDuration = 300;
mHandler.start((int) mDuration);
}
//private float mScrollX, mScrollY;
public void updateAnimation() {
//scroll();
}
synchronized boolean scroll(double div) {
if (mScroller.isFinished()) {
return false;
}
int x = mScrollX; //mScroller.getCurrX();
int y = mScrollY; //mScroller.getCurrY();
mScroller.computeScrollOffset();
int mx = mScroller.getCurrX() - x;
int my = mScroller.getCurrY() - y;
mx *= div;
my *= div;
if (mx >= 1 || my >= 1 || mx <= -1 || my <= -1) {
moveMap(mx, my);
//mMapView.redrawMap(true);
mScrollX = mScroller.getCurrX();
mScrollY = mScroller.getCurrY();
}
return true;
}
public synchronized void animateTo(float dx, float dy, float duration) { public synchronized void animateTo(float dx, float dy, float duration) {
getMove(dx, dy); applyRotation(dx, dy);
mEndX = mPosX - mMovePoint.x; //mEndX = mRealX - mMovePoint.x;
mEndY = mPosY - mMovePoint.y; //mEndY = mRealY - mMovePoint.y;
mStartX = mPosX; //mStartX = mRealX;
mStartY = mPosY; //mStartY = mRealY;
mDuration = duration; mDuration = duration;
mHandler.start((int) mDuration); mHandler.start((int) mDuration);
} }
synchronized void setMapPosition(double x, double y) { void onTick(long millisLeft) {
boolean changed = false;
mLatitude = MercatorProjection.pixelYToLatitude(y, mZoomLevel); double div = 1;
mLatitude = MercatorProjection.limitLatitude(mLatitude); if (mStartScale != 0) {
double adv = millisLeft / mDuration;
mLongitude = MercatorProjection.pixelXToLongitude(x, mZoomLevel); mMapScale = (adv * mStartScale) + (mEndScale * (1.0 - adv));
mLongitude = MercatorProjection.wrapLongitude(mLongitude); div = mMapScale / mStartScale;
updatePosition(); updatePosition();
changed = true;
} }
void onTick(long millisLeft) { if (scroll(div))
double adv = millisLeft / mDuration; changed = true;
double mx = (mStartX + (mEndX - mStartX) * (1.0 - adv));
double my = (mStartY + (mEndY - mStartY) * (1.0 - adv)); if (changed)
setMapPosition(mx, my);
mMapView.redrawMap(true); mMapView.redrawMap(true);
} }
void onFinish() { void onFinish() {
setMapPosition(mEndX, mEndY); //setMapPosition(mEndX, mEndY);
//mMapView.mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
mMapView.redrawMap(true); mMapView.redrawMap(true);
mStartScale = 0;
} }
/**
* below is borrowed from CountDownTimer class:
* Copyright (C) 2008 The Android Open Source Project
*/
static class AnimationHandler extends Handler { static class AnimationHandler extends Handler {
private final WeakReference<MapViewPosition> mMapViewPosition; private final WeakReference<MapViewPosition> mMapViewPosition;
private static final int MSG = 1; private static final int MSG = 1;

View File

@ -1,358 +1,358 @@
/* ///*
* Copyright 2010, 2011, 2012 mapsforge.org // * Copyright 2010, 2011, 2012 mapsforge.org
* // *
* This program is free software: you can redistribute it and/or modify it under the // * 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 // * 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. // * 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 // * 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 // * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // * 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 // * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. // * this program. If not, see <http://www.gnu.org/licenses/>.
*/ // */
package org.oscim.view; //package org.oscim.view;
//
import org.oscim.generator.TileGenerator; //import org.oscim.generator.TileGenerator;
//
import android.content.Context; //import android.content.Context;
import android.os.Handler; //import android.os.Handler;
import android.os.Message; //import android.os.Message;
import android.view.Gravity; //import android.view.Gravity;
import android.view.MotionEvent; //import android.view.MotionEvent;
import android.view.View; //import android.view.View;
import android.view.ViewConfiguration; //import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams; //import android.view.ViewGroup.LayoutParams;
import android.widget.ZoomControls; //import android.widget.ZoomControls;
//
/** ///**
* A MapZoomControls instance displays buttons for zooming in and out in a map. // * A MapZoomControls instance displays buttons for zooming in and out in a map.
*/ // */
public class MapZoomControls { //public class MapZoomControls {
private static class ZoomControlsHideHandler extends Handler { // private static class ZoomControlsHideHandler extends Handler {
private final ZoomControls mZoomControls; // private final ZoomControls mZoomControls;
//
ZoomControlsHideHandler(ZoomControls zoomControls) { // ZoomControlsHideHandler(ZoomControls zoomControls) {
super(); // super();
mZoomControls = zoomControls; // mZoomControls = zoomControls;
} // }
//
@Override // @Override
public void handleMessage(Message message) { // public void handleMessage(Message message) {
mZoomControls.hide(); // mZoomControls.hide();
} // }
} // }
//
private static class ZoomInClickListener implements View.OnClickListener { // private static class ZoomInClickListener implements View.OnClickListener {
private final MapZoomControls mMapZoomControls; // private final MapZoomControls mMapZoomControls;
//
ZoomInClickListener(MapZoomControls mapZoomControls) { // ZoomInClickListener(MapZoomControls mapZoomControls) {
mMapZoomControls = mapZoomControls; // mMapZoomControls = mapZoomControls;
} // }
//
@Override // @Override
public void onClick(View view) { // public void onClick(View view) {
// if (MapView.testRegionZoom) // // if (MapView.testRegionZoom)
// mMapView.mRegionLookup.updateRegion(1, null); // // mMapView.mRegionLookup.updateRegion(1, null);
// else // // else
// MapZoomControls.this.zoom((byte) 1); // // MapZoomControls.this.zoom((byte) 1);
mMapZoomControls.zoom((byte) 1); // mMapZoomControls.zoom((byte) 1);
} // }
} // }
//
private static class ZoomOutClickListener implements View.OnClickListener { // private static class ZoomOutClickListener implements View.OnClickListener {
private final MapZoomControls mMapZoomControls; // private final MapZoomControls mMapZoomControls;
//
ZoomOutClickListener(MapZoomControls mapZoomControls) { // ZoomOutClickListener(MapZoomControls mapZoomControls) {
mMapZoomControls = mapZoomControls; // mMapZoomControls = mapZoomControls;
} // }
//
@Override // @Override
public void onClick(View view) { // public void onClick(View view) {
// if (MapView.testRegionZoom) // // if (MapView.testRegionZoom)
// mMapView.mRegionLookup.updateRegion(-1, null); // // mMapView.mRegionLookup.updateRegion(-1, null);
// else // // else
mMapZoomControls.zoom((byte) -1); // mMapZoomControls.zoom((byte) -1);
} // }
} // }
//
/** // /**
* Default {@link Gravity} of the zoom controls. // * Default {@link Gravity} of the zoom controls.
*/ // */
private static final int DEFAULT_ZOOM_CONTROLS_GRAVITY = Gravity.BOTTOM // private static final int DEFAULT_ZOOM_CONTROLS_GRAVITY = Gravity.BOTTOM
| Gravity.RIGHT; // | Gravity.RIGHT;
//
/** // /**
* Default maximum zoom level. // * Default maximum zoom level.
*/ // */
private static final byte DEFAULT_ZOOM_LEVEL_MAX = 18; // private static final byte DEFAULT_ZOOM_LEVEL_MAX = 18;
//
/** // /**
* Default minimum zoom level. // * Default minimum zoom level.
*/ // */
private static final byte DEFAULT_ZOOM_LEVEL_MIN = 1; // private static final byte DEFAULT_ZOOM_LEVEL_MIN = 1;
//
/** // /**
* Message code for the handler to hide the zoom controls. // * Message code for the handler to hide the zoom controls.
*/ // */
private static final int MSG_ZOOM_CONTROLS_HIDE = 0; // private static final int MSG_ZOOM_CONTROLS_HIDE = 0;
//
/** // /**
* Horizontal padding for the zoom controls. // * Horizontal padding for the zoom controls.
*/ // */
private static final int ZOOM_CONTROLS_HORIZONTAL_PADDING = 5; // private static final int ZOOM_CONTROLS_HORIZONTAL_PADDING = 5;
//
/** // /**
* Delay in milliseconds after which the zoom controls disappear. // * Delay in milliseconds after which the zoom controls disappear.
*/ // */
private static final long ZOOM_CONTROLS_TIMEOUT = ViewConfiguration // private static final long ZOOM_CONTROLS_TIMEOUT = ViewConfiguration
.getZoomControlsTimeout(); // .getZoomControlsTimeout();
//
private boolean mGravityChanged; // private boolean mGravityChanged;
private boolean mShowMapZoomControls; // private boolean mShowMapZoomControls;
private final ZoomControls mZoomControls; // private final ZoomControls mZoomControls;
private int mZoomControlsGravity; // private int mZoomControlsGravity;
private final Handler mZoomControlsHideHandler; // private final Handler mZoomControlsHideHandler;
private byte mZoomLevelMax; // private byte mZoomLevelMax;
private byte mZoomLevelMin; // private byte mZoomLevelMin;
private final MapView mMapView; // private final MapView mMapView;
//
MapZoomControls(Context context, final MapView mapView) { // MapZoomControls(Context context, final MapView mapView) {
mMapView = mapView; // mMapView = mapView;
mZoomControls = new ZoomControls(context); // mZoomControls = new ZoomControls(context);
mShowMapZoomControls = true; // mShowMapZoomControls = true;
mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX; // mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX;
mZoomLevelMin = DEFAULT_ZOOM_LEVEL_MIN; // mZoomLevelMin = DEFAULT_ZOOM_LEVEL_MIN;
// if (!MapView.testRegionZoom) // // if (!MapView.testRegionZoom)
mZoomControls.setVisibility(View.GONE); // mZoomControls.setVisibility(View.GONE);
mZoomControlsGravity = DEFAULT_ZOOM_CONTROLS_GRAVITY; // mZoomControlsGravity = DEFAULT_ZOOM_CONTROLS_GRAVITY;
//
mZoomControls.setOnZoomInClickListener(new ZoomInClickListener(this)); // mZoomControls.setOnZoomInClickListener(new ZoomInClickListener(this));
mZoomControls.setOnZoomOutClickListener(new ZoomOutClickListener(this)); // mZoomControls.setOnZoomOutClickListener(new ZoomOutClickListener(this));
mZoomControlsHideHandler = new ZoomControlsHideHandler(mZoomControls); // mZoomControlsHideHandler = new ZoomControlsHideHandler(mZoomControls);
//
int wrapContent = android.view.ViewGroup.LayoutParams.WRAP_CONTENT; // int wrapContent = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
LayoutParams layoutParams = new LayoutParams(wrapContent, wrapContent); // LayoutParams layoutParams = new LayoutParams(wrapContent, wrapContent);
mapView.addView(mZoomControls, layoutParams); // mapView.addView(mZoomControls, layoutParams);
} // }
//
/** // /**
* Zooms in or out by the given amount of zoom levels. // * Zooms in or out by the given amount of zoom levels.
* // *
* @param zoomLevelDiff // * @param zoomLevelDiff
* the difference to the current zoom level. // * the difference to the current zoom level.
* @return true if the zoom level was changed, false otherwise. // * @return true if the zoom level was changed, false otherwise.
*/ // */
boolean zoom(byte zoomLevelDiff) { // boolean zoom(byte zoomLevelDiff) {
MapViewPosition mapViewPosition = mMapView.getMapViewPosition(); // MapViewPosition mapViewPosition = mMapView.getMapViewPosition();
int z = mapViewPosition.getZoomLevel() + zoomLevelDiff; // int z = mapViewPosition.getZoomLevel() + zoomLevelDiff;
if (zoomLevelDiff > 0) { // if (zoomLevelDiff > 0) {
// check if zoom in is possible // // check if zoom in is possible
if (z > mZoomLevelMax) { // if (z > mZoomLevelMax) {
return false; // return false;
} // }
//
} else if (zoomLevelDiff < 0) { // } else if (zoomLevelDiff < 0) {
// check if zoom out is possible // // check if zoom out is possible
if (z < getZoomLevelMin()) { // if (z < getZoomLevelMin()) {
return false; // return false;
} // }
} // }
//
mapViewPosition.setZoomLevel((byte) z); // mapViewPosition.setZoomLevel((byte) z);
mMapView.redrawMap(true); // mMapView.redrawMap(true);
//
return true; // return true;
} // }
//
/** // /**
* @return the current gravity for the placing of the zoom controls. // * @return the current gravity for the placing of the zoom controls.
* @see Gravity // * @see Gravity
*/ // */
public int getZoomControlsGravity() { // public int getZoomControlsGravity() {
return mZoomControlsGravity; // return mZoomControlsGravity;
} // }
//
/** // /**
* @return the maximum zoom level of the map. // * @return the maximum zoom level of the map.
*/ // */
public byte getZoomLevelMax() { // public byte getZoomLevelMax() {
return mZoomLevelMax; // return mZoomLevelMax;
} // }
//
/** // /**
* @return the minimum zoom level of the map. // * @return the minimum zoom level of the map.
*/ // */
public byte getZoomLevelMin() { // public byte getZoomLevelMin() {
return mZoomLevelMin; // return mZoomLevelMin;
} // }
//
/** // /**
* @return true if the zoom controls are visible, false otherwise. // * @return true if the zoom controls are visible, false otherwise.
*/ // */
public boolean isShowMapZoomControls() { // public boolean isShowMapZoomControls() {
return mShowMapZoomControls; // return mShowMapZoomControls;
} // }
//
/** // /**
* @param show // * @param show
* true if the zoom controls should be visible, false otherwise. // * true if the zoom controls should be visible, false otherwise.
*/ // */
public void setShowMapZoomControls(boolean show) { // public void setShowMapZoomControls(boolean show) {
mShowMapZoomControls = show; // mShowMapZoomControls = show;
} // }
//
/** // /**
* Sets the gravity for the placing of the zoom controls. Supported values // * Sets the gravity for the placing of the zoom controls. Supported values
* are {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL}, // * are {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL},
* {@link Gravity#BOTTOM}, {@link Gravity#LEFT}, // * {@link Gravity#BOTTOM}, {@link Gravity#LEFT},
* {@link Gravity#CENTER_HORIZONTAL} and {@link Gravity#RIGHT}. // * {@link Gravity#CENTER_HORIZONTAL} and {@link Gravity#RIGHT}.
* // *
* @param zoomControlsGravity // * @param zoomControlsGravity
* a combination of {@link Gravity} constants describing the // * a combination of {@link Gravity} constants describing the
* desired placement. // * desired placement.
*/ // */
public void setZoomControlsGravity(int zoomControlsGravity) { // public void setZoomControlsGravity(int zoomControlsGravity) {
if (mZoomControlsGravity != zoomControlsGravity) { // if (mZoomControlsGravity != zoomControlsGravity) {
mZoomControlsGravity = zoomControlsGravity; // mZoomControlsGravity = zoomControlsGravity;
mGravityChanged = true; // mGravityChanged = true;
} // }
} // }
//
/** // /**
* Sets the maximum zoom level of the map. // * Sets the maximum zoom level of the map.
* <p> // * <p>
* The maximum possible zoom level of the MapView depends also on the // * The maximum possible zoom level of the MapView depends also on the
* current {@link TileGenerator}. For example, downloading map tiles may // * current {@link TileGenerator}. For example, downloading map tiles may
* only be possible up to a certain zoom level. Setting a higher maximum // * only be possible up to a certain zoom level. Setting a higher maximum
* zoom level has no effect in this case. // * zoom level has no effect in this case.
* // *
* @param zoomLevelMax // * @param zoomLevelMax
* the maximum zoom level. // * the maximum zoom level.
* @throws IllegalArgumentException // * @throws IllegalArgumentException
* if the maximum zoom level is smaller than the current minimum // * if the maximum zoom level is smaller than the current minimum
* zoom level. // * zoom level.
*/ // */
public void setZoomLevelMax(byte zoomLevelMax) { // public void setZoomLevelMax(byte zoomLevelMax) {
if (zoomLevelMax < mZoomLevelMin) { // if (zoomLevelMax < mZoomLevelMin) {
throw new IllegalArgumentException(); // throw new IllegalArgumentException();
} // }
mZoomLevelMax = zoomLevelMax; // mZoomLevelMax = zoomLevelMax;
} // }
//
/** // /**
* Sets the minimum zoom level of the map. // * Sets the minimum zoom level of the map.
* // *
* @param zoomLevelMin // * @param zoomLevelMin
* the minimum zoom level. // * the minimum zoom level.
* @throws IllegalArgumentException // * @throws IllegalArgumentException
* if the minimum zoom level is larger than the current maximum // * if the minimum zoom level is larger than the current maximum
* zoom level. // * zoom level.
*/ // */
public void setZoomLevelMin(byte zoomLevelMin) { // public void setZoomLevelMin(byte zoomLevelMin) {
if (zoomLevelMin > mZoomLevelMax) { // if (zoomLevelMin > mZoomLevelMax) {
throw new IllegalArgumentException(); // throw new IllegalArgumentException();
} // }
mZoomLevelMin = zoomLevelMin; // mZoomLevelMin = zoomLevelMin;
} // }
//
private int calculatePositionLeft(int left, int right, int zoomControlsWidth) { // private int calculatePositionLeft(int left, int right, int zoomControlsWidth) {
int gravity = mZoomControlsGravity & Gravity.HORIZONTAL_GRAVITY_MASK; // int gravity = mZoomControlsGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
switch (gravity) { // switch (gravity) {
case Gravity.LEFT: // case Gravity.LEFT:
return ZOOM_CONTROLS_HORIZONTAL_PADDING; // return ZOOM_CONTROLS_HORIZONTAL_PADDING;
//
case Gravity.CENTER_HORIZONTAL: // case Gravity.CENTER_HORIZONTAL:
return (right - left - zoomControlsWidth) / 2; // return (right - left - zoomControlsWidth) / 2;
//
case Gravity.RIGHT: // case Gravity.RIGHT:
return right - left - zoomControlsWidth // return right - left - zoomControlsWidth
- ZOOM_CONTROLS_HORIZONTAL_PADDING; // - ZOOM_CONTROLS_HORIZONTAL_PADDING;
} // }
//
throw new IllegalArgumentException("unknown horizontal gravity: " + gravity); // throw new IllegalArgumentException("unknown horizontal gravity: " + gravity);
} // }
//
private int calculatePositionTop(int top, int bottom, int zoomControlsHeight) { // private int calculatePositionTop(int top, int bottom, int zoomControlsHeight) {
int gravity = mZoomControlsGravity & Gravity.VERTICAL_GRAVITY_MASK; // int gravity = mZoomControlsGravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (gravity) { // switch (gravity) {
case Gravity.TOP: // case Gravity.TOP:
return 0; // return 0;
//
case Gravity.CENTER_VERTICAL: // case Gravity.CENTER_VERTICAL:
return (bottom - top - zoomControlsHeight) / 2; // return (bottom - top - zoomControlsHeight) / 2;
//
case Gravity.BOTTOM: // case Gravity.BOTTOM:
return bottom - top - zoomControlsHeight; // return bottom - top - zoomControlsHeight;
} // }
//
throw new IllegalArgumentException("unknown vertical gravity: " + gravity); // throw new IllegalArgumentException("unknown vertical gravity: " + gravity);
} // }
//
private void showZoomControls() { // private void showZoomControls() {
mZoomControlsHideHandler.removeMessages(MSG_ZOOM_CONTROLS_HIDE); // mZoomControlsHideHandler.removeMessages(MSG_ZOOM_CONTROLS_HIDE);
if (mZoomControls.getVisibility() != View.VISIBLE) { // if (mZoomControls.getVisibility() != View.VISIBLE) {
mZoomControls.show(); // mZoomControls.show();
} // }
} // }
//
private void showZoomControlsWithTimeout() { // private void showZoomControlsWithTimeout() {
showZoomControls(); // showZoomControls();
mZoomControlsHideHandler.sendEmptyMessageDelayed(MSG_ZOOM_CONTROLS_HIDE, // mZoomControlsHideHandler.sendEmptyMessageDelayed(MSG_ZOOM_CONTROLS_HIDE,
ZOOM_CONTROLS_TIMEOUT); // ZOOM_CONTROLS_TIMEOUT);
} // }
//
int getMeasuredHeight() { // int getMeasuredHeight() {
return mZoomControls.getMeasuredHeight(); // return mZoomControls.getMeasuredHeight();
} // }
//
int getMeasuredWidth() { // int getMeasuredWidth() {
return mZoomControls.getMeasuredWidth(); // return mZoomControls.getMeasuredWidth();
} // }
//
void measure(int widthMeasureSpec, int heightMeasureSpec) { // void measure(int widthMeasureSpec, int heightMeasureSpec) {
mZoomControls.measure(widthMeasureSpec, heightMeasureSpec); // mZoomControls.measure(widthMeasureSpec, heightMeasureSpec);
} // }
//
void onLayout(boolean changed, int left, int top, int right, int bottom) { // void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!changed && !mGravityChanged) { // if (!changed && !mGravityChanged) {
return; // return;
} // }
//
int zoomControlsWidth = mZoomControls.getMeasuredWidth(); // int zoomControlsWidth = mZoomControls.getMeasuredWidth();
int zoomControlsHeight = mZoomControls.getMeasuredHeight(); // int zoomControlsHeight = mZoomControls.getMeasuredHeight();
//
int positionLeft = calculatePositionLeft(left, right, zoomControlsWidth); // int positionLeft = calculatePositionLeft(left, right, zoomControlsWidth);
int positionTop = calculatePositionTop(top, bottom, zoomControlsHeight); // int positionTop = calculatePositionTop(top, bottom, zoomControlsHeight);
int positionRight = positionLeft + zoomControlsWidth; // int positionRight = positionLeft + zoomControlsWidth;
int positionBottom = positionTop + zoomControlsHeight; // int positionBottom = positionTop + zoomControlsHeight;
//
mZoomControls.layout(positionLeft, positionTop, positionRight, positionBottom); // mZoomControls.layout(positionLeft, positionTop, positionRight, positionBottom);
mGravityChanged = false; // mGravityChanged = false;
} // }
//
void onMapViewTouchEvent(int action) { // void onMapViewTouchEvent(int action) {
if (mShowMapZoomControls) { // if (mShowMapZoomControls) {
switch (action) { // switch (action) {
case MotionEvent.ACTION_DOWN: // case MotionEvent.ACTION_DOWN:
showZoomControls(); // showZoomControls();
break; // break;
case MotionEvent.ACTION_CANCEL: // case MotionEvent.ACTION_CANCEL:
showZoomControlsWithTimeout(); // showZoomControlsWithTimeout();
break; // break;
case MotionEvent.ACTION_UP: // case MotionEvent.ACTION_UP:
showZoomControlsWithTimeout(); // showZoomControlsWithTimeout();
break; // break;
} // }
} // }
} // }
//
void onZoomLevelChange(int zoomLevel) { // void onZoomLevelChange(int zoomLevel) {
boolean zoomInEnabled = zoomLevel < mZoomLevelMax; // boolean zoomInEnabled = zoomLevel < mZoomLevelMax;
boolean zoomOutEnabled = zoomLevel > mZoomLevelMin; // boolean zoomOutEnabled = zoomLevel > mZoomLevelMin;
//
mZoomControls.setIsZoomInEnabled(zoomInEnabled); // mZoomControls.setIsZoomInEnabled(zoomInEnabled);
mZoomControls.setIsZoomOutEnabled(zoomOutEnabled); // mZoomControls.setIsZoomOutEnabled(zoomOutEnabled);
} // }
} //}

View File

@ -20,14 +20,11 @@ import org.oscim.core.Tile;
import org.oscim.overlay.OverlayManager; import org.oscim.overlay.OverlayManager;
import android.content.Context; import android.content.Context;
import android.os.CountDownTimer;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener; import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener; import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
/** /**
* @author Hannes Janetzek * @author Hannes Janetzek
@ -40,18 +37,19 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
private static final String TAG = TouchHandler.class.getName(); private static final String TAG = TouchHandler.class.getName();
private static final boolean debug = false;
private final MapView mMapView; private final MapView mMapView;
private final MapViewPosition mMapPosition; private final MapViewPosition mMapPosition;
private final OverlayManager mOverlayManager; private final OverlayManager mOverlayManager;
private final DecelerateInterpolator mInterpolator;
private boolean mBeginScale;
private float mSumScale; private float mSumScale;
private float mSumRotate; private float mSumRotate;
private boolean mBeginScale;
private boolean mBeginRotate; private boolean mBeginRotate;
private boolean mBeginTilt; private boolean mBeginTilt;
private boolean mLongPress; private boolean mDoubleTap;
private float mPrevX; private float mPrevX;
private float mPrevY; private float mPrevY;
@ -60,16 +58,17 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
private float mPrevY2; private float mPrevY2;
private double mAngle; private double mAngle;
private double mPrevPinchWidth;
private float mFocusX;
private float mFocusY;
private final GestureDetector mGestureDetector; private final GestureDetector mGestureDetector;
private static final float SCALE_DURATION = 500;
protected static final int JUMP_THRESHOLD = 100; protected static final int JUMP_THRESHOLD = 100;
protected static final double PINCH_ZOOM_THRESHOLD = 5; protected static final double PINCH_ZOOM_THRESHOLD = 5;
protected static final double PINCH_ROTATE_THRESHOLD = 0.02; protected static final double PINCH_ROTATE_THRESHOLD = 0.02;
protected static final float PINCH_TILT_THRESHOLD = 1f; protected static final float PINCH_TILT_THRESHOLD = 1f;
protected int mPrevPointerCount = 0;
protected double mPrevPinchWidth = -1;
/** /**
* @param context * @param context
@ -83,8 +82,6 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
mOverlayManager = mapView.getOverlayManager(); mOverlayManager = mapView.getOverlayManager();
mGestureDetector = new GestureDetector(context, this); mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setOnDoubleTapListener(this); mGestureDetector.setOnDoubleTapListener(this);
mInterpolator = new DecelerateInterpolator(2f);
mScroller = new Scroller(mMapView.getContext(), mInterpolator);
} }
/** /**
@ -128,8 +125,7 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
} }
private boolean onActionCancel() { private boolean onActionCancel() {
//mPointerId1 = INVALID_POINTER_ID; mDoubleTap = false;
mLongPress = true;
return true; return true;
} }
@ -143,8 +139,14 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
float width = mMapView.getWidth(); float width = mMapView.getWidth();
float height = mMapView.getHeight(); float height = mMapView.getHeight();
// return if detect a new gesture, as indicated by a large jump
if (Math.abs(mx) > JUMP_THRESHOLD || Math.abs(my) > JUMP_THRESHOLD)
return true;
// double-tap + hold // double-tap + hold
if (mLongPress) { if (mDoubleTap) {
if (debug)
Log.d(TAG, "tap scale: " + mx + " " + my);
mMapPosition.scaleMap(1 - my / (height / 5), 0, 0); mMapPosition.scaleMap(1 - my / (height / 5), 0, 0);
mMapView.redrawMap(true); mMapView.redrawMap(true);
@ -239,12 +241,7 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
mSumRotate += da; mSumRotate += da;
if (Math.abs(da) > 0.001) { if (Math.abs(da) > 0.001) {
double rsin = Math.sin(r); mMapPosition.rotateMap(da, mFocusX, mFocusY);
double rcos = Math.cos(r);
float x = (float) (mFocusX * rcos + mFocusY * -rsin - mFocusX);
float y = (float) (mFocusX * rsin + mFocusY * rcos - mFocusY);
mMapPosition.rotateMap((float) Math.toDegrees(da), x, y);
changed = true; changed = true;
} }
} }
@ -265,60 +262,59 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
return true; return true;
} }
private int mMulti = 0; private int mMulti;
private boolean mWasMulti; private boolean mWasMulti;
private boolean onActionPointerDown(MotionEvent event) { private void updateMulti(MotionEvent e) {
int cnt = e.getPointerCount();
mMulti++; if (cnt == 2) {
mWasMulti = true; mPrevX = e.getX(0);
mSumScale = 1; mPrevY = e.getY(0);
if (mMulti == 1) { mPrevX2 = e.getX(1);
mPrevX2 = event.getX(1); mPrevY2 = e.getY(1);
mPrevY2 = event.getY(1);
double dx = mPrevX - mPrevX2; double dx = mPrevX - mPrevX2;
double dy = mPrevY - mPrevY2; double dy = mPrevY - mPrevY2;
mAngle = Math.atan2(dy, dx); mAngle = Math.atan2(dy, dx);
mPrevPinchWidth = Math.sqrt(dx * dx + dy * dy); mPrevPinchWidth = Math.sqrt(dx * dx + dy * dy);
mSumScale = 1;
} }
}
private boolean onActionPointerDown(MotionEvent e) {
mMulti++;
mWasMulti = true;
updateMulti(e);
return true; return true;
} }
private boolean onActionPointerUp(MotionEvent e) { private boolean onActionPointerUp(MotionEvent e) {
int cnt = e.getPointerCount(); updateMulti(e);
if (cnt >= 2) {
mPrevX = e.getX(0);
mPrevY = e.getY(0);
mPrevX2 = e.getX(1);
mPrevY2 = e.getY(1);
double dx = mPrevX - mPrevX2;
double dy = mPrevY - mPrevY2;
mAngle = Math.atan2(dy, dx);
mPrevPinchWidth = Math.sqrt(dx * dx + dy * dy);
}
mMulti--; mMulti--;
mLongPress = false;
return true; return true;
} }
private boolean onActionDown(MotionEvent e) { private void printState(String action) {
mPrevX = e.getX(); Log.d(TAG, action
mPrevY = e.getY(); + " " + mDoubleTap
+ " " + mBeginScale
+ " " + mBeginRotate
+ " " + mBeginTilt);
}
mBeginRotate = false; private boolean onActionDown(MotionEvent e) {
mBeginTilt = false; mPrevX = e.getX(0);
mBeginScale = false; mPrevY = e.getY(0);
if (debug)
printState("onActionDown");
return true; return true;
} }
@ -330,19 +326,22 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
*/ */
private boolean onActionUp(MotionEvent event) { private boolean onActionUp(MotionEvent event) {
mLongPress = false; if (debug)
mMulti = 0; printState("onActionUp");
mPrevPinchWidth = -1;
mPrevPointerCount = 0; mBeginRotate = false;
mBeginTilt = false;
mBeginScale = false;
mDoubleTap = false;
return true; return true;
} }
/******************* GestureListener *******************/ /******************* GestureListener *******************/
private final Scroller mScroller; //private final Scroller mScroller;
private float mScrollX, mScrollY; //private float mScrollX, mScrollY;
private boolean fling = false; // private boolean fling = false;
@Override @Override
public void onShowPress(MotionEvent e) { public void onShowPress(MotionEvent e) {
@ -356,37 +355,22 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
@Override @Override
public boolean onDown(MotionEvent e) { public boolean onDown(MotionEvent e) {
if (fling) { if (debug)
mScroller.forceFinished(true); printState("onDown");
if (mTimer != null) { // if (fling) {
mTimer.cancel(); // mScroller.forceFinished(true);
mTimer = null; //
} // if (mTimer != null) {
fling = false; // mTimer.cancel();
} // mTimer = null;
// }
// fling = false;
// }
return true; return true;
} }
boolean scroll() {
if (mScroller.isFinished()) {
return false;
}
mScroller.computeScrollOffset();
float moveX = mScroller.getCurrX() - mScrollX;
float moveY = mScroller.getCurrY() - mScrollY;
if (moveX >= 1 || moveY >= 1 || moveX <= -1 || moveY <= -1) {
mMapPosition.moveMap(moveX, moveY);
mMapView.redrawMap(true);
mScrollX = mScroller.getCurrX();
mScrollY = mScroller.getCurrY();
}
return true;
}
@Override @Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY) { final float distanceY) {
@ -396,6 +380,8 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
} }
if (mMulti == 0) { if (mMulti == 0) {
if (debug)
printState("onScroll " + distanceX + " " + distanceY);
mMapPosition.moveMap(-distanceX, -distanceY); mMapPosition.moveMap(-distanceX, -distanceY);
mMapView.redrawMap(true); mMapView.redrawMap(true);
} }
@ -412,13 +398,6 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
int w = Tile.TILE_SIZE * 6; int w = Tile.TILE_SIZE * 6;
int h = Tile.TILE_SIZE * 6; int h = Tile.TILE_SIZE * 6;
mScrollX = 0;
mScrollY = 0;
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
if (mMapView.enablePagedFling) { if (mMapView.enablePagedFling) {
@ -436,28 +415,16 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
mMapPosition.animateTo(vx * move, vy * move, 250); mMapPosition.animateTo(vx * move, vy * move, 250);
} else { } else {
float s = (300 / mMapView.dpi) / 2; float s = (300 / mMapView.dpi) / 2;
mScroller.fling(0, 0, Math.round(velocityX * s),
Math.round(velocityY * s),
-w, w, -h, h);
mTimer = new CountDownTimer(1000, 16) { mMapPosition.animateFling(Math.round(velocityX * s), Math.round(velocityY * s), -w, w, -h, h);
@Override // fling = true;
public void onTick(long tick) {
scroll();
}
@Override
public void onFinish() {
}
}.start();
fling = true;
} }
return true; return true;
} }
@Override @Override
public void onLongPress(MotionEvent e) { public void onLongPress(MotionEvent e) {
if (mLongPress) if (mDoubleTap)
return; return;
if (mOverlayManager.onLongPress(e)) { if (mOverlayManager.onLongPress(e)) {
@ -470,28 +437,6 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
// } // }
} }
boolean scale2(long tick) {
fling = true;
if (mPrevScale >= 1)
return false;
float adv = (SCALE_DURATION - tick) / SCALE_DURATION;
adv = mInterpolator.getInterpolation(adv);
float scale = adv - mPrevScale;
mPrevScale += scale;
scale *= 0.75;
scale += 1;
adv += 1;
if (scale > 1) {
mMapPosition.scaleMap(scale, mScrollX / adv, mScrollY / adv);
mMapView.redrawMap(true);
}
return true;
}
/******************* DoubleTapListener ****************/ /******************* DoubleTapListener ****************/
@Override @Override
public boolean onSingleTapConfirmed(MotionEvent e) { public boolean onSingleTapConfirmed(MotionEvent e) {
@ -503,7 +448,11 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
if (mOverlayManager.onDoubleTap(e)) if (mOverlayManager.onDoubleTap(e))
return true; return true;
mLongPress = true; //mDoubleTap = true;
mMapPosition.animateZoom(2);
if (debug)
printState("onDoubleTap");
return true; return true;
} }
@ -514,13 +463,12 @@ final class TouchHandler implements OnGestureListener, OnDoubleTapListener {
} }
// /******************* ScaleListener *******************/ // /******************* ScaleListener *******************/
private float mPrevScale; //private float mPrevScale;
private CountDownTimer mTimer; //private CountDownTimer mTimer;
boolean mZooutOut; //boolean mZooutOut;
// private float mCenterX; // private float mCenterX;
// private float mCenterY; // private float mCenterY;
private float mFocusX;
private float mFocusY;
// private long mTimeStart; // private long mTimeStart;
// private long mTimeEnd; // private long mTimeEnd;
// //