package com.mapbox.mapboxsdk.maps; import android.location.Location; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.v4.util.LongSparseArray; import android.text.TextUtils; import android.util.Log; import android.view.View; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.layers.CustomLayer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * The general class to interact with in the Android Mapbox SDK. It exposes the entry point for all * methods related to the MapView. You cannot instantiate {@link MapboxMap} object directly, rather, * you must obtain one from the getMapAsync() method on a MapFragment or MapView that you have * added to your application. *

* Note: Similar to a View object, a GoogleMap should only be read and modified from the main thread. *

*/ public class MapboxMap { private MapView mMapView; private UiSettings mUiSettings; private TrackingSettings mTrackingSettings; private Projection mProjection; private CameraPosition mCameraPosition; private boolean mInvalidCameraPosition; private LongSparseArray mAnnotations; private List mSelectedMarkers; private List mInfoWindows; private MapboxMap.InfoWindowAdapter mInfoWindowAdapter; private boolean mMyLocationEnabled; private boolean mAllowConcurrentMultipleInfoWindows; private MapboxMap.OnMapClickListener mOnMapClickListener; private MapboxMap.OnMapLongClickListener mOnMapLongClickListener; private MapboxMap.OnMarkerClickListener mOnMarkerClickListener; private MapboxMap.OnInfoWindowClickListener mOnInfoWindowClickListener; private MapboxMap.OnInfoWindowLongClickListener mOnInfoWindowLongClickListener; private MapboxMap.OnInfoWindowCloseListener mOnInfoWindowCloseListener; private MapboxMap.OnFlingListener mOnFlingListener; private MapboxMap.OnScrollListener mOnScrollListener; private MapboxMap.OnMyLocationTrackingModeChangeListener mOnMyLocationTrackingModeChangeListener; private MapboxMap.OnMyBearingTrackingModeChangeListener mOnMyBearingTrackingModeChangeListener; private MapboxMap.OnFpsChangedListener mOnFpsChangedListener; private MapboxMap.OnCameraChangeListener mOnCameraChangeListener; MapboxMap(@NonNull MapView mapView) { mMapView = mapView; mMapView.addOnMapChangedListener(new MapChangeCameraPositionListener()); mUiSettings = new UiSettings(mapView); mTrackingSettings = new TrackingSettings(mMapView, mUiSettings); mProjection = new Projection(mapView); mAnnotations = new LongSparseArray<>(); mSelectedMarkers = new ArrayList<>(); mInfoWindows = new ArrayList<>(); } // // UiSettings // /** * Gets the user interface settings for the map. * * @return */ public UiSettings getUiSettings() { return mUiSettings; } // // TrackingSettings // /** * Gets the tracking interface settings for the map. * * @return */ public TrackingSettings getTrackingSettings() { return mTrackingSettings; } // // Projection // /** * Get the Projection object that you can use to convert between screen coordinates and latitude/longitude coordinates. */ public Projection getProjection() { return mProjection; } // // Camera API // /** * Gets the current position of the camera. * The CameraPosition returned is a snapshot of the current position, and will not automatically update when the camera moves. * * @return The current position of the Camera. */ public final CameraPosition getCameraPosition() { if (mInvalidCameraPosition) { invalidateCameraPosition(); } return mCameraPosition; } /** * Repositions the camera according to the cameraPosition. * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. * See CameraUpdateFactory for a set of updates. * * @param cameraPosition */ public void setCameraPosition(@NonNull CameraPosition cameraPosition) { moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); } /** * Repositions the camera according to the instructions defined in the update. * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. * See CameraUpdateFactory for a set of updates. * * @param update The change that should be applied to the camera. */ @UiThread public final void moveCamera(CameraUpdate update) { moveCamera(update, null); } /** * Repositions the camera according to the instructions defined in the update. * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. * See CameraUpdateFactory for a set of updates. * * @param update The change that should be applied to the camera. */ @UiThread public final void moveCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); mMapView.jumpTo(mCameraPosition.bearing, mCameraPosition.target, mCameraPosition.tilt, mCameraPosition.zoom); if (mOnCameraChangeListener != null) { mOnCameraChangeListener.onCameraChange(mCameraPosition); } if (callback != null) { callback.onFinish(); } } /** * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates. * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight. * * @param update The change that should be applied to the camera. */ @UiThread public final void easeCamera(CameraUpdate update) { easeCamera(update, MapboxConstants.ANIMATION_DURATION); } /** * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates. * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight. * * @param update The change that should be applied to the camera. * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown. */ @UiThread public final void easeCamera(CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } /** * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates. * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight. * * @param update The change that should be applied to the camera. * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown. * @param callback An optional callback to be notified from the main thread when the animation stops. If the animation stops due to its natural completion, the callback will be notified with onFinish(). If the animation stops due to interruption by a later camera movement or a user gesture, onCancel() will be called. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null. */ @UiThread public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() { @Override public void onCancel() { if (callback != null) { callback.onCancel(); } } @Override public void onFinish() { if (mOnCameraChangeListener != null) { mOnCameraChangeListener.onCameraChange(mCameraPosition); } if (callback != null) { callback.onFinish(); } } }); } /** * Animates the movement of the camera from the current position to the position defined in the update. * During the animation, a call to getCameraPosition() returns an intermediate location of the camera. *

* See CameraUpdateFactory for a set of updates. * * @param update The change that should be applied to the camera. */ @UiThread public final void animateCamera(CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } /** * Animates the movement of the camera from the current position to the position defined in the update and calls an optional callback on completion. * See CameraUpdateFactory for a set of updates. * During the animation, a call to getCameraPosition() returns an intermediate location of the camera. * * @param update The change that should be applied to the camera. * @param callback The callback to invoke from the main thread when the animation stops. If the animation completes normally, onFinish() is called; otherwise, onCancel() is called. Do not update or animate the camera from within onCancel(). */ @UiThread public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } /** * Moves the map according to the update with an animation over a specified duration. See CameraUpdateFactory for a set of updates. * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight. * * @param update The change that should be applied to the camera. * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown. */ @UiThread public final void animateCamera(CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } /** * Moves the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates. * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight. * * @param update The change that should be applied to the camera. * @param durationMs The duration of the animation in milliseconds. This must be strictly positive, otherwise an IllegalArgumentException will be thrown. * @param callback An optional callback to be notified from the main thread when the animation stops. If the animation stops due to its natural completion, the callback will be notified with onFinish(). If the animation stops due to interruption by a later camera movement or a user gesture, onCancel() will be called. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null. */ @UiThread public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { mCameraPosition = update.getCameraPosition(this); mMapView.flyTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() { @Override public void onCancel() { if (callback != null) { callback.onCancel(); } } @Override public void onFinish() { if (mOnCameraChangeListener != null) { mOnCameraChangeListener.onCameraChange(mCameraPosition); } if (callback != null) { callback.onFinish(); } } }); } /** * Converts milliseconds to nanoseconds * * @param durationMs The time in milliseconds * @return time in nanoseconds */ private long getDurationNano(long durationMs) { return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0; } /** * Invalidates the current camera position by reconstructing it from mbgl */ private void invalidateCameraPosition() { mInvalidCameraPosition = false; mCameraPosition = mMapView.invalidateCameraPosition(); if (mOnCameraChangeListener != null) { mOnCameraChangeListener.onCameraChange(mCameraPosition); } } // // Reset North // /** * */ public void resetNorth() { mMapView.resetNorth(); } // // Debug // /** * Returns whether the map debug information is currently shown. * * @return If true, map debug information is currently shown. */ @UiThread public boolean isDebugActive() { return mMapView.isDebugActive(); } /** *

* Changes whether the map debug information is shown. *

* The default value is false. * * @param debugActive If true, map debug information is shown. */ @UiThread public void setDebugActive(boolean debugActive) { mMapView.setDebugActive(debugActive); } /** *

* Cycles through the map debug options. *

* The value of {@link MapView#isDebugActive()} reflects whether there are * any map debug options enabled or disabled. * * @see MapView#isDebugActive() */ @UiThread public void cycleDebugOptions() { mMapView.cycleDebugOptions(); } // // Styling // /** *

* Loads a new map style from the specified URL. *

* {@code url} can take the following forms: *
    *
  • {@code Style.*}: load one of the bundled styles in {@link Style}.
  • *
  • {@code mapbox://styles//