diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..9828157bcc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +## 7.0.0 + +### removed: + +* isTelemeryEnbaled from android [#1](https://github.com/mfazekas/maps/pull/1) + diff --git a/android/install.md b/android/install.md index 2ba5b0dbfd..41905a2c52 100644 --- a/android/install.md +++ b/android/install.md @@ -46,7 +46,7 @@ Include project, so gradle knows where to find the project ``` include ':mapbox-react-native-mapbox-gl' -project(':mapbox-react-native-mapbox-gl').projectDir = new File(rootProject.projectDir, '../node_modules/@mapbox/react-native-mapbox-gl/android/rctmgl') +project(':mapbox-react-native-mapbox-gl').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-mapbox/maps/android/rctmgl') ``` ### MainApplication.java diff --git a/android/rctmgl/build.gradle b/android/rctmgl/build.gradle index 34acc1db85..9cbd89c096 100644 --- a/android/rctmgl/build.gradle +++ b/android/rctmgl/build.gradle @@ -30,8 +30,9 @@ dependencies { compileOnly "com.facebook.react:react-native:+" // Mapbox SDK - implementation 'com.mapbox.mapboxsdk:mapbox-android-services:2.2.9' - implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.3@aar' + + implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:4.6.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:7.3.0' // Dependencies implementation "com.android.support:support-vector-drawable:${safeExtGet('supportLibVersion', '28.0.0')}" @@ -40,6 +41,6 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:${safeExtGet('okhttpVersion', '3.12.1')}" // Mapbox plugins - implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization:0.1.0' - implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-locationlayer:0.3.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v7:0.9.0' + implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-markerview-v7:0.2.0' } diff --git a/android/rctmgl/src/main/AndroidManifest.xml b/android/rctmgl/src/main/AndroidManifest.xml index 0eaea12981..cdc05e12a9 100644 --- a/android/rctmgl/src/main/AndroidManifest.xml +++ b/android/rctmgl/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/RCTMGLPackage.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/RCTMGLPackage.java index 29460c1ef4..728b9e4200 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/RCTMGLPackage.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/RCTMGLPackage.java @@ -12,6 +12,7 @@ import com.mapbox.rctmgl.components.annotation.RCTMGLCalloutManager; import com.mapbox.rctmgl.components.annotation.RCTMGLPointAnnotationManager; +import com.mapbox.rctmgl.components.camera.RCTMGLCameraManager; import com.mapbox.rctmgl.components.mapview.RCTMGLMapViewManager; import com.mapbox.rctmgl.components.mapview.RCTMGLAndroidTextureMapViewManager; import com.mapbox.rctmgl.components.styles.layers.RCTMGLBackgroundLayerManager; @@ -26,6 +27,7 @@ import com.mapbox.rctmgl.components.styles.sources.RCTMGLRasterSourceManager; import com.mapbox.rctmgl.components.styles.sources.RCTMGLShapeSourceManager; import com.mapbox.rctmgl.components.styles.sources.RCTMGLVectorSourceManager; +import com.mapbox.rctmgl.modules.RCTMGLLocationModule; import com.mapbox.rctmgl.modules.RCTMGLModule; import com.mapbox.rctmgl.modules.RCTMGLOfflineModule; import com.mapbox.rctmgl.modules.RCTMGLSnapshotModule; @@ -43,6 +45,7 @@ public List createNativeModules(ReactApplicationContext reactAppli modules.add(new RCTMGLModule(reactApplicationContext)); modules.add(new RCTMGLOfflineModule(reactApplicationContext)); modules.add(new RCTMGLSnapshotModule(reactApplicationContext)); + modules.add(new RCTMGLLocationModule(reactApplicationContext)); return modules; } @@ -57,6 +60,7 @@ public List createViewManagers(ReactApplicationContext reactApplica List managers = new ArrayList<>(); // components + managers.add(new RCTMGLCameraManager(reactApplicationContext)); managers.add(new RCTMGLMapViewManager(reactApplicationContext)); managers.add(new RCTMGLAndroidTextureMapViewManager(reactApplicationContext)); managers.add(new RCTMGLLightManager()); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/AbstractEventEmitter.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/AbstractEventEmitter.java index 9af23ffc94..7fa9007449 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/AbstractEventEmitter.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/AbstractEventEmitter.java @@ -2,11 +2,13 @@ import android.view.ViewGroup; +import com.facebook.react.ReactApplication; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.events.RCTEventEmitter; import java.util.HashMap; import java.util.Map; @@ -14,6 +16,7 @@ import javax.annotation.Nullable; import com.facebook.react.uimanager.events.EventDispatcher; +import com.mapbox.rctmgl.events.EventEmitter; import com.mapbox.rctmgl.events.IEvent; /** @@ -24,9 +27,11 @@ abstract public class AbstractEventEmitter extends ViewGrou private static final double BRIDGE_TIMEOUT_MS = 10; private Map mRateLimitedEvents; private EventDispatcher mEventDispatcher; + private ReactApplicationContext mRCTAppContext; public AbstractEventEmitter(ReactApplicationContext reactApplicationContext) { mRateLimitedEvents = new HashMap<>(); + mRCTAppContext = reactApplicationContext; } public void handleEvent(IEvent event) { @@ -39,6 +44,11 @@ public void handleEvent(IEvent event) { mRateLimitedEvents.put(eventCacheKey, System.currentTimeMillis()); mEventDispatcher.dispatchEvent(new AbstractEvent(event.getID(), event.getKey(), event.toJSON())); + + RCTEventEmitter emitter = EventEmitter.getViewEmitter(mRCTAppContext); + if (emitter != null) { + emitter.receiveEvent(event.getID(), event.getKey(), event.toJSON()); + } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotation.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotation.java index 8628566527..12e3b3c967 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotation.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotation.java @@ -5,19 +5,17 @@ import android.view.MotionEvent; import android.view.View; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.uimanager.UIManagerModule; -import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.plugins.markerview.MarkerView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.rctmgl.components.AbstractMapFeature; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.events.PointAnnotationClickEvent; import com.mapbox.rctmgl.events.constants.EventTypes; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.Point; import java.util.Arrays; import java.util.List; @@ -28,7 +26,7 @@ public class RCTMGLPointAnnotation extends AbstractMapFeature { private RCTMGLPointAnnotationManager mManager; - private MarkerView mAnnotation; + private Marker mAnnotation; private MapboxMap mMap; private RCTMGLMapView mMapView; @@ -141,7 +139,9 @@ public void setAnchor(float x, float y) { mAnchor = Arrays.asList(x, y); if (mAnnotation != null) { - mAnnotation.setAnchor(x, y); + mAnnotation.setRightOffsetPixels((int)x); + mAnnotation.setTopOffsetPixels((int)y); + /* FMTODO mAnnotation.setAnchor(x, y); */ } } @@ -157,7 +157,7 @@ public void setReactSelected(boolean isSelected) { } } - public MarkerView getMarker() { + public Marker getMarker() { return mAnnotation; } @@ -179,7 +179,10 @@ public void makeMarker() { mAnnotation = mMap.addMarker(buildOptions()); if (mAnchor != null && mAnchor.size() == 2) { - mAnnotation.setAnchor(mAnchor.get(0), mAnchor.get(1)); + + //mAnnotation.setAnchor(mAnchor.get(0), mAnchor.get(1)); + mAnnotation.setRightOffsetPixels((int)mAnchor.get(0).floatValue()); + mAnnotation.setTopOffsetPixels((int)mAnchor.get(1).floatValue()); } final RCTMGLPointAnnotation self = this; @@ -188,6 +191,7 @@ public void makeMarker() { } } + private RCTMGLPointAnnotationOptions buildOptions() { RCTMGLPointAnnotationOptions options = new RCTMGLPointAnnotationOptions(); options.annotationID(mID); @@ -212,6 +216,25 @@ private PointF getScreenPosition() { return new PointF((float) loc[0], (float) loc[1]); } + public static class CustomMarker extends Marker { + private String mAnnotationID; + private RCTMGLPointAnnotationOptions mOptions; + + public CustomMarker(String annotationID, RCTMGLPointAnnotationOptions options) { + super(options); + mOptions = options; + mAnnotationID = annotationID; + } + + public String getAnnotationID() { + return mAnnotationID; + } + + public boolean isDefaultIcon() { + return !mOptions.getHasChildren(); + } + } +/* public static class CustomView extends MarkerView { private String mAnnotationID; private RCTMGLPointAnnotationOptions mOptions; @@ -229,5 +252,5 @@ public String getAnnotationID() { public boolean isDefaultIcon() { return !mOptions.getHasChildren(); } - } + } */ } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationAdapter.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationAdapter.java index 61ce059219..fafb835bc2 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationAdapter.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationAdapter.java @@ -22,19 +22,20 @@ * Created by nickitaliano on 9/27/17. */ -public class RCTMGLPointAnnotationAdapter extends MapboxMap.MarkerViewAdapter { +public class RCTMGLPointAnnotationAdapter /* extends MapboxMap.MarkerViewAdapter */ { + /* private RCTMGLMapView mMapView; private LayoutInflater mInflater; public RCTMGLPointAnnotationAdapter(RCTMGLMapView mapView, Context context) { - super(context); + super(context, RCTMGLPointAnnotation.CustomMarker.class); mMapView = mapView; mInflater = LayoutInflater.from(context); } @Nullable @Override - public View getView(@NonNull RCTMGLPointAnnotation.CustomView customAnnotationView, @Nullable View convertView, @NonNull ViewGroup parent) { + public View getView(@NonNull RCTMGLPointAnnotation.CustomMarker customAnnotationView, @Nullable View convertView, @NonNull ViewGroup parent) { final RCTMGLPointAnnotation pointAnnotation = mMapView.getPointAnnotationByID(customAnnotationView.getAnnotationID()); if (pointAnnotation == null) { @@ -97,5 +98,5 @@ private float getZIndex(RCTMGLPointAnnotation pointAnnotation) { private static class ViewHolder { ImageView imageView; LinearLayout customLayout; - } + }*/ } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationOptions.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationOptions.java index 0b024d3c22..706b39fd52 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationOptions.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/annotation/RCTMGLPointAnnotationOptions.java @@ -4,7 +4,8 @@ import android.os.Parcel; import android.os.Parcelable; -import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +// import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -13,7 +14,7 @@ * Created by nickitaliano on 9/27/17. */ -public class RCTMGLPointAnnotationOptions extends BaseMarkerViewOptions { +public class RCTMGLPointAnnotationOptions extends BaseMarkerOptions { private String mAnnotationID; private boolean mHasChildren; @@ -23,12 +24,12 @@ protected RCTMGLPointAnnotationOptions(Parcel in) { position((LatLng) in.readParcelable(LatLng.class.getClassLoader())); snippet(in.readString()); title(in.readString()); - flat(in.readByte() != 0); + /* flat(in.readByte() != 0); */ anchor(in.readFloat(), in.readFloat()); - infoWindowAnchor(in.readFloat(), in.readFloat()); + /* infoWindowAnchor(in.readFloat(), in.readFloat()); rotation(in.readFloat()); visible(in.readByte() != 0); - alpha(in.readFloat()); + alpha(in.readFloat()); */ if (in.readByte() != 0) { // this means we have an icon String iconId = in.readString(); @@ -46,8 +47,8 @@ public RCTMGLPointAnnotationOptions getThis() { } @Override - public RCTMGLPointAnnotation.CustomView getMarker() { - return new RCTMGLPointAnnotation.CustomView(getAnnotationID(), this); + public RCTMGLPointAnnotation.CustomMarker getMarker() { + return new RCTMGLPointAnnotation.CustomMarker(getAnnotationID(), this); } @Override @@ -57,22 +58,29 @@ public int describeContents() { @Override public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(getPosition(), flags); - out.writeString(getSnippet()); - out.writeString(getTitle()); + out.writeParcelable(position, flags); + out.writeString(snippet); + out.writeString(title); + /* out.writeByte((byte) (isFlat() ? 1 : 0)); + */ + /* out.writeFloat(getAnchorU()); out.writeFloat(getAnchorV()); + out.writeFloat(getInfoWindowAnchorU()); out.writeFloat(getInfoWindowAnchorV()); out.writeFloat(getRotation()); + */ +/* out.writeByte((byte) (isVisible() ? 1 : 0)); out.writeFloat(alpha); - Icon icon = getIcon(); + */ + Icon icon = this.icon; out.writeByte((byte) (icon != null ? 1 : 0)); if (icon != null) { - out.writeString(getIcon().getId()); - out.writeParcelable(getIcon().getBitmap(), flags); + out.writeString(this.icon.getId()); + out.writeParcelable(this.icon.getBitmap(), flags); } out.writeString(getAnnotationID()); out.writeByte((byte) (getHasChildren() ? 1 : 0)); @@ -96,6 +104,11 @@ public boolean getHasChildren() { return mHasChildren; } + public RCTMGLPointAnnotationOptions anchor(float x, float y) { + // TODO + return this; + } + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java index c60da0c045..d6fde76f9c 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraStop.java @@ -5,6 +5,8 @@ import android.util.DisplayMetrics; import com.facebook.react.bridge.ReadableMap; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; @@ -13,8 +15,6 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.rctmgl.components.camera.constants.CameraMode; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; /** * Created by nickitaliano on 9/5/17. @@ -74,11 +74,11 @@ public void setMode(@CameraMode.Mode int mode) { mMode = mode; } - public CameraUpdateItem toCameraUpdate() { + public CameraUpdateItem toCameraUpdate(MapboxMap map) { if (mBounds != null) { CameraUpdate update = CameraUpdateFactory.newLatLngBounds(mBounds, mBoundsPaddingLeft, mBooundsPaddingTop, mBoundsPaddingRight, mBoundsPaddingBottom); - return new CameraUpdateItem(update, mDuration, mCallback, CameraMode.FLIGHT); + return new CameraUpdateItem(map, update, mDuration, mCallback, CameraMode.FLIGHT); } CameraPosition.Builder builder = new CameraPosition.Builder(); @@ -99,7 +99,7 @@ public CameraUpdateItem toCameraUpdate() { builder.target(mLatLng); } - return new CameraUpdateItem(CameraUpdateFactory.newCameraPosition(builder.build()), mDuration, mCallback, mMode); + return new CameraUpdateItem(map, CameraUpdateFactory.newCameraPosition(builder.build()), mDuration, mCallback, mMode); } public static CameraStop fromReadableMap(Context context, @NonNull ReadableMap readableMap, MapboxMap.CancelableCallback callback) { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java index 2d8d5f4dd4..0fb250f3db 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateItem.java @@ -1,48 +1,69 @@ package com.mapbox.rctmgl.components.camera; +import android.support.annotation.NonNull; + import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.rctmgl.components.camera.constants.CameraMode; +import java.lang.ref.WeakReference; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + /** * Created by nickitaliano on 9/5/17. */ -public class CameraUpdateItem { +public class CameraUpdateItem implements RunnableFuture { private int mDuration; private MapboxMap.CancelableCallback mCallback; private CameraUpdate mCameraUpdate; private int mCameraMode; - public interface OnCameraCompleteListener { - void onComplete(); - } + private boolean isCameraActionFinished; + private boolean isCameraActionCancelled; - public CameraUpdateItem(CameraUpdate update, int duration, MapboxMap.CancelableCallback callback, @CameraMode.Mode int cameraMode) { + private WeakReference mMap; + + public CameraUpdateItem(MapboxMap map, CameraUpdate update, int duration, MapboxMap.CancelableCallback callback, @CameraMode.Mode int cameraMode) { mCameraUpdate = update; mDuration = duration; mCallback = callback; mCameraMode = cameraMode; + mMap = new WeakReference<>(map); } public int getDuration() { return mDuration; } - public void execute(MapboxMap map, final OnCameraCompleteListener listener) { + @Override + public void run() { final MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { @Override public void onCancel() { - handleCallbackResponse(listener, true); + handleCallbackResponse(true); } @Override public void onFinish() { - handleCallbackResponse(listener, false); + handleCallbackResponse(false); } }; - if (mCameraMode == CameraMode.FLIGHT) { + MapboxMap map = mMap.get(); + if (map == null) { + isCameraActionCancelled = true; + return; + } + + if (mCameraMode == CameraMode.FLIGHT && mDuration > 0) { map.animateCamera(mCameraUpdate, mDuration, callback); } else if (mCameraMode == CameraMode.EASE) { map.easeCamera(mCameraUpdate, mDuration, callback); @@ -51,13 +72,39 @@ public void onFinish() { } } - private void handleCallbackResponse(OnCameraCompleteListener listener, boolean isCancel) { - listener.onComplete(); + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + @Override + public boolean isCancelled() { + return isCameraActionCancelled; + } + + @Override + public boolean isDone() { + return isCameraActionFinished; + } + + @Override + public Void get() throws InterruptedException, ExecutionException { + return null; + } + + @Override + public Void get(long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return null; + } + + private void handleCallbackResponse(boolean isCancel) { if (mCallback == null) { return; } + isCameraActionCancelled = isCancel; + isCameraActionFinished = !isCancel; + if (isCancel) { mCallback.onCancel(); } else { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateQueue.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateQueue.java index 0d9055a9d6..e9799ac169 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateQueue.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/CameraUpdateQueue.java @@ -2,9 +2,14 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Queue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; /** * Created by nickitaliano on 9/5/17. @@ -12,6 +17,7 @@ public class CameraUpdateQueue { private Queue mQueue; + private OnCompleteAllListener mCompleteListener; public interface OnCompleteAllListener { @@ -35,16 +41,17 @@ public boolean isEmpty() { } public void flush() { - while (!mQueue.isEmpty()) { + while (mQueue.size() > 0) { mQueue.remove(); } + mQueue = new LinkedList<>(); } public void setOnCompleteAllListener(OnCompleteAllListener listener) { mCompleteListener = listener; } - public void execute(final MapboxMap map) { + public void execute(MapboxMap map) { if (mQueue.isEmpty()) { if (mCompleteListener != null) { mCompleteListener.onCompleteAll(); @@ -57,12 +64,8 @@ public void execute(final MapboxMap map) { return; } - final CameraUpdateItem item = stop.toCameraUpdate(); - item.execute(map, new CameraUpdateItem.OnCameraCompleteListener() { - @Override - public void onComplete() { - execute(map); - } - }); + CameraUpdateItem item = stop.toCameraUpdate(map); + item.run(); + execute(map); } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java new file mode 100644 index 0000000000..aa56cd2fcb --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCamera.java @@ -0,0 +1,464 @@ +package com.mapbox.rctmgl.components.camera; + +import android.content.Context; +import android.location.Location; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdate; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.VisibleRegion; +import com.mapbox.mapboxsdk.location.modes.CameraMode; +import com.mapbox.mapboxsdk.location.modes.RenderMode; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.location.LocationComponent; +import com.mapbox.mapboxsdk.location.LocationComponentOptions; +import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions; +// import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.rctmgl.components.AbstractMapFeature; +import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; +import com.mapbox.rctmgl.events.IEvent; +import com.mapbox.rctmgl.events.MapUserTrackingModeEvent; +import com.mapbox.rctmgl.events.MapChangeEvent; +import com.mapbox.rctmgl.location.LocationManager; +import com.mapbox.rctmgl.location.UserLocation; +import com.mapbox.rctmgl.location.UserLocationLayerConstants; +import com.mapbox.rctmgl.location.UserLocationVerticalAlignment; +import com.mapbox.rctmgl.location.UserTrackingMode; +import com.mapbox.rctmgl.location.UserTrackingState; +import com.mapbox.rctmgl.utils.GeoJSONUtils; + +import com.mapbox.mapboxsdk.R; + +import com.mapbox.rctmgl.events.constants.EventTypes; + +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; + +import com.mapbox.geojson.Point; + +import com.mapbox.android.core.permissions.PermissionsManager; + +import android.support.annotation.NonNull; + +public class RCTMGLCamera extends AbstractMapFeature { + private RCTMGLCameraManager mManager; + private RCTMGLMapView mMapView; + + private boolean hasSentFirstRegion = false; + + private CameraStop mCameraStop; + private CameraUpdateQueue mCameraUpdateQueue; + + private LocationComponent mLocationComponent; + + private int mUserTrackingMode; + private int mUserTrackingState = UserTrackingState.POSSIBLE; + private int mUserLocationVerticalAlignment = UserLocationVerticalAlignment.CENTER; + + public static final int USER_LOCATION_CAMERA_MOVE_DURATION = 1000; + + private LocationManager mLocationManager; + private UserLocation mUserLocation; + private boolean mShowUserLocation; + + private Point mCenterCoordinate; + + private boolean mAnimated; + private double mHeading; + private double mPitch; + private double mZoomLevel; + + private boolean mFollowUserLocation; + private String mFollowUserMode; + + private Context mContext; + + + private LocationManager.OnUserLocationChange mLocationChangeListener = new LocationManager.OnUserLocationChange() { + @Override + public void onLocationChange(Location nextLocation) { + if (getMapboxMap() == null || mLocationComponent == null || !mShowUserLocation) { + return; + } + + float distToNextLocation = mUserLocation.getDistance(nextLocation); + mLocationComponent.forceLocationUpdate(nextLocation); // FMTODO - use builtin location tracking. + mUserLocation.setCurrentLocation(nextLocation); + + if (mUserTrackingState == UserTrackingState.POSSIBLE || distToNextLocation > 0.0f) { + updateUserLocation(true); + } + sendUserLocationUpdateEvent(nextLocation); + } + }; + + private MapboxMap.CancelableCallback mCameraCallback = new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + if (!hasSentFirstRegion) { + mMapView.sendRegionChangeEvent(false); + hasSentFirstRegion = true; + } + } + + @Override + public void onFinish() { + if (!hasSentFirstRegion) { + mMapView.sendRegionChangeEvent(false); + hasSentFirstRegion = true; + } + } + }; + + public RCTMGLCamera(Context context, RCTMGLCameraManager manager) { + super(context); + mContext = context; + mManager = manager; + mCameraUpdateQueue = new CameraUpdateQueue(); + + mUserLocation = new UserLocation(); + mLocationManager = LocationManager.getInstance(context); + } + + @Override + public void addToMap(RCTMGLMapView mapView) { + mMapView = mapView; + + if (mCameraStop != null) { + updateCamera(); + } + + if (mShowUserLocation) { + enableLocation(); + } + } + + @Override + public void removeFromMap(RCTMGLMapView mapView) { + + } + + public void setStop(CameraStop stop) { + mCameraStop = stop; + mCameraStop.setCallback(mCameraCallback); + + if (mMapView != null) { + updateCamera(); + } + } + + private void updateCamera() { + mCameraUpdateQueue.offer(mCameraStop); + mCameraUpdateQueue.execute(mMapView.getMapboxMap()); + } + + private void updateUserTrackingMode(int userTrackingMode) { + mUserLocation.setTrackingMode(userTrackingMode); + IEvent event = new MapUserTrackingModeEvent(this, userTrackingMode); + mManager.handleEvent(event); + } + + private void updateUserLocation(boolean isAnimated) { + if (!mShowUserLocation || mUserLocation.getTrackingMode() == UserTrackingMode.NONE) { + return; + } + + if (mUserTrackingState == UserTrackingState.POSSIBLE) { + updateUserLocationSignificantly(isAnimated); + } else if (mUserTrackingState == UserTrackingState.CHANGED) { + updateUserLocationIncrementally(isAnimated); + } + } + + private CameraPosition getUserLocationUpdateCameraPosition(double zoomLevel) { + LatLng center = mUserLocation.getCoordinate(); + + if (mUserLocationVerticalAlignment != UserLocationVerticalAlignment.CENTER) { + VisibleRegion region = mMapView.getVisibleRegion(center, zoomLevel); + + switch (mUserLocationVerticalAlignment) { + case UserLocationVerticalAlignment.TOP: + center = new LatLng(region.nearRight.getLatitude(), center.getLongitude()); + break; + case UserLocationVerticalAlignment.BOTTOM: + center = new LatLng(region.farLeft.getLatitude(), center.getLongitude()); + break; + } + } + + return new CameraPosition.Builder() + .target(center) + .bearing(getDirectionForUserLocationUpdate()) + .tilt(mPitch) + .zoom(zoomLevel) + .build(); + } + + private double getDirectionForUserLocationUpdate() { + // NOTE: The direction of this is used for map rotation only, not location layer rotation + CameraPosition currentCamera = mMapView.getCameraPosition(); + double direction = currentCamera.bearing; + + int userTrackingMode = mUserLocation.getTrackingMode(); + if (userTrackingMode == UserTrackingMode.FollowWithHeading || userTrackingMode == UserTrackingMode.FollowWithCourse) { + direction = mUserLocation.getBearing(); + } else if (mHeading != 0.0) { + direction = mHeading; + } + + return direction; + } + + private void sendUserLocationUpdateEvent(Location location) { + if(location == null){ + return; + } + IEvent event = new MapChangeEvent(this, makeLocationChangePayload(location), EventTypes.USER_LOCATION_UPDATED); + mManager.handleEvent(event); + } + + private boolean hasSetCenterCoordinate() { + CameraPosition cameraPosition = mMapView.getCameraPosition(); + LatLng center = cameraPosition.target; + return center.getLatitude() != 0.0 && center.getLongitude() != 0.0; + } + + + private void updateUserLocationSignificantly(boolean isAnimated) { + mUserTrackingState = UserTrackingState.BEGAN; + + CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(getUserLocationUpdateCameraPosition(mZoomLevel)); + MapboxMap.CancelableCallback cameraCallback = new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + mUserTrackingState = UserTrackingState.CHANGED; + } + + @Override + public void onFinish() { + mUserTrackingState = UserTrackingState.CHANGED; + } + }; + + if (isAnimated && hasSetCenterCoordinate()) { + mMapView.animateCamera(cameraUpdate, cameraCallback); + } else { + mMapView.moveCamera(cameraUpdate, cameraCallback); + } + } + + private void updateUserLocationIncrementally(boolean isAnimated) { + mUserTrackingState = UserTrackingState.BEGAN; + + CameraPosition cameraPosition = mMapView.getCameraPosition(); + CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(getUserLocationUpdateCameraPosition(cameraPosition.zoom)); + + MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + mUserTrackingState = UserTrackingState.CHANGED; + } + + @Override + public void onFinish() { + mUserTrackingState = UserTrackingState.CHANGED; + } + }; + + if (isAnimated) { + mMapView.easeCamera(cameraUpdate, USER_LOCATION_CAMERA_MOVE_DURATION, callback); + } else { + mMapView.moveCamera(cameraUpdate, callback); + } + } + + public void setReactUserTrackingMode(int userTrackingMode) { + int oldTrackingMode = mUserTrackingMode; + mUserTrackingMode = userTrackingMode; + updateUserTrackingMode(userTrackingMode); + + switch (mUserTrackingMode) { + case UserTrackingMode.NONE: + mUserTrackingState = UserTrackingState.POSSIBLE; + break; + case UserTrackingMode.FOLLOW: + case UserTrackingMode.FollowWithCourse: + case UserTrackingMode.FollowWithHeading: + if (oldTrackingMode == UserTrackingMode.NONE) { + mUserTrackingState = UserTrackingState.POSSIBLE; + } + mShowUserLocation = true; + break; + + } + + if (mMapView != null) { + updateUserLocation(false); + updateLocationLayer(mMapView.getMapboxMap().getStyle()); + } + } + + private void enableLocation() { + if (!PermissionsManager.areLocationPermissionsGranted(mContext)) { + return; + } + + if (!mLocationManager.isActive()) { + mLocationManager.enable(); + } + + mMapView.getMapboxMap().getStyle(new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + enableLocationComponent(style); + } + }); + } + + private void enableLocationComponent(@NonNull Style style) { + updateLocationLayer(style); + + Location lastKnownLocation = mLocationManager.getLastKnownLocation(); + mLocationManager.addLocationListener(mLocationChangeListener); + + if (lastKnownLocation != null) { + mLocationChangeListener.onLocationChange(lastKnownLocation); + + postDelayed(new Runnable() { + @Override + public void run() { + mMapView.sendRegionDidChangeEvent(); + } + }, 200); + } + + } + + private void updateLocationLayer(@NonNull Style style) { + if (mLocationComponent == null) { + mLocationComponent = getMapboxMap().getLocationComponent(); + } + + LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(mContext) + .build(); + + LocationComponentActivationOptions locationComponentActivationOptions = LocationComponentActivationOptions + .builder(mContext, style) + .locationComponentOptions(locationComponentOptions) + .build(); + mLocationComponent.activateLocationComponent(locationComponentActivationOptions); + mLocationComponent.setLocationEngine(mLocationManager.getEngine()); + + int userLayerMode = UserTrackingMode.getMapLayerMode(mUserLocation.getTrackingMode(), mShowUserLocation); + mLocationComponent.setLocationComponentEnabled(userLayerMode != -1); + + if (userLayerMode != -1) { + mLocationComponent.setRenderMode(userLayerMode); + } + } + + public void setZoomLevel(double zoomLevel) { + mZoomLevel = zoomLevel; + updateCameraPositionIfNeeded(false); + } + + private CameraPosition buildCamera(CameraPosition previousPosition, boolean shouldUpdateTarget) { + CameraPosition.Builder builder = new CameraPosition.Builder(previousPosition) + .bearing(mHeading) + .tilt(mPitch) + .zoom(mZoomLevel); + + if (shouldUpdateTarget) { + builder.target(GeoJSONUtils.toLatLng(mCenterCoordinate)); + } + + return builder.build(); + } + + private void updateCameraPositionIfNeeded(boolean shouldUpdateTarget) { + if (mMapView != null) { + CameraPosition prevPosition = mMapView.getCameraPosition(); + CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(buildCamera(prevPosition, shouldUpdateTarget)); + + if (mAnimated) { + mMapView.easeCamera(cameraUpdate); + } else { + mMapView.moveCamera(cameraUpdate); + } + } + } + + public void setUserTrackingMode(int userTrackingMode) { + int oldTrackingMode = mUserTrackingMode; + mUserTrackingMode = userTrackingMode; + updateUserTrackingMode(userTrackingMode); + + switch (mUserTrackingMode) { + case UserTrackingMode.NONE: + mUserTrackingState = UserTrackingState.POSSIBLE; + break; + case UserTrackingMode.FOLLOW: + case UserTrackingMode.FollowWithCourse: + case UserTrackingMode.FollowWithHeading: + if (oldTrackingMode == UserTrackingMode.NONE) { + mUserTrackingState = UserTrackingState.POSSIBLE; + } + mShowUserLocation = true; + break; + + } + + if (getMapboxMap() != null) { + updateUserLocation(false); + updateLocationLayer(getMapboxMap().getStyle()); + } + } + + + public void setFollowUserLocation(boolean value) { + mFollowUserLocation = value; + if (value) { + setUserTrackingMode(UserTrackingMode.FOLLOW); + } else { + setUserTrackingMode(UserTrackingMode.NONE); + } + } + + public void setFollowUserMode(String mode) { + mFollowUserMode = mode; + + } + + MapboxMap getMapboxMap() { + if (mMapView == null) { + return null; + } + return mMapView.getMapboxMap(); + } + + /** + * Create a payload of the location data per the web api geolocation spec + * https://dev.w3.org/geo/api/spec-source.html#position + * @return + */ + private WritableMap makeLocationChangePayload(Location location) { + WritableMap positionProperties = new WritableNativeMap(); + WritableMap coords = new WritableNativeMap(); + + coords.putDouble("longitude", location.getLongitude()); + coords.putDouble("latitude", location.getLatitude()); + coords.putDouble("altitude", location.getAltitude()); + coords.putDouble("accuracy", location.getAccuracy()); + coords.putDouble("heading", location.getBearing()); + coords.putDouble("speed", location.getSpeed()); + + positionProperties.putMap("coords", coords); + positionProperties.putDouble("timestamp", location.getTime()); + return positionProperties; + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCameraManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCameraManager.java new file mode 100644 index 0000000000..6de75d0e29 --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/camera/RCTMGLCameraManager.java @@ -0,0 +1,65 @@ +package com.mapbox.rctmgl.components.camera; + +import com.facebook.common.logging.FLog; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.mapbox.rctmgl.components.AbstractEventEmitter; + +import java.util.HashMap; +import java.util.Map; + +public class RCTMGLCameraManager extends AbstractEventEmitter { + public static final String REACT_CLASS = RCTMGLCamera.class.getSimpleName(); + + private ReactApplicationContext mContext; + + public RCTMGLCameraManager(ReactApplicationContext context) { + super(context); + mContext = context; + } + + @Override + public Map customEvents() { + return new HashMap<>(); + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + protected RCTMGLCamera createViewInstance(ThemedReactContext reactContext) { + return new RCTMGLCamera(reactContext, this); + } + + @ReactProp(name="stop") + public void setStop(RCTMGLCamera camera, ReadableMap map) { + if (map != null) { + CameraStop stop = CameraStop.fromReadableMap(mContext, map, null); + camera.setStop(stop); + } + } + + @ReactProp(name="userTrackingMode") + public void setUserTrackingMode(RCTMGLCamera camera, int userTrackingMode) { + camera.setUserTrackingMode(userTrackingMode); + } + + @ReactProp(name="followZoomLevel") + public void setZoomLevel(RCTMGLCamera camera, double zoomLevel) { + camera.setZoomLevel(zoomLevel); + } + + @ReactProp(name="followUserLocation") + public void setFollowUserLocation(RCTMGLCamera camera, boolean value) { + camera.setFollowUserLocation(value); + } + + @ReactProp(name="followUserMode") + public void setFollowUserMode(RCTMGLCamera camera, String value) { + camera.setFollowUserMode(value); + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java index 046cf62680..b5c03dd84b 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.java @@ -21,9 +21,14 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; +import com.mapbox.android.core.permissions.PermissionsManager; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewManager; +import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.plugins.markerview.MarkerView; +import com.mapbox.mapboxsdk.plugins.markerview.MarkerViewManager; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; @@ -36,7 +41,8 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.maps.UiSettings; import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +// import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.PropertyFactory; @@ -47,6 +53,7 @@ import com.mapbox.rctmgl.components.annotation.RCTMGLPointAnnotationAdapter; import com.mapbox.rctmgl.components.camera.CameraStop; import com.mapbox.rctmgl.components.camera.CameraUpdateQueue; +import com.mapbox.rctmgl.components.camera.RCTMGLCamera; import com.mapbox.rctmgl.components.mapview.helpers.CameraChangeTracker; import com.mapbox.rctmgl.components.styles.light.RCTMGLLight; import com.mapbox.rctmgl.components.styles.sources.RCTSource; @@ -61,18 +68,11 @@ import com.mapbox.rctmgl.location.UserLocation; import com.mapbox.rctmgl.location.UserLocationLayerConstants; import com.mapbox.rctmgl.location.UserLocationVerticalAlignment; -import com.mapbox.rctmgl.location.UserTrackingMode; import com.mapbox.rctmgl.location.UserTrackingState; import com.mapbox.rctmgl.utils.BitmapUtils; -import com.mapbox.rctmgl.utils.FilterParser; import com.mapbox.rctmgl.utils.GeoJSONUtils; import com.mapbox.rctmgl.utils.GeoViewport; import com.mapbox.rctmgl.utils.SimpleEventCallback; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.models.Position; import java.util.ArrayList; import java.util.HashMap; @@ -83,6 +83,7 @@ import javax.annotation.Nullable; + /** * Created by nickitaliano on 8/18/17. */ @@ -90,11 +91,14 @@ @SuppressWarnings({"MissingPermission"}) public class RCTMGLMapView extends MapView implements OnMapReadyCallback, MapboxMap.OnMapClickListener, MapboxMap.OnMapLongClickListener, - MapView.OnMapChangedListener, MapboxMap.OnMarkerViewClickListener { + /* MapView.OnMapChangedListener*/ + MapView.OnCameraDidChangeListener, MapView.OnDidFailLoadingMapListener, + MapView.OnDidFinishLoadingMapListener, MapView.OnWillStartRenderingFrameListener, + MapView.OnDidFinishRenderingFrameListener, MapView.OnWillStartRenderingMapListener, + MapView.OnDidFinishRenderingMapListener, MapView.OnDidFinishLoadingStyleListener, + MapboxMap.OnMarkerClickListener { public static final String LOG_TAG = RCTMGLMapView.class.getSimpleName(); - public static final int USER_LOCATION_CAMERA_MOVE_DURATION = 1000; - private RCTMGLMapViewManager mManager; private Context mContext; private Handler mHandler; @@ -102,25 +106,19 @@ public class RCTMGLMapView extends MapView implements private boolean mPaused; private boolean mDestroyed; + private RCTMGLCamera mCamera; private List mFeatures; private List mQueuedFeatures; private Map mPointAnnotations; private Map mSources; - private CameraUpdateQueue mCameraUpdateQueue; private CameraChangeTracker mCameraChangeTracker = new CameraChangeTracker(); private Map mPreRenderMethodMap = new HashMap<>(); private MapboxMap mMap; - private LocationManager mLocationManger; - private UserLocation mUserLocation; - - private LocationLayerPlugin mLocationLayer; - private LocalizationPlugin mLocalizationPlugin; private String mStyleURL; - private boolean mAnimated; private boolean mLocalizeLabels; private Boolean mScrollEnabled; private Boolean mPitchEnabled; @@ -129,44 +127,15 @@ public class RCTMGLMapView extends MapView implements private Boolean mLogoEnabled; private Boolean mCompassEnabled; private Boolean mZoomEnabled; - private boolean mShowUserLocation; - - private long mActiveMarkerID = -1; - private int mUserTrackingMode; - private int mUserTrackingState = UserTrackingState.POSSIBLE; - private int mUserLocationVerticalAlignment = UserLocationVerticalAlignment.CENTER; - private double mHeading; - private double mPitch; - private double mZoomLevel; + private MarkerViewManager markerViewManager; - private Double mMinZoomLevel; - private Double mMaxZoomLevel; + private long mActiveMarkerID = -1; private ReadableArray mInsets; - private Point mCenterCoordinate; - private LatLngBounds mVisibleCoordinateBounds; private HashSet mHandledMapChangedEvents = null; - private LocationManager.OnUserLocationChange mLocationChangeListener = new LocationManager.OnUserLocationChange() { - @Override - public void onLocationChange(Location nextLocation) { - if (mMap == null || mLocationLayer == null || !mShowUserLocation) { - return; - } - - float distToNextLocation = mUserLocation.getDistance(nextLocation); - mLocationLayer.onLocationChanged(nextLocation); - mUserLocation.setCurrentLocation(nextLocation); - - if (mUserTrackingState == UserTrackingState.POSSIBLE || distToNextLocation > 0.0f) { - updateUserLocation(true); - } - sendUserLocationUpdateEvent(nextLocation); - } - }; - public RCTMGLMapView(Context context, RCTMGLMapViewManager manager, MapboxMapOptions options) { super(context, options); @@ -178,11 +147,6 @@ public RCTMGLMapView(Context context, RCTMGLMapViewManager manager, MapboxMapOpt getMapAsync(this); mManager = manager; - mCameraUpdateQueue = new CameraUpdateQueue(); - - mUserLocation = new UserLocation(); - mLocationManger = new LocationManager(context); - mLocationManger.setOnLocationChangeListener(mLocationChangeListener); mSources = new HashMap<>(); mPointAnnotations = new HashMap<>(); @@ -193,7 +157,16 @@ public RCTMGLMapView(Context context, RCTMGLMapViewManager manager, MapboxMapOpt setLifecycleListeners(); - addOnMapChangedListener(this); +// addOnMapChangedListener(this); + addOnCameraDidChangeListener(this); + addOnDidFailLoadingMapListener(this); + addOnDidFinishLoadingMapListener(this); + + addOnWillStartRenderingFrameListener(this); + addOnDidFinishRenderingFrameListener(this); + addOnWillStartRenderingMapListener(this); + addOnDidFinishRenderingMapListener(this); + addOnDidFinishLoadingStyleListener(this); } @Override @@ -214,20 +187,6 @@ public void onDestroy() { mDestroyed = true; } - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - if (mLocationLayer == null) { - return; - } - if (hasWindowFocus) { - mLocationLayer.onStart(); - } else { - mLocationLayer.onStop(); - } - - } - public void enqueuePreRenderMapMethod(Integer methodID, @Nullable ReadableArray args) { mPreRenderMethodMap.put(methodID, args); } @@ -245,6 +204,10 @@ public void addFeature(View childView, int childPosition) { RCTMGLPointAnnotation annotation = (RCTMGLPointAnnotation) childView; mPointAnnotations.put(annotation.getID(), annotation); feature = (AbstractMapFeature) childView; + } else if (childView instanceof RCTMGLCamera) { + RCTMGLCamera camera = (RCTMGLCamera) childView; + mCamera = camera; + feature = (AbstractMapFeature) childView; } else { ViewGroup children = (ViewGroup) childView; @@ -303,12 +266,6 @@ public synchronized void dispose() { ReactContext reactContext = (ReactContext) mContext; reactContext.removeLifecycleEventListener(mLifeCycleListener); - if(mLocationLayer != null){ - mLocationLayer.onStop(); - } - - mLocationManger.dispose(); - if (!mPaused) { onPause(); } @@ -317,6 +274,42 @@ public synchronized void dispose() { onDestroy(); } + public VisibleRegion getVisibleRegion(LatLng center, double zoomLevel) { + DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); + int[] contentPadding = mMap.getPadding(); + + // we want to get the width, and height scaled based on pixel density, that also includes content padding + // (width * percentOfWidthWeWant - (leftPadding + rightPadding)) / dpi + int mapWidth = (int)((mMap.getWidth() * 0.75 - (contentPadding[0] + contentPadding[2])) / metrics.scaledDensity); + int mapHeight = (int)((mMap.getHeight() * 0.75 - (contentPadding[1] + contentPadding[3])) / metrics.scaledDensity); + VisibleRegion region = GeoViewport.getRegion(center, (int) zoomLevel, mapWidth, mapHeight); + return region; + } + + public CameraPosition getCameraPosition() { + return mMap.getCameraPosition(); + } + + public void animateCamera(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) { + mMap.animateCamera(cameraUpdate, callback); + } + + public void moveCamera(CameraUpdate cameraUpdate, MapboxMap.CancelableCallback callback) { + mMap.moveCamera(cameraUpdate, callback); + } + + public void moveCamera(CameraUpdate cameraUpdate) { + mMap.moveCamera(cameraUpdate); + } + + public void easeCamera(CameraUpdate cameraUpdate, int duration, MapboxMap.CancelableCallback callback) { + mMap.easeCamera(cameraUpdate, duration, callback); + } + + public void easeCamera(CameraUpdate cameraUpdate) { + mMap.easeCamera(cameraUpdate); + } + public RCTMGLPointAnnotation getPointAnnotationByID(String annotationID) { if (annotationID == null) { return null; @@ -355,45 +348,24 @@ public MapboxMap getMapboxMap() { public void onMapReady(final MapboxMap mapboxMap) { mMap = mapboxMap; + mMap.setStyle(new Style.Builder().fromUrl(mStyleURL)); + reflow(); // the internal widgets(compass, attribution, etc) need this to position themselves correctly - final MarkerViewManager markerViewManager = mMap.getMarkerViewManager(); - markerViewManager.addMarkerViewAdapter(new RCTMGLPointAnnotationAdapter(this, mContext)); - markerViewManager.setOnMarkerViewClickListener(this); + mMap.setOnMarkerClickListener(this); + + markerViewManager = new MarkerViewManager(this, mMap); /* mMap.getMarkerViewManager(); */ + // FMTODO markerViewManager.addMarker(new RCTMGLPointAnnotationAdapter(this, mContext)); + // FMTODO markerViewManager.addMarkerViewAdapter(new RCTMGLPointAnnotationAdapter(this, mContext)); + // FMTODO markerViewManager.setOnMarkerViewClickListener(this); mMap.setInfoWindowAdapter(new RCTMGLCalloutAdapter(this)); - mMap.setOnMapClickListener(this); - mMap.setOnMapLongClickListener(this); + mMap.addOnMapClickListener(this); + mMap.addOnMapLongClickListener(this); // in case props were set before the map was ready lets set them updateInsets(); updateUISettings(); - setMinMaxZoomLevels(); - - if (mShowUserLocation) { - enableLocation(); - } - - // extract target centerCoordinate / zoomLevel from mVisibleCoordinateBounds - updateCenterCoordinateIfNeeded(); - - if (mCenterCoordinate != null && mUserTrackingMode == UserTrackingMode.NONE) { - mMap.moveCamera(CameraUpdateFactory.newCameraPosition(buildCamera()), new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - sendRegionDidChangeEvent(); - } - - @Override - public void onFinish() { - sendRegionDidChangeEvent(); - } - }); - } - - if (!mCameraUpdateQueue.isEmpty()) { - mCameraUpdateQueue.execute(mMap); - } if (mQueuedFeatures.size() > 0) { for (int i = 0; i < mQueuedFeatures.size(); i++) { @@ -404,16 +376,18 @@ public void onFinish() { mQueuedFeatures = null; } + /* FMTODO if (mPointAnnotations.size() > 0) { markerViewManager.invalidateViewMarkersInVisibleRegion(); - } + } */ mMap.addOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() { @Override public void onCameraIdle() { + /* FMTODO if (mPointAnnotations.size() > 0) { markerViewManager.invalidateViewMarkersInVisibleRegion(); - } + } */ // if we have onCameraIdle during mCameraChangeTracker.isAnimating() // it's a 'fling animation' after user gesture @@ -459,49 +433,7 @@ public void onCameraMoveStarted(int reason) { } }); - mMap.setOnScrollListener(new MapboxMap.OnScrollListener() { - @Override - public void onScroll() { - if (mUserLocation.getTrackingMode() != UserTrackingMode.NONE) { - updateUserTrackingMode(UserTrackingMode.NONE); - } - } - }); - - mMap.setOnFlingListener(new MapboxMap.OnFlingListener() { - @Override - public void onFling() { - if (mUserLocation.getTrackingMode() != UserTrackingMode.NONE) { - updateUserTrackingMode(UserTrackingMode.NONE); - } - } - }); - - mMap.addOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() { - double lastMapRotation = getMapRotation(); - - @Override - public void onCameraMove() { - handleMapChangedEvent(EventTypes.REGION_IS_CHANGING); - - int userTrackingMode = mUserLocation.getTrackingMode(); - boolean isFollowWithCourseOrHeading = userTrackingMode == UserTrackingMode.FollowWithCourse || userTrackingMode == UserTrackingMode.FollowWithHeading; - - if (!isFollowWithCourseOrHeading) { - lastMapRotation = getRotation(); - return; - } - - double currentMapRotation = getMapRotation(); - if (lastMapRotation != currentMapRotation && mCameraChangeTracker.isUserInteraction()) { - updateUserTrackingMode(UserTrackingMode.FOLLOW); - } - - lastMapRotation = currentMapRotation; - } - }); - - mLocalizationPlugin = new LocalizationPlugin(this, mMap); + /*mLocalizationPlugin = new LocalizationPlugin(this, mMap); if (mLocalizeLabels) { try { mLocalizationPlugin.matchMapLanguageWithDeviceDefault(); @@ -509,7 +441,7 @@ public void onCameraMove() { final String localeString = Locale.getDefault().toString(); Log.w(LOG_TAG, String.format("Could not find matching locale for %s", localeString)); } - } + }*/ } public void reflow() { @@ -543,7 +475,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } @Override - public void onMapClick(@NonNull LatLng point) { + public boolean onMapClick(@NonNull LatLng point) { boolean isEventCaptured = false; if (mActiveMarkerID != -1) { @@ -557,7 +489,7 @@ public void onMapClick(@NonNull LatLng point) { } if (isEventCaptured) { - return; + return true; } PointF screenPoint = mMap.getProjection().toScreenLocation(point); @@ -592,23 +524,25 @@ public void onMapClick(@NonNull LatLng point) { RCTSource source = getTouchableSourceWithHighestZIndex(hitTouchableSources); if (source != null && source.hasPressListener()) { source.onPress(hits.get(source.getID())); - return; + return true; } } MapClickEvent event = new MapClickEvent(this, point, screenPoint); mManager.handleEvent(event); + return false; } @Override - public void onMapLongClick(@NonNull LatLng point) { + public boolean onMapLongClick(@NonNull LatLng point) { PointF screenPoint = mMap.getProjection().toScreenLocation(point); MapClickEvent event = new MapClickEvent(this, point, screenPoint, EventTypes.MAP_LONG_CLICK); mManager.handleEvent(event); + return false; } @Override - public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) { + public boolean onMarkerClick(@NonNull Marker marker) { final long selectedMarkerID = marker.getId(); RCTMGLPointAnnotation activeAnnotation = null; @@ -640,27 +574,27 @@ public void selectAnnotation(RCTMGLPointAnnotation annotation) { final long id = annotation.getMapboxID(); if (id != mActiveMarkerID) { - final MarkerView markerView = annotation.getMarker(); - mMap.selectMarker(markerView); + final Marker marker = annotation.getMarker(); + mMap.selectMarker(marker); annotation.onSelect(true); mActiveMarkerID = id; RCTMGLCallout calloutView = annotation.getCalloutView(); - if (!markerView.isInfoWindowShown() && calloutView != null) { - markerView.showInfoWindow(mMap, this); + if (!marker.isInfoWindowShown() && calloutView != null) { + marker.showInfoWindow(mMap, this); } } } public boolean deselectAnnotation(RCTMGLPointAnnotation annotation) { - MarkerView markerView = annotation.getMarker(); + Marker marker = annotation.getMarker(); RCTMGLCallout calloutView = annotation.getCalloutView(); if (calloutView != null) { - markerView.hideInfoWindow(); + marker.hideInfoWindow(); } - mMap.deselectMarker(markerView); + mMap.deselectMarker(marker); mActiveMarkerID = -1; annotation.onDeselect(); @@ -668,6 +602,61 @@ public boolean deselectAnnotation(RCTMGLPointAnnotation annotation) { } @Override + public void onCameraDidChange(boolean animated) { + mCameraChangeTracker.setIsAnimating(animated); + } + + @Override + public void onDidFailLoadingMap(String errorMessage) { + handleMapChangedEvent(EventTypes.DID_FAIL_LOADING_MAP); + } + + @Override + public void onDidFinishLoadingMap() { + handleMapChangedEvent(EventTypes.DID_FINISH_LOADING_MAP); + } + + @Override + public void onWillStartRenderingFrame() { + handleMapChangedEvent(EventTypes.WILL_START_RENDERING_FRAME); + } + + @Override + public void onDidFinishRenderingFrame(boolean fully) { + if (fully) { + handleMapChangedEvent(EventTypes.DID_FINISH_RENDERING_FRAME_FULLY); + } else { + handleMapChangedEvent(EventTypes.DID_FINISH_RENDERING_FRAME); + } + } + + @Override + public void onWillStartRenderingMap() { + handleMapChangedEvent(EventTypes.WILL_START_RENDERING_MAP); + } + + @Override + public void onDidFinishRenderingMap(boolean fully) { + if (fully) { + if (mPreRenderMethodMap.size() > 0) { + for (Integer methodID : mPreRenderMethodMap.keySet()) { + mManager.receiveCommand(this, methodID, mPreRenderMethodMap.get(methodID)); + } + mPreRenderMethodMap.clear(); + } + handleMapChangedEvent(EventTypes.DID_FINISH_RENDERING_MAP_FULLY); + } else { + handleMapChangedEvent(EventTypes.DID_FINISH_RENDERING_MAP); + } + } + + @Override + public void onDidFinishLoadingStyle() { + handleMapChangedEvent(EventTypes.DID_FINISH_LOADING_STYLE); + } + + + /* public void onMapChanged(int changed) { String eventType = null; @@ -677,36 +666,36 @@ public void onMapChanged(int changed) { case REGION_DID_CHANGE: break; case REGION_WILL_CHANGE_ANIMATED: - mCameraChangeTracker.setIsAnimating(true); + mCameraChangeTracker.setIsAnimating(true); //* break; case REGION_DID_CHANGE_ANIMATED: - mCameraChangeTracker.setIsAnimating(false); + mCameraChangeTracker.setIsAnimating(false); //* break; case WILL_START_LOADING_MAP: eventType = EventTypes.WILL_START_LOADING_MAP; break; case DID_FAIL_LOADING_MAP: - eventType = EventTypes.DID_FAIL_LOADING_MAP; + eventType = EventTypes.DID_FAIL_LOADING_MAP; //* break; case DID_FINISH_LOADING_MAP: - eventType = EventTypes.DID_FINISH_LOADING_MAP; + eventType = EventTypes.DID_FINISH_LOADING_MAP; //* break; case WILL_START_RENDERING_FRAME: - eventType = EventTypes.WILL_START_RENDERING_FRAME; + eventType = EventTypes.WILL_START_RENDERING_FRAME; //* break; case DID_FINISH_RENDERING_FRAME: - eventType = EventTypes.DID_FINISH_RENDERING_FRAME; + eventType = EventTypes.DID_FINISH_RENDERING_FRAME; //* break; case DID_FINISH_RENDERING_FRAME_FULLY_RENDERED: - eventType = EventTypes.DID_FINISH_RENDERING_FRAME_FULLY; + eventType = EventTypes.DID_FINISH_RENDERING_FRAME_FULLY; //* break; case WILL_START_RENDERING_MAP: - eventType = EventTypes.WILL_START_RENDERING_MAP; + eventType = EventTypes.WILL_START_RENDERING_MAP; // * break; case DID_FINISH_RENDERING_MAP: - eventType = EventTypes.DID_FINISH_RENDERING_MAP; + eventType = EventTypes.DID_FINISH_RENDERING_MAP; // * break; - case DID_FINISH_RENDERING_MAP_FULLY_RENDERED: + case DID_FINISH_RENDERING_MAP_FULLY_RENDERED: // * FMTODO no equivalent if (mPreRenderMethodMap.size() > 0) { for (Integer methodID : mPreRenderMethodMap.keySet()) { mManager.receiveCommand(this, methodID, mPreRenderMethodMap.get(methodID)); @@ -716,14 +705,14 @@ public void onMapChanged(int changed) { eventType = EventTypes.DID_FINISH_RENDERING_MAP_FULLY; break; case DID_FINISH_LOADING_STYLE: - eventType = EventTypes.DID_FINISH_LOADING_STYLE; + eventType = EventTypes.DID_FINISH_LOADING_STYLE; //* break; } if (eventType != null) { handleMapChangedEvent(eventType); } - } + } */ //endregion @@ -735,20 +724,15 @@ public void setReactStyleURL(String styleURL) { if (mMap != null) { removeAllSourcesFromMap(); - mMap.setStyle(styleURL, new MapboxMap.OnStyleLoadedListener() { + mMap.setStyle(styleURL, new Style.OnStyleLoaded() { @Override - public void onStyleLoaded(String style) { + public void onStyleLoaded(@NonNull Style style) { addAllSourcesToMap(); } }); } } - public void setReactAnimated(boolean animated) { - mAnimated = animated; - updateCameraPositionIfNeeded(false); - } - public void setReactContentInset(ReadableArray array) { mInsets = array; updateInsets(); @@ -793,151 +777,12 @@ public void setReactAttributionEnabled(boolean attributionEnabled) { updateUISettings(); } - public void setReactHeading(double heading) { - mHeading = heading; - updateCameraPositionIfNeeded(false); - } - - public void setReactPitch(double pitch) { - mPitch = pitch; - updateCameraPositionIfNeeded(false); - } - - public void setReactZoomLevel(double zoomLevel) { - mZoomLevel = zoomLevel; - updateCameraPositionIfNeeded(false); - } - - public void setReactMinZoomLevel(double minZoomLevel) { - mMinZoomLevel = minZoomLevel; - setMinMaxZoomLevels(); - } - - public void setReactMaxZoomLevel(double maxZoomLevel) { - mMaxZoomLevel = maxZoomLevel; - setMinMaxZoomLevels(); - } - - public void setReactCenterCoordinate(Point centerCoordinate) { - mCenterCoordinate = centerCoordinate; - updateCameraPositionIfNeeded(true); - } - - public void setReactVisibleCoordinateBounds(LatLngBounds visibleCoordinateBounds) { - mVisibleCoordinateBounds = visibleCoordinateBounds; - updateCenterCoordinateIfNeeded(); - updateCameraPositionIfNeeded(true); - } - - public void setReactShowUserLocation(boolean showUserLocation) { - mShowUserLocation = showUserLocation; - - if (mMap != null) { - if (mLocationManger.isActive() && !mShowUserLocation) { - mLocationManger.disable(); - - if (mLocationLayer != null) { - int trackingMode = mUserLocation.getTrackingMode(); - - if (trackingMode != UserTrackingMode.NONE) { - mUserLocation.setTrackingMode(UserTrackingMode.NONE); - updateUserTrackingMode(UserTrackingMode.NONE); - } - - updateLocationLayer(); - } - } else { - enableLocation(); - } - } - } - - public void setReactUserTrackingMode(int userTrackingMode) { - int oldTrackingMode = mUserTrackingMode; - mUserTrackingMode = userTrackingMode; - updateUserTrackingMode(userTrackingMode); - - switch (mUserTrackingMode) { - case UserTrackingMode.NONE: - mUserTrackingState = UserTrackingState.POSSIBLE; - break; - case UserTrackingMode.FOLLOW: - case UserTrackingMode.FollowWithCourse: - case UserTrackingMode.FollowWithHeading: - if (oldTrackingMode == UserTrackingMode.NONE) { - mUserTrackingState = UserTrackingState.POSSIBLE; - } - mShowUserLocation = true; - break; - - } - - if (mMap != null) { - updateUserLocation(false); - updateLocationLayer(); - } - } - - public void setReactUserLocationVerticalAlignment(int userLocationVerticalAlignment) { - mUserLocationVerticalAlignment = userLocationVerticalAlignment; - - if (mMap != null && mUserLocation.getTrackingMode() != UserTrackingMode.NONE) { - updateUserLocation(false); - } - } - //endregion //region Methods - - public void setCamera(String callbackID, ReadableMap args) { - IEvent event = new AndroidCallbackEvent(this, callbackID, EventKeys.MAP_ANDROID_CALLBACK); - final SimpleEventCallback callback = new SimpleEventCallback(mManager, event); - - // remove any current camera updates - mCameraUpdateQueue.flush(); - - if (args.hasKey("stops")) { - ReadableArray stops = args.getArray("stops"); - - for (int i = 0; i < stops.size(); i++) { - CameraStop stop = CameraStop.fromReadableMap(mContext, stops.getMap(i), null); - mCameraUpdateQueue.offer(stop); - } - - mCameraUpdateQueue.setOnCompleteAllListener(new CameraUpdateQueue.OnCompleteAllListener() { - @Override - public void onCompleteAll() { - callback.onFinish(); - mCameraChangeTracker.setReason(3); - } - }); - } else { - CameraStop stop = CameraStop.fromReadableMap(mContext, args, new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - callback.onCancel(); - mCameraChangeTracker.setReason(1); - } - - @Override - public void onFinish() { - callback.onFinish(); - mCameraChangeTracker.setReason(3); - } - }); - mCameraUpdateQueue.offer(stop); - } - - // if map is already ready start executing on the queue - if (mMap != null) { - mCameraUpdateQueue.execute(mMap); - } - } - - public void queryRenderedFeaturesAtPoint(String callbackID, PointF point, FilterParser.FilterList filter, List layerIDs) { + public void queryRenderedFeaturesAtPoint(String callbackID, PointF point, Expression filter, List layerIDs) { AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, EventKeys.MAP_ANDROID_CALLBACK); - List features = mMap.queryRenderedFeatures(point, FilterParser.parse(filter), layerIDs.toArray(new String[layerIDs.size()])); + List features = mMap.queryRenderedFeatures(point, filter, layerIDs.toArray(new String[layerIDs.size()])); WritableMap payload = new WritableNativeMap(); payload.putString("data", FeatureCollection.fromFeatures(features).toJson()); @@ -957,9 +802,9 @@ public void getZoom(String callbackID) { mManager.handleEvent(event); } - public void queryRenderedFeaturesInRect(String callbackID, RectF rect, FilterParser.FilterList filter, List layerIDs) { + public void queryRenderedFeaturesInRect(String callbackID, RectF rect, Expression filter, List layerIDs) { AndroidCallbackEvent event = new AndroidCallbackEvent(this, callbackID, EventKeys.MAP_ANDROID_CALLBACK); - List features = mMap.queryRenderedFeatures(rect, FilterParser.parse(filter), layerIDs.toArray(new String[layerIDs.size()])); + List features = mMap.queryRenderedFeatures(rect, filter, layerIDs.toArray(new String[layerIDs.size()])); WritableMap payload = new WritableNativeMap(); payload.putString("data", FeatureCollection.fromFeatures(features).toJson()); @@ -1048,8 +893,6 @@ public void showAttribution() { } public void init() { - setStyleUrl(mStyleURL); - // very important, this will make sure that mapbox-gl-native initializes the gl surface // https://github.com/mapbox/react-native-mapbox-gl/issues/955 getViewTreeObserver().dispatchOnGlobalLayout(); @@ -1059,50 +902,6 @@ public boolean isDestroyed(){ return mDestroyed; } - - private void updateCenterCoordinateIfNeeded() { - if (mMap != null && mVisibleCoordinateBounds != null) { - CameraUpdate boundsUpdate = CameraUpdateFactory.newLatLngBounds(mVisibleCoordinateBounds, 0); - CameraPosition boundsPosition = boundsUpdate.getCameraPosition(mMap); - if (boundsPosition != null) { - mCenterCoordinate = Point.fromCoordinates(Position.fromLngLat( - boundsPosition.target.getLongitude(), boundsPosition.target.getLatitude() - )); - mZoomLevel = boundsPosition.zoom; - } - } - } - - private void updateCameraPositionIfNeeded(boolean shouldUpdateTarget) { - if (mMap != null) { - CameraPosition prevPosition = mMap.getCameraPosition(); - CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(buildCamera(prevPosition, shouldUpdateTarget)); - - if (mAnimated) { - mMap.easeCamera(cameraUpdate); - } else { - mMap.moveCamera(cameraUpdate); - } - } - } - - private CameraPosition buildCamera() { - return buildCamera(null, true); - } - - private CameraPosition buildCamera(CameraPosition previousPosition, boolean shouldUpdateTarget) { - CameraPosition.Builder builder = new CameraPosition.Builder(previousPosition) - .bearing(mHeading) - .tilt(mPitch) - .zoom(mZoomLevel); - - if (shouldUpdateTarget) { - builder.target(GeoJSONUtils.toLatLng(mCenterCoordinate)); - } - - return builder.build(); - } - private void updateUISettings() { if (mMap == null) { return; @@ -1171,37 +970,17 @@ private void updateInsets() { Float.valueOf(bottom * metrics.scaledDensity).intValue()); } - private void setMinMaxZoomLevels() { - if (mMap == null) { - return; - } - - if (mMinZoomLevel != null) { - mMap.setMinZoomPreference(mMinZoomLevel); - } - - if (mMaxZoomLevel != null) { - mMap.setMaxZoomPreference(mMaxZoomLevel); - } - } - private void setLifecycleListeners() { final ReactContext reactContext = (ReactContext) mContext; mLifeCycleListener = new LifecycleEventListener() { @Override public void onHostResume() { - if (mShowUserLocation && !mLocationManger.isActive()) { - mLocationManger.enable(); - } onResume(); } @Override public void onHostPause() { - if (mLocationManger.isActive()) { - mLocationManger.disable(); - } onPause(); } @@ -1214,47 +993,7 @@ public void onHostDestroy() { reactContext.addLifecycleEventListener(mLifeCycleListener); } - private void enableLocation() { - if (!PermissionsManager.areLocationPermissionsGranted(mContext)) { - return; - } - - if (!mLocationManger.isActive()) { - mLocationManger.enable(); - } - - updateLocationLayer(); - - Location lastKnownLocation = mLocationManger.getLastKnownLocation(); - if (lastKnownLocation != null) { - mLocationChangeListener.onLocationChange(lastKnownLocation); - - postDelayed(new Runnable() { - @Override - public void run() { - sendRegionDidChangeEvent(); - } - }, 200); - } - } - - private void updateLocationLayer() { - if (mLocationLayer == null) { - mLocationLayer = new LocationLayerPlugin(this, mMap, mLocationManger.getEngine()); - } - - int userLayerMode = UserTrackingMode.getMapLayerMode(mUserLocation.getTrackingMode(), mShowUserLocation); - if (userLayerMode != mLocationLayer.getLocationLayerMode()) { - mLocationLayer.setLocationLayerEnabled(userLayerMode); - - Layer accLayer = mMap.getLayer(UserLocationLayerConstants.ACCURACY_LAYER_ID); - if (accLayer != null) { - accLayer.setProperties(PropertyFactory.visibility(Property.NONE)); - } - } - } - - private WritableMap makeRegionPayload() { + private WritableMap makeRegionPayload(Boolean isAnimated) { CameraPosition position = mMap.getCameraPosition(); LatLng latLng = new LatLng(position.target.getLatitude(), position.target.getLongitude()); @@ -1262,7 +1001,7 @@ private WritableMap makeRegionPayload() { properties.putDouble("zoomLevel", position.zoom); properties.putDouble("heading", position.bearing); properties.putDouble("pitch", position.tilt); - properties.putBoolean("animated", mCameraChangeTracker.isAnimated()); + properties.putBoolean("animated", (null == isAnimated) ? mCameraChangeTracker.isAnimated() : isAnimated.booleanValue()); properties.putBoolean("isUserInteraction", mCameraChangeTracker.isUserInteraction()); VisibleRegion visibleRegion = mMap.getProjection().getVisibleRegion(); @@ -1271,6 +1010,12 @@ private WritableMap makeRegionPayload() { return GeoJSONUtils.toPointFeature(latLng, properties); } + public void sendRegionChangeEvent(boolean isAnimated) { + IEvent event = new MapChangeEvent(this, makeRegionPayload(new Boolean(isAnimated)), EventTypes.REGION_DID_CHANGE); + mManager.handleEvent(event); + mCameraChangeTracker.setReason(CameraChangeTracker.EMPTY); + } + private void removeAllSourcesFromMap() { if (mSources.size() == 0) { return; @@ -1324,7 +1069,7 @@ private RCTSource getTouchableSourceWithHighestZIndex(List sources) { } // getLayers returns from back(N - 1) to front(0) - List mapboxLayers = mMap.getLayers(); + List mapboxLayers = mMap.getStyle().getLayers(); for (int i = mapboxLayers.size() - 1; i >= 0; i--) { Layer mapboxLayer = mapboxLayers.get(i); @@ -1337,118 +1082,6 @@ private RCTSource getTouchableSourceWithHighestZIndex(List sources) { return null; } - private void updateUserTrackingMode(int userTrackingMode) { - mUserLocation.setTrackingMode(userTrackingMode); - IEvent event = new MapUserTrackingModeEvent(this, userTrackingMode); - mManager.handleEvent(event); - } - - private void updateUserLocation(boolean isAnimated) { - if (!mShowUserLocation || mUserLocation.getTrackingMode() == UserTrackingMode.NONE) { - return; - } - - if (mUserTrackingState == UserTrackingState.POSSIBLE) { - updateUserLocationSignificantly(isAnimated); - } else if (mUserTrackingState == UserTrackingState.CHANGED) { - updateUserLocationIncrementally(isAnimated); - } - } - - private void updateUserLocationSignificantly(boolean isAnimated) { - mUserTrackingState = UserTrackingState.BEGAN; - - CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(getUserLocationUpdateCameraPosition(mZoomLevel)); - MapboxMap.CancelableCallback cameraCallback = new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - mUserTrackingState = UserTrackingState.CHANGED; - } - - @Override - public void onFinish() { - mUserTrackingState = UserTrackingState.CHANGED; - } - }; - - if (isAnimated && hasSetCenterCoordinate()) { - mMap.animateCamera(cameraUpdate, cameraCallback); - } else { - mMap.moveCamera(cameraUpdate, cameraCallback); - } - } - - private void updateUserLocationIncrementally(boolean isAnimated) { - mUserTrackingState = UserTrackingState.BEGAN; - - CameraPosition cameraPosition = mMap.getCameraPosition(); - CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(getUserLocationUpdateCameraPosition(cameraPosition.zoom)); - - MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - mUserTrackingState = UserTrackingState.CHANGED; - } - - @Override - public void onFinish() { - mUserTrackingState = UserTrackingState.CHANGED; - } - }; - - if (isAnimated) { - mMap.easeCamera(cameraUpdate, USER_LOCATION_CAMERA_MOVE_DURATION, callback); - } else { - mMap.moveCamera(cameraUpdate, callback); - } - } - - private CameraPosition getUserLocationUpdateCameraPosition(double zoomLevel) { - LatLng center = mUserLocation.getCoordinate(); - - if (mUserLocationVerticalAlignment != UserLocationVerticalAlignment.CENTER) { - DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); - int[] contentPadding = mMap.getPadding(); - - // we want to get the width, and height scaled based on pixel density, that also includes content padding - // (width * percentOfWidthWeWant - (leftPadding + rightPadding)) / dpi - int mapWidth = (int)((mMap.getWidth() * 0.75 - (contentPadding[0] + contentPadding[2])) / metrics.scaledDensity); - int mapHeight = (int)((mMap.getHeight() * 0.75 - (contentPadding[1] + contentPadding[3])) / metrics.scaledDensity); - VisibleRegion region = GeoViewport.getRegion(center, (int) zoomLevel, mapWidth, mapHeight); - - switch (mUserLocationVerticalAlignment) { - case UserLocationVerticalAlignment.TOP: - center = new LatLng(region.nearRight.getLatitude(), center.getLongitude()); - break; - case UserLocationVerticalAlignment.BOTTOM: - center = new LatLng(region.farLeft.getLatitude(), center.getLongitude()); - break; - } - } - - return new CameraPosition.Builder() - .target(center) - .bearing(getDirectionForUserLocationUpdate()) - .tilt(mPitch) - .zoom(zoomLevel) - .build(); - } - - private double getDirectionForUserLocationUpdate() { - // NOTE: The direction of this is used for map rotation only, not location layer rotation - CameraPosition currentCamera = mMap.getCameraPosition(); - double direction = currentCamera.bearing; - - int userTrackingMode = mUserLocation.getTrackingMode(); - if (userTrackingMode == UserTrackingMode.FollowWithHeading || userTrackingMode == UserTrackingMode.FollowWithCourse) { - direction = mUserLocation.getBearing(); - } else if (mHeading != 0.0) { - direction = mHeading; - } - - return direction; - } - private boolean hasSetCenterCoordinate() { CameraPosition cameraPosition = mMap.getCameraPosition(); LatLng center = cameraPosition.target; @@ -1460,7 +1093,7 @@ private double getMapRotation() { return cameraPosition.bearing; } - private void sendRegionDidChangeEvent() { + public void sendRegionDidChangeEvent() { handleMapChangedEvent(EventTypes.REGION_DID_CHANGE); mCameraChangeTracker.setReason(-1); } @@ -1475,7 +1108,7 @@ private void handleMapChangedEvent(String eventType) { case EventTypes.REGION_WILL_CHANGE: case EventTypes.REGION_DID_CHANGE: case EventTypes.REGION_IS_CHANGING: - event = new MapChangeEvent(this, makeRegionPayload(), eventType); + event = new MapChangeEvent(this, makeRegionPayload(null), eventType); break; default: event = new MapChangeEvent(this, eventType); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java index 8d435993e1..901c63606a 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/mapview/RCTMGLMapViewManager.java @@ -14,10 +14,10 @@ import com.mapbox.rctmgl.components.AbstractEventEmitter; import com.mapbox.rctmgl.events.constants.EventKeys; import com.mapbox.rctmgl.utils.ConvertUtils; -import com.mapbox.rctmgl.utils.FilterParser; +import com.mapbox.rctmgl.utils.ExpressionParser; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import java.util.ArrayList; import java.util.HashMap; @@ -117,11 +117,6 @@ public void setStyleURL(RCTMGLMapView mapView, String styleURL) { mapView.setReactStyleURL(styleURL); } - @ReactProp(name="animated") - public void setAnimated(RCTMGLMapView mapView, boolean isAnimated) { - mapView.setReactAnimated(isAnimated); - } - @ReactProp(name="localizeLabels") public void setLocalizeLabels(RCTMGLMapView mapView, boolean localizeLabels) { mapView.setLocalizeLabels(localizeLabels); @@ -162,68 +157,11 @@ public void setCompassEnabled(RCTMGLMapView mapView, boolean compassEnabled) { mapView.setReactCompassEnabled(compassEnabled); } - @ReactProp(name="heading") - public void setHeading(RCTMGLMapView mapView, double heading) { - mapView.setReactHeading(heading); - } - - @ReactProp(name="pitch") - public void setPitch(RCTMGLMapView mapView, double pitch) { - mapView.setReactPitch(pitch); - } - - @ReactProp(name="zoomLevel") - public void setZoomLevel(RCTMGLMapView mapView, double zoomLevel) { - mapView.setReactZoomLevel(zoomLevel); - } - - @ReactProp(name="minZoomLevel") - public void setMinZoomLevel(RCTMGLMapView mapView, double minZoomLevel) { - mapView.setReactMinZoomLevel(minZoomLevel); - } - - @ReactProp(name="maxZoomLevel") - public void setMaxZoomLevel(RCTMGLMapView mapView, double maxZoomLevel) { - mapView.setReactMaxZoomLevel(maxZoomLevel); - } - @ReactProp(name="contentInset") public void setContentInset(RCTMGLMapView mapView, ReadableArray array) { mapView.setReactContentInset(array); } - @ReactProp(name="centerCoordinate") - public void setCenterCoordinate(RCTMGLMapView mapView, String featureJSONStr) { - Point centerCoordinate = GeoJSONUtils.toPointGeometry(featureJSONStr); - if (centerCoordinate != null) { - mapView.setReactCenterCoordinate(centerCoordinate); - } - } - - @ReactProp(name="visibleCoordinateBounds") - public void setVisibleCoordinateBounds(RCTMGLMapView mapView, String featureJSONStr) { - FeatureCollection collection = FeatureCollection.fromJson(featureJSONStr); - LatLngBounds bounds = GeoJSONUtils.toLatLngBounds(collection); - if (bounds != null) { - mapView.setReactVisibleCoordinateBounds(bounds); - } - } - - @ReactProp(name="showUserLocation") - public void setShowUserLocation(RCTMGLMapView mapView, boolean showUserLocation) { - mapView.setReactShowUserLocation(showUserLocation); - } - - @ReactProp(name="userTrackingMode") - public void setUserTrackingMode(RCTMGLMapView mapView, int userTrackingMode) { - mapView.setReactUserTrackingMode(userTrackingMode); - } - - @ReactProp(name="userLocationVerticalAlignment") - public void setUserLocationVerticalAlignment(RCTMGLMapView mapView, int userLocationVerticalAlignment) { - mapView.setReactUserLocationVerticalAlignment(userLocationVerticalAlignment); - } - //endregion //region Custom Events @@ -243,8 +181,6 @@ public Map customEvents() { //endregion //region React Methods - - public static final int METHOD_SET_CAMERA = 1; public static final int METHOD_QUERY_FEATURES_POINT = 2; public static final int METHOD_QUERY_FEATURES_RECT = 3; public static final int METHOD_VISIBLE_BOUNDS = 4; @@ -260,7 +196,6 @@ public Map customEvents() { @Override public Map getCommandsMap() { return MapBuilder.builder() - .put("setCamera", METHOD_SET_CAMERA) .put("queryRenderedFeaturesAtPoint", METHOD_QUERY_FEATURES_POINT) .put("queryRenderedFeaturesInRect", METHOD_QUERY_FEATURES_RECT) .put("getVisibleBounds", METHOD_VISIBLE_BOUNDS) @@ -284,21 +219,18 @@ public void receiveCommand(RCTMGLMapView mapView, int commandID, @Nullable Reada } switch (commandID) { - case METHOD_SET_CAMERA: - mapView.setCamera(args.getString(0), args.getMap(1)); - break; case METHOD_QUERY_FEATURES_POINT: mapView.queryRenderedFeaturesAtPoint( args.getString(0), ConvertUtils.toPointF(args.getArray(1)), - FilterParser.getFilterList(args.getArray(2)), + ExpressionParser.from(args.getArray(2)), ConvertUtils.toStringList(args.getArray(3))); break; case METHOD_QUERY_FEATURES_RECT: mapView.queryRenderedFeaturesInRect( args.getString(0), ConvertUtils.toRectF(args.getArray(1)), - FilterParser.getFilterList(args.getArray(2)), + ExpressionParser.from(args.getArray(2)), ConvertUtils.toStringList(args.getArray(3))); break; case METHOD_VISIBLE_BOUNDS: diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyle.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyle.java index 45d8e33097..786f04b7cc 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyle.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyle.java @@ -4,7 +4,6 @@ import android.net.Uri; import android.support.annotation.NonNull; -import com.facebook.common.util.UriUtil; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -65,27 +64,16 @@ public void addImage(RCTMGLStyleValue styleValue) { } public void addImage(RCTMGLStyleValue styleValue, DownloadMapImageTask.OnAllImagesLoaded callback) { - String uriStr = styleValue.getString(RCTMGLStyleFactory.VALUE_KEY); - boolean shouldAddImage = styleValue.getBoolean(RCTMGLStyleFactory.SHOULD_ADD_IMAGE_KEY); - - if (!shouldAddImage) { + if (!styleValue.shouldAddImage()) { if (callback != null) { callback.onAllImagesLoaded(); } return; } + String uriStr = styleValue.getImageURI(); Map.Entry[] images = new Map.Entry[]{ new AbstractMap.SimpleEntry(uriStr, uriStr) }; DownloadMapImageTask task = new DownloadMapImageTask(mContext, mMap, callback); task.execute(images); } - - private boolean shouldAddImage(String uriStr) { - return uriStr != null && isValidURI(uriStr); - } - - private boolean isValidURI(String str) { - Uri uri = Uri.parse(str); - return UriUtil.isLocalAssetUri(uri) || UriUtil.isNetworkUri(uri); - } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFactory.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFactory.java index 602b956970..f4610ef9ae 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFactory.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFactory.java @@ -568,9 +568,6 @@ public static void setRasterLayerStyle(final RasterLayer layer, RCTMGLStyle styl case "rasterFadeDuration": RCTMGLStyleFactory.setRasterFadeDuration(layer, styleValue); break; - case "rasterFadeDurationTransition": - RCTMGLStyleFactory.setRasterFadeDurationTransition(layer, styleValue); - break; } } } @@ -651,44 +648,20 @@ public static void setLightLayerStyle(final Light layer, RCTMGLStyle style) { } public static void setVisibility(FillLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setFillAntialias(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.fillAntialias(value); - } - }; - - layer.setProperties(PropertyFactory.fillAntialias(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillAntialias(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillAntialias(styleValue.getBoolean(VALUE_KEY))); } } public static void setFillOpacity(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.fillOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.fillOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -703,20 +676,8 @@ public static void setFillOpacityTransition(FillLayer layer, RCTMGLStyleValue st } public static void setFillColor(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.fillColor(value); - } - }; - - layer.setProperties(PropertyFactory.fillColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillColor(styleValue.getInt(VALUE_KEY))); } @@ -731,20 +692,8 @@ public static void setFillColorTransition(FillLayer layer, RCTMGLStyleValue styl } public static void setFillOutlineColor(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.fillOutlineColor(value); - } - }; - - layer.setProperties(PropertyFactory.fillOutlineColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillOutlineColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillOutlineColor(styleValue.getInt(VALUE_KEY))); } @@ -759,20 +708,8 @@ public static void setFillOutlineColorTransition(FillLayer layer, RCTMGLStyleVal } public static void setFillTranslate(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.fillTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.fillTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -787,40 +724,16 @@ public static void setFillTranslateTransition(FillLayer layer, RCTMGLStyleValue } public static void setFillTranslateAnchor(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.fillTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.fillTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setFillPattern(FillLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.fillPattern(value); - } - }; - - layer.setProperties(PropertyFactory.fillPattern(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillPattern(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillPattern(styleValue.getString(VALUE_KEY))); } @@ -835,104 +748,44 @@ public static void setFillPatternTransition(FillLayer layer, RCTMGLStyleValue st } public static void setLineCap(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.lineCap(value); - } - }; - - layer.setProperties(PropertyFactory.lineCap(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineCap(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineCap(styleValue.getString(VALUE_KEY))); } } public static void setLineJoin(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.lineJoin(value); - } - }; - - layer.setProperties(PropertyFactory.lineJoin(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineJoin(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineJoin(styleValue.getString(VALUE_KEY))); } } public static void setLineMiterLimit(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineMiterLimit(value); - } - }; - - layer.setProperties(PropertyFactory.lineMiterLimit(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineMiterLimit(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineMiterLimit(styleValue.getFloat(VALUE_KEY))); } } public static void setLineRoundLimit(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineRoundLimit(value); - } - }; - - layer.setProperties(PropertyFactory.lineRoundLimit(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineRoundLimit(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineRoundLimit(styleValue.getFloat(VALUE_KEY))); } } public static void setVisibility(LineLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setLineOpacity(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.lineOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -947,20 +800,8 @@ public static void setLineOpacityTransition(LineLayer layer, RCTMGLStyleValue st } public static void setLineColor(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.lineColor(value); - } - }; - - layer.setProperties(PropertyFactory.lineColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineColor(styleValue.getInt(VALUE_KEY))); } @@ -975,20 +816,8 @@ public static void setLineColorTransition(LineLayer layer, RCTMGLStyleValue styl } public static void setLineTranslate(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.lineTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.lineTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -1003,40 +832,16 @@ public static void setLineTranslateTransition(LineLayer layer, RCTMGLStyleValue } public static void setLineTranslateAnchor(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.lineTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.lineTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setLineWidth(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineWidth(value); - } - }; - - layer.setProperties(PropertyFactory.lineWidth(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineWidth(styleValue.getFloat(VALUE_KEY))); } @@ -1051,20 +856,8 @@ public static void setLineWidthTransition(LineLayer layer, RCTMGLStyleValue styl } public static void setLineGapWidth(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineGapWidth(value); - } - }; - - layer.setProperties(PropertyFactory.lineGapWidth(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineGapWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineGapWidth(styleValue.getFloat(VALUE_KEY))); } @@ -1079,20 +872,8 @@ public static void setLineGapWidthTransition(LineLayer layer, RCTMGLStyleValue s } public static void setLineOffset(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineOffset(value); - } - }; - - layer.setProperties(PropertyFactory.lineOffset(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineOffset(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineOffset(styleValue.getFloat(VALUE_KEY))); } @@ -1107,20 +888,8 @@ public static void setLineOffsetTransition(LineLayer layer, RCTMGLStyleValue sty } public static void setLineBlur(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.lineBlur(value); - } - }; - - layer.setProperties(PropertyFactory.lineBlur(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineBlur(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineBlur(styleValue.getFloat(VALUE_KEY))); } @@ -1135,20 +904,8 @@ public static void setLineBlurTransition(LineLayer layer, RCTMGLStyleValue style } public static void setLineDasharray(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.lineDasharray(value); - } - }; - - layer.setProperties(PropertyFactory.lineDasharray(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.lineDasharray(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.lineDasharray(styleValue.getFloatArray(VALUE_KEY))); } @@ -1163,20 +920,8 @@ public static void setLineDasharrayTransition(LineLayer layer, RCTMGLStyleValue } public static void setLinePattern(LineLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.linePattern(value); - } - }; - - layer.setProperties(PropertyFactory.linePattern(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.linePattern(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.linePattern(styleValue.getString(VALUE_KEY))); } @@ -1191,744 +936,300 @@ public static void setLinePatternTransition(LineLayer layer, RCTMGLStyleValue st } public static void setSymbolPlacement(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.symbolPlacement(value); - } - }; - - layer.setProperties(PropertyFactory.symbolPlacement(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.symbolPlacement(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.symbolPlacement(styleValue.getString(VALUE_KEY))); } } public static void setSymbolSpacing(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.symbolSpacing(value); - } - }; - - layer.setProperties(PropertyFactory.symbolSpacing(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.symbolSpacing(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.symbolSpacing(styleValue.getFloat(VALUE_KEY))); } } public static void setSymbolAvoidEdges(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.symbolAvoidEdges(value); - } - }; - - layer.setProperties(PropertyFactory.symbolAvoidEdges(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.symbolAvoidEdges(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.symbolAvoidEdges(styleValue.getBoolean(VALUE_KEY))); } } public static void setIconAllowOverlap(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.iconAllowOverlap(value); - } - }; - - layer.setProperties(PropertyFactory.iconAllowOverlap(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconAllowOverlap(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconAllowOverlap(styleValue.getBoolean(VALUE_KEY))); } } public static void setIconIgnorePlacement(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.iconIgnorePlacement(value); - } - }; - - layer.setProperties(PropertyFactory.iconIgnorePlacement(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconIgnorePlacement(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconIgnorePlacement(styleValue.getBoolean(VALUE_KEY))); } } public static void setIconOptional(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.iconOptional(value); - } - }; - - layer.setProperties(PropertyFactory.iconOptional(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconOptional(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconOptional(styleValue.getBoolean(VALUE_KEY))); } } public static void setIconRotationAlignment(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconRotationAlignment(value); - } - }; - - layer.setProperties(PropertyFactory.iconRotationAlignment(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconRotationAlignment(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconRotationAlignment(styleValue.getString(VALUE_KEY))); } } public static void setIconSize(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconSize(value); - } - }; - - layer.setProperties(PropertyFactory.iconSize(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconSize(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconSize(styleValue.getFloat(VALUE_KEY))); } } public static void setIconTextFit(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconTextFit(value); - } - }; - - layer.setProperties(PropertyFactory.iconTextFit(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconTextFit(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconTextFit(styleValue.getString(VALUE_KEY))); } } public static void setIconTextFitPadding(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.iconTextFitPadding(value); - } - }; - - layer.setProperties(PropertyFactory.iconTextFitPadding(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconTextFitPadding(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconTextFitPadding(styleValue.getFloatArray(VALUE_KEY))); } } public static void setIconImage(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconImage(value); - } - }; - - layer.setProperties(PropertyFactory.iconImage(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconImage(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconImage(styleValue.getString(VALUE_KEY))); } } public static void setIconRotate(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconRotate(value); - } - }; - - layer.setProperties(PropertyFactory.iconRotate(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconRotate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconRotate(styleValue.getFloat(VALUE_KEY))); } } public static void setIconPadding(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconPadding(value); - } - }; - - layer.setProperties(PropertyFactory.iconPadding(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconPadding(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconPadding(styleValue.getFloat(VALUE_KEY))); } } public static void setIconKeepUpright(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.iconKeepUpright(value); - } - }; - - layer.setProperties(PropertyFactory.iconKeepUpright(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconKeepUpright(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconKeepUpright(styleValue.getBoolean(VALUE_KEY))); } } public static void setIconOffset(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.iconOffset(value); - } - }; - - layer.setProperties(PropertyFactory.iconOffset(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconOffset(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconOffset(styleValue.getFloatArray(VALUE_KEY))); } } public static void setIconAnchor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.iconAnchor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconAnchor(styleValue.getString(VALUE_KEY))); } } public static void setIconPitchAlignment(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconPitchAlignment(value); - } - }; - - layer.setProperties(PropertyFactory.iconPitchAlignment(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconPitchAlignment(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconPitchAlignment(styleValue.getString(VALUE_KEY))); } } public static void setTextPitchAlignment(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textPitchAlignment(value); - } - }; - - layer.setProperties(PropertyFactory.textPitchAlignment(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textPitchAlignment(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textPitchAlignment(styleValue.getString(VALUE_KEY))); } } public static void setTextRotationAlignment(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textRotationAlignment(value); - } - }; - - layer.setProperties(PropertyFactory.textRotationAlignment(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textRotationAlignment(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textRotationAlignment(styleValue.getString(VALUE_KEY))); } } public static void setTextField(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textField(value); - } - }; - - layer.setProperties(PropertyFactory.textField(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textField(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textField(styleValue.getString(VALUE_KEY))); } } public static void setTextFont(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getStringArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String[] value) { - return PropertyFactory.textFont(value); - } - }; - - layer.setProperties(PropertyFactory.textFont(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textFont(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textFont(styleValue.getStringArray(VALUE_KEY))); } } public static void setTextSize(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textSize(value); - } - }; - - layer.setProperties(PropertyFactory.textSize(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textSize(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textSize(styleValue.getFloat(VALUE_KEY))); } } public static void setTextMaxWidth(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textMaxWidth(value); - } - }; - - layer.setProperties(PropertyFactory.textMaxWidth(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textMaxWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textMaxWidth(styleValue.getFloat(VALUE_KEY))); } } public static void setTextLineHeight(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textLineHeight(value); - } - }; - - layer.setProperties(PropertyFactory.textLineHeight(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textLineHeight(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textLineHeight(styleValue.getFloat(VALUE_KEY))); } } public static void setTextLetterSpacing(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textLetterSpacing(value); - } - }; - - layer.setProperties(PropertyFactory.textLetterSpacing(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textLetterSpacing(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textLetterSpacing(styleValue.getFloat(VALUE_KEY))); } } public static void setTextJustify(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textJustify(value); - } - }; - - layer.setProperties(PropertyFactory.textJustify(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textJustify(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textJustify(styleValue.getString(VALUE_KEY))); } } public static void setTextAnchor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.textAnchor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textAnchor(styleValue.getString(VALUE_KEY))); } } public static void setTextMaxAngle(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textMaxAngle(value); - } - }; - - layer.setProperties(PropertyFactory.textMaxAngle(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textMaxAngle(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textMaxAngle(styleValue.getFloat(VALUE_KEY))); } } public static void setTextRotate(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textRotate(value); - } - }; - - layer.setProperties(PropertyFactory.textRotate(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textRotate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textRotate(styleValue.getFloat(VALUE_KEY))); } } public static void setTextPadding(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textPadding(value); - } - }; - - layer.setProperties(PropertyFactory.textPadding(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textPadding(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textPadding(styleValue.getFloat(VALUE_KEY))); } } public static void setTextKeepUpright(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.textKeepUpright(value); - } - }; - - layer.setProperties(PropertyFactory.textKeepUpright(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textKeepUpright(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textKeepUpright(styleValue.getBoolean(VALUE_KEY))); } } public static void setTextTransform(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textTransform(value); - } - }; - - layer.setProperties(PropertyFactory.textTransform(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textTransform(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textTransform(styleValue.getString(VALUE_KEY))); } } public static void setTextOffset(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.textOffset(value); - } - }; - - layer.setProperties(PropertyFactory.textOffset(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textOffset(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textOffset(styleValue.getFloatArray(VALUE_KEY))); } } public static void setTextAllowOverlap(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.textAllowOverlap(value); - } - }; - - layer.setProperties(PropertyFactory.textAllowOverlap(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textAllowOverlap(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textAllowOverlap(styleValue.getBoolean(VALUE_KEY))); } } public static void setTextIgnorePlacement(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.textIgnorePlacement(value); - } - }; - - layer.setProperties(PropertyFactory.textIgnorePlacement(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textIgnorePlacement(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textIgnorePlacement(styleValue.getBoolean(VALUE_KEY))); } } public static void setTextOptional(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Boolean getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getBoolean(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Boolean value) { - return PropertyFactory.textOptional(value); - } - }; - - layer.setProperties(PropertyFactory.textOptional(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textOptional(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textOptional(styleValue.getBoolean(VALUE_KEY))); } } public static void setVisibility(SymbolLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setIconOpacity(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.iconOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -1943,20 +1244,8 @@ public static void setIconOpacityTransition(SymbolLayer layer, RCTMGLStyleValue } public static void setIconColor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.iconColor(value); - } - }; - - layer.setProperties(PropertyFactory.iconColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconColor(styleValue.getInt(VALUE_KEY))); } @@ -1971,20 +1260,8 @@ public static void setIconColorTransition(SymbolLayer layer, RCTMGLStyleValue st } public static void setIconHaloColor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.iconHaloColor(value); - } - }; - - layer.setProperties(PropertyFactory.iconHaloColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconHaloColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconHaloColor(styleValue.getInt(VALUE_KEY))); } @@ -1999,20 +1276,8 @@ public static void setIconHaloColorTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setIconHaloWidth(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconHaloWidth(value); - } - }; - - layer.setProperties(PropertyFactory.iconHaloWidth(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconHaloWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconHaloWidth(styleValue.getFloat(VALUE_KEY))); } @@ -2027,20 +1292,8 @@ public static void setIconHaloWidthTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setIconHaloBlur(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.iconHaloBlur(value); - } - }; - - layer.setProperties(PropertyFactory.iconHaloBlur(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconHaloBlur(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconHaloBlur(styleValue.getFloat(VALUE_KEY))); } @@ -2055,20 +1308,8 @@ public static void setIconHaloBlurTransition(SymbolLayer layer, RCTMGLStyleValue } public static void setIconTranslate(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.iconTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.iconTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -2083,40 +1324,16 @@ public static void setIconTranslateTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setIconTranslateAnchor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.iconTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.iconTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.iconTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.iconTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setTextOpacity(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.textOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -2131,20 +1348,8 @@ public static void setTextOpacityTransition(SymbolLayer layer, RCTMGLStyleValue } public static void setTextColor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.textColor(value); - } - }; - - layer.setProperties(PropertyFactory.textColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textColor(styleValue.getInt(VALUE_KEY))); } @@ -2159,20 +1364,8 @@ public static void setTextColorTransition(SymbolLayer layer, RCTMGLStyleValue st } public static void setTextHaloColor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.textHaloColor(value); - } - }; - - layer.setProperties(PropertyFactory.textHaloColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textHaloColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textHaloColor(styleValue.getInt(VALUE_KEY))); } @@ -2187,20 +1380,8 @@ public static void setTextHaloColorTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setTextHaloWidth(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textHaloWidth(value); - } - }; - - layer.setProperties(PropertyFactory.textHaloWidth(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textHaloWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textHaloWidth(styleValue.getFloat(VALUE_KEY))); } @@ -2215,20 +1396,8 @@ public static void setTextHaloWidthTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setTextHaloBlur(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.textHaloBlur(value); - } - }; - - layer.setProperties(PropertyFactory.textHaloBlur(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textHaloBlur(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textHaloBlur(styleValue.getFloat(VALUE_KEY))); } @@ -2243,20 +1412,8 @@ public static void setTextHaloBlurTransition(SymbolLayer layer, RCTMGLStyleValue } public static void setTextTranslate(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.textTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.textTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -2271,44 +1428,20 @@ public static void setTextTranslateTransition(SymbolLayer layer, RCTMGLStyleValu } public static void setTextTranslateAnchor(SymbolLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.textTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.textTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.textTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.textTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setVisibility(CircleLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setCircleRadius(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.circleRadius(value); - } - }; - - layer.setProperties(PropertyFactory.circleRadius(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleRadius(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleRadius(styleValue.getFloat(VALUE_KEY))); } @@ -2323,20 +1456,8 @@ public static void setCircleRadiusTransition(CircleLayer layer, RCTMGLStyleValue } public static void setCircleColor(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.circleColor(value); - } - }; - - layer.setProperties(PropertyFactory.circleColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleColor(styleValue.getInt(VALUE_KEY))); } @@ -2351,20 +1472,8 @@ public static void setCircleColorTransition(CircleLayer layer, RCTMGLStyleValue } public static void setCircleBlur(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.circleBlur(value); - } - }; - - layer.setProperties(PropertyFactory.circleBlur(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleBlur(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleBlur(styleValue.getFloat(VALUE_KEY))); } @@ -2379,20 +1488,8 @@ public static void setCircleBlurTransition(CircleLayer layer, RCTMGLStyleValue s } public static void setCircleOpacity(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.circleOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.circleOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -2407,20 +1504,8 @@ public static void setCircleOpacityTransition(CircleLayer layer, RCTMGLStyleValu } public static void setCircleTranslate(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.circleTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.circleTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -2435,80 +1520,32 @@ public static void setCircleTranslateTransition(CircleLayer layer, RCTMGLStyleVa } public static void setCircleTranslateAnchor(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.circleTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.circleTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setCirclePitchScale(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.circlePitchScale(value); - } - }; - - layer.setProperties(PropertyFactory.circlePitchScale(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circlePitchScale(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circlePitchScale(styleValue.getString(VALUE_KEY))); } } public static void setCirclePitchAlignment(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.circlePitchAlignment(value); - } - }; - - layer.setProperties(PropertyFactory.circlePitchAlignment(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circlePitchAlignment(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circlePitchAlignment(styleValue.getString(VALUE_KEY))); } } public static void setCircleStrokeWidth(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.circleStrokeWidth(value); - } - }; - - layer.setProperties(PropertyFactory.circleStrokeWidth(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleStrokeWidth(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleStrokeWidth(styleValue.getFloat(VALUE_KEY))); } @@ -2523,20 +1560,8 @@ public static void setCircleStrokeWidthTransition(CircleLayer layer, RCTMGLStyle } public static void setCircleStrokeColor(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.circleStrokeColor(value); - } - }; - - layer.setProperties(PropertyFactory.circleStrokeColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleStrokeColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleStrokeColor(styleValue.getInt(VALUE_KEY))); } @@ -2551,20 +1576,8 @@ public static void setCircleStrokeColorTransition(CircleLayer layer, RCTMGLStyle } public static void setCircleStrokeOpacity(CircleLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.circleStrokeOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.circleStrokeOpacity(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.circleStrokeOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.circleStrokeOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -2579,24 +1592,12 @@ public static void setCircleStrokeOpacityTransition(CircleLayer layer, RCTMGLSty } public static void setVisibility(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setFillExtrusionOpacity(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.fillExtrusionOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionOpacity(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -2611,20 +1612,8 @@ public static void setFillExtrusionOpacityTransition(FillExtrusionLayer layer, R } public static void setFillExtrusionColor(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.fillExtrusionColor(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionColor(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionColor(styleValue.getInt(VALUE_KEY))); } @@ -2639,20 +1628,8 @@ public static void setFillExtrusionColorTransition(FillExtrusionLayer layer, RCT } public static void setFillExtrusionTranslate(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float[] getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloatArray(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float[] value) { - return PropertyFactory.fillExtrusionTranslate(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionTranslate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionTranslate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionTranslate(styleValue.getFloatArray(VALUE_KEY))); } @@ -2667,40 +1644,16 @@ public static void setFillExtrusionTranslateTransition(FillExtrusionLayer layer, } public static void setFillExtrusionTranslateAnchor(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.fillExtrusionTranslateAnchor(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionTranslateAnchor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionTranslateAnchor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionTranslateAnchor(styleValue.getString(VALUE_KEY))); } } public static void setFillExtrusionPattern(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.fillExtrusionPattern(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionPattern(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionPattern(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionPattern(styleValue.getString(VALUE_KEY))); } @@ -2715,20 +1668,8 @@ public static void setFillExtrusionPatternTransition(FillExtrusionLayer layer, R } public static void setFillExtrusionHeight(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.fillExtrusionHeight(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionHeight(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionHeight(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionHeight(styleValue.getFloat(VALUE_KEY))); } @@ -2743,20 +1684,8 @@ public static void setFillExtrusionHeightTransition(FillExtrusionLayer layer, RC } public static void setFillExtrusionBase(FillExtrusionLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.fillExtrusionBase(value); - } - }; - - layer.setProperties(PropertyFactory.fillExtrusionBase(styleValue.makeStyleFunction(parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.fillExtrusionBase(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.fillExtrusionBase(styleValue.getFloat(VALUE_KEY))); } @@ -2771,24 +1700,12 @@ public static void setFillExtrusionBaseTransition(FillExtrusionLayer layer, RCTM } public static void setVisibility(RasterLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setRasterOpacity(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.rasterOpacity(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -2803,20 +1720,8 @@ public static void setRasterOpacityTransition(RasterLayer layer, RCTMGLStyleValu } public static void setRasterHueRotate(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterHueRotate(value); - } - }; - - layer.setProperties(PropertyFactory.rasterHueRotate(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterHueRotate(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterHueRotate(styleValue.getFloat(VALUE_KEY))); } @@ -2831,20 +1736,8 @@ public static void setRasterHueRotateTransition(RasterLayer layer, RCTMGLStyleVa } public static void setRasterBrightnessMin(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterBrightnessMin(value); - } - }; - - layer.setProperties(PropertyFactory.rasterBrightnessMin(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterBrightnessMin(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterBrightnessMin(styleValue.getFloat(VALUE_KEY))); } @@ -2859,20 +1752,8 @@ public static void setRasterBrightnessMinTransition(RasterLayer layer, RCTMGLSty } public static void setRasterBrightnessMax(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterBrightnessMax(value); - } - }; - - layer.setProperties(PropertyFactory.rasterBrightnessMax(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterBrightnessMax(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterBrightnessMax(styleValue.getFloat(VALUE_KEY))); } @@ -2887,20 +1768,8 @@ public static void setRasterBrightnessMaxTransition(RasterLayer layer, RCTMGLSty } public static void setRasterSaturation(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterSaturation(value); - } - }; - - layer.setProperties(PropertyFactory.rasterSaturation(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterSaturation(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterSaturation(styleValue.getFloat(VALUE_KEY))); } @@ -2915,20 +1784,8 @@ public static void setRasterSaturationTransition(RasterLayer layer, RCTMGLStyleV } public static void setRasterContrast(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterContrast(value); - } - }; - - layer.setProperties(PropertyFactory.rasterContrast(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterContrast(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterContrast(styleValue.getFloat(VALUE_KEY))); } @@ -2943,52 +1800,20 @@ public static void setRasterContrastTransition(RasterLayer layer, RCTMGLStyleVal } public static void setRasterFadeDuration(RasterLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.rasterFadeDuration(value); - } - }; - - layer.setProperties(PropertyFactory.rasterFadeDuration(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.rasterFadeDuration(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.rasterFadeDuration(styleValue.getFloat(VALUE_KEY))); } } - - public static void setRasterFadeDurationTransition(RasterLayer layer, RCTMGLStyleValue styleValue) { - TransitionOptions transition = styleValue.getTransition(); - if (transition != null) { - layer.setRasterFadeDurationTransition(transition); - } - } - public static void setVisibility(BackgroundLayer layer, RCTMGLStyleValue styleValue) { - layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); } public static void setBackgroundColor(BackgroundLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Integer getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getInt(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Integer value) { - return PropertyFactory.backgroundColor(value); - } - }; - - layer.setProperties(PropertyFactory.backgroundColor(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.backgroundColor(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.backgroundColor(styleValue.getInt(VALUE_KEY))); } @@ -3003,20 +1828,8 @@ public static void setBackgroundColorTransition(BackgroundLayer layer, RCTMGLSty } public static void setBackgroundPattern(BackgroundLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected String getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getString(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(String value) { - return PropertyFactory.backgroundPattern(value); - } - }; - - layer.setProperties(PropertyFactory.backgroundPattern(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.backgroundPattern(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.backgroundPattern(styleValue.getString(VALUE_KEY))); } @@ -3031,20 +1844,8 @@ public static void setBackgroundPatternTransition(BackgroundLayer layer, RCTMGLS } public static void setBackgroundOpacity(BackgroundLayer layer, RCTMGLStyleValue styleValue) { - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser parser = new RCTMGLStyleFunctionParser(styleValue) { - @Override - protected Float getRawStopValue(RCTMGLStyleValue styleValue) { - return styleValue.getFloat(VALUE_KEY); - } - - @Override - protected PropertyValue getStopValue(Float value) { - return PropertyFactory.backgroundOpacity(value); - } - }; - - layer.setProperties(PropertyFactory.backgroundOpacity(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.backgroundOpacity(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.backgroundOpacity(styleValue.getFloat(VALUE_KEY))); } @@ -3059,12 +1860,12 @@ public static void setBackgroundOpacityTransition(BackgroundLayer layer, RCTMGLS } public static void setAnchor(Light layer, RCTMGLStyleValue styleValue) { - layer.setAnchor(styleValue.getString(VALUE_KEY)); + layer.setAnchor(styleValue.getString(VALUE_KEY)); } public static void setPosition(Light layer, RCTMGLStyleValue styleValue) { - Float[] values = styleValue.getFloatArray(VALUE_KEY); - layer.setPosition(Position.fromPosition(values[0], values[1], values[2])); + Float[] values = styleValue.getFloatArray(VALUE_KEY); + layer.setPosition(Position.fromPosition(values[0], values[1], values[2])); } @@ -3076,7 +1877,7 @@ public static void setPositionTransition(Light layer, RCTMGLStyleValue styleValu } public static void setColor(Light layer, RCTMGLStyleValue styleValue) { - layer.setColor(styleValue.getInt(VALUE_KEY)); + layer.setColor(styleValue.getInt(VALUE_KEY)); } @@ -3088,7 +1889,7 @@ public static void setColorTransition(Light layer, RCTMGLStyleValue styleValue) } public static void setIntensity(Light layer, RCTMGLStyleValue styleValue) { - layer.setIntensity(styleValue.getFloat(VALUE_KEY)); + layer.setIntensity(styleValue.getFloat(VALUE_KEY)); } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFunctionParser.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFunctionParser.java index b989a57369..3aa05227ca 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFunctionParser.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleFunctionParser.java @@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator; import com.facebook.react.bridge.ReadableNativeArray; import com.facebook.react.bridge.ReadableType; -import com.mapbox.mapboxsdk.style.functions.stops.Stop; +import com.mapbox.mapboxsdk.style.expressions.Expression.Stop; import com.mapbox.mapboxsdk.style.layers.PropertyValue; import java.text.NumberFormat; @@ -73,22 +73,6 @@ public List getRawStops() { return rawStops; } - protected Stop[] getStops(List rawStops) { - Stop[] stops = new Stop[rawStops.size()]; - - for (int i = 0; i < rawStops.size(); i++) { - StopConfig config = rawStops.get(i); - - if (config.propertyValue != null) { - stops[i] = Stop.stop((Number) config.key, config.propertyValue, getStopValue(config.value)); - } else { - stops[i] = Stop.stop(config.key, getStopValue(config.value)); - } - } - - return stops; - } - protected abstract T getRawStopValue (RCTMGLStyleValue styleValue); protected abstract PropertyValue getStopValue(T value); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java index 8888d5f35c..519cf496fc 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/RCTMGLStyleValue.java @@ -5,13 +5,10 @@ import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.mapbox.mapboxsdk.style.functions.CameraFunction; -import com.mapbox.mapboxsdk.style.functions.CompositeFunction; -import com.mapbox.mapboxsdk.style.functions.Function; -import com.mapbox.mapboxsdk.style.functions.SourceFunction; -import com.mapbox.mapboxsdk.style.functions.stops.Stop; -import com.mapbox.mapboxsdk.style.functions.stops.Stops; +import com.facebook.react.bridge.ReadableType; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.rctmgl.utils.ExpressionParser; /** * Created by nickitaliano on 9/12/17. @@ -20,11 +17,12 @@ public class RCTMGLStyleValue { private String mType; + private boolean isExpression; + private Expression mExpression; private ReadableMap mPayload; - public static final String FunctionTypeCamera = "camera"; - public static final String FunctionTypeSource = "source"; - public static final String FunctionTypeComposite = "composite"; + private String imageURI = ""; + private boolean isAddImage; public static final int InterpolationModeExponential = 100; public static final int InterpolationModeInterval = 101; @@ -33,7 +31,25 @@ public class RCTMGLStyleValue { public RCTMGLStyleValue(@NonNull ReadableMap config) { mType = config.getString("styletype"); - mPayload = config.getMap("payload"); + mPayload = config.getMap("stylevalue"); + + if ("image".equals(mType)) { + imageURI = mPayload.getString("value"); + isAddImage = imageURI != null && imageURI.contains("://"); + return; + } + + Dynamic dynamic = mPayload.getDynamic("value"); + if (dynamic.getType().equals(ReadableType.Array)) { + ReadableArray array = dynamic.asArray(); + if (array.size() > 0) { + ReadableMap map = array.getMap(0); + if (map != null && map.getString("type").equals("string")) { + isExpression = true; + mExpression = ExpressionParser.from(array); + } + } + } } public String getType() { @@ -77,7 +93,8 @@ public Float[] getFloatArray(String key) { Float[] floatArr = new Float[arr.size()]; for (int i = 0; i < arr.size(); i++) { - floatArr[i] = (float) arr.getDouble(i); + ReadableMap item = arr.getMap(i); + floatArr[i] = (float) item.getDouble("value"); } return floatArr; @@ -88,7 +105,8 @@ public String[] getStringArray(String key) { String[] stringArr = new String[arr.size()]; for (int i = 0; i < arr.size(); i++) { - stringArr[i] = arr.getString(i); + ReadableMap item = arr.getMap(i); + stringArr[i] = item.getString("value"); } return stringArr; @@ -98,65 +116,20 @@ public ReadableMap getMap(String key) { return mPayload.getMap(key); } - public Function makeStyleFunction(RCTMGLStyleFunctionParser functionParser) { - String fnType = getString("fn"); - int mode = getInt("mode"); - - switch (fnType) { - case FunctionTypeCamera: - return makeCameraFunction(mode, functionParser); - case FunctionTypeSource: - return makeSourceFunction(mode, getString("attributeName"), functionParser); - case FunctionTypeComposite: - return makeCompositeFunction(mode, getString("attributeName"), functionParser); - default: - return null; - } + public Expression getExpression() { + return mExpression; } - public CameraFunction makeCameraFunction(int mode, RCTMGLStyleFunctionParser functionParser) { - Stop[] stops = functionParser.getStops(functionParser.getRawStops()); - - switch (mode) { - case InterpolationModeExponential: - return Function.zoom(Stops.exponential(stops)); - case InterpolationModeInterval: - return Function.zoom(Stops.interval(stops)); - default: - return null; - } + public boolean isExpression() { + return isExpression; } - public SourceFunction makeSourceFunction(int mode, String property, RCTMGLStyleFunctionParser functionParser) { - Stop[] stops = functionParser.getStops(functionParser.getRawStops()); - - switch (mode) { - case InterpolationModeExponential: - return Function.property(property, Stops.exponential(stops)); - case InterpolationModeInterval: - return Function.property(property, Stops.interval(stops)); - case InterpolationModeCategorical: - return Function.property(property, Stops.categorical(stops)); - case InterpolationModeIdentity: - return Function.property(property, Stops.identity()); - default: - return null; - } + public boolean shouldAddImage() { + return isAddImage; } - public CompositeFunction makeCompositeFunction(int mode, String property, RCTMGLStyleFunctionParser functionParser) { - Stop[] stops = functionParser.getStops(functionParser.getRawStops()); - - switch (mode) { - case InterpolationModeExponential: - return Function.composite(property, Stops.exponential(stops)); - case InterpolationModeInterval: - return Function.composite(property, Stops.interval(stops)); - case InterpolationModeCategorical: - return Function.composite(property, Stops.categorical(stops)); - default: - return null; - } + public String getImageURI() { + return imageURI; } public TransitionOptions getTransition() { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java index 0646a664a1..272404ec2a 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTLayer.java @@ -1,27 +1,21 @@ package com.mapbox.rctmgl.components.styles.layers; import android.content.Context; -import android.util.Log; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.style.layers.Filter; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.PropertyFactory; import com.mapbox.rctmgl.components.AbstractMapFeature; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; -import com.mapbox.rctmgl.components.styles.sources.RCTSource; import com.mapbox.rctmgl.location.UserLocationLayerConstants; -import com.mapbox.rctmgl.utils.ConvertUtils; -import com.mapbox.rctmgl.utils.DownloadMapImageTask; -import com.mapbox.rctmgl.utils.FilterParser; +import com.mapbox.rctmgl.utils.ExpressionParser; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; /** @@ -31,26 +25,6 @@ public abstract class RCTLayer extends AbstractMapFeature { public static final String LOG_TAG = RCTLayer.class.getSimpleName(); - public static final Set FILTER_OPS = new HashSet(Arrays.asList( - "all", - "any", - "none", - "in", - "!in", - "<=", - "<", - ">=", - ">", - "!=", - "==", - "has", - "!has" - )); - - public static final int COMPOUND_FILTER_ALL = 3; - public static final int COMPOUND_FILTER_ANY = 2; - public static final int COMPOUND_FILTER_NONE = 1; - protected String mID; protected String mSourceID; protected String mAboveLayerID; @@ -61,7 +35,7 @@ public abstract class RCTLayer extends AbstractMapFeature { protected Double mMinZoomLevel; protected Double mMaxZoomLevel; protected ReadableMap mReactStyle; - protected Filter.Statement mFilter; + protected Expression mFilter; protected MapboxMap mMap; protected T mLayer; @@ -69,9 +43,12 @@ public abstract class RCTLayer extends AbstractMapFeature { protected Context mContext; protected RCTMGLMapView mMapView; + protected boolean mHadFilter; + public RCTLayer(Context context) { super(context); mContext = context; + mHadFilter = false; } public String getID() { @@ -156,13 +133,16 @@ public void setReactStyle(ReadableMap reactStyle) { } public void setFilter(ReadableArray readableFilterArray) { - FilterParser.FilterList filterList = FilterParser.getFilterList(readableFilterArray); + Expression filterExpression = ExpressionParser.from(readableFilterArray); - mFilter = buildFilter(filterList); + mFilter = filterExpression; if (mLayer != null) { if (mFilter != null) { + mHadFilter = true; updateFilter(mFilter); + } else if (mHadFilter) { + updateFilter(Expression.literal(true)); } } } @@ -173,40 +153,40 @@ public void add() { } String userBackgroundID = UserLocationLayerConstants.BACKGROUND_LAYER_ID; - Layer userLocationBackgroundLayer = mMap.getLayer(userBackgroundID); + Layer userLocationBackgroundLayer = mMap.getStyle().getLayer(userBackgroundID); // place below user location layer if (userLocationBackgroundLayer != null) { - mMap.addLayerBelow(mLayer, userBackgroundID); + mMap.getStyle().addLayerBelow(mLayer, userBackgroundID); return; } - mMap.addLayer(mLayer); + mMap.getStyle().addLayer(mLayer); } public void addAbove(String aboveLayerID) { if (!hasInitialized()) { return; } - mMap.addLayerAbove(mLayer, aboveLayerID); + mMap.getStyle().addLayerAbove(mLayer, aboveLayerID); } public void addBelow(String belowLayerID) { if (!hasInitialized()) { return; } - mMap.addLayerBelow(mLayer, belowLayerID); + mMap.getStyle().addLayerBelow(mLayer, belowLayerID); } public void addAtIndex(int index) { if (!hasInitialized()) { return; } - mMap.addLayerAt(mLayer, index); + mMap.getStyle().addLayerAt(mLayer, index); } protected void insertLayer() { - if (mMap.getLayer(mID) != null) { + if (mMap.getStyle().getLayer(mID) != null) { return; // prevent adding a layer twice } @@ -233,11 +213,7 @@ protected void setZoomBounds() { } } - protected Filter.Statement buildFilter(FilterParser.FilterList filterList) { - return FilterParser.parse(filterList); - } - - protected void updateFilter(Filter.Statement statement) { + protected void updateFilter(Expression expression) { // override if you want to update the filter } @@ -246,7 +222,7 @@ public void addToMap(RCTMGLMapView mapView) { mMap = mapView.getMapboxMap(); mMapView = mapView; - T existingLayer = mMap.getLayerAs(mID); + T existingLayer = mMap.getStyle().getLayerAs(mID); if (existingLayer != null) { mLayer = existingLayer; } else { @@ -255,11 +231,17 @@ public void addToMap(RCTMGLMapView mapView) { } addStyles(); + if (mFilter != null) { + mHadFilter = true; + updateFilter(mFilter); + } } @Override public void removeFromMap(RCTMGLMapView mapView) { - mMap.removeLayer(mLayer); + if (mMap.getStyle() != null) { + mMap.getStyle().removeLayer(mLayer); + } } public abstract T makeLayer(); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLCircleLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLCircleLayer.java index c8d9a091f6..2bc3d8581b 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLCircleLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLCircleLayer.java @@ -2,8 +2,8 @@ import android.content.Context; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.CircleLayer; -import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; import com.mapbox.rctmgl.components.styles.RCTMGLStyleFactory; @@ -20,17 +20,13 @@ public RCTMGLCircleLayer(Context context) { } @Override - protected void updateFilter(Filter.Statement statement) { - mLayer.setFilter(statement); + protected void updateFilter(Expression expression) { + mLayer.setFilter(expression); } @Override public void addToMap(RCTMGLMapView mapView) { super.addToMap(mapView); - - if (mFilter != null) { - updateFilter(mFilter); - } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillExtrusionLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillExtrusionLayer.java index 5ae4a60abc..4f9864cdae 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillExtrusionLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillExtrusionLayer.java @@ -2,13 +2,11 @@ import android.content.Context; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; -import com.mapbox.mapboxsdk.style.layers.FillLayer; -import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; import com.mapbox.rctmgl.components.styles.RCTMGLStyleFactory; -import com.mapbox.rctmgl.components.styles.sources.RCTSource; /** * Created by nickitaliano on 9/15/17. @@ -22,17 +20,13 @@ public RCTMGLFillExtrusionLayer(Context context) { } @Override - protected void updateFilter(Filter.Statement statement) { - mLayer.setFilter(statement); + protected void updateFilter(Expression expression) { + mLayer.setFilter(expression); } @Override public void addToMap(RCTMGLMapView mapView) { super.addToMap(mapView); - - if (mFilter != null) { - updateFilter(mFilter); - } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillLayer.java index 6ed72fd7f0..ccb1d1fa99 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLFillLayer.java @@ -2,12 +2,11 @@ import android.content.Context; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.FillLayer; -import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; import com.mapbox.rctmgl.components.styles.RCTMGLStyleFactory; -import com.mapbox.rctmgl.components.styles.sources.RCTSource; /** * Created by nickitaliano on 9/8/17. @@ -21,17 +20,13 @@ public RCTMGLFillLayer(Context context) { } @Override - protected void updateFilter(Filter.Statement statement) { - mLayer.setFilter(statement); + protected void updateFilter(Expression expression) { + mLayer.setFilter(expression); } @Override public void addToMap(RCTMGLMapView mapView) { super.addToMap(mapView); - - if (mFilter != null) { - updateFilter(mFilter); - } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLLineLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLLineLayer.java index 6610f9ff34..5f1d4c6bcc 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLLineLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLLineLayer.java @@ -2,8 +2,7 @@ import android.content.Context; -import com.facebook.react.uimanager.UIManagerModule; -import com.mapbox.mapboxsdk.style.layers.Filter; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.LineLayer; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; @@ -21,17 +20,13 @@ public RCTMGLLineLayer(Context context) { } @Override - protected void updateFilter(Filter.Statement statement) { - mLayer.setFilter(statement); + protected void updateFilter(Expression expression) { + mLayer.setFilter(expression); } @Override public void addToMap(RCTMGLMapView mapView) { super.addToMap(mapView); - - if (mFilter != null) { - updateFilter(mFilter); - } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLRasterLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLRasterLayer.java index 1eedb51faf..c7c65325b3 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLRasterLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLRasterLayer.java @@ -2,9 +2,7 @@ import android.content.Context; -import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.RasterLayer; -import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; import com.mapbox.rctmgl.components.styles.RCTMGLStyleFactory; diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLSymbolLayer.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLSymbolLayer.java index 7c5ae05f12..95106790c9 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLSymbolLayer.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/layers/RCTMGLSymbolLayer.java @@ -1,19 +1,9 @@ package com.mapbox.rctmgl.components.styles.layers; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.View; -import com.facebook.react.views.view.ReactViewGroup; -import com.mapbox.mapboxsdk.style.layers.Filter; -import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; -import com.mapbox.rctmgl.R; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.RCTMGLStyle; import com.mapbox.rctmgl.components.styles.RCTMGLStyleFactory; @@ -30,17 +20,13 @@ public RCTMGLSymbolLayer(Context context) { } @Override - protected void updateFilter(Filter.Statement statement) { - mLayer.setFilter(statement); + protected void updateFilter(Expression expression) { + mLayer.setFilter(expression); } @Override public void addToMap(RCTMGLMapView mapView) { super.addToMap(mapView); - - if (mFilter != null) { - updateFilter(mFilter); - } } @Override diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/light/RCTMGLLight.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/light/RCTMGLLight.java index 5418f14166..1e35421536 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/light/RCTMGLLight.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/light/RCTMGLLight.java @@ -30,7 +30,7 @@ public RCTMGLLight(Context context) { @Override public void addToMap(RCTMGLMapView mapView) { mMap = mapView.getMapboxMap(); - setLight(mMap.getLight()); + setLight(mMap.getStyle().getLight()); } @Override @@ -42,7 +42,7 @@ public void setReactStyle(ReadableMap reactStyle) { mReactStyle = reactStyle; if (mMap != null) { - setLight(mMap.getLight()); + setLight(mMap.getStyle().getLight()); } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java index 62525a1720..8f07b0769d 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLImageSource.java @@ -1,25 +1,18 @@ package com.mapbox.rctmgl.components.styles.sources; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Movie; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Handler; import android.util.Log; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.geometry.LatLngQuad; import com.mapbox.mapboxsdk.style.sources.ImageSource; -import com.mapbox.rctmgl.utils.BitmapUtils; -import com.mapbox.services.commons.geojson.Feature; import java.net.MalformedURLException; import java.net.URL; -import java.util.List; -import java.util.Map; + +import android.net.Uri; /** * Created by nickitaliano on 11/29/17. diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLRasterSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLRasterSource.java index bad887d71e..8aae694553 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLRasterSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLRasterSource.java @@ -2,9 +2,9 @@ import android.content.Context; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.style.sources.RasterSource; import com.mapbox.mapboxsdk.style.sources.TileSet; -import com.mapbox.services.commons.geojson.Feature; /** * Created by nickitaliano on 9/25/17. diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java index 3d9287df92..2ac8a62b35 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLShapeSource.java @@ -1,33 +1,18 @@ package com.mapbox.rctmgl.components.styles.sources; import android.content.Context; -import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import com.facebook.react.bridge.ReadableMap; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; -import com.mapbox.rctmgl.components.annotation.RCTMGLCallout; -import com.mapbox.rctmgl.components.annotation.RCTMGLPointAnnotationOptions; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; -import com.mapbox.rctmgl.components.styles.layers.RCTLayer; import com.mapbox.rctmgl.events.FeatureClickEvent; -import com.mapbox.rctmgl.events.IEvent; import com.mapbox.rctmgl.utils.DownloadMapImageTask; -import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; -import com.mapbox.services.commons.geojson.Point; import java.net.URL; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -71,7 +56,7 @@ public void addToMap(final RCTMGLMapView mapView) { // add all images from drawables folder if (hasNativeImages()) { for (Map.Entry nativeImage : mNativeImages) { - map.addImage(nativeImage.getKey(), nativeImage.getValue().getBitmap()); + map.getStyle().addImage(nativeImage.getKey(), nativeImage.getValue().getBitmap()); } } @@ -100,15 +85,18 @@ public void removeFromMap(RCTMGLMapView mapView) { mRemoved = true; if (mMap == null) return; - if (hasImages()) { - for (Map.Entry image : mImages) { - mMap.removeImage(image.getKey()); + Style style = this.getStyle(); + if (style != null) { + if (hasImages()) { + for (Map.Entry image : mImages) { + style.removeImage(image.getKey()); + } } - } - if (hasNativeImages()) { - for (Map.Entry image : mNativeImages) { - mMap.removeImage(image.getKey()); + if (hasNativeImages()) { + for (Map.Entry image : mNativeImages) { + style.removeImage(image.getKey()); + } } } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLVectorSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLVectorSource.java index 0fe2c8c6f0..f8737e10a6 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLVectorSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTMGLVectorSource.java @@ -2,9 +2,9 @@ import android.content.Context; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.rctmgl.events.FeatureClickEvent; -import com.mapbox.services.commons.geojson.Feature; /** * Created by nickitaliano on 9/8/17. @@ -30,7 +30,7 @@ public void onPress(Feature feature) { @Override public VectorSource makeSource() { if (isDefaultSource(mID)) { - return (VectorSource)mMap.getSource(DEFAULT_ID); + return (VectorSource)mMap.getStyle().getSource(DEFAULT_ID); } return new VectorSource(mID, mURL); } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java index 8b90d19d77..34cd16ef0b 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/components/styles/sources/RCTSource.java @@ -1,16 +1,18 @@ package com.mapbox.rctmgl.components.styles.sources; import android.content.Context; +import android.support.annotation.NonNull; import android.view.View; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; +import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.rctmgl.components.AbstractMapFeature; import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; import com.mapbox.rctmgl.components.styles.layers.RCTLayer; -import com.mapbox.services.commons.geojson.Feature; import java.util.ArrayList; import java.util.HashMap; @@ -113,24 +115,30 @@ public void addToMap(RCTMGLMapView mapView) { mMapView = mapView; mMap = mapView.getMapboxMap(); - T existingSource = mMap.getSourceAs(mID); - if (existingSource != null) { - mSource = existingSource; - } else { - mSource = makeSource(); - mMap.addSource(mSource); - } - - if (mQueuedLayers != null && mQueuedLayers.size() > 0) { // first load - for (int i = 0; i < mQueuedLayers.size(); i++) { - addLayerToMap(mQueuedLayers.get(i), i); - } - mQueuedLayers = null; - } else if (mLayers.size() > 0) { // handles the case of switching style url, but keeping layers on map - for (int i = 0; i < mLayers.size(); i++) { - addLayerToMap(mLayers.get(i), i); + mMap.getStyle(new Style.OnStyleLoaded() { + public void onStyleLoaded(@NonNull Style style) { + T existingSource = mMap.getStyle().getSourceAs(mID); + if (existingSource != null) { + mSource = existingSource; + } else { + mSource = makeSource(); + mMap.getStyle().addSource(mSource); + } + + if (mQueuedLayers != null && mQueuedLayers.size() > 0) { // first load + for (int i = 0; i < mQueuedLayers.size(); i++) { + addLayerToMap(mQueuedLayers.get(i), i); + } + mQueuedLayers = null; + } else if (mLayers.size() > 0) { // handles the case of switching style url, but keeping layers on map + for (int i = 0; i < mLayers.size(); i++) { + addLayerToMap(mLayers.get(i), i); + } + } } - } + }); + + } @Override @@ -144,8 +152,8 @@ public void removeFromMap(RCTMGLMapView mapView) { if (mQueuedLayers != null) { mQueuedLayers.clear(); } - if (mMap != null && mSource != null) { - mMap.removeSource(mSource); + if (mMap != null && mSource != null && mMap.getStyle() != null) { + mMap.getStyle().removeSource(mSource); } } @@ -201,6 +209,11 @@ protected void removeLayerFromMap(RCTLayer layer, int childPosition) { } } + public Style getStyle() { + if (mMap == null) return null; + return mMap.getStyle(); + } + public abstract T makeSource(); public abstract void onPress(Feature feature); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/AbstractEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/AbstractEvent.java index cfc4492d3f..9d027ebf89 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/AbstractEvent.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/AbstractEvent.java @@ -51,7 +51,10 @@ public long getTimestamp() { public WritableMap toJSON() { WritableMap map = Arguments.createMap(); map.putString("type", getType()); - map.putMap("payload", getPayload()); + + WritableMap payloadClone = Arguments.createMap(); + payloadClone.merge(getPayload()); + map.putMap("payload", payloadClone); return map; } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java new file mode 100644 index 0000000000..b45473c8e5 --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/EventEmitter.java @@ -0,0 +1,58 @@ +package com.mapbox.rctmgl.events; + +import android.util.Log; + +import com.facebook.react.ReactApplication; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.core.RCTNativeAppEventEmitter; +import com.facebook.react.uimanager.events.RCTEventEmitter; + + +public class EventEmitter { + public static final String LOG_TAG = EventEmitter.class.getSimpleName(); + + + private static ReactContext getCurrentReactContext(ReactApplicationContext reactApplicationContext) { + if (reactApplicationContext.getApplicationContext() instanceof ReactApplication) { + ReactApplication reactApplication = ((ReactApplication) reactApplicationContext + .getApplicationContext()); + + return reactApplication + .getReactNativeHost() + .getReactInstanceManager() + .getCurrentReactContext(); + } else { + Log.d(LOG_TAG, "getApplicationContext() application doesn't implement ReactApplication"); + return reactApplicationContext; + } + } + + + public static RCTNativeAppEventEmitter getModuleEmitter(ReactApplicationContext reactApplicationContext) { + RCTNativeAppEventEmitter emitter = null; + + try { + emitter = getCurrentReactContext(reactApplicationContext) + .getJSModule(RCTNativeAppEventEmitter.class); + } catch (NullPointerException e) { + Log.d(LOG_TAG, e.getLocalizedMessage()); + } + + return emitter; + } + + public static RCTEventEmitter getViewEmitter(ReactApplicationContext reactApplicationContext) { + RCTEventEmitter emitter = null; + + try { + emitter = getCurrentReactContext(reactApplicationContext) + .getJSModule(RCTEventEmitter.class); + } catch (NullPointerException e) { + Log.d(LOG_TAG, e.getLocalizedMessage()); + } + + return emitter; + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/FeatureClickEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/FeatureClickEvent.java index 7ccf91e18b..adf26013fb 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/FeatureClickEvent.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/FeatureClickEvent.java @@ -1,22 +1,12 @@ package com.mapbox.rctmgl.events; -import android.os.StrictMode; import android.view.View; -import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.google.gson.JsonObject; -import com.mapbox.rctmgl.events.AbstractEvent; +import com.mapbox.geojson.Feature; import com.mapbox.rctmgl.events.constants.EventKeys; import com.mapbox.rctmgl.events.constants.EventTypes; -import com.mapbox.rctmgl.utils.ConvertUtils; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; - -import java.util.HashMap; -import java.util.Map; /** * Created by nickitaliano on 11/7/17. diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java new file mode 100644 index 0000000000..0eefe33413 --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/LocationEvent.java @@ -0,0 +1,92 @@ +package com.mapbox.rctmgl.events; + +import android.location.Location; +import android.support.annotation.NonNull; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; +import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; +import com.mapbox.rctmgl.events.constants.EventKeys; +import com.mapbox.rctmgl.events.constants.EventTypes; + +import java.util.UUID; + +public class LocationEvent implements IEvent { + private UUID uuid; + private RCTMGLMapView mapView; + private Location location; + + public LocationEvent(@NonNull Location location, RCTMGLMapView mapView) { + this.mapView = mapView; + this.location = location; + this.uuid = UUID.randomUUID(); + } + + public LocationEvent(Location location) { + this(location, null); + } + + @Override + public int getID() { + if (mapView != null) { + return mapView.getId(); + } + return -1; + } + + public UUID getUUID() { + return uuid; + } + + @Override + public String getKey() { + return EventKeys.USER_LOCATION_UPDATE; + } + + @Override + public String getType() { + return EventTypes.USER_LOCATION_UPDATED; + } + + @Override + public long getTimestamp() { + return System.currentTimeMillis(); + } + + @Override + public boolean equals(IEvent event) { + LocationEvent other = (LocationEvent) event; + return getUUID().equals(other.getUUID()); + } + + public boolean equals(LocationEvent event) { + return uuid.equals(event.getUUID()); + } + + @Override + public WritableMap getPayload() { + WritableMap positionProperties = new WritableNativeMap(); + WritableMap coords = new WritableNativeMap(); + + coords.putDouble("longitude", location.getLongitude()); + coords.putDouble("latitude", location.getLatitude()); + coords.putDouble("altitude", location.getAltitude()); + coords.putDouble("accuracy", location.getAccuracy()); + coords.putDouble("heading", location.getBearing()); + coords.putDouble("speed", location.getSpeed()); + + positionProperties.putMap("coords", coords); + positionProperties.putDouble("timestamp", location.getTime()); + + return positionProperties; + } + + @Override + public WritableMap toJSON() { + WritableMap map = Arguments.createMap(); + map.putString("type", getType()); + map.putMap("payload", getPayload()); + return map; + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/MapChangeEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/MapChangeEvent.java index 413767d638..471cd410f1 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/MapChangeEvent.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/MapChangeEvent.java @@ -30,6 +30,9 @@ public String getKey() { @Override public WritableMap getPayload() { - return mPayload; + // FMTODO + WritableMap payloadClone = Arguments.createMap(); + payloadClone.merge(mPayload); + return payloadClone; } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/PointAnnotationClickEvent.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/PointAnnotationClickEvent.java index 7ca3e8f40d..cd92579eee 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/PointAnnotationClickEvent.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/PointAnnotationClickEvent.java @@ -6,7 +6,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; -import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.plugins.markerview.MarkerView; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.rctmgl.components.annotation.RCTMGLPointAnnotation; import com.mapbox.rctmgl.events.constants.EventKeys; diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/constants/EventKeys.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/constants/EventKeys.java index 373f60d532..3606d9854a 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/constants/EventKeys.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/events/constants/EventKeys.java @@ -24,6 +24,9 @@ public class EventKeys { public static final String VECTOR_SOURCE_LAYER_CLICK = ns("vectorsource.layer.pressed"); public static final String RASTER_SOURCE_LAYER_CLICK = ns("rastersource.layer.pressed"); + // location events + public static final String USER_LOCATION_UPDATE = ns("user.location.update"); + private static String ns(String name) { return String.format("%s.%s", NAMESPACE, name); } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java index 4aafea0b0f..f6d58d2aa5 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/LocationManager.java @@ -2,116 +2,99 @@ import android.content.Context; import android.location.Location; +import android.os.Looper; import android.util.Log; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.plugins.locationlayer.CompassListener; -import com.mapbox.rctmgl.components.mapview.RCTMGLMapView; -import com.mapbox.services.android.location.MockLocationEngine; -import com.mapbox.services.android.telemetry.location.GoogleLocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; -import com.mapbox.services.android.telemetry.location.LostLocationEngine; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; -import com.mapbox.services.api.directions.v5.DirectionsCriteria; -import com.mapbox.services.api.directions.v5.MapboxDirections; -import com.mapbox.services.api.directions.v5.models.DirectionsResponse; -import com.mapbox.services.api.directions.v5.models.DirectionsRoute; -import com.mapbox.services.commons.models.Position; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineCallback; +/* +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEnginePriority; +*/ + +import com.mapbox.android.core.location.LocationEngineProvider; +import com.mapbox.android.core.location.LocationEngineRequest; +import com.mapbox.android.core.location.LocationEngineResult; +import com.mapbox.android.core.permissions.PermissionsManager; + +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; +import java.util.Locale; /** * Created by nickitaliano on 12/12/17. */ @SuppressWarnings({"MissingPermission"}) -public class LocationManager implements LocationEngineListener { - // TODO: Add JS API to allow easier UI testing - private static final boolean MOCK_LOCATION = false; - private static final double[] originCoord = new double[]{ -74.135319, 40.795952 }; - private static final double[] destCoord = new double[]{ -74.134510, 40.787626 }; +public class LocationManager implements LocationEngineCallback { + static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; + static final long DEFAULT_INTERVAL_MILLIS = 1000; + + public static final String LOG_TAG = LocationManager.class.getSimpleName(); private LocationEngine locationEngine; - private OnUserLocationChange userLocationListener; private Context context; + private List listeners = new ArrayList<>(); + + private boolean isActive = false; + private Location lastLocation = null; + + private LocationEngineRequest locationEngineRequest = null; + + private static WeakReference INSTANCE = null; + + public static LocationManager getInstance(Context context) { + if (INSTANCE == null) { + INSTANCE = new WeakReference<>(new LocationManager(context)); + } + return INSTANCE.get(); + } public interface OnUserLocationChange { void onLocationChange(Location location); } - public LocationManager(Context context) { + private LocationManager(Context context) { this.context = context; + locationEngine = LocationEngineProvider.getBestLocationEngine(context.getApplicationContext()); + // locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngineRequest = new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) + .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) + .build(); + // locationEngine.addLocationEngineListener(this); + //locationEngine.setFastestInterval(1000); } - public void enable() { - if (!PermissionsManager.areLocationPermissionsGranted(context)) { - return; + public void addLocationListener(OnUserLocationChange listener) { + if (!listeners.contains(listener)) { + listeners.add(listener); } + } - if (locationEngine == null) { - if (MOCK_LOCATION) { - Position origin = Position.fromCoordinates(originCoord); - Position dest = Position.fromCoordinates(destCoord); - - List positionList = new ArrayList<>(); - positionList.add(origin); - positionList.add(dest); - - MapboxDirections client = new MapboxDirections.Builder<>() - .setAccessToken(Mapbox.getAccessToken()) - .setProfile(DirectionsCriteria.PROFILE_DRIVING) - .setSteps(true) - .setCoordinates(positionList) - .build(); - - final LocationManager self = this; - client.enqueueCall(new Callback() { - @Override - public void onResponse(Call call, Response response) { - DirectionsRoute route = response.body().getRoutes().get(0); - - MockLocationEngine mockLocationEngine = new MockLocationEngine(); - mockLocationEngine.setRoute(route); - - locationEngine = mockLocationEngine; - locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); - locationEngine.addLocationEngineListener(self); - locationEngine.setFastestInterval(1000); - locationEngine.activate(); - } - - @Override - public void onFailure(Call call, Throwable t) { - - } - }); - } else { - locationEngine = LostLocationEngine.getLocationEngine(context); - locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); - locationEngine.addLocationEngineListener(this); - locationEngine.setFastestInterval(1000); - locationEngine.activate(); - } - } else { - locationEngine.activate(); + public void removeLocationListener(OnUserLocationChange listener) { + if (listeners.contains(listener)) { + listeners.remove(listener); } } - public void disable() { - if (locationEngine == null) { + public void enable() { + if (!PermissionsManager.areLocationPermissionsGranted(context)) { return; } - locationEngine.removeLocationUpdates(); - locationEngine.deactivate(); + locationEngine.requestLocationUpdates( + locationEngineRequest, + this, + Looper.getMainLooper() + ); + isActive = true; + } + + public void disable() { + locationEngine.removeLocationUpdates(this); + isActive = false; } public void dispose() { @@ -119,46 +102,50 @@ public void dispose() { return; } disable(); - locationEngine.removeLocationUpdates(); - locationEngine.removeLocationEngineListener(this); + locationEngine.removeLocationUpdates(this); } - public void setOnLocationChangeListener(OnUserLocationChange listener) { - this.userLocationListener = listener; + public boolean isActive() { + return locationEngine != null && this.isActive; } - public boolean isActive() { + public Location getLastKnownLocation() { if (locationEngine == null) { - return false; + return null; } - return locationEngine.isConnected(); + return lastLocation; } - public Location getLastKnownLocation() { + + public void getLastKnownLocation(LocationEngineCallback callback) { if (locationEngine == null) { - return null; + callback.onFailure(new Exception("LocationEngine not initialized")); } - return locationEngine.getLastLocation(); + + locationEngine.getLastLocation(callback); } public LocationEngine getEngine() { return locationEngine; } - @Override - public void onConnected() { - locationEngine.requestLocationUpdates(); + public void onLocationChanged(Location location) { + lastLocation = location; + Log.d(LOG_TAG, String.format(Locale.ENGLISH, "Tick [%f, %f]", location.getLongitude(), location.getLatitude())); + Log.d(LOG_TAG, String.format(Locale.ENGLISH, "Listener count %d", listeners.size())); - Location lastKnownLocation = getLastKnownLocation(); - if (lastKnownLocation != null) { - onLocationChanged(lastKnownLocation); + for (OnUserLocationChange listener : listeners) { + listener.onLocationChange(location); } } @Override - public void onLocationChanged(Location location) { - if (this.userLocationListener != null) { - this.userLocationListener.onLocationChange(location); - } + public void onFailure(Exception exception) { + // FMTODO handle this. + } + + @Override + public void onSuccess(LocationEngineResult result) { + onLocationChanged(result.getLastLocation()); } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/UserTrackingMode.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/UserTrackingMode.java index c2470588f9..51a892656c 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/UserTrackingMode.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/location/UserTrackingMode.java @@ -1,6 +1,10 @@ package com.mapbox.rctmgl.location; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerMode; +/* +import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode; +*/ + +import com.mapbox.mapboxsdk.location.modes.RenderMode; /** * Created by nickitaliano on 12/13/17. @@ -14,15 +18,15 @@ public class UserTrackingMode { public static int getMapLayerMode(int mode, boolean isShowUserLocation) { if (!isShowUserLocation) { - return LocationLayerMode.NONE; + return -1; } else if (mode == NONE) { - return LocationLayerMode.TRACKING; + return -1; } else if (mode == FollowWithCourse) { - return LocationLayerMode.NAVIGATION; + return RenderMode.GPS; } else if (mode == FollowWithHeading) { - return LocationLayerMode.COMPASS; + return RenderMode.COMPASS; } else { - return LocationLayerMode.TRACKING; + return RenderMode.NORMAL; } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLLocationModule.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLLocationModule.java new file mode 100644 index 0000000000..9ba9aa5174 --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLLocationModule.java @@ -0,0 +1,129 @@ +package com.mapbox.rctmgl.modules; + +import android.location.Location; +import android.support.annotation.NonNull; + +import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.modules.core.RCTNativeAppEventEmitter; +import com.mapbox.android.core.location.LocationEngineCallback; +import com.mapbox.android.core.location.LocationEngineResult; +import com.mapbox.rctmgl.events.EventEmitter; +import com.mapbox.rctmgl.events.IEvent; +import com.mapbox.rctmgl.events.LocationEvent; +import com.mapbox.rctmgl.location.LocationManager; + +public class RCTMGLLocationModule extends ReactContextBaseJavaModule { + public static final String REACT_CLASS = RCTMGLLocationModule.class.getSimpleName(); + public static final String LOCATION_UPDATE = "MapboxUserLocationUpdate"; + + private boolean isEnabled; + private boolean isPaused; + + private LocationManager locationManager; + + private LifecycleEventListener lifecycleEventListener = new LifecycleEventListener() { + @Override + public void onHostResume() { + if (isEnabled) { + startLocationManager(); + } + } + + @Override + public void onHostPause() { + pauseLocationManager(); + } + + @Override + public void onHostDestroy() { + startLocationManager(); + } + }; + + private LocationManager.OnUserLocationChange onUserLocationChangeCallback = new LocationManager.OnUserLocationChange() { + @Override + public void onLocationChange(Location location) { + LocationEvent locationEvent = new LocationEvent(location); + + RCTNativeAppEventEmitter emitter = EventEmitter.getModuleEmitter(getReactApplicationContext()); + if (emitter != null) { + emitter.emit(LOCATION_UPDATE, locationEvent.getPayload()); + } + } + }; + + public RCTMGLLocationModule(ReactApplicationContext reactContext) { + super(reactContext); + locationManager = LocationManager.getInstance(reactContext); + reactContext.addLifecycleEventListener(lifecycleEventListener); + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @ReactMethod + public void start() { + isEnabled = true; + startLocationManager(); + } + + @ReactMethod + public void stop() { + stopLocationManager(); + } + + @ReactMethod + public void pause() { + pauseLocationManager(); + } + + @ReactMethod + public void getLastKnownLocation(final Promise promise) { + locationManager.getLastKnownLocation( + new LocationEngineCallback() { + public void onSuccess(LocationEngineResult result) { + Location location = result.getLastLocation(); + if (result.getLastLocation() != null) { + LocationEvent locationEvent = new LocationEvent(location); + promise.resolve(locationEvent.getPayload()); + } else { + promise.resolve(null); + } + } + public void onFailure(@NonNull Exception exception) { + promise.reject(exception); + } + } + ); + } + + private void startLocationManager() { + locationManager.addLocationListener(onUserLocationChangeCallback); + locationManager.enable(); + isPaused = false; + } + + private void pauseLocationManager() { + if (isPaused) { + return; + } + locationManager.disable(); + isPaused = true; + } + + private void stopLocationManager() { + if (!isEnabled) { + return; + } + locationManager.removeLocationListener(onUserLocationChangeCallback); + locationManager.dispose(); + isEnabled = false; + isPaused = false; + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLModule.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLModule.java index 3e48b3afa3..fd5ec6e4d8 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLModule.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLModule.java @@ -10,11 +10,9 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.MapBuilder; +import com.mapbox.mapboxsdk.maps.TelemetryDefinition; import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.offline.OfflineRegion; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerMode; -import com.mapbox.mapboxsdk.storage.FileSource; +// import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.rctmgl.components.camera.constants.CameraMode; import com.mapbox.rctmgl.components.styles.RCTMGLStyleValue; @@ -22,7 +20,8 @@ import com.mapbox.rctmgl.events.constants.EventTypes; import com.mapbox.rctmgl.location.UserLocationVerticalAlignment; import com.mapbox.rctmgl.location.UserTrackingMode; -import com.mapbox.services.android.telemetry.MapboxTelemetry; +import com.mapbox.mapboxsdk.maps.Style; + import java.util.HashMap; import java.util.Map; @@ -237,6 +236,10 @@ public Map getConstants() { offlineModuleCallbackNames.put("Error", RCTMGLOfflineModule.OFFLINE_ERROR); offlineModuleCallbackNames.put("Progress", RCTMGLOfflineModule.OFFLINE_PROGRESS); + // location module callback names + Map locationModuleCallbackNames = new HashMap<>(); + locationModuleCallbackNames.put("Update", RCTMGLLocationModule.LOCATION_UPDATE); + return MapBuilder.builder() .put("StyleURL", styleURLS) .put("EventTypes", eventTypes) @@ -268,6 +271,7 @@ public Map getConstants() { .put("LightAnchor", lightAnchor) .put("OfflinePackDownloadState", offlinePackDownloadStates) .put("OfflineCallbackName", offlineModuleCallbackNames) + .put("LocationCallbackName", locationModuleCallbackNames) .build(); } @@ -293,13 +297,9 @@ public void setTelemetryEnabled(final boolean telemetryEnabled) { mReactContext.runOnUiQueueThread(new Runnable() { @Override public void run() { - MapboxTelemetry.getInstance().setTelemetryEnabled(telemetryEnabled); + TelemetryDefinition telemetry = Mapbox.getTelemetry(); + telemetry.setUserTelemetryRequestState(telemetryEnabled); } }); } - - @ReactMethod - public void isTelemetryEnabled(Promise promise) { - promise.resolve(MapboxTelemetry.getInstance().isTelemetryEnabled()); - } } diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java index 4dcf5ecbc2..52004024d7 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLOfflineModule.java @@ -14,7 +14,8 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.modules.core.RCTNativeAppEventEmitter; -import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.geojson.FeatureCollection; +// import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.offline.OfflineManager; import com.mapbox.mapboxsdk.offline.OfflineRegion; @@ -28,7 +29,7 @@ import com.mapbox.rctmgl.events.constants.EventTypes; import com.mapbox.rctmgl.utils.ConvertUtils; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.FeatureCollection; +import com.mapbox.mapboxsdk.maps.Style; import org.json.JSONException; import org.json.JSONObject; diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLSnapshotModule.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLSnapshotModule.java index 5454b84be4..ebb855b77e 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLSnapshotModule.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/modules/RCTMGLSnapshotModule.java @@ -15,21 +15,16 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.rctmgl.utils.BitmapUtils; import com.mapbox.rctmgl.utils.GeoJSONUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; @@ -117,7 +112,7 @@ private MapSnapshotter.Options getOptions(ReadableMap jsOptions) { } else { Feature centerPoint = Feature.fromJson(jsOptions.getString("centerCoordinate")); CameraPosition cameraPosition = new CameraPosition.Builder() - .target(GeoJSONUtils.toLatLng((Point) centerPoint.getGeometry())) + .target(GeoJSONUtils.toLatLng((Point) centerPoint.geometry())) .tilt(jsOptions.getDouble("pitch")) .bearing(jsOptions.getDouble("heading")) .zoom(jsOptions.getDouble("zoomLevel")) diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java index fbe005431b..a66f95f403 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/BitmapUtils.java @@ -55,6 +55,16 @@ public static Bitmap getBitmapFromURL(String url, BitmapFactory.Options options) return bitmap; } + public static Bitmap getBitmapFromPath(String path, BitmapFactory.Options options) { + try { + Bitmap bitmap = BitmapFactory.decodeFile(path, options); + return bitmap; + } catch (Exception e) { + Log.w(LOG_TAG, e.getLocalizedMessage()); + return Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); // Returns a transparent bitmap + } + } + public static Bitmap getBitmapFromResource(Context context, String resourceName, BitmapFactory.Options options) { Resources resources = context.getResources(); int resID = resources.getIdentifier(resourceName, "drawable", context.getPackageName()); diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java index 55a7046a7f..d713b90194 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ConvertUtils.java @@ -14,11 +14,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Point; import java.text.NumberFormat; import java.text.ParseException; diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java index 74cdf7d6d6..d470b04946 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/DownloadMapImageTask.java @@ -5,6 +5,7 @@ import android.os.AsyncTask; import android.util.Log; +import com.facebook.common.logging.FLog; import com.mapbox.mapboxsdk.maps.MapboxMap; import java.util.AbstractMap; @@ -48,10 +49,17 @@ protected final List> doInBackground(Map.Entry(object.getKey(), bitmap)); } else { // local asset required from JS require('image.png') or import icon from 'image.png' while in release mode Bitmap bitmap = BitmapUtils.getBitmapFromResource(mContext, uri, null); - images.add(new AbstractMap.SimpleEntry(object.getKey(), bitmap)); + if (bitmap != null) { + images.add(new AbstractMap.SimpleEntry(object.getKey(), bitmap)); + } else { + FLog.e(LOG_TAG, "Failed to load bitmap from: " + uri); + } } } @@ -65,7 +73,9 @@ protected void onPostExecute(List> images) { } for (Map.Entry image : images) { - mMap.addImage(image.getKey(), image.getValue()); + if (mMap.getStyle() != null) { + mMap.getStyle().addImage(image.getKey(), image.getValue()); + } } if (mCallback != null) { diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ExpressionParser.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ExpressionParser.java new file mode 100644 index 0000000000..4aa56a450f --- /dev/null +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/ExpressionParser.java @@ -0,0 +1,78 @@ +package com.mapbox.rctmgl.utils; + +import com.facebook.react.bridge.Dynamic; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.mapbox.mapboxsdk.style.expressions.Expression; + +import java.util.Locale; + +public class ExpressionParser { + static final String TYPE_STRING = "string"; + static final String TYPE_ARRAY = "array"; + static final String TYPE_NUMBER = "number"; + static final String TYPE_MAP = "hashmap"; + static final String TYPE_BOOL = "boolean"; + + public static Expression from(ReadableArray rawExpressions) { + StringBuilder builder = new StringBuilder(); + + if (rawExpressions == null || rawExpressions.size() == 0) { + return null; + } + + builder.append("["); + for (int i = 0; i < rawExpressions.size(); i++) { + ReadableMap item = rawExpressions.getMap(i); + + String curExpression = stringExpression(item); + if (!curExpression.isEmpty()) { + builder.append(curExpression); + + if (i < rawExpressions.size() - 1) { + builder.append(","); + } + } + } + builder.append("]"); + + return Expression.raw(builder.toString()); + } + + public static Expression from(ReadableMap rawExpression) { + return Expression.raw("[" + stringExpression(rawExpression) + "]"); + } + + private static String stringExpression(ReadableMap item) { + String expression = ""; + String type = item.getString("type"); + + if (TYPE_STRING.equals(type)) { + String value = item.getString("value"); + expression = String.format(Locale.ENGLISH, "\"%s\"", value); + } else if (TYPE_NUMBER.equals(type)) { + Double value = item.getDouble("value"); + expression = String.format(Locale.ENGLISH, "%f", value); + } else if (TYPE_BOOL.equals(type)) { + Boolean value = item.getBoolean("value"); + expression = String.format(Locale.ENGLISH, "%b", value); + } else if (TYPE_ARRAY.equals(type)) { + ReadableArray entries = item.getArray("value"); + + expression += "["; + + for (int i = 0; i < entries.size(); i++) { + String entryExpression = stringExpression(entries.getMap(i)); + expression += entryExpression; + + if (i < entries.size() - 1) { + expression += ","; + } + } + + expression += "]"; + } + + return expression; + } +} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/FilterParser.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/FilterParser.java deleted file mode 100644 index 97e1c6ed2e..0000000000 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/FilterParser.java +++ /dev/null @@ -1,240 +0,0 @@ -package com.mapbox.rctmgl.utils; - -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.mapbox.mapboxsdk.style.layers.Filter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Created by nickitaliano on 10/3/17. - */ - -public class FilterParser { - public static final Set FILTER_OPS = new HashSet(Arrays.asList( - "all", - "any", - "none", - "in", - "!in", - "<=", - "<", - ">=", - ">", - "!=", - "==", - "has", - "!has" - )); - - public static final int COMPOUND_FILTER_ALL = 3; - public static final int COMPOUND_FILTER_ANY = 2; - public static final int COMPOUND_FILTER_NONE = 1; - - public static FilterList getFilterList(ReadableArray readableArray) { - List> rawFilterList = new ArrayList<>(); - - for (int i = 0; i < readableArray.size(); i++) { - ReadableMap readableMap = readableArray.getMap(i); - - Map filterItem = new HashMap<>(); - filterItem.put("type", readableMap.getString("type")); - - switch (readableMap.getString("type")) { - case "boolean": - filterItem.put("value", readableMap.getBoolean("value")); - break; - case "number": - filterItem.put("value", readableMap.getDouble("value")); - break; - default: - filterItem.put("value", readableMap.getString("value")); - break; - } - - rawFilterList.add(filterItem); - } - - return new FilterList(rawFilterList); - } - - public static Filter.Statement parse(FilterList filterList) { - Filter.Statement completeStatement = null; - - int compound = 0; - - // no filter - if (filterList == null || filterList.size() < 2) { - return null; - } - - // peak ahead to see if this is a compound filter or not - switch (filterList.getString(0)) { - case "all": - compound = COMPOUND_FILTER_ALL; - break; - case "any": - compound = COMPOUND_FILTER_ANY; - break; - case "none": - compound = COMPOUND_FILTER_NONE; - break; - } - - List compoundStatement = new ArrayList<>(); - - if (compound > 0) { - filterList.removeFirst(); - } - - while (!filterList.isEmpty()) { - - int posPointer = 1; - - while (posPointer < filterList.size()) { - if (FILTER_OPS.contains(filterList.getString(posPointer))) { - break; - } - posPointer++; - } - - // TODO: throw useful exceptions here when popping from list fails due to an invalid filter - - FilterList currentFilters = filterList.subList(posPointer); - filterList.removeAll(currentFilters); - - String op = currentFilters.getString(0); - currentFilters.removeFirst(); - - Filter.Statement statement = null; - String key = currentFilters.getString(0); - currentFilters.removeFirst(); - - Object[] values = getObjectValues(currentFilters); - - switch (op) { - case "in": - statement = Filter.in(key, values); - break; - case "!in": - statement = Filter.notIn(key, values); - break; - case "<=": - statement = Filter.lte(key, values[0]); - break; - case "<": - statement = Filter.lt(key, values[0]); - break; - case ">=": - statement = Filter.gte(key, values[0]); - break; - case ">": - statement = Filter.gt(key, values[0]); - break; - case "!=": - statement = Filter.neq(key, values[0]); - break; - case "==": - statement = Filter.eq(key, values[0]); - break; - case "has": - statement = Filter.has(key); - break; - case "!has": - statement = Filter.notHas(key); - break; - } - - if (compound > 0) { - compoundStatement.add(statement); - } else { - completeStatement = statement; - } - } - - if (compound > 0) { - Filter.Statement[] statements = new Filter.Statement[compoundStatement.size()]; - compoundStatement.toArray(statements); - - switch (compound) { - case COMPOUND_FILTER_ALL: - return Filter.all(statements); - case COMPOUND_FILTER_ANY: - return Filter.any(statements); - case COMPOUND_FILTER_NONE: - return Filter.none(statements); - } - } - - return completeStatement; - } - - private static Object[] getObjectValues(FilterList filterList) { - List objects = new ArrayList<>(); - - for (int i = 0; i < filterList.size(); i++) { - Map item = filterList.get(i); - objects.add(item.get("value")); - } - - return objects.toArray(new Object[objects.size()]); - } - - public static class FilterList { - private List> mFilterList; - - FilterList(List> filterList) { - mFilterList = new ArrayList<>(filterList); - } - - Object removeFirst() { - Map item = mFilterList.remove(0); - return item.get("value"); - } - - Map get(int index) { - Map item = mFilterList.get(index); - - if (item == null) { - return null; - } - - return item; - } - - String getString(int index) { - Map item = get(index); - - if (!item.get("type").equals("string")) { - return ""; - } - - return (String)item.get("value"); - } - - int size() { - return mFilterList.size(); - } - - boolean isEmpty() { - return mFilterList.isEmpty(); - } - - FilterList subList(int lastPosition) { - List> slice = mFilterList.subList(0, lastPosition); - return new FilterList(slice); - } - - void removeAll(FilterList itemsToRemove) { - for (int i = 0; i < itemsToRemove.size(); i++) { - mFilterList.remove(itemsToRemove.get(i)); - } - } - } -} diff --git a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/GeoJSONUtils.java b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/GeoJSONUtils.java index 1d66c85502..69c9172381 100644 --- a/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/GeoJSONUtils.java +++ b/android/rctmgl/src/main/java/com/mapbox/rctmgl/utils/GeoJSONUtils.java @@ -6,17 +6,21 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Geometry; +import com.mapbox.geojson.GeometryCollection; +import com.mapbox.geojson.LineString; +import com.mapbox.geojson.MultiPoint; +import com.mapbox.geojson.Point; +import com.mapbox.geojson.Polygon; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.LatLngQuad; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.FeatureCollection; -import com.mapbox.services.commons.geojson.Geometry; -import com.mapbox.services.commons.geojson.LineString; -import com.mapbox.services.commons.geojson.Point; -import com.mapbox.services.commons.geojson.Polygon; -import com.mapbox.services.commons.models.Position; +import com.mapbox.mapboxsdk.style.light.Position; +import com.mapbox.turf.TurfMeasurement; +import java.util.ArrayList; import java.util.List; /** @@ -27,19 +31,19 @@ public class GeoJSONUtils { public static WritableMap fromFeature(Feature feature) { WritableMap map = Arguments.createMap(); map.putString("type", "Feature"); - map.putString("id", feature.getId()); + map.putString("id", feature.id()); - WritableMap geometry = fromGeometry(feature.getGeometry()); + WritableMap geometry = fromGeometry(feature.geometry()); map.putMap("geometry", geometry); - WritableMap properties = ConvertUtils.toWritableMap(feature.getProperties()); + WritableMap properties = ConvertUtils.toWritableMap(feature.properties()); map.putMap("properties", properties); return map; } public static WritableMap fromGeometry(Geometry geometry) { - final String type = geometry.getType(); + final String type = geometry.type(); switch (type) { case "Point": @@ -75,16 +79,15 @@ public static WritableMap fromPolygon(Polygon polygon) { } public static WritableArray getCoordinates(Point point) { - double[] coords = point.getCoordinates().getCoordinates(); - return Arguments.fromArray(coords); + return Arguments.fromArray(pointToDoubleArray(point)); } public static WritableArray getCoordinates(LineString lineString) { WritableArray array = Arguments.createArray(); - List positions = lineString.getCoordinates(); - for (Position position : positions) { - array.pushArray(Arguments.fromArray(position.getCoordinates())); + List points = lineString.coordinates(); + for (Point point : points) { + array.pushArray(Arguments.fromArray(pointToDoubleArray(point))); } return array; @@ -93,12 +96,16 @@ public static WritableArray getCoordinates(LineString lineString) { public static WritableArray getCoordinates(Polygon polygon) { WritableArray array = Arguments.createArray(); - List> positions = polygon.getCoordinates(); - for (List curPositions : positions) { + List> points = polygon.coordinates(); + if (points == null) { + return array; + } + + for (List curPoint : points) { WritableArray innerArray = Arguments.createArray(); - for (Position position : curPositions) { - innerArray.pushArray(Arguments.fromArray(position.getCoordinates())); + for (Point point : curPoint) { + innerArray.pushArray(Arguments.fromArray(pointToDoubleArray(point))); } array.pushArray(innerArray); @@ -134,13 +141,7 @@ public static LatLng toLatLng(Point point) { if (point == null) { return null; } - - Position position = point.getCoordinates(); - if (position == null) { - return null; - } - - return new LatLng(position.getLatitude(), position.getLongitude()); + return new LatLng(point.latitude(), point.longitude()); } public static LatLng toLatLng(ReadableArray coordinates) { @@ -155,7 +156,7 @@ public static Point toPointGeometry(String featureJSONString) { if (feature == null) { return null; } - return (Point)feature.getGeometry(); + return (Point)feature.geometry(); } public static WritableArray fromLatLngBounds(LatLngBounds latLngBounds) { @@ -169,18 +170,21 @@ public static WritableArray fromLatLngBounds(LatLngBounds latLngBounds) { return array; } - public static LatLngBounds toLatLngBounds(FeatureCollection featureCollection) { - List features = featureCollection.getFeatures(); - - if (features.size() != 2) { - return null; + private static GeometryCollection toGeometryCollection(List features) { + ArrayList geometries = new ArrayList<>(); + geometries.ensureCapacity(features.size()); + for (Feature feature : features) { + geometries.add(feature.geometry()); } + return GeometryCollection.fromGeometries(geometries); + } + + public static LatLngBounds toLatLngBounds(FeatureCollection featureCollection) { + List features = featureCollection.features(); - LatLng neLatLng = toLatLng((Point)features.get(0).getGeometry()); - LatLng swLatLng = toLatLng((Point)features.get(1).getGeometry()); + double[] bbox = TurfMeasurement.bbox(toGeometryCollection(features)); - return LatLngBounds.from(neLatLng.getLatitude(), neLatLng.getLongitude(), - swLatLng.getLatitude(), swLatLng.getLongitude()); + return LatLngBounds.from(bbox[3], bbox[2], bbox[1], bbox[0]); } public static LatLngQuad toLatLngQuad(ReadableArray array) { @@ -195,4 +199,11 @@ public static LatLngQuad toLatLngQuad(ReadableArray array) { toLatLng(array.getArray(3)) ); } + + public static double[] pointToDoubleArray(Point point) { + if (point == null) { + return new double[] { 0.0, 0.0 }; + } + return new double[] { point.longitude(), point.latitude() }; + } } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index a66595807c..35b4f7ce55 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -78,19 +78,8 @@ project.ext.react = [ apply from: "../../node_modules/react-native/react.gradle" -/** - * Set this to true to create two separate APKs instead of one: - * - An APK that only works on ARM devices - * - An APK that only works on x86 devices - * The advantage is the size of the APK is reduced by about 4MB. - * Upload all the APKs to the Play Store and people will download - * the correct one based on the CPU architecture of their device. - */ -def enableSeparateBuildPerCPUArchitecture = false -/** - * Run Proguard to shrink the Java bytecode in release builds. - */ +def enableSeparateBuildPerCPUArchitecture = false def enableProguardInReleaseBuilds = false android { @@ -140,10 +129,12 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation project(':mapbox-react-native-mapbox-gl') - implementation project(':react-native-vector-icons') implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" + implementation "android.arch.lifecycle:extensions:1.1.1" + implementation "com.google.android.gms:play-services-location:15.0.1" implementation "com.facebook.react:react-native:+" // From node_modules + implementation project(':android-mapbox-react-native-mapbox-gl') + implementation project(':react-native-vector-icons') } // Run this once to be able to run the application with BUCK diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 9022abed53..4c36bd7a2c 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,8 +1,8 @@ rootProject.name = 'RNMapboxGLExample' include ':app' -include ':mapbox-react-native-mapbox-gl' -project(':mapbox-react-native-mapbox-gl').projectDir = new File(rootProject.projectDir, '../node_modules/@mapbox/react-native-mapbox-gl/android/rctmgl') +include ':android-mapbox-react-native-mapbox-gl' +project(':android-mapbox-react-native-mapbox-gl').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-mapbox/maps/android/rctmgl') include ':react-native-vector-icons' project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') diff --git a/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj b/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj index 9428ee0384..597c119d3e 100644 --- a/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj +++ b/example/ios/RNMapboxGLExample.xcodeproj/project.pbxproj @@ -284,13 +284,6 @@ remoteGlobalIDString = 3D3C06751DE3340C00C268FA; remoteInfo = "yoga-tvOS"; }; - 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; - remoteInfo = cxxreact; - }; 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; @@ -312,13 +305,6 @@ remoteGlobalIDString = 2D2A28201D9B03D100D4039D; remoteInfo = "RCTAnimation-tvOS"; }; - 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; @@ -333,6 +319,34 @@ remoteGlobalIDString = 358F4ED71D1E81A9004DF814; remoteInfo = RCTBlob; }; + C4692AC021588C120039E2BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BDC1FC498900052F4D5; + remoteInfo = jsinspector; + }; + C4692AC221588C120039E2BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; + remoteInfo = "jsinspector-tvOS"; + }; + C4692AC421588C120039E2BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; + remoteInfo = privatedata; + }; + C4692AC621588C120039E2BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; + remoteInfo = "privatedata-tvOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -545,7 +559,6 @@ 3DAD3EA31DF850E9000B6D8A /* libReact.a */, 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, - 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 2DF0FFDF2056DD460020B375 /* libjsinspector.a */, 2DF0FFE12056DD460020B375 /* libjsinspector-tvOS.a */, @@ -684,6 +697,10 @@ FE03836274308650C287AEFF /* Pods-RNMapboxGLExample.release.xcconfig */, 29CF8AEAEB85CD9239D0DA42 /* Pods-RNMapboxGLExampleTests.debug.xcconfig */, EA906C5240540673CDECFBA0 /* Pods-RNMapboxGLExampleTests.release.xcconfig */, + C4692AC121588C120039E2BC /* libjsinspector.a */, + C4692AC321588C120039E2BC /* libjsinspector-tvOS.a */, + C4692AC521588C120039E2BC /* libprivatedata.a */, + C4692AC721588C120039E2BC /* libprivatedata-tvOS.a */, ); name = Pods; sourceTree = ""; @@ -719,8 +736,8 @@ 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - D4320733523AC1CD21E84EF3 /* [CP] Embed Pods Frameworks */, 166F03A4477869C6DD0E6B59 /* [CP] Copy Pods Resources */, + 0EF90845EE3D7944D02DA256 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1073,13 +1090,6 @@ remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libcxxreact.a; - remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1105,7 +1115,6 @@ isa = PBXReferenceProxy; fileType = archive.ar; path = libRCTLinking.a; - remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { @@ -1122,6 +1131,34 @@ remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + C4692AC121588C120039E2BC /* libjsinspector.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjsinspector.a; + remoteRef = C4692AC021588C120039E2BC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + C4692AC321588C120039E2BC /* libjsinspector-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libjsinspector-tvOS.a"; + remoteRef = C4692AC221588C120039E2BC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + C4692AC521588C120039E2BC /* libprivatedata.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libprivatedata.a; + remoteRef = C4692AC421588C120039E2BC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + C4692AC721588C120039E2BC /* libprivatedata-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-tvOS.a"; + remoteRef = C4692AC621588C120039E2BC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -1206,6 +1243,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 0EF90845EE3D7944D02DA256 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-RNMapboxGLExample/Pods-RNMapboxGLExample-frameworks.sh", + "${PODS_ROOT}/../../node_modules/@react-native-mapbox/maps/ios/Mapbox.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RNMapboxGLExample/Pods-RNMapboxGLExample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 166F03A4477869C6DD0E6B59 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1415,7 +1474,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/../node_modules/@mapbox/react-native-mapbox-gl/ios", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = RNMapboxGLExample/Info.plist; + INSTALL_PATH = "$(INSTALL_PATH)"; IPHONEOS_DEPLOYMENT_TARGET = 9.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( @@ -1436,7 +1501,13 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/../node_modules/@mapbox/react-native-mapbox-gl/ios", + ); + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = RNMapboxGLExample/Info.plist; + INSTALL_PATH = "$(INSTALL_PATH)"; IPHONEOS_DEPLOYMENT_TARGET = 9.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( diff --git a/example/package.json b/example/package.json index bfc40b1540..0b6f42828e 100644 --- a/example/package.json +++ b/example/package.json @@ -21,16 +21,18 @@ "@turf/helpers": "^4.7.3", "@turf/line-distance": "^4.7.3", "@turf/nearest": "^4.7.3", + "buffer": "^5.1.0", "install": "^0.12.2", - "mapbox": "^1.0.0-beta10", + "@mapbox/mapbox-sdk": "^0.6.0", "moment": "^2.24.0", "npm": "^5.10.0", "prop-types": "^15.7.2", - "react": "16.8.3", + "react": "^16.8.3", "react-native": "0.59.0", "react-native-elements": "^1.1.0", - "react-native-safe-area-view": "^0.13.1", "react-native-vector-icons": "^6.3.0", + "react-native-safe-area-view": "^0.13.1", + "react-navigation": "^2.18.3", "url": "^0.11.0" }, "devDependencies": { diff --git a/example/src/App.js b/example/src/App.js index b699a9ef0b..01ca8c7505 100755 --- a/example/src/App.js +++ b/example/src/App.js @@ -7,125 +7,57 @@ import { Text, TouchableOpacity, View, + YellowBox, } from 'react-native'; import {Icon} from 'react-native-elements'; import SafeAreaView from 'react-native-safe-area-view'; +import {createStackNavigator} from 'react-navigation'; +import CardStackStyleInterpolator from 'react-navigation-stack/dist/views/StackView/StackViewStyleInterpolator'; -import MapHeader from './components/common/MapHeader'; // Styles import sheet from './styles/sheet'; import colors from './styles/colors'; // Utils import {IS_ANDROID} from './utils'; import config from './utils/config'; -// Examples -import ShowMap from './components/ShowMap'; -import SetPitch from './components/SetPitch'; -import SetBearing from './components/SetBearing'; -import ShowClick from './components/ShowClick'; -import FlyTo from './components/FlyTo'; -import FitBounds from './components/FitBounds'; -import SetUserTrackingModes from './components/SetUserTrackingModes'; -import SetUserLocationVerticalAlignment from './components/SetUserLocationVerticalAlignment'; -import ShowRegionChange from './components/ShowRegionChange'; -import CustomIcon from './components/CustomIcon'; -import YoYo from './components/YoYo'; -import EarthQuakes from './components/EarthQuakes'; -import GeoJSONSource from './components/GeoJSONSource'; -import WatercolorRasterTiles from './components/WatercolorRasterTiles'; -import TwoByTwo from './components/TwoByTwo'; -import IndoorBuilding from './components/IndoorBuilding'; -import QueryAtPoint from './components/QueryAtPoint'; -import QueryWithRect from './components/QueryWithRect'; -import ShapeSourceIcon from './components/ShapeSourceIcon'; -import CustomVectorSource from './components/CustomVectorSource'; -import ShowPointAnnotation from './components/ShowPointAnnotation'; -import CreateOfflineRegion from './components/CreateOfflineRegion'; -import DriveTheLine from './components/DriveTheLine'; -import ImageOverlay from './components/ImageOverlay'; -import DataDrivenCircleColors from './components/DataDrivenCircleColors'; -import ChoroplethLayerByZoomLevel from './components/ChoroplethLayerByZoomLevel'; -import PointInMapView from './components/PointInMapView'; -import TakeSnapshot from './components/TakeSnapshot'; -import TakeSnapshotWithMap from './components/TakeSnapshotWithMap'; -import GetZoom from './components/GetZoom'; -import GetCenter from './components/GetCenter'; -import UserLocationChange from './components/UserLocationChange'; + +// screens +import Home from './scenes/Home'; +import Demo from './scenes/Demo'; + +// :( +YellowBox.ignoreWarnings([ + 'Warning: isMounted(...) is deprecated', + 'Module RCTImageLoader', +]); const styles = StyleSheet.create({ noPermissionsText: { fontSize: 18, fontWeight: 'bold', }, - exampleList: { - flex: 1, - }, - exampleListItemBorder: { - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: '#ccc', - }, - exampleListItem: { - paddingVertical: 32, - paddingHorizontal: 16, - flexDirection: 'row', - justifyContent: 'space-between', - backgroundColor: colors.secondary.white, - }, - exampleListLabel: { - fontSize: 18, - }, - exampleBackground: { - flex: 1, - backgroundColor: colors.primary.pinkFaint, - }, }); MapboxGL.setAccessToken(config.get('accessToken')); -class ExampleItem { - constructor(label, Component) { - this.label = label; - this.Component = Component; - } -} +const AppStackNavigator = createStackNavigator( + { + Home: {screen: Home}, + Demo: {screen: Demo}, + }, + { + initialRouteName: 'Home', + + navigationOptions: { + header: null, + }, -const Examples = [ - new ExampleItem('Show Map', ShowMap), - new ExampleItem('Set Pitch', SetPitch), - new ExampleItem('Set Bearing', SetBearing), - new ExampleItem('Show Click', ShowClick), - new ExampleItem('Fly To', FlyTo), - new ExampleItem('Fit Bounds', FitBounds), - new ExampleItem('Set User Tracking Modes', SetUserTrackingModes), - new ExampleItem( - 'Set User Location Vertical Alignment', - SetUserLocationVerticalAlignment, - ), - new ExampleItem('Show Region Change', ShowRegionChange), - new ExampleItem('Custom Icon', CustomIcon), - new ExampleItem('Yo Yo Camera', YoYo), - new ExampleItem('Clustering Earthquakes', EarthQuakes), - new ExampleItem('GeoJSON Source', GeoJSONSource), - new ExampleItem('Watercolor Raster Tiles', WatercolorRasterTiles), - new ExampleItem('Two Map Views', TwoByTwo), - new ExampleItem('Indoor Building Map', IndoorBuilding), - new ExampleItem('Query Feature Point', QueryAtPoint), - new ExampleItem('Query Features Bounding Box', QueryWithRect), - new ExampleItem('Shape Source From Icon', ShapeSourceIcon), - new ExampleItem('Custom Vector Source', CustomVectorSource), - new ExampleItem('Show Point Annotation', ShowPointAnnotation), - new ExampleItem('Create Offline Region', CreateOfflineRegion), - new ExampleItem('Animation Along a Line', DriveTheLine), - new ExampleItem('Image Overlay', ImageOverlay), - new ExampleItem('Data Driven Circle Colors', DataDrivenCircleColors), - new ExampleItem('Choropleth Layer By Zoom Level', ChoroplethLayerByZoomLevel), - new ExampleItem('Get Pixel Point in MapView', PointInMapView), - new ExampleItem('Take Snapshot Without Map', TakeSnapshot), - new ExampleItem('Take Snapshot With Map', TakeSnapshotWithMap), - new ExampleItem('Get Current Zoom', GetZoom), - new ExampleItem('Get Center', GetCenter), - new ExampleItem('User Location Updates', UserLocationChange), -]; + transitionConfig: () => ({ + screenInterpolator: props => + CardStackStyleInterpolator.forVertical(props), + }), + }, +); class App extends React.Component { constructor(props) { @@ -136,12 +68,9 @@ class App extends React.Component { isAndroidPermissionGranted: false, activeExample: -1, }; - - this.renderItem = this.renderItem.bind(this); - this.onCloseExample = this.onCloseExample.bind(this); } - async componentWillMount() { + async componentDidMount() { if (IS_ANDROID) { const isGranted = await MapboxGL.requestAndroidLocationPermissions(); this.setState({ @@ -151,67 +80,6 @@ class App extends React.Component { } } - getActiveItem() { - if ( - this.state.activeExample < 0 || - this.state.activeExample >= Examples.length - ) { - return null; - } - return Examples[this.state.activeExample]; - } - - onExamplePress(activeExamplePosition) { - this.setState({activeExample: activeExamplePosition}); - } - - onCloseExample() { - this.setState({activeExample: -1}); - } - - renderItem({item, index}) { - return ( - - this.onExamplePress(index)}> - - {item.label} - - - - - ); - } - - renderActiveExample() { - const item = this.getActiveItem(); - - const modalProps = { - visible: !!item, - transparent: true, - animationType: 'slide', - onRequestClose: this.onCloseExample, - }; - - return ( - - - - {modalProps.visible ? ( - - ) : null} - - - - ); - } - render() { if (IS_ANDROID && !this.state.isAndroidPermissionGranted) { if (this.state.isFetchingAndroidPermission) { @@ -231,28 +99,7 @@ class App extends React.Component { ); } - - return ( - - - - - - item.label} - renderItem={this.renderItem} - /> - - - {this.renderActiveExample()} - - - ); + return ; } } diff --git a/example/src/MapboxClient.js b/example/src/MapboxClient.js index 0fba8dd9a8..07c0466564 100755 --- a/example/src/MapboxClient.js +++ b/example/src/MapboxClient.js @@ -1,6 +1,8 @@ -import MapboxClient from 'mapbox'; +import MapboxDirectionsFactory from '@mapbox/mapbox-sdk/services/directions'; import config from './utils/config'; -const client = new MapboxClient(config.get('accessToken')); -export default client; +const clientOptions = {accessToken: config.get('accessToken')}; +const directionsClient = MapboxDirectionsFactory(clientOptions); + +export {directionsClient}; diff --git a/example/src/components/ChoroplethLayerByZoomLevel.js b/example/src/examples/ChoroplethLayerByZoomLevel.js similarity index 60% rename from example/src/components/ChoroplethLayerByZoomLevel.js rename to example/src/examples/ChoroplethLayerByZoomLevel.js index 63175fee8e..d262eab8f4 100755 --- a/example/src/components/ChoroplethLayerByZoomLevel.js +++ b/example/src/examples/ChoroplethLayerByZoomLevel.js @@ -6,44 +6,63 @@ import sheet from '../styles/sheet'; import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import Page from './common/Page'; -const styles = MapboxGL.StyleSheet.create({ +const styles = { statePopulation: { - fillColor: MapboxGL.StyleSheet.source( - [ - [0, '#F2F12D'], - [500000, '#EED322'], - [750000, '#E6B71E'], - [1000000, '#DA9C20'], - [2500000, '#CA8323'], - [5000000, '#B86B25'], - [7500000, '#A25626'], - [10000000, '#8B4225'], - [25000000, '#723122'], - ], - 'population', - MapboxGL.InterpolationMode.Exponential, - ), + fillColor: [ + 'interpolate', + ['linear'], + ['get', 'population'], + 0, + '#F2F12D', + 500000, + '#EED322', + 750000, + '#E6B71E', + 1000000, + '#DA9C20', + 2500000, + '#CA8323', + 5000000, + '#B86B25', + 7500000, + '#A25626', + 10000000, + '#8B4225', + 25000000, + '#723122', + ], + fillOpacity: 0.75, }, + countyPopulation: { - fillColor: MapboxGL.StyleSheet.source( - [ - [0, '#F2F12D'], - [100, '#EED322'], - [1000, '#E6B71E'], - [5000, '#DA9C20'], - [10000, '#CA8323'], - [50000, '#B86B25'], - [100000, '#A25626'], - [500000, '#8B4225'], - [1000000, '#723122'], - ], - 'population', - MapboxGL.InterpolationMode.Exponential, - ), + fillColor: [ + 'interpolate', + ['linear'], + ['get', 'population'], + 0, + '#F2F12D', + 100, + '#EED322', + 1000, + '#E6B71E', + 5000, + '#DA9C20', + 10000, + '#CA8323', + 50000, + '#B86B25', + 100000, + '#A25626', + 500000, + '#8B4225', + 1000000, + '#723122', + ], + fillOpacity: 0.75, }, -}); +}; class ChoroplethLayerByZoomLevel extends React.PureComponent { static propTypes = { @@ -54,12 +73,15 @@ class ChoroplethLayerByZoomLevel extends React.PureComponent { return ( + + (this._map = c)} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingStyle} - centerCoordinate={CENTER_COORD} style={sheet.matchParent} - /> + > + + {offlineRegionStatus !== null ? ( diff --git a/example/src/components/CustomIcon.js b/example/src/examples/CustomIcon.js similarity index 93% rename from example/src/components/CustomIcon.js rename to example/src/examples/CustomIcon.js index 059e8537d6..e987db888b 100755 --- a/example/src/components/CustomIcon.js +++ b/example/src/examples/CustomIcon.js @@ -9,13 +9,12 @@ import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import Page from './common/Page'; import Bubble from './common/Bubble'; -const styles = MapboxGL.StyleSheet.create({ +const styles = { icon: { iconImage: exampleIcon, iconAllowOverlap: true, - iconSize: 0.5, }, -}); +}; class CustomIcon extends React.Component { static propTypes = { @@ -54,12 +53,15 @@ class CustomIcon extends React.Component { return ( (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={sheet.matchParent} > + + - + + + + + (this._map = c)} - centerCoordinate={[-122.452652, 37.762963]} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark} > + + {this.renderOrigin()} {this.renderRoute()} diff --git a/example/src/components/EarthQuakes.js b/example/src/examples/EarthQuakes.js similarity index 76% rename from example/src/components/EarthQuakes.js rename to example/src/examples/EarthQuakes.js index a72bfd333f..4ec8f47ace 100755 --- a/example/src/components/EarthQuakes.js +++ b/example/src/examples/EarthQuakes.js @@ -7,7 +7,7 @@ import {SF_OFFICE_COORDINATE} from '../utils'; import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import Page from './common/Page'; -const layerStyles = MapboxGL.StyleSheet.create({ +const layerStyles = { singlePoint: { circleColor: 'green', circleOpacity: 0.84, @@ -19,24 +19,18 @@ const layerStyles = MapboxGL.StyleSheet.create({ clusteredPoints: { circlePitchAlignment: 'map', - circleColor: MapboxGL.StyleSheet.source( - [ - [25, 'yellow'], - [50, 'red'], - [75, 'blue'], - [100, 'orange'], - [300, 'pink'], - [750, 'white'], - ], - 'point_count', - MapboxGL.InterpolationMode.Exponential, - ), - circleRadius: MapboxGL.StyleSheet.source( - [[0, 15], [100, 20], [750, 30]], - 'point_count', - MapboxGL.InterpolationMode.Exponential, - ), + circleColor: [ + 'step', + ['get', 'point_count'], + '#51bbd6', + 100, + '#f1f075', + 750, + '#f28cb1', + ], + + circleRadius: ['step', ['get', 'point_count'], 20, 100, 30, 750, 40], circleOpacity: 0.84, circleStrokeWidth: 2, @@ -48,7 +42,7 @@ const layerStyles = MapboxGL.StyleSheet.create({ textSize: 12, textPitchAlignment: 'map', }, -}); +}; class EarthQuakes extends React.Component { static propTypes = { @@ -59,12 +53,15 @@ class EarthQuakes extends React.Component { return ( + + - (this.map = ref)} - style={sheet.matchParent} - > + + + + + - - diff --git a/example/src/components/GeoJSONSource.js b/example/src/examples/GeoJSONSource.js similarity index 87% rename from example/src/components/GeoJSONSource.js rename to example/src/examples/GeoJSONSource.js index 836991a1ee..10b88883c0 100755 --- a/example/src/components/GeoJSONSource.js +++ b/example/src/examples/GeoJSONSource.js @@ -5,10 +5,10 @@ import sheet from '../styles/sheet'; import gridPattern from '../assets/grid_pattern.png'; import smileyFaceGeoJSON from '../assets/smiley_face.json'; -import Page from './common/Page'; import BaseExamplePropTypes from './common/BaseExamplePropTypes'; +import Page from './common/Page'; -const layerStyles = MapboxGL.StyleSheet.create({ +const layerStyles = { background: { backgroundPattern: gridPattern, }, @@ -17,7 +17,7 @@ const layerStyles = MapboxGL.StyleSheet.create({ fillColor: 'white', fillOutlineColor: 'rgba(255, 255, 255, 0.84)', }, -}); +}; class GeoJSONSource extends React.Component { static propTypes = { @@ -28,13 +28,15 @@ class GeoJSONSource extends React.Component { return ( (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark} > + + (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={{flex: 1}} - /> + > + + Center diff --git a/example/src/components/GetZoom.js b/example/src/examples/GetZoom.js similarity index 86% rename from example/src/components/GetZoom.js rename to example/src/examples/GetZoom.js index 22f80f52ab..3d414df8a2 100755 --- a/example/src/components/GetZoom.js +++ b/example/src/examples/GetZoom.js @@ -31,12 +31,15 @@ class GetZoom extends React.Component { (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={{flex: 1}} - /> + > + + Current zoom: {this.state.zoom} diff --git a/example/src/components/ImageOverlay.js b/example/src/examples/ImageOverlay.js similarity index 94% rename from example/src/components/ImageOverlay.js rename to example/src/examples/ImageOverlay.js index 93b357e7b3..0108e32639 100755 --- a/example/src/components/ImageOverlay.js +++ b/example/src/examples/ImageOverlay.js @@ -62,12 +62,15 @@ class ImageOverlay extends React.Component { return ( (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark} > + + (this.map = ref)} style={sheet.matchParent} > + + (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={{flex: 1}} - /> + > + + {this.renderPointInView()} diff --git a/example/src/components/QueryAtPoint.js b/example/src/examples/QueryAtPoint.js similarity index 94% rename from example/src/components/QueryAtPoint.js rename to example/src/examples/QueryAtPoint.js index 36f4bcffe3..1b08ddd9b3 100755 --- a/example/src/components/QueryAtPoint.js +++ b/example/src/examples/QueryAtPoint.js @@ -9,7 +9,7 @@ import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import Page from './common/Page'; import Bubble from './common/Bubble'; -const styles = MapboxGL.StyleSheet.create({ +const styles = { neighborhoods: { fillAntialias: true, fillColor: 'blue', @@ -21,7 +21,7 @@ const styles = MapboxGL.StyleSheet.create({ fillColor: 'green', fillOpacity: 0.84, }, -}); +}; class QueryAtPoint extends React.Component { static propTypes = { @@ -62,13 +62,16 @@ class QueryAtPoint extends React.Component { return ( (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Light} > + + diff --git a/example/src/components/QueryWithRect.js b/example/src/examples/QueryWithRect.js similarity index 95% rename from example/src/components/QueryWithRect.js rename to example/src/examples/QueryWithRect.js index f967700c26..c89582af9e 100755 --- a/example/src/components/QueryWithRect.js +++ b/example/src/examples/QueryWithRect.js @@ -9,7 +9,7 @@ import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import Page from './common/Page'; import Bubble from './common/Bubble'; -const styles = MapboxGL.StyleSheet.create({ +const styles = { neighborhoods: { fillAntialias: true, fillColor: 'blue', @@ -21,7 +21,7 @@ const styles = MapboxGL.StyleSheet.create({ fillColor: 'green', fillOpacity: 0.84, }, -}); +}; class QueryWithRect extends React.Component { static propTypes = { @@ -82,13 +82,16 @@ class QueryWithRect extends React.Component { return ( (this._map = c)} onPress={this.onPress} - centerCoordinate={[-73.970895, 40.723279]} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Light} > + + diff --git a/example/src/components/SetBearing.js b/example/src/examples/SetHeading.js similarity index 53% rename from example/src/components/SetBearing.js rename to example/src/examples/SetHeading.js index 8db381647e..7c24264188 100755 --- a/example/src/components/SetBearing.js +++ b/example/src/examples/SetHeading.js @@ -6,7 +6,7 @@ import sheet from '../styles/sheet'; import BaseExamplePropTypes from './common/BaseExamplePropTypes'; import TabBarPage from './common/TabBarPage'; -class SetBearing extends React.Component { +class SetHeading extends React.Component { static propTypes = { ...BaseExamplePropTypes, }; @@ -14,17 +14,31 @@ class SetBearing extends React.Component { constructor(props) { super(props); + this.state = { + heading: 0, + zoomLevel: 16, + animationDuration: 150, + }; + this._bearingOptions = [ {label: '0', data: 0}, {label: '90', data: 90}, {label: '180', data: 180}, ]; - this.onBearingChange = this.onBearingChange.bind(this); + this.onHeadingChange = this.onHeadingChange.bind(this); + } + + componentDidMount() { + MapboxGL.locationManager.start(); + } + + componentWillUnmount() { + MapboxGL.locationManager.dispose(); } - onBearingChange(index, bearing) { - this.map.setCamera({heading: bearing, duration: 150}); + onHeadingChange(index, heading) { + this.setState({heading}); } render() { @@ -32,18 +46,18 @@ class SetBearing extends React.Component { (this.map = ref)} - heading={0} - showUserLocation={true} - userTrackingMode={MapboxGL.UserTrackingModes.Follow} style={sheet.matchParent} - /> + > + + + ); } } -export default SetBearing; +export default SetHeading; diff --git a/example/src/components/SetPitch.js b/example/src/examples/SetPitch.js similarity index 64% rename from example/src/components/SetPitch.js rename to example/src/examples/SetPitch.js index 7e232ad728..9d9bb1195c 100755 --- a/example/src/components/SetPitch.js +++ b/example/src/examples/SetPitch.js @@ -14,6 +14,12 @@ class SetPitch extends React.Component { constructor(props) { super(props); + this.state = { + followPitch: 15, + zoomLevel: 16, + duration: 300, + }; + this._pitchOptions = [ {label: '15', data: 15}, {label: '45', data: 45}, @@ -23,8 +29,16 @@ class SetPitch extends React.Component { this.onUpdatePitch = this.onUpdatePitch.bind(this); } + componentDidMount() { + MapboxGL.locationManager.start(); + } + + componentWillUnmount() { + MapboxGL.locationManager.dispose(); + } + onUpdatePitch(index, pitch) { - this.map.setCamera({pitch, duration: 300}); + this.setState({followPitch: pitch}); } render() { @@ -34,13 +48,10 @@ class SetPitch extends React.Component { options={this._pitchOptions} onOptionPress={this.onUpdatePitch} > - (this.map = ref)} - pitch={15} - showUserLocation={true} - userTrackingMode={MapboxGL.UserTrackingModes.Follow} - style={sheet.matchParent} - /> + + + + ); } diff --git a/example/src/components/SetUserLocationVerticalAlignment.js b/example/src/examples/SetUserLocationVerticalAlignment.js similarity index 83% rename from example/src/components/SetUserLocationVerticalAlignment.js rename to example/src/examples/SetUserLocationVerticalAlignment.js index f5575eff54..22f9b8f6df 100755 --- a/example/src/components/SetUserLocationVerticalAlignment.js +++ b/example/src/examples/SetUserLocationVerticalAlignment.js @@ -42,12 +42,10 @@ class SetUserLocationVerticalAlignment extends React.Component { options={this._alignmentOptions} onOptionPress={this.onAlignmentChange} > - + + + + ); } diff --git a/example/src/components/SetUserTrackingModes.js b/example/src/examples/SetUserTrackingModes.js similarity index 100% rename from example/src/components/SetUserTrackingModes.js rename to example/src/examples/SetUserTrackingModes.js diff --git a/example/src/components/ShapeSourceIcon.js b/example/src/examples/ShapeSourceIcon.js similarity index 79% rename from example/src/components/ShapeSourceIcon.js rename to example/src/examples/ShapeSourceIcon.js index 4e6633adfe..5efb48d915 100755 --- a/example/src/components/ShapeSourceIcon.js +++ b/example/src/examples/ShapeSourceIcon.js @@ -3,21 +3,25 @@ import MapboxGL from '@react-native-mapbox/maps'; import sheet from '../styles/sheet'; import exampleIcon from '../assets/example.png'; -import {IS_ANDROID} from '../utils'; -import Page from './common/Page'; import BaseExamplePropTypes from './common/BaseExamplePropTypes'; +import Page from './common/Page'; -const styles = MapboxGL.StyleSheet.create({ +const styles = { icon: { iconImage: '{icon}', - iconSize: MapboxGL.StyleSheet.source( - [['example', IS_ANDROID ? 1 : 0.5], ['airport-15', 1.2]], - 'icon', - MapboxGL.InterpolationMode.Categorical, - ), + + iconSize: [ + 'match', + ['get', 'icon'], + 'example', + 1.2, + 'airport-15', + 1.2, + /* default */ 1, + ], }, -}); +}; const featureCollection = { type: 'FeatureCollection', @@ -66,11 +70,12 @@ class ShapeSourceIcon extends React.Component { render() { return ( - + + + - - + + + {this.renderLastClicked()} ); diff --git a/example/src/components/ShowMap.js b/example/src/examples/ShowMap.js similarity index 69% rename from example/src/components/ShowMap.js rename to example/src/examples/ShowMap.js index f870344bdb..66b423383a 100755 --- a/example/src/components/ShowMap.js +++ b/example/src/examples/ShowMap.js @@ -1,4 +1,5 @@ import React from 'react'; +import {Alert} from 'react-native'; import MapboxGL from '@react-native-mapbox/maps'; import sheet from '../styles/sheet'; @@ -29,12 +30,25 @@ class ShowMap extends React.Component { }; this.onMapChange = this.onMapChange.bind(this); + this.onUserMarkerPress = this.onUserMarkerPress.bind(this); + } + + componentDidMount() { + MapboxGL.locationManager.start(); + } + + componentWillUnmount() { + MapboxGL.locationManager.dispose(); } onMapChange(index, styleURL) { this.setState({styleURL}); } + onUserMarkerPress() { + Alert.alert('You pressed on the user location annotation'); + } + render() { return ( + > + + + + ); } diff --git a/example/src/components/ShowPointAnnotation.js b/example/src/examples/ShowPointAnnotation.js similarity index 81% rename from example/src/components/ShowPointAnnotation.js rename to example/src/examples/ShowPointAnnotation.js index fa22c14ec7..f5db312868 100755 --- a/example/src/components/ShowPointAnnotation.js +++ b/example/src/examples/ShowPointAnnotation.js @@ -90,9 +90,8 @@ class ShowPointAnnotation extends React.Component { for (let i = 0; i < this.state.coordinates.length; i++) { const coordinate = this.state.coordinates[i]; - const title = `Longitude: ${this.state.coordinates[i][0]} Latitude: ${ - this.state.coordinates[i][1] - }`; + + const title = `Longitude: ${coordinate[0]} Latitude: ${coordinate[1]}`; const id = `pointAnnotation${i}`; const animationStyle = {}; @@ -103,21 +102,9 @@ class ShowPointAnnotation extends React.Component { } items.push( - this.onAnnotationSelected(i, feature)} - onDeselected={() => this.onAnnotationDeselected(i)} - coordinate={coordinate} - > - - - - - - , + + + , ); } @@ -129,12 +116,15 @@ class ShowPointAnnotation extends React.Component { (this._map = c)} - zoomLevel={16} onPress={this.onPress} onDidFinishLoadingMap={this.onDidFinishLoadingMap} - centerCoordinate={this.state.coordinates[0]} style={sheet.matchParent} > + + {this.renderAnnotations()} diff --git a/example/src/examples/ShowRegionDidChange.js b/example/src/examples/ShowRegionDidChange.js new file mode 100644 index 0000000000..fef80a8029 --- /dev/null +++ b/example/src/examples/ShowRegionDidChange.js @@ -0,0 +1,152 @@ +import React from 'react'; +import {Text} from 'react-native'; +import MapboxGL from '@react-native-mapbox/maps'; + +import sheet from '../styles/sheet'; +import {DEFAULT_CENTER_COORDINATE, SF_OFFICE_COORDINATE} from '../utils'; + +import BaseExamplePropTypes from './common/BaseExamplePropTypes'; +import TabBarPage from './common/TabBarPage'; +import Bubble from './common/Bubble'; + +class ShowRegionDidChange extends React.Component { + static propTypes = { + ...BaseExamplePropTypes, + }; + + constructor(props) { + super(props); + + this.state = { + reason: '', + cameraConfig: { + centerCoordinate: DEFAULT_CENTER_COORDINATE, + zoomLevel: 12, + }, + regionFeature: undefined, + }; + + this._tabOptions = [ + {label: 'Fly To', data: SF_OFFICE_COORDINATE}, + { + label: 'Fit Bounds', + data: [[-74.12641, 40.797968], [-74.143727, 40.772177]], + }, + {label: 'Zoom To', data: 16}, + ]; + + this.onRegionDidChange = this.onRegionDidChange.bind(this); + this.onRegionWillChange = this.onRegionWillChange.bind(this); + this.onDidFinishLoadingMap = this.onDidFinishLoadingMap.bind(this); + this.onOptionPress = this.onOptionPress.bind(this); + } + + async onOptionPress(optionIndex, optionData) { + if (optionIndex === 0) { + this.setState({ + cameraConfig: { + triggerKey: Date.now(), + centerCoordinate: optionData, + animationMode: MapboxGL.Camera.Mode.Flight, + animationDuration: 2000, + }, + }); + } else if (optionIndex === 1) { + this.setState({ + cameraConfig: { + triggerKey: Date.now(), + bounds: optionData, + }, + }); + } else if (optionIndex === 2) { + this.setState({ + cameraConfig: { + triggerKey: Date.now(), + zoomLevel: optionData, + }, + }); + } + } + + async onDidFinishLoadingMap() { + const visibleBounds = await this.map.getVisibleBounds(); + console.log('Visible Bounds', visibleBounds); // eslint-disable-line no-console + } + + isValidCoordinate(geometry) { + if (!geometry) { + return false; + } + return geometry.coordinates[0] !== 0 && geometry.coordinates[1] !== 0; + } + + onRegionWillChange(regionFeature) { + this.setState({reason: 'will change', regionFeature}); + } + + onRegionDidChange(regionFeature) { + this.setState({reason: 'did change', regionFeature}); + } + + renderRegionChange() { + if ( + !this.state.regionFeature || + !this.isValidCoordinate(this.state.regionFeature.geometry) + ) { + return ( + + Move the map! + + ); + } + + const {geometry, properties} = this.state.regionFeature; + + const neCoord = properties.visibleBounds[0] + .map(n => n.toPrecision(6)) + .join(', '); + const swCoord = properties.visibleBounds[1] + .map(n => n.toPrecision(6)) + .join(', '); + + return ( + + {this.state.reason} + Latitude: {geometry.coordinates[1]} + Longitude: {geometry.coordinates[0]} + Visible Bounds NE: {neCoord} + Visible Bounds SW: {swCoord} + Zoom Level: {properties.zoomLevel} + Heading: {properties.heading} + Pitch: {properties.pitch} + + Is User Interaction: {properties.isUserInteraction ? 'true' : 'false'} + + Animated: {properties.animated ? 'true' : 'false'} + + ); + } + + render() { + return ( + + (this.map = c)} + style={sheet.matchParent} + onDidFinishLoadingMap={this.onDidFinishLoadingMap} + onRegionWillChange={this.onRegionWillChange} + onRegionDidChange={this.onRegionDidChange} + > + + + {this.renderRegionChange()} + + ); + } +} + +export default ShowRegionDidChange; diff --git a/example/src/components/TakeSnapshot.js b/example/src/examples/TakeSnapshot.js similarity index 100% rename from example/src/components/TakeSnapshot.js rename to example/src/examples/TakeSnapshot.js diff --git a/example/src/components/TakeSnapshotWithMap.js b/example/src/examples/TakeSnapshotWithMap.js similarity index 81% rename from example/src/components/TakeSnapshotWithMap.js rename to example/src/examples/TakeSnapshotWithMap.js index d60c75fd9c..f162b74b74 100755 --- a/example/src/components/TakeSnapshotWithMap.js +++ b/example/src/examples/TakeSnapshotWithMap.js @@ -17,13 +17,13 @@ const styles = StyleSheet.create({ }, }); -const layerStyles = MapboxGL.StyleSheet.create({ +const layerStyles = { building: { - fillExtrusionHeight: MapboxGL.StyleSheet.identity('height'), - fillExtrusionBase: MapboxGL.StyleSheet.identity('min_height'), + fillExtrusionHeight: ['get', 'height'], + fillExtrusionBase: ['get', 'min_height'], fillExtrusionColor: 'blue', }, -}); +}; class TakeSnapshotWithMap extends React.Component { static propTypes = { @@ -47,13 +47,13 @@ class TakeSnapshotWithMap extends React.Component { return ( - (this.map = ref)} - style={{flex: 0.5}} - > + (this.map = ref)} style={{flex: 0.5}}> + + - + + + { - await this.map.zoomTo(this.state.zoomLevel, 8000); const nextZoomLevel = this.state.zoomLevel === 12 ? 2 : 12; this.setState({zoomLevel: nextZoomLevel}); - this.cameraLoop(); + setTimeout(() => this.cameraLoop(), 2000); }); } @@ -55,12 +58,15 @@ class YoYo extends React.Component { return ( (this.map = ref)} style={sheet.matchParent} styleURL={MapboxGL.StyleURL.Dark} > + + this.onDismissExample()} + /> + ); + } +} + +export default Demo; diff --git a/example/src/scenes/Home.js b/example/src/scenes/Home.js new file mode 100644 index 0000000000..160d9af9c6 --- /dev/null +++ b/example/src/scenes/Home.js @@ -0,0 +1,162 @@ +import React from 'react'; +import MapboxGL from '@react-native-mapbox/maps'; +import {View, Text, FlatList, StyleSheet, TouchableOpacity} from 'react-native'; +import {Icon} from 'react-native-elements'; + +// components +import MapHeader from '../examples/common/MapHeader'; + +// styles +import sheet from '../styles/sheet'; +import colors from '../styles/colors'; + +// examples +import ShowMap from '../examples/ShowMap'; +import SetPitch from '../examples/SetPitch'; +import SetHeading from '../examples/SetHeading'; +import ShowClick from '../examples/ShowClick'; +import FlyTo from '../examples/FlyTo'; +import FitBounds from '../examples/FitBounds'; +import SetUserTrackingModes from '../examples/SetUserTrackingModes'; +import SetUserLocationVerticalAlignment from '../examples/SetUserLocationVerticalAlignment'; +import ShowRegionDidChange from '../examples/ShowRegionDidChange'; +import CustomIcon from '../examples/CustomIcon'; +import YoYo from '../examples/YoYo'; +import EarthQuakes from '../examples/EarthQuakes'; +import GeoJSONSource from '../examples/GeoJSONSource'; +import WatercolorRasterTiles from '../examples/WatercolorRasterTiles'; +import TwoByTwo from '../examples/TwoByTwo'; +import IndoorBuilding from '../examples/IndoorBuilding'; +import QueryAtPoint from '../examples/QueryAtPoint'; +import QueryWithRect from '../examples/QueryWithRect'; +import ShapeSourceIcon from '../examples/ShapeSourceIcon'; +import CustomVectorSource from '../examples/CustomVectorSource'; +import ShowPointAnnotation from '../examples/ShowPointAnnotation'; +import CreateOfflineRegion from '../examples/CreateOfflineRegion'; +import DriveTheLine from '../examples/DriveTheLine'; +import ImageOverlay from '../examples/ImageOverlay'; +import DataDrivenCircleColors from '../examples/DataDrivenCircleColors'; +import ChoroplethLayerByZoomLevel from '../examples/ChoroplethLayerByZoomLevel'; +import PointInMapView from '../examples/PointInMapView'; +import TakeSnapshot from '../examples/TakeSnapshot'; +import TakeSnapshotWithMap from '../examples/TakeSnapshotWithMap'; +import GetZoom from '../examples/GetZoom'; +import GetCenter from '../examples/GetCenter'; +import UserLocationChange from '../examples/UserLocationChange'; + +const styles = StyleSheet.create({ + header: { + marginTop: 48, + fontSize: 24, + textAlign: 'center', + }, + exampleList: { + flex: 1, + marginTop: 60 + 12, // header + list padding, + }, + exampleListItemBorder: { + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: '#ccc', + }, + exampleListItem: { + paddingVertical: 32, + paddingHorizontal: 16, + flexDirection: 'row', + justifyContent: 'space-between', + }, + exampleListLabel: { + fontSize: 18, + }, + exampleBackground: { + flex: 1, + backgroundColor: colors.primary.pinkFaint, + }, +}); + +class ExampleItem { + constructor(label, Component) { + this.label = label; + this.Component = Component; + } +} + +const Examples = [ + new ExampleItem('Show Map', ShowMap), + new ExampleItem('Set Pitch', SetPitch), + new ExampleItem('Set Heading', SetHeading), + new ExampleItem('Show Click', ShowClick), + new ExampleItem('Fly To', FlyTo), + new ExampleItem('Fit Bounds', FitBounds), + new ExampleItem('Set User Tracking Modes', SetUserTrackingModes), + new ExampleItem( + 'Set User Location Vertical Alignment', + SetUserLocationVerticalAlignment, + ), + new ExampleItem('Show Region Did Change', ShowRegionDidChange), + new ExampleItem('Custom Icon', CustomIcon), + new ExampleItem('Yo Yo Camera', YoYo), + new ExampleItem('Clustering Earthquakes', EarthQuakes), + new ExampleItem('GeoJSON Source', GeoJSONSource), + new ExampleItem('Watercolor Raster Tiles', WatercolorRasterTiles), + new ExampleItem('Two Map Views', TwoByTwo), + new ExampleItem('Indoor Building Map', IndoorBuilding), + new ExampleItem('Query Feature Point', QueryAtPoint), + new ExampleItem('Query Features Bounding Box', QueryWithRect), + new ExampleItem('Shape Source From Icon', ShapeSourceIcon), + new ExampleItem('Custom Vector Source', CustomVectorSource), + new ExampleItem('Show Point Annotation', ShowPointAnnotation), + new ExampleItem('Create Offline Region', CreateOfflineRegion), + new ExampleItem('Animation Along a Line', DriveTheLine), + new ExampleItem('Image Overlay', ImageOverlay), + new ExampleItem('Data Driven Circle Colors', DataDrivenCircleColors), + new ExampleItem('Choropleth Layer By Zoom Level', ChoroplethLayerByZoomLevel), + new ExampleItem('Get Pixel Point in MapView', PointInMapView), + new ExampleItem('Take Snapshot Without Map', TakeSnapshot), + new ExampleItem('Take Snapshot With Map', TakeSnapshotWithMap), + new ExampleItem('Get Current Zoom', GetZoom), + new ExampleItem('Get Center', GetCenter), + new ExampleItem('User Location Updates', UserLocationChange), +]; + +class Home extends React.Component { + constructor(props) { + super(props); + this.renderItem = this.renderItem.bind(this); + } + + onExamplePress(activeExamplePosition) { + this.props.navigation.navigate('Demo', Examples[activeExamplePosition]); + } + + renderItem({item, index}) { + return ( + + this.onExamplePress(index)}> + + {item.label} + + + + + ); + } + + render() { + return ( + + + + + item.label} + renderItem={this.renderItem} + /> + + + ); + } +} + +export default Home; diff --git a/ios/RCTMGL.xcodeproj/project.pbxproj b/ios/RCTMGL.xcodeproj/project.pbxproj index 6cd19b2c23..dccf55adcb 100644 --- a/ios/RCTMGL.xcodeproj/project.pbxproj +++ b/ios/RCTMGL.xcodeproj/project.pbxproj @@ -11,9 +11,6 @@ C410D5D81F7AD0C100CF822A /* RCTMGLLightManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C410D5D71F7AD0C100CF822A /* RCTMGLLightManager.m */; }; C416015020112B5200006116 /* RNMBImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = C416014F20112B5200006116 /* RNMBImageUtils.m */; }; C41DCC9D1FBBA8F000895BB4 /* FilterList.m in Sources */ = {isa = PBXBuildFile; fileRef = C41DCC9C1FBBA8F000895BB4 /* FilterList.m */; }; - C42219B51FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = C42219B41FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.m */; }; - C42219BC1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C42219BB1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.m */; }; - C42219BF1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C42219BE1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.m */; }; C42236A41F6B012000FA9C1C /* RCTMGLStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = C42236A31F6B012000FA9C1C /* RCTMGLStyle.m */; }; C42236A71F6C7F4E00FA9C1C /* RCTMGLFillExtrusionLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C42236A61F6C7F4E00FA9C1C /* RCTMGLFillExtrusionLayer.m */; }; C42236AA1F6C7F6200FA9C1C /* RCTMGLFillExtrusionLayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C42236A91F6C7F6200FA9C1C /* RCTMGLFillExtrusionLayerManager.m */; }; @@ -24,6 +21,11 @@ C45339CD204780EB00888553 /* RCTMGLImageQueueOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C45339CC204780EB00888553 /* RCTMGLImageQueueOperation.m */; }; C470F5B81F9E79D400614A69 /* RCTMGLImageQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = C470F5B71F9E79D400614A69 /* RCTMGLImageQueue.m */; }; C470F6AC1FA11C9500614A69 /* MGLOfflineModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C470F6AB1FA11C9500614A69 /* MGLOfflineModule.m */; }; + C490EE0220DC565000CB2E57 /* RCTMGLLocationModule.m in Sources */ = {isa = PBXBuildFile; fileRef = C490EE0120DC565000CB2E57 /* RCTMGLLocationModule.m */; }; + C490EE0520DC575100CB2E57 /* RCTMGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C490EE0420DC575100CB2E57 /* RCTMGLLocationManager.m */; }; + C490EE0B20DC6B7C00CB2E57 /* RCTMGLLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = C490EE0A20DC6B7C00CB2E57 /* RCTMGLLocation.m */; }; + C490EE0E20DD70AD00CB2E57 /* RCTMGLCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = C490EE0D20DD70AD00CB2E57 /* RCTMGLCamera.m */; }; + C490EE1120DD710000CB2E57 /* RCTMGLCameraManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C490EE1020DD710000CB2E57 /* RCTMGLCameraManager.m */; }; C4C87A601F844C820064AE95 /* FilterParser.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C87A5F1F844C820064AE95 /* FilterParser.m */; }; C4C87B7B1F8FEBF50064AE95 /* RCTMGLPointAnnotationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C87B7A1F8FEBF40064AE95 /* RCTMGLPointAnnotationManager.m */; }; C4C87B7E1F8FEC090064AE95 /* RCTMGLPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = C4C87B7D1F8FEC090064AE95 /* RCTMGLPointAnnotation.m */; }; @@ -87,13 +89,6 @@ C416014F20112B5200006116 /* RNMBImageUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNMBImageUtils.m; sourceTree = ""; }; C41DCC9B1FBBA8F000895BB4 /* FilterList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilterList.h; sourceTree = ""; }; C41DCC9C1FBBA8F000895BB4 /* FilterList.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FilterList.m; sourceTree = ""; }; - C42219B31FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLFaux3DUserLocationAnnotationView.h; sourceTree = ""; }; - C42219B41FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLFaux3DUserLocationAnnotationView.m; sourceTree = ""; }; - C42219B91FEB211700EE9E35 /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = ""; }; - C42219BA1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingArrowLayer.h; sourceTree = ""; }; - C42219BB1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingArrowLayer.m; sourceTree = ""; }; - C42219BD1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingBeamLayer.h; sourceTree = ""; }; - C42219BE1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingBeamLayer.m; sourceTree = ""; }; C42236A21F6B012000FA9C1C /* RCTMGLStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMGLStyle.h; sourceTree = ""; }; C42236A31F6B012000FA9C1C /* RCTMGLStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMGLStyle.m; sourceTree = ""; }; C42236A51F6C7F4E00FA9C1C /* RCTMGLFillExtrusionLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMGLFillExtrusionLayer.h; sourceTree = ""; }; @@ -115,6 +110,17 @@ C470F5B71F9E79D400614A69 /* RCTMGLImageQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLImageQueue.m; sourceTree = ""; }; C470F6AA1FA11C9500614A69 /* MGLOfflineModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLOfflineModule.h; sourceTree = ""; }; C470F6AB1FA11C9500614A69 /* MGLOfflineModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLOfflineModule.m; sourceTree = ""; }; + C490EE0020DC565000CB2E57 /* RCTMGLLocationModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLLocationModule.h; sourceTree = ""; }; + C490EE0120DC565000CB2E57 /* RCTMGLLocationModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLLocationModule.m; sourceTree = ""; }; + C490EE0320DC575100CB2E57 /* RCTMGLLocationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLLocationManager.h; sourceTree = ""; }; + C490EE0420DC575100CB2E57 /* RCTMGLLocationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLLocationManager.m; sourceTree = ""; }; + C490EE0620DC5CDB00CB2E57 /* RCTMGLLocationManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLLocationManagerDelegate.h; sourceTree = ""; }; + C490EE0720DC66DD00CB2E57 /* RCTMGLLocation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLLocation.h; sourceTree = ""; }; + C490EE0A20DC6B7C00CB2E57 /* RCTMGLLocation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLLocation.m; sourceTree = ""; }; + C490EE0C20DD70AD00CB2E57 /* RCTMGLCamera.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLCamera.h; sourceTree = ""; }; + C490EE0D20DD70AD00CB2E57 /* RCTMGLCamera.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLCamera.m; sourceTree = ""; }; + C490EE0F20DD710000CB2E57 /* RCTMGLCameraManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLCameraManager.h; sourceTree = ""; }; + C490EE1020DD710000CB2E57 /* RCTMGLCameraManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMGLCameraManager.m; sourceTree = ""; }; C4C87A5E1F844C820064AE95 /* FilterParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilterParser.h; sourceTree = ""; }; C4C87A5F1F844C820064AE95 /* FilterParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FilterParser.m; sourceTree = ""; }; C4C87B791F8FEBF40064AE95 /* RCTMGLPointAnnotationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTMGLPointAnnotationManager.h; sourceTree = ""; }; @@ -219,13 +225,11 @@ C42219B21FEB1FD600EE9E35 /* Location */ = { isa = PBXGroup; children = ( - C42219B31FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.h */, - C42219B41FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.m */, - C42219B91FEB211700EE9E35 /* MGLUserLocationHeadingIndicator.h */, - C42219BA1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.h */, - C42219BB1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.m */, - C42219BD1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.h */, - C42219BE1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.m */, + C490EE0320DC575100CB2E57 /* RCTMGLLocationManager.h */, + C490EE0420DC575100CB2E57 /* RCTMGLLocationManager.m */, + C490EE0620DC5CDB00CB2E57 /* RCTMGLLocationManagerDelegate.h */, + C490EE0720DC66DD00CB2E57 /* RCTMGLLocation.h */, + C490EE0A20DC6B7C00CB2E57 /* RCTMGLLocation.m */, ); name = Location; sourceTree = ""; @@ -330,6 +334,8 @@ C470F6AB1FA11C9500614A69 /* MGLOfflineModule.m */, C4FD1DC31FD1F04200213AF2 /* MGLSnapshotModule.h */, C4FD1DC41FD1F04200213AF2 /* MGLSnapshotModule.m */, + C490EE0020DC565000CB2E57 /* RCTMGLLocationModule.h */, + C490EE0120DC565000CB2E57 /* RCTMGLLocationModule.m */, ); name = Modules; sourceTree = ""; @@ -363,6 +369,10 @@ C4EAF1261F6083C70016DEE8 /* CameraUpdateItem.m */, C4EAF1281F6084920016DEE8 /* CameraUpdateQueue.h */, C4EAF1291F6084920016DEE8 /* CameraUpdateQueue.m */, + C490EE0C20DD70AD00CB2E57 /* RCTMGLCamera.h */, + C490EE0D20DD70AD00CB2E57 /* RCTMGLCamera.m */, + C490EE0F20DD710000CB2E57 /* RCTMGLCameraManager.h */, + C490EE1020DD710000CB2E57 /* RCTMGLCameraManager.m */, ); name = Camera; sourceTree = ""; @@ -528,13 +538,14 @@ C42236AA1F6C7F6200FA9C1C /* RCTMGLFillExtrusionLayerManager.m in Sources */, C4FB12331F799F190055AE1F /* RCTMGLRasterLayer.m in Sources */, C4D1444C1F4E178100396F26 /* RCTMGLMapView.m in Sources */, - C42219BF1FEB21D500EE9E35 /* MGLUserLocationHeadingBeamLayer.m in Sources */, C410D5D51F7AD0B300CF822A /* RCTMGLLight.m in Sources */, + C490EE0520DC575100CB2E57 /* RCTMGLLocationManager.m in Sources */, C42236A41F6B012000FA9C1C /* RCTMGLStyle.m in Sources */, C4FD1DC21FCF550E00213AF2 /* RCTMGLImageSourceManager.m in Sources */, C4D1444F1F4E181100396F26 /* RCTMGLMapViewManager.m in Sources */, C4FB12361F799F280055AE1F /* RCTMGLRasterLayerManager.m in Sources */, C4FB110D1F71D0B10055AE1F /* RCTMGLShapeSourceManager.m in Sources */, + C490EE0B20DC6B7C00CB2E57 /* RCTMGLLocation.m in Sources */, C4EAF1921F6341520016DEE8 /* RCTMGLFillLayerManager.m in Sources */, C4D144971F4E2F1100396F26 /* RCTMGLUtils.m in Sources */, C43CA50D1F53A4EF000B9CB7 /* RCTMGLEventTypes.m in Sources */, @@ -543,6 +554,7 @@ C4EAF1241F606F7C0016DEE8 /* CameraMode.m in Sources */, C4D144421F4E16F600396F26 /* RCTMGL.m in Sources */, C42236A71F6C7F4E00FA9C1C /* RCTMGLFillExtrusionLayer.m in Sources */, + C490EE0E20DD70AD00CB2E57 /* RCTMGLCamera.m in Sources */, C4FB11011F707A280055AE1F /* RCTMGLLineLayerManager.m in Sources */, C43CA5061F507BCC000B9CB7 /* RCTMGLMapTouchEvent.m in Sources */, C4FB12301F7972B40055AE1F /* RCTMGLRasterSourceManager.m in Sources */, @@ -550,14 +562,14 @@ C4FB10FE1F707A140055AE1F /* RCTMGLLineLayer.m in Sources */, C4EAF1831F6336E20016DEE8 /* RCTMGLVectorSource.m in Sources */, C4EAF1891F633C010016DEE8 /* RCTMGLVectorSourceManager.m in Sources */, - C42219BC1FEB215E00EE9E35 /* MGLUserLocationHeadingArrowLayer.m in Sources */, + C490EE1120DD710000CB2E57 /* RCTMGLCameraManager.m in Sources */, C4FD1DC51FD1F04300213AF2 /* MGLSnapshotModule.m in Sources */, C4FB122D1F7972A00055AE1F /* RCTMGLRasterSource.m in Sources */, C4C87B941F9143DA0064AE95 /* RCTMGLCalloutManager.m in Sources */, C410D5D81F7AD0C100CF822A /* RCTMGLLightManager.m in Sources */, C4EAF17D1F6324970016DEE8 /* RCTMGLSource.m in Sources */, + C490EE0220DC565000CB2E57 /* RCTMGLLocationModule.m in Sources */, C4FB11131F71EC9F0055AE1F /* RCTMGLSymbolLayerManager.m in Sources */, - C42219B51FEB205300EE9E35 /* MGLFaux3DUserLocationAnnotationView.m in Sources */, C4C87A601F844C820064AE95 /* FilterParser.m in Sources */, C4FD1DBF1FCF54FB00213AF2 /* RCTMGLImageSource.m in Sources */, C41DCC9D1FBBA8F000895BB4 /* FilterList.m in Sources */, diff --git a/ios/RCTMGL/CameraStop.h b/ios/RCTMGL/CameraStop.h index 674b644012..1f7fb73461 100644 --- a/ios/RCTMGL/CameraStop.h +++ b/ios/RCTMGL/CameraStop.h @@ -7,6 +7,7 @@ // @import Mapbox; +#import "RCTMGLCamera.h" @interface CameraStop : NSObject diff --git a/ios/RCTMGL/CameraStop.m b/ios/RCTMGL/CameraStop.m index 78ae35a068..816cb9e4d7 100644 --- a/ios/RCTMGL/CameraStop.m +++ b/ios/RCTMGL/CameraStop.m @@ -9,6 +9,7 @@ #import "CameraStop.h" #import "CameraMode.h" #import "RCTMGLUtils.h" +#import "RCTMGLCamera.h" @implementation CameraStop diff --git a/ios/RCTMGL/CameraUpdateItem.m b/ios/RCTMGL/CameraUpdateItem.m index 7cfcf4304c..0d9246de04 100644 --- a/ios/RCTMGL/CameraUpdateItem.m +++ b/ios/RCTMGL/CameraUpdateItem.m @@ -70,12 +70,14 @@ - (void)_fitBoundsCamera:(RCTMGLMapView*)mapView withCompletionHandler:(void (^) - (void)_centerCoordWithZoomCamera:(RCTMGLMapView*)mapView animated:(BOOL)animated withCompletionHandler:(void (^)(void))completionHandler { - CLLocationDirection direction = _cameraStop.heading != nil ? [_cameraStop.heading doubleValue] : mapView.direction; - [mapView setCenterCoordinate:_cameraStop.coordinate - zoomLevel:[_cameraStop.zoom doubleValue] - direction:direction - animated:animated - completionHandler:completionHandler]; + MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:_cameraStop.coordinate + fromDistance:[mapView altitudeFromZoom:[_cameraStop.zoom doubleValue]] + pitch:[_cameraStop.pitch floatValue] + heading:[_cameraStop.heading floatValue]]; + [mapView setCamera:camera + withDuration:animated ? _cameraStop.duration : 0 + animationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] + completionHandler:completionHandler]; } - (MGLMapCamera*)_makeCamera:(RCTMGLMapView*)mapView diff --git a/ios/RCTMGL/CameraUpdateQueue.h b/ios/RCTMGL/CameraUpdateQueue.h index 78734c51b1..45332bac69 100644 --- a/ios/RCTMGL/CameraUpdateQueue.h +++ b/ios/RCTMGL/CameraUpdateQueue.h @@ -16,6 +16,6 @@ - (CameraStop* _Nonnull)dequeue; - (void)flush; - (BOOL)isEmpty; -- (void)execute:(RCTMGLMapView* _Nonnull)mapView withCompletionHandler:(nullable void (^)(void))completionHandler; +- (void)execute:(RCTMGLMapView* _Nonnull)mapView; @end diff --git a/ios/RCTMGL/CameraUpdateQueue.m b/ios/RCTMGL/CameraUpdateQueue.m index 6cc0552f0b..35c4d43a53 100644 --- a/ios/RCTMGL/CameraUpdateQueue.m +++ b/ios/RCTMGL/CameraUpdateQueue.m @@ -47,12 +47,13 @@ - (BOOL)isEmpty return queue.count == 0; } -- (void)execute:(RCTMGLMapView*)mapView withCompletionHandler:(nullable void (^)(void))completeAllHandler +- (void)execute:(RCTMGLMapView*)mapView { + if (mapView == nil) { + return; + } + if ([self isEmpty]) { - if (completeAllHandler != nil) { - completeAllHandler(); - } return; } @@ -65,7 +66,9 @@ - (void)execute:(RCTMGLMapView*)mapView withCompletionHandler:(nullable void (^) item.cameraStop = stop; __weak CameraUpdateQueue *weakSelf = self; - [item execute:mapView withCompletionHandler:^{ [weakSelf execute:mapView withCompletionHandler:completeAllHandler]; }]; + __weak RCTMGLMapView *weakMap = mapView; + + [item execute:mapView withCompletionHandler:^{ [weakSelf execute:weakMap]; }]; } @end diff --git a/ios/RCTMGL/MGLFaux3DUserLocationAnnotationView.m b/ios/RCTMGL/MGLFaux3DUserLocationAnnotationView.m index a0597dba31..ea6a331ae2 100644 --- a/ios/RCTMGL/MGLFaux3DUserLocationAnnotationView.m +++ b/ios/RCTMGL/MGLFaux3DUserLocationAnnotationView.m @@ -54,7 +54,8 @@ - (void)update if (CLLocationCoordinate2DIsValid(self.userLocation.coordinate)) { RCTMGLMapView *reactMapView = (RCTMGLMapView *)self.mapView; - (reactMapView.reactUserTrackingMode == MGLUserTrackingModeFollowWithCourse) ? [self drawPuck] : [self drawDot]; + // FM - TODO + (reactMapView.userTrackingMode == MGLUserTrackingModeFollowWithCourse) ? [self drawPuck] : [self drawDot]; [self updatePitch]; } @@ -234,7 +235,7 @@ - (void)drawDot // heading indicator (tinted, beam or arrow) RCTMGLMapView *reactMapView = (RCTMGLMapView *)self.mapView; - BOOL headingTrackingModeEnabled = reactMapView.reactUserTrackingMode == MGLUserTrackingModeFollowWithHeading; + BOOL headingTrackingModeEnabled = reactMapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading; BOOL showHeadingIndicator = self.mapView.showsUserHeadingIndicator || headingTrackingModeEnabled; if (showHeadingIndicator) diff --git a/ios/RCTMGL/MGLModule.m b/ios/RCTMGL/MGLModule.m index 5632f661c6..5ca78d2474 100644 --- a/ios/RCTMGL/MGLModule.m +++ b/ios/RCTMGL/MGLModule.m @@ -32,9 +32,7 @@ + (BOOL)requiresMainQueueSetup [styleURLS setObject:[MGLStyle.outdoorsStyleURL absoluteString] forKey:@"Outdoors"]; [styleURLS setObject:[MGLStyle.satelliteStyleURL absoluteString] forKey:@"Satellite"]; [styleURLS setObject:[MGLStyle.satelliteStreetsStyleURL absoluteString] forKey:@"SatelliteStreet"]; - [styleURLS setObject:[MGLStyle.trafficDayStyleURL absoluteString] forKey:@"TrafficDay"]; - [styleURLS setObject:[MGLStyle.trafficNightStyleURL absoluteString] forKey:@"TrafficNight"]; - + // event types NSMutableDictionary *eventTypes = [[NSMutableDictionary alloc] init]; [eventTypes setObject:RCT_MAPBOX_EVENT_TAP forKey:@"MapClick"]; @@ -52,88 +50,84 @@ + (BOOL)requiresMainQueueSetup [eventTypes setObject:RCT_MAPBOX_DID_FINISH_RENDERING_MAP forKey:@"DidFinishRenderingMap"]; [eventTypes setObject:RCT_MAPBOX_DID_FINISH_RENDERING_MAP_FULLY forKey:@"DidFinishRenderingMapFully"]; [eventTypes setObject:RCT_MAPBOX_DID_FINISH_LOADING_STYLE forKey:@"DidFinishLoadingStyle"]; - [eventTypes setObject:RCT_MAPBOX_USER_LOCATION_UPDATE forKey:@"UserLocationUpdated"]; + // location module events + NSMutableDictionary *locationModuleEvents = [[NSMutableDictionary alloc] init]; + [locationModuleEvents setObject:RCT_MAPBOX_USER_LOCATION_UPDATE forKey:@"Update"]; + // user tracking modes NSMutableDictionary *userTrackingModes = [[NSMutableDictionary alloc] init]; [userTrackingModes setObject:[NSNumber numberWithInt:MGLUserTrackingModeNone] forKey:@"None"]; [userTrackingModes setObject:[NSNumber numberWithInt:MGLUserTrackingModeFollow] forKey:@"Follow"]; [userTrackingModes setObject:[NSNumber numberWithInt:MGLUserTrackingModeFollowWithHeading] forKey:@"FollowWithHeading"]; [userTrackingModes setObject:[NSNumber numberWithInt:MGLUserTrackingModeFollowWithCourse] forKey:@"FollowWithCourse"]; - + // user location vertical alignment NSMutableDictionary *userLocationVerticalAlignment = [[NSMutableDictionary alloc] init]; [userLocationVerticalAlignment setObject:[NSNumber numberWithInt:MGLAnnotationVerticalAlignmentTop] forKey:@"Top"]; [userLocationVerticalAlignment setObject:[NSNumber numberWithInt:MGLAnnotationVerticalAlignmentCenter] forKey:@"Center"]; [userLocationVerticalAlignment setObject:[NSNumber numberWithInt:MGLAnnotationVerticalAlignmentBottom] forKey:@"Bottom"]; - + // camera modes NSMutableDictionary *cameraModes = [[NSMutableDictionary alloc] init]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_FLIGHT] forKey:@"Flight"]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_EASE] forKey:@"Ease"]; [cameraModes setObject:[NSNumber numberWithInt:RCT_MAPBOX_CAMERA_MODE_NONE] forKey:@"None"]; - + // style sources NSMutableDictionary *styleSourceConsts = [[NSMutableDictionary alloc] init]; [styleSourceConsts setObject:DEFAULT_SOURCE_ID forKey:@"DefaultSourceID"]; - // interpolation modes - NSMutableDictionary *interpolationModes = [[NSMutableDictionary alloc] init]; - [interpolationModes setObject:@(MGLInterpolationModeExponential) forKey:@"Exponential"]; - [interpolationModes setObject:@(MGLInterpolationModeCategorical) forKey:@"Categorical"]; - [interpolationModes setObject:@(MGLInterpolationModeInterval) forKey:@"Interval"]; - [interpolationModes setObject:@(MGLInterpolationModeIdentity) forKey:@"Identity"]; - // line layer constants NSMutableDictionary *lineJoin = [[NSMutableDictionary alloc] init]; [lineJoin setObject:@(MGLLineJoinBevel) forKey:@"Bevel"]; [lineJoin setObject:@(MGLLineJoinRound) forKey:@"Round"]; [lineJoin setObject:@(MGLLineJoinMiter) forKey:@"Miter"]; - + NSMutableDictionary *lineCap = [[NSMutableDictionary alloc] init]; [lineCap setObject:@(MGLLineCapButt) forKey:@"Butt"]; [lineCap setObject:@(MGLLineCapRound) forKey:@"Round"]; [lineCap setObject:@(MGLLineCapSquare) forKey:@"Square"]; - + NSMutableDictionary *lineTranslateAnchor = [[NSMutableDictionary alloc] init]; [lineTranslateAnchor setObject:@(MGLLineTranslationAnchorMap) forKey:@"Map"]; [lineTranslateAnchor setObject:@(MGLLineTranslationAnchorViewport) forKey:@"Viewport"]; - + // circle layer constants NSMutableDictionary *circlePitchScale = [[NSMutableDictionary alloc] init]; [circlePitchScale setObject:@(MGLCircleScaleAlignmentMap) forKey:@"Map"]; [circlePitchScale setObject:@(MGLCircleScaleAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *circlePitchAlignment = [[NSMutableDictionary alloc] init]; [circlePitchAlignment setObject:@(MGLCirclePitchAlignmentMap) forKey:@"Map"]; [circlePitchAlignment setObject:@(MGLCirclePitchAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *circleTranslateAnchor = [[NSMutableDictionary alloc] init]; [circleTranslateAnchor setObject:@(MGLCircleTranslationAnchorMap) forKey:@"Map"]; [circleTranslateAnchor setObject:@(MGLCircleTranslationAnchorViewport) forKey:@"Viewport"]; - + // fill extrusion layer constants NSMutableDictionary *fillExtrusionTranslateAnchor = [[NSMutableDictionary alloc] init]; [fillExtrusionTranslateAnchor setObject:@(MGLFillExtrusionTranslationAnchorMap) forKey:@"Map"]; [fillExtrusionTranslateAnchor setObject:@(MGLFillExtrusionTranslationAnchorViewport) forKey:@"Viewport"]; - + // fill layer constants NSMutableDictionary *fillTranslateAnchor = [[NSMutableDictionary alloc] init]; [fillTranslateAnchor setObject:@(MGLFillTranslationAnchorMap) forKey:@"Map"]; [fillTranslateAnchor setObject:@(MGLFillTranslationAnchorViewport) forKey:@"Viewport"]; - + // symbol layer constants NSMutableDictionary *iconRotationAlignment = [[NSMutableDictionary alloc] init]; [iconRotationAlignment setObject:@(MGLIconRotationAlignmentAuto) forKey:@"Auto"]; [iconRotationAlignment setObject:@(MGLIconRotationAlignmentMap) forKey:@"Map"]; [iconRotationAlignment setObject:@(MGLIconRotationAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *iconTextFit = [[NSMutableDictionary alloc] init]; [iconTextFit setObject:@(MGLIconTextFitNone) forKey:@"None"]; [iconTextFit setObject:@(MGLIconTextFitWidth) forKey:@"Width"]; [iconTextFit setObject:@(MGLIconTextFitHeight) forKey:@"Height"]; [iconTextFit setObject:@(MGLIconTextFitBoth) forKey:@"Both"]; - + NSMutableDictionary *iconAnchor = [[NSMutableDictionary alloc] init]; [iconAnchor setObject:@(MGLIconAnchorCenter) forKey:@"Center"]; [iconAnchor setObject:@(MGLIconAnchorTop) forKey:@"Top"]; @@ -144,20 +138,20 @@ + (BOOL)requiresMainQueueSetup [iconAnchor setObject:@(MGLIconAnchorTopRight) forKey:@"TopRight"]; [iconAnchor setObject:@(MGLIconAnchorBottomLeft) forKey:@"BottomLeft"]; [iconAnchor setObject:@(MGLIconAnchorBottomRight) forKey:@"BottomRight"]; - + NSMutableDictionary *iconTranslateAnchor = [[NSMutableDictionary alloc] init]; [iconTranslateAnchor setObject:@(MGLIconTranslationAnchorMap) forKey:@"Map"]; [iconTranslateAnchor setObject:@(MGLIconTranslationAnchorViewport) forKey:@"Viewport"]; - + NSMutableDictionary *iconPitchAlignment = [[NSMutableDictionary alloc] init]; [iconPitchAlignment setObject:@(MGLIconPitchAlignmentAuto) forKey:@"Auto"]; [iconPitchAlignment setObject:@(MGLIconPitchAlignmentMap) forKey:@"Map"]; [iconPitchAlignment setObject:@(MGLIconPitchAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *symbolPlacement = [[NSMutableDictionary alloc] init]; [symbolPlacement setObject:@(MGLSymbolPlacementLine) forKey:@"Line"]; [symbolPlacement setObject:@(MGLSymbolPlacementPoint) forKey:@"Point"]; - + NSMutableDictionary *textAnchor = [[NSMutableDictionary alloc] init]; [textAnchor setObject:@(MGLTextAnchorCenter) forKey:@"Center"]; [textAnchor setObject:@(MGLTextAnchorLeft) forKey:@"Left"]; @@ -168,46 +162,46 @@ + (BOOL)requiresMainQueueSetup [textAnchor setObject:@(MGLTextAnchorTopRight) forKey:@"TopRight"]; [textAnchor setObject:@(MGLTextAnchorBottomLeft) forKey:@"BottomLeft"]; [textAnchor setObject:@(MGLTextAnchorBottomRight) forKey:@"BottomRight"]; - + NSMutableDictionary *textJustify = [[NSMutableDictionary alloc] init]; [textJustify setObject:@(MGLTextJustificationCenter) forKey:@"Center"]; [textJustify setObject:@(MGLTextJustificationLeft) forKey:@"Left"]; [textJustify setObject:@(MGLTextJustificationRight) forKey:@"Right"]; - + NSMutableDictionary *textPitchAlignment = [[NSMutableDictionary alloc] init]; [textPitchAlignment setObject:@(MGLTextPitchAlignmentAuto) forKey:@"Auto"]; [textPitchAlignment setObject:@(MGLTextPitchAlignmentMap) forKey:@"Map"]; [textPitchAlignment setObject:@(MGLTextPitchAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *textRotationAlignment = [[NSMutableDictionary alloc] init]; [textRotationAlignment setObject:@(MGLTextRotationAlignmentAuto) forKey:@"Auto"]; [textRotationAlignment setObject:@(MGLTextRotationAlignmentMap) forKey:@"Map"]; [textRotationAlignment setObject:@(MGLTextRotationAlignmentViewport) forKey:@"Viewport"]; - + NSMutableDictionary *textTransform = [[NSMutableDictionary alloc] init]; [textTransform setObject:@(MGLTextTransformNone) forKey:@"None"]; [textTransform setObject:@(MGLTextTransformLowercase) forKey:@"Lowercase"]; [textTransform setObject:@(MGLTextTransformUppercase) forKey:@"Uppercase"]; - + NSMutableDictionary *textTranslateAnchor = [[NSMutableDictionary alloc] init]; [textTranslateAnchor setObject:@(MGLTextTranslationAnchorMap) forKey:@"Map"]; [textTranslateAnchor setObject:@(MGLTextTranslationAnchorViewport) forKey:@"Viewport"]; - + // light constants NSMutableDictionary *lightAnchor = [[NSMutableDictionary alloc] init]; [lightAnchor setObject:@(MGLLightAnchorMap) forKey:@"Map"]; [lightAnchor setObject:@(MGLLightAnchorViewport) forKey:@"Viewport"]; - + // offline module callback names NSMutableDictionary *offlineModuleCallbackNames = [[NSMutableDictionary alloc] init]; [offlineModuleCallbackNames setObject:RCT_MAPBOX_OFFLINE_CALLBACK_ERROR forKey:@"Error"]; [offlineModuleCallbackNames setObject:RCT_MAPBOX_OFFLINE_CALLBACK_PROGRESS forKey:@"Progress"]; - + NSMutableDictionary *offlinePackDownloadState = [[NSMutableDictionary alloc] init]; [offlinePackDownloadState setObject:@(MGLOfflinePackStateInactive) forKey:@"Inactive"]; [offlinePackDownloadState setObject:@(MGLOfflinePackStateActive) forKey:@"Active"]; [offlinePackDownloadState setObject:@(MGLOfflinePackStateComplete) forKey:@"Complete"]; - + return @{ @"StyleURL": styleURLS, @"EventTypes": eventTypes, @@ -215,7 +209,6 @@ + (BOOL)requiresMainQueueSetup @"UserLocationVerticalAlignment": userLocationVerticalAlignment, @"CameraModes": cameraModes, @"StyleSource": styleSourceConsts, - @"InterpolationMode": interpolationModes, @"LineJoin": lineJoin, @"LineCap": lineCap, @"LineTranslateAnchor": lineTranslateAnchor, @@ -238,7 +231,8 @@ + (BOOL)requiresMainQueueSetup @"TextTranslateAnchor": textTranslateAnchor, @"LightAnchor": lightAnchor, @"OfflineCallbackName": offlineModuleCallbackNames, - @"OfflinePackDownloadState": offlinePackDownloadState + @"OfflinePackDownloadState": offlinePackDownloadState, + @"LocationCallbackName": locationModuleEvents }; } @@ -250,12 +244,12 @@ + (BOOL)requiresMainQueueSetup RCT_EXPORT_METHOD(getAccessToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSString *accessToken = MGLAccountManager.accessToken; - + if (accessToken != nil) { resolve(accessToken); return; } - + reject(@"missing_access_token", @"No access token has been set", nil); } diff --git a/ios/RCTMGL/RCTMGLCamera.h b/ios/RCTMGL/RCTMGLCamera.h new file mode 100644 index 0000000000..d343b6d2a2 --- /dev/null +++ b/ios/RCTMGL/RCTMGLCamera.h @@ -0,0 +1,30 @@ +// +// RCTMGLCamera.h +// RCTMGL +// +// Created by Nick Italiano on 6/22/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import + +@class RCTMGLMapView; + +@interface RCTMGLCamera : UIView + +@property (nonatomic, strong) NSDictionary *stop; +@property (nonatomic, strong) RCTMGLMapView *map; + +@property (nonatomic, copy) NSNumber *animationDuration; +@property (nonatomic, copy) NSString *animationMode; + +@property (nonatomic, assign) BOOL followUserLocation; +@property (nonatomic, copy) NSString *followUserMode; +@property (nonatomic, copy) NSNumber *followZoomLevel; +@property (nonatomic, copy) NSNumber *followPitch; +@property (nonatomic, copy) NSNumber *followHeading; + +@property (nonatomic, copy) NSString *alignment; +@property (nonatomic, copy, readonly) NSNumber *cameraAnimationMode; + +@end diff --git a/ios/RCTMGL/RCTMGLCamera.m b/ios/RCTMGL/RCTMGLCamera.m new file mode 100644 index 0000000000..4fc0503f50 --- /dev/null +++ b/ios/RCTMGL/RCTMGLCamera.m @@ -0,0 +1,151 @@ +// +// RCTMGLCamera.m +// RCTMGL +// +// Created by Nick Italiano on 6/22/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import "RCTMGLCamera.h" +#import "CameraStop.h" +#import "CameraUpdateQueue.h" +#import "RCTMGLLocation.h" +#import "RCTMGLUtils.h" +#import "RCTMGLLocationManager.h" + +@implementation RCTMGLCamera +{ + CameraUpdateQueue *cameraUpdateQueue; + RCTMGLCamera *followCamera; +} + +- (instancetype)init +{ + if (self = [super init]) { + cameraUpdateQueue = [[CameraUpdateQueue alloc] init]; + } + return self; +} + +- (void)setStop:(NSDictionary *)stop +{ + _stop = stop; + + if (_map != nil) { + if (_followUserLocation) { + [self _updateCameraFromTrackingMode]; + } else { + [self _updateCameraFromJavascript]; + } + } +} + +- (void)setMap:(RCTMGLMapView *)map +{ + _map = map; + + if (_map != nil) { + if (_followUserLocation) { + [self _updateCameraFromTrackingMode]; + } else { + [self _updateCameraFromJavascript]; + } + } +} + +- (void)setFollowUserLocation:(BOOL)followUserLocation +{ + _followUserLocation = followUserLocation; + [self _updateCameraFromTrackingMode]; +} + +- (void)setFollowUserMode:(NSString *)followUserMode +{ + _followUserMode = followUserMode; + [self _updateCameraFromTrackingMode]; +} + +- (void)setFollowPitch:(NSNumber *)followPitch +{ + _followPitch = followPitch; + [self _updateCameraFromTrackingMode]; +} + +- (void)setFollowZoomLevel:(NSNumber *)followZoomLevel +{ + _followZoomLevel = followZoomLevel; + [self _updateCameraFromTrackingMode]; +} + +- (void)setFollowHeading:(NSNumber *)followHeading +{ + _followHeading = followHeading; + [self _updateCameraFromTrackingMode]; +} + +- (void)_updateCameraFromJavascript +{ + if (_stop == nil) { + return; + } + + if (_followUserLocation) { + return; + } + + if (_map != nil && _map.userTrackingMode != MGLUserTrackingModeNone) { + _map.userTrackingMode = MGLUserTrackingModeNone; + } + + [cameraUpdateQueue enqueue:[CameraStop fromDictionary:_stop]]; + [cameraUpdateQueue execute:_map]; +} + +- (void)_updateCameraFromTrackingMode +{ + if (!_followUserLocation || _map == nil) { + _map.userTrackingMode = MGLUserTrackingModeNone; + return; + } + + if (_map.userTrackingMode != [self _userTrackingMode]) { + _map.showsUserLocation = [self _userTrackingMode] != MGLUserTrackingModeNone; + _map.userTrackingMode = [self _userTrackingMode]; + } + + MGLMapCamera *camera = _map.camera; + if (_followPitch != nil && [_followPitch floatValue] >= 0.0) { + camera.pitch = [_followPitch floatValue]; + } else if (_stop != nil && _stop[@"pitch"] != nil) { + camera.pitch = [_stop[@"pitch"] floatValue]; + } + + if ([self _userTrackingMode] != MGLUserTrackingModeFollowWithCourse && [self _userTrackingMode] != MGLUserTrackingModeFollowWithHeading) { + if (_followHeading != nil && [_followHeading floatValue] >= 0.0) { + camera.heading = [_followHeading floatValue]; + } else if (_stop != nil && _stop[@"heading"] != nil) { + camera.heading = [_stop[@"heading"] floatValue]; + } + } + + if (_followZoomLevel != nil && [_followZoomLevel doubleValue] >= 0.0) { + camera.altitude = [_map altitudeFromZoom:[_followZoomLevel doubleValue]]; + } + + [_map setCamera:camera animated:YES]; +} + +- (NSUInteger)_userTrackingMode +{ + if ([_followUserMode isEqualToString:@"heading"]) { + return MGLUserTrackingModeFollowWithHeading; + } else if ([_followUserMode isEqualToString:@"course"]) { + return MGLUserTrackingModeFollowWithCourse; + } else if (_followUserLocation) { + return MGLUserTrackingModeFollow; + } else { + return MGLUserTrackingModeNone; + } +} + +@end diff --git a/ios/RCTMGL/RCTMGLCameraManager.h b/ios/RCTMGL/RCTMGLCameraManager.h new file mode 100644 index 0000000000..8b9ef9a01c --- /dev/null +++ b/ios/RCTMGL/RCTMGLCameraManager.h @@ -0,0 +1,13 @@ +// +// RCTMGLCameraManager.h +// RCTMGL +// +// Created by Nick Italiano on 6/22/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import "ViewManager.h" + +@interface RCTMGLCameraManager : ViewManager + +@end diff --git a/ios/RCTMGL/RCTMGLCameraManager.m b/ios/RCTMGL/RCTMGLCameraManager.m new file mode 100644 index 0000000000..796ca540d0 --- /dev/null +++ b/ios/RCTMGL/RCTMGLCameraManager.m @@ -0,0 +1,43 @@ +// +// RCTMGLCameraManager.m +// RCTMGL +// +// Created by Nick Italiano on 6/22/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import "RCTMGLCameraManager.h" +#import "RCTMGLCamera.h" + +@implementation RCTMGLCameraManager + +RCT_EXPORT_MODULE(RCTMGLCamera) + +#pragma - View Properties + +RCT_EXPORT_VIEW_PROPERTY(stop, NSDictionary) + +RCT_EXPORT_VIEW_PROPERTY(animationDuration, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(animationMode, NSString) + +RCT_EXPORT_VIEW_PROPERTY(followUserLocation, BOOL) +RCT_EXPORT_VIEW_PROPERTY(followUserMode, NSString) +RCT_EXPORT_VIEW_PROPERTY(followZoomLevel, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(followPitch, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(followHeading, NSNumber) + +RCT_EXPORT_VIEW_PROPERTY(alignment, NSString) + +#pragma Methods + +- (BOOL)requiresMainQueueSetup +{ + return YES; +} + +- (UIView *)view +{ + return [[RCTMGLCamera alloc] init]; +} + +@end diff --git a/ios/RCTMGL/RCTMGLEventTypes.h b/ios/RCTMGL/RCTMGLEventTypes.h index 77b645e093..017b7889f4 100644 --- a/ios/RCTMGL/RCTMGLEventTypes.h +++ b/ios/RCTMGL/RCTMGLEventTypes.h @@ -42,4 +42,7 @@ extern NSString *const RCT_MAPBOX_OFFLINE_TILE_LIMIT; extern NSString *const RCT_MAPBOX_SHAPE_SOURCE_LAYER_PRESS; extern NSString *const RCT_MAPBOX_VECTOR_SOURCE_LAYER_PRESS; + +extern NSString *const RCT_MAPBOX_USER_LOCATION_UPDATE; + @end diff --git a/ios/RCTMGL/RCTMGLEventTypes.m b/ios/RCTMGL/RCTMGLEventTypes.m index 12b7d2141a..51bb31d40e 100644 --- a/ios/RCTMGL/RCTMGLEventTypes.m +++ b/ios/RCTMGL/RCTMGLEventTypes.m @@ -13,9 +13,6 @@ @implementation RCTMGLEventTypes NSString *const RCT_MAPBOX_EVENT_TAP = @"press"; NSString *const RCT_MAPBOX_EVENT_LONGPRESS = @"longpress"; -NSString *const RCT_MAPBOX_USER_LOCATION_UPDATE = @"userlocationdupdated"; -NSString *const RCT_MAPBOX_USER_TRACKING_MODE_CHANGE = @"usertrackingmodechange"; - NSString *const RCT_MAPBOX_REGION_WILL_CHANGE_EVENT = @"regionwillchange"; NSString *const RCT_MAPBOX_REGION_IS_CHANGING = @"regionischanging"; NSString *const RCT_MAPBOX_REGION_DID_CHANGE = @"regiondidchange"; @@ -43,4 +40,6 @@ @implementation RCTMGLEventTypes NSString *const RCT_MAPBOX_SHAPE_SOURCE_LAYER_PRESS = @"shapesourcelayerpress"; NSString *const RCT_MAPBOX_VECTOR_SOURCE_LAYER_PRESS = @"vectorsourcelayerpress"; +NSString *const RCT_MAPBOX_USER_LOCATION_UPDATE = @"MapboxUserLocationUpdate"; + @end diff --git a/ios/RCTMGL/RCTMGLLayer.m b/ios/RCTMGL/RCTMGLLayer.m index 820cfac9fe..d92fb1d90d 100644 --- a/ios/RCTMGL/RCTMGLLayer.m +++ b/ios/RCTMGL/RCTMGLLayer.m @@ -79,9 +79,7 @@ - (void)setFilter:(NSArray *> *)filter if (_styleLayer != nil) { NSPredicate *predicate = [self buildFilters]; - if (predicate != nil) { - [self updateFilter:predicate]; - } + [self updateFilter:predicate]; } } @@ -99,7 +97,10 @@ -(void)setReactStyle:(NSDictionary *)reactStyle - (void)addToMap:(MGLStyle *)style { _style = style; - + if (_id == nil) { + RCTLogError(@"Cannot add a layer without id to the map: %@", self); + return; + } MGLStyleLayer *existingLayer = [style layerWithIdentifier:_id]; if (existingLayer != nil) { _styleLayer = existingLayer; diff --git a/ios/RCTMGL/RCTMGLLocation.h b/ios/RCTMGL/RCTMGLLocation.h new file mode 100644 index 0000000000..a4501f95f9 --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocation.h @@ -0,0 +1,19 @@ +// +// RCTMGLLocation.h +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import +#import + +@interface RCTMGLLocation : NSObject + +@property (nonatomic, strong) CLLocation *location; +@property (nonatomic, strong) CLHeading *heading; + +- (NSDictionary *)toJSON; + +@end diff --git a/ios/RCTMGL/RCTMGLLocation.m b/ios/RCTMGL/RCTMGLLocation.m new file mode 100644 index 0000000000..bb0ec9406f --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocation.m @@ -0,0 +1,31 @@ +// +// RCTMGLLocation.m +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import "RCTMGLLocation.h" + +@implementation RCTMGLLocation + +- (NSDictionary *)toJSON +{ + NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; + + NSMutableDictionary *coords = [[NSMutableDictionary alloc] init]; + coords[@"longitude"] = @(_location.coordinate.longitude); + coords[@"latitude"] = @(_location.coordinate.latitude); + coords[@"altitude"] = @(_location.altitude); + coords[@"accuracy"] = @(_location.horizontalAccuracy); + coords[@"heading"] = @(_heading.trueHeading); + coords[@"speed"] = @(_location.speed); + + json[@"coords"] = coords; + json[@"timestamp"] = @([_location.timestamp timeIntervalSince1970]); + + return json; +} + +@end diff --git a/ios/RCTMGL/RCTMGLLocationManager.h b/ios/RCTMGL/RCTMGLLocationManager.h new file mode 100644 index 0000000000..2891f50b35 --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocationManager.h @@ -0,0 +1,29 @@ +// +// RCTMGLLocationManager.h +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import + +#import "RCTMGLLocation.h" +#import "RCTMGLLocationManagerDelegate.h" + +typedef void (^RCTMGLLocationBlock)(RCTMGLLocation *location); + +@interface RCTMGLLocationManager : NSObject + +@property (nonatomic, strong) id delegate; + ++ (id)sharedInstance; + +- (void)start; +- (void)stop; +- (BOOL)isEnabled; +- (RCTMGLLocation *)getLastKnownLocation; +- (void)addListener:(RCTMGLLocationBlock)listener; +- (void)removeListener:(RCTMGLLocationBlock)listener; + +@end diff --git a/ios/RCTMGL/RCTMGLLocationManager.m b/ios/RCTMGL/RCTMGLLocationManager.m new file mode 100644 index 0000000000..3bfb561b6a --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocationManager.m @@ -0,0 +1,159 @@ +// +// RCTMGLLocationManager.m +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import +#import "RCTMGLLocationManager.h" + +@interface RCTMGLLocationManager() +@end + +@implementation RCTMGLLocationManager +{ + CLLocationManager *locationManager; + CLLocation *lastKnownLocation; + CLHeading *lastKnownHeading; + NSMutableArray *listeners; + BOOL isListening; +} + ++ (id)sharedInstance +{ + static RCTMGLLocationManager *manager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ manager = [[self alloc] init]; }); + return manager; +} + +- (instancetype)init +{ + if (self = [super init]) { + [self _setupLocationManager]; + listeners = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dealloc +{ + locationManager.delegate = nil; + [self stop]; +} + +- (void)start +{ + if ([self isEnabled]) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [locationManager requestWhenInUseAuthorization]; + [locationManager startUpdatingLocation]; + [locationManager startUpdatingHeading]; + isListening = YES; + }); +} + +- (void)stop +{ + if (![self isEnabled]) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [locationManager stopUpdatingLocation]; + [locationManager stopUpdatingHeading]; + isListening = NO; + }); +} + +- (BOOL)isEnabled +{ + return isListening; +} + +- (RCTMGLLocation *)getLastKnownLocation +{ + CLLocation* newLastLocation = locationManager.location; + if (newLastLocation) { + lastKnownLocation = newLastLocation; + } + RCTMGLLocation *location = [self _convertToMapboxLocation:lastKnownLocation]; + return location; +} + +- (void)addListener:(RCTMGLLocationBlock)listener +{ + if (![listeners containsObject:listener]) { + [listeners addObject:listener]; + } +} + +- (void)removeListener:(RCTMGLLocationBlock)listener +{ + NSUInteger indexOf = [listeners indexOfObject:listener]; + + if (indexOf == NSNotFound) { + return; + } + + [listeners removeObjectAtIndex:indexOf]; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading +{ + lastKnownHeading = heading; + [self _updateDelegate]; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations +{ + lastKnownLocation = [locations lastObject]; + [self _updateDelegate]; +} + +- (void)_setupLocationManager +{ + __weak RCTMGLLocationManager *weakSelf = self; + + dispatch_async(dispatch_get_main_queue(), ^{ + locationManager = [[CLLocationManager alloc] init]; + locationManager.delegate = weakSelf; + }); +} + +- (void)_updateDelegate +{ + if (_delegate == nil) { + return; + } + + RCTMGLLocation *userLocation = [self _convertToMapboxLocation:lastKnownLocation]; + + if (listeners.count > 0) { + for (int i = 0; i < listeners.count; i++) { + RCTMGLLocationBlock listener = listeners[i]; + listener(userLocation); + } + } + + [_delegate locationManager:self didUpdateLocation:userLocation]; +} + +- (RCTMGLLocation *)_convertToMapboxLocation:(CLLocation *)location +{ + if (location == nil) { + return nil; + } + + RCTMGLLocation *userLocation = [[RCTMGLLocation alloc] init]; + userLocation.location = location; + userLocation.heading = lastKnownHeading; + return userLocation; +} + +@end diff --git a/ios/RCTMGL/RCTMGLLocationManagerDelegate.h b/ios/RCTMGL/RCTMGLLocationManagerDelegate.h new file mode 100644 index 0000000000..03bed36a87 --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocationManagerDelegate.h @@ -0,0 +1,20 @@ +// +// RCTMGLLocationManagerDelegate.h +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import + +#import "RCTMGLLocation.h" + +@class RCTMGLLocationManager; + +@protocol RCTMGLLocationManagerDelegate + +- (void)locationManager:(RCTMGLLocationManager *)locationManager didUpdateLocation:(RCTMGLLocation *)location; + +@end + diff --git a/ios/RCTMGL/RCTMGLLocationModule.h b/ios/RCTMGL/RCTMGLLocationModule.h new file mode 100644 index 0000000000..1301ea03fa --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocationModule.h @@ -0,0 +1,15 @@ +// +// RCTMGLLocationManager.h +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import +#import +#import + +@interface RCTMGLLocationModule : RCTEventEmitter + +@end diff --git a/ios/RCTMGL/RCTMGLLocationModule.m b/ios/RCTMGL/RCTMGLLocationModule.m new file mode 100644 index 0000000000..3851bc1b69 --- /dev/null +++ b/ios/RCTMGL/RCTMGLLocationModule.m @@ -0,0 +1,93 @@ +// +// RCTMGLLocationManager.m +// RCTMGL +// +// Created by Nick Italiano on 6/21/18. +// Copyright © 2018 Mapbox Inc. All rights reserved. +// + +#import + +#import "RCTMGLLocation.h" +#import "RCTMGLLocationModule.h" +#import "RCTMGLLocationManager.h" +#import "RCTMGLLocationManagerDelegate.h" +#import "RCTMGLEventTypes.h" + +@interface RCTMGLLocationModule() +@end + +@implementation RCTMGLLocationModule +{ + RCTMGLLocationManager *locationManager; + BOOL hasListeners; +} + +RCT_EXPORT_MODULE(); + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +- (instancetype)init +{ + if (self = [super init]) { + locationManager = [[RCTMGLLocationManager alloc] init]; + locationManager.delegate = self; + } + return self; +} + +- (void)startObserving +{ + [super startObserving]; + hasListeners = YES; +} + +- (void)stopObserving +{ + [super stopObserving]; + hasListeners = NO; +} + +- (NSArray *)supportedEvents +{ + return @[RCT_MAPBOX_USER_LOCATION_UPDATE]; +} + +RCT_EXPORT_METHOD(start) +{ + [locationManager start]; +} + +RCT_EXPORT_METHOD(pause) +{ + [locationManager stop]; +} + +RCT_EXPORT_METHOD(stop) +{ + [locationManager stop]; +} + +RCT_EXPORT_METHOD(getLastKnownLocation:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + RCTMGLLocation *lastKnownLocation = [locationManager getLastKnownLocation]; + resolve(lastKnownLocation); +} + +- (void)locationManager:(RCTMGLLocationManager *)locationManager didUpdateLocation:(RCTMGLLocation *)location +{ + if (!hasListeners) { + return; + } + + if (self.bridge == nil) { + return; + } + + [self sendEventWithName:RCT_MAPBOX_USER_LOCATION_UPDATE body:[location toJSON]]; +} + +@end diff --git a/ios/RCTMGL/RCTMGLMapView.h b/ios/RCTMGL/RCTMGLMapView.h index 88f4c9ccef..1e810811a9 100644 --- a/ios/RCTMGL/RCTMGLMapView.h +++ b/ios/RCTMGL/RCTMGLMapView.h @@ -11,6 +11,7 @@ #import "RCTMGLShapeSource.h" #import "RCTMGLPointAnnotation.h" #import "RCTMGLLight.h" +#import "RCTMGLCamera.h" @import Mapbox; @@ -25,7 +26,6 @@ @property (nonatomic, strong) RCTMGLLight *light; @property (nonatomic, copy) NSArray *reactContentInset; -@property (nonatomic, assign) BOOL animated; @property (nonatomic, assign) BOOL reactLocalizeLabels; @property (nonatomic, assign) BOOL reactScrollEnabled; @property (nonatomic, assign) BOOL reactPitchEnabled; @@ -33,27 +33,15 @@ @property (nonatomic, assign) BOOL reactAttributionEnabled; @property (nonatomic, assign) BOOL reactLogoEnabled; @property (nonatomic, assign) BOOL reactCompassEnabled; -@property (nonatomic, assign) BOOL reactShowUserLocation; @property (nonatomic, assign) BOOL reactZoomEnabled; -@property (nonatomic, copy) NSString *reactCenterCoordinate; -@property (nonatomic, copy) NSString *reactVisibleCoordinateBounds; @property (nonatomic, copy) NSString *reactStyleURL; @property (nonatomic, assign) BOOL isUserInteraction; -@property (nonatomic, assign) int reactUserTrackingMode; -@property (nonatomic, assign) int reactUserLocationVerticalAlignment; - -@property (nonatomic, assign) double heading; -@property (nonatomic, assign) double pitch; -@property (nonatomic, assign) double reactZoomLevel; -@property (nonatomic, assign) double reactMinZoomLevel; -@property (nonatomic, assign) double reactMaxZoomLevel; @property (nonatomic, copy) RCTBubblingEventBlock onPress; @property (nonatomic, copy) RCTBubblingEventBlock onLongPress; @property (nonatomic, copy) RCTBubblingEventBlock onMapChange; -@property (nonatomic, copy) RCTBubblingEventBlock onUserTrackingModeChange; - (CLLocationDistance)getMetersPerPixelAtLatitude:(double)latitude withZoom:(double)zoomLevel; - (CLLocationDistance)altitudeFromZoom:(double)zoomLevel; diff --git a/ios/RCTMGL/RCTMGLMapView.m b/ios/RCTMGL/RCTMGLMapView.m index bf08146c84..932ecd6372 100644 --- a/ios/RCTMGL/RCTMGLMapView.m +++ b/ios/RCTMGL/RCTMGLMapView.m @@ -40,7 +40,8 @@ - (void)layoutSubviews [super layoutSubviews]; if (_pendingInitialLayout) { _pendingInitialLayout = NO; - [self _updateCameraAfterInitialLayout]; + // FMTODO + // [self.camera _updateCameraAfterInitialLayout]; } } @@ -68,6 +69,9 @@ - (void) addToMap:(id)subview RCTMGLPointAnnotation *pointAnnotation = (RCTMGLPointAnnotation *)subview; pointAnnotation.map = self; [_pointAnnotations addObject:pointAnnotation]; + } else if ([subview isKindOfClass:[RCTMGLCamera class]]) { + RCTMGLCamera *camera = (RCTMGLCamera *)subview; + camera.map = self; } else { NSArray> *childSubviews = [subview reactSubviews]; @@ -87,6 +91,9 @@ - (void) removeFromMap:(id)subview RCTMGLPointAnnotation *pointAnnotation = (RCTMGLPointAnnotation *)subview; pointAnnotation.map = nil; [_pointAnnotations removeObject:pointAnnotation]; + } else if ([subview isKindOfClass:[RCTMGLCamera class]]) { + RCTMGLCamera *camera = (RCTMGLCamera *)subview; + camera.map = nil; } else { NSArray> *childSubViews = [subview reactSubviews]; @@ -166,22 +173,9 @@ - (void)setReactCompassEnabled:(BOOL)reactCompassEnabled - (void)setReactShowUserLocation:(BOOL)reactShowUserLocation { - _reactShowUserLocation = reactShowUserLocation; - self.showsUserLocation = _reactShowUserLocation; -} - -- (void)setReactCenterCoordinate:(NSString *)reactCenterCoordinate -{ - _reactCenterCoordinate = reactCenterCoordinate; - [self _updateCameraIfNeeded:YES]; -} - -- (void)setReactVisibleCoordinateBounds:(NSString *)reactVisibleCoordinateBounds -{ - _reactVisibleCoordinateBounds = reactVisibleCoordinateBounds; - if (!_pendingInitialLayout) { - [self _updateCameraIfNeeded:YES]; - } + // FMTODO + //_reactShowUserLocation = reactShowUserLocation; + self.showsUserLocation = reactShowUserLocation; //_reactShowUserLocation; } - (void)setReactContentInset:(NSArray *)reactContentInset @@ -215,49 +209,6 @@ - (void)setReactStyleURL:(NSString *)reactStyleURL self.styleURL = [self _getStyleURLFromKey:_reactStyleURL]; } -- (void)setHeading:(double)heading -{ - _heading = heading; - [self _updateCameraIfNeeded:NO]; -} - -- (void)setPitch:(double)pitch -{ - _pitch = pitch; - [self _updateCameraIfNeeded:NO]; -} - -- (void)setReactZoomLevel:(double)reactZoomLevel -{ - _reactZoomLevel = reactZoomLevel; - self.zoomLevel = _reactZoomLevel; -} - -- (void)setReactMinZoomLevel:(double)reactMinZoomLevel -{ - _reactMinZoomLevel = reactMinZoomLevel; - self.minimumZoomLevel = _reactMinZoomLevel; -} - -- (void)setReactMaxZoomLevel:(double)reactMaxZoomLevel -{ - _reactMaxZoomLevel = reactMaxZoomLevel; - self.maximumZoomLevel = reactMaxZoomLevel; -} - -- (void)setReactUserTrackingMode:(int)reactUserTrackingMode -{ - _reactUserTrackingMode = reactUserTrackingMode; - [self setUserTrackingMode:_reactUserTrackingMode animated:NO]; - self.showsUserHeadingIndicator = (NSUInteger)_reactUserTrackingMode == MGLUserTrackingModeFollowWithHeading; -} - -- (void)setReactUserLocationVerticalAlignment:(int)reactUserLocationVerticalAlignment -{ - _reactUserLocationVerticalAlignment = reactUserLocationVerticalAlignment; - self.userLocationVerticalAlignment = reactUserLocationVerticalAlignment; -} - #pragma mark - methods - (NSString *)takeSnap:(BOOL)writeToDisk @@ -352,35 +303,6 @@ - (NSURL*)_getStyleURLFromKey:(NSString *)styleURL return [NSURL URLWithString:styleURL]; } - -/** - setVisibleCoordinateBounds() won't properly work if the view has empty bounds so - we need to wait for the initial layoutSubviews() before we can use reactVisibleCoordinateBounds - */ -- (void)_updateCameraAfterInitialLayout { - if (_reactVisibleCoordinateBounds != nil) { - [self _updateCameraIfNeeded:YES]; - } -} - -- (void)_updateCameraIfNeeded:(BOOL)shouldUpdateCenterCoord -{ - if (shouldUpdateCenterCoord) { - if (_reactCenterCoordinate != nil) { - [self setCenterCoordinate:[RCTMGLUtils fromFeature:_reactCenterCoordinate] animated:_animated]; - } else { - MGLCoordinateBounds bounds = [RCTMGLUtils fromFeatureCollection:_reactVisibleCoordinateBounds]; - [self setVisibleCoordinateBounds:bounds animated:_animated]; - - } - } else { - MGLMapCamera *camera = [self.camera copy]; - camera.pitch = _pitch; - camera.heading = _heading; - [self setCamera:camera animated:_animated]; - } -} - - (void)_removeAllSourcesFromMap { if (self.style == nil || _sources.count == 0) { diff --git a/ios/RCTMGL/RCTMGLMapViewManager.m b/ios/RCTMGL/RCTMGLMapViewManager.m index 5f7cb3e423..b6736e96c2 100644 --- a/ios/RCTMGL/RCTMGLMapViewManager.m +++ b/ios/RCTMGL/RCTMGLMapViewManager.m @@ -17,7 +17,6 @@ #import "CameraStop.h" #import "CameraUpdateQueue.h" #import "FilterParser.h" -#import "MGLFaux3DUserLocationAnnotationView.h" @interface RCTMGLMapViewManager() @end @@ -67,35 +66,21 @@ - (UIView *)view #pragma mark - React View Props -RCT_EXPORT_VIEW_PROPERTY(animated, BOOL) RCT_REMAP_VIEW_PROPERTY(localizeLabels, reactLocalizeLabels, BOOL) RCT_REMAP_VIEW_PROPERTY(scrollEnabled, reactScrollEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(pitchEnabled, reactPitchEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(rotateEnabled, reactRotateEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(attributionEnabled, reactAttributionEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(logoEnabled, reactLogoEnabled, BOOL) -RCT_REMAP_VIEW_PROPERTY(showUserLocation, reactShowUserLocation, BOOL) RCT_REMAP_VIEW_PROPERTY(compassEnabled, reactCompassEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(zoomEnabled, reactZoomEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(contentInset, reactContentInset, NSArray) -RCT_REMAP_VIEW_PROPERTY(centerCoordinate, reactCenterCoordinate, NSString) -RCT_REMAP_VIEW_PROPERTY(visibleCoordinateBounds, reactVisibleCoordinateBounds, NSString) RCT_REMAP_VIEW_PROPERTY(styleURL, reactStyleURL, NSString) -RCT_REMAP_VIEW_PROPERTY(userTrackingMode, reactUserTrackingMode, int) -RCT_REMAP_VIEW_PROPERTY(userLocationVerticalAlignment, reactUserLocationVerticalAlignment, int) - -RCT_EXPORT_VIEW_PROPERTY(heading, double) -RCT_EXPORT_VIEW_PROPERTY(pitch, double) -RCT_REMAP_VIEW_PROPERTY(zoomLevel, reactZoomLevel, double) -RCT_REMAP_VIEW_PROPERTY(minZoomLevel, reactMinZoomLevel, double) -RCT_REMAP_VIEW_PROPERTY(maxZoomLevel, reactMaxZoomLevel, double) - RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onLongPress, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(onMapChange, RCTBubblingEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onUserTrackingModeChange, RCTBubblingEventBlock) #pragma mark - React Methods @@ -290,39 +275,6 @@ - (UIView *)view }]; } -RCT_EXPORT_METHOD(setCamera:(nonnull NSNumber*)reactTag - withConfiguration:(nonnull NSDictionary*)config - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *manager, NSDictionary *viewRegistry) { - id view = viewRegistry[reactTag]; - - if (![view isKindOfClass:[RCTMGLMapView class]]) { - RCTLogError(@"Invalid react tag, could not find RCTMGLMapView"); - return; - } - - __weak RCTMGLMapView *reactMapView = (RCTMGLMapView*)view; - - [reactMapView.cameraUpdateQueue flush]; // remove any curreny camera updates - - if (config[@"stops"]) { - NSArray *stops = (NSArray*)config[@"stops"]; - - for (int i = 0; i < stops.count; i++) { - [reactMapView.cameraUpdateQueue enqueue:[CameraStop fromDictionary:stops[i]]]; - } - } else { - [reactMapView.cameraUpdateQueue enqueue:[CameraStop fromDictionary:config]]; - } - - [reactMapView.cameraUpdateQueue execute:reactMapView withCompletionHandler:^{ - resolve(nil); - }]; - }]; -} - RCT_EXPORT_METHOD(showAttribution:(nonnull NSNumber *)reactTag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @@ -408,48 +360,11 @@ - (void)didLongPressMap:(UILongPressGestureRecognizer *)recognizer #pragma mark - MGLMapViewDelegate -- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(MGLUserLocation *)userLocation -{ - if (userLocation == nil) { - return; - } - - RCTMGLMapView *reactMapView = (RCTMGLMapView *)mapView; - - NSDictionary *coords = @{ - @"speed": @(userLocation.location.speed), - @"heading": @(userLocation.heading.trueHeading), - @"accuracy": @(userLocation.location.horizontalAccuracy), - @"altitude": @(userLocation.location.altitude), - @"latitude": @(userLocation.location.coordinate.latitude), - @"longitude": @(userLocation.location.coordinate.longitude) - }; - - double utcTimestampMS = [userLocation.location.timestamp timeIntervalSince1970] * 1000.0; - NSDictionary *payload = @{ @"timestamp": @(utcTimestampMS), @"coords": coords }; - RCTMGLEvent *locationEvent = [RCTMGLEvent makeEvent:RCT_MAPBOX_USER_LOCATION_UPDATE withPayload:payload]; - [self fireEvent:locationEvent withCallback:reactMapView.onMapChange]; -} - -- (void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated -{ - RCTMGLMapView *reactMapView = (RCTMGLMapView *)mapView; - if (reactMapView.onUserTrackingModeChange == nil) { - return; - } - - NSDictionary *payload = @{ @"userTrackingMode": @(mode) }; - RCTMGLEvent *event = [RCTMGLEvent makeEvent:RCT_MAPBOX_USER_TRACKING_MODE_CHANGE withPayload:payload]; - [self fireEvent:event withCallback:reactMapView.onUserTrackingModeChange]; -} - - (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id)annotation { if ([annotation isKindOfClass:[RCTMGLPointAnnotation class]]) { RCTMGLPointAnnotation *rctAnnotation = (RCTMGLPointAnnotation *)annotation; return [rctAnnotation getAnnotationView]; - } else if ([annotation isKindOfClass:[MGLUserLocation class]]) { - return [[MGLFaux3DUserLocationAnnotationView alloc] init]; } return nil; } @@ -497,7 +412,6 @@ - (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id 0) { for (int i = 0; i < reactMapView.sources.count; i++) { diff --git a/ios/RCTMGL/RCTMGLRasterSource.m b/ios/RCTMGL/RCTMGLRasterSource.m index 772a02226f..ebe85a5eec 100644 --- a/ios/RCTMGL/RCTMGLRasterSource.m +++ b/ios/RCTMGL/RCTMGLRasterSource.m @@ -12,7 +12,7 @@ @implementation RCTMGLRasterSource - (MGLSource*)makeSource { - return [[MGLRasterSource alloc] initWithIdentifier:self.id + return [[MGLRasterTileSource alloc] initWithIdentifier:self.id tileURLTemplates:@[_url] options:[self _getOptions]]; } diff --git a/ios/RCTMGL/RCTMGLShapeSource.m b/ios/RCTMGL/RCTMGLShapeSource.m index e03f58551e..fec439d8d1 100644 --- a/ios/RCTMGL/RCTMGLShapeSource.m +++ b/ios/RCTMGL/RCTMGLShapeSource.m @@ -17,7 +17,7 @@ - (void)setShape:(NSString *)shape if (self.source != nil) { MGLShapeSource *source = (MGLShapeSource *)self.source; - [source setShape:[RCTMGLUtils shapeFromGeoJSON:_shape]]; + [source setShape: shape == nil ? nil : [RCTMGLUtils shapeFromGeoJSON:_shape]]; } } diff --git a/ios/RCTMGL/RCTMGLStyle.h b/ios/RCTMGL/RCTMGLStyle.h index 0ba9f36713..75c403b327 100644 --- a/ios/RCTMGL/RCTMGLStyle.h +++ b/ios/RCTMGL/RCTMGLStyle.h @@ -171,7 +171,6 @@ - (void)setRasterContrast:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; - (void)setRasterContrastTransition:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; - (void)setRasterFadeDuration:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; -- (void)setRasterFadeDurationTransition:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; - (void)setBackgroundStyleLayerVisibility:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; - (void)setBackgroundColor:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; - (void)setBackgroundColorTransition:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue; diff --git a/ios/RCTMGL/RCTMGLStyle.m b/ios/RCTMGL/RCTMGLStyle.m index f1ac3d2305..286256c4ad 100644 --- a/ios/RCTMGL/RCTMGLStyle.m +++ b/ios/RCTMGL/RCTMGLStyle.m @@ -53,13 +53,15 @@ - (void)fillLayer:(MGLFillStyleLayer *)layer withReactStyle:(NSDictionary *)reac } else if ([prop isEqualToString:@"fillTranslateAnchor"]) { [self setFillTranslateAnchor:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"fillPattern"]) { - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self setFillPattern:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self setFillPattern:layer withReactStyleValue:styleValue]; }); } @@ -133,13 +135,15 @@ - (void)lineLayer:(MGLLineStyleLayer *)layer withReactStyle:(NSDictionary *)reac } else if ([prop isEqualToString:@"lineDasharrayTransition"]) { [self setLineDasharrayTransition:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"linePattern"]) { - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self setLinePattern:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self setLinePattern:layer withReactStyleValue:styleValue]; }); } @@ -189,13 +193,15 @@ - (void)symbolLayer:(MGLSymbolStyleLayer *)layer withReactStyle:(NSDictionary *) } else if ([prop isEqualToString:@"iconTextFitPadding"]) { [self setIconTextFitPadding:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"iconImage"]) { - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self setIconImage:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self setIconImage:layer withReactStyleValue:styleValue]; }); } @@ -404,13 +410,15 @@ - (void)fillExtrusionLayer:(MGLFillExtrusionStyleLayer *)layer withReactStyle:(N } else if ([prop isEqualToString:@"fillExtrusionTranslateAnchor"]) { [self setFillExtrusionTranslateAnchor:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"fillExtrusionPattern"]) { - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self setFillExtrusionPattern:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self setFillExtrusionPattern:layer withReactStyleValue:styleValue]; }); } @@ -475,8 +483,6 @@ - (void)rasterLayer:(MGLRasterStyleLayer *)layer withReactStyle:(NSDictionary *) [self setRasterContrastTransition:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"rasterFadeDuration"]) { [self setRasterFadeDuration:layer withReactStyleValue:styleValue]; - } else if ([prop isEqualToString:@"rasterFadeDurationTransition"]) { - [self setRasterFadeDurationTransition:layer withReactStyleValue:styleValue]; } else { // TODO throw exception } @@ -505,13 +511,15 @@ - (void)backgroundLayer:(MGLBackgroundStyleLayer *)layer withReactStyle:(NSDicti } else if ([prop isEqualToString:@"backgroundColorTransition"]) { [self setBackgroundColorTransition:layer withReactStyleValue:styleValue]; } else if ([prop isEqualToString:@"backgroundPattern"]) { - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self setBackgroundPattern:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self setBackgroundPattern:layer withReactStyleValue:styleValue]; }); } @@ -574,21 +582,11 @@ - (void)setFillStyleLayerVisibility:(MGLFillStyleLayer *)layer withReactStyleVal - (void)setFillAntialias:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillAntialiased = styleValue.mglStyleValue; } - (void)setFillOpacity:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillOpacity = styleValue.mglStyleValue; } @@ -599,11 +597,6 @@ - (void)setFillOpacityTransition:(MGLFillStyleLayer *)layer withReactStyleValue: - (void)setFillColor:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillColor = styleValue.mglStyleValue; } @@ -614,11 +607,6 @@ - (void)setFillColorTransition:(MGLFillStyleLayer *)layer withReactStyleValue:(R - (void)setFillOutlineColor:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillOutlineColor = styleValue.mglStyleValue; } @@ -629,11 +617,6 @@ - (void)setFillOutlineColorTransition:(MGLFillStyleLayer *)layer withReactStyleV - (void)setFillTranslate:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillTranslation = styleValue.mglStyleValue; } @@ -644,21 +627,11 @@ - (void)setFillTranslateTransition:(MGLFillStyleLayer *)layer withReactStyleValu - (void)setFillTranslateAnchor:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillTranslationAnchor = styleValue.mglStyleValue; } - (void)setFillPattern:(MGLFillStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillPattern = styleValue.mglStyleValue; } @@ -671,41 +644,21 @@ - (void)setFillPatternTransition:(MGLFillStyleLayer *)layer withReactStyleValue: - (void)setLineCap:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineCap = styleValue.mglStyleValue; } - (void)setLineJoin:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineJoin = styleValue.mglStyleValue; } - (void)setLineMiterLimit:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineMiterLimit = styleValue.mglStyleValue; } - (void)setLineRoundLimit:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineRoundLimit = styleValue.mglStyleValue; } @@ -716,11 +669,6 @@ - (void)setLineStyleLayerVisibility:(MGLLineStyleLayer *)layer withReactStyleVal - (void)setLineOpacity:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineOpacity = styleValue.mglStyleValue; } @@ -731,11 +679,6 @@ - (void)setLineOpacityTransition:(MGLLineStyleLayer *)layer withReactStyleValue: - (void)setLineColor:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineColor = styleValue.mglStyleValue; } @@ -746,11 +689,6 @@ - (void)setLineColorTransition:(MGLLineStyleLayer *)layer withReactStyleValue:(R - (void)setLineTranslate:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineTranslation = styleValue.mglStyleValue; } @@ -761,21 +699,11 @@ - (void)setLineTranslateTransition:(MGLLineStyleLayer *)layer withReactStyleValu - (void)setLineTranslateAnchor:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineTranslationAnchor = styleValue.mglStyleValue; } - (void)setLineWidth:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineWidth = styleValue.mglStyleValue; } @@ -786,11 +714,6 @@ - (void)setLineWidthTransition:(MGLLineStyleLayer *)layer withReactStyleValue:(R - (void)setLineGapWidth:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineGapWidth = styleValue.mglStyleValue; } @@ -801,11 +724,6 @@ - (void)setLineGapWidthTransition:(MGLLineStyleLayer *)layer withReactStyleValue - (void)setLineOffset:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineOffset = styleValue.mglStyleValue; } @@ -816,11 +734,6 @@ - (void)setLineOffsetTransition:(MGLLineStyleLayer *)layer withReactStyleValue:( - (void)setLineBlur:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineBlur = styleValue.mglStyleValue; } @@ -831,11 +744,6 @@ - (void)setLineBlurTransition:(MGLLineStyleLayer *)layer withReactStyleValue:(RC - (void)setLineDasharray:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.lineDashPattern = styleValue.mglStyleValue; } @@ -846,11 +754,6 @@ - (void)setLineDasharrayTransition:(MGLLineStyleLayer *)layer withReactStyleValu - (void)setLinePattern:(MGLLineStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.linePattern = styleValue.mglStyleValue; } @@ -863,361 +766,181 @@ - (void)setLinePatternTransition:(MGLLineStyleLayer *)layer withReactStyleValue: - (void)setSymbolPlacement:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.symbolPlacement = styleValue.mglStyleValue; } - (void)setSymbolSpacing:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.symbolSpacing = styleValue.mglStyleValue; } - (void)setSymbolAvoidEdges:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.symbolAvoidsEdges = styleValue.mglStyleValue; } - (void)setIconAllowOverlap:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconAllowsOverlap = styleValue.mglStyleValue; } - (void)setIconIgnorePlacement:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconIgnoresPlacement = styleValue.mglStyleValue; } - (void)setIconOptional:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconOptional = styleValue.mglStyleValue; } - (void)setIconRotationAlignment:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconRotationAlignment = styleValue.mglStyleValue; } - (void)setIconSize:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconScale = styleValue.mglStyleValue; } - (void)setIconTextFit:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconTextFit = styleValue.mglStyleValue; } - (void)setIconTextFitPadding:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconTextFitPadding = styleValue.mglStyleValue; } - (void)setIconImage:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconImageName = styleValue.mglStyleValue; } - (void)setIconRotate:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconRotation = styleValue.mglStyleValue; } - (void)setIconPadding:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconPadding = styleValue.mglStyleValue; } - (void)setIconKeepUpright:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.keepsIconUpright = styleValue.mglStyleValue; } - (void)setIconOffset:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconOffset = styleValue.mglStyleValue; } - (void)setIconAnchor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconAnchor = styleValue.mglStyleValue; } - (void)setIconPitchAlignment:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconPitchAlignment = styleValue.mglStyleValue; } - (void)setTextPitchAlignment:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textPitchAlignment = styleValue.mglStyleValue; } - (void)setTextRotationAlignment:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textRotationAlignment = styleValue.mglStyleValue; } - (void)setTextField:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.text = styleValue.mglStyleValue; } - (void)setTextFont:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textFontNames = styleValue.mglStyleValue; } - (void)setTextSize:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textFontSize = styleValue.mglStyleValue; } - (void)setTextMaxWidth:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.maximumTextWidth = styleValue.mglStyleValue; } - (void)setTextLineHeight:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textLineHeight = styleValue.mglStyleValue; } - (void)setTextLetterSpacing:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textLetterSpacing = styleValue.mglStyleValue; } - (void)setTextJustify:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textJustification = styleValue.mglStyleValue; } - (void)setTextAnchor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textAnchor = styleValue.mglStyleValue; } - (void)setTextMaxAngle:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.maximumTextAngle = styleValue.mglStyleValue; } - (void)setTextRotate:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textRotation = styleValue.mglStyleValue; } - (void)setTextPadding:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textPadding = styleValue.mglStyleValue; } - (void)setTextKeepUpright:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.keepsTextUpright = styleValue.mglStyleValue; } - (void)setTextTransform:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textTransform = styleValue.mglStyleValue; } - (void)setTextOffset:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textOffset = styleValue.mglStyleValue; } - (void)setTextAllowOverlap:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textAllowsOverlap = styleValue.mglStyleValue; } - (void)setTextIgnorePlacement:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textIgnoresPlacement = styleValue.mglStyleValue; } - (void)setTextOptional:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textOptional = styleValue.mglStyleValue; } @@ -1228,11 +951,6 @@ - (void)setSymbolStyleLayerVisibility:(MGLSymbolStyleLayer *)layer withReactStyl - (void)setIconOpacity:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconOpacity = styleValue.mglStyleValue; } @@ -1243,11 +961,6 @@ - (void)setIconOpacityTransition:(MGLSymbolStyleLayer *)layer withReactStyleValu - (void)setIconColor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconColor = styleValue.mglStyleValue; } @@ -1258,11 +971,6 @@ - (void)setIconColorTransition:(MGLSymbolStyleLayer *)layer withReactStyleValue: - (void)setIconHaloColor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconHaloColor = styleValue.mglStyleValue; } @@ -1273,11 +981,6 @@ - (void)setIconHaloColorTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setIconHaloWidth:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconHaloWidth = styleValue.mglStyleValue; } @@ -1288,11 +991,6 @@ - (void)setIconHaloWidthTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setIconHaloBlur:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconHaloBlur = styleValue.mglStyleValue; } @@ -1303,11 +1001,6 @@ - (void)setIconHaloBlurTransition:(MGLSymbolStyleLayer *)layer withReactStyleVal - (void)setIconTranslate:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconTranslation = styleValue.mglStyleValue; } @@ -1318,21 +1011,11 @@ - (void)setIconTranslateTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setIconTranslateAnchor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.iconTranslationAnchor = styleValue.mglStyleValue; } - (void)setTextOpacity:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textOpacity = styleValue.mglStyleValue; } @@ -1343,11 +1026,6 @@ - (void)setTextOpacityTransition:(MGLSymbolStyleLayer *)layer withReactStyleValu - (void)setTextColor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textColor = styleValue.mglStyleValue; } @@ -1358,11 +1036,6 @@ - (void)setTextColorTransition:(MGLSymbolStyleLayer *)layer withReactStyleValue: - (void)setTextHaloColor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textHaloColor = styleValue.mglStyleValue; } @@ -1373,11 +1046,6 @@ - (void)setTextHaloColorTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setTextHaloWidth:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textHaloWidth = styleValue.mglStyleValue; } @@ -1388,11 +1056,6 @@ - (void)setTextHaloWidthTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setTextHaloBlur:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textHaloBlur = styleValue.mglStyleValue; } @@ -1403,11 +1066,6 @@ - (void)setTextHaloBlurTransition:(MGLSymbolStyleLayer *)layer withReactStyleVal - (void)setTextTranslate:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textTranslation = styleValue.mglStyleValue; } @@ -1418,11 +1076,6 @@ - (void)setTextTranslateTransition:(MGLSymbolStyleLayer *)layer withReactStyleVa - (void)setTextTranslateAnchor:(MGLSymbolStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.textTranslationAnchor = styleValue.mglStyleValue; } @@ -1435,11 +1088,6 @@ - (void)setCircleStyleLayerVisibility:(MGLCircleStyleLayer *)layer withReactStyl - (void)setCircleRadius:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleRadius = styleValue.mglStyleValue; } @@ -1450,11 +1098,6 @@ - (void)setCircleRadiusTransition:(MGLCircleStyleLayer *)layer withReactStyleVal - (void)setCircleColor:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleColor = styleValue.mglStyleValue; } @@ -1465,11 +1108,6 @@ - (void)setCircleColorTransition:(MGLCircleStyleLayer *)layer withReactStyleValu - (void)setCircleBlur:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleBlur = styleValue.mglStyleValue; } @@ -1480,11 +1118,6 @@ - (void)setCircleBlurTransition:(MGLCircleStyleLayer *)layer withReactStyleValue - (void)setCircleOpacity:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleOpacity = styleValue.mglStyleValue; } @@ -1495,11 +1128,6 @@ - (void)setCircleOpacityTransition:(MGLCircleStyleLayer *)layer withReactStyleVa - (void)setCircleTranslate:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleTranslation = styleValue.mglStyleValue; } @@ -1510,41 +1138,21 @@ - (void)setCircleTranslateTransition:(MGLCircleStyleLayer *)layer withReactStyle - (void)setCircleTranslateAnchor:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleTranslationAnchor = styleValue.mglStyleValue; } - (void)setCirclePitchScale:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleScaleAlignment = styleValue.mglStyleValue; } - (void)setCirclePitchAlignment:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circlePitchAlignment = styleValue.mglStyleValue; } - (void)setCircleStrokeWidth:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleStrokeWidth = styleValue.mglStyleValue; } @@ -1555,11 +1163,6 @@ - (void)setCircleStrokeWidthTransition:(MGLCircleStyleLayer *)layer withReactSty - (void)setCircleStrokeColor:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleStrokeColor = styleValue.mglStyleValue; } @@ -1570,11 +1173,6 @@ - (void)setCircleStrokeColorTransition:(MGLCircleStyleLayer *)layer withReactSty - (void)setCircleStrokeOpacity:(MGLCircleStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.circleStrokeOpacity = styleValue.mglStyleValue; } @@ -1592,11 +1190,6 @@ - (void)setFillExtrusionStyleLayerVisibility:(MGLFillExtrusionStyleLayer *)layer - (void)setFillExtrusionOpacity:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionOpacity = styleValue.mglStyleValue; } @@ -1607,11 +1200,6 @@ - (void)setFillExtrusionOpacityTransition:(MGLFillExtrusionStyleLayer *)layer wi - (void)setFillExtrusionColor:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionColor = styleValue.mglStyleValue; } @@ -1622,11 +1210,6 @@ - (void)setFillExtrusionColorTransition:(MGLFillExtrusionStyleLayer *)layer with - (void)setFillExtrusionTranslate:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionTranslation = styleValue.mglStyleValue; } @@ -1637,21 +1220,11 @@ - (void)setFillExtrusionTranslateTransition:(MGLFillExtrusionStyleLayer *)layer - (void)setFillExtrusionTranslateAnchor:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionTranslationAnchor = styleValue.mglStyleValue; } - (void)setFillExtrusionPattern:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionPattern = styleValue.mglStyleValue; } @@ -1662,11 +1235,6 @@ - (void)setFillExtrusionPatternTransition:(MGLFillExtrusionStyleLayer *)layer wi - (void)setFillExtrusionHeight:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionHeight = styleValue.mglStyleValue; } @@ -1677,11 +1245,6 @@ - (void)setFillExtrusionHeightTransition:(MGLFillExtrusionStyleLayer *)layer wit - (void)setFillExtrusionBase:(MGLFillExtrusionStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera", @"source", @"composite"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.fillExtrusionBase = styleValue.mglStyleValue; } @@ -1699,11 +1262,6 @@ - (void)setRasterStyleLayerVisibility:(MGLRasterStyleLayer *)layer withReactStyl - (void)setRasterOpacity:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.rasterOpacity = styleValue.mglStyleValue; } @@ -1714,11 +1272,6 @@ - (void)setRasterOpacityTransition:(MGLRasterStyleLayer *)layer withReactStyleVa - (void)setRasterHueRotate:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.rasterHueRotation = styleValue.mglStyleValue; } @@ -1729,11 +1282,6 @@ - (void)setRasterHueRotateTransition:(MGLRasterStyleLayer *)layer withReactStyle - (void)setRasterBrightnessMin:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.minimumRasterBrightness = styleValue.mglStyleValue; } @@ -1744,11 +1292,6 @@ - (void)setRasterBrightnessMinTransition:(MGLRasterStyleLayer *)layer withReactS - (void)setRasterBrightnessMax:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.maximumRasterBrightness = styleValue.mglStyleValue; } @@ -1759,11 +1302,6 @@ - (void)setRasterBrightnessMaxTransition:(MGLRasterStyleLayer *)layer withReactS - (void)setRasterSaturation:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.rasterSaturation = styleValue.mglStyleValue; } @@ -1774,11 +1312,6 @@ - (void)setRasterSaturationTransition:(MGLRasterStyleLayer *)layer withReactStyl - (void)setRasterContrast:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.rasterContrast = styleValue.mglStyleValue; } @@ -1789,19 +1322,9 @@ - (void)setRasterContrastTransition:(MGLRasterStyleLayer *)layer withReactStyleV - (void)setRasterFadeDuration:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.rasterFadeDuration = styleValue.mglStyleValue; } -- (void)setRasterFadeDurationTransition:(MGLRasterStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue -{ - layer.rasterFadeDurationTransition = [styleValue getTransition]; -} - - (void)setBackgroundStyleLayerVisibility:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue @@ -1811,11 +1334,6 @@ - (void)setBackgroundStyleLayerVisibility:(MGLBackgroundStyleLayer *)layer withR - (void)setBackgroundColor:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.backgroundColor = styleValue.mglStyleValue; } @@ -1826,11 +1344,6 @@ - (void)setBackgroundColorTransition:(MGLBackgroundStyleLayer *)layer withReactS - (void)setBackgroundPattern:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.backgroundPattern = styleValue.mglStyleValue; } @@ -1841,11 +1354,6 @@ - (void)setBackgroundPatternTransition:(MGLBackgroundStyleLayer *)layer withReac - (void)setBackgroundOpacity:(MGLBackgroundStyleLayer *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - NSArray *allowedFunctionTypes = @[@"camera"]; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } layer.backgroundOpacity = styleValue.mglStyleValue; } diff --git a/ios/RCTMGL/RCTMGLStyleValue.h b/ios/RCTMGL/RCTMGLStyleValue.h index 6f9eb55bec..e11d5c9dce 100644 --- a/ios/RCTMGL/RCTMGLStyleValue.h +++ b/ios/RCTMGL/RCTMGLStyleValue.h @@ -11,18 +11,16 @@ @interface RCTMGLStyleValue : NSObject -@property (nonatomic, strong) NSDictionary *config; +@property (nonatomic, strong) NSString *styleType; +@property (nonatomic, strong) NSDictionary *rawStyleValue; +@property (nonatomic, readonly) NSExpression *mglStyleValue; -@property (nonatomic, readonly) NSString *type; -@property (nonatomic, readonly) NSDictionary *payload; -@property (nonatomic, readonly) id mglStyleValue; - -- (BOOL)isFunction; -- (BOOL)isFunctionTypeSupported:(NSArray*)allowedFunctionTypes; +- (BOOL)shouldAddImage; +- (NSString *)getImageURI; - (MGLTransition)getTransition; -- (MGLStyleValue*)getSphericalPosition; +- (NSExpression *)getSphericalPosition; - (BOOL)isVisible; -+ (RCTMGLStyleValue*)make:(NSDictionary*)config; ++ (RCTMGLStyleValue*)make:(NSString*)expressionJSONStr; @end diff --git a/ios/RCTMGL/RCTMGLStyleValue.m b/ios/RCTMGL/RCTMGLStyleValue.m index 20680d8cfb..471e1750e7 100644 --- a/ios/RCTMGL/RCTMGLStyleValue.m +++ b/ios/RCTMGL/RCTMGLStyleValue.m @@ -12,177 +12,121 @@ @implementation RCTMGLStyleValue { - NSString *type; - NSDictionary *payload; + NSObject *expressionJSON; } -- (void)setConfig:(NSDictionary *)config +- (NSExpression *)mglStyleValue { - _config = config; - type = (NSString*)config[@"styletype"]; - payload = (NSDictionary*)config[@"payload"]; -} - -- (NSString*)type -{ - return type; + if ([_styleType isEqualToString:@"color"] && [expressionJSON respondsToSelector:@selector(objectEnumerator)] && [[[(NSArray*)expressionJSON objectEnumerator] nextObject] isKindOfClass:[NSNumber class]]) { + UIColor *color = [RCTMGLUtils toColor:expressionJSON]; + return [NSExpression expressionWithMGLJSONObject:color]; + } else if ([_styleType isEqualToString:@"color"] && [expressionJSON isKindOfClass:[NSNumber class]]) { + + UIColor *color = [RCTMGLUtils toColor:expressionJSON]; + return [NSExpression expressionWithMGLJSONObject:color]; + } else if ([_styleType isEqualToString:@"vector"] && [expressionJSON respondsToSelector:@selector(objectEnumerator)] && [[[(NSArray*)expressionJSON objectEnumerator] nextObject] isKindOfClass:[NSNumber class]]) { + CGVector vector = [RCTMGLUtils toCGVector:(NSArray *)expressionJSON]; + return [NSExpression expressionWithMGLJSONObject:[NSValue valueWithCGVector:vector]]; + } else if ([_styleType isEqual:@"edgeinsets"] && [expressionJSON isKindOfClass:[NSNumber class]]){ + UIEdgeInsets edgeInsets = [RCTMGLUtils toUIEdgeInsets:(NSArray *)expressionJSON]; + return [NSExpression expressionWithMGLJSONObject:[NSValue valueWithUIEdgeInsets:edgeInsets]]; + } else { + return [NSExpression expressionWithMGLJSONObject:expressionJSON]; + } } -- (NSDictionary*)payload +- (void)setStyleObject:(NSObject *)object { - return payload; + expressionJSON = object; } -- (id)mglStyleValue +- (NSObject *)parse:(NSDictionary *)rawStyleValue { - if ([self isFunction]) { - return [self makeStyleFunction]; - } + NSObject *object = nil; + NSString *type = (NSString *)rawStyleValue[@"type"]; - id rawValue = self.payload[@"value"]; - - if ([self.type isEqualToString:@"color"]) { - rawValue = [RCTMGLUtils toColor:rawValue]; - } else if ([self.type isEqualToString:@"translate"]) { - rawValue = [NSValue valueWithCGVector:[RCTMGLUtils toCGVector:rawValue]]; - } - - // check for overrides that handle special cases like NSArray vs CGVector - NSDictionary *iosTypeOverride = self.payload[@"iosType"]; - if (iosTypeOverride != nil) { - if ([iosTypeOverride isEqual:@"vector"]) { - rawValue = [NSValue valueWithCGVector:[RCTMGLUtils toCGVector:rawValue]]; - } else if ([iosTypeOverride isEqual:@"edgeinsets"]){ - rawValue = [NSValue valueWithUIEdgeInsets:[RCTMGLUtils toUIEdgeInsets:rawValue]]; + if ([type isEqualToString:@"string"]) { + object = (NSString *)rawStyleValue[@"value"]; + } else if ([type isEqualToString:@"number"]) { + object = (NSNumber *)rawStyleValue[@"value"]; + } else if ([type isEqualToString:@"boolean"]) { + object = rawStyleValue[@"value"]; + } else if ([type isEqualToString:@"hashmap"]) { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + NSArray *values = (NSArray *)rawStyleValue[@"value"]; + + for (int i = 0; i < values.count; i++) { + NSObject *key = [self parse:values[i][0]]; + NSObject *value = [self parse:values[i][1]]; + dict[[key mutableCopy]] = value; } + + object = dict; + } else if ([type isEqualToString:@"array"]) { + NSMutableArray *arr = [[NSMutableArray alloc] init]; + NSArray *values = (NSArray *)rawStyleValue[@"value"]; + + for (int i = 0; i < values.count; i++) { + [arr addObject:[self parse:values[i]]]; + } + + object = arr; } - id propertyValue = self.payload[@"propertyValue"]; - if (propertyValue != nil) { - return @{ propertyValue: [MGLStyleValue valueWithRawValue:rawValue] }; - } - - return [MGLStyleValue valueWithRawValue:rawValue]; + return object; } -- (BOOL)isFunction +- (BOOL)shouldAddImage { - return [type isEqualToString:@"function"]; + NSString *imageURI = (NSString *)expressionJSON; + return [imageURI containsString:@"://"]; } -- (BOOL)isTranslation +- (NSString *)getImageURI { - return [type isEqualToString:@"translate"]; -} - -- (BOOL)isFunctionTypeSupported:(NSArray *)allowedFunctionTypes -{ - NSString *fnType = (NSString*)payload[@"fn"]; - - for (NSString *curFnType in allowedFunctionTypes) { - if ([curFnType isEqualToString:fnType]) { - return YES; - } - } - - return NO; -} - -- (MGLStyleValue*)makeStyleFunction -{ - NSString *fnType = (NSString*)payload[@"fn"]; - NSArray *> *rawStops = payload[@"stops"]; - NSNumber *mode = payload[@"mode"]; - NSString *attributeName = payload[@"attributeName"]; - - NSMutableDictionary *stops = nil; - if (rawStops.count > 0) { - stops = [[NSMutableDictionary alloc] init]; - - for (NSArray *rawStop in rawStops) { - NSDictionary *jsStopKey = rawStop[0]; - NSDictionary *jsStopValue = rawStop[1]; - RCTMGLStyleValue *rctStyleValue = [RCTMGLStyleValue make:jsStopValue]; - stops[[self _getStopKey:jsStopKey]] = rctStyleValue.mglStyleValue; - } - } - - MGLInterpolationMode interpolationMode = [mode integerValue]; - if ([fnType isEqualToString:@"camera"]) { - return [MGLStyleValue valueWithInterpolationMode:interpolationMode - cameraStops:stops - options:nil]; - } else if ([fnType isEqualToString:@"source"]) { - return [MGLStyleValue valueWithInterpolationMode:interpolationMode - sourceStops:stops - attributeName:attributeName - options:nil]; - } else if ([fnType isEqualToString:@"composite"]) { - return [MGLStyleValue valueWithInterpolationMode:interpolationMode - compositeStops:stops - attributeName:attributeName - options:nil]; - } else { - return nil; - } + return (NSString *)expressionJSON; } - (MGLTransition)getTransition { - if (![self.type isEqualToString:@"transition"]) { - return MGLTransitionMake(0, 0); - } - - NSDictionary *config = self.payload[@"value"]; - if (config == nil) { - return MGLTransitionMake(0, 0); + if (![expressionJSON isKindOfClass:[NSDictionary class]]) { + return MGLTransitionMake(0.f, 0.f); } - NSNumber *duration = config[@"duration"]; - NSNumber *delay = config[@"delay"]; + NSDictionary *config = (NSDictionary *)expressionJSON; + NSNumber *duration = config[@"duration"] != nil ? @([config[@"duration"] floatValue]) : @(0.f); + NSNumber *delay = config[@"delay"] != nil ? @([config[@"delay"] floatValue]) : @(0.f); - return MGLTransitionMake([duration doubleValue], [delay doubleValue]); + return MGLTransitionMake([duration floatValue], [delay floatValue]); } -- (MGLStyleValue*)getSphericalPosition +- (NSExpression *)getSphericalPosition { - NSArray *values = self.payload[@"value"]; + NSArray *values = (NSArray *)expressionJSON; CGFloat radial = [values[0] floatValue]; CLLocationDistance azimuthal = [values[1] doubleValue]; CLLocationDistance polar = [values[2] doubleValue]; MGLSphericalPosition pos = MGLSphericalPositionMake(radial, azimuthal, polar); - return [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLSphericalPosition:pos]]; + return [NSExpression expressionWithMGLJSONObject:@(pos)]; } - (BOOL)isVisible { - id value = self.payload[@"value"]; - if (![value isKindOfClass:[NSString class]]) { - return NO; - } - return [value isEqualToString:@"visible"]; -} - -- (id)_getStopKey:(NSDictionary *)jsStopKey -{ - NSString *payloadKey = @"value"; - NSString *type = jsStopKey[@"type"]; - - if ([type isEqualToString:@"number"]) { - return (NSNumber *)jsStopKey[payloadKey]; - } else if ([type isEqualToString:@"boolean"]) { - return [NSNumber numberWithBool:jsStopKey[payloadKey]]; - } else { - return (NSString *)jsStopKey[payloadKey]; + if ([expressionJSON isKindOfClass:[NSString class]]) { + NSString *visible = (NSString *)expressionJSON; + return [visible isEqualToString:@"visible"]; } + return YES; } -+ (RCTMGLStyleValue*)make:(NSDictionary*)config; ++ (RCTMGLStyleValue*)make:(NSDictionary*)rawStyleValue; { RCTMGLStyleValue *styleValue = [[RCTMGLStyleValue alloc] init]; - styleValue.config = config; + styleValue.styleType = (NSString *)rawStyleValue[@"styletype"]; + NSObject *object = [styleValue parse:(NSDictionary *)rawStyleValue[@"stylevalue"]]; + [styleValue setStyleObject:object]; return styleValue; } diff --git a/ios/RCTMGL/RCTMGLSymbolLayer.h b/ios/RCTMGL/RCTMGLSymbolLayer.h index b0f879ec9d..a9c17336f8 100644 --- a/ios/RCTMGL/RCTMGLSymbolLayer.h +++ b/ios/RCTMGL/RCTMGLSymbolLayer.h @@ -6,10 +6,14 @@ // Copyright © 2017 Mapbox Inc. All rights reserved. // +#import #import "RCTMGLLayer.h" -@interface RCTMGLSymbolLayer : RCTMGLLayer +@interface RCTMGLSymbolLayer : RCTMGLLayer +@property (nonatomic, strong) NSMutableArray> *reactSubviews; + +@property (nonatomic, assign) BOOL snapshot; @property (nonatomic, copy) NSString *sourceLayerID; @end diff --git a/ios/RCTMGL/RCTMGLSymbolLayer.m b/ios/RCTMGL/RCTMGLSymbolLayer.m index 7d70300392..c9254efe17 100644 --- a/ios/RCTMGL/RCTMGLSymbolLayer.m +++ b/ios/RCTMGL/RCTMGLSymbolLayer.m @@ -8,9 +8,46 @@ #import "RCTMGLSymbolLayer.h" #import "RCTMGLStyle.h" +#import "UIView+React.h" @implementation RCTMGLSymbolLayer +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + _reactSubviews = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)invalidate +{ + if (_snapshot == YES && self.style != nil) { + [self.style removeImageForName:self.id]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" +- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex { + [_reactSubviews insertObject:(UIView *)subview atIndex:(NSUInteger) atIndex]; +} +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" +- (void)removeReactSubview:(id)subview { + [_reactSubviews removeObject:(UIView *)subview]; +} +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" +- (NSArray> *)reactSubviews { + return nil; +} +#pragma clang diagnostic pop + - (void)updateFilter:(NSPredicate *)predicate { ((MGLSymbolStyleLayer *) self.styleLayer).predicate = predicate; @@ -25,6 +62,29 @@ - (void)setSourceLayerID:(NSString *)sourceLayerID } } +- (void)setSnapshot:(BOOL)snapshot +{ + _snapshot = snapshot; + + if (self.style != nil) { + UIImage *image; + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *) self.styleLayer; + + if (_snapshot == YES) { + image = [self _createViewSnapshot]; + [self.style setImage:image forName:self.id]; + layer.iconImageName = [NSExpression expressionWithFormat:self.id]; + } else { + image = [self.style imageForName:self.id]; + + if (image != nil) { + [self.style removeImageForName:self.id]; + layer.iconImageName = nil; + } + } + } +} + - (void)addToMap:(MGLStyle *)style { [super addToMap:style]; @@ -33,6 +93,17 @@ - (void)addToMap:(MGLStyle *)style if (filter != nil) { [self updateFilter:filter]; } + + if (_snapshot == YES) { + UIImage *image = [self _createViewSnapshot]; + + if (image != nil) { + [style setImage:image forName:self.id]; + + MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)self.styleLayer; + layer.iconImageName = [NSExpression expressionForConstantValue:self.id]; + } + } } - (MGLSymbolStyleLayer*)makeLayer:(MGLStyle*)style @@ -50,4 +121,17 @@ - (void)addStyles [style symbolLayer:(MGLSymbolStyleLayer*)self.styleLayer withReactStyle:self.reactStyle]; } +- (UIImage *)_createViewSnapshot +{ + if (_reactSubviews.count == 0) { + return nil; + } + UIView *view = (UIView *)_reactSubviews[0]; + UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.f); + [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; + UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return snapshot; +} + @end diff --git a/ios/RCTMGL/RCTMGLSymbolLayerManager.m b/ios/RCTMGL/RCTMGLSymbolLayerManager.m index e4934631d7..5fd8b3e05b 100644 --- a/ios/RCTMGL/RCTMGLSymbolLayerManager.m +++ b/ios/RCTMGL/RCTMGLSymbolLayerManager.m @@ -20,6 +20,7 @@ @implementation RCTMGLSymbolLayerManager RCT_EXPORT_VIEW_PROPERTY(id, NSString); RCT_EXPORT_VIEW_PROPERTY(sourceID, NSString); RCT_EXPORT_VIEW_PROPERTY(filter, NSArray); +RCT_EXPORT_VIEW_PROPERTY(snapshot, BOOL); RCT_EXPORT_VIEW_PROPERTY(aboveLayerID, NSString); RCT_EXPORT_VIEW_PROPERTY(belowLayerID, NSString); diff --git a/ios/RCTMGL/RCTMGLUtils.m b/ios/RCTMGL/RCTMGLUtils.m index 0530cb4d7e..75f792d9a3 100644 --- a/ios/RCTMGL/RCTMGLUtils.m +++ b/ios/RCTMGL/RCTMGLUtils.m @@ -30,7 +30,21 @@ + (UIEdgeInsets)toUIEdgeInsets:(NSArray *)arr + (MGLShape*)shapeFromGeoJSON:(NSString*)jsonStr { NSData* data = [jsonStr dataUsingEncoding:NSUTF8StringEncoding]; - return [MGLShape shapeWithData:data encoding:NSUTF8StringEncoding error:nil]; + NSError* error = nil; + MGLShape* result = [MGLShape shapeWithData:data encoding:NSUTF8StringEncoding error:&error]; + if (error != nil) { + RCTLogWarn(@"Failed to convert data to shape error:%@ src:%@", error, jsonStr); + } + return result; +} + ++ (NSString *)hashURI:(NSString *)uri +{ + if (uri == nil) { + return @"-1"; + } + NSUInteger hash = [uri hash]; + return [NSString stringWithFormat:@"%lu", (unsigned long)hash]; } + (MGLCoordinateBounds)fromFeatureCollection:(NSString*)jsonStr @@ -108,10 +122,15 @@ + (void)fetchImages:(RCTBridge *)bridge style:(MGLStyle *)style objects:(NSDicti if (foundImage == nil) { [RCTMGLImageQueue.sharedInstance addImage:objects[imageName] bridge:bridge completionHandler:^(NSError *error, UIImage *image) { + if (!image) { + RCTLogWarn(@"Failed to fetch image: %@ error:%@", imageName, error); + } + else { dispatch_async(dispatch_get_main_queue(), ^{ [weakStyle setImage:image forName:imageName]; imageLoadedBlock(); }); + } }]; } else { imageLoadedBlock(); diff --git a/ios/RCTMGL/RCTMGLVectorSource.m b/ios/RCTMGL/RCTMGLVectorSource.m index 9df8b86f83..fbdb8e9ec0 100644 --- a/ios/RCTMGL/RCTMGLVectorSource.m +++ b/ios/RCTMGL/RCTMGLVectorSource.m @@ -12,7 +12,7 @@ @implementation RCTMGLVectorSource - (MGLSource*)makeSource { - return [[MGLVectorSource alloc] initWithIdentifier:self.id configurationURL:[NSURL URLWithString:_url]]; + return [[MGLVectorTileSource alloc] initWithIdentifier:self.id configurationURL:[NSURL URLWithString:_url]]; } @end diff --git a/javascript/components/AbstractLayer.js b/javascript/components/AbstractLayer.js index a943339c9c..977fff3e0f 100644 --- a/javascript/components/AbstractLayer.js +++ b/javascript/components/AbstractLayer.js @@ -1,10 +1,13 @@ /* eslint react/prop-types:0 */ import React from 'react'; +import {processColor} from 'react-native'; +import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; -import MapboxStyleSheet from '../utils/MapboxStyleSheet'; import {getFilter} from '../utils/filterUtils'; +import {getStyleType} from '../utils/styleMap'; +import BridgeValue from '../utils/BridgeValue'; -class AbstractLayer extends React.Component { +class AbstractLayer extends React.PureComponent { get baseProps() { return { ...this.props, @@ -21,31 +24,44 @@ class AbstractLayer extends React.Component { }; } + getStyleTypeFormatter(styleType) { + if (styleType === 'color') { + return processColor; + } + } + getStyle() { if (!this.props.style) { return; } - if (!Array.isArray(this.props.style)) { - return this._getMapboxStyleSheet(this.props.style); - } + const nativeStyle = {}; + const styleProps = Object.keys(this.props.style); + for (const styleProp of styleProps) { + const styleType = getStyleType(styleProp); + let rawStyle = this.props.style[styleProp]; - const styles = this.props.style; - let flattenStyle = {}; - - for (const style of styles) { - if (!style) { - continue; + if (styleType === 'color' && typeof rawStyle === 'string') { + rawStyle = processColor(rawStyle); + } else if (styleType === 'image' && typeof rawStyle === 'number') { + const asset = resolveAssetSource(rawStyle) || {}; + rawStyle = asset.uri || rawStyle; } - const mapboxStyle = this._getMapboxStyleSheet(style); - flattenStyle = Object.assign(flattenStyle, mapboxStyle); + + const bridgeValue = new BridgeValue(rawStyle); + nativeStyle[styleProp] = { + styletype: styleType, + stylevalue: bridgeValue.toJSON(), + }; } - return flattenStyle; + return nativeStyle; } - _getMapboxStyleSheet(style) { - return MapboxStyleSheet.create(style); + setNativeProps(props) { + if (this.refs.nativeLayer) { + this.refs.nativeLayer.setNativeProps(props); + } } } diff --git a/javascript/components/AbstractSource.js b/javascript/components/AbstractSource.js new file mode 100644 index 0000000000..db68c91cbf --- /dev/null +++ b/javascript/components/AbstractSource.js @@ -0,0 +1,11 @@ +import React from 'react'; + +class AbstractSource extends React.PureComponent { + setNativeProps(props) { + if (this.refs.nativeSource) { + this.refs.nativeSource.setNativeProps(props); + } + } +} + +export default AbstractSource; diff --git a/javascript/components/BackgroundLayer.js b/javascript/components/BackgroundLayer.js index 1be9b76937..4f3ffcbe74 100644 --- a/javascript/components/BackgroundLayer.js +++ b/javascript/components/BackgroundLayer.js @@ -74,7 +74,7 @@ class BackgroundLayer extends AbstractLayer { }; render() { - return ; + return ; } } diff --git a/javascript/components/Camera.js b/javascript/components/Camera.js new file mode 100644 index 0000000000..697e95ca31 --- /dev/null +++ b/javascript/components/Camera.js @@ -0,0 +1,324 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {NativeModules, requireNativeComponent} from 'react-native'; + +import locationManager from '../modules/location/locationManager'; +import {isNumber, toJSONString, viewPropTypes, existenceChange} from '../utils'; +import * as geoUtils from '../utils/geoUtils'; + +import NativeBridgeComponent from './NativeBridgeComponent'; + +const MapboxGL = NativeModules.MGLModule; + +export const NATIVE_MODULE_NAME = 'RCTMGLCamera'; + +class Camera extends NativeBridgeComponent { + static propTypes = { + ...viewPropTypes, + + animationDuration: PropTypes.number, + + animationMode: PropTypes.oneOf(['flyTo', 'easeTo', 'moveTo']), + + // normal + centerCoordinate: PropTypes.arrayOf(PropTypes.number), + heading: PropTypes.number, + pitch: PropTypes.number, + bounds: PropTypes.shape({ + ne: PropTypes.arrayOf(PropTypes.number).isRequired, + sw: PropTypes.arrayOf(PropTypes.number).isRequired, + paddingLeft: PropTypes.number, + paddingRight: PropTypes.number, + paddingTop: PropTypes.number, + paddingBottom: PropTypes.number, + }), + zoomLevel: PropTypes.number, + minZoomLevel: PropTypes.number, + maxZoomLevel: PropTypes.number, + + // user tracking + followUserLocation: PropTypes.bool, + + followUserMode: PropTypes.oneOf(['normal', 'compass', 'course']), + + followZoomLevel: PropTypes.number, + followPitch: PropTypes.number, + followHeading: PropTypes.number, + + // manual update + triggerKey: PropTypes.any, + + // position + alignment: PropTypes.arrayOf(PropTypes.number), + }; + + static defaultProps = { + animationMode: 'easeTo', + animationDuration: 2000, + isUserInteraction: false, + }; + + static Mode = { + Flight: 'flyTo', + Move: 'moveTo', + Ease: 'easeTo', + }; + + componentWillReceiveProps(nextProps) { + this._handleCameraChange(this.props, nextProps); + } + + shouldComponentUpdate() { + return false; + } + + _handleCameraChange(currentCamera, nextCamera) { + const hasCameraChanged = this._hasCameraChanged(currentCamera, nextCamera); + if (!hasCameraChanged) { + return; + } + + if (currentCamera.followUserLocation && !nextCamera.followUserLocation) { + this.refs.camera.setNativeProps({followUserLocation: false}); + return; + } + if (!currentCamera.followUserLocation && nextCamera.followUserLocation) { + this.refs.camera.setNativeProps({followUserLocation: true}); + } + + if (nextCamera.followUserLocation) { + this.refs.camera.setNativeProps({ + followPitch: nextCamera.followPitch || nextCamera.pitch, + followHeading: nextCamera.followHeading || nextCamera.heading, + followZoomLevel: nextCamera.followZoomLevel || nextCamera.zoomLevel, + }); + return; + } + + const cameraConfig = { + animationMode: nextCamera.animationMode, + animationDuration: nextCamera.animationDuration, + zoomLevel: nextCamera.zoomLevel, + pitch: nextCamera.pitch, + heading: nextCamera.heading, + }; + + if ( + nextCamera.bounds && + this._hasBoundsChanged(currentCamera, nextCamera) + ) { + cameraConfig.bounds = nextCamera.bounds; + } else { + cameraConfig.centerCoordinate = nextCamera.centerCoordinate; + } + + this._setCamera(cameraConfig); + } + + _hasCameraChanged(currentCamera, nextCamera) { + const c = currentCamera; + const n = nextCamera; + + const hasDefaultPropsChanged = + c.heading !== n.heading || + this._hasCenterCoordinateChanged(c, n) || + this._hasBoundsChanged(c, n) || + c.pitch !== n.pitch || + c.zoomLevel !== n.zoomLevel || + c.triggerKey !== n.triggerKey; + + const hasFollowPropsChanged = + c.followUserLocation !== n.followUserLocation || + c.followUserMode !== n.followUserMode || + c.followZoomLevel !== n.followZoomLevel || + c.followHeading !== n.followHeading || + c.followPitch !== n.followPitch; + + const hasAnimationPropsChanged = + c.animationMode !== n.animationMode || + c.animationDuration !== n.animationDuration; + + return ( + hasDefaultPropsChanged || + hasFollowPropsChanged || + hasAnimationPropsChanged + ); + } + + _hasCenterCoordinateChanged(currentCamera, nextCamera) { + const cC = currentCamera.centerCoordinate; + const nC = nextCamera.centerCoordinate; + + if (existenceChange(cC, nC)) { + return true; + } + + if (!cC && !nC) { + return false; + } + + const isLngDiff = + currentCamera.centerCoordinate[0] !== nextCamera.centerCoordinate[0]; + const isLatDiff = + currentCamera.centerCoordinate[1] !== nextCamera.centerCoordinate[1]; + return isLngDiff || isLatDiff; + } + + _hasBoundsChanged(currentCamera, nextCamera) { + const cB = currentCamera.bounds; + const nB = nextCamera.bounds; + + if (!cB && !nB) { + return false; + } + + if (existenceChange(cB, nB)) { + return true; + } + + return ( + cB.ne[0] !== nB.ne[0] || + cB.ne[1] !== nB.ne[1] || + cB.sw[0] !== nB.sw[0] || + cB.sw[1] !== nB.sw[1] || + cB.paddingTop != nB.paddingTop || + cB.paddingLeft != nB.pddingLeft || + cB.paddingRight != nB.paddingRight || + cB.paddingBottom != nB.paddingBottom + ); + } + + _setCamera(config = {}) { + let cameraConfig = {}; + + if (config.stops) { + cameraConfig.stops = []; + + for (const stop of config.stops) { + cameraConfig.stops.push(this._createStopConfig(stop)); + } + } else { + cameraConfig = this._createStopConfig(config); + } + + this.refs.camera.setNativeProps({stop: cameraConfig}); + } + + _createStopConfig(config = {}) { + if (this.props.followUserLocation) { + return null; + } + + const stopConfig = { + mode: this._getNativeCameraMode(config), + pitch: config.pitch, + heading: config.heading, + duration: config.animationDuration || 0, + zoom: config.zoomLevel, + }; + + if (config.centerCoordinate) { + stopConfig.centerCoordinate = toJSONString( + geoUtils.makePoint(config.centerCoordinate), + ); + } + + if (config.bounds && config.bounds.ne && config.bounds.sw) { + const { + ne, + sw, + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + } = config.bounds; + stopConfig.bounds = toJSONString(geoUtils.makeLatLngBounds(ne, sw)); + stopConfig.boundsPaddingTop = paddingTop || 0; + stopConfig.boundsPaddingRight = paddingRight || 0; + stopConfig.boundsPaddingBottom = paddingBottom || 0; + stopConfig.boundsPaddingLeft = paddingLeft || 0; + } + + return stopConfig; + } + + _getNativeCameraMode(config) { + switch (config.animationMode) { + case Camera.Mode.Flight: + return MapboxGL.CameraModes.Flight; + case Camera.Mode.Move: + return MapboxGL.CameraModes.None; + default: + return MapboxGL.CameraModes.Ease; + } + } + + _getAlignment(coordinate, zoomLevel) { + const region = geoUtils.getOrCalculateVisibleRegion( + coordinate, + zoomLevel, + this.props._mapWidth, + this.props._mapHeight, + this.props._region, + ); + + const topLeftCorner = [region.sw[0], region.ne[1]]; + const topRightCorner = [region.ne[0], region.ne[1]]; + const bottomLeftCorner = [region.sw[0], region.sw[1]]; + + const verticalLineString = geoUtils.makeLineString([ + topLeftCorner, + bottomLeftCorner, + ]); + + const horizontalLineString = geoUtils.makeLineString([ + topLeftCorner, + topRightCorner, + ]); + + const distVertical = geoUtils.calculateDistance( + topLeftCorner, + bottomLeftCorner, + ); + const distHorizontal = geoUtils.calculateDistance( + topLeftCorner, + topRightCorner, + ); + + const verticalPoint = geoUtils.pointAlongLine( + verticalLineString, + distVertical * this.props.alignment[0], + ); + + const horizontalPoint = geoUtils.pointAlongLine( + horizontalLineString, + distHorizontal * this.props.alignment[1], + ); + + return [verticalPoint[0], horizontalPoint[1]]; + } + + render() { + const props = Object.assign({}, this.props); + + return ( + + ); + } +} + +const RCTMGLCamera = requireNativeComponent(NATIVE_MODULE_NAME, Camera, { + nativeOnly: { + stop: true, + }, +}); + +export default Camera; diff --git a/javascript/components/CircleLayer.js b/javascript/components/CircleLayer.js index 62585ca477..0e18a5ebb4 100644 --- a/javascript/components/CircleLayer.js +++ b/javascript/components/CircleLayer.js @@ -83,7 +83,7 @@ class CircleLayer extends AbstractLayer { ...this.baseProps, sourceLayerID: this.props.sourceLayerID, }; - return ; + return ; } } diff --git a/javascript/components/FillExtrusionLayer.js b/javascript/components/FillExtrusionLayer.js index 9cad1e9d3d..9b30ad93d1 100644 --- a/javascript/components/FillExtrusionLayer.js +++ b/javascript/components/FillExtrusionLayer.js @@ -81,7 +81,7 @@ class FillExtrusionLayer extends AbstractLayer { ...this.baseProps, sourceLayerID: this.props.sourceLayerID, }; - return ; + return ; } } diff --git a/javascript/components/FillLayer.js b/javascript/components/FillLayer.js index 90c3504bf8..7bb480ac92 100644 --- a/javascript/components/FillLayer.js +++ b/javascript/components/FillLayer.js @@ -81,7 +81,7 @@ class FillLayer extends AbstractLayer { ...this.baseProps, sourceLayerID: this.props.sourceLayerID, }; - return ; + return ; } } diff --git a/javascript/components/ImageSource.js b/javascript/components/ImageSource.js index 3d77648689..011c6a9faf 100644 --- a/javascript/components/ImageSource.js +++ b/javascript/components/ImageSource.js @@ -9,13 +9,15 @@ import { resolveImagePath, } from '../utils'; +import AbstractSource from './AbstractSource'; + export const NATIVE_MODULE_NAME = 'RCTMGLImageSource'; /** * ImageSource is a content source that is used for a georeferenced raster image to be shown on the map. * The georeferenced image scales and rotates as the user zooms and rotates the map */ -class ImageSource extends React.PureComponent { +class ImageSource extends AbstractSource { static propTypes = { ...viewPropTypes, @@ -63,7 +65,7 @@ class ImageSource extends React.PureComponent { }; return ( - + {cloneReactChildrenWithProps(this.props.children, { sourceID: this.props.id, })} diff --git a/javascript/components/Light.js b/javascript/components/Light.js index c8ceacedf5..e7c2360844 100644 --- a/javascript/components/Light.js +++ b/javascript/components/Light.js @@ -21,9 +21,16 @@ class Light extends AbstractLayer { style: LightLayerStyleProp, }; + setNativeProps(props) { + if (this.refs.nativeLight) { + this.refs.nativeLight.setNativeProps(props); + } + } + render() { return ( ; + return ; } } diff --git a/javascript/components/MapView.js b/javascript/components/MapView.js index 54de1686b6..07ea6a5e44 100644 --- a/javascript/components/MapView.js +++ b/javascript/components/MapView.js @@ -19,6 +19,9 @@ import { } from '../utils'; import {getFilter} from '../utils/filterUtils'; +import NativeBridgeComponent from './NativeBridgeComponent'; +import Camera from './Camera'; + const MapboxGL = NativeModules.MGLModule; export const NATIVE_MODULE_NAME = 'RCTMGLMapView'; @@ -32,27 +35,10 @@ const styles = StyleSheet.create({ /** * MapView backed by Mapbox Native GL */ -class MapView extends React.Component { +class MapView extends NativeBridgeComponent { static propTypes = { ...viewPropTypes, - /** - * Animates changes between pitch and bearing - */ - animated: PropTypes.bool, - - /** - * Initial center coordinate on map [lng, lat] - */ - centerCoordinate: PropTypes.arrayOf(PropTypes.number), - - /** - * Initial bounds on map [[lng, lat], [lng, lat]] - */ - visibleCoordinateBounds: PropTypes.arrayOf( - PropTypes.arrayOf(PropTypes.number), - ), - /** * Shows the users location on the map */ @@ -76,16 +62,6 @@ class MapView extends React.Component { PropTypes.number, ]), - /** - * Initial heading on map - */ - heading: PropTypes.number, - - /** - * Initial pitch on map - */ - pitch: PropTypes.number, - /** * Style for wrapping React Native View */ @@ -96,21 +72,6 @@ class MapView extends React.Component { */ styleURL: PropTypes.string, - /** - * Initial zoom level of map - */ - zoomLevel: PropTypes.number, - - /** - * Min zoom level of map - */ - minZoomLevel: PropTypes.number, - - /** - * Max zoom level of map - */ - maxZoomLevel: PropTypes.number, - /** * Automatically change the language of the map labels to the system’s preferred language, * this is not something that can be toggled on/off @@ -190,11 +151,6 @@ class MapView extends React.Component { */ onRegionDidChange: PropTypes.func, - /** - * This event is triggered whenever the location engine receives a location update - */ - onUserLocationUpdate: PropTypes.func, - /** * This event is triggered when the map is about to start loading a new map style. */ @@ -245,11 +201,6 @@ class MapView extends React.Component { */ onDidFinishLoadingStyle: PropTypes.func, - /** - * This event is triggered when the users tracking mode is changed. - */ - onUserTrackingModeChange: PropTypes.func, - /** * The emitted frequency of regionwillchange events */ @@ -262,17 +213,12 @@ class MapView extends React.Component { }; static defaultProps = { - animated: false, - heading: 0, - pitch: 0, localizeLabels: false, scrollEnabled: true, pitchEnabled: true, rotateEnabled: true, attributionEnabled: true, logoEnabled: true, - zoomLevel: 16, - userTrackingMode: MapboxGL.UserTrackingModes.None, styleURL: MapboxGL.StyleURL.Street, surfaceView: false, regionWillChangeDebounceTime: 10, @@ -284,12 +230,15 @@ class MapView extends React.Component { this.state = { isReady: null, + region: null, + width: 0, + height: 0, + isUserInteraction: false, }; this._onPress = this._onPress.bind(this); this._onLongPress = this._onLongPress.bind(this); this._onChange = this._onChange.bind(this); - this._onAndroidCallback = this._onAndroidCallback.bind(this); this._onLayout = this._onLayout.bind(this); // debounced map change methods @@ -304,7 +253,6 @@ class MapView extends React.Component { props.regionDidChangeDebounceTime, ); - this._callbackMap = new Map(); this._preRefMapMethodQueue = []; } @@ -569,7 +517,7 @@ class MapView extends React.Component { } /** - * Map camera will perform updates based on provided config. Advanced use only! + * Map camera will perform updates based on provided config. * * @example * this.map.setCamera({ @@ -588,19 +536,9 @@ class MapView extends React.Component { * @param {Object} config - Camera configuration */ setCamera(config = {}) { - let cameraConfig = {}; - - if (config.stops) { - cameraConfig.stops = []; - - for (const stop of config.stops) { - cameraConfig.stops.push(this._createStopConfig(stop)); - } - } else { - cameraConfig = this._createStopConfig(config); - } - - return this._runNativeCommand('setCamera', [cameraConfig]); + console.warn( + 'MapView.setCamera is deprecated - please use a Camera#setCamera', + ); } /** @@ -666,10 +604,10 @@ class MapView extends React.Component { runNativeCommand(NATIVE_MODULE_NAME, methodName, this._nativeRef, args); }); } - return runNativeCommand( + return super._runNativeCommand( NATIVE_MODULE_NAME, - methodName, this._nativeRef, + methodName, args, ); } @@ -744,12 +682,14 @@ class MapView extends React.Component { if (isFunction(this.props.onRegionWillChange)) { this.props.onRegionWillChange(payload); } + this.setState({isUserInteraction: payload.properties.isUserInteraction}); } _onRegionDidChange(payload) { if (isFunction(this.props.onRegionDidChange)) { this.props.onRegionDidChange(payload); } + this.setState({region: payload}); } _onChange(e) { @@ -820,8 +760,12 @@ class MapView extends React.Component { } } - _onLayout() { - this.setState({isReady: true}); + _onLayout(e) { + this.setState({ + isReady: true, + width: e.nativeEvent.layout.width, + height: e.nativeEvent.layout.height, + }); } _handleOnChange(propName, payload) { @@ -877,11 +821,15 @@ class MapView extends React.Component { } } + setNativeProps(props) { + if (this._nativeRef) { + this._nativeRef.setNativeProps(props); + } + } + render() { const props = { ...this.props, - centerCoordinate: this._getCenterCoordinate(), - visibleCoordinateBounds: this._getVisibleCoordinateBounds(), contentInset: this._getContentInset(), style: styles.matchParent, }; diff --git a/javascript/components/NativeBridgeComponent.js b/javascript/components/NativeBridgeComponent.js new file mode 100644 index 0000000000..f92d9dfb2f --- /dev/null +++ b/javascript/components/NativeBridgeComponent.js @@ -0,0 +1,47 @@ +import React from 'react'; + +import {runNativeCommand, isAndroid} from '../utils'; + +class NativeBridgeComponent extends React.Component { + constructor(props) { + super(props); + + this._onAndroidCallback = this._onAndroidCallback.bind(this); + this._callbackMap = new Map(); + } + + _addAddAndroidCallback(id, callback) { + this._callbackMap.set(id, callback); + } + + _removeAndroidCallback(id) { + this._callbackMap.remove(id); + } + + _onAndroidCallback(e) { + const callbackID = e.nativeEvent.type; + const callback = this._callbackMap.get(callbackID); + + if (!callback) { + return; + } + + this._callbackMap.delete(callbackID); + callback.call(null, e.nativeEvent.payload); + } + + _runNativeCommand(nativeModuleName, nativeRef, methodName, args = []) { + if (isAndroid()) { + return new Promise(resolve => { + const callbackID = `${Date.now()}`; + this._addAddAndroidCallback(callbackID, resolve); + args.unshift(callbackID); + runNativeCommand(nativeModuleName, methodName, this._nativeRef, args); + }); + } + + return runNativeCommand(nativeModuleName, methodName, nativeRef, args); + } +} + +export default NativeBridgeComponent; diff --git a/javascript/components/RasterLayer.js b/javascript/components/RasterLayer.js index 60c2a52e17..7a09542a98 100644 --- a/javascript/components/RasterLayer.js +++ b/javascript/components/RasterLayer.js @@ -78,7 +78,7 @@ class RasterLayer extends AbstractLayer { ...this.baseProps, sourceLayerID: this.props.sourceLayerID, }; - return ; + return ; } } diff --git a/javascript/components/RasterSource.js b/javascript/components/RasterSource.js index d160e6edd0..b2c5aa1c9e 100644 --- a/javascript/components/RasterSource.js +++ b/javascript/components/RasterSource.js @@ -4,6 +4,8 @@ import {NativeModules, requireNativeComponent} from 'react-native'; import {cloneReactChildrenWithProps, viewPropTypes} from '../utils'; +import AbstractSource from './AbstractSource'; + const MapboxGL = NativeModules.MGLModule; export const NATIVE_MODULE_NAME = 'RCTMGLRasterSource'; @@ -13,7 +15,7 @@ export const NATIVE_MODULE_NAME = 'RCTMGLRasterSource'; * The location of and metadata about the tiles are defined either by an option dictionary * or by an external file that conforms to the TileJSON specification. */ -class RasterSource extends React.Component { +class RasterSource extends AbstractSource { static propTypes = { ...viewPropTypes, @@ -75,7 +77,7 @@ class RasterSource extends React.Component { attribution: this.props.attribution, }; return ( - + {cloneReactChildrenWithProps(this.props.children, { sourceID: this.props.id, })} diff --git a/javascript/components/ShapeSource.js b/javascript/components/ShapeSource.js index 167df3a02e..509cd78844 100644 --- a/javascript/components/ShapeSource.js +++ b/javascript/components/ShapeSource.js @@ -10,6 +10,8 @@ import { isFunction, } from '../utils'; +import AbstractSource from './AbstractSource'; + const MapboxGL = NativeModules.MGLModule; export const NATIVE_MODULE_NAME = 'RCTMGLShapeSource'; @@ -18,7 +20,7 @@ export const NATIVE_MODULE_NAME = 'RCTMGLShapeSource'; * ShapeSource is a map content source that supplies vector shapes to be shown on the map. * The shape may be a url or a GeoJSON object */ -class ShapeSource extends React.Component { +class ShapeSource extends AbstractSource { static NATIVE_ASSETS_KEY = 'assets'; static propTypes = { @@ -106,6 +108,17 @@ class ShapeSource extends React.Component { id: MapboxGL.StyleSource.DefaultSourceID, }; + setNativeProps(props) { + const shallowProps = Object.assign({}, props); + + // Adds support for Animated + if (shallowProps.shape && typeof shallowProps !== 'string') { + shallowProps.shape = JSON.stringify(shallowProps.shape); + } + + super.setNativeProps(shallowProps); + } + _getShape() { if (!this.props.shape) { return; @@ -160,8 +173,9 @@ class ShapeSource extends React.Component { ...this._getImages(), onPress: undefined, }; + return ( - + {cloneReactChildrenWithProps(this.props.children, { sourceID: this.props.id, })} diff --git a/javascript/components/SymbolLayer.js b/javascript/components/SymbolLayer.js index 8fdcee4604..e3ba6e508a 100644 --- a/javascript/components/SymbolLayer.js +++ b/javascript/components/SymbolLayer.js @@ -1,4 +1,5 @@ import React from 'react'; +import {View} from 'react-native'; import PropTypes from 'prop-types'; import {NativeModules, requireNativeComponent} from 'react-native'; @@ -76,12 +77,32 @@ class SymbolLayer extends AbstractLayer { sourceID: MapboxGL.StyleSource.DefaultSourceID, }; + _shouldSnapshot() { + let isSnapshot = false; + + if (React.Children.count(this.props.children) <= 0) { + return isSnapshot; + } + + React.Children.forEach(this.props.children, child => { + if (child.type === View) { + isSnapshot = true; + } + }); + + return isSnapshot; + } + render() { const props = { ...this.baseProps, + snapshot: this._shouldSnapshot(), sourceLayerID: this.props.sourceLayerID, }; - return ; + + return ( + {this.props.children} + ); } } @@ -89,7 +110,7 @@ const RCTMGLSymbolLayer = requireNativeComponent( NATIVE_MODULE_NAME, SymbolLayer, { - nativeOnly: {reactStyle: true}, + nativeOnly: {reactStyle: true, snapshot: true}, }, ); diff --git a/javascript/components/UserLocation.js b/javascript/components/UserLocation.js new file mode 100644 index 0000000000..6409f11ebf --- /dev/null +++ b/javascript/components/UserLocation.js @@ -0,0 +1,166 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {NativeModules, requireNativeComponent} from 'react-native'; + +import {viewPropTypes} from '../utils'; +import locationManager from '../modules/location/locationManager'; + +import Annotation from './annotations/Annotation'; +import CircleLayer from './CircleLayer'; + +const mapboxBlue = 'rgba(51, 181, 229, 100)'; + +const layerStyles = { + normal: { + pluse: { + circleRadius: 15, + circleColor: mapboxBlue, + circleOpacity: 0.2, + circlePitchAlignment: 'map', + }, + background: { + circleRadius: 9, + circleColor: '#fff', + circlePitchAlignment: 'map', + }, + foreground: { + circleRadius: 6, + circleColor: mapboxBlue, + circlePitchAlignment: 'map', + }, + }, +}; + +const normalIcon = [ + , + , + , +]; + +const compassIcon = null; +const navigationIcon = null; + +class UserLocation extends React.Component { + static propTypes = { + animated: PropTypes.bool, + + renderMode: PropTypes.oneOf(['normal', 'compass', 'navigation', 'custom']), + + visible: PropTypes.bool, + + onPress: PropTypes.func, + onUpdate: PropTypes.func, + }; + + static defaultProps = { + animated: true, + visible: true, + renderMode: 'normal', + }; + + static RenderMode = { + Normal: 'normal', + Compass: 'compass', + Navigation: 'navigation', + Custom: 'custom', + }; + + static TrackingMode = { + None: 'none', + Follow: 'follow', + FollowWithHeading: 'followWithHeading', + FollowWithCourse: 'followWithCourse', + }; + + constructor(props) { + super(props); + + this.state = { + shouldShowUserLocation: false, + coordinates: null, + }; + + this._onLocationUpdate = this._onLocationUpdate.bind(this); + } + + async componentDidMount() { + const lastKnownLocation = await locationManager.getLastKnownLocation(); + + if (lastKnownLocation) { + this.setState({ + coordinates: this._getCoordinatesFromLocation(lastKnownLocation), + }); + } + + locationManager.addListener(this._onLocationUpdate); + } + + componentWillUnmount() { + locationManager.removeListener(this._onLocationUpdate); + } + + _onLocationUpdate(location) { + this.setState({ + coordinates: this._getCoordinatesFromLocation(location), + }); + + if (this.props.onUpdate) { + this.props.onUpdate(location); + } + } + + _getCoordinatesFromLocation(location) { + if (!location || !location.coords) { + return; + } + return [location.coords.longitude, location.coords.latitude]; + } + + get userIconLayers() { + switch (this.props.renderMode) { + case UserLocation.RenderMode.Normal: + return normalIcon; + case UserLocation.RenderMode.Compass: + return compassIcon; + case UserLocation.RenderMode.Navigation: + return navigationIcon; + default: + return this.props.children; + } + } + + render() { + if (!this.props.visible || !this.state.coordinates) { + return null; + } + + const children = this.props.children + ? this.props.children + : this.userIconLayers; + return ( + + {children} + + ); + } +} + +export default UserLocation; diff --git a/javascript/components/VectorSource.js b/javascript/components/VectorSource.js index dced568db1..65c999ad3e 100644 --- a/javascript/components/VectorSource.js +++ b/javascript/components/VectorSource.js @@ -4,6 +4,8 @@ import {NativeModules, requireNativeComponent} from 'react-native'; import {cloneReactChildrenWithProps, viewPropTypes, isFunction} from '../utils'; +import AbstractSource from './AbstractSource'; + const MapboxGL = NativeModules.MGLModule; export const NATIVE_MODULE_NAME = 'RCTMGLVectorSource'; @@ -12,7 +14,7 @@ export const NATIVE_MODULE_NAME = 'RCTMGLVectorSource'; * VectorSource is a map content source that supplies tiled vector data in Mapbox Vector Tile format to be shown on the map. * The location of and metadata about the tiles are defined either by an option dictionary or by an external file that conforms to the TileJSON specification. */ -class VectorSource extends React.Component { +class VectorSource extends AbstractSource { static propTypes = { ...viewPropTypes, @@ -55,7 +57,7 @@ class VectorSource extends React.Component { onPress: undefined, }; return ( - + {cloneReactChildrenWithProps(this.props.children, { sourceID: this.props.id, })} diff --git a/javascript/components/annotations/Annotation.js b/javascript/components/annotations/Annotation.js new file mode 100644 index 0000000000..ac2ee91790 --- /dev/null +++ b/javascript/components/annotations/Annotation.js @@ -0,0 +1,126 @@ +import React from 'react'; +import {Easing} from 'react-native'; +import PropTypes from 'prop-types'; + +import MapboxGL from '../../index'; +import AnimatedPoint from '../../utils/AnimatedPoint'; + +class Annotation extends React.Component { + static propTypes = { + id: PropTypes.string.isRequired, + + animated: PropTypes.bool, + + animationDuration: PropTypes.number, + + animationEasingFunction: PropTypes.func, + + coordinates: PropTypes.arrayOf(PropTypes.number), + + onPress: PropTypes.func, + + icon: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.object, + ]), + }; + + static defaultProps = { + animated: false, + animationDuration: 1000, + animationEasingFunction: Easing.linear, + }; + + constructor(props) { + super(props); + + const shape = this._getShapeFromProps(props); + + this.state = { + shape: props.animated ? new AnimatedPoint(shape) : shape, + }; + + this.onPress = this.onPress.bind(this); + } + + componentDidUpdate(prevProps) { + if (!Array.isArray(this.props.coordinates)) { + this.setState({shape: null}); + return; + } + + const hasCoordChanged = + prevProps.coordinates[0] !== this.props.coordinates[0] || + prevProps.coordinates[1] !== this.props.coordinates[1]; + + if (!hasCoordChanged) { + return; + } + + if (this.props.animated && this.state.shape) { + // flush current animations + this.state.shape.stopAnimation(); + + this.state.shape + .timing({ + coordinates: this.props.coordinates, + easing: this.props.animationEasingFunction, + duration: this.props.animationDuration, + }) + .start(); + } else if (!this.state.shape || !this.props.animated) { + const shape = this._getShapeFromProps(this.props); + + this.setState({ + shape: this.props.animated ? new AnimatedPoint(shape) : shape, + }); + } + } + + onPress() { + if (this.props.onPress) { + this.props.onPress(); + } + } + + _getShapeFromProps(props = {}) { + const lng = props.coordinates[0] || 0; + const lat = props.coordinates[1] || 0; + return {type: 'Point', coordinates: [lng, lat]}; + } + + get symbolStyle() { + if (!this.props.icon) { + return; + } + return Object.assign({}, this.props.style, { + iconImage: this.props.icon, + }); + } + + render() { + if (!this.props.coordinates) { + return null; + } + + return ( + + {this.symbolStyle && ( + + )} + {this.props.children} + + ); + } +} + +export default Annotation; diff --git a/javascript/index.js b/javascript/index.js index 0475bfc15f..65698ce2c8 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -4,11 +4,14 @@ import {isAndroid} from './utils'; import * as geoUtils from './utils/geoUtils'; // Components import MapView from './components/MapView'; -import MapboxStyleSheet from './utils/MapboxStyleSheet'; import Light from './components/Light'; import PointAnnotation from './components/PointAnnotation'; +import Annotation from './components/annotations/Annotation'; import Callout from './components/Callout'; -// Sources +import UserLocation from './components/UserLocation'; +import Camera from './components/Camera'; + +// sources import VectorSource from './components/VectorSource'; import ShapeSource from './components/ShapeSource'; import RasterSource from './components/RasterSource'; @@ -21,10 +24,15 @@ import CircleLayer from './components/CircleLayer'; import SymbolLayer from './components/SymbolLayer'; import RasterLayer from './components/RasterLayer'; import BackgroundLayer from './components/BackgroundLayer'; -// Modules + +// modules +import locationManager from './modules/location/locationManager'; import offlineManager from './modules/offline/offlineManager'; import snapshotManager from './modules/snapshot/snapshotManager'; +// helpers +import AnimatedPoint from './utils/AnimatedPoint'; + const MapboxGL = {...NativeModules.MGLModule}; // static methods @@ -54,10 +62,14 @@ MapboxGL.requestAndroidLocationPermissions = async function() { // components MapboxGL.MapView = MapView; -MapboxGL.StyleSheet = MapboxStyleSheet; MapboxGL.Light = Light; MapboxGL.PointAnnotation = PointAnnotation; MapboxGL.Callout = Callout; +MapboxGL.UserLocation = UserLocation; +MapboxGL.Camera = Camera; + +// annotations +MapboxGL.Annotation = Annotation; // sources MapboxGL.VectorSource = VectorSource; @@ -75,11 +87,13 @@ MapboxGL.RasterLayer = RasterLayer; MapboxGL.BackgroundLayer = BackgroundLayer; // modules +MapboxGL.locationManager = locationManager; MapboxGL.offlineManager = offlineManager; MapboxGL.snapshotManager = snapshotManager; // utils MapboxGL.geoUtils = geoUtils; +MapboxGL.AnimatedPoint = AnimatedPoint; // animated MapboxGL.Animated = { diff --git a/javascript/modules/location/locationManager.js b/javascript/modules/location/locationManager.js new file mode 100644 index 0000000000..b5aec87b53 --- /dev/null +++ b/javascript/modules/location/locationManager.js @@ -0,0 +1,95 @@ +import {NativeModules, NativeEventEmitter} from 'react-native'; + +const MapboxGL = NativeModules.MGLModule; +const MapboxGLLocationManager = NativeModules.MGLLocationModule; + +export const LocationModuleEventEmitter = new NativeEventEmitter( + MapboxGLLocationManager, +); + +class LocationManager { + constructor() { + this._listeners = []; + this._lastKnownLocation = null; + this._isListening = false; + this._isPaused = false; + this.onUpdate = this.onUpdate.bind(this); + } + + async getLastKnownLocation() { + if (!this._lastKnownLocation) { + const lastKnownLocation = await MapboxGLLocationManager.getLastKnownLocation(); + if (!this._lastKnownLocation && lastKnownLocation) { + this._lastKnownLocation = lastKnownLocation; + } + } + return this._lastKnownLocation; + } + + addListener(listener) { + if (!this._listeners.includes(listener)) { + this._listeners.push(listener); + + if (this._lastKnownLocation) { + listener(this._lastKnownLocation); + } + } + } + + removeListener(listener) { + this._listeners = this._listeners.filter(l => l !== listener); + } + + removeAllListeners() { + this._listeners = []; + } + + start() { + if (this._isPaused) { + MapboxGLLocationManager.start(); + this._isPaused = false; + return; + } + + if (!this._isListening) { + MapboxGLLocationManager.start(); + + LocationModuleEventEmitter.addListener( + MapboxGL.LocationCallbackName.Update, + this.onUpdate, + ); + + this._isListening = true; + } + } + + pause() { + if (!this._isPaused && this._isListening) { + MapboxGLLocationManager.pause(); + this._isListening = false; + } + } + + dispose() { + MapboxGLLocationManager.stop(); + + if (this._isListening) { + LocationModuleEventEmitter.removeListener( + MapboxGL.LocationCallbackName.Update, + this.onUpdate, + ); + } + + this._isListening = false; + } + + onUpdate(location) { + this._lastKnownLocation = location; + + for (const listener of this._listeners) { + listener(location); + } + } +} + +export default new LocationManager(); diff --git a/javascript/utils/AnimatedPoint.js b/javascript/utils/AnimatedPoint.js new file mode 100644 index 0000000000..12cb010def --- /dev/null +++ b/javascript/utils/AnimatedPoint.js @@ -0,0 +1,121 @@ +import {Animated} from 'react-native'; + +// Used react-native-maps as a reference +// https://github.com/react-community/react-native-maps/blob/master/lib/components/AnimatedRegion.js +const AnimatedWithChildren = Object.getPrototypeOf(Animated.ValueXY); + +const DEFAULT_COORD = [0, 0]; +const DEFAULT_POINT = {type: 'Point', coordinates: DEFAULT_COORD}; + +let uniqueID = 0; + +export class AnimatedPoint extends AnimatedWithChildren { + constructor(point = DEFAULT_POINT) { + super(); + + this.longitude = point.coordinates[0] || 0; + this.latitude = point.coordinates[1] || 0; + + if (!(this.longitude instanceof Animated.Value)) { + this.longitude = new Animated.Value(this.longitude); + } + + if (!(this.latitude instanceof Animated.Value)) { + this.latitude = new Animated.Value(this.latitude); + } + + this._listeners = {}; + } + + setValue(coordinates = DEFAULT_POINT) { + this.longitude._value = point.coordinates[0]; + this.latitude._value = point.coordinates[1]; + } + + setOffset(coordinates = DEFAULT_POINT) { + this.longitude.setOffset(point.coordinates[0]); + this.latitude.setOffset(point.coordinates[1]); + } + + flattenOffset() { + this.longitude.flattenOffset(); + this.latitude.flattenOffset(); + } + + stopAnimation(cb) { + this.longitude.stopAnimation(); + this.latitude.stopAnimation(); + + if (typeof cb === 'function') { + cb(this.__getValue()); + } + } + + addListener(cb) { + const id = `${String(uniqueID++)}-${String(Date.now())}`; + + const completeCB = () => { + if (typeof cb === 'function') { + cb(this.__getValue()); + } + }; + + this._listeners[id] = { + longitude: this.longitude.addListener(completeCB), + latitude: this.latitude.addListener(completeCB), + }; + + return id; + } + + removeListener(id) { + this.longitude.removeListener(this._listeners[id].longitude); + this.latitude.removeListener(this._listeners[id].latitude); + delete this._listeners[id]; + } + + spring(config = {coordinates: DEFAULT_COORD}) { + return Animated.parallel([ + Animated.spring(this.longitude, { + ...config, + toValue: config.coordinates[0], + }), + Animated.spring(this.latitude, { + ...config, + toValue: config.coordinates[1], + }), + ]); + } + + timing(config = {coordinates: DEFAULT_COORD}) { + return Animated.parallel([ + Animated.timing(this.longitude, { + ...config, + toValue: config.coordinates[0], + }), + Animated.timing(this.latitude, { + ...config, + toValue: config.coordinates[1], + }), + ]); + } + + __getValue() { + return { + type: 'Point', + coordinates: [this.longitude.__getValue(), this.latitude.__getValue()], + }; + } + + __attach() { + this.longitude.__addChild(this); + this.latitude.__addChild(this); + } + + __detach() { + this.longitude.__removeChild(this); + this.latitude.__removeChild(this); + } +} + +export default AnimatedPoint; diff --git a/javascript/utils/BridgeValue.js b/javascript/utils/BridgeValue.js index d109f66e05..3272d960ad 100644 --- a/javascript/utils/BridgeValue.js +++ b/javascript/utils/BridgeValue.js @@ -29,7 +29,9 @@ export default class BridgeValue { if (this.rawValue && typeof this.rawValue === 'object') { return Types.HashMap; } - throw new Error('[type] BridgeValue must be a primitive/array/object'); + throw new Error( + `[type - ${this.rawValue}] BridgeValue must be a primitive/array/object`, + ); } get value() { @@ -61,16 +63,21 @@ export default class BridgeValue { ) { value = this.rawValue; } else { - throw new Error('[value] BridgeValue must be a primitive/array/object'); + throw new Error( + `[value - ${ + this.rawValue + }] BridgeValue must be a primitive/array/object`, + ); } return value; } - toJSON() { + toJSON(formatter) { return { type: this.type, - value: this.value, + value: + typeof formatter === 'function' ? formatter(this.value) : this.value, }; } } diff --git a/javascript/utils/MapboxStyleSheet.js b/javascript/utils/MapboxStyleSheet.js deleted file mode 100644 index 87e36147ba..0000000000 --- a/javascript/utils/MapboxStyleSheet.js +++ /dev/null @@ -1,316 +0,0 @@ -// @flow - -import {processColor, NativeModules} from 'react-native'; -import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; - -import styleMap, { - StyleTypes, - StyleFunctionTypes, - styleExtras, -} from './styleMap'; -import BridgeValue from './BridgeValue'; - -import {isUndefined, isPrimitive, isString} from './index'; - -const MapboxGL = NativeModules.MGLModule; - -class MapStyleItem { - constructor(type, payload) { - this.type = type; - this.payload = payload; - } - - toJSON(shouldMarkAsStyle) { - const json = { - styletype: this.type, - payload: this.payload, - __MAPBOX_STYLE__: true, - }; - - if (!shouldMarkAsStyle) { - delete json.__MAPBOX_STYLE__; - } - - return json; - } -} - -class MapStyleTransitionItem extends MapStyleItem { - constructor(duration = 0, delay = 0, extras = {}) { - super(StyleTypes.Transition, { - value: { - duration, - delay, - }, - ...extras, - }); - } -} - -class MapStyleTranslationItem extends MapStyleItem { - constructor(x, y, extras = {}) { - super(StyleTypes.Translation, { - value: [x, y], - ...extras, - }); - } -} - -class MapStyleConstantItem extends MapStyleItem { - constructor(prop, value, extras = {}) { - super(StyleTypes.Constant, { - value: resolveStyleValue(prop, value), - ...extras, - }); - } -} - -class MapStyleColorItem extends MapStyleItem { - constructor(value, extras = {}) { - super(StyleTypes.Color, {value, ...extras}); - } -} - -class MapStyleFunctionItem extends MapStyleItem { - constructor(fn, mode = MapboxGL.InterpolationMode.Exponential, payload) { - super(StyleTypes.Function, { - fn, - mode, - stops: [], - attributeName: payload.attributeName, - }); - - this._rawStops = payload.stops; - } - - processStops(prop) { - const stops = []; - - const isComposite = this.payload.fn === StyleFunctionTypes.Composite; - for (const rawStop of this._rawStops) { - const [stopKey, stopValue] = rawStop; - - if (isComposite) { - stops.push([ - stopKey, - makeStyleValue( - prop, - stopValue[1], - {propertyValue: stopValue[0]}, - false, - ), - ]); - } else { - stops.push([stopKey, makeStyleValue(prop, stopValue, null, false)]); - } - } - - this.payload.stops = stops; - } -} - -const STYLE_MAP = {}; -Object.keys(MapboxGL).forEach(key => { - if ( - !['setAccessToken', 'getAccessToken', 'setTelemetryEnabled'].includes(key) - ) { - STYLE_MAP[key.toLowerCase()] = MapboxGL[key]; - } -}); - -function resolveStyleValue(styleProp, styleValue) { - // style is not an enum value - if (!STYLE_MAP[styleProp.toLowerCase()] || !isString(styleValue)) { - return styleValue; - } - - // can't find enum values abort abort - const valueMap = STYLE_MAP[styleProp.toLowerCase()]; - if (!valueMap) { - return styleValue; - } - - // find enum value that matches - const enumKeys = Object.keys(valueMap); - for (const enumKey of enumKeys) { - if (enumKey.toLowerCase() === styleValue.toLowerCase()) { - return valueMap[enumKey]; - } - } - - return styleValue; -} - -function resolveImage(imageURL) { - let resolved = imageURL; - - if (typeof imageURL === 'number') { - // required from JS, local file resolve it's asset filepath - const res = resolveAssetSource(imageURL) || {}; - - // we found a local uri - if (res.uri) { - resolved = res.uri; - } - } - - return resolved; -} - -function makeStyleValue(prop, value, extras = {}, shouldMarkAsStyle = true) { - let item; - - // search for any extras - const extraData = Object.assign({}, styleExtras[prop], extras); - - // eslint-disable-next-line no-use-before-define - if (MapboxStyleSheet.isFunctionStyleItem(value)) { - item = value; - item.processStops(prop); - } else if (styleMap[prop] === StyleTypes.Transition) { - item = new MapStyleTransitionItem(value.duration, value.delay, extraData); - } else if (styleMap[prop] === StyleTypes.Color) { - item = new MapStyleColorItem(processColor(value), extraData); - } else if (styleMap[prop] === StyleTypes.Translation) { - if (Array.isArray(value)) { - item = new MapStyleTranslationItem(value[0], value[1], extraData); - } else { - item = new MapStyleTranslationItem(value.x, value.y, extraData); // supports object based API - } - } else if (styleMap[prop] === StyleTypes.Image) { - item = new MapStyleConstantItem(prop, resolveImage(value), { - image: true, - shouldAddImage: typeof value === 'number', // required from JS, tell native code to add image to map style - ...extraData, - }); - } else { - item = new MapStyleConstantItem(prop, value, extraData); - } - - return item.toJSON(shouldMarkAsStyle); -} - -class MapboxStyleSheet { - static create(userStyles, depth = 0) { - const styleProps = Object.keys(userStyles); - const style = {}; - - for (const styleProp of styleProps) { - const userStyle = userStyles[styleProp]; - - if (MapboxStyleSheet.isStyleItem(userStyle)) { - style[styleProp] = userStyle; - continue; - } - - if (!styleMap[styleProp] && depth === 0 && !isPrimitive(userStyle)) { - style[styleProp] = MapboxStyleSheet.create(userStyle, depth + 1); - continue; - } else if ( - !styleMap[styleProp] || - isUndefined(userStyle) || - userStyle === null - ) { - throw new Error(`Invalid Mapbox Style ${styleProp}`); - } - - style[styleProp] = makeStyleValue(styleProp, userStyle); - } - - return style; - } - - static camera(stops, mode) { - const stopNativeArray = []; - const cameraZoomLevels = Object.keys(stops); - - for (const cameraZoomLevel of cameraZoomLevels) { - const keyBridgeValue = new BridgeValue(cameraZoomLevel | 0); - - stopNativeArray.push([keyBridgeValue.toJSON(), stops[cameraZoomLevel]]); - } - - return new MapStyleFunctionItem(StyleFunctionTypes.Camera, (mode: mode), { - stops: stopNativeArray, - }); - } - - static source(stops, attributeName, mode) { - const stopNativeArray = []; - - if (Array.isArray(stops)) { - for (const stop of stops) { - const keyBridgeValue = new BridgeValue(stop[0]); - - stopNativeArray.push([keyBridgeValue.toJSON(), stop[1]]); - } - } else if (stops) { - const stopKeys = Object.keys(stops); - for (const stopKey of stopKeys) { - const keyBridgeValue = new BridgeValue(stopKey); - - stopNativeArray.push([keyBridgeValue.toJSON(), stops[stopKey]]); - } - } - - return new MapStyleFunctionItem(StyleFunctionTypes.Source, (mode: mode), { - stops: stopNativeArray, - attributeName, - }); - } - - static composite(stops, attributeName, mode) { - const stopNativeArray = []; - - if (Array.isArray(stops)) { - for (const zoomPropertyStop of stops) { - const propValue = zoomPropertyStop[0].value; - const styleValue = zoomPropertyStop[1]; - - stopNativeArray.push([ - new BridgeValue(zoomPropertyStop[0].zoom).toJSON(), - [propValue, styleValue], - ]); - } - } else { - const cameraZoomLevels = Object.keys(stops); - - for (const cameraZoomLevel of cameraZoomLevels) { - const [propName, styleValue] = stops[cameraZoomLevel]; - const keyBridgeValue = new BridgeValue(cameraZoomLevel | 0); - - stopNativeArray.push([keyBridgeValue.toJSON(), [propName, styleValue]]); - } - } - - return new MapStyleFunctionItem( - StyleFunctionTypes.Composite, - (mode: mode), - {stops: stopNativeArray, attributeName}, - ); - } - - static isStyleItem(item) { - return typeof item === 'object' && item.__MAPBOX_STYLE__ === true; - } - - static isFunctionStyleItem(item) { - if (item instanceof MapStyleFunctionItem) { - return true; - } - const isStyleItem = MapboxStyleSheet.isStyleItem(item); - return isStyleItem && item.type === StyleTypes.Function; - } - - // helpers - - static identity(attrName) { - return MapboxStyleSheet.source( - null, - attrName, - MapboxGL.InterpolationMode.Identity, - ); - } -} - -export default MapboxStyleSheet; diff --git a/javascript/utils/geoUtils.js b/javascript/utils/geoUtils.js index da9ca75d53..94712650ba 100644 --- a/javascript/utils/geoUtils.js +++ b/javascript/utils/geoUtils.js @@ -1,9 +1,18 @@ import turfHelpers from '@turf/helpers'; +import distance from '@turf/distance'; +import along from '@turf/along'; +import geoViewport from '@mapbox/geo-viewport'; + +const VECTOR_TILE_SIZE = 512; export function makePoint(coordinates, properties) { return turfHelpers.point(coordinates, properties); } +export function makeLineString(coordinates, properties) { + return turfHelpers.lineString(coordinates, properties); +} + export function makeLatLngBounds(northEastCoordinates, southWestCoordinates) { return turfHelpers.featureCollection([ turfHelpers.point(northEastCoordinates), @@ -24,3 +33,40 @@ export function addToFeatureCollection(featureCollection, feature) { shallowFeatureCollection.features.push(feature); return featureCollection; } + +export function calculateDistance(origin, dest) { + return distance(origin, dest); +} + +export function pointAlongLine(lineString, distAlong) { + return along(lineString, distAlong); +} + +export function getOrCalculateVisibleRegion( + coord, + zoomLevel, + width, + height, + nativeRegion, +) { + const region = { + ne: [0, 0], + sw: [0, 0], + }; + + if (!nativeRegion || !Array.isArray(nativeRegion.visibleBounds)) { + const bounds = geoViewport.bounds( + coord, + zoomLevel, + [width, height], + VECTOR_TILE_SIZE, + ); + region.ne = [bounds[3], bounds[2]]; + region.sw = [bounds[1], bounds[0]]; + } else { + region.ne = nativeRegion.properties.visibleBounds[0]; + region.sw = nativeRegion.properties.visibleBounds[1]; + } + + return region; +} diff --git a/javascript/utils/index.js b/javascript/utils/index.js index beda5cb510..4a9866e07c 100644 --- a/javascript/utils/index.js +++ b/javascript/utils/index.js @@ -26,6 +26,13 @@ export function isAndroid() { return Platform.OS === 'android'; } +export function existenceChange(cur, next) { + if (!cur && !next) { + return false; + } + return (!cur && next) || (cur && !next); +} + export function isFunction(fn) { return typeof fn === 'function'; } diff --git a/javascript/utils/styleMap.js b/javascript/utils/styleMap.js index fc6f6fef4f..4256bddf3f 100644 --- a/javascript/utils/styleMap.js +++ b/javascript/utils/styleMap.js @@ -3,6 +3,7 @@ // THIS FILE IS AUTOGENERATED import PropTypes from 'prop-types'; +import { IS_ANDROID } from './index'; export const StyleTypes = { Constant: 'constant', @@ -13,41 +14,17 @@ export const StyleTypes = { Image: 'image', }; -export const StyleFunctionTypes = { - Camera: 'camera', - Source: 'source', - Composite: 'composite', -}; - -export const ConstantPropType = PropTypes.shape({ - styletype: PropTypes.string.isRequired, - payload: PropTypes.shape({ - value: PropTypes.any.isRequired, - }).isRequired, -}); +export function getStyleType(styleProp) { + if (!IS_ANDROID && styleExtras[styleProp]) { + return styleExtras[styleProp].iosType; + } -export const StyleFunctionPropType = PropTypes.shape({ - styletype: PropTypes.string.isRequired, - payload: PropTypes.shape({ - fn: PropTypes.string, - attributeName: PropTypes.string, - stops: PropTypes.array, - mode: PropTypes.any, - }).isRequired, -}); - -export const TransitionPropType = PropTypes.shape({ - duration: PropTypes.number, - delay: PropTypes.number, -}); + if (styleMap[styleProp]) { + return styleMap[styleProp]; + } -export const TranslationPropType = PropTypes.oneOfType([ - PropTypes.shape({ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - PropTypes.arrayOf(PropTypes.number), -]); + throw new Error(`${styleProp} is not a valid Mapbox layer style`); +} export const FillLayerStyleProp = PropTypes.shape({ @@ -61,8 +38,7 @@ export const FillLayerStyleProp = PropTypes.shape({ */ fillAntialias: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -70,14 +46,16 @@ export const FillLayerStyleProp = PropTypes.shape({ */ fillOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillOpacity property. */ - fillOpacityTransition: TransitionPropType, + fillOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used. @@ -86,14 +64,16 @@ export const FillLayerStyleProp = PropTypes.shape({ */ fillColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillColor property. */ - fillColorTransition: TransitionPropType, + fillColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The outline color of the fill. Matches the value of `fillColor` if unspecified. @@ -102,27 +82,32 @@ export const FillLayerStyleProp = PropTypes.shape({ */ fillOutlineColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillOutlineColor property. */ - fillOutlineColorTransition: TransitionPropType, + fillOutlineColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. */ fillTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillTranslate property. */ - fillTranslateTransition: TransitionPropType, + fillTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `fillTranslate`. @@ -130,24 +115,26 @@ export const FillLayerStyleProp = PropTypes.shape({ * @requires fillTranslate */ fillTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** - * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoomDependent expressions will be evaluated only at integer zoom levels. */ fillPattern: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillPattern property. */ - fillPatternTransition: TransitionPropType, + fillPatternTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); export const LineLayerStyleProp = PropTypes.shape({ @@ -157,7 +144,7 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineCap: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -165,7 +152,7 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineJoin: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -173,8 +160,7 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineMiterLimit: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -182,8 +168,7 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineRoundLimit: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -196,14 +181,16 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineOpacity property. */ - lineOpacityTransition: TransitionPropType, + lineOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color with which the line will be drawn. @@ -212,27 +199,32 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineColor property. */ - lineColorTransition: TransitionPropType, + lineColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. */ lineTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineTranslate property. */ - lineTranslateTransition: TransitionPropType, + lineTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `lineTranslate`. @@ -240,8 +232,8 @@ export const LineLayerStyleProp = PropTypes.shape({ * @requires lineTranslate */ lineTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** @@ -249,87 +241,99 @@ export const LineLayerStyleProp = PropTypes.shape({ */ lineWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineWidth property. */ - lineWidthTransition: TransitionPropType, + lineWidthTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap. */ lineGapWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineGapWidth property. */ - lineGapWidthTransition: TransitionPropType, + lineGapWidthTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset. */ lineOffset: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineOffset property. */ - lineOffsetTransition: TransitionPropType, + lineOffsetTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Blur applied to the line, in pixels. */ lineBlur: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineBlur property. */ - lineBlurTransition: TransitionPropType, + lineBlurTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** - * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width. + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoomDependent expressions will be evaluated only at integer zoom levels. * * @disabledBy linePattern */ lineDasharray: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s lineDasharray property. */ - lineDasharrayTransition: TransitionPropType, + lineDasharrayTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** - * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoomDependent expressions will be evaluated only at integer zoom levels. */ linePattern: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s linePattern property. */ - linePatternTransition: TransitionPropType, + linePatternTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); export const SymbolLayerStyleProp = PropTypes.shape({ @@ -339,7 +343,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ symbolPlacement: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -347,8 +351,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ symbolSpacing: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -356,8 +359,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ symbolAvoidEdges: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -367,8 +369,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconAllowOverlap: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -378,8 +379,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconIgnorePlacement: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -389,8 +389,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconOptional: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -400,7 +399,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconRotationAlignment: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -410,8 +409,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconSize: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -421,7 +419,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconTextFit: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -431,18 +429,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconTextFitPadding: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** - * Name of image in sprite to use for drawing an image background. A string with `{tokens}` replaced, referencing the data property to pull from. (`{token}` replacement is only supported for literal `iconImage` values; not for property functions.) + * Name of image in sprite to use for drawing an image background. */ iconImage: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -452,8 +448,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconRotate: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -463,8 +458,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconPadding: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -474,19 +468,17 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconKeepUpright: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** - * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. When combined with `iconRotate` the offset will be as if the rotated direction was up. + * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. Each component is multiplied by the value of `iconSize` to obtain the final offset in pixels. When combined with `iconRotate` the offset will be as if the rotated direction was up. * * @requires iconImage */ iconOffset: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -496,7 +488,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconAnchor: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -506,7 +498,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconPitchAlignment: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -516,7 +508,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textPitchAlignment: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -526,16 +518,15 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textRotationAlignment: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** - * Value to use for a text label. Feature properties are specified using tokens like `{field_name}`. (`{token}` replacement is only supported for literal `textField` values; not for property functions.) + * Value to use for a text label. */ textField: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -546,8 +537,6 @@ export const SymbolLayerStyleProp = PropTypes.shape({ textFont: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.string), PropTypes.array, - ConstantPropType, - StyleFunctionPropType, ]), /** @@ -557,8 +546,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textSize: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -568,8 +556,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textMaxWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -579,8 +566,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textLineHeight: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -590,8 +576,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textLetterSpacing: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -601,7 +586,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textJustify: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -611,7 +596,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textAnchor: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -621,8 +606,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textMaxAngle: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -632,8 +616,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textRotate: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -643,8 +626,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textPadding: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -654,8 +636,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textKeepUpright: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -665,7 +646,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textTransform: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -675,8 +656,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textOffset: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -686,8 +666,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textAllowOverlap: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -697,8 +676,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textIgnorePlacement: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -708,8 +686,7 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textOptional: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -724,14 +701,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconOpacity property. */ - iconOpacityTransition: TransitionPropType, + iconOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color of the icon. This can only be used with sdf icons. @@ -740,14 +719,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconColor property. */ - iconColorTransition: TransitionPropType, + iconColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color of the icon's halo. Icon halos can only be used with SDF icons. @@ -756,14 +737,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconHaloColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconHaloColor property. */ - iconHaloColorTransition: TransitionPropType, + iconHaloColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Distance of halo to the icon outline. @@ -772,14 +755,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconHaloWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconHaloWidth property. */ - iconHaloWidthTransition: TransitionPropType, + iconHaloWidthTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Fade out the halo towards the outside. @@ -788,14 +773,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ iconHaloBlur: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconHaloBlur property. */ - iconHaloBlurTransition: TransitionPropType, + iconHaloBlurTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up. @@ -803,14 +790,17 @@ export const SymbolLayerStyleProp = PropTypes.shape({ * @requires iconImage */ iconTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s iconTranslate property. */ - iconTranslateTransition: TransitionPropType, + iconTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `iconTranslate`. @@ -818,8 +808,8 @@ export const SymbolLayerStyleProp = PropTypes.shape({ * @requires iconImage, iconTranslate */ iconTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** @@ -829,14 +819,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textOpacity property. */ - textOpacityTransition: TransitionPropType, + textOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color with which the text will be drawn. @@ -845,14 +837,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textColor property. */ - textColorTransition: TransitionPropType, + textColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The color of the text's halo, which helps it stand out from backgrounds. @@ -861,14 +855,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textHaloColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textHaloColor property. */ - textHaloColorTransition: TransitionPropType, + textHaloColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Distance of halo to the font outline. Max text halo width is 1/4 of the fontSize. @@ -877,14 +873,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textHaloWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textHaloWidth property. */ - textHaloWidthTransition: TransitionPropType, + textHaloWidthTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The halo's fadeout distance towards the outside. @@ -893,14 +891,16 @@ export const SymbolLayerStyleProp = PropTypes.shape({ */ textHaloBlur: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textHaloBlur property. */ - textHaloBlurTransition: TransitionPropType, + textHaloBlurTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up. @@ -908,14 +908,17 @@ export const SymbolLayerStyleProp = PropTypes.shape({ * @requires textField */ textTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s textTranslate property. */ - textTranslateTransition: TransitionPropType, + textTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `textTranslate`. @@ -923,8 +926,8 @@ export const SymbolLayerStyleProp = PropTypes.shape({ * @requires textField, textTranslate */ textTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), }); @@ -940,69 +943,80 @@ export const CircleLayerStyleProp = PropTypes.shape({ */ circleRadius: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleRadius property. */ - circleRadiusTransition: TransitionPropType, + circleRadiusTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The fill color of the circle. */ circleColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleColor property. */ - circleColorTransition: TransitionPropType, + circleColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. */ circleBlur: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleBlur property. */ - circleBlurTransition: TransitionPropType, + circleBlurTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The opacity at which the circle will be drawn. */ circleOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleOpacity property. */ - circleOpacityTransition: TransitionPropType, + circleOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively. */ circleTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleTranslate property. */ - circleTranslateTransition: TransitionPropType, + circleTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `circleTranslate`. @@ -1010,8 +1024,8 @@ export const CircleLayerStyleProp = PropTypes.shape({ * @requires circleTranslate */ circleTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** @@ -1019,7 +1033,7 @@ export const CircleLayerStyleProp = PropTypes.shape({ */ circlePitchScale: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -1027,7 +1041,7 @@ export const CircleLayerStyleProp = PropTypes.shape({ */ circlePitchAlignment: PropTypes.oneOfType([ PropTypes.any, - StyleFunctionPropType, + PropTypes.array, ]), /** @@ -1035,42 +1049,48 @@ export const CircleLayerStyleProp = PropTypes.shape({ */ circleStrokeWidth: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleStrokeWidth property. */ - circleStrokeWidthTransition: TransitionPropType, + circleStrokeWidthTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The stroke color of the circle. */ circleStrokeColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleStrokeColor property. */ - circleStrokeColorTransition: TransitionPropType, + circleStrokeColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The opacity of the circle's stroke. */ circleStrokeOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s circleStrokeOpacity property. */ - circleStrokeOpacityTransition: TransitionPropType, + circleStrokeOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); export const FillExtrusionLayerStyleProp = PropTypes.shape({ @@ -1085,14 +1105,16 @@ export const FillExtrusionLayerStyleProp = PropTypes.shape({ */ fillExtrusionOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionOpacity property. */ - fillExtrusionOpacityTransition: TransitionPropType, + fillExtrusionOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use `fillExtrusionOpacity` to set layer opacity. @@ -1101,27 +1123,32 @@ export const FillExtrusionLayerStyleProp = PropTypes.shape({ */ fillExtrusionColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionColor property. */ - fillExtrusionColorTransition: TransitionPropType, + fillExtrusionColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively. */ fillExtrusionTranslate: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionTranslate property. */ - fillExtrusionTranslateTransition: TransitionPropType, + fillExtrusionTranslateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Controls the frame of reference for `fillExtrusionTranslate`. @@ -1129,38 +1156,42 @@ export const FillExtrusionLayerStyleProp = PropTypes.shape({ * @requires fillExtrusionTranslate */ fillExtrusionTranslateAnchor: PropTypes.oneOfType([ - TranslationPropType, - StyleFunctionPropType, + PropTypes.arrayOf(PropTypes.number), + PropTypes.array, ]), /** - * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoomDependent expressions will be evaluated only at integer zoom levels. */ fillExtrusionPattern: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionPattern property. */ - fillExtrusionPatternTransition: TransitionPropType, + fillExtrusionPatternTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The height with which to extrude this layer. */ fillExtrusionHeight: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionHeight property. */ - fillExtrusionHeightTransition: TransitionPropType, + fillExtrusionHeightTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The height with which to extrude the base of this layer. Must be less than or equal to `fillExtrusionHeight`. @@ -1169,14 +1200,16 @@ export const FillExtrusionLayerStyleProp = PropTypes.shape({ */ fillExtrusionBase: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s fillExtrusionBase property. */ - fillExtrusionBaseTransition: TransitionPropType, + fillExtrusionBaseTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); export const RasterLayerStyleProp = PropTypes.shape({ @@ -1191,98 +1224,104 @@ export const RasterLayerStyleProp = PropTypes.shape({ */ rasterOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterOpacity property. */ - rasterOpacityTransition: TransitionPropType, + rasterOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Rotates hues around the color wheel. */ rasterHueRotate: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterHueRotate property. */ - rasterHueRotateTransition: TransitionPropType, + rasterHueRotateTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Increase or reduce the brightness of the image. The value is the minimum brightness. */ rasterBrightnessMin: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterBrightnessMin property. */ - rasterBrightnessMinTransition: TransitionPropType, + rasterBrightnessMinTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Increase or reduce the brightness of the image. The value is the maximum brightness. */ rasterBrightnessMax: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterBrightnessMax property. */ - rasterBrightnessMaxTransition: TransitionPropType, + rasterBrightnessMaxTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Increase or reduce the saturation of the image. */ rasterSaturation: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterSaturation property. */ - rasterSaturationTransition: TransitionPropType, + rasterSaturationTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Increase or reduce the contrast of the image. */ rasterContrast: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s rasterContrast property. */ - rasterContrastTransition: TransitionPropType, + rasterContrastTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Fade duration when a new tile is added. */ rasterFadeDuration: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), - - /** - * The transition affecting any changes to this layer’s rasterFadeDuration property. - */ - rasterFadeDurationTransition: TransitionPropType, }); export const BackgroundLayerStyleProp = PropTypes.shape({ @@ -1299,43 +1338,49 @@ export const BackgroundLayerStyleProp = PropTypes.shape({ */ backgroundColor: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s backgroundColor property. */ - backgroundColorTransition: TransitionPropType, + backgroundColorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** - * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoomDependent expressions will be evaluated only at integer zoom levels. */ backgroundPattern: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s backgroundPattern property. */ - backgroundPatternTransition: TransitionPropType, + backgroundPatternTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * The opacity at which the background will be drawn. */ backgroundOpacity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, - StyleFunctionPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s backgroundOpacity property. */ - backgroundOpacityTransition: TransitionPropType, + backgroundOpacityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); export const LightLayerStyleProp = PropTypes.shape({ @@ -1343,46 +1388,58 @@ export const LightLayerStyleProp = PropTypes.shape({ /** * Whether extruded geometries are lit relative to the map or viewport. */ - anchor: PropTypes.any, + anchor: PropTypes.oneOfType([ + PropTypes.any, + PropTypes.array, + ]), /** * Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below). */ position: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.number), - ConstantPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s position property. */ - positionTransition: TransitionPropType, + positionTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Color tint for lighting extruded geometries. */ color: PropTypes.oneOfType([ PropTypes.string, - ConstantPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s color property. */ - colorTransition: TransitionPropType, + colorTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), /** * Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. */ intensity: PropTypes.oneOfType([ PropTypes.number, - ConstantPropType, + PropTypes.array, ]), /** * The transition affecting any changes to this layer’s intensity property. */ - intensityTransition: TransitionPropType, + intensityTransition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), }); @@ -1534,7 +1591,6 @@ const styleMap = { rasterContrast: StyleTypes.Constant, rasterContrastTransition: StyleTypes.Transition, rasterFadeDuration: StyleTypes.Constant, - rasterFadeDurationTransition: StyleTypes.Transition, backgroundColor: StyleTypes.Color, backgroundColorTransition: StyleTypes.Transition, diff --git a/package.json b/package.json index 06b19302c6..9aabb66b29 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "url": "https://github.com/react-native-mapbox/maps" }, "scripts": { - "fetch:ios:sdk": "node ./scripts/download-mapbox-gl-native-ios-if-on-mac.js 3.7.8", + "fetch:ios:sdk": "node ./scripts/download-mapbox-gl-native-ios-if-on-mac.js 4.9.0", "fetch:style:spec": ". ./scripts/download-style-spec.sh", "generate": "node ./scripts/autogenerate", "preinstall": "npm run fetch:ios:sdk", @@ -35,6 +35,9 @@ }, "dependencies": { "@turf/helpers": "4.6.0", + "@turf/distance": ">= 4.0.0", + "@turf/along": ">= 4.0.0", + "@mapbox/geo-viewport": ">= 0.4.0", "underscore": "^1.8.3" }, "devDependencies": { diff --git a/scripts/autogenHelpers/globals.js b/scripts/autogenHelpers/globals.js index e5b580554b..389cfaa3c0 100644 --- a/scripts/autogenHelpers/globals.js +++ b/scripts/autogenHelpers/globals.js @@ -270,10 +270,9 @@ global.jsDocReactProp = function(prop) { let propTypes = []; if (prop.name.indexOf('Translate') !== -1) { - propTypes.push('TranslationPropType'); + propTypes.push('PropTypes.arrayOf(PropTypes.number)'); } else if (prop.type === 'color') { propTypes.push('PropTypes.string'); - propTypes.push('ConstantPropType'); } else if (prop.type === 'array') { switch (prop.value) { case 'number': @@ -287,10 +286,8 @@ global.jsDocReactProp = function(prop) { default: propTypes.push('PropTypes.array'); } - propTypes.push('ConstantPropType'); } else if (prop.type === 'number') { propTypes.push('PropTypes.number'); - propTypes.push('ConstantPropType'); } else if (prop.type === 'enum') { propTypes.push('PropTypes.any'); } else { @@ -302,11 +299,10 @@ global.jsDocReactProp = function(prop) { propTypes.push('PropTypes.number'); } propTypes.push('PropTypes.string'); - propTypes.push('ConstantPropType'); } - if (prop.allowedFunctionTypes && prop.allowedFunctionTypes.length) { - propTypes.push('StyleFunctionPropType'); + if (prop.expressionSupported && !propTypes.includes('PropTypes.array')) { + propTypes.push('PropTypes.array'); } if (propTypes.length > 1) { diff --git a/scripts/autogenerate.js b/scripts/autogenerate.js index 4bfa8cb88e..8a4a7ed401 100644 --- a/scripts/autogenerate.js +++ b/scripts/autogenerate.js @@ -164,6 +164,8 @@ function buildProperties(attributes, attrName) { image: isImage(attrName), translate: isTranslate(attrName), transition: attributes[attrName].transition, + expression: attributes[attrName].expression, + expressionSupported: Object.keys(attributes[attrName].expression || {}).length > 0, support: getAttributeSupport(attributes[attrName]['sdk-support']), allowedFunctionTypes: getAllowedFunctionTypes(attributes[attrName]), }; diff --git a/scripts/templates/RCTMGLStyle.m.ejs b/scripts/templates/RCTMGLStyle.m.ejs index 626bb3c006..2e8fe4a511 100644 --- a/scripts/templates/RCTMGLStyle.m.ejs +++ b/scripts/templates/RCTMGLStyle.m.ejs @@ -36,13 +36,15 @@ <% for (let i = 0; i < layer.properties.length; i++) { -%> <%- ifOrElseIf(i) -%> ([prop isEqualToString:@"<%= layer.properties[i].name %>"]) { <%_ if (layer.properties[i].image) { _%> - if (![styleValue.payload[@"shouldAddImage"] boolValue]) { + if (![styleValue shouldAddImage]) { [self set<%- iosPropMethodName(layer, pascelCase(layer.properties[i].name)) -%>:layer withReactStyleValue:styleValue]; } else { - [RCTMGLUtils fetchImage:_bridge url:styleValue.payload[@"value"] callback:^(NSError *error, UIImage *image) { + NSString *imageURI = [styleValue getImageURI]; + + [RCTMGLUtils fetchImage:_bridge url:imageURI callback:^(NSError *error, UIImage *image) { if (image != nil) { dispatch_async(dispatch_get_main_queue(), ^{ - [_style setImage:image forName:styleValue.payload[@"value"]]; + [_style setImage:image forName:imageURI]; [self set<%- iosPropMethodName(layer, pascelCase(layer.properties[i].name)) -%>:layer withReactStyleValue:styleValue]; }); } @@ -67,13 +69,6 @@ <% for (const prop of layer.properties) {%> - (void)set<%- iosPropMethodName(layer, pascelCase(prop.name)) -%>:(<%- getLayerType(layer, 'ios') -%> *)layer withReactStyleValue:(RCTMGLStyleValue *)styleValue { - <%_ if (prop.allowedFunctionTypes.length) { _%> - NSArray *allowedFunctionTypes = <%- iosStringArrayLiteral(prop.allowedFunctionTypes) -%>; - if ([styleValue isFunction] && ![styleValue isFunctionTypeSupported:allowedFunctionTypes]) { - // TODO throw execpetion - return; - } - <%_ } _%> <%_ if (layer.name === 'light' && prop.name === 'position') { _%> layer.position = [styleValue getSphericalPosition]; <%_ } else if (prop.name === 'visibility') { _%> diff --git a/scripts/templates/RCTMGLStyleFactory.java.ejs b/scripts/templates/RCTMGLStyleFactory.java.ejs index ae0ab31650..039223756b 100644 --- a/scripts/templates/RCTMGLStyleFactory.java.ejs +++ b/scripts/templates/RCTMGLStyleFactory.java.ejs @@ -65,37 +65,19 @@ public class RCTMGLStyleFactory { <%_ for (const layer of layers) { _%> <%_ for (const prop of layer.properties) { _%> public static void set<%- pascelCase(prop.name) -%>(<%- getLayerType(layer, 'android') -%> layer, RCTMGLStyleValue styleValue) { - <%_ if (prop.allowedFunctionTypes.length > 0) { _%> - if (styleValue.isFunction()) { - RCTMGLStyleFunctionParser<<%- androidInputType(prop.type, prop.value) -%>, <%- androidOutputType(prop.type, prop.value) -%>> parser = new RCTMGLStyleFunctionParser<<%- androidInputType(prop.type, prop.value) -%>, <%- androidOutputType(prop.type, prop.value) -%>>(styleValue) { - @Override - protected <%- androidInputType(prop.type, prop.value) -%> getRawStopValue(RCTMGLStyleValue styleValue) { - return <%- androidGetConfigType(androidInputType(prop.type, prop.value)) -%>; - } - - @Override - protected PropertyValue<<%- androidOutputType(prop.type, prop.value) -%>> getStopValue(<%- androidInputType(prop.type, prop.value) -%> value) { - return PropertyFactory.<%= prop.name %>(value); - } - }; - - <%_ if (prop.allowedFunctionTypes.length === 1) { _%> - layer.setProperties(PropertyFactory.<%= prop.name %>(styleValue.makeCameraFunction(styleValue.getInt("mode"), parser))); - <%_ } else { _%> - layer.setProperties(PropertyFactory.<%= prop.name %>(styleValue.makeStyleFunction(parser))); - <%_ } _%> + <%_ if (layer.name === 'light' && prop.name === 'position') { _%> + Float[] values = styleValue.getFloatArray(VALUE_KEY); + layer.set<%- pascelCase(prop.name) -%>(Position.fromPosition(values[0], values[1], values[2])); + <%_ } else if (layer.name === 'light') { _%> + layer.set<%- pascelCase(prop.name) -%>(<%- androidGetConfigType(androidInputType(prop.type, prop.value)) -%>); + <%_ } else if (prop.name === 'visibility') { _%> + layer.setProperties(PropertyFactory.visibility(styleValue.getString(VALUE_KEY))); + <%_ } else { _%> + if (styleValue.isExpression()) { + layer.setProperties(PropertyFactory.<%= prop.name %>(styleValue.getExpression())); } else { layer.setProperties(PropertyFactory.<%= prop.name %>(<%- androidGetConfigType(androidInputType(prop.type, prop.value)) -%>)); } - <%_ } else { _%> - <%_ if (layer.name === 'light' && prop.name === 'position') { _%> - Float[] values = styleValue.getFloatArray(VALUE_KEY); - layer.set<%- pascelCase(prop.name) -%>(Position.fromPosition(values[0], values[1], values[2])); - <%_ } else if (layer.name === 'light') { _%> - layer.set<%- pascelCase(prop.name) -%>(<%- androidGetConfigType(androidInputType(prop.type, prop.value)) -%>); - <%_ } else { _%> - layer.setProperties(PropertyFactory.<%= prop.name %>(<%- androidGetConfigType(androidInputType(prop.type, prop.value)) -%>)); - <%_ } _%> <%_ } _%> } diff --git a/scripts/templates/styleMap.js.ejs b/scripts/templates/styleMap.js.ejs index 664e6db6c1..184f3a981f 100644 --- a/scripts/templates/styleMap.js.ejs +++ b/scripts/templates/styleMap.js.ejs @@ -6,6 +6,7 @@ // THIS FILE IS AUTOGENERATED import PropTypes from 'prop-types'; +import { IS_ANDROID } from './index'; export const StyleTypes = { Constant: 'constant', @@ -16,41 +17,17 @@ export const StyleTypes = { Image: 'image', }; -export const StyleFunctionTypes = { - Camera: 'camera', - Source: 'source', - Composite: 'composite', -}; - -export const ConstantPropType = PropTypes.shape({ - styletype: PropTypes.string.isRequired, - payload: PropTypes.shape({ - value: PropTypes.any.isRequired, - }).isRequired, -}); - -export const StyleFunctionPropType = PropTypes.shape({ - styletype: PropTypes.string.isRequired, - payload: PropTypes.shape({ - fn: PropTypes.string, - attributeName: PropTypes.string, - stops: PropTypes.array, - mode: PropTypes.any, - }).isRequired, -}); +export function getStyleType(styleProp) { + if (!IS_ANDROID && styleExtras[styleProp]) { + return styleExtras[styleProp].iosType; + } -export const TransitionPropType = PropTypes.shape({ - duration: PropTypes.number, - delay: PropTypes.number, -}); + if (styleMap[styleProp]) { + return styleMap[styleProp]; + } -export const TranslationPropType = PropTypes.oneOfType([ - PropTypes.shape({ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - PropTypes.arrayOf(PropTypes.number), -]); + throw new Error(`${styleProp} is not a valid Mapbox layer style`); +} <%_ for (let layer of layers) { _%> export const <%- pascelCase(layer.name) _%>LayerStyleProp = PropTypes.shape({ @@ -71,7 +48,10 @@ export const <%- pascelCase(layer.name) _%>LayerStyleProp = PropTypes.shape({ /** * The transition affecting any changes to this layer’s <%= prop.name %> property. */ - <%= prop.name %>Transition: TransitionPropType, + <%= prop.name %>Transition: PropTypes.shape({ + duration: PropTypes.number, + delay: PropTypes.number, + }), <%_ } _%> <%_ } _%> }); diff --git a/style-spec/v8.json b/style-spec/v8.json index c480565dba..c404deed47 100644 --- a/style-spec/v8.json +++ b/style-spec/v8.json @@ -4,7 +4,9 @@ "version": { "required": true, "type": "enum", - "values": [8], + "values": [ + 8 + ], "doc": "Style specification version number. Must be 8.", "example": 8 }, @@ -21,7 +23,10 @@ "type": "array", "value": "number", "doc": "Default map center in longitude and latitude. The style center will be used only if the map has not been positioned by other means (e.g. map options or user interaction).", - "example": [-73.9749, 40.7736] + "example": [ + -73.9749, + 40.7736 + ] }, "zoom": { "type": "number", @@ -33,7 +38,7 @@ "default": 0, "period": 360, "units": "degrees", - "doc": "Default bearing, in degrees clockwise from true north. The style bearing will be used only if the map has not been positioned by other means (e.g. map options or user interaction).", + "doc": "Default bearing, in degrees. The bearing is the compass direction that is \"up\"; for example, a bearing of 90° orients the map so that east is up. This value will be used only if the map has not been positioned by other means (e.g. map options or user interaction).", "example": 29 }, "pitch": { @@ -108,10 +113,10 @@ "source": [ "source_vector", "source_raster", + "source_raster_dem", "source_geojson", "source_video", - "source_image", - "source_canvas" + "source_image" ], "source_vector": { "type": { @@ -137,7 +142,12 @@ "type": "array", "value": "number", "length": 4, - "default": [-180, -85.0511, 180, 85.0511], + "default": [ + -180, + -85.0511, + 180, + 85.0511 + ], "doc": "An array containing the longitude and latitude of the southwest and northeast corners of the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL." }, "minzoom": { @@ -183,7 +193,12 @@ "type": "array", "value": "number", "length": 4, - "default": [-180, -85.0511, 180, 85.0511], + "default": [ + -180, + -85.0511, + 180, + 85.0511 + ], "doc": "An array containing the longitude and latitude of the southwest and northeast corners of the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL." }, "minzoom": { @@ -224,6 +239,76 @@ "doc": "Other keys to configure the data source." } }, + "source_raster_dem": { + "type": { + "required": true, + "type": "enum", + "values": { + "raster-dem": { + "doc": "A RGB-encoded raster DEM source" + } + }, + "doc": "The type of the source." + }, + "url": { + "type": "string", + "doc": "A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`." + }, + "tiles": { + "type": "array", + "value": "string", + "doc": "An array of one or more tile source URLs, as in the TileJSON spec." + }, + "bounds": { + "type": "array", + "value": "number", + "length": 4, + "default": [ + -180, + -85.0511, + 180, + 85.0511 + ], + "doc": "An array containing the longitude and latitude of the southwest and northeast corners of the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL." + }, + "minzoom": { + "type": "number", + "default": 0, + "doc": "Minimum zoom level for which tiles are available, as in the TileJSON spec." + }, + "maxzoom": { + "type": "number", + "default": 22, + "doc": "Maximum zoom level for which tiles are available, as in the TileJSON spec. Data from tiles at the maxzoom are used when displaying the map at higher zoom levels." + }, + "tileSize": { + "type": "number", + "default": 512, + "units": "pixels", + "doc": "The minimum visual size to display tiles for this layer. Only configurable for raster layers." + }, + "attribution": { + "type": "string", + "doc": "Contains an attribution to be displayed when the map is shown to a user." + }, + "encoding": { + "type": "enum", + "values": { + "terrarium": { + "doc": "Terrarium format PNG tiles. See https://aws.amazon.com/es/public-datasets/terrain/ for more info." + }, + "mapbox": { + "doc": "Mapbox Terrain RGB tiles. See https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb for more info." + } + }, + "default": "mapbox", + "doc": "The encoding used by this source. Mapbox Terrain RGB is used by default" + }, + "*": { + "type": "*", + "doc": "Other keys to configure the data source." + } + }, "source_geojson": { "type": { "required": true, @@ -244,6 +329,10 @@ "default": 18, "doc": "Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels)." }, + "attribution": { + "type": "string", + "doc": "Contains an attribution to be displayed when the map is shown to a user." + }, "buffer": { "type": "number", "default": 128, @@ -270,6 +359,11 @@ "clusterMaxZoom": { "type": "number", "doc": "Max zoom on which to cluster points if clustering is enabled. Defaults to one zoom less than maxzoom (so that last zoom features are not clustered)." + }, + "lineMetrics": { + "type": "boolean", + "default": false, + "doc": "Whether to calculate line distance metrics. This is required for line layers that specify `line-gradient` values." } }, "source_video": { @@ -331,40 +425,6 @@ } } }, - "source_canvas": { - "type": { - "required": true, - "type": "enum", - "values": { - "canvas": { - "doc": "A canvas data source." - } - }, - "doc": "The data type of the canvas source." - }, - "coordinates": { - "required": true, - "doc": "Corners of canvas specified in longitude, latitude pairs.", - "type": "array", - "length": 4, - "value": { - "type": "array", - "length": 2, - "value": "number", - "doc": "A single longitude, latitude pair." - } - }, - "animate": { - "type": "boolean", - "default": "true", - "doc": "Whether the canvas source is animated. If the canvas is static, `animate` should be set to `false` to improve performance." - }, - "canvas": { - "type": "string", - "required": true, - "doc": "HTML ID of the canvas from which to read pixels." - } - }, "layer": { "id": { "type": "string", @@ -422,7 +482,10 @@ "doc": "A heatmap.", "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" } } }, @@ -448,6 +511,17 @@ } } }, + "hillshade": { + "doc": "Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles.", + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, "background": { "doc": "The background color or pattern of the map.", "sdk-support": { @@ -460,7 +534,8 @@ } } }, - "doc": "Rendering type of this layer." + "doc": "Rendering type of this layer.", + "required": true }, "metadata": { "type": "*", @@ -478,13 +553,13 @@ "type": "number", "minimum": 0, "maximum": 24, - "doc": "The minimum zoom level on which the layer gets parsed and appears on." + "doc": "The minimum zoom level for the layer. At zoom levels less than the minzoom, the layer will be hidden." }, "maxzoom": { "type": "number", "minimum": 0, "maximum": 24, - "doc": "The maximum zoom level on which the layer gets parsed and appears on." + "doc": "The maximum zoom level for the layer. At zoom levels equal to or greater than the maxzoom, the layer will be hidden." }, "filter": { "type": "filter", @@ -507,6 +582,7 @@ "layout_fill-extrusion", "layout_symbol", "layout_raster", + "layout_hillshade", "layout_background" ], "layout_background": { @@ -514,10 +590,10 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -529,7 +605,8 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "property-type": "constant" } }, "layout_fill": { @@ -537,10 +614,10 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -552,7 +629,8 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "property-type": "constant" } }, "layout_circle": { @@ -560,10 +638,10 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -575,7 +653,8 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "property-type": "constant" } }, "layout_heatmap": { @@ -583,19 +662,23 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", "doc": "Whether this layer is displayed.", "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" } - } + }, + "property-type": "constant" } }, "layout_fill-extrusion": { @@ -603,10 +686,10 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -618,23 +701,22 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "property-type": "constant" } }, "layout_line": { "line-cap": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "butt": { - "doc": "A cap with a squared-off end which is drawn to the exact endpoint of the line." + "doc": "A cap with a squared-off end which is drawn to the exact endpoint of the line." }, "round": { - "doc": "A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line." + "doc": "A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line." }, "square": { - "doc": "A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width." + "doc": "A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width." } }, "default": "butt", @@ -647,22 +729,26 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "line-join": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "values": { "bevel": { - "doc": "A join with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width." + "doc": "A join with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width." }, "round": { - "doc": "A join with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line." + "doc": "A join with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line." }, "miter": { - "doc": "A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of the path until they meet." + "doc": "A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of the path until they meet." } }, "default": "miter", @@ -680,13 +766,19 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-miter-limit": { "type": "number", "default": 2, - "function": "interpolated", - "zoom-function": true, "doc": "Used to automatically convert miter joins to bevel joins for sharp angles.", "requires": [ { @@ -701,13 +793,18 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "line-round-limit": { "type": "number", "default": 1.05, - "function": "interpolated", - "zoom-function": true, "doc": "Used to automatically convert round joins to miter joins for shallow angles.", "requires": [ { @@ -722,16 +819,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "visibility": { "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -744,21 +848,20 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "property-type": "constant" } }, "layout_symbol": { "symbol-placement": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { - "point": { - "doc": "The label is placed at the point where the geometry is located." - }, - "line": { - "doc": "The label is placed along the line of the geometry. Can only be used on `LineString` and `Polygon` geometries." - } + "point": { + "doc": "The label is placed at the point where the geometry is located." + }, + "line": { + "doc": "The label is placed along the line of the geometry. Can only be used on `LineString` and `Polygon` geometries." + } }, "default": "point", "doc": "Label placement relative to its geometry.", @@ -770,14 +873,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "symbol-spacing": { "type": "number", "default": 250, "minimum": 1, - "function": "interpolated", - "zoom-function": true, "units": "pixels", "doc": "Distance between two symbol anchors.", "requires": [ @@ -793,12 +901,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "symbol-avoid-edges": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.", "sdk-support": { @@ -809,12 +922,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-allow-overlap": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, the icon will be visible even if it collides with other previously drawn symbols.", "requires": [ @@ -828,12 +946,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-ignore-placement": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, other symbols can be visible even if they collide with the icon.", "requires": [ @@ -847,12 +970,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-optional": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.", "requires": [ @@ -867,21 +995,26 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-rotation-alignment": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "When `symbol-placement` is set to `point`, aligns icons east-west. When `symbol-placement` is set to `line`, aligns icon x-axes with the line." + "doc": "When `symbol-placement` is set to `point`, aligns icons east-west. When `symbol-placement` is set to `line`, aligns icon x-axes with the line." }, "viewport": { - "doc": "Produces icons whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbol-placement`." + "doc": "Produces icons whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbol-placement`." }, "auto": { - "doc": "When `symbol-placement` is set to `point`, this is equivalent to `viewport`. When `symbol-placement` is set to `line`, this is equivalent to `map`." + "doc": "When `symbol-placement` is set to `point`, this is equivalent to `viewport`. When `symbol-placement` is set to `line`, this is equivalent to `map`." } }, "default": "auto", @@ -903,15 +1036,19 @@ "macos": "0.3.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-size": { "type": "number", "default": 1, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "units": "factor of the original icon size", "doc": "Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by `icon-size`. 1 is the original size; 3 triples the size of the image.", "requires": [ @@ -930,24 +1067,30 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-text-fit": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "none": { - "doc": "The icon is displayed at its intrinsic aspect ratio." + "doc": "The icon is displayed at its intrinsic aspect ratio." }, "width": { - "doc": "The icon is scaled in the x-dimension to fit the width of the text." + "doc": "The icon is scaled in the x-dimension to fit the width of the text." }, "height": { - "doc": "The icon is scaled in the y-dimension to fit the height of the text." + "doc": "The icon is scaled in the y-dimension to fit the height of the text." }, "both": { - "doc": "The icon is scaled in both x- and y-dimensions." + "doc": "The icon is scaled in both x- and y-dimensions." } }, "default": "none", @@ -964,7 +1107,14 @@ "macos": "0.2.1" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-text-fit-padding": { "type": "array", @@ -977,8 +1127,6 @@ 0 ], "units": "pixels", - "function": "interpolated", - "zoom-function": true, "doc": "Size of the additional area added to dimensions determined by `icon-text-fit`, in clockwise order: top, right, bottom, left.", "requires": [ "icon-image", @@ -999,14 +1147,18 @@ "macos": "0.2.1" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-image": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, - "doc": "Name of image in sprite to use for drawing an image background. A string with `{tokens}` replaced, referencing the data property to pull from. (`{token}` replacement is only supported for literal `icon-image` values; not for property functions.)", + "doc": "Name of image in sprite to use for drawing an image background.", "tokens": true, "sdk-support": { "basic functionality": { @@ -1021,15 +1173,20 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-rotate": { "type": "number", "default": 0, "period": 360, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "units": "degrees", "doc": "Rotates the icon clockwise.", "requires": [ @@ -1048,14 +1205,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-padding": { "type": "number", "default": 2, "minimum": 0, - "function": "interpolated", - "zoom-function": true, "units": "pixels", "doc": "Size of the additional area around the icon bounding box used for detecting symbol collisions.", "requires": [ @@ -1069,12 +1232,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-keep-upright": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, the icon may be flipped to prevent it from being rendered upside-down.", "requires": [ @@ -1094,21 +1262,24 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-offset": { "type": "array", "value": "number", - "units": "pixels multiplied by the value of \"icon-size\"", "length": 2, "default": [ 0, 0 ], - "function": "interpolated", - "zoom-function": true, - "property-function": true, - "doc": "Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. When combined with `icon-rotate` the offset will be as if the rotated direction was up.", + "doc": "Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up. Each component is multiplied by the value of `icon-size` to obtain the final offset in pixels. When combined with `icon-rotate` the offset will be as if the rotated direction was up.", "requires": [ "icon-image" ], @@ -1125,40 +1296,45 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "values": { "center": { - "doc": "The center of the icon is placed closest to the anchor." + "doc": "The center of the icon is placed closest to the anchor." }, "left": { - "doc": "The left side of the icon is placed closest to the anchor." + "doc": "The left side of the icon is placed closest to the anchor." }, "right": { - "doc": "The right side of the icon is placed closest to the anchor." + "doc": "The right side of the icon is placed closest to the anchor." }, "top": { - "doc": "The top of the icon is placed closest to the anchor." + "doc": "The top of the icon is placed closest to the anchor." }, "bottom": { - "doc": "The bottom of the icon is placed closest to the anchor." + "doc": "The bottom of the icon is placed closest to the anchor." }, "top-left": { - "doc": "The top left corner of the icon is placed closest to the anchor." + "doc": "The top left corner of the icon is placed closest to the anchor." }, "top-right": { - "doc": "The top right corner of the icon is placed closest to the anchor." + "doc": "The top right corner of the icon is placed closest to the anchor." }, "bottom-left": { - "doc": "The bottom left corner of the icon is placed closest to the anchor." + "doc": "The bottom left corner of the icon is placed closest to the anchor." }, "bottom-right": { - "doc": "The bottom right corner of the icon is placed closest to the anchor." + "doc": "The bottom right corner of the icon is placed closest to the anchor." } }, "default": "center", @@ -1179,21 +1355,27 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-pitch-alignment": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The icon is aligned to the plane of the map." + "doc": "The icon is aligned to the plane of the map." }, "viewport": { - "doc": "The icon is aligned to the plane of the viewport." + "doc": "The icon is aligned to the plane of the viewport." }, "auto": { - "doc": "Automatically matches the value of `icon-rotation-alignment`." + "doc": "Automatically matches the value of `icon-rotation-alignment`." } }, "default": "auto", @@ -1209,21 +1391,26 @@ "macos": "0.6.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-pitch-alignment": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The text is aligned to the plane of the map." + "doc": "The text is aligned to the plane of the map." }, "viewport": { - "doc": "The text is aligned to the plane of the viewport." + "doc": "The text is aligned to the plane of the viewport." }, "auto": { - "doc": "Automatically matches the value of `text-rotation-alignment`." + "doc": "Automatically matches the value of `text-rotation-alignment`." } }, "default": "auto", @@ -1245,21 +1432,26 @@ "macos": "0.3.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-rotation-alignment": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "When `symbol-placement` is set to `point`, aligns text east-west. When `symbol-placement` is set to `line`, aligns text x-axes with the line." + "doc": "When `symbol-placement` is set to `point`, aligns text east-west. When `symbol-placement` is set to `line`, aligns text x-axes with the line." }, "viewport": { - "doc": "Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbol-placement`." + "doc": "Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the value of `symbol-placement`." }, "auto": { - "doc": "When `symbol-placement` is set to `point`, this is equivalent to `viewport`. When `symbol-placement` is set to `line`, this is equivalent to `map`." + "doc": "When `symbol-placement` is set to `point`, this is equivalent to `viewport`. When `symbol-placement` is set to `line`, this is equivalent to `map`." } }, "default": "auto", @@ -1281,16 +1473,20 @@ "macos": "0.3.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-field": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "default": "", "tokens": true, - "doc": "Value to use for a text label. Feature properties are specified using tokens like `{field_name}`. (`{token}` replacement is only supported for literal `text-field` values; not for property functions.)", + "doc": "Value to use for a text label.", "sdk-support": { "basic functionality": { "js": "0.10.0", @@ -1304,15 +1500,23 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-font": { "type": "array", "value": "string", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, - "default": ["Open Sans Regular", "Arial Unicode MS Regular"], + "default": [ + "Open Sans Regular", + "Arial Unicode MS Regular" + ], "doc": "Font stack to use for displaying text.", "requires": [ "text-field" @@ -1324,17 +1528,27 @@ "ios": "2.0.0", "macos": "0.1.0" }, - "data-driven styling": {} - } + "data-driven styling": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-size": { "type": "number", "default": 16, "minimum": 0, "units": "pixels", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "doc": "Font size.", "requires": [ "text-field" @@ -1352,16 +1566,21 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-max-width": { "type": "number", "default": 10, "minimum": 0, "units": "ems", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "doc": "The maximum line width for text wrapping.", "requires": [ "text-field" @@ -1379,14 +1598,20 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-line-height": { "type": "number", "default": 1.2, "units": "ems", - "function": "interpolated", - "zoom-function": true, "doc": "Text leading value for multi-line text.", "requires": [ "text-field" @@ -1399,15 +1624,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-letter-spacing": { "type": "number", "default": 0, "units": "ems", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "doc": "Text tracking amount.", "requires": [ "text-field" @@ -1425,22 +1654,27 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-justify": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "values": { "left": { - "doc": "The text is aligned to the left." + "doc": "The text is aligned to the left." }, "center": { - "doc": "The text is centered." + "doc": "The text is centered." }, "right": { - "doc": "The text is aligned to the right." + "doc": "The text is aligned to the right." } }, "default": "center", @@ -1461,40 +1695,45 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "values": { "center": { - "doc": "The center of the text is placed closest to the anchor." + "doc": "The center of the text is placed closest to the anchor." }, "left": { - "doc": "The left side of the text is placed closest to the anchor." + "doc": "The left side of the text is placed closest to the anchor." }, "right": { - "doc": "The right side of the text is placed closest to the anchor." + "doc": "The right side of the text is placed closest to the anchor." }, "top": { - "doc": "The top of the text is placed closest to the anchor." + "doc": "The top of the text is placed closest to the anchor." }, "bottom": { - "doc": "The bottom of the text is placed closest to the anchor." + "doc": "The bottom of the text is placed closest to the anchor." }, "top-left": { - "doc": "The top left corner of the text is placed closest to the anchor." + "doc": "The top left corner of the text is placed closest to the anchor." }, "top-right": { - "doc": "The top right corner of the text is placed closest to the anchor." + "doc": "The top right corner of the text is placed closest to the anchor." }, "bottom-left": { - "doc": "The bottom left corner of the text is placed closest to the anchor." + "doc": "The bottom left corner of the text is placed closest to the anchor." }, "bottom-right": { - "doc": "The bottom right corner of the text is placed closest to the anchor." + "doc": "The bottom right corner of the text is placed closest to the anchor." } }, "default": "center", @@ -1515,14 +1754,20 @@ "ios": "3.7.0", "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-max-angle": { "type": "number", "default": 45, "units": "degrees", - "function": "interpolated", - "zoom-function": true, "doc": "Maximum angle change between adjacent characters.", "requires": [ "text-field", @@ -1538,16 +1783,20 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-rotate": { "type": "number", "default": 0, "period": 360, "units": "degrees", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "doc": "Rotates the text clockwise.", "requires": [ "text-field" @@ -1565,15 +1814,21 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-padding": { "type": "number", "default": 2, "minimum": 0, "units": "pixels", - "function": "interpolated", - "zoom-function": true, "doc": "Size of the additional area around the text bounding box used for detecting symbol collisions.", "requires": [ "text-field" @@ -1586,12 +1841,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-keep-upright": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": true, "doc": "If true, the text may be flipped vertically to prevent it from being rendered upside-down.", "requires": [ @@ -1611,22 +1871,26 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-transform": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, - "property-function": true, "values": { "none": { - "doc": "The text is not altered." + "doc": "The text is not altered." }, "uppercase": { - "doc": "Forces all letters to be displayed in uppercase." + "doc": "Forces all letters to be displayed in uppercase." }, "lowercase": { - "doc": "Forces all letters to be displayed in lowercase." + "doc": "Forces all letters to be displayed in lowercase." } }, "default": "none", @@ -1647,16 +1911,21 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-offset": { "type": "array", "doc": "Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.", "value": "number", "units": "ems", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "length": 2, "default": [ 0, @@ -1678,12 +1947,18 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-allow-overlap": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, the text will be visible even if it collides with other previously drawn symbols.", "requires": [ @@ -1697,12 +1972,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-ignore-placement": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, other symbols can be visible even if they collide with the text.", "requires": [ @@ -1716,12 +1996,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-optional": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": false, "doc": "If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.", "requires": [ @@ -1736,16 +2021,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "visibility": { "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -1758,7 +2050,8 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "property-type": "constant" } }, "layout_raster": { @@ -1766,10 +2059,10 @@ "type": "enum", "values": { "visible": { - "doc": "The layer is shown." + "doc": "The layer is shown." }, "none": { - "doc": "The layer is not shown." + "doc": "The layer is not shown." } }, "default": "visible", @@ -1782,55 +2075,81 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "property-type": "constant" } }, - "filter": { - "type": "array", - "value": "*", - "doc": "A filter selects specific features from a layer." - }, - "filter_operator": { - "type": "enum", - "values": { - "==": { - "doc": "`[\"==\", key, value]` equality: `feature[key] = value`" - }, - "!=": { - "doc": "`[\"!=\", key, value]` inequality: `feature[key] ≠ value`" + "layout_hillshade": { + "visibility": { + "type": "enum", + "values": { + "visible": { + "doc": "The layer is shown." + }, + "none": { + "doc": "The layer is not shown." + } + }, + "default": "visible", + "doc": "Whether this layer is displayed.", + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "property-type": "constant" + } + }, + "filter": { + "type": "array", + "value": "*", + "doc": "A filter selects specific features from a layer." + }, + "filter_operator": { + "type": "enum", + "values": { + "==": { + "doc": "`[\"==\", key, value]` equality: `feature[key] = value`" + }, + "!=": { + "doc": "`[\"!=\", key, value]` inequality: `feature[key] ≠ value`" }, ">": { - "doc": "`[\">\", key, value]` greater than: `feature[key] > value`" + "doc": "`[\">\", key, value]` greater than: `feature[key] > value`" }, ">=": { - "doc": "`[\">=\", key, value]` greater than or equal: `feature[key] ≥ value`" + "doc": "`[\">=\", key, value]` greater than or equal: `feature[key] ≥ value`" }, "<": { - "doc": "`[\"<\", key, value]` less than: `feature[key] < value`" + "doc": "`[\"<\", key, value]` less than: `feature[key] < value`" }, "<=": { - "doc": "`[\"<=\", key, value]` less than or equal: `feature[key] ≤ value`" + "doc": "`[\"<=\", key, value]` less than or equal: `feature[key] ≤ value`" }, "in": { - "doc": "`[\"in\", key, v0, ..., vn]` set inclusion: `feature[key] ∈ {v0, ..., vn}`" + "doc": "`[\"in\", key, v0, ..., vn]` set inclusion: `feature[key] ∈ {v0, ..., vn}`" }, "!in": { - "doc": "`[\"!in\", key, v0, ..., vn]` set exclusion: `feature[key] ∉ {v0, ..., vn}`" + "doc": "`[\"!in\", key, v0, ..., vn]` set exclusion: `feature[key] ∉ {v0, ..., vn}`" }, "all": { - "doc": "`[\"all\", f0, ..., fn]` logical `AND`: `f0 ∧ ... ∧ fn`" + "doc": "`[\"all\", f0, ..., fn]` logical `AND`: `f0 ∧ ... ∧ fn`" }, "any": { - "doc": "`[\"any\", f0, ..., fn]` logical `OR`: `f0 ∨ ... ∨ fn`" + "doc": "`[\"any\", f0, ..., fn]` logical `OR`: `f0 ∨ ... ∨ fn`" }, "none": { - "doc": "`[\"none\", f0, ..., fn]` logical `NOR`: `¬f0 ∧ ... ∧ ¬fn`" + "doc": "`[\"none\", f0, ..., fn]` logical `NOR`: `¬f0 ∧ ... ∧ ¬fn`" }, "has": { - "doc": "`[\"has\", key]` `feature[key]` exists" + "doc": "`[\"has\", key]` `feature[key]` exists" }, "!has": { - "doc": "`[\"!has\", key]` `feature[key]` does not exist" + "doc": "`[\"!has\", key]` `feature[key]` does not exist" } }, "doc": "The filter operator." @@ -1839,13 +2158,13 @@ "type": "enum", "values": { "Point": { - "doc": "Filter to point geometries." + "doc": "Filter to point geometries." }, "LineString": { - "doc": "Filter to line geometries." + "doc": "Filter to line geometries." }, "Polygon": { - "doc": "Filter to polygon geometries." + "doc": "Filter to polygon geometries." } }, "doc": "The geometry type for the filter to select." @@ -1874,18 +2193,18 @@ "type": { "type": "enum", "values": { - "identity": { - "doc": "Return the input value as the output value." - }, - "exponential": { - "doc": "Generate an output by interpolating between stops just less than and just greater than the function input." - }, - "interval": { - "doc": "Return the output value of the stop just less than the function input." - }, - "categorical": { - "doc": "Return the output value of the stop equal to the function input." - } + "identity": { + "doc": "Return the input value as the output value." + }, + "exponential": { + "doc": "Generate an output by interpolating between stops just less than and just greater than the function input." + }, + "interval": { + "doc": "Return the output value of the stop just less than the function input." + }, + "categorical": { + "doc": "Return the output value of the stop equal to the function input." + } }, "doc": "The interpolation strategy to use in function evaluation.", "default": "exponential" @@ -1893,15 +2212,15 @@ "colorSpace": { "type": "enum", "values": { - "rgb": { - "doc": "Use the RGB color space to interpolate color values" - }, - "lab": { - "doc": "Use the LAB color space to interpolate color values." - }, - "hcl": { - "doc": "Use the HCL color space to interpolate color values, interpolating the Hue, Chroma, and Luminance channels individually." - } + "rgb": { + "doc": "Use the RGB color space to interpolate color values" + }, + "lab": { + "doc": "Use the LAB color space to interpolate color values." + }, + "hcl": { + "doc": "Use the HCL color space to interpolate color values, interpolating the Hue, Chroma, and Luminance channels individually." + } }, "doc": "The color space in which colors interpolated. Interpolating colors in perceptual color spaces like LAB and HCL tend to produce color ramps that look more consistent and produce colors that can be differentiated more easily than those interpolated in RGB space.", "default": "rgb" @@ -1935,255 +2254,842 @@ "values": { "let": { "doc": "Binds expressions to named variables, which can then be referenced in the result expression using [\"var\", \"variable_name\"].", - "group": "Variable binding" + "group": "Variable binding", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "var": { "doc": "References variable bound using \"let\".", - "group": "Variable binding" + "group": "Variable binding", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "literal": { "doc": "Provides a literal array or object value.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "array": { "doc": "Asserts that the input is an array (optionally with a specific item type and length). If, when the input expression is evaluated, it is not of the asserted type, then this assertion will cause the whole expression to be aborted.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "at": { "doc": "Retrieves an item from an array.", - "group": "Lookup" + "group": "Lookup", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, - "case": { + "case": { "doc": "Selects the first output whose corresponding test condition evaluates to true.", - "group": "Decision" + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "match": { - "doc": "Selects the output whose label value matches the input value, or the fallback value if no match is found. The `input` can be any string or number expression (e.g. `[\"get\", \"building_type\"]`). Each label can either be a single literal value or an array of values.", - "group": "Decision" + "doc": "Selects the output whose label value matches the input value, or the fallback value if no match is found. The input can be any expression (e.g. `[\"get\", \"building_type\"]`). Each label must either be a single literal value or an array of literal values (e.g. `\"a\"` or `[\"c\", \"b\"]`), and those values must be all strings or all numbers. (The values `\"1\"` and `1` cannot both be labels in the same match expression.) If the input type does not match the type of the labels, the result will be the fallback value.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "coalesce": { "doc": "Evaluates each expression in turn until the first non-null value is obtained, and returns that value.", - "group": "Decision" + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "step": { "doc": "Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order. Returns the output value of the stop just less than the input, or the first input if the input is less than the first stop.", - "group": "Ramps, scales, curves" + "group": "Ramps, scales, curves", + "sdk-support": { + "basic functionality": { + "js": "0.42.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "interpolate": { - "doc": "Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order. The output type must be `number`, `array`, or `color`.\n\nInterpolation types:\n- `[\"linear\"]`: interpolates linearly between the pair of stops just less than and just greater than the input.\n- `[\"exponential\", base]`: interpolates exponentially between the stops just less than and just greater than the input. `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y2, x2, y2]`: interpolates using the cubic bezier curve defined by the given control points.", - "group": "Ramps, scales, curves" + "doc": "Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order. The output type must be `number`, `array`, or `color`.\n\nInterpolation types:\n- `[\"linear\"]`: interpolates linearly between the pair of stops just less than and just greater than the input.\n- `[\"exponential\", base]`: interpolates exponentially between the stops just less than and just greater than the input. `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y1, x2, y2]`: interpolates using the cubic bezier curve defined by the given control points.", + "group": "Ramps, scales, curves", + "sdk-support": { + "basic functionality": { + "js": "0.42.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "ln2": { "doc": "Returns mathematical constant ln(2).", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "pi": { "doc": "Returns the mathematical constant pi.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "e": { "doc": "Returns the mathematical constant e.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "typeof": { "doc": "Returns a string describing the type of the given value.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "string": { - "doc": "Asserts that the input value is a string. If multiple values are provided, each one is evaluated in order until a string value is obtained. If none of the inputs are strings, the expression is an error.", - "group": "Types" + "doc": "Asserts that the input value is a string. If multiple values are provided, each one is evaluated in order until a string is obtained. If none of the inputs are strings, the expression is an error.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "number": { - "doc": "Asserts that the input value is a number. If multiple values are provided, each one is evaluated in order until a number value is obtained. If none of the inputs are numbers, the expression is an error.", - "group": "Types" + "doc": "Asserts that the input value is a number. If multiple values are provided, each one is evaluated in order until a number is obtained. If none of the inputs are numbers, the expression is an error.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "boolean": { - "doc": "Asserts that the input value is a boolean. If multiple values are provided, each one is evaluated in order until a boolean value is obtained. If none of the inputs are booleans, the expression is an error.", - "group": "Types" + "doc": "Asserts that the input value is a boolean. If multiple values are provided, each one is evaluated in order until a boolean is obtained. If none of the inputs are booleans, the expression is an error.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "object": { - "doc": "Asserts that the input value is an object. If it is not, the expression is an error.", - "group": "Types" + "doc": "Asserts that the input value is an object. If multiple values are provided, each one is evaluated in order until an object is obtained. If none of the inputs are objects, the expression is an error.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "collator": { + "doc": "Returns a `collator` for use in locale-dependent comparison operations. The `case-sensitive` and `diacritic-sensitive` options default to `false`. The `locale` argument specifies the IETF language tag of the locale to use. If none is provided, the default locale is used. If the requested locale is not available, the `collator` will use a system-defined fallback locale. Use `resolved-locale` to test the results of locale fallback behavior.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.45.0" + } + } }, "to-string": { - "doc": "Converts the input value to a string. If the input is `null`, the result is `\"null\"`. If the input is a boolean, the result is `\"true\"` or `\"false\"`. If the input is a number, it is converted to a string as specified by the [\"NumberToString\" algorithm](https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type) of the ECMAScript Language Specification. If the input is a color, it is converted to a string of the form `\"rgba(r,g,b,a)\"`, where `r`, `g`, and `b` are numerals ranging from 0 to 255, and `a` ranges from 0 to 1. Otherwise, the input is converted to a string in the format specified by the [`JSON.stringify`](https://tc39.github.io/ecma262/#sec-json.stringify) function of the ECMAScript Language Specification.", - "group": "Types" + "doc": "Converts the input value to a string. If the input is `null`, the result is `\"\"`. If the input is a boolean, the result is `\"true\"` or `\"false\"`. If the input is a number, it is converted to a string as specified by the [\"NumberToString\" algorithm](https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type) of the ECMAScript Language Specification. If the input is a color, it is converted to a string of the form `\"rgba(r,g,b,a)\"`, where `r`, `g`, and `b` are numerals ranging from 0 to 255, and `a` ranges from 0 to 1. Otherwise, the input is converted to a string in the format specified by the [`JSON.stringify`](https://tc39.github.io/ecma262/#sec-json.stringify) function of the ECMAScript Language Specification.", + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "to-number": { "doc": "Converts the input value to a number, if possible. If the input is `null` or `false`, the result is 0. If the input is `true`, the result is 1. If the input is a string, it is converted to a number as specified by the [\"ToNumber Applied to the String Type\" algorithm](https://tc39.github.io/ecma262/#sec-tonumber-applied-to-the-string-type) of the ECMAScript Language Specification. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained. If none of the inputs can be converted, the expression is an error.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "to-boolean": { "doc": "Converts the input value to a boolean. The result is `false` when then input is an empty string, 0, `false`, `null`, or `NaN`; otherwise it is `true`.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "to-rgba": { "doc": "Returns a four-element array containing the input color's red, green, blue, and alpha components, in that order.", - "group": "Color" + "group": "Color", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "to-color": { "doc": "Converts the input value to a color. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained. If none of the inputs can be converted, the expression is an error.", - "group": "Types" + "group": "Types", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "rgb": { "doc": "Creates a color value from red, green, and blue components, which must range between 0 and 255, and an alpha component of 1. If any component is out of range, the expression is an error.", - "group": "Color" + "group": "Color", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "rgba": { "doc": "Creates a color value from red, green, blue components, which must range between 0 and 255, and an alpha component which must range between 0 and 1. If any component is out of range, the expression is an error.", - "group": "Color" + "group": "Color", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "get": { "doc": "Retrieves a property value from the current feature's properties, or from another object if a second argument is provided. Returns null if the requested property is missing.", - "group": "Lookup" + "group": "Lookup", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "has": { "doc": "Tests for the presence of an property value in the current feature's properties, or from another object if a second argument is provided.", - "group": "Lookup" + "group": "Lookup", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "length": { "doc": "Gets the length of an array or string.", - "group": "Lookup" + "group": "Lookup", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "properties": { "doc": "Gets the feature properties object. Note that in some cases, it may be more efficient to use [\"get\", \"property_name\"] directly.", - "group": "Feature data" + "group": "Feature data", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "feature-state": { + "doc": "Retrieves a property value from the current feature's state. Returns null if the requested property is not present on the feature's state. A feature's state is not part of the GeoJSON or vector tile data, and must be set programmatically on each feature. Note that [\"feature-state\"] can only be used with paint properties that support data-driven styling.", + "group": "Feature data", + "sdk-support": { + "basic functionality": { + "js": "0.46.0" + } + } }, "geometry-type": { "doc": "Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon.", - "group": "Feature data" + "group": "Feature data", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "id": { "doc": "Gets the feature's id, if it has one.", - "group": "Feature data" + "group": "Feature data", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "zoom": { "doc": "Gets the current zoom level. Note that in style layout and paint properties, [\"zoom\"] may only appear as the input to a top-level \"step\" or \"interpolate\" expression.", - "group": "Zoom" + "group": "Zoom", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "heatmap-density": { "doc": "Gets the kernel density estimation of a pixel in a heatmap layer, which is a relative measure of how many data points are crowded around a particular pixel. Can only be used in the `heatmap-color` property.", + "group": "Heatmap", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "line-progress": { + "doc": "Gets the progress along a gradient line. Can only be used in the `line-gradient` property.", "group": "Heatmap" }, "+": { "doc": "Returns the sum of the inputs.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "*": { "doc": "Returns the product of the inputs.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "-": { "doc": "For two inputs, returns the result of subtracting the second input from the first. For a single input, returns the result of subtracting it from 0.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "/": { "doc": "Returns the result of floating point division of the first input by the second.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "%": { "doc": "Returns the remainder after integer division of the first input by the second.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "^": { "doc": "Returns the result of raising the first input to the power specified by the second.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "sqrt": { "doc": "Returns the square root of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.42.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "log10": { "doc": "Returns the base-ten logarithm of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "ln": { "doc": "Returns the natural logarithm of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "log2": { "doc": "Returns the base-two logarithm of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "sin": { "doc": "Returns the sine of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "cos": { "doc": "Returns the cosine of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "tan": { "doc": "Returns the tangent of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "asin": { "doc": "Returns the arcsine of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "acos": { "doc": "Returns the arccosine of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "atan": { "doc": "Returns the arctangent of the input.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "min": { "doc": "Returns the minimum value of the inputs.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "max": { "doc": "Returns the maximum value of the inputs.", - "group": "Math" + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "round": { + "doc": "Rounds the input to the nearest integer. Halfway values are rounded away from zero. For example, `[\"round\", -1.5]` evaluates to -2.", + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.45.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "abs": { + "doc": "Returns the absolute value of the input.", + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.45.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "ceil": { + "doc": "Returns the smallest integer that is greater than or equal to the input.", + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.45.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "floor": { + "doc": "Returns the largest integer that is less than or equal to the input.", + "group": "Math", + "sdk-support": { + "basic functionality": { + "js": "0.45.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "==": { - "doc": "Returns `true` if the input values are equal, `false` otherwise. The inputs must be numbers, strings, or booleans, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the input values are equal, `false` otherwise. Equality is strictly typed: values of different types are always considered not equal. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "!=": { - "doc": "Returns `true` if the input values are not equal, `false` otherwise. The inputs must be numbers, strings, or booleans, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the input values are not equal, `false` otherwise. Equality is strictly typed: values of different types are always considered not equal. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, ">": { - "doc": "Returns `true` if the first input is strictly greater than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the first input is strictly greater than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "<": { - "doc": "Returns `true` if the first input is strictly less than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the first input is strictly less than the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, ">=": { - "doc": "Returns `true` if the first input is greater than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the first input is greater than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "<=": { - "doc": "Returns `true` if the first input is less than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type.", - "group": "Decision" + "doc": "Returns `true` if the first input is less than or equal to the second, `false` otherwise. The inputs must be numbers or strings, and both of the same type. Accepts an optional `collator` argument to control locale-dependent string comparisons.", + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "all": { "doc": "Returns `true` if all the inputs are `true`, `false` otherwise. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to `false`, the result is `false` and no further input expressions are evaluated.", - "group": "Decision" + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "any": { "doc": "Returns `true` if any of the inputs are `true`, `false` otherwise. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to `true`, the result is `true` and no further input expressions are evaluated.", - "group": "Decision" + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "!": { "doc": "Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.", - "group": "Decision" + "group": "Decision", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "is-supported-script": { + "doc": "Returns `true` if the input string is expected to render legibly. Returns `false` if the input string contains sections that cannot be rendered without potential loss of meaning (e.g. Indic scripts that require complex text shaping, or right-to-left scripts if the the `mapbox-gl-rtl-text` plugin is not in use in Mapbox GL JS).", + "group": "String" }, "upcase": { "doc": "Returns the input string converted to uppercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database.", - "group": "String" + "group": "String", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "downcase": { "doc": "Returns the input string converted to lowercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database.", - "group": "String" + "group": "String", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } }, "concat": { "doc": "Returns a string consisting of the concatenation of the inputs.", - "group": "String" + "group": "String", + "sdk-support": { + "basic functionality": { + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + } + }, + "resolved-locale": { + "doc": "Returns the IETF language tag of the locale being used by the provided `collator`. This can be used to determine the default system locale, or to determine if a requested locale was successfully loaded.", + "group": "String", + "sdk-support": { + "basic functionality": { + "js": "0.45.0" + } + } } } }, @@ -2199,10 +3105,14 @@ "doc": "The position of the light source is aligned to the rotation of the viewport." } }, + "property-type": "data-constant", "transition": false, - "zoom-function": true, - "property-function": false, - "function": "piecewise-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, "doc": "Whether extruded geometries are lit relative to the map or viewport.", "example": "map", "sdk-support": { @@ -2216,15 +3126,27 @@ }, "position": { "type": "array", - "default": [1.15, 210, 30], + "default": [ + 1.15, + 210, + 30 + ], "length": 3, "value": "number", + "property-type": "data-constant", "transition": true, - "function": "interpolated", - "zoom-function": true, - "property-function": false, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, "doc": "Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below).", - "example": [1.5, 90, 80], + "example": [ + 1.5, + 90, + 80 + ], "sdk-support": { "basic functionality": { "js": "0.27.0", @@ -2236,10 +3158,14 @@ }, "color": { "type": "color", + "property-type": "data-constant", "default": "#ffffff", - "function": "interpolated", - "zoom-function": true, - "property-function": false, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, "transition": true, "doc": "Color tint for lighting extruded geometries.", "sdk-support": { @@ -2253,12 +3179,16 @@ }, "intensity": { "type": "number", + "property-type": "data-constant", "default": 0.5, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": false, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, "transition": true, "doc": "Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast.", "sdk-support": { @@ -2279,13 +3209,12 @@ "paint_fill-extrusion", "paint_symbol", "paint_raster", + "paint_hillshade", "paint_background" ], "paint_fill": { "fill-antialias": { "type": "boolean", - "function": "piecewise-constant", - "zoom-function": true, "default": true, "doc": "Whether or not the fill should be antialiased.", "sdk-support": { @@ -2296,13 +3225,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-opacity": { "type": "number", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "default": 1, "minimum": 0, "maximum": 1, @@ -2321,15 +3254,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "fill-color": { "type": "color", "default": "#000000", "doc": "The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ { @@ -2349,14 +3287,19 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "fill-outline-color": { "type": "color", "doc": "The outline color of the fill. Matches the value of `fill-color` if unspecified.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ { @@ -2379,7 +3322,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "fill-translate": { "type": "array", @@ -2389,8 +3340,6 @@ 0, 0 ], - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "pixels", "doc": "The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.", @@ -2402,18 +3351,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The fill is translated relative to the map." + "doc": "The fill is translated relative to the map." }, "viewport": { - "doc": "The fill is translated relative to the viewport." + "doc": "The fill is translated relative to the viewport." } }, "doc": "Controls the frame of reference for `fill-translate`.", @@ -2429,14 +3383,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-pattern": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, "transition": true, - "doc": "Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).", + "doc": "Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.", "sdk-support": { "basic functionality": { "js": "0.10.0", @@ -2445,15 +3404,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "cross-faded" } }, "paint_fill-extrusion": { "fill-extrusion-opacity": { "type": "number", - "function": "interpolated", - "zoom-function": true, - "property-function": false, "default": 1, "minimum": 0, "maximum": 1, @@ -2466,15 +3429,19 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-extrusion-color": { "type": "color", "default": "#000000", "doc": "The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacity.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ { @@ -2494,7 +3461,15 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "fill-extrusion-translate": { "type": "array", @@ -2504,8 +3479,6 @@ 0, 0 ], - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "pixels", "doc": "The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.", @@ -2517,18 +3490,23 @@ "macos": "0.5.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-extrusion-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The fill extrusion is translated relative to the map." + "doc": "The fill extrusion is translated relative to the map." }, "viewport": { - "doc": "The fill extrusion is translated relative to the viewport." + "doc": "The fill extrusion is translated relative to the viewport." } }, "doc": "Controls the frame of reference for `fill-extrusion-translate`.", @@ -2544,14 +3522,19 @@ "macos": "0.5.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "fill-extrusion-pattern": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, "transition": true, - "doc": "Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).", + "doc": "Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.", "sdk-support": { "basic functionality": { "js": "0.27.0", @@ -2560,13 +3543,17 @@ "macos": "0.5.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "cross-faded" }, "fill-extrusion-height": { "type": "number", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "default": 0, "minimum": 0, "units": "meters", @@ -2585,13 +3572,18 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "fill-extrusion-base": { "type": "number", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "default": 0, "minimum": 0, "units": "meters", @@ -2613,16 +3605,21 @@ "ios": "3.6.0", "macos": "0.5.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" } }, "paint_line": { "line-opacity": { "type": "number", "doc": "The opacity at which the line will be drawn.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "default": 1, "minimum": 0, "maximum": 1, @@ -2640,15 +3637,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-color": { "type": "color", "doc": "The color with which the line will be drawn.", "default": "#000000", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ { @@ -2668,7 +3670,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-translate": { "type": "array", @@ -2678,8 +3688,6 @@ 0, 0 ], - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "pixels", "doc": "The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.", @@ -2691,18 +3699,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "line-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The line is translated relative to the map." + "doc": "The line is translated relative to the map." }, "viewport": { - "doc": "The line is translated relative to the viewport." + "doc": "The line is translated relative to the viewport." } }, "doc": "Controls the frame of reference for `line-translate`.", @@ -2718,15 +3731,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "line-width": { "type": "number", "default": 1, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Stroke thickness.", @@ -2738,18 +3755,26 @@ "macos": "0.1.0" }, "data-driven styling": { - "js": "0.39.0" + "js": "0.39.0", + "android": "5.2.0", + "ios": "3.7.0", + "macos": "0.6.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-gap-width": { "type": "number", "default": 0, "minimum": 0, "doc": "Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "sdk-support": { @@ -2765,15 +3790,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-offset": { "type": "number", "default": 0, "doc": "The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "sdk-support": { @@ -2789,15 +3819,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-blur": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Blur applied to the line, in pixels.", @@ -2814,14 +3849,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "line-dasharray": { "type": "array", "value": "number", - "function": "piecewise-constant", - "zoom-function": true, - "doc": "Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width.", + "doc": "Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels.", "minimum": 0, "transition": true, "units": "line widths", @@ -2838,14 +3879,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "cross-faded" }, "line-pattern": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, "transition": true, - "doc": "Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).", + "doc": "Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.", "sdk-support": { "basic functionality": { "js": "0.10.0", @@ -2854,7 +3900,46 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "cross-faded" + }, + "line-gradient": { + "type": "color", + "doc": "Defines a gradient with which to color a line feature. Can only be used with GeoJSON sources that specify `\"lineMetrics\": true`.", + "transition": false, + "requires": [ + { + "!": "line-dasharray" + }, + { + "!": "line-pattern" + }, + { + "source": "geojson", + "has": { + "lineMetrics": true + } + } + ], + "sdk-support": { + "basic functionality": { + "js": "0.45.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "line-progress" + ] + }, + "property-type": "color-ramp" } }, "paint_circle": { @@ -2862,9 +3947,6 @@ "type": "number", "default": 5, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Circle radius.", @@ -2881,15 +3963,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-color": { "type": "color", "default": "#000000", "doc": "The fill color of the circle.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -2904,15 +3991,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-blur": { "type": "number", "default": 0, "doc": "Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -2927,7 +4019,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-opacity": { "type": "number", @@ -2935,9 +4035,6 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -2952,15 +4049,24 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-translate": { "type": "array", "value": "number", "length": 2, - "default": [0, 0], - "function": "interpolated", - "zoom-function": true, + "default": [ + 0, + 0 + ], "transition": true, "units": "pixels", "doc": "The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.", @@ -2972,18 +4078,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "circle-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The circle is translated relative to the map." + "doc": "The circle is translated relative to the map." }, "viewport": { - "doc": "The circle is translated relative to the viewport." + "doc": "The circle is translated relative to the viewport." } }, "doc": "Controls the frame of reference for `circle-translate`.", @@ -2999,18 +4110,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "circle-pitch-scale": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "Circles are scaled according to their apparent distance to the camera." + "doc": "Circles are scaled according to their apparent distance to the camera." }, "viewport": { - "doc": "Circles are not scaled." + "doc": "Circles are not scaled." } }, "default": "map", @@ -3023,18 +4139,23 @@ "macos": "0.2.1" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "circle-pitch-alignment": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The circle is aligned to the plane of the map." + "doc": "The circle is aligned to the plane of the map." }, "viewport": { - "doc": "The circle is aligned to the plane of the viewport." + "doc": "The circle is aligned to the plane of the viewport." } }, "default": "viewport", @@ -3047,15 +4168,19 @@ "macos": "0.6.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "circle-stroke-width": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "The width of the circle's stroke. Strokes are placed outside of the `circle-radius`.", @@ -3072,15 +4197,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-stroke-color": { "type": "color", "default": "#000000", "doc": "The stroke color of the circle.", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3095,7 +4225,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "circle-stroke-opacity": { "type": "number", @@ -3103,9 +4241,6 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3120,7 +4255,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" } }, "paint_heatmap": { @@ -3128,77 +4271,125 @@ "type": "number", "default": 30, "minimum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": false, "transition": true, "units": "pixels", "doc": "Radius of influence of one heatmap point in pixels. Increasing the value makes the heatmap smoother, but less detailed.", "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" }, - "data-driven styling": {} - } + "data-driven styling": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "heatmap-weight": { "type": "number", "default": 1, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": false, "doc": "A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.", "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" }, "data-driven styling": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "heatmap-intensity": { "type": "number", "default": 1, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": false, "transition": true, "doc": "Similar to `heatmap-weight` but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.", "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "heatmap-color": { "type": "color", "default": [ "interpolate", - ["linear"], - ["heatmap-density"], - 0, "rgba(0, 0, 255, 0)", - 0.1, "royalblue", - 0.3, "cyan", - 0.5, "lime", - 0.7, "yellow", - 1, "red" + [ + "linear" + ], + [ + "heatmap-density" + ], + 0, + "rgba(0, 0, 255, 0)", + 0.1, + "royalblue", + 0.3, + "cyan", + 0.5, + "lime", + 0.7, + "yellow", + 1, + "red" ], "doc": "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `[\"heatmap-density\"]` as input.", - "function": "interpolated", - "zoom-function": false, - "property-function": false, - "transition": true, + "transition": false, "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "heatmap-density" + ] + }, + "property-type": "color-ramp" }, "heatmap-opacity": { "type": "number", @@ -3206,16 +4397,23 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": false, "transition": true, "sdk-support": { "basic functionality": { - "js": "0.41.0" + "js": "0.41.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" } }, "paint_symbol": { @@ -3225,9 +4423,6 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ "icon-image" @@ -3245,14 +4440,19 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-color": { "type": "color", "default": "#000000", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "doc": "The color of the icon. This can only be used with sdf icons.", "requires": [ @@ -3271,14 +4471,19 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-halo-color": { "type": "color", "default": "rgba(0, 0, 0, 0)", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "doc": "The color of the icon's halo. Icon halos can only be used with SDF icons.", "requires": [ @@ -3297,15 +4502,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-halo-width": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Distance of halo to the icon outline.", @@ -3325,15 +4535,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-halo-blur": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Fade out the halo towards the outside.", @@ -3353,7 +4568,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "icon-translate": { "type": "array", @@ -3363,8 +4586,6 @@ 0, 0 ], - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "pixels", "doc": "Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.", @@ -3379,18 +4600,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "icon-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "Icons are translated relative to the map." + "doc": "Icons are translated relative to the map." }, "viewport": { - "doc": "Icons are translated relative to the viewport." + "doc": "Icons are translated relative to the viewport." } }, "doc": "Controls the frame of reference for `icon-translate`.", @@ -3407,7 +4633,14 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-opacity": { "type": "number", @@ -3415,9 +4648,6 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ "text-field" @@ -3435,15 +4665,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-color": { "type": "color", "doc": "The color with which the text will be drawn.", "default": "#000000", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "requires": [ "text-field" @@ -3461,14 +4696,19 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-halo-color": { "type": "color", "default": "rgba(0, 0, 0, 0)", - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "doc": "The color of the text's halo, which helps it stand out from backgrounds.", "requires": [ @@ -3487,15 +4727,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-halo-width": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.", @@ -3515,15 +4760,20 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-halo-blur": { "type": "number", "default": 0, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "property-function": true, "transition": true, "units": "pixels", "doc": "The halo's fadeout distance towards the outside.", @@ -3543,7 +4793,15 @@ "ios": "3.5.0", "macos": "0.4.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature" + ] + }, + "property-type": "data-driven" }, "text-translate": { "type": "array", @@ -3553,8 +4811,6 @@ 0, 0 ], - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "pixels", "doc": "Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.", @@ -3569,18 +4825,23 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "text-translate-anchor": { "type": "enum", - "function": "piecewise-constant", - "zoom-function": true, "values": { "map": { - "doc": "The text is translated relative to the map." + "doc": "The text is translated relative to the map." }, "viewport": { - "doc": "The text is translated relative to the viewport." + "doc": "The text is translated relative to the viewport." } }, "doc": "Controls the frame of reference for `text-translate`.", @@ -3597,7 +4858,14 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" } }, "paint_raster": { @@ -3607,8 +4875,6 @@ "default": 1, "minimum": 0, "maximum": 1, - "function": "interpolated", - "zoom-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3618,14 +4884,19 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-hue-rotate": { "type": "number", "default": 0, "period": 360, - "function": "interpolated", - "zoom-function": true, "transition": true, "units": "degrees", "doc": "Rotates hues around the color wheel.", @@ -3637,12 +4908,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-brightness-min": { "type": "number", - "function": "interpolated", - "zoom-function": true, "doc": "Increase or reduce the brightness of the image. The value is the minimum brightness.", "default": 0, "minimum": 0, @@ -3656,12 +4932,17 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-brightness-max": { "type": "number", - "function": "interpolated", - "zoom-function": true, "doc": "Increase or reduce the brightness of the image. The value is the maximum brightness.", "default": 1, "minimum": 0, @@ -3675,7 +4956,14 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-saturation": { "type": "number", @@ -3683,8 +4971,6 @@ "default": 0, "minimum": -1, "maximum": 1, - "function": "interpolated", - "zoom-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3694,7 +4980,14 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-contrast": { "type": "number", @@ -3702,8 +4995,6 @@ "default": 0, "minimum": -1, "maximum": 1, - "function": "interpolated", - "zoom-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3713,15 +5004,20 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "raster-fade-duration": { "type": "number", "default": 300, "minimum": 0, - "function": "interpolated", - "zoom-function": true, - "transition": true, + "transition": false, "units": "milliseconds", "doc": "Fade duration when a new tile is added.", "sdk-support": { @@ -3732,7 +5028,159 @@ "macos": "0.1.0" }, "data-driven styling": {} - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + } + }, + "paint_hillshade": { + "hillshade-illumination-direction": { + "type": "number", + "default": 335, + "minimum": 0, + "maximum": 359, + "doc": "The direction of the light source used to generate the hillshading with 0 as the top of the viewport if `hillshade-illumination-anchor` is set to `viewport` and due north if `hillshade-illumination-anchor` is set to `map`.", + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "hillshade-illumination-anchor": { + "type": "enum", + "values": { + "map": { + "doc": "The hillshade illumination is relative to the north direction." + }, + "viewport": { + "doc": "The hillshade illumination is relative to the top of the viewport." + } + }, + "default": "viewport", + "doc": "Direction of light source when map is rotated.", + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "hillshade-exaggeration": { + "type": "number", + "doc": "Intensity of the hillshade", + "default": 0.5, + "minimum": 0, + "maximum": 1, + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "hillshade-shadow-color": { + "type": "color", + "default": "#000000", + "doc": "The shading color of areas that face away from the light source.", + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "hillshade-highlight-color": { + "type": "color", + "default": "#FFFFFF", + "doc": "The shading color of areas that faces towards the light source.", + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" + }, + "hillshade-accent-color": { + "type": "color", + "default": "#000000", + "doc": "The shading color used to accentuate rugged terrain like sharp cliffs and gorges.", + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "0.43.0", + "android": "6.0.0", + "ios": "4.0.0", + "macos": "0.7.0" + }, + "data-driven styling": {} + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" } }, "paint_background": { @@ -3740,8 +5188,6 @@ "type": "color", "default": "#000000", "doc": "The color with which the background will be drawn.", - "function": "interpolated", - "zoom-function": true, "transition": true, "requires": [ { @@ -3755,14 +5201,19 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" }, "background-pattern": { "type": "string", - "function": "piecewise-constant", - "zoom-function": true, "transition": true, - "doc": "Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).", + "doc": "Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.", "sdk-support": { "basic functionality": { "js": "0.10.0", @@ -3770,7 +5221,14 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "property-type": "cross-faded" }, "background-opacity": { "type": "number", @@ -3778,8 +5236,6 @@ "minimum": 0, "maximum": 1, "doc": "The opacity at which the background will be drawn.", - "function": "interpolated", - "zoom-function": true, "transition": true, "sdk-support": { "basic functionality": { @@ -3788,7 +5244,14 @@ "ios": "2.0.0", "macos": "0.1.0" } - } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "property-type": "data-constant" } }, "transition": { @@ -3806,5 +5269,31 @@ "units": "milliseconds", "doc": "Length of time before a transition begins." } + }, + "property-type": { + "data-driven": { + "type": "property-type", + "doc": "Property is interpolable and can be represented using a property expression." + }, + "cross-faded": { + "type": "property-type", + "doc": "Property is non-interpolable; rather, its values will be cross-faded to smoothly transition between integer zooms." + }, + "cross-faded-data-driven": { + "type": "property-type", + "doc": "Property is non-interpolable; rather, its values will be cross-faded to smoothly transition between integer zooms. It can be represented using a property expression." + }, + "color-ramp": { + "type": "property-type", + "doc": "Property should be specified using a color ramp from which the output color can be sampled based on a property calculation." + }, + "data-constant": { + "type": "property-type", + "doc": "Property is interpolable but cannot be represented using a property expression." + }, + "constant": { + "type": "property-type", + "doc": "Property is constant across all zoom levels and property values." + } } }