Skip to content

Commit

Permalink
feature/#790: map scale merged into floating point zoom (#800)
Browse files Browse the repository at this point in the history
* feature/#728 - related: more verbose assertions
Not very often the unit test fails. With more verbose assertions, we will be able to find out why.

Impacted class:
* `OpenStreetMapViewTest`: more verbose assertions

* feature/#790: map scale merged into floating point zoom

This is just a teaser, there are things to fix. For instance, the pinch gesture center is not yet taking into consideration (zooming in or out scrolls the map to the bottom right).
All comments are welcomed.

Impacted classes:
* `CompassOverlay`: used the new `Canvas` related `Matrix` methods of `Projection`
* `CopyrightOverlay`: used the new `Canvas` related `Matrix` methods of `Projection`
* `LinearRing`: integrated the fact that there's no map scale anymore / should be considered as equal to 1
* `MapController`
  * feature-related: edited `zoomToFixing` and `onAnimationUpdate`; simplified `onAnimationEnd`
  * refactoring: removed members `mZoomInAnimation` and `mZoomOutAnimation`; simplified methods `zoomIn`, `zoomInFixing`, `zoomOut`, `zoomOutFixing`
* `MapView`
  * feature-related: removed members `ZOOM_SENSITIVITY`, `ZOOM_LOG_BASE_INV`, `mTargetZoomLevel` and `mMultiTouchScale`; deprecated methods `getZoomLevel(final boolean aPending)`, `zoomIn`, `zoomOut` and `getMapScale`; simplified `canZoomIn` and `canZoomOut`; optimized `dispatchDraw` using the new `Canvas` related `Matrix` methods of `Projection`; created new methods `startAnimation`, `setMultiTouchScale` and new member `mStartAnimationZoom`; impacted pinch gesture methods `getPositionAndScale`, `selectObject` and `setPositionAndScale`
  * refactoring: removed members `sMotionEventTransformMethod` and `mMercatorPoint`; deprecated methods `getLatitudeSpan`, `getLongitudeSpan` and `getBoundingBoxE6`; simplified method `rotateTouchEvent`
* `MinimapOverlay`: removed the "display only when not animating" restriction
* `Projection`
  * feature-related: removed member `float mMultiTouchScale`; removed parameter `pScale` from constructor; removed scale operations from matrices; optimized the computation of `mCurrentCenter`; added methods `save` and `restore` that apply the matrices to canvas only if necessary.
  * refactoring: renamed member as `mWrapEnabled`; simplified the initialization of `mScreenRectProjection`; added generic method `apply` that apply a `Matrix` to a `Point`
* `ScaleBarOverlay`: used the new `Canvas` related `Matrix` methods of `Projection`
* `ZoomButtonsOverlay`: used the new `Canvas` related `Matrix` methods of `Projection`; restricted the zoom action to a `ACTION_UP` event

* Desperate try to resolve conflict

* Desperate try to resolve conflict

* Desperate try to resolve conflict

* Desperate try to resolve conflict

* Merging the pinch gesture scale into floating point zoom (last fix?)

Impacted classes:
* `Projection`: rewrote the code in order to comply with `ProjectionTest`'s important notice (cf. javadoc)
* `ProjectionTest`: fixed the call to `Projection` constructor

* Lousy attempt to decrease the time taken by travis.

Impacted classes:
* `ExtraSamplesTest`: limited to 60 the number of items to run.
  • Loading branch information
monsieurtanuki authored and spyhunter99 committed Dec 16, 2017
1 parent c2f942c commit 850580d
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 392 deletions.
Expand Up @@ -73,6 +73,10 @@ private void executeTest(ISampleFactory sampleFactory){

Log.i(SamplesMenuFragment.TAG, "Memory allocation: INIT Free: " + Runtime.getRuntime().freeMemory() + " Total:" + Runtime.getRuntime().totalMemory() + " Max:" + Runtime.getRuntime().maxMemory());
for (int i = 0; i < fireOrder.length; i++) {
// lousy attempt to decrease the time taken by travis
if (i > 60) {
break;
}


for (int k = 0; k < 1; k++) {
Expand Down
Expand Up @@ -117,8 +117,12 @@ private void checkCenter(final Projection pProjection, final GeoPoint pCenter, f
final Point point = pProjection.toPixels(pCenter, null);
assertTrue("MapView does not have layout. Make sure device is unlocked.", width_2 > 0 && height_2 > 0);
final Point expected = new Point(width_2, height_2);
assertEquals("the " + tag + " center of the map is in the pixel center of the map (X)", expected.x, point.x, roundingTolerance);
assertEquals("the " + tag + " center of the map is in the pixel center of the map (Y)", expected.y, point.y, roundingTolerance);
assertEquals("the " + tag + " center of the map is in the pixel center of the map (X)"
+"(zoom=" + pProjection.getZoomLevel() + ",center=" + pCenter + ")",
expected.x, point.x, roundingTolerance);
assertEquals("the " + tag + " center of the map is in the pixel center of the map (Y)"
+ "(zoom=" + pProjection.getZoomLevel() + ",center=" + pCenter + ")",
expected.y, point.y, roundingTolerance);
}

/**
Expand Down
24 changes: 15 additions & 9 deletions osmdroid-android/src/main/java/org/osmdroid/util/BoundingBox.java
Expand Up @@ -5,7 +5,6 @@
import static org.osmdroid.util.MyMath.gudermannInverse;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.osmdroid.api.IGeoPoint;
Expand All @@ -32,20 +31,27 @@ public class BoundingBox implements Parcelable, Serializable, MapViewConstants {
// Fields
// ===========================================================

protected final double mLatNorth;
protected final double mLatSouth;
protected final double mLonEast;
protected final double mLonWest;
private double mLatNorth;
private double mLatSouth;
private double mLonEast;
private double mLonWest;

// ===========================================================
// Constructors
// ===========================================================

public BoundingBox(final double north, final double east, final double south, final double west) {
this.mLatNorth = north;
this.mLonEast = east;
this.mLatSouth = south;
this.mLonWest = west;
set(north, east, south, west);
}

/**
* @since 6.0.0
*/
public void set(final double north, final double east, final double south, final double west) {
mLatNorth = north;
mLonEast = east;
mLatSouth = south;
mLonWest = west;
//validate the values
// 30 > 0 OK
// 30 < 0 not ok
Expand Down
205 changes: 60 additions & 145 deletions osmdroid-android/src/main/java/org/osmdroid/views/MapController.java
Expand Up @@ -19,7 +19,6 @@
import org.osmdroid.events.ZoomEvent;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView.OnFirstLayoutListener;
import org.osmdroid.views.util.MyMath;

Expand All @@ -43,8 +42,6 @@ public class MapController implements IMapController, OnFirstLayoutListener {
protected final MapView mMapView;

// Zoom animations
private ValueAnimator mZoomInAnimation;
private ValueAnimator mZoomOutAnimation;
private ScaleAnimation mZoomInAnimationOld;
private ScaleAnimation mZoomOutAnimationOld;

Expand All @@ -67,18 +64,7 @@ public MapController(MapView mapView) {
}


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ZoomAnimatorListener zoomAnimatorListener = new ZoomAnimatorListener(this);
mZoomInAnimation = ValueAnimator.ofFloat(1f, 2f);
mZoomInAnimation.addListener(zoomAnimatorListener);
mZoomInAnimation.addUpdateListener(zoomAnimatorListener);
mZoomInAnimation.setDuration(Configuration.getInstance().getAnimationSpeedShort());

mZoomOutAnimation = ValueAnimator.ofFloat(1f, 0.5f);
mZoomOutAnimation.addListener(zoomAnimatorListener);
mZoomOutAnimation.addUpdateListener(zoomAnimatorListener);
mZoomOutAnimation.setDuration(Configuration.getInstance().getAnimationSpeedShort());
} else {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
ZoomAnimationListener zoomAnimationListener = new ZoomAnimationListener(this);
mZoomInAnimationOld = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
Expand Down Expand Up @@ -254,12 +240,12 @@ public double setZoom(final double pZoomlevel) {
*/
@Override
public boolean zoomIn() {
return zoomTo(mMapView.getZoomLevel(false) + 1);
return zoomIn(null);
}

@Override
public boolean zoomIn(Long animationSpeed) {
return zoomTo(mMapView.getZoomLevel(false) + 1, animationSpeed);
return zoomTo(mMapView.getZoomLevelDouble() + 1, animationSpeed);
}

/**
Expand All @@ -270,106 +256,44 @@ public boolean zoomIn(Long animationSpeed) {
*/
@Override
public boolean zoomInFixing(final int xPixel, final int yPixel, Long zoomAnimation) {
mMapView.mMultiTouchScalePoint.set(xPixel, yPixel);
if (mMapView.canZoomIn()) {
double newZoomLevel = Math.min(mMapView.getZoomLevelDouble() + 1, mMapView.getMaxZoomLevel());
if (mMapView.mListener != null) {
mMapView.mListener.onZoom(new ZoomEvent(mMapView, newZoomLevel));
}
if (mMapView.mIsAnimating.getAndSet(true)) {
// TODO extend zoom (and return true)
return false;
} else {
float zoomDiffScale = (float) Math.pow(2.0, newZoomLevel - mMapView.getZoomLevel(false));
mMapView.mTargetZoomLevel.set(newZoomLevel);
if (zoomAnimation == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mCurrentAnimator = mZoomInAnimation;
mZoomInAnimation.setFloatValues(1f, zoomDiffScale);
mZoomInAnimation.start();
} else {
mMapView.startAnimation(mZoomInAnimationOld);
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ZoomAnimatorListener zoomAnimatorListener = new ZoomAnimatorListener(this);
ValueAnimator mZoomInAnimation = ValueAnimator.ofFloat(1f, zoomDiffScale);
mZoomInAnimation.addListener(zoomAnimatorListener);
mZoomInAnimation.addUpdateListener(zoomAnimatorListener);
mZoomInAnimation.setDuration(Configuration.getInstance().getAnimationSpeedShort());
mZoomInAnimation.start();

} else {
ZoomAnimationListener zoomAnimationListener = new ZoomAnimationListener(this);
ScaleAnimation mZoomInAnimationOld = new ScaleAnimation(
1, zoomDiffScale, 1, zoomDiffScale,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mZoomInAnimationOld.setDuration(Configuration.getInstance().getAnimationSpeedShort());
mZoomInAnimationOld.setAnimationListener(zoomAnimationListener);
mMapView.startAnimation(mZoomInAnimationOld);
}
}
return true;
}
} else {
return false;
}
return zoomToFixing(mMapView.getZoomLevelDouble() + 1, xPixel, yPixel, zoomAnimation);
}

@Override
public boolean zoomInFixing(final int xPixel, final int yPixel) {
return this.zoomInFixing(xPixel, yPixel, null);
return zoomInFixing(xPixel, yPixel, null);
}

@Override
public boolean zoomOut(Long animationSpeed) {
return zoomTo(mMapView.getZoomLevel(false) - 1, animationSpeed);
return zoomTo(mMapView.getZoomLevelDouble() - 1, animationSpeed);
}

/**
* Zoom out by one zoom level.
*/
@Override
public boolean zoomOut() {
return zoomTo(mMapView.getZoomLevel(false) - 1);
return zoomOut(null);
}

@Deprecated
@Override
public boolean zoomOutFixing(final int xPixel, final int yPixel) {
mMapView.mMultiTouchScalePoint.set(xPixel, yPixel);
if (mMapView.canZoomOut()) {
if (mMapView.mListener != null) {
mMapView.mListener.onZoom(new ZoomEvent(mMapView, mMapView.getZoomLevelDouble() - 1));
}
if (mMapView.mIsAnimating.getAndSet(true)) {
// TODO extend zoom (and return true)
return false;
} else {
mMapView.mTargetZoomLevel.set(mMapView.getZoomLevel(false) - 1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mCurrentAnimator = mZoomOutAnimation;
mZoomOutAnimation.start();
} else {
mMapView.startAnimation(mZoomOutAnimationOld);
}
return true;
}
} else {
return false;
}
return zoomToFixing(mMapView.getZoomLevelDouble() - 1, xPixel, yPixel, null);
}

@Override
public boolean zoomTo(int zoomLevel) {
return zoomToFixing(zoomLevel, mMapView.getWidth() / 2, mMapView.getHeight() / 2);
return zoomTo(zoomLevel, null);
}

/**
* @since 6.0
*/
@Override
public boolean zoomTo(int zoomLevel, Long animationSpeed) {
return zoomToFixing(zoomLevel, mMapView.getWidth() / 2, mMapView.getHeight() / 2, animationSpeed);
return zoomTo((double)zoomLevel, animationSpeed);
}

/**
Expand All @@ -390,8 +314,9 @@ public boolean zoomTo(double pZoomLevel, Long animationSpeed) {
return zoomToFixing(pZoomLevel, mMapView.getWidth() / 2, mMapView.getHeight() / 2, animationSpeed);
}

@Override
public boolean zoomTo(double pZoomLevel) {
return zoomToFixing(pZoomLevel, mMapView.getWidth() / 2, mMapView.getHeight() / 2);
return zoomTo(pZoomLevel, null);
}


Expand All @@ -404,64 +329,61 @@ public boolean zoomToFixing(double zoomLevel, int xPixel, int yPixel, Long zoomA
boolean canZoom = zoomLevel < currentZoomLevel && mMapView.canZoomOut() ||
zoomLevel > currentZoomLevel && mMapView.canZoomIn();

mMapView.mMultiTouchScalePoint.set(xPixel, yPixel);
if (canZoom) {
if (mMapView.mListener != null) {
mMapView.mListener.onZoom(new ZoomEvent(mMapView, zoomLevel));
}
if (mMapView.mIsAnimating.getAndSet(true)) {
// TODO extend zoom (and return true)
return false;
} else {
mMapView.mTargetZoomLevel.set(zoomLevel);

float end = (float) Math.pow(2.0, zoomLevel - currentZoomLevel);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ZoomAnimatorListener zoomAnimatorListener = new ZoomAnimatorListener(this);
ValueAnimator zoomToAnimator = ValueAnimator.ofFloat(1f, end);
zoomToAnimator.addListener(zoomAnimatorListener);
zoomToAnimator.addUpdateListener(zoomAnimatorListener);
if (zoomAnimationSpeed == null) {
zoomToAnimator.setDuration(Configuration.getInstance().getAnimationSpeedShort());
} else {
zoomToAnimator.setDuration(zoomAnimationSpeed);
}

mCurrentAnimator = zoomToAnimator;
zoomToAnimator.start();
} else {
if (zoomLevel > currentZoomLevel)
mMapView.startAnimation(mZoomInAnimationOld);
else
mMapView.startAnimation(mZoomOutAnimationOld);
ScaleAnimation scaleAnimation;

scaleAnimation = new ScaleAnimation(
1f, end, //X
1f, end, //Y
Animation.RELATIVE_TO_SELF, 0.5f, //Pivot X
Animation.RELATIVE_TO_SELF, 0.5f); //Pivot Y
if (zoomAnimationSpeed == null) {
scaleAnimation.setDuration(Configuration.getInstance().getAnimationSpeedShort());
} else {
scaleAnimation.setDuration(zoomAnimationSpeed);
}
scaleAnimation.setAnimationListener(new ZoomAnimationListener(this));
if (!canZoom) {
return false;
}
if (mMapView.mIsAnimating.getAndSet(true)) {
// TODO extend zoom (and return true)
return false;
}
if (mMapView.mListener != null) {
mMapView.mListener.onZoom(new ZoomEvent(mMapView, zoomLevel));
}
mMapView.setMultiTouchScaleInitPoint(xPixel, yPixel);
mMapView.startAnimation();

}
return true;
float end = (float) Math.pow(2.0, zoomLevel - currentZoomLevel);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ZoomAnimatorListener zoomAnimatorListener = new ZoomAnimatorListener(this);
ValueAnimator zoomToAnimator = ValueAnimator.ofFloat(1f, end);
zoomToAnimator.addListener(zoomAnimatorListener);
zoomToAnimator.addUpdateListener(zoomAnimatorListener);
if (zoomAnimationSpeed == null) {
zoomToAnimator.setDuration(Configuration.getInstance().getAnimationSpeedShort());
} else {
zoomToAnimator.setDuration(zoomAnimationSpeed);
}

mCurrentAnimator = zoomToAnimator;
zoomToAnimator.start();
return true;
}
if (zoomLevel > currentZoomLevel)
mMapView.startAnimation(mZoomInAnimationOld);
else
mMapView.startAnimation(mZoomOutAnimationOld);
ScaleAnimation scaleAnimation;

scaleAnimation = new ScaleAnimation(
1f, end, //X
1f, end, //Y
Animation.RELATIVE_TO_SELF, 0.5f, //Pivot X
Animation.RELATIVE_TO_SELF, 0.5f); //Pivot Y
if (zoomAnimationSpeed == null) {
scaleAnimation.setDuration(Configuration.getInstance().getAnimationSpeedShort());
} else {
return false;
scaleAnimation.setDuration(zoomAnimationSpeed);
}
scaleAnimation.setAnimationListener(new ZoomAnimationListener(this));
return true;
}

/**
* @since 6.0
*/
@Override
public boolean zoomToFixing(double zoomLevel, int xPixel, int yPixel) {
return this.zoomToFixing(zoomLevel, xPixel, yPixel, (Long) null);
return zoomToFixing(zoomLevel, xPixel, yPixel, null);
}

@Override
Expand All @@ -475,18 +397,11 @@ protected void onAnimationStart() {
}

protected void onAnimationEnd() {
final GeoPoint currentCenter = mMapView.getProjection().getCurrentCenter();
final double newZoom = mMapView.mTargetZoomLevel.get();
mMapView.mIsAnimating.set(false);
setZoom(newZoom);
mMapView.setCenter(currentCenter);
mMapView.mMultiTouchScale = 1f;
mMapView.resetMultiTouchScale();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mCurrentAnimator = null;
}

// Fix for issue 477
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
} else { // Fix for issue 477
mMapView.clearAnimation();
mZoomInAnimationOld.reset();
mZoomOutAnimationOld.reset();
Expand Down Expand Up @@ -525,7 +440,7 @@ public void onAnimationRepeat(Animator animator) {

@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mMapController.mMapView.mMultiTouchScale = (Float) valueAnimator.getAnimatedValue();
mMapController.mMapView.setMultiTouchScale((Float) valueAnimator.getAnimatedValue());
mMapController.mMapView.invalidate();
}
}
Expand Down

0 comments on commit 850580d

Please sign in to comment.