[android] Camera API

This commit is contained in:
Brad Leege
2015-12-10 11:04:26 -08:00
committed by John Firebaugh
parent e2ad0e008a
commit f7d1a40350
17 changed files with 848 additions and 15 deletions

View File

@@ -0,0 +1,169 @@
package com.mapbox.mapboxsdk.camera;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.FloatRange;
import com.mapbox.mapboxsdk.geometry.LatLng;
public final class CameraPosition implements Parcelable {
public static final Parcelable.Creator<CameraPosition> CREATOR
= new Parcelable.Creator<CameraPosition>() {
public CameraPosition createFromParcel(Parcel in) {
float bearing = in.readFloat();
LatLng target = in.readParcelable(LatLng.class.getClassLoader());
float tilt = in.readFloat();
float zoom = in.readFloat();
return new CameraPosition(target, zoom, tilt, bearing);
}
public CameraPosition[] newArray(int size) {
return new CameraPosition[size];
}
};
/**
* Direction that the camera is pointing in, in degrees clockwise from north.
*/
public final float bearing;
/**
* The location that the camera is pointing at.
*/
public final LatLng target;
/**
* The angle, in degrees, of the camera angle from the nadir (directly facing the Earth). See tilt(float) for details of restrictions on the range of values.
*/
public final float tilt;
/**
* Zoom level near the center of the screen. See zoom(float) for the definition of the camera's zoom level.
*/
public final float zoom;
/**
*
* Constructs a CameraPosition.
* @param target The target location to align with the center of the screen.
* @param zoom Zoom level at target. See zoom(float) for details of restrictions.
* @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float) for details of restrictions.
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north. This value will be normalized to be within 0 degrees inclusive and 360 degrees exclusive.
* @throws NullPointerException if target is null
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
*/
public CameraPosition (LatLng target, float zoom, float tilt, float bearing) throws NullPointerException, IllegalArgumentException{
super();
if (target == null) {
throw new NullPointerException("target is NULL");
}
this.target = target;
// Allow for default value of -1
if (tilt != -1) {
if (tilt < 0.0f || tilt > 90.0f) {
throw new IllegalArgumentException("tilt is outside of 0 to 90 degrees range");
}
}
this.tilt = tilt;
this.bearing = bearing;
this.zoom = zoom;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeFloat(bearing);
out.writeParcelable(target, flags);
out.writeFloat(tilt);
out.writeFloat(zoom);
}
/**
* Builds camera position.
*/
public static final class Builder {
private float bearing = -1;
private LatLng target = null;
private float tilt = -1;
private float zoom = -1;
/**
* Creates an empty builder.
*/
public Builder() {
super();
}
/**
* Create Builder with an existing CameraPosition data.
* @param previous Existing CameraPosition values to use
*/
public Builder(CameraPosition previous) {
super();
if (previous != null) {
this.bearing = previous.bearing;
this.target = previous.target;
this.tilt = previous.tilt;
this.zoom = previous.zoom;
}
}
/**
* Sets the direction that the camera is pointing in, in degrees clockwise from north.
* @param bearing Bearing
* @return Builder
*/
public Builder bearing (float bearing) {
this.bearing = bearing;
return this;
}
/**
* Builds a CameraPosition.
* @return CameraPosition
*/
public CameraPosition build() {
return new CameraPosition(target, zoom, tilt, bearing);
}
/**
* Sets the location that the camera is pointing at.
* @param location Location
* @return Builder
*/
public Builder target(LatLng location) {
this.target = location;
return this;
}
/**
* Set the tilt
* @param tilt Tilt value
* @return Builder
*/
@FloatRange(from = 0.0, to = 60.0)
public Builder tilt(float tilt) {
this.tilt = tilt;
return this;
}
/**
* Set the zoom
* @param zoom Zoom value
* @return Builder
*/
public Builder zoom(float zoom) {
this.zoom = zoom;
return this;
}
}
}

View File

@@ -0,0 +1,56 @@
package com.mapbox.mapboxsdk.camera;
import com.mapbox.mapboxsdk.geometry.LatLng;
public final class CameraUpdate {
/**
* Direction that the camera is pointing in, in degrees clockwise from north.
*/
private final float bearing;
/**
* The location that the camera is pointing at.
*/
private final LatLng target;
/**
* The angle, in degrees, of the camera angle from the nadir (directly facing the Earth). See tilt(float) for details of restrictions on the range of values.
*/
private final float tilt;
/**
* Zoom level near the center of the screen. See zoom(float) for the definition of the camera's zoom level.
*/
private final float zoom;
/**
* Package Private Constructor to only be used CameraUpdateFactory
* @param bearing Final Bearing
* @param target Final Target
* @param tilt Final Tilt
* @param zoom Final Zoom
*/
CameraUpdate(float bearing, LatLng target, float tilt, float zoom) {
this.bearing = bearing;
this.target = target;
this.tilt = tilt;
this.zoom = zoom;
}
public float getBearing() {
return bearing;
}
public LatLng getTarget() {
return target;
}
public float getTilt() {
return tilt;
}
public float getZoom() {
return zoom;
}
}

View File

@@ -0,0 +1,15 @@
package com.mapbox.mapboxsdk.camera;
import android.support.annotation.NonNull;
public class CameraUpdateFactory {
/**
* Returns a CameraUpdate that moves the camera to a specified CameraPosition.
* @param cameraPosition Camera Position to change to
* @return CameraUpdate Final Camera Position data
*/
public static CameraUpdate newCameraPosition (@NonNull CameraPosition cameraPosition) {
return new CameraUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom);
}
}

View File

@@ -0,0 +1,16 @@
package com.mapbox.mapboxsdk.utils;
public class MathUtils {
/**
* Test a value in specified range, returning minimum if it's below, and maximum if it's above
* @param value Value to test
* @param min Minimum value of range
* @param max Maximum value of range
* @return value if it's between min and max, min if it's below, max if it's above
*/
public static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
}

View File

@@ -65,6 +65,9 @@ import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.annotations.Sprite;
import com.mapbox.mapboxsdk.annotations.SpriteFactory;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.constants.MathConstants;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
@@ -76,7 +79,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngZoom;
import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.utils.ApiAccess;
import com.mapbox.mapboxsdk.utils.MathUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@@ -84,6 +87,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* <p>
@@ -581,6 +585,21 @@ public final class MapView extends FrameLayout {
void onMyLocationChange(@Nullable Location location);
}
/**
* A callback interface for reporting when a task is complete or cancelled.
*/
public static interface CancelableCallback {
/**
* Invoked when a task is cancelled.
*/
public abstract void onCancel();
/**
* Invoked when a task is complete.
*/
public abstract void onFinish();
}
//
// Constructors
//
@@ -1452,6 +1471,109 @@ public final class MapView extends FrameLayout {
this.mTiltEnabled = tiltEnabled;
}
//
// 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 () {
return new CameraPosition(getCenterCoordinate(), (float)getZoomLevel(), (float)getTilt(), (float)getBearing());
}
/**
* 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, 0, 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, MapView.CancelableCallback callback) {
animateCamera(update, 0, callback);
}
/**
* 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 MapView.CancelableCallback callback) {
if (update.getTarget() == null) {
Log.w(TAG, "animateCamera with null target coordinate passed in. Will immediatele return without animating camera.");
return;
}
mNativeMapView.cancelTransitions();
// Register callbacks early enough
if (callback != null) {
final MapView view = this;
addOnMapChangedListener(new OnMapChangedListener() {
@Override
public void onMapChanged(@MapChange int change) {
if (change == REGION_DID_CHANGE_ANIMATED || change == REGION_DID_CHANGE) {
callback.onFinish();
// Clean up after self
removeOnMapChangedListener(this);
}
}
});
}
// Convert Degrees To Radians
double angle = -1;
if (update.getBearing() >= 0) {
angle = (-update.getBearing()) * MathConstants.DEG2RAD;
}
double pitch = -1;
if (update.getTilt() >= 0) {
double dp = MathUtils.clamp(update.getTilt(), MINIMUM_TILT, MAXIMUM_TILT);
pitch = dp * MathConstants.DEG2RAD;
}
double zoom = -1;
if (update.getZoom() >= 0) {
zoom = update.getZoom();
}
long durationNano = 0;
if (durationMs > 0) {
durationNano = TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS);
}
if (durationMs == 0) {
// Route To `jumpTo`
Log.i(TAG, "jumpTo() called with angle = " + angle + "; target = " + update.getTarget() + "; durationNano = " + durationNano + "; Pitch = " + pitch + "; Zoom = " + update.getZoom());
jumpTo(angle, update.getTarget(), pitch, zoom);
} else {
// Use `flyTo`
Log.i(TAG, "flyTo() called with angle = " + angle + "; target = " + update.getTarget() + "; durationNano = " + durationNano + "; Pitch = " + pitch + "; Zoom = " + update.getZoom());
flyTo(angle, update.getTarget(), durationNano, pitch, zoom);
}
}
//
// InfoWindows
//
@@ -2276,9 +2398,51 @@ public final class MapView extends FrameLayout {
}
//
// Camera
// Mapbox Core GL Camera
//
/**
* Change any combination of center, zoom, bearing, and pitch, without
* a transition. The map will retain the current values for any options
* not included in `options`.
* @param bearing Bearing in Radians
* @param center Center Coordinate
* @param pitch Pitch in Radians
* @param zoom Zoom Level
*/
@UiThread
public void jumpTo(double bearing, LatLng center, double pitch, double zoom) {
mNativeMapView.jumpTo(bearing, center, pitch, zoom);
}
/**
* Change any combination of center, zoom, bearing, and pitch, with a smooth animation
* between old and new values. The map will retain the current values for any options
* not included in `options`.
* @param bearing Bearing in Radians
* @param center Center Coordinate
* @param duration Animation time in Nanoseconds
* @param pitch Pitch in Radians
* @param zoom Zoom Level
*/
@UiThread
public void easeTo(double bearing, LatLng center, long duration, double pitch, double zoom) {
mNativeMapView.easeTo(bearing, center, duration, pitch, zoom);
}
/**
* Flying animation to a specified location/zoom/bearing with automatic curve.
* @param bearing Bearing in Radians
* @param center Center Coordinate
* @param duration Animation time in Nanoseconds
* @param pitch Pitch in Radians
* @param zoom Zoom Level
*/
@UiThread
public void flyTo(double bearing, LatLng center, long duration, double pitch, double zoom) {
mNativeMapView.flyTo(bearing, center, duration, pitch, zoom);
}
/**
* Changes the map's viewport to fit the given coordinate bounds.
*
@@ -2464,9 +2628,32 @@ public final class MapView extends FrameLayout {
}
}
// Used by UserLocationView
void setBearing(float bearing) {
mNativeMapView.setBearing(bearing, 100);
/**
* Get Bearing in degrees
* @return Bearing in degrees
*/
public double getBearing() {
return mNativeMapView.getBearing();
}
/**
* Set Bearing in degrees
* @param bearing Bearing in degrees
*/
public void setBearing(float bearing) {
mNativeMapView.setBearing(bearing);
}
/**
* Sets Bearing in degrees
*
* NOTE: Used by UserLocationView
*
* @param bearing Bearing in degrees
* @param duration Length of time to rotate
*/
public void setBearing(float bearing, long duration) {
mNativeMapView.setBearing(bearing, duration);
}
//

View File

@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.views;
import android.graphics.PointF;
import android.graphics.RectF;
import android.view.Surface;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
@@ -12,7 +11,6 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngZoom;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.layers.CustomLayer;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -450,6 +448,18 @@ final class NativeMapView {
return nativeGetTopOffsetPixelsForAnnotationSymbol(mNativeMapViewPtr, symbolName);
}
public void jumpTo(double angle, LatLng center, double pitch, double zoom) {
nativeJumpTo(mNativeMapViewPtr, angle, center, pitch, zoom);
}
public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom) {
nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom);
}
public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) {
nativeFlyTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom);
}
public void addCustomLayer(CustomLayer customLayer, String before) {
nativeAddCustomLayer(mNativeMapViewPtr, customLayer, before);
}
@@ -639,6 +649,12 @@ final class NativeMapView {
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName);
private native void nativeJumpTo(long nativeMapViewPtr, double angle, LatLng center, double pitch, double zoom);
private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
private native void nativeFlyTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
private native void nativeAddCustomLayer(long nativeMapViewPtr, CustomLayer customLayer, String before);
private native void nativeRemoveCustomLayer(long nativeMapViewPtr, String id);

View File

@@ -91,6 +91,7 @@ final class UserLocationView extends View implements LocationListener {
// Compass data
private MyBearingListener mBearingChangeListener;
private static final long BEARING_DURATION = 100;
public UserLocationView(Context context) {
super(context);
@@ -555,7 +556,7 @@ final class UserLocationView extends View implements LocationListener {
mShowDirection = true;
mGpsMarkerDirection = 0;
if (location.hasBearing()) {
mMapView.setBearing(location.getBearing());
mMapView.setBearing(location.getBearing(), BEARING_DURATION);
}
}
@@ -602,7 +603,7 @@ final class UserLocationView extends View implements LocationListener {
mShowDirection = true;
mGpsMarkerDirection = 0;
mCompassMarkerDirection = 0;
mMapView.setBearing(bearing);
mMapView.setBearing(bearing, BEARING_DURATION);
}
}
}