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

com.mapbox.mapboxsdk.views.MapController Maven / Gradle / Ivy

package com.mapbox.mapboxsdk.views;

import android.graphics.PointF;
import android.view.animation.LinearInterpolator;

import com.mapbox.mapboxsdk.api.ILatLng;
import com.mapbox.mapboxsdk.views.util.Projection;
import com.mapbox.mapboxsdk.views.util.constants.MapViewConstants;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.PropertyValuesHolder;
import com.nineoldandroids.animation.TypeEvaluator;

import java.util.ArrayList;
import java.util.List;

public class MapController implements MapViewConstants {

    public class PointEvaluator implements TypeEvaluator {

        public PointEvaluator() {
        }

        public PointF evaluate(float fraction, PointF startValue,
                               PointF endValue) {
            return new PointF((fraction * (endValue.x - startValue.x) + startValue.x), (fraction * (endValue.y - startValue.y) + startValue.y));
        }

    }

    private static final String TAG = "Mapbox MapController";

    protected final MapView mMapView;

    // Zoom animations
    private ObjectAnimator mCurrentAnimation;

    private ILatLng zoomOnLatLong = null;
    private PointF zoomDeltaScroll = new PointF();
    private ILatLng animateToTargetPoint = null;
    private boolean mCurrentlyUserAction = false;
    private ILatLng mPointToGoTo = null;
    private float mZoomToZoomTo = -1;

    /**
     * Constructor
     *
     * @param mapView MapView to be controlled
     */
    public MapController(MapView mapView) {
        mMapView = mapView;
    }

    public boolean currentlyInUserAction() {
        return mCurrentlyUserAction;
    }

    public void setCurrentlyInUserAction(final boolean value) {
        mCurrentlyUserAction = value;
    }


    protected void aboutToStartAnimation(final ILatLng latlong, final PointF mapCoords) {
        zoomOnLatLong = latlong;
        final Projection projection = mMapView.getProjection();
        mMapView.mMultiTouchScalePoint.set(mapCoords.x, mapCoords.y);
        projection.toPixels(mapCoords, mapCoords);
        zoomDeltaScroll.set((float) (mMapView.getMeasuredWidth() / 2.0 - mapCoords.x), (float) (mMapView.getMeasuredHeight() / 2.0 - mapCoords.y));
    }

    protected void aboutToStartAnimation(final ILatLng latlong) {
        PointF mapCoords = mMapView.getProjection().toMapPixels(latlong, null);
        aboutToStartAnimation(latlong, mapCoords);
    }

    protected void aboutToStartAnimation(final PointF mapCoords) {
        final float zoom = mMapView.getZoomLevel(false);
        final double worldSize_2 = mMapView.getProjection().mapSize(zoom) >> 1;
        final ILatLng latlong = mMapView.getProjection()
                .pixelXYToLatLong(mapCoords.x + worldSize_2,
                        mapCoords.y + worldSize_2, zoom);
        aboutToStartAnimation(latlong, mapCoords);
    }

    protected void aboutToStartAnimation(final float screenX, final float screenY) {
        final float width_2 = mMapView.getMeasuredWidth() / 2.0f;
        final float height_2 = mMapView.getMeasuredHeight() / 2.0f;
        final PointF scrollPoint = mMapView.getScrollPoint();
        final double mapX = screenX + scrollPoint.x - width_2;
        final double mapY = screenY + scrollPoint.y - height_2;
        final float zoom = mMapView.getZoomLevel(false);
        final double worldSize_2 = mMapView.getProjection().mapSize(zoom) >> 1;
        zoomOnLatLong = mMapView.getProjection().pixelXYToLatLong(mapX + worldSize_2,
                        mapY + worldSize_2, zoom);
        mMapView.mMultiTouchScalePoint.set((float) mapX, (float) mapY);
        zoomDeltaScroll.set(width_2 - screenX, height_2 - screenY);
    }

    /**
     * Start animating the map towards the given point.
     */
    public boolean animateTo(final ILatLng point, final boolean userAction) {
        return setZoomAnimated(mMapView.getZoomLevel(), point, true, userAction);
    }

    public boolean animateTo(final ILatLng point) {
        return animateTo(point, false);
    }

    /**
     * Go to a given point (not animated)
     */
    public boolean goTo(final ILatLng point, PointF delta) {

        final Projection projection = mMapView.getProjection();
        PointF p = projection.toMapPixels(point, null);
        if (delta != null) {
            p.offset(delta.x, delta.y);
        }
        if (mMapView.getScrollPoint().equals(p)) {
            return false;
        }
        mMapView.scrollTo(p.x, p.y);
        return true;
    }

    public void panBy(float x, float y, final boolean userAction) {
        mCurrentlyUserAction = userAction;
        this.mMapView.scrollBy(x, y);
        mCurrentlyUserAction = false;
    }

    public void offsetDeltaScroll(float x, float y) {
        zoomDeltaScroll.offset(x, y);
    }

    public void panBy(int x, int y) {
        panBy(x, y, false);
    }

    /**
     * Set the map view to the given center. There will be no animation.
     */
    public void setCenter(final ILatLng latlng) {
        setCenter(latlng, null);
    }

    public void setCenter(final ILatLng latlng, final PointF decale) {
        if (latlng == null) {
            return;
        }
        if (!mMapView.isLayedOut()) {
            mPointToGoTo = latlng;
            return;
        }
        PointF p = mMapView.getProjection().toMapPixels(latlng, null);
        if (decale != null) {
            p.offset(decale.x, decale.y);
        }
        this.mMapView.scrollTo(p.x, p.y);
    }

    public void stopPanning() {
        mMapView.mIsFlinging = false;
        mMapView.getScroller().forceFinished(true);
    }

    /**
     * Stops a running animation.
     */
    public void stopAnimation(final boolean jumpToTarget) {

        if (!mMapView.getScroller().isFinished()) {
            if (jumpToTarget) {
                mMapView.mIsFlinging = false;
                mMapView.getScroller().abortAnimation();
                setCenter(animateToTargetPoint);
            } else {
                stopPanning();
            }
        }

        // We ignore the jumpToTarget for zoom levels since it doesn't make sense to stop
        // the animation in the middle. Maybe we could have it cancel the zoom operation and jump
        // back to original zoom level?
        if (mMapView.isAnimating()) {
            if (mCurrentAnimation != null) {
                mCurrentAnimation.cancel();
            }
            mMapView.setZoomInternal(mMapView.getAnimatedZoom());
            if (jumpToTarget && zoomOnLatLong != null) {
                goTo(zoomOnLatLong, zoomDeltaScroll);
            }
            mMapView.setIsAnimating(false);
        }
    }

    public boolean setZoomAnimated(final float zoomlevel, final ILatLng latlong, final boolean move, final boolean userAction) {
        return setZoomAnimated(zoomlevel, latlong, move, userAction, null);
    }

    public boolean setZoomAnimated(final float zoomlevel, final ILatLng latlong, final boolean move, final boolean userAction, Animator.AnimatorListener listener) {
        if (userAction && mMapView.isAnimating()) {
            return false;
        }
        if (!mMapView.isLayedOut()) {
            mPointToGoTo = latlong;
            mZoomToZoomTo = zoomlevel;
            return false;
        }

        stopAnimation(true);
        mCurrentlyUserAction = userAction;
        mMapView.mIsFlinging = false;

        float currentZoom = mMapView.getZoomLevel(false);

        final PointF dCurrentScroll = mMapView.getScrollPoint();
        PointF p = Projection.toMapPixels(latlong.getLatitude(), latlong.getLongitude(), currentZoom, dCurrentScroll.x, dCurrentScroll.y, null);

        float targetZoom = mMapView.getClampedZoomLevel(zoomlevel);
        boolean zoomAnimating = (targetZoom != currentZoom);
        boolean zoomAndMove = move && !p.equals(dCurrentScroll);

        if (!zoomAnimating && !zoomAndMove) {
            mMapView.invalidate();
            return false;
        }

        mMapView.mMultiTouchScalePoint.set(p.x, p.y);
        List propertiesList = new ArrayList();
        zoomDeltaScroll.set(0, 0);
        if (zoomAnimating) {
            zoomOnLatLong = latlong;
            mMapView.setAnimatedZoom(targetZoom);

            float factor = (float) Math.pow(2, targetZoom - currentZoom);
            float delta = (targetZoom - currentZoom);
            if (delta > 0) {
                propertiesList.add(PropertyValuesHolder.ofFloat("scale", 1.0f, factor));
            } else {
                propertiesList.add(PropertyValuesHolder.ofFloat("scale", 1.0f, factor));
            }
        } else {
            //this is to make sure we don't change the zoom incorrectly at the end of the animation
            mMapView.setAnimatedZoom(currentZoom);
        }
        if (zoomAndMove) {
            PointEvaluator evaluator = new PointEvaluator();
            propertiesList.add(PropertyValuesHolder.ofObject(
                    "scrollPoint", evaluator,
                    p));
        } else {
            mMapView.getProjection().toPixels(p, p);
            zoomDeltaScroll.set((float) (mMapView.getMeasuredWidth() / 2.0 - p.x), (float) (mMapView.getMeasuredHeight() / 2.0 - p.y));
        }

        if (propertiesList.size() > 0) {
            ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(this, propertiesList.toArray(new PropertyValuesHolder[0]));

            anim.setInterpolator(new LinearInterpolator());
            anim.setDuration(zoomAndMove ? ANIMATION_DURATION_DEFAULT : ANIMATION_DURATION_SHORT);
            anim.setTarget(mMapView);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    MapController.this.onAnimationStart();
                    super.onAnimationStart(animation);
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    MapController.this.onAnimationEnd();
                    super.onAnimationEnd(animation);
                }
            });
            if (listener != null) {
                anim.addListener(listener);
            }
            mCurrentAnimation = anim;
            anim.start();
            return true;
        }

        return false;
    }

    public MapView setZoom(final float zoomlevel) {
        return setZoom(zoomlevel, false);
    }

    public MapView setZoom(final float zoomlevel, final ILatLng latlong, final boolean userAction) {
        mCurrentlyUserAction = userAction;
        stopAnimation(true);
        mMapView.setZoomInternal(zoomlevel, latlong, null);
        mCurrentlyUserAction = false;
        return mMapView;
    }

    public MapView setZoom(final float zoomlevel, final boolean userAction) {
        mCurrentlyUserAction = userAction;
        stopAnimation(true);
        mMapView.setZoomInternal(zoomlevel);
        mCurrentlyUserAction = false;
        return mMapView;
    }

    public MapView setZoomAnimated(final float zoomlevel) {
        setZoomAnimated(zoomlevel, mMapView.getCenter(), false);
        return mMapView;
    }

    public MapView setZoomAnimated(final float zoomlevel, final ILatLng latlong, final boolean userAction) {
        setZoomAnimated(zoomlevel, latlong, false, userAction);
        return mMapView;
    }

    /**
     * Zoom in by one zoom level.
     */
    public boolean zoomIn(final boolean userAction) {
        return zoomInAbout(mMapView.getCenter(), userAction);
    }

    public boolean zoomIn() {
        return zoomIn(false);
    }

    public boolean zoomInAbout(final ILatLng latlong, final boolean userAction) {
        float currentZoom = mMapView.getZoomLevel(false);
        float targetZoom = (float) (Math.ceil(currentZoom) + 1);
        float factor = (float) Math.pow(2, targetZoom - currentZoom);

        if (factor > 2.25) {
            targetZoom = (float) Math.ceil(currentZoom);
        }
        return setZoomAnimated(targetZoom, latlong, false, userAction);
    }

    public boolean zoomInAbout(final ILatLng latlong) {
        return zoomInAbout(latlong, false);
    }

    /**
     * Zoom out by one zoom level.
     */
    public boolean zoomOut(final boolean userAction) {
        return zoomOutAbout(mMapView.getCenter(), userAction);
    }

    public boolean zoomOut() {
        return zoomOut(false);
    }

    public boolean zoomOutAbout(final ILatLng latlong, final boolean userAction) {
        float currentZoom = mMapView.getZoomLevel(false);
        float targetZoom = (float) (Math.floor(currentZoom));
        float factor = (float) Math.pow(2, targetZoom - currentZoom);

        if (factor > 0.75) {
            targetZoom = (float) (Math.floor(currentZoom) - 1);
        }

        return setZoomAnimated(targetZoom, latlong, false, userAction);
    }

    public boolean zoomOutAbout(final ILatLng latlong) {
        return zoomOutAbout(latlong, false);
    }

    protected void onAnimationStart() {
        mMapView.setIsAnimating(true);
    }

    public void onAnimationEnd() {
        stopPanning();
        mMapView.setIsAnimating(false);
        mMapView.setZoomInternal(mMapView.getAnimatedZoom(), zoomOnLatLong, zoomDeltaScroll);
        zoomOnLatLong = null;
        mCurrentlyUserAction = false;
    }

    /**
     * Called when the mapView is layed out for the first time
     * if action were triggered before we had to wait because
     * we didn't have any projection
     */
    public void mapViewLayedOut() {
        if (mPointToGoTo != null) {
            setCenter(mPointToGoTo);
            mPointToGoTo = null;
        }
        if (mZoomToZoomTo != -1) {
            setZoom(mZoomToZoomTo);
            mZoomToZoomTo = -1;
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy