-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
This commit is contained in:
Hannes Janetzek
2013-04-07 15:58:45 +02:00
parent a6a729244f
commit 8e01dce85e
29 changed files with 658 additions and 710 deletions

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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:

View File

@@ -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);