All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.osmdroid.views.MapController Maven / Gradle / Ivy

There is a newer version: 6.1.20
Show newest version
// Created by plusminus on 21:37:08 - 27.09.2008
package org.osmdroid.views;

import microsoft.mappoint.TileSystem;

import org.osmdroid.api.IGeoPoint;
import org.osmdroid.api.IMapController;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.util.MyMath;
import org.osmdroid.views.util.constants.MapViewConstants;
import org.osmdroid.views.util.constants.MathConstants;

import android.graphics.Point;

/**
 *
 * @author Nicolas Gramlich
 */
public class MapController implements IMapController, MapViewConstants {

	// ===========================================================
	// Constants
	// ===========================================================

	// ===========================================================
	// Fields
	// ===========================================================

	private final MapView mOsmv;
	private AbstractAnimationRunner mCurrentAnimationRunner;

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

	public MapController(final MapView osmv) {
		this.mOsmv = osmv;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods from SuperClass/Interfaces
	// ===========================================================

	// ===========================================================
	// Methods
	// ===========================================================

	public void zoomToSpan(final BoundingBoxE6 bb) {
		zoomToSpan(bb.getLatitudeSpanE6(), bb.getLongitudeSpanE6());
	}

	// TODO rework zoomToSpan
	@Override
	public void zoomToSpan(final int reqLatSpan, final int reqLonSpan) {
		if (reqLatSpan <= 0 || reqLonSpan <= 0) {
			return;
		}

		final BoundingBoxE6 bb = this.mOsmv.getBoundingBox();
		final int curZoomLevel = this.mOsmv.getZoomLevel();

		final int curLatSpan = bb.getLatitudeSpanE6();
		final int curLonSpan = bb.getLongitudeSpanE6();

		final float diffNeededLat = (float) reqLatSpan / curLatSpan; // i.e. 600/500 = 1,2
		final float diffNeededLon = (float) reqLonSpan / curLonSpan; // i.e. 300/400 = 0,75

		final float diffNeeded = Math.max(diffNeededLat, diffNeededLon); // i.e. 1,2

		if (diffNeeded > 1) { // Zoom Out
			this.mOsmv.setZoomLevel(curZoomLevel - MyMath.getNextSquareNumberAbove(diffNeeded));
		} else if (diffNeeded < 0.5) { // Can Zoom in
			this.mOsmv.setZoomLevel(curZoomLevel + MyMath.getNextSquareNumberAbove(1 / diffNeeded)
					- 1);
		}
	}

	/**
	 * Start animating the map towards the given point.
	 */
	@Override
	public void animateTo(final IGeoPoint point) {
		animateTo(point.getLatitudeE6() / 1E6, point.getLongitudeE6() / 1E6);
	}

	/**
	 * Start animating the map towards the given point.
	 */
	public void animateTo(final double latitude, final double longitude) {
		final int x = mOsmv.getScrollX();
		final int y = mOsmv.getScrollY();
		final Point p = TileSystem.LatLongToPixelXY(latitude, longitude, mOsmv.getZoomLevel(), null);
		final int worldSize_2 = TileSystem.MapSize(mOsmv.getZoomLevel()) / 2;
		mOsmv.getScroller().startScroll(x, y, p.x - worldSize_2 - x, p.y - worldSize_2 - y,
				ANIMATION_DURATION_DEFAULT);
		mOsmv.postInvalidate();
	}

	/**
	 * Animates the underlying {@link MapView} that it centers the passed {@link GeoPoint} in the
	 * end. Uses: {@link MapController.ANIMATION_SMOOTHNESS_DEFAULT} and
	 * {@link MapController.ANIMATION_DURATION_DEFAULT}.
	 *
	 * @param gp
	 */
	public void animateTo(final GeoPoint gp, final AnimationType aAnimationType) {
		animateTo(gp.getLatitudeE6(), gp.getLongitudeE6(), aAnimationType,
				ANIMATION_DURATION_DEFAULT, ANIMATION_SMOOTHNESS_DEFAULT);
	}

	/**
	 * Animates the underlying {@link MapView} that it centers the passed {@link GeoPoint} in the
	 * end.
	 *
	 * @param gp
	 *            GeoPoint to be centered in the end.
	 * @param aSmoothness
	 *            steps made during animation. I.e.: {@link MapController.ANIMATION_SMOOTHNESS_LOW},
	 *            {@link MapController.ANIMATION_SMOOTHNESS_DEFAULT},
	 *            {@link MapController.ANIMATION_SMOOTHNESS_HIGH}
	 * @param aDuration
	 *            in Milliseconds. I.e.: {@link MapController.ANIMATION_DURATION_SHORT},
	 *            {@link MapController.ANIMATION_DURATION_DEFAULT},
	 *            {@link MapController.ANIMATION_DURATION_LONG}
	 */
	public void animateTo(final GeoPoint gp, final AnimationType aAnimationType,
			final int aSmoothness, final int aDuration) {
		animateTo(gp.getLatitudeE6(), gp.getLongitudeE6(), aAnimationType, aSmoothness, aDuration);
	}

	/**
	 * Animates the underlying {@link MapView} that it centers the passed coordinates in the end.
	 * Uses: {@link MapController.ANIMATION_SMOOTHNESS_DEFAULT} and
	 * {@link MapController.ANIMATION_DURATION_DEFAULT}.
	 *
	 * @param aLatitudeE6
	 * @param aLongitudeE6
	 */
	public void animateTo(final int aLatitudeE6, final int aLongitudeE6,
			final AnimationType aAnimationType) {
		animateTo(aLatitudeE6, aLongitudeE6, aAnimationType, ANIMATION_SMOOTHNESS_DEFAULT,
				ANIMATION_DURATION_DEFAULT);
	}

	/**
	 * Animates the underlying {@link MapView} that it centers the passed coordinates in the end.
	 *
	 * @param aLatitudeE6
	 * @param aLongitudeE6
	 * @param aSmoothness
	 *            steps made during animation. I.e.: {@link MapController.ANIMATION_SMOOTHNESS_LOW},
	 *            {@link MapController.ANIMATION_SMOOTHNESS_DEFAULT},
	 *            {@link MapController.ANIMATION_SMOOTHNESS_HIGH}
	 * @param aDuration
	 *            in Milliseconds. I.e.: {@link MapController.ANIMATION_DURATION_SHORT},
	 *            {@link MapController.ANIMATION_DURATION_DEFAULT},
	 *            {@link MapController.ANIMATION_DURATION_LONG}
	 */
	public void animateTo(final int aLatitudeE6, final int aLongitudeE6,
			final AnimationType aAnimationType, final int aSmoothness, final int aDuration) {
		this.stopAnimation(false);

		switch (aAnimationType) {
		case LINEAR:
			this.mCurrentAnimationRunner = new LinearAnimationRunner(aLatitudeE6, aLongitudeE6,
					aSmoothness, aDuration);
			break;
		case EXPONENTIALDECELERATING:
			this.mCurrentAnimationRunner = new ExponentialDeceleratingAnimationRunner(aLatitudeE6,
					aLongitudeE6, aSmoothness, aDuration);
			break;
		case QUARTERCOSINUSALDECELERATING:
			this.mCurrentAnimationRunner = new QuarterCosinusalDeceleratingAnimationRunner(
					aLatitudeE6, aLongitudeE6, aSmoothness, aDuration);
			break;
		case HALFCOSINUSALDECELERATING:
			this.mCurrentAnimationRunner = new HalfCosinusalDeceleratingAnimationRunner(
					aLatitudeE6, aLongitudeE6, aSmoothness, aDuration);
			break;
		case MIDDLEPEAKSPEED:
			this.mCurrentAnimationRunner = new MiddlePeakSpeedAnimationRunner(aLatitudeE6,
					aLongitudeE6, aSmoothness, aDuration);
			break;
		}

		this.mCurrentAnimationRunner.start();
	}

	public void scrollBy(final int x, final int y) {
		this.mOsmv.scrollBy(x, y);
	}

	/**
	 * Set the map view to the given center. There will be no animation.
	 */
	@Override
	public void setCenter(final IGeoPoint point) {
		final Point p = TileSystem.LatLongToPixelXY(point.getLatitudeE6() / 1E6,
				point.getLongitudeE6() / 1E6, this.mOsmv.getZoomLevel(), null);
		final int worldSize_2 = TileSystem.MapSize(this.mOsmv.getZoomLevel()) / 2;
		this.mOsmv.scrollTo(p.x - worldSize_2, p.y - worldSize_2);
	}

	/**
	 * Stops a running animation.
	 *
	 * @param jumpToTarget
	 */
	public void stopAnimation(final boolean jumpToTarget) {
		final AbstractAnimationRunner currentAnimationRunner = this.mCurrentAnimationRunner;

		if (currentAnimationRunner != null && !currentAnimationRunner.isDone()) {
			currentAnimationRunner.interrupt();
			if (jumpToTarget) {
				setCenter(new GeoPoint(currentAnimationRunner.mTargetLatitudeE6,
						currentAnimationRunner.mTargetLongitudeE6));
			}
		}
	}

	@Override
	public int setZoom(final int zoomlevel) {
		return mOsmv.setZoomLevel(zoomlevel);
	}

	/**
	 * Zoom in by one zoom level.
	 */
	@Override
	public boolean zoomIn() {
		return mOsmv.zoomIn();
	}

	public boolean zoomInFixing(final GeoPoint point) {
		return mOsmv.zoomInFixing(point);
	}

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

	/**
	 * Zoom out by one zoom level.
	 */
	@Override
	public boolean zoomOut() {
		return mOsmv.zoomOut();
	}

	public boolean zoomOutFixing(final GeoPoint point) {
		return mOsmv.zoomOutFixing(point);
	}

	@Override
	public boolean zoomOutFixing(final int xPixel, final int yPixel) {
		return mOsmv.zoomOutFixing(xPixel, yPixel);
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	/**
	 * Choose on of the Styles of approacing the target Coordinates.
	 * 
    *
  • LINEAR *
      *
    • Uses ses linear interpolation
    • *
    • Values produced: 10%, 20%, 30%, 40%, 50%, ...
    • *
    • Style: Always average speed.
    • *
    *
  • *
  • EXPONENTIALDECELERATING *
      *
    • Uses a exponential interpolation/li> *
    • Values produced: 50%, 75%, 87.5%, 93.5%, ...
    • *
    • Style: Starts very fast, really slow in the end.
    • *
    *
  • *
  • QUARTERCOSINUSALDECELERATING *
      *
    • Uses the first quarter of the cos curve (from zero to PI/2) for interpolation.
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Average speed, slows out medium.
    • *
    *
  • *
  • HALFCOSINUSALDECELERATING *
      *
    • Uses the first half of the cos curve (from zero to PI) for interpolation
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Average speed, slows out smoothly.
    • *
    *
  • *
  • MIDDLEPEAKSPEED *
      *
    • Uses the values of cos around the 0 (from -PI/2 to +PI/2) for interpolation
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Starts medium, speeds high in middle, slows out medium.
    • *
    *
  • *
*/ public static enum AnimationType { /** *
    *
  • LINEAR *
      *
    • Uses ses linear interpolation
    • *
    • Values produced: 10%, 20%, 30%, 40%, 50%, ...
    • *
    • Style: Always average speed.
    • *
    *
  • *
*/ LINEAR, /** *
    *
  • EXPONENTIALDECELERATING *
      *
    • Uses a exponential interpolation/li> *
    • Values produced: 50%, 75%, 87.5%, 93.5%, ...
    • *
    • Style: Starts very fast, really slow in the end.
    • *
    *
  • *
*/ EXPONENTIALDECELERATING, /** *
    *
  • QUARTERCOSINUSALDECELERATING *
      *
    • Uses the first quarter of the cos curve (from zero to PI/2) for interpolation.
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Average speed, slows out medium.
    • *
    *
  • *
*/ QUARTERCOSINUSALDECELERATING, /** *
    *
  • HALFCOSINUSALDECELERATING *
      *
    • Uses the first half of the cos curve (from zero to PI) for interpolation
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Average speed, slows out smoothly.
    • *
    *
  • *
*/ HALFCOSINUSALDECELERATING, /** *
    *
  • MIDDLEPEAKSPEED *
      *
    • Uses the values of cos around the 0 (from -PI/2 to +PI/2) for interpolation
    • *
    • Values produced: See cos curve :)
    • *
    • Style: Starts medium, speeds high in middle, slows out medium.
    • *
    *
  • *
*/ MIDDLEPEAKSPEED; } private abstract class AbstractAnimationRunner extends Thread { // =========================================================== // Fields // =========================================================== protected final int mSmoothness; protected final int mTargetLatitudeE6, mTargetLongitudeE6; protected boolean mDone = false; protected final int mStepDuration; protected final int mPanTotalLatitudeE6, mPanTotalLongitudeE6; // =========================================================== // Constructors // =========================================================== @SuppressWarnings("unused") public AbstractAnimationRunner(final MapController mapViewController, final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, MapViewConstants.ANIMATION_SMOOTHNESS_DEFAULT, MapViewConstants.ANIMATION_DURATION_DEFAULT); } public AbstractAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { this.mTargetLatitudeE6 = aTargetLatitudeE6; this.mTargetLongitudeE6 = aTargetLongitudeE6; this.mSmoothness = aSmoothness; this.mStepDuration = aDuration / aSmoothness; /* Get the current mapview-center. */ final MapView mapview = MapController.this.mOsmv; final IGeoPoint mapCenter = mapview.getMapCenter(); this.mPanTotalLatitudeE6 = mapCenter.getLatitudeE6() - aTargetLatitudeE6; this.mPanTotalLongitudeE6 = mapCenter.getLongitudeE6() - aTargetLongitudeE6; } @Override public void run() { onRunAnimation(); this.mDone = true; } public boolean isDone() { return this.mDone; } public abstract void onRunAnimation(); } private class LinearAnimationRunner extends AbstractAnimationRunner { // =========================================================== // Fields // =========================================================== protected final int mPanPerStepLatitudeE6, mPanPerStepLongitudeE6; // =========================================================== // Constructors // =========================================================== @SuppressWarnings("unused") public LinearAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT); } public LinearAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration); /* Get the current mapview-center. */ final MapView mapview = MapController.this.mOsmv; final IGeoPoint mapCenter = mapview.getMapCenter(); this.mPanPerStepLatitudeE6 = (mapCenter.getLatitudeE6() - aTargetLatitudeE6) / aSmoothness; this.mPanPerStepLongitudeE6 = (mapCenter.getLongitudeE6() - aTargetLongitudeE6) / aSmoothness; this.setName("LinearAnimationRunner"); } // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override public void onRunAnimation() { final MapView mapview = MapController.this.mOsmv; final IGeoPoint mapCenter = mapview.getMapCenter(); final int panPerStepLatitudeE6 = this.mPanPerStepLatitudeE6; final int panPerStepLongitudeE6 = this.mPanPerStepLongitudeE6; final int stepDuration = this.mStepDuration; try { int newMapCenterLatE6; int newMapCenterLonE6; for (int i = this.mSmoothness; i > 0; i--) { newMapCenterLatE6 = mapCenter.getLatitudeE6() - panPerStepLatitudeE6; newMapCenterLonE6 = mapCenter.getLongitudeE6() - panPerStepLongitudeE6; mapview.setMapCenter(newMapCenterLatE6, newMapCenterLonE6); Thread.sleep(stepDuration); } } catch (final Exception e) { this.interrupt(); } } } private class ExponentialDeceleratingAnimationRunner extends AbstractAnimationRunner { // =========================================================== // Fields // =========================================================== // =========================================================== // Constructors // =========================================================== @SuppressWarnings("unused") public ExponentialDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT); } public ExponentialDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration); this.setName("ExponentialDeceleratingAnimationRunner"); } // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override public void onRunAnimation() { final MapView mapview = MapController.this.mOsmv; final IGeoPoint mapCenter = mapview.getMapCenter(); final int stepDuration = this.mStepDuration; try { int newMapCenterLatE6; int newMapCenterLonE6; for (int i = 0; i < this.mSmoothness; i++) { final double delta = Math.pow(0.5, i + 1); final int deltaLatitudeE6 = (int) (this.mPanTotalLatitudeE6 * delta); final int detlaLongitudeE6 = (int) (this.mPanTotalLongitudeE6 * delta); newMapCenterLatE6 = mapCenter.getLatitudeE6() - deltaLatitudeE6; newMapCenterLonE6 = mapCenter.getLongitudeE6() - detlaLongitudeE6; mapview.setMapCenter(newMapCenterLatE6, newMapCenterLonE6); Thread.sleep(stepDuration); } mapview.setMapCenter(super.mTargetLatitudeE6, super.mTargetLongitudeE6); } catch (final Exception e) { this.interrupt(); } } } private class CosinusalBasedAnimationRunner extends AbstractAnimationRunner implements MathConstants { // =========================================================== // Fields // =========================================================== protected final float mStepIncrement, mAmountStretch; protected final float mYOffset, mStart; // =========================================================== // Constructors // =========================================================== @SuppressWarnings("unused") public CosinusalBasedAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final float aStart, final float aRange, final float aYOffset) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT, aStart, aRange, aYOffset); } public CosinusalBasedAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration, final float aStart, final float aRange, final float aYOffset) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration); this.mYOffset = aYOffset; this.mStart = aStart; this.mStepIncrement = aRange / aSmoothness; /* We need to normalize the amount in the end, so wee need the the: sum^(-1) . */ float amountSum = 0; for (int i = 0; i < aSmoothness; i++) { amountSum += aYOffset + Math.cos(this.mStepIncrement * i + aStart); } this.mAmountStretch = 1 / amountSum; this.setName("QuarterCosinusalDeceleratingAnimationRunner"); } // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override public void onRunAnimation() { final MapView mapview = MapController.this.mOsmv; final IGeoPoint mapCenter = mapview.getMapCenter(); final int stepDuration = this.mStepDuration; final float amountStretch = this.mAmountStretch; try { int newMapCenterLatE6; int newMapCenterLonE6; for (int i = 0; i < this.mSmoothness; i++) { final double delta = (this.mYOffset + Math.cos(this.mStepIncrement * i + this.mStart)) * amountStretch; final int deltaLatitudeE6 = (int) (this.mPanTotalLatitudeE6 * delta); final int deltaLongitudeE6 = (int) (this.mPanTotalLongitudeE6 * delta); newMapCenterLatE6 = mapCenter.getLatitudeE6() - deltaLatitudeE6; newMapCenterLonE6 = mapCenter.getLongitudeE6() - deltaLongitudeE6; mapview.setMapCenter(newMapCenterLatE6, newMapCenterLonE6); Thread.sleep(stepDuration); } mapview.setMapCenter(super.mTargetLatitudeE6, super.mTargetLongitudeE6); } catch (final Exception e) { this.interrupt(); } } } protected class QuarterCosinusalDeceleratingAnimationRunner extends CosinusalBasedAnimationRunner implements MathConstants { // =========================================================== // Constructors // =========================================================== protected QuarterCosinusalDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT); } protected QuarterCosinusalDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration, 0, PI_2, 0); } } protected class HalfCosinusalDeceleratingAnimationRunner extends CosinusalBasedAnimationRunner implements MathConstants { // =========================================================== // Constructors // =========================================================== protected HalfCosinusalDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT); } protected HalfCosinusalDeceleratingAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration, 0, PI, 1); } } protected class MiddlePeakSpeedAnimationRunner extends CosinusalBasedAnimationRunner implements MathConstants { // =========================================================== // Constructors // =========================================================== protected MiddlePeakSpeedAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6) { this(aTargetLatitudeE6, aTargetLongitudeE6, ANIMATION_SMOOTHNESS_DEFAULT, ANIMATION_DURATION_DEFAULT); } protected MiddlePeakSpeedAnimationRunner(final int aTargetLatitudeE6, final int aTargetLongitudeE6, final int aSmoothness, final int aDuration) { super(aTargetLatitudeE6, aTargetLongitudeE6, aSmoothness, aDuration, -PI_2, PI, 0); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy