package com.mapbox.mapboxsdk.maps; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; import android.os.Bundle; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.Size; import android.support.annotation.UiThread; import android.text.TextUtils; import android.view.View; import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.gestures.MoveGestureDetector; import com.mapbox.android.gestures.RotateGestureDetector; import com.mapbox.android.gestures.ShoveGestureDetector; import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.geojson.Feature; import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; 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.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.location.LocationComponent; import com.mapbox.mapboxsdk.log.Logger; import com.mapbox.mapboxsdk.style.expressions.Expression; import java.util.List; /** * 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 MapboxMap should only be read and modified from the main thread. *

*/ @UiThread public final class MapboxMap { private static final String TAG = "Mbgl-MapboxMap"; private final NativeMapView nativeMapView; private final UiSettings uiSettings; private final Projection projection; private final Transform transform; private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; private final MapChangeReceiver mapChangeReceiver; private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; private LocationComponent locationComponent; @Nullable private MapboxMap.OnFpsChangedListener onFpsChangedListener; private Style style; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, Projection projection, OnGesturesManagerInteractionListener listener, AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher, MapChangeReceiver mapChangeReceiver) { this.nativeMapView = map; this.style = new Style(nativeMapView); this.uiSettings = ui; this.projection = projection; this.annotationManager = annotations.bind(this); this.transform = transform; this.onGesturesManagerInteractionListener = listener; this.cameraChangeDispatcher = cameraChangeDispatcher; this.mapChangeReceiver = mapChangeReceiver; } void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { transform.initialise(this, options); uiSettings.initialise(context, options); // Map configuration setDebugActive(options.getDebugActive()); setApiBaseUrl(options); setStyleUrl(options); setStyleJson(options); setPrefetchesTiles(options); } public Style getStyle(){ return style; } /** * Called when the hosting Activity/Fragment onStart() method is called. */ void onStart() { nativeMapView.update(); if (TextUtils.isEmpty(nativeMapView.getStyleUrl()) && TextUtils.isEmpty(nativeMapView.getStyleJson())) { // if user hasn't loaded a Style yet nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); } locationComponent.onStart(); } /** * Called when the hosting Activity/Fragment onStop() method is called. */ void onStop() { locationComponent.onStop(); } /** * Called when the hosting Activity/Fragment is going to be destroyed and map state needs to be saved. * * @param outState the bundle to save the state to. */ void onSaveInstanceState(@NonNull Bundle outState) { outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); uiSettings.onSaveInstanceState(outState); } /** * Called when the hosting Activity/Fragment is recreated and map state needs to be restored. * * @param savedInstanceState the bundle containing the saved state */ void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); uiSettings.onRestoreInstanceState(savedInstanceState); if (cameraPosition != null) { moveCamera(CameraUpdateFactory.newCameraPosition( new CameraPosition.Builder(cameraPosition).build()) ); } nativeMapView.setDebug(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE)); final String styleUrl = savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL); if (!TextUtils.isEmpty(styleUrl)) { nativeMapView.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL)); } } /** * Called when the hosting Activity/Fragment onDestroy()/onDestroyView() method is called. */ void onDestroy() { locationComponent.onDestroy(); } /** * Called before the OnMapReadyCallback is invoked. */ void onPreMapReady() { transform.invalidateCameraPosition(); annotationManager.reloadMarkers(); annotationManager.adjustTopOffsetPixels(this); } /** * Called when the map will start loading style. */ void onStartLoadingMap() { locationComponent.onStartLoadingMap(); } /** * Called the map finished loading style. */ void onFinishLoadingStyle() { locationComponent.onFinishLoadingStyle(); } /** * Called when the region is changing or has changed. */ void onUpdateRegionChange() { annotationManager.update(); } /** * Called when the map frame is fully rendered. */ void onUpdateFullyRendered() { CameraPosition cameraPosition = transform.invalidateCameraPosition(); if (cameraPosition != null) { uiSettings.update(cameraPosition); } } // Style /** * Sets tile pre-fetching from MapboxOptions. * * @param options the options object */ private void setPrefetchesTiles(@NonNull MapboxMapOptions options) { setPrefetchesTiles(options.getPrefetchesTiles()); } /** * Enable or disable tile pre-fetching. Pre-fetching makes sure that a low-resolution * tile is rendered as soon as possible at the expense of a little bandwidth. * * @param enable true to enable */ public void setPrefetchesTiles(boolean enable) { nativeMapView.setPrefetchesTiles(enable); } /** * Check whether tile pre-fetching is enabled or not. * * @return true if enabled * @see MapboxMap#setPrefetchesTiles(boolean) */ public boolean getPrefetchesTiles() { return nativeMapView.getPrefetchesTiles(); } // // MinZoom // /** *

* Sets the minimum zoom level the map can be displayed at. *

* * @param minZoom The new minimum zoom level. */ public void setMinZoomPreference( @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { transform.setMinZoom(minZoom); } /** *

* Gets the minimum zoom level the map can be displayed at. *

* * @return The minimum zoom level. */ public double getMinZoomLevel() { return transform.getMinZoom(); } // // MaxZoom // /** *

* Sets the maximum zoom level the map can be displayed at. *

*

* The default maximum zoomn level is 22. The upper bound for this value is 25.5. *

* * @param maxZoom The new maximum zoom level. */ public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { transform.setMaxZoom(maxZoom); } /** *

* Gets the maximum zoom level the map can be displayed at. *

* * @return The maximum zoom level. */ public double getMaxZoomLevel() { return transform.getMaxZoom(); } // // UiSettings // /** * Gets the user interface settings for the map. * * @return the UiSettings associated with this map */ @NonNull public UiSettings getUiSettings() { return uiSettings; } // // Projection // /** * Get the Projection object that you can use to convert between screen coordinates and latitude/longitude * coordinates. * * @return the Projection associated with this map */ @NonNull public Projection getProjection() { return projection; } // // Camera API // /** * Cancels ongoing animations. *

* This invokes the {@link CancelableCallback} for ongoing camera updates. *

*/ public void cancelTransitions() { transform.cancelTransitions(); } /** * 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. */ @NonNull public final CameraPosition getCameraPosition() { return transform.getCameraPosition(); } /** * 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 the camera position to set */ public void setCameraPosition(@NonNull CameraPosition cameraPosition) { moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 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. */ public final void moveCamera(@NonNull 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 * @param callback the callback to be invoked when an animation finishes or is canceled */ public final void moveCamera(@NonNull final CameraUpdate update, @Nullable final MapboxMap.CancelableCallback callback) { transform.moveCamera(MapboxMap.this, update, callback); } /** * Gradually move the camera by the default duration, zoom will not be affected unless specified * within {@link CameraUpdate}. If {@link #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. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void easeCamera(@NonNull CameraUpdate update) { easeCamera(update, MapboxConstants.ANIMATION_DURATION); } /** * Gradually move the camera by the default duration, zoom will not be affected unless specified * within {@link CameraUpdate}. If {@link #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 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. * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void easeCamera(@NonNull CameraUpdate update, @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } /** * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected * unless specified within {@link CameraUpdate}. If {@link #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. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void easeCamera(@NonNull CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } /** * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected * unless specified within {@link CameraUpdate}. A callback can be used to be notified when * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it * will return the current location of the camera in flight. *

* Note that this will cancel location tracking mode if enabled. *

* * @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. * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void easeCamera(@NonNull CameraUpdate update, int durationMs, @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, true, callback); } /** * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected * unless specified within {@link CameraUpdate}. A callback can be used to be notified when * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it * will return the current location of the camera in flight. *

* Note that this will cancel location tracking mode if enabled. *

* * @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 easingInterpolator True for easing interpolator, false for linear. */ public final void easeCamera(@NonNull CameraUpdate update, int durationMs, boolean easingInterpolator) { easeCamera(update, durationMs, easingInterpolator, null); } /** * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected * unless specified within {@link CameraUpdate}. A callback can be used to be notified when * easing the camera stops. If {@link #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 easingInterpolator True for easing interpolator, false for linear. * @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. * Do not update or ease the camera from within onCancel(). */ public final void easeCamera(@NonNull final CameraUpdate update, final int durationMs, final boolean easingInterpolator, @Nullable final MapboxMap.CancelableCallback callback) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into easeCamera"); } transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback); } /** * Animate the camera to a new location defined within {@link CameraUpdate} using a transition * animation that evokes powered flight. The animation will last the default amount of time. * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location * of the camera in flight. * * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void animateCamera(@NonNull CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } /** * Animate the camera to a new location defined within {@link CameraUpdate} using a transition * animation that evokes powered flight. The animation will last the default amount of time. A * callback can be used to be notified when animating the camera stops. During the animation, a * call to {@link #getCameraPosition()} returns an intermediate location of the camera in flight. * * @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(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void animateCamera(@NonNull CameraUpdate update, @Nullable MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } /** * Animate the camera to a new location defined within {@link CameraUpdate} using a transition * animation that evokes powered flight. The animation will last a specified amount of time * given in milliseconds. During the animation, a call to {@link #getCameraPosition()} returns * an intermediate 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. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void animateCamera(@NonNull CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } /** * Animate the camera to a new location defined within {@link CameraUpdate} using a transition * animation that evokes powered flight. The animation will last a specified amount of time * given in milliseconds. A callback can be used to be notified when animating the camera stops. * During the animation, a call to {@link #getCameraPosition()} returns an intermediate 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. * Do not update or animate the camera from within onCancel(). If a callback * isn't required, leave it as null. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ public final void animateCamera(@NonNull final CameraUpdate update, final int durationMs, @Nullable final MapboxMap.CancelableCallback callback) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into animateCamera"); } transform.animateCamera(MapboxMap.this, update, durationMs, callback); } /** * Scrolls the camera over the map, shifting the center of view by the specified number of pixels in the x and y * directions. * * @param x Amount of pixels to scroll to in x direction * @param y Amount of pixels to scroll to in y direction */ public void scrollBy(float x, float y) { nativeMapView.moveBy(x, y); } /** * Scrolls the camera over the map, shifting the center of view by the specified number of pixels in the x and y * directions. * * @param x Amount of pixels to scroll to in x direction * @param y Amount of pixels to scroll to in y direction * @param duration Amount of time the scrolling should take */ public void scrollBy(float x, float y, long duration) { nativeMapView.moveBy(x, y, duration); } // // Reset North // /** * Resets the map view to face north. */ public void resetNorth() { transform.resetNorth(); } /** * Transform the map bearing given a bearing, focal point coordinates, and a duration. * * @param bearing The bearing of the Map to be transformed to * @param focalX The x coordinate of the focal point * @param focalY The y coordinate of the focal point * @param duration The duration of the transformation */ public void setFocalBearing(double bearing, float focalX, float focalY, long duration) { transform.setBearing(bearing, focalX, focalY, duration); } /** * Returns the measured height of the Map. * * @return the height of the map */ public float getHeight() { return nativeMapView.getHeight(); } /** * Returns the measured width of the Map. * * @return the width of the map */ public float getWidth() { return nativeMapView.getWidth(); } // // Debug // /** * Returns whether the map debug information is currently shown. * * @return If true, map debug information is currently shown. */ public boolean isDebugActive() { return nativeMapView.getDebug(); } /** *

* Changes whether the map debug information is shown. *

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

* Cycles through the map debug options. *

* The value of isDebugActive reflects whether there are * any map debug options enabled or disabled. * * @see #isDebugActive() */ public void cycleDebugOptions() { nativeMapView.cycleDebugOptions(); } // // API endpoint config // private void setApiBaseUrl(@NonNull MapboxMapOptions options) { String apiBaseUrl = options.getApiBaseUrl(); if (!TextUtils.isEmpty(apiBaseUrl)) { nativeMapView.setApiBaseUrl(apiBaseUrl); } } // // Styling // /** *

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

* {@code url} can take the following forms: *