From d3c5f9ca46675af8324eb3e83ee9146a9986cf89 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 1 Sep 2017 12:00:10 -0700 Subject: [PATCH] Removed old projects after rebase --- .../ReactNativeMapboxGLModule.java | 651 -------------- .../ReactNativeMapboxGLPackage.java | 36 - ios/RCTMapboxGL/RCTMapboxGL.m | 774 ----------------- ios/RCTMapboxGL/RCTMapboxGLManager.m | 818 ------------------ 4 files changed, 2279 deletions(-) delete mode 100644 android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLModule.java delete mode 100644 android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLPackage.java delete mode 100644 ios/RCTMapboxGL/RCTMapboxGL.m delete mode 100644 ios/RCTMapboxGL/RCTMapboxGLManager.m diff --git a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLModule.java b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLModule.java deleted file mode 100644 index ceaaeb6..0000000 --- a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLModule.java +++ /dev/null @@ -1,651 +0,0 @@ - -package com.mapbox.reactnativemapboxgl; - -import android.os.Handler; -import android.util.Log; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.JSApplicationCausedNativeException; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; -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.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.modules.core.RCTNativeAppEventEmitter; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.constants.MyBearingTracking; -import com.mapbox.mapboxsdk.constants.MyLocationTracking; -import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.offline.OfflineManager; -import com.mapbox.mapboxsdk.offline.OfflineRegion; -import com.mapbox.mapboxsdk.offline.OfflineRegionError; -import com.mapbox.mapboxsdk.offline.OfflineRegionStatus; -import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; -import com.mapbox.services.android.telemetry.MapboxTelemetry; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Nullable; - -public class ReactNativeMapboxGLModule extends ReactContextBaseJavaModule { - - private static final String TAG = ReactNativeMapboxGLModule.class.getSimpleName(); - - private static final int ANDROID_SDK_OFFLINE_PACK_STATE_INACTIVE = 0; - private static final int ANDROID_SDK_OFFLINE_PACK_STATE_ACTIVE = 1; - - private static final int OFFLINE_PACK_STATE_UNKNOWN = 0; - private static final int OFFLINE_PACK_STATE_INACTIVE = 1; - private static final int OFFLINE_PACK_STATE_ACTIVE = 2; - private static final int OFFLINE_PACK_STATE_COMPLETE = 3; - - private ReactApplicationContext context; - private ReactNativeMapboxGLPackage aPackage; - Handler mainHandler; - private int throttleInterval = 300; - - private static boolean initialized = false; - - public ReactNativeMapboxGLModule(ReactApplicationContext reactContext, ReactNativeMapboxGLPackage thePackage) { - super(reactContext); - this.mainHandler = new Handler(reactContext.getApplicationContext().getMainLooper()); - this.context = reactContext; - this.aPackage = thePackage; - Log.d(TAG, "Context " + context); - Log.d(TAG, "reactContext " + reactContext); - } - - @Override - public String getName() { - return "MapboxGLManager"; - } - - static private ArrayList serializeTracking(int locationTracking, int bearingTracking) { - ArrayList result = new ArrayList(); - result.add(locationTracking); - result.add(bearingTracking); - return result; - } - - public static final int[] locationTrackingModes = new int[] { - MyLocationTracking.TRACKING_NONE, - MyLocationTracking.TRACKING_FOLLOW, - MyLocationTracking.TRACKING_FOLLOW, - MyLocationTracking.TRACKING_FOLLOW - }; - - public static final int[] bearingTrackingModes = new int[] { - MyBearingTracking.NONE, - MyBearingTracking.NONE, - MyBearingTracking.GPS, - MyBearingTracking.COMPASS - }; - - @Override - public @Nullable Map getConstants() { - HashMap constants = new HashMap(); - - HashMap userTrackingMode = new HashMap(); - HashMap mapStyles = new HashMap(); - HashMap userLocationVerticalAlignment = new HashMap(); - HashMap offlinePackState = new HashMap(); - - // User tracking constants - userTrackingMode.put("none", 0); - userTrackingMode.put("follow", 1); - userTrackingMode.put("followWithCourse", 2); - userTrackingMode.put("followWithHeading", 3); - - // Style constants - mapStyles.put("light", Style.LIGHT); - mapStyles.put("dark", Style.DARK); - mapStyles.put("streets", Style.MAPBOX_STREETS); - mapStyles.put("outdoors", Style.OUTDOORS); - mapStyles.put("satellite", Style.SATELLITE); - mapStyles.put("hybrid", Style.SATELLITE_STREETS); - - // These need to be here for compatibility, even if they're not supported on Android - userLocationVerticalAlignment.put("center", 0); - userLocationVerticalAlignment.put("top", 1); - userLocationVerticalAlignment.put("bottom", 2); - - // Offline Pack State constants - offlinePackState.put("unknown", 0); - offlinePackState.put("inactive", 1); - offlinePackState.put("active", 2); - offlinePackState.put("complete", 3); - offlinePackState.put("invalid", 4); - - // Other constants - constants.put("unknownResourceCount", Long.MAX_VALUE); - - constants.put("userTrackingMode", userTrackingMode); - constants.put("mapStyles", mapStyles); - constants.put("userLocationVerticalAlignment", userLocationVerticalAlignment); - constants.put("offlinePackState", offlinePackState); - - return constants; - } - - // Access Token - - @ReactMethod - public void setAccessToken(final String accessToken, final Promise promise) { - if (accessToken == null || accessToken.length() == 0 || accessToken.equals("your-mapbox.com-access-token")) { - throw new JSApplicationIllegalArgumentException("Invalid access token. Register to mapbox.com and request an access token, then pass it to setAccessToken()"); - } - if (initialized) { - String oldToken = Mapbox.getAccessToken(); - if (!oldToken.equals(accessToken)) { - JSApplicationIllegalArgumentException error = - new JSApplicationIllegalArgumentException("Mapbox access token cannot be initialized twice with different values"); - promise.reject(error); - throw error; - } - promise.resolve(null); - return; - } - initialized = true; - mainHandler.post(new Runnable() { - @Override - public void run() { - Mapbox.getInstance(context.getApplicationContext(), accessToken); - promise.resolve(null); - } - }); - } - - // Connected - @ReactMethod - public void setConnected(boolean connected) { - Mapbox.getInstance(context.getApplicationContext(), Mapbox.getAccessToken()).setConnected(connected); - } - - // Metrics - - @ReactMethod - public void getMetricsEnabled(final Promise promise) { - try { - promise.resolve(MapboxTelemetry.getInstance().isTelemetryEnabled()); - } catch (NullPointerException e) { - promise.reject(new JSApplicationCausedNativeException("You should call getMetricsEnabled after setAccessToken")); - } - } - - @ReactMethod - public void setMetricsEnabled(boolean value) { - MapboxTelemetry.getInstance().setTelemetryEnabled(value); - } - - // Offline packs - - // Offline pack events and initialization - - class OfflineRegionProgressObserver implements OfflineRegion.OfflineRegionObserver { - ReactNativeMapboxGLModule module; - OfflineRegion region; - OfflineRegionStatus status; - String name; - boolean recentlyUpdated = false; - boolean throttled = true; - boolean invalid = false; - - OfflineRegionProgressObserver(ReactNativeMapboxGLModule module, OfflineRegion region, String name) { - this.module = module; - this.region = region; - if (name == null) { - this.name = getOfflineRegionName(region); - } else { - this.name = name; - } - } - - void fireUpdateEvent() { - if (invalid) { return; } - - recentlyUpdated = true; - WritableMap event = serializeOfflineRegionStatus(region, this.status); - module.getReactApplicationContext().getJSModule(RCTNativeAppEventEmitter.class) - .emit("MapboxOfflineProgressDidChange", event); - - module.mainHandler.postDelayed(new Runnable() { - @Override - public void run() { - recentlyUpdated = false; - if (throttled) { - throttled = false; - fireUpdateEvent(); - } - } - }, throttleInterval); - } - - @Override - public void onStatusChanged(OfflineRegionStatus status) { - if (invalid) { return; } - - this.status = status; - - if (!recentlyUpdated) { - fireUpdateEvent(); - } else { - throttled = true; - } - } - - @Override - public void onError(OfflineRegionError error) { - if (invalid) { return; } - - WritableMap event = Arguments.createMap(); - event.putString("name", getOfflineRegionName(region)); - event.putString("error", error.toString()); - - module.getReactApplicationContext().getJSModule(RCTNativeAppEventEmitter.class) - .emit("MapboxOfflineError", event); - } - - @Override - public void mapboxTileCountLimitExceeded(long limit) { - if (invalid) { return; } - - WritableMap event = Arguments.createMap(); - event.putString("name", getOfflineRegionName(region)); - event.putDouble("maxTiles", limit); - - module.getReactApplicationContext().getJSModule(RCTNativeAppEventEmitter.class) - .emit("MapboxOfflineMaxAllowedTiles", event); - } - - public void invalidate() { - invalid = true; - } - } - - private int uninitializedObserverCount = -1; - private ArrayList offlinePackObservers = new ArrayList<>(); - private ArrayList offlinePackListingRequests = new ArrayList<>(); - - void flushListingRequests() { - WritableArray result = _getOfflinePacks(); - for (Promise promise : offlinePackListingRequests) { - promise.resolve(result); - } - offlinePackListingRequests.clear(); - } - - class OfflineRegionsInitialRequest implements OfflineManager.ListOfflineRegionsCallback { - private final ReactNativeMapboxGLModule module; - - OfflineRegionsInitialRequest(ReactNativeMapboxGLModule module) { - this.module = module; - } - - @Override - public void onList(OfflineRegion[] offlineRegions) { - uninitializedObserverCount = offlineRegions.length; - for (OfflineRegion region : offlineRegions) { - final OfflineRegionProgressObserver observer = new OfflineRegionProgressObserver(module, region, null); - offlinePackObservers.add(observer); - region.setObserver(observer); - region.setDownloadState(OfflineRegion.STATE_ACTIVE); - region.getStatus(new OfflineRegion.OfflineRegionStatusCallback() { - @Override - public void onStatus(OfflineRegionStatus status) { - observer.onStatusChanged(status); - uninitializedObserverCount--; - if (uninitializedObserverCount == 0) { - flushListingRequests(); - } - } - @Override - public void onError(String error) { - Log.e(context.getApplicationContext().getPackageName(), error); - } - }); - } - - module.context - .getJSModule(RCTNativeAppEventEmitter.class) - .emit("MapboxOfflinePacksLoaded", null); - } - - @Override - public void onError(String error) { - Log.e(module.getReactApplicationContext().getPackageName(), error); - } - } - - @ReactMethod - void initializeOfflinePacks() { - final ReactNativeMapboxGLModule _this = this; - mainHandler.post(new Runnable() { - @Override - public void run() { - OfflineManager.getInstance(context.getApplicationContext()).listOfflineRegions( - new OfflineRegionsInitialRequest(_this) - ); - } - }); - - } - - // Offline pack utils - - static WritableMap serializeOfflineRegionStatus(OfflineRegion region, OfflineRegionStatus status) { - WritableMap result = Arguments.createMap(); - - try { - ByteArrayInputStream bis = new ByteArrayInputStream(region.getMetadata()); - ObjectInputStream ois = new ObjectInputStream(bis); - - result.putString("name", (String)ois.readObject()); - result.putString("metadata", (String)ois.readObject()); - - ois.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - - result.putInt("state", normalizeOfflineRegionState(status)); - result.putInt("countOfBytesCompleted", (int)status.getCompletedResourceSize()); - result.putInt("countOfResourcesCompleted", (int)status.getCompletedResourceCount()); - result.putInt("countOfResourcesExpected", (int)status.getRequiredResourceCount()); - result.putInt("maximumResourcesExpected", (int)status.getRequiredResourceCount()); - - return result; - } - - /* - * Normalizes offline region status state for the sake of parity with iOS for React Native - * Essentially we force Android state to be the same as iOS state for ease of cross-platform development - * - * On iOS: - * 0: Unknown - * 1: Inactive - * 2: Active - * 3: Complete - * 4: Invalid (iOS ONLY) - * - * On Android: - * 0: Inactive (Complete is inactive, AND countOfResourcesCompleted == countOfResourcesExpected) - * 1: Active - */ - static int normalizeOfflineRegionState(OfflineRegionStatus status) { - int state = (int)status.getDownloadState(); - boolean isComplete = (boolean)status.isComplete(); - - switch (state) { - case ANDROID_SDK_OFFLINE_PACK_STATE_INACTIVE: - if (isComplete) { - state = OFFLINE_PACK_STATE_COMPLETE; - } else { - state = OFFLINE_PACK_STATE_INACTIVE; - } - break; - case ANDROID_SDK_OFFLINE_PACK_STATE_ACTIVE: - state = OFFLINE_PACK_STATE_ACTIVE; - break; - default: - state = OFFLINE_PACK_STATE_UNKNOWN; - } - - return state; - } - - static String getOfflineRegionName(OfflineRegion region) { - try { - ByteArrayInputStream bis = new ByteArrayInputStream(region.getMetadata()); - ObjectInputStream ois = new ObjectInputStream(bis); - String name = (String)ois.readObject(); - ois.close(); - return name; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - // Offline pack listing - - WritableArray _getOfflinePacks() { - WritableArray result = Arguments.createArray(); - for (OfflineRegionProgressObserver observer : offlinePackObservers) { - result.pushMap(serializeOfflineRegionStatus(observer.region, observer.status)); - } - return result; - } - - @ReactMethod - public void getOfflinePacks(final Promise promise) { - mainHandler.post(new Runnable() { - @Override - public void run() { - promise.resolve(_getOfflinePacks()); - } - }); - } - - // Offline pack insertion - - @ReactMethod - public void addOfflinePack(ReadableMap options, final Promise promise) { - if (!options.hasKey("name")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): name is required.")); - return; - } - if (!options.hasKey("minZoomLevel")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): minZoomLevel is required.")); - return; - } - if (!options.hasKey("maxZoomLevel")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): maxZoomLevel is required.")); - return; - } - if (!options.hasKey("bounds")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): bounds is required.")); - return; - } - if (!options.hasKey("styleURL")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): styleURL is required.")); - return; - } - if (!options.hasKey("type")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): type is required.")); - return; - } - if (!options.getString("type").equals("bbox")) { - promise.reject(new JSApplicationIllegalArgumentException("addOfflinePack(): Offline pack type " + - options.getString("type") + - " not supported. Only \"bbox\" is currently supported.")); - return; - } - - float pixelRatio = context.getResources().getDisplayMetrics().density; - pixelRatio = pixelRatio < 1.5f ? 1.0f : 2.0f; - - ReadableArray boundsArray = options.getArray("bounds"); - LatLngBounds bounds = new LatLngBounds.Builder() - .include(new LatLng(boundsArray.getDouble(0), boundsArray.getDouble(1))) - .include(new LatLng(boundsArray.getDouble(2), boundsArray.getDouble(3))) - .build(); - - final OfflineTilePyramidRegionDefinition regionDef = new OfflineTilePyramidRegionDefinition( - options.getString("styleURL"), - bounds, - options.getDouble("minZoomLevel"), - options.getDouble("maxZoomLevel"), - pixelRatio - ); - - byte [] metadata; - - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(options.getString("name")); - oos.writeObject(options.getString("metadata")); - oos.close(); - metadata = bos.toByteArray(); - } catch (IOException e) { - promise.reject(e); - return; - } - - final ReactNativeMapboxGLModule _this = this; - final byte [] _metadata = metadata; - mainHandler.post(new Runnable() { - @Override - public void run() { - OfflineManager.getInstance(context.getApplicationContext()).createOfflineRegion( - regionDef, - _metadata, - new OfflineManager.CreateOfflineRegionCallback() { - @Override - public void onCreate(OfflineRegion offlineRegion) { - OfflineRegionProgressObserver observer = new OfflineRegionProgressObserver(_this, offlineRegion, null); - offlinePackObservers.add(observer); - offlineRegion.setObserver(observer); - offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE); - promise.resolve(null); - } - - @Override - public void onError(String error) { - promise.reject(new JSApplicationIllegalArgumentException(error)); - } - } - ); - } - }); - } - - // Offline pack removal - - @ReactMethod - public void removeOfflinePack(final String packName, final Promise promise) { - mainHandler.post(new Runnable() { - @Override - public void run() { - final OfflineRegionProgressObserver foundObserver = getObserver(packName); - - if (foundObserver == null) { - promise.resolve(Arguments.createMap()); - return; - } - - offlinePackObservers.remove(foundObserver); - foundObserver.invalidate(); - foundObserver.region.setDownloadState(OfflineRegion.STATE_INACTIVE); - foundObserver.region.delete(new OfflineRegion.OfflineRegionDeleteCallback() { - @Override - public void onDelete() { - WritableMap result = Arguments.createMap(); - result.putString("deleted", foundObserver.name); - promise.resolve(result); - } - - @Override - public void onError(String error) { - promise.reject(new JSApplicationIllegalArgumentException(error)); - } - }); - } - }); - } - - @ReactMethod - public void suspendOfflinePack(final String packName, final Promise promise) { - mainHandler.post(new Runnable() { - @Override - public void run() { - final OfflineRegionProgressObserver foundObserver = getObserver(packName); - - if (foundObserver == null) { - promise.resolve(Arguments.createMap()); - return; - } - - foundObserver.region.setDownloadState(OfflineRegion.STATE_INACTIVE); - foundObserver.region.getStatus(new OfflineRegion.OfflineRegionStatusCallback() { - @Override - public void onStatus(OfflineRegionStatus status) { - foundObserver.onStatusChanged(status); - WritableMap result = Arguments.createMap(); - result.putString("suspended", foundObserver.name); - promise.resolve(result); - } - @Override - public void onError(String error) { - Log.e(context.getApplicationContext().getPackageName(), error); - promise.reject(new JSApplicationIllegalArgumentException(error)); - } - }); - } - }); - } - - @ReactMethod - public void resumeOfflinePack(final String packName, final Promise promise) { - mainHandler.post(new Runnable() { - @Override - public void run() { - final OfflineRegionProgressObserver foundObserver = getObserver(packName); - - if (foundObserver == null) { - promise.resolve(Arguments.createMap()); - return; - } - - foundObserver.region.setDownloadState(OfflineRegion.STATE_ACTIVE); - foundObserver.region.getStatus(new OfflineRegion.OfflineRegionStatusCallback() { - @Override - public void onStatus(OfflineRegionStatus status) { - foundObserver.onStatusChanged(status); - WritableMap result = Arguments.createMap(); - result.putString("resumed", foundObserver.name); - promise.resolve(result); - } - @Override - public void onError(String error) { - Log.e(context.getApplicationContext().getPackageName(), error); - promise.reject(new JSApplicationIllegalArgumentException(error)); - } - }); - } - }); - } - - OfflineRegionProgressObserver getObserver(String name) { - OfflineRegionProgressObserver foundObserver = null; - - for (OfflineRegionProgressObserver observer : offlinePackObservers) { - if (name.equals(observer.name)) { - foundObserver = observer; - break; - } - } - - return foundObserver; - } - - // Offline throttle control - - @ReactMethod - public void setOfflinePackProgressThrottleInterval(int milis) { - throttleInterval = milis; - } -} diff --git a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLPackage.java b/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLPackage.java deleted file mode 100644 index 92fb148..0000000 --- a/android/src/main/java/com/mapbox/reactnativemapboxgl/ReactNativeMapboxGLPackage.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.mapbox.reactnativemapboxgl; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class ReactNativeMapboxGLPackage implements ReactPackage { - - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - List modules = new ArrayList<>(); - ReactNativeMapboxGLModule module = new ReactNativeMapboxGLModule(reactContext, this); - modules.add(module); - return modules; - } - -// Deprecated RN 0.47 - public List> createJSModules() { - return Collections.emptyList(); - } - - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Arrays.asList( - new ReactNativeMapboxGLManager(reactContext), - new RNMGLAnnotationViewManager() - ); - } -} diff --git a/ios/RCTMapboxGL/RCTMapboxGL.m b/ios/RCTMapboxGL/RCTMapboxGL.m deleted file mode 100644 index 66d491d..0000000 --- a/ios/RCTMapboxGL/RCTMapboxGL.m +++ /dev/null @@ -1,774 +0,0 @@ -// -// RCTMapboxGL.m -// RCTMapboxGL -// -// Created by Bobby Sudekum on 4/30/15. -// Copyright (c) 2015 Mapbox. All rights reserved. -// - -#import "RCTMapboxGL.h" -#import -#import -#import -#import -#import "RCTMapboxGLConversions.h" -#import "RCTMapboxAnnotation.h" - -@implementation RCTMapboxGL { - /* Required to publish events */ - RCTEventDispatcher *_eventDispatcher; - - /* Our map subview instance */ - MGLMapView *_map; - - /* Map properties */ - NSMutableDictionary *_annotations; - CLLocationCoordinate2D _initialCenterCoordinate; - double _initialDirection; - double _initialZoomLevel; - BOOL _zoomEnabled; - double _minimumZoomLevel; - double _maximumZoomLevel; - BOOL _clipsToBounds; - BOOL _debugActive; - BOOL _finishedLoading; - BOOL _rotateEnabled; - BOOL _scrollEnabled; - BOOL _pitchEnabled; - BOOL _showsUserLocation; - NSURL *_styleURL; - int _userTrackingMode; - BOOL _attributionButton; - BOOL _logo; - BOOL _compass; - UIEdgeInsets _contentInset; - MGLAnnotationVerticalAlignment _userLocationVerticalAlignment; - /* So we don't fire onChangeUserTracking mode when triggered by props */ - BOOL _isChangingUserTracking; - NSMutableArray *_reactSubviews; -} - -// View creation - -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher -{ - if (self = [super init]) { - _eventDispatcher = eventDispatcher; - _clipsToBounds = YES; - _finishedLoading = NO; - _annotations = [NSMutableDictionary dictionary]; - _reactSubviews = [NSMutableArray new]; - } - - return self; -} - -- (void)createMapIfNeeded -{ - CGRect bounds = self.bounds; - if (_map || - !_styleURL || - bounds.size.width <= 0 || bounds.size.height <= 0 - ) { - return; - } - - _map = [[MGLMapView alloc] initWithFrame:self.bounds]; - _map.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _map.delegate = self; - - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; - [self addGestureRecognizer:longPress]; - - UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; - singleTap.delegate = self; - [_map addGestureRecognizer:singleTap]; - - _map.centerCoordinate = _initialCenterCoordinate; - _map.clipsToBounds = _clipsToBounds; - _map.debugActive = _debugActive; - _map.direction = _initialDirection; - _map.rotateEnabled = _rotateEnabled; - _map.scrollEnabled = _scrollEnabled; - _map.zoomEnabled = _zoomEnabled; - _map.pitchEnabled = _pitchEnabled; - _map.minimumZoomLevel = _minimumZoomLevel; - _map.maximumZoomLevel = _maximumZoomLevel; - _map.showsUserLocation = _showsUserLocation; - _map.styleURL = _styleURL; - _map.zoomLevel = _initialZoomLevel; - _map.contentInset = _contentInset; - [_map.attributionButton setHidden:_attributionButton]; - [_map.logoView setHidden:_logo]; - [_map.compassView setHidden:_compass]; - _map.userLocationVerticalAlignment = _userLocationVerticalAlignment; - _isChangingUserTracking = YES; - _map.userTrackingMode = _userTrackingMode; - _isChangingUserTracking = NO; - for (NSString * annotationId in _annotations) { - [_map addAnnotation:_annotations[annotationId]]; - } - - [self addSubview:_map]; - for (UIView *annotation in _reactSubviews) { - [_map addAnnotation:(RCTMapboxAnnotation *)annotation]; - } - - [self layoutSubviews]; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer -{ - return YES; -} - -- (void)layoutSubviews -{ - if (!_map) { - [self createMapIfNeeded]; - } - _map.frame = self.bounds; - [_map layoutSubviews]; -} - -// React subviews for custom annotation management -- (void)insertReactSubview:(id)subview atIndex:(NSInteger)atIndex { - // Our desired API is to pass up markers/overlays as children to the mapview component. - // This is where we intercept them and do the appropriate underlying mapview action. - if ([subview isKindOfClass:[RCTMapboxAnnotation class]]) { - RCTMapboxAnnotation * annotation = (RCTMapboxAnnotation *) subview; - annotation.map = self; - [_map addAnnotation:annotation]; - [_reactSubviews insertObject:annotation atIndex:atIndex]; - } -} - -- (void)removeReactSubview:(id)subview { - // similarly, when the children are being removed we have to do the appropriate - // underlying mapview action here. - if ([subview isKindOfClass:[RCTMapboxAnnotation class]]) { - RCTMapboxAnnotation * annotation = (RCTMapboxAnnotation *) subview; - [_map removeAnnotation:annotation]; - [_reactSubviews removeObject:annotation]; - } -} - -- (NSArray> *)reactSubviews { - return _reactSubviews; -} - - - -// Annotation management - -- (void)upsertAnnotation:(RCTMGLAnnotation *) annotation { - NSString * identifier = [annotation id]; - if (!identifier || [identifier length] == 0) { - RCTLogError(@"field `id` is required on all annotations"); - return; - } - - RCTMGLAnnotation * oldAnnotation = [_annotations objectForKey:identifier]; - [_annotations setObject:annotation forKey:identifier]; - [_map addAnnotation:annotation]; - if (oldAnnotation) { - [_map removeAnnotation:oldAnnotation]; - } -} - -- (void)removeAnnotation:(NSString*)selectedIdentifier -{ - RCTMGLAnnotation * annotation = [_annotations objectForKey:selectedIdentifier]; - if (!annotation) { return; } - [_map removeAnnotation:annotation]; - [_annotations removeObjectForKey:selectedIdentifier]; -} - -- (void)removeAllAnnotations -{ - [_map removeAnnotations:_map.annotations]; - [_annotations removeAllObjects]; -} - -- (void)deselectAnnotation -{ - NSArray * annotations = [_map selectedAnnotations]; - if (!annotations) { return; } - for (id annotation in annotations) { - [_map deselectAnnotation:annotation animated:YES]; - } -} - -- (void)restoreAnnotationPosition:(NSString *)annotationId { - for (UIView *annotation in _reactSubviews) { - if ([annotation isKindOfClass:[RCTMapboxAnnotation class]] && ((RCTMapboxAnnotation *) annotation).reuseIdentifier == annotationId) { - CGPoint point = [_map convertCoordinate:((RCTMapboxAnnotation *) annotation).coordinate toPointToView:_map]; - annotation.center = point; - return; - } - } -} -- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(RCTMGLAnnotationPolyline *)shape -{ - if ([shape isKindOfClass:[RCTMGLAnnotationPolyline class]]) { - return shape.strokeAlpha; - } else if ([shape isKindOfClass:[RCTMGLAnnotationPolygon class]]) { - return [(RCTMGLAnnotationPolygon *) shape fillAlpha]; - } else { - return 1.0; - } -} - -- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(RCTMGLAnnotationPolyline *)shape -{ - if ([shape isKindOfClass:[RCTMGLAnnotationPolyline class]]) { - return [self getUIColorObjectFromHexString:shape.strokeColor alpha:1]; - } else if ([shape isKindOfClass:[RCTMGLAnnotationPolygon class]]) { - return [self getUIColorObjectFromHexString:[(RCTMGLAnnotationPolygon *) shape strokeColor] alpha:1]; - } else { - return [UIColor blueColor]; - } -} - -- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(RCTMGLAnnotationPolyline *)shape -{ - return shape.strokeWidth; -} - -- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(RCTMGLAnnotationPolygon *)shape -{ - return [self getUIColorObjectFromHexString:shape.fillColor alpha:1]; -} - -- (BOOL)mapView:(RCTMapboxGL *)mapView annotationCanShowCallout:(id )annotation { - if (!_annotationsPopUpEnabled) { return NO; } - NSString *title = [(RCTMGLAnnotation *) annotation title]; - NSString *subtitle = [(RCTMGLAnnotation *) annotation subtitle]; - return ([title length] != 0 || [subtitle length] != 0); -} - -- (nullable MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id )annotation { - if ([annotation isKindOfClass:[RCTMapboxAnnotation class]] ){ - RCTMapboxAnnotation *customAnnotation = (RCTMapboxAnnotation *)annotation; - MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:customAnnotation.reuseIdentifier]; - if (!annotationView){ - annotationView = customAnnotation; - } - return annotationView; - } - - return nil; -} - -- (UIButton *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(id )annotation; -{ - if ([annotation isKindOfClass:[RCTMGLAnnotation class]]) { - UIButton *accessoryButton = [(RCTMGLAnnotation *) annotation rightCalloutAccessory]; - return accessoryButton; - } - return nil; -} - -- (void)mapView:(MGLMapView *)mapView annotation:(id)annotation calloutAccessoryControlTapped:(UIControl *)control -{ - if (annotation.title && annotation.subtitle) { - - NSString *id = [(RCTMGLAnnotation *) annotation id]; - - NSDictionary *event = @{ @"target": self.reactTag, - @"src": @{ @"title": annotation.title, - @"subtitle": annotation.subtitle, - @"id": id, - @"latitude": @(annotation.coordinate.latitude), - @"longitude": @(annotation.coordinate.longitude)} }; - - [_eventDispatcher sendInputEventWithName:@"onRightAnnotationTapped" body:event]; - } -} - -- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id)annotation -{ - NSDictionary *source = [(RCTMGLAnnotation *) annotation annotationImageSource]; - if (!source) { return nil; } - - CGSize imageSize = [(RCTMGLAnnotation *) annotation annotationImageSize]; - NSString *reuseIdentifier = source[@"uri"]; - MGLAnnotationImage *annotationImage = [mapView dequeueReusableAnnotationImageWithIdentifier:reuseIdentifier]; - - if (!annotationImage) { - UIImage *image = imageFromSource(source); - UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0); - [image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - annotationImage = [MGLAnnotationImage annotationImageWithImage:newImage reuseIdentifier:reuseIdentifier]; - } - - return annotationImage; -} - -// React props - -- (void)setInitialCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate -{ - _initialCenterCoordinate = centerCoordinate; -} - -- (void)setInitialZoomLevel:(double)zoomLevel -{ - _initialZoomLevel = zoomLevel; -} - -- (void)setInitialDirection:(double)direction -{ - _initialDirection = direction; -} - - -- (void)setClipsToBounds:(BOOL)clipsToBounds -{ - if (_clipsToBounds == clipsToBounds) { return; } - _clipsToBounds = clipsToBounds; - if (_map) { _map.clipsToBounds = clipsToBounds; } -} - -- (void)setDebugActive:(BOOL)debugActive -{ - if (_debugActive == debugActive) { return; } - _debugActive = debugActive; - if (_map) { _map.debugActive = debugActive; } -} - -- (void)setRotateEnabled:(BOOL)rotateEnabled -{ - if (_rotateEnabled == rotateEnabled) { return; } - _rotateEnabled = rotateEnabled; - if (_map) { _map.rotateEnabled = rotateEnabled; } -} - -- (void)setScrollEnabled:(BOOL)scrollEnabled -{ - if (_scrollEnabled == scrollEnabled) { return; } - _scrollEnabled = scrollEnabled; - if (_map) { _map.scrollEnabled = scrollEnabled; } -} - -- (void)setZoomEnabled:(BOOL)zoomEnabled -{ - if (_zoomEnabled == zoomEnabled) { return; } - _zoomEnabled = zoomEnabled; - if (_map) { _map.zoomEnabled = zoomEnabled; } -} - -- (void)setMinimumZoomLevel:(double)minimumZoomLevel -{ - if (_minimumZoomLevel == minimumZoomLevel) { return; } - _minimumZoomLevel = minimumZoomLevel; - if (_map) { _map.minimumZoomLevel = minimumZoomLevel; } -} - -- (void)setMaximumZoomLevel:(double)maximumZoomLevel -{ - if (_maximumZoomLevel == maximumZoomLevel) { return; } - _maximumZoomLevel = maximumZoomLevel; - if (_map) { _map.maximumZoomLevel = maximumZoomLevel; } -} - -- (void)setPitchEnabled:(BOOL)pitchEnabled -{ - if (_pitchEnabled == pitchEnabled) { return; } - _pitchEnabled = pitchEnabled; - if (_map) { _map.pitchEnabled = pitchEnabled; } -} - -- (void)setShowsUserLocation:(BOOL)showsUserLocation -{ - if (_showsUserLocation == showsUserLocation) { return; } - _showsUserLocation = showsUserLocation; - if (_map) { _map.showsUserLocation = showsUserLocation; } -} - -- (void)setStyleURL:(NSURL *)styleURL -{ - if (_styleURL && [styleURL isEqual:_styleURL]) { return; } - _styleURL = styleURL; - if (_map) { - _map.styleURL = styleURL; - } else { - [self createMapIfNeeded]; - } -} - -- (void)setUserTrackingMode:(int)userTrackingMode -{ - if (_userTrackingMode == userTrackingMode) { return; } - if (userTrackingMode > 3 || userTrackingMode < 0) { - _userTrackingMode = 0; - } else { - _userTrackingMode = userTrackingMode; - } - if (_map) { - _isChangingUserTracking = YES; - _map.userTrackingMode = _userTrackingMode; - _isChangingUserTracking = NO; - } -} - -- (void)setAttributionButtonIsHidden:(BOOL)isHidden -{ - if (_attributionButton == isHidden) { return; } - _attributionButton = isHidden; - if (_map) { _map.attributionButton.hidden = isHidden; } -} - -- (void)setLogoIsHidden:(BOOL)isHidden -{ - if (_logo == isHidden) { return; } - _logo = isHidden; - if (_map) { _map.logoView.hidden = isHidden; } -} - -- (void)setCompassIsHidden:(BOOL)isHidden -{ - if (_compass == isHidden) { return; } - _compass = isHidden; - if (_map) { _map.compassView.hidden = isHidden; } -} - -- (void)setContentInset:(UIEdgeInsets)inset -{ - _contentInset = inset; - if (_map) { _map.contentInset = inset; } -} - -- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment -{ - if (_userLocationVerticalAlignment == alignment) { return; } - _userLocationVerticalAlignment = alignment; - if (_map) { _map.userLocationVerticalAlignment = alignment; } -} - -// Getters - -- (MGLCoordinateBounds) visibleCoordinateBounds -{ - return [_map visibleCoordinateBounds]; -} - --(CLLocationCoordinate2D)centerCoordinate { - if (!_map) { return _initialCenterCoordinate; } - return _map.centerCoordinate; -} - --(double)direction { - if (!_map) { return _initialDirection; } - return _map.direction; -} - --(double)pitch { - if (!_map) { return 0; } - return _map.camera.pitch; -} - --(double)zoomLevel { - if (!_map) { return _initialZoomLevel; } - return _map.zoomLevel; -} - --(MGLMapCamera*)camera { - if (!_map) { return nil; } - return _map.camera; -} - -// Imperative methods - -- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate zoomLevel:(double)zoomLevel direction:(double)direction animated:(BOOL)animated completionHandler:(void (^)())callback -{ - if (!_map) { - _initialCenterCoordinate = coordinate; - _initialZoomLevel = zoomLevel; - _initialDirection = direction; - callback(); - return; - } - [_map setCenterCoordinate:coordinate - zoomLevel:zoomLevel - direction:direction - animated:animated - completionHandler:callback]; -} - -- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))handler -{ - [_map setCamera: camera withDuration:duration animationTimingFunction:function completionHandler:handler]; -} - -- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)padding animated:(BOOL)animated -{ - [_map setVisibleCoordinateBounds:bounds edgePadding:padding animated:animated]; -} - -- (void)selectAnnotation:(NSString*)selectedId animated:(BOOL)animated; -{ - RCTMGLAnnotation * annotation = [_annotations objectForKey:selectedId]; - if (!annotation) { return; } - [_map selectAnnotation:annotation animated:animated]; -} - - -// Events - --(void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated -{ - if (_isChangingUserTracking) { return; } - if (!_onChangeUserTrackingMode) { return; } - - _onChangeUserTrackingMode(@{ @"target": self.reactTag, - @"src": @(mode) }); -} - -- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(MGLUserLocation *)userLocation; -{ - if (!_onUpdateUserLocation) { return; } - _onUpdateUserLocation(@{ @"target": self.reactTag, - @"src": @{ @"latitude": @(userLocation.coordinate.latitude), - @"longitude": @(userLocation.coordinate.longitude), - @"verticalAccuracy": @(userLocation.location.verticalAccuracy), - @"horizontalAccuracy": @(userLocation.location.horizontalAccuracy), - @"headingAccuracy": @(userLocation.heading.headingAccuracy), - @"magneticHeading": @(userLocation.heading.magneticHeading), - @"trueHeading": @(userLocation.heading.trueHeading), - @"isUpdating": [NSNumber numberWithBool:userLocation.isUpdating]} }); -} - -- (void)mapView:(MGLMapView *)mapView didFailToLocateUserWithError:(NSError *)error -{ - if (!_onLocateUserFailed) { return; } - _onLocateUserFailed(@{ @"target": self.reactTag, - @"src": @{ @"message": [error localizedDescription] } }); -} - --(void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id)annotation -{ - if (!annotation.title || !annotation.subtitle) { return; } - if (!_onOpenAnnotation) { return; } - _onOpenAnnotation(@{ @"target": self.reactTag, - @"src": @{ @"title": annotation.title, - @"subtitle": annotation.subtitle, - @"id": [(RCTMGLAnnotation *) annotation id], - @"latitude": @(annotation.coordinate.latitude), - @"longitude": @(annotation.coordinate.longitude)} }); -} - --(void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(nonnull id)annotation -{ - if (!annotation.title || !annotation.subtitle) { return; } - if (!_onCloseAnnotation) { return; } - _onCloseAnnotation(@{ @"target": self.reactTag, - @"src": @{ @"title": annotation.title, - @"subtitle": annotation.subtitle, - @"id": [(RCTMGLAnnotation *) annotation id], - @"latitude": @(annotation.coordinate.latitude), - @"longitude": @(annotation.coordinate.longitude)} }); -} - -- (void)mapView:(RCTMapboxGL *)mapView regionDidChangeAnimated:(BOOL)animated -{ - if (!_onRegionDidChange) { return; } - - CLLocationCoordinate2D region = _map.centerCoordinate; - _onRegionDidChange(@{ @"target": self.reactTag, - @"src": @{ @"latitude": @(region.latitude), - @"longitude": @(region.longitude), - @"zoomLevel": @(_map.zoomLevel), - @"direction": @(_map.direction), - @"pitch": @(_map.camera.pitch), - @"animated": @(animated) } }); -} - - -- (void)mapView:(RCTMapboxGL *)mapView regionWillChangeAnimated:(BOOL)animated -{ - if (!_onRegionWillChange) { return; } - - CLLocationCoordinate2D region = _map.centerCoordinate; - _onRegionWillChange(@{ @"target": self.reactTag, - @"src": @{ @"latitude": @(region.latitude), - @"longitude": @(region.longitude), - @"zoomLevel": @(_map.zoomLevel), - @"direction": @(_map.direction), - @"pitch": @(_map.camera.pitch), - @"animated": @(animated) } }); -} - -- (void)handleSingleTap:(UITapGestureRecognizer *)sender -{ - if (!_onTap) { return; } - - CLLocationCoordinate2D location = [_map convertPoint:[sender locationInView:_map] toCoordinateFromView:_map]; - CGPoint screenCoord = [sender locationInView:_map]; - - _onTap(@{ @"target": self.reactTag, - @"src": @{ @"latitude": @(location.latitude), - @"longitude": @(location.longitude), - @"screenCoordY": @(screenCoord.y), - @"screenCoordX": @(screenCoord.x) } }); -} - -- (void)handleLongPress:(UITapGestureRecognizer *)sender -{ - if (!_onLongPress) { return; } - if (sender.state != UIGestureRecognizerStateBegan) { return; } - - CLLocationCoordinate2D location = [_map convertPoint:[sender locationInView:_map] toCoordinateFromView:_map]; - CGPoint screenCoord = [sender locationInView:_map]; - - _onLongPress(@{ @"target": self.reactTag, - @"src": @{ @"latitude": @(location.latitude), - @"longitude": @(location.longitude), - @"screenCoordY": @(screenCoord.y), - @"screenCoordX": @(screenCoord.x) } }); -} - -- (nonnull NSArray> *)visibleFeaturesAtPoint:(CGPoint)point - inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers -{ - return [_map visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers]; -} - -- (nonnull NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet *)identifiers -{ - return [_map visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:identifiers]; -} - -- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView -{ - if (!_onFinishLoadingMap) { return; } - _onFinishLoadingMap(@{ @"target": self.reactTag }); -} - -- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView -{ - if (!_onStartLoadingMap) { return; } - _onStartLoadingMap(@{ @"target": self.reactTag }); -} - -// Utils - -- (unsigned int)intFromHexString:(NSString *)hexStr -{ - unsigned int hexInt = 0; - - // Create scanner - NSScanner *scanner = [NSScanner scannerWithString:hexStr]; - - // Tell scanner to skip the # character - [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"#"]]; - - // Scan hex value - [scanner scanHexInt:&hexInt]; - - return hexInt; -} - - -- (UIColor *)getUIColorObjectFromHexString:(NSString *)hexStr alpha:(CGFloat)alpha -{ - // Convert hex string to an integer - unsigned int hexint = [self intFromHexString:hexStr]; - - // Create color object, specifying alpha as well - UIColor *color = - [UIColor colorWithRed:((CGFloat) ((hexint & 0xFF0000) >> 16))/255 - green:((CGFloat) ((hexint & 0xFF00) >> 8))/255 - blue:((CGFloat) (hexint & 0xFF))/255 - alpha:alpha]; - - return color; -} - -@end - - -@interface RCTMGLAnnotation () - -@property (nonatomic) CLLocationCoordinate2D coordinate; -@property (nonatomic) NSString *title; -@property (nonatomic) NSString *subtitle; - -@end - -@implementation RCTMGLAnnotation - -+ (instancetype)annotationWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle id:(NSString *)id -{ - return [[self alloc] initWithLocation:coordinate title:title subtitle:subtitle id:id]; -} - -+ (instancetype)annotationWithLocationRightCallout:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle id:(NSString *)id rightCalloutAccessory:(UIButton *)rightCalloutAccessory -{ - return [[self alloc] initWithLocationRightCallout:coordinate title:title subtitle:subtitle id:id rightCalloutAccessory:rightCalloutAccessory]; -} - - -- (instancetype)initWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle id:(NSString *)id -{ - if (self = [super init]) { - _coordinate = coordinate; - _title = title; - _subtitle = subtitle; - _id = id; - } - - return self; -} - - -- (instancetype)initWithLocationRightCallout:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle id:(NSString *)id rightCalloutAccessory:(UIButton *)rightCalloutAccessory -{ - if (self = [super init]) { - _rightCalloutAccessory = rightCalloutAccessory; - _coordinate = coordinate; - _title = title; - _subtitle = subtitle; - _id = id; - } - - return self; -} -@end - -@interface RCTMGLAnnotationPolyline () -@end - -@implementation RCTMGLAnnotationPolyline - -+ (instancetype)polylineAnnotation:(CLLocationCoordinate2D *)coordinates strokeAlpha:(double)strokeAlpha strokeColor:(NSString *)strokeColor strokeWidth:(double)strokeWidth id:(NSString *)id type:(NSString *)type count:(NSUInteger)count -{ - RCTMGLAnnotationPolyline *polyline = [self polylineWithCoordinates:coordinates count:count]; - polyline.strokeAlpha = strokeAlpha; - polyline.strokeColor = strokeColor; - polyline.strokeWidth = strokeWidth; - polyline.id = id; - return polyline; -} -@end - -@interface RCTMGLAnnotationPolygon () -@end - -@implementation RCTMGLAnnotationPolygon - -+ (instancetype)polygonAnnotation:(CLLocationCoordinate2D *)coordinates fillAlpha:(double)fillAlpha fillColor:(NSString *)fillColor strokeColor:(NSString *)strokeColor strokeAlpha:(double)strokeAlpha id:(NSString *)id type:(NSString *)type count:(NSUInteger)count -{ - RCTMGLAnnotationPolygon *polygon = [self polygonWithCoordinates:coordinates count:count]; - polygon.fillAlpha = fillAlpha; - polygon.fillColor = fillColor; - polygon.strokeAlpha = strokeAlpha; - polygon.strokeColor = strokeColor; - polygon.id = id; - return polygon; -} - - -@end diff --git a/ios/RCTMapboxGL/RCTMapboxGLManager.m b/ios/RCTMapboxGL/RCTMapboxGLManager.m deleted file mode 100644 index 6141983..0000000 --- a/ios/RCTMapboxGL/RCTMapboxGLManager.m +++ /dev/null @@ -1,818 +0,0 @@ -// -// RCTMapboxGLManager.m -// RCTMapboxGL -// -// Created by Bobby Sudekum on 4/30/15. -// Copyright (c) 2015 Mapbox. All rights reserved. -// - -#import "RCTMapboxGLManager.h" -#import "RCTMapboxGL.h" -#import -#import -#import -#import -#import -#import -#import -#import "RCTMapboxGLConversions.h" -#import "MGLPolygon+RCTAdditions.h" -#import "MGLPolyline+RCTAdditions.h" - -@implementation RCTMapboxGLManager -{ - BOOL isOfflineObserverSet; -} - -- (UIView *)view -{ - return [[RCTMapboxGL alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; -} - -@synthesize bridge = _bridge; - -- (dispatch_queue_t)methodQueue -{ - return _bridge.uiManager.methodQueue; -} - -RCT_EXPORT_MODULE(); - -// Props - -RCT_EXPORT_VIEW_PROPERTY(initialCenterCoordinate, CLLocationCoordinate2D); -RCT_EXPORT_VIEW_PROPERTY(initialZoomLevel, double); -RCT_EXPORT_VIEW_PROPERTY(initialDirection, double); -RCT_EXPORT_VIEW_PROPERTY(clipsToBounds, BOOL); -RCT_EXPORT_VIEW_PROPERTY(debugActive, BOOL); -RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL); -RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL); -RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL); -RCT_EXPORT_VIEW_PROPERTY(minimumZoomLevel, double); -RCT_EXPORT_VIEW_PROPERTY(maximumZoomLevel, double); -RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL); -RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL); -RCT_EXPORT_VIEW_PROPERTY(styleURL, NSURL); -RCT_EXPORT_VIEW_PROPERTY(userTrackingMode, int); -RCT_EXPORT_VIEW_PROPERTY(attributionButtonIsHidden, BOOL); -RCT_EXPORT_VIEW_PROPERTY(logoIsHidden, BOOL); -RCT_EXPORT_VIEW_PROPERTY(compassIsHidden, BOOL); -RCT_EXPORT_VIEW_PROPERTY(userLocationVerticalAlignment, int); -RCT_EXPORT_VIEW_PROPERTY(annotationsPopUpEnabled, BOOL); - -RCT_EXPORT_VIEW_PROPERTY(onRegionDidChange, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onRegionWillChange, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onChangeUserTrackingMode, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onOpenAnnotation, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onCloseAnnotation, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onRightAnnotationTapped, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onUpdateUserLocation, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onTap, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onLongPress, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onFinishLoadingMap, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onStartLoadingMap, RCTDirectEventBlock); -RCT_EXPORT_VIEW_PROPERTY(onLocateUserFailed, RCTDirectEventBlock); - -RCT_CUSTOM_VIEW_PROPERTY(contentInset, UIEdgeInsetsMake, RCTMapboxGL) -{ - int top = [json[0] doubleValue]; - int left = [json[3] doubleValue]; - int bottom = [json[2] doubleValue]; - int right = [json[1] doubleValue]; - UIEdgeInsets inset = UIEdgeInsetsMake(top, left, bottom, right); - view.contentInset = inset; -} - -// Constants - -- (NSDictionary *)constantsToExport -{ - return @{ - @"mapStyles": @{ - @"light": [[MGLStyle lightStyleURL] absoluteString], - @"dark": [[MGLStyle darkStyleURL] absoluteString], - @"streets": [[MGLStyle streetsStyleURL] absoluteString], - @"emerald": [[MGLStyle emeraldStyleURL] absoluteString], - @"satellite": [[MGLStyle satelliteStyleURL] absoluteString], - @"hybrid": [[MGLStyle hybridStyleURL] absoluteString], - }, - @"userTrackingMode": @{ - @"none": [NSNumber numberWithUnsignedInt:MGLUserTrackingModeNone], - @"follow": [NSNumber numberWithUnsignedInt:MGLUserTrackingModeFollow], - @"followWithCourse": [NSNumber numberWithUnsignedInt:MGLUserTrackingModeFollowWithCourse], - @"followWithHeading": [NSNumber numberWithUnsignedInt:MGLUserTrackingModeFollowWithHeading] - }, - @"userLocationVerticalAlignment" : @{ - @"top": @(MGLAnnotationVerticalAlignmentTop), - @"center": @(MGLAnnotationVerticalAlignmentCenter), - @"bottom": @(MGLAnnotationVerticalAlignmentBottom) - }, - @"offlinePackState": @{ - @"unknown": [NSNumber numberWithUnsignedInt:MGLOfflinePackStateUnknown], - @"inactive": [NSNumber numberWithUnsignedInt:MGLOfflinePackStateInactive], - @"active": [NSNumber numberWithUnsignedInt:MGLOfflinePackStateActive], - @"complete": [NSNumber numberWithUnsignedInt:MGLOfflinePackStateComplete], - @"invalid": [NSNumber numberWithUnsignedInt:MGLOfflinePackStateInvalid] - }, - @"unknownResourceCount": @(UINT64_MAX) - }; -}; - -// Metrics - -RCT_EXPORT_METHOD(getMetricsEnabled:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; - NSNumber * nr = [ud valueForKey:@"MGLMapboxMetricsEnabled"]; - if (!nr || ![nr isKindOfClass:[NSNumber class]]) { - resolve(@YES); - return; - } - - resolve([NSNumber numberWithBool:nr.boolValue]); -} - -RCT_EXPORT_METHOD(setMetricsEnabled:(BOOL)enabled) -{ - [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:@"MGLMapboxMetricsEnabled"]; -} - -// Access token - -RCT_EXPORT_METHOD(setAccessToken:(nonnull NSString *)accessToken - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - if (!accessToken || ![accessToken length] || [accessToken isEqual:@"your-mapbox.com-access-token"]) { - reject(nil, @"Mapbox api token is not valid.", nil); - return; - } - [MGLAccountManager setAccessToken:accessToken]; - resolve(nil); - }); -} - -// Offline - -- (id)init -{ - if (!(self = [super init])) { return nil; } - - return self; -} - -- (void)dealloc -{ - if (isOfflineObserverSet) { - [[MGLOfflineStorage sharedOfflineStorage] removeObserver:self forKeyPath:@"packs"]; - } - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)offlinePacksDidFinishLoading -{ - _loadedPacks = YES; - - NSArray * packs = [MGLOfflineStorage sharedOfflineStorage].packs; - - if ([_packRequests count]) { - NSArray * callbackArray = [self serializePacksArray:packs]; - for (RCTPromiseResolveBlock callback in _packRequests) { - callback(callbackArray); - } - [_packRequests removeAllObjects]; - } - - for (MGLOfflinePack * pack in packs) { - if (pack.state != MGLOfflinePackStateComplete) { - [pack resume]; - } - } - [_bridge.eventDispatcher sendAppEventWithName:@"MapboxOfflinePacksLoaded" body:@{}]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - NSNumber * changeKind = change[NSKeyValueChangeKindKey]; - if (changeKind == [NSNull null]) { return; } - if ([changeKind integerValue] != NSKeyValueChangeSetting) { return; } - - NSArray * packs = [[MGLOfflineStorage sharedOfflineStorage] packs]; - - if (!packs) { return; } - if (_loadedPacks) { return; } - - [_loadingPacks addObjectsFromArray:packs]; - - for (MGLOfflinePack * pack in packs) { - [pack requestProgress]; - } - - if (!packs.count) { - [self offlinePacksDidFinishLoading]; - } -} - -- (void)firePackProgress:(MGLOfflinePack*)pack { - if (pack.state == MGLOfflinePackStateInvalid) { - return; - } - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - MGLOfflinePackProgress progress = pack.progress; - - NSDictionary *event = @{ @"name": userInfo[@"name"], - @"metadata": userInfo[@"metadata"], - @"state": @(pack.state), - @"countOfResourcesCompleted": @(progress.countOfResourcesCompleted), - @"countOfResourcesExpected": @(progress.countOfResourcesExpected), - @"countOfBytesCompleted": @(progress.countOfBytesCompleted), - @"maximumResourcesExpected": @(progress.maximumResourcesExpected) }; - - [_bridge.eventDispatcher sendAppEventWithName:@"MapboxOfflineProgressDidChange" body:event]; - - [_recentPacks addObject:pack]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, _throttleInterval * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_recentPacks removeObject:pack]; - if ([_throttledPacks containsObject:pack]) { - [_throttledPacks removeObject:pack]; - [self firePackProgress:pack]; - } - }); -} - -- (void)flushThrottleForPack:(MGLOfflinePack*)pack { - if ([_throttledPacks containsObject:pack]) { - [_throttledPacks removeObject:pack]; - [self firePackProgress:pack]; - } -} - -- (void)discardThrottleForPack:(MGLOfflinePack*)pack { - if ([_throttledPacks containsObject:pack]) { - [_throttledPacks removeObject:pack]; - } -} - -- (void)offlinePackProgressDidChange:(NSNotification *)notification { - MGLOfflinePack *pack = notification.object; - - if (!_loadedPacks && [_loadingPacks containsObject:pack]) { - [_loadingPacks removeObject:pack]; - if ([_loadingPacks count] == 0) { - [self offlinePacksDidFinishLoading]; - } - } - - if ([_removedPacks containsObject:pack]) { - return; - } - - if ([_recentPacks containsObject:pack]) { - [_throttledPacks addObject:pack]; - return; - } - - [self firePackProgress:pack]; -} - -- (void)offlinePackDidReceiveMaximumAllowedMapboxTiles:(NSNotification *)notification { - MGLOfflinePack *pack = notification.object; - [self flushThrottleForPack:pack]; - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - uint64_t maximumCount = [notification.userInfo[MGLOfflinePackMaximumCountUserInfoKey] unsignedLongLongValue]; - - NSDictionary *event = @{ @"name": userInfo[@"name"], - @"maxTiles": @(maximumCount) }; - - [_bridge.eventDispatcher sendAppEventWithName:@"MapboxOfflineMaxAllowedTiles" body:event]; -} - -- (void)offlinePackDidReceiveError:(NSNotification *)notification { - MGLOfflinePack *pack = notification.object; - [self flushThrottleForPack:pack]; - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - NSError *error = notification.userInfo[MGLOfflinePackErrorUserInfoKey]; - - NSDictionary *event = @{ @"name": userInfo[@"name"], - @"error": [error localizedDescription] }; - - [_bridge.eventDispatcher sendAppEventWithName:@"MapboxOfflineError" body:event]; -} - -RCT_EXPORT_METHOD(initializeOfflinePacks) -{ - _recentPacks = [NSMutableSet new]; - _throttledPacks = [NSMutableSet new]; - _removedPacks = [NSMutableSet new]; - _throttleInterval = 300; - - _loadingPacks = [NSMutableSet new]; - _loadedPacks = NO; - - // Setup pack array loading notifications - [[MGLOfflineStorage sharedOfflineStorage] addObserver:self forKeyPath:@"packs" options:NSKeyValueObservingOptionInitial context:NULL]; - isOfflineObserverSet = YES; - _packRequests = [NSMutableArray new]; - - // Setup offline pack notification handlers. - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackProgressDidChange:) name:MGLOfflinePackProgressChangedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveError:) name:MGLOfflinePackErrorNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveMaximumAllowedMapboxTiles:) name:MGLOfflinePackMaximumMapboxTilesReachedNotification object:nil]; -} - -RCT_REMAP_METHOD(addOfflinePack, - pack:(NSDictionary*)options - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - if (options[@"name"] == nil) { - reject(@"invalid_arguments", @"addOfflinePack(): name is required.", nil); - return; - } - if (options[@"minZoomLevel"] == nil) { - reject(@"invalid_arguments", @"addOfflinePack(): minZoomLevel is required.", nil); - return; - } - if (options[@"maxZoomLevel"] == nil) { - reject(@"invalid_arguments", @"addOfflinePack(): maxZoomLevel is required.", nil); - return; - } - if (options[@"bounds"] == nil) { - reject(@"invalid_arguments", @"addOfflinePack(): bounds is required.", nil); - return; - } - if (options[@"styleURL"] == nil) { - reject(@"invalid_arguments", @"addOfflinePack(): styleURL is required.", nil); - return; - } - if (!([options[@"type"] isEqualToString:@"bbox"])) { - reject(@"invalid_arguments", - [NSString stringWithFormat:@"addOfflinePack(): Offline type %@ not supported. Only type \"bbox\" supported.", options[@"type"]] - , nil); - return; - } - - NSArray *b = [options valueForKey:@"bounds"]; - MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake([b[0] floatValue], [b[1] floatValue]), CLLocationCoordinate2DMake([b[2] floatValue], [b[3] floatValue])); - - NSURL * styleURL = [NSURL URLWithString:[options valueForKey:@"styleURL"]]; - float fromZoomLevel = [[options valueForKey:@"minZoomLevel"] floatValue]; - float toZoomLevel = [[options valueForKey:@"maxZoomLevel"] floatValue]; - NSString * name = [options valueForKey:@"name"]; - NSString * type = [options valueForKey:@"type"]; - NSDictionary * metadata = [options valueForKey:@"metadata"]; - - dispatch_async(dispatch_get_main_queue(), ^{ - id region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds fromZoomLevel:fromZoomLevel toZoomLevel:toZoomLevel]; - - NSMutableDictionary *userInfo = @{ @"name": name, - @"metadata": metadata ? metadata : [NSNull null] }; - NSData *context = [NSKeyedArchiver archivedDataWithRootObject:userInfo]; - - [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack *pack, NSError *error) { - if (error != nil) { - reject(@"add_pack_failed", error.localizedFailureReason, error); - } else { - [pack resume]; - resolve([NSNull null]); - } - }]; - }); -} - -- (NSArray*)serializePacksArray:(NSArray*)packs -{ - NSMutableArray* callbackArray = [NSMutableArray new]; - - for (MGLOfflinePack *pack in packs) { - NSMutableDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - [callbackArray addObject:@{ @"name": userInfo[@"name"], - @"metadata": userInfo[@"metadata"], - @"state": @(pack.state), - @"countOfBytesCompleted": @(pack.progress.countOfBytesCompleted), - @"countOfResourcesCompleted": @(pack.progress.countOfResourcesCompleted), - @"countOfResourcesExpected": @(pack.progress.countOfResourcesExpected), - @"maximumResourcesExpected": @(pack.progress.maximumResourcesExpected) }]; - } - - return callbackArray; -} - -RCT_REMAP_METHOD(suspendOfflinePack, - suspendName:(NSString*)packName - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - MGLOfflinePack *packs = [MGLOfflineStorage sharedOfflineStorage].packs; - MGLOfflinePack *tempPack; - - for (MGLOfflinePack *pack in packs) { - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - if ([packName isEqualToString:userInfo[@"name"]]) { - tempPack = pack; - break; - } - } - - if (tempPack == nil) { - return resolve(@{}); - } - - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:tempPack.context]; - [tempPack suspend]; - - resolve(@{ @"suspended": userInfo[@"name"] }); - }); -} - -RCT_REMAP_METHOD(resumeOfflinePack, - resumeName:(NSString*)packName - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - MGLOfflinePack *packs = [MGLOfflineStorage sharedOfflineStorage].packs; - MGLOfflinePack *tempPack; - - for (MGLOfflinePack *pack in packs) { - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - if ([packName isEqualToString:userInfo[@"name"]]) { - tempPack = pack; - break; - } - } - - if (tempPack == nil) { - return resolve(@{}); - } - - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:tempPack.context]; - [tempPack resume]; - - resolve(@{ @"resumed": userInfo[@"name"] }); - }); -} - -RCT_REMAP_METHOD(getOfflinePacks, - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - NSMutableArray* callbackArray = [NSMutableArray new]; - - if (!_loadedPacks) { - [_packRequests addObject:resolve]; - } else { - MGLOfflinePack *packs = [MGLOfflineStorage sharedOfflineStorage].packs; - resolve([self serializePacksArray:packs]); - } - }); -} - -RCT_REMAP_METHOD(removeOfflinePack, - name:(NSString*)packName - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - MGLOfflinePack *packs = [MGLOfflineStorage sharedOfflineStorage].packs; - MGLOfflinePack *tempPack; - - for (MGLOfflinePack *pack in packs) { - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; - if ([packName isEqualToString:userInfo[@"name"]]) { - tempPack = pack; - break; - } - } - - if (tempPack == nil) { - return resolve(@{}); - } - - NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:tempPack.context]; - - - // Workaround for https://github.com/mapbox/mapbox-gl-native/issues/5508 - - [_removedPacks addObject:tempPack]; - [self discardThrottleForPack:tempPack]; - [tempPack suspend]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_removedPacks removeObject:tempPack]; - [[MGLOfflineStorage sharedOfflineStorage] removePack:tempPack withCompletionHandler:^(NSError * _Nullable error) { - if (error != nil) { - reject(@"remove_pack_failed", error.localizedFailureReason, error); - } else { - resolve(@{ @"deleted": userInfo[@"name"] }); - } - }]; - }); - }); -} - -RCT_EXPORT_METHOD(setOfflinePackProgressThrottleInterval:(nonnull NSNumber *)milis) -{ - _throttleInterval = [milis intValue]; -} - -// View methods - -RCT_EXPORT_METHOD(spliceAnnotations:(nonnull NSNumber *)reactTag - deleteAll:(BOOL)deleteAll - toDelete:(nonnull NSArray *)toDelete - toAdd:(nonnull NSArray *)toAdd) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - - if (deleteAll) { - [mapView removeAllAnnotations]; - } else { - for (NSString * key in toDelete) { - [mapView removeAnnotation:key]; - } - } - - for (NSObject * annotationObject in toAdd) { - [mapView upsertAnnotation:convertToMGLAnnotation(annotationObject)]; - } - }]; -} - -RCT_EXPORT_METHOD(getCenterCoordinateZoomLevel:(nonnull NSNumber *)reactTag - callback:(RCTResponseSenderBlock)callback) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - CLLocationCoordinate2D region = [mapView centerCoordinate]; - double zoom = [mapView zoomLevel]; - - callback(@[ @{ @"latitude": @(region.latitude), - @"longitude": @(region.longitude), - @"zoomLevel": @(zoom) } ]); - }]; -} - -RCT_EXPORT_METHOD(getBounds:(nonnull NSNumber *)reactTag - callback:(RCTResponseSenderBlock)callback) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - MGLCoordinateBounds bounds = [mapView visibleCoordinateBounds]; - NSMutableArray *callbackArray = [[NSMutableArray alloc] init]; - - [callbackArray addObject:@(bounds.sw.latitude)]; - [callbackArray addObject:@(bounds.sw.longitude)]; - [callbackArray addObject:@(bounds.ne.latitude)]; - [callbackArray addObject:@(bounds.ne.longitude)]; - - callback(@[callbackArray]); - }]; -} - -RCT_EXPORT_METHOD(getDirection:(nonnull NSNumber *)reactTag - callback:(RCTResponseSenderBlock)callback) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - double direction = [mapView direction]; - - callback(@[ @(direction) ]); - }]; -} - -RCT_EXPORT_METHOD(getPitch:(nonnull NSNumber *)reactTag - callback:(RCTResponseSenderBlock)callback) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - double pitch = [mapView pitch]; - - callback(@[ @(pitch) ]); - }]; -} - -RCT_EXPORT_METHOD(easeTo:(nonnull NSNumber *)reactTag - options:(NSDictionary *)options - animated:(BOOL)animated - callback:(RCTResponseSenderBlock)callback) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - if ([mapView isKindOfClass:[RCTMapboxGL class]]) { - - NSNumber * latitude = options[@"latitude"]; - NSNumber * longitude = options[@"longitude"]; - NSNumber * zoom = options[@"zoomLevel"]; - NSNumber * direction = options[@"direction"]; - NSNumber * pitch = options[@"pitch"]; - NSNumber * altitude = options[@"altitude"]; - - if (pitch && zoom) { - RCTLogError(@"Pitch and zoomLevel can't be set together with MapView.easeTo() on iOS. Use altitude instead of zoomLevel"); - return; - } - - if (zoom && altitude) { - RCTLogError(@"Altitude and zoomLevel are mutually exclusive with MapView.easeTo()"); - return; - } - - CLLocationCoordinate2D _center = (latitude && longitude) - ? CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]) - : mapView.centerCoordinate; - - double _direction = direction ? [direction doubleValue] : mapView.direction; - - if (pitch || altitude) { - MGLMapCamera * oldCamera = (!pitch || !altitude) ? mapView.camera : nil; - double _altitude = altitude ? [altitude doubleValue] : oldCamera ? oldCamera.altitude : 0; - double _pitch = pitch ? [pitch doubleValue] : oldCamera ? oldCamera.pitch : 0; - - MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:_center - fromDistance:_altitude - pitch:_pitch - heading:_direction]; - - [mapView setCamera: camera - withDuration: 0.3 - animationTimingFunction: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] - completionHandler: ^{ - callback(@[[NSNull null]]); - }]; - - } else { - double _zoomLevel = zoom ? [zoom doubleValue] : mapView.zoomLevel; - - [mapView setCenterCoordinate: _center - zoomLevel: _zoomLevel - direction: _direction - animated: animated - completionHandler: ^{ - callback(@[[NSNull null]]); - }]; - } - } - }]; -} - -RCT_EXPORT_METHOD(setVisibleCoordinateBounds:(nonnull NSNumber *)reactTag - latitudeSW:(float) latitudeSW - longitudeSW:(float) longitudeSW - latitudeNE:(float) latitudeNE - longitudeNE:(float) longitudeNE - paddingTop:(double) paddingTop - paddingRight:(double) paddingRight - paddingBottom:(double) paddingBottom - paddingLeft:(double) paddingLeft - animated:(BOOL) animated) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - if ([mapView isKindOfClass:[RCTMapboxGL class]]) { - MGLCoordinateBounds coordinatesBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(latitudeSW, longitudeSW), CLLocationCoordinate2DMake(latitudeNE, longitudeNE)); - [mapView setVisibleCoordinateBounds:coordinatesBounds edgePadding:UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight) animated:animated]; - } - }]; -} - -RCT_EXPORT_METHOD(selectAnnotation:(nonnull NSNumber *) reactTag - selectedIdentifier:(NSString*)selectedIdentifier - animated:(BOOL)animated) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - if ([mapView isKindOfClass:[RCTMapboxGL class]]) { - [mapView selectAnnotation:selectedIdentifier animated:animated]; - } - }]; -} - - -RCT_EXPORT_METHOD(deselectAnnotation:(nonnull NSNumber *) reactTag) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - if ([mapView isKindOfClass:[RCTMapboxGL class]]) { - [mapView deselectAnnotation]; - } - }]; -} - -RCT_EXPORT_METHOD(queryRenderedFeatures:(nonnull NSNumber *)reactTag - options:(NSDictionary *)options - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) -{ - [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTMapboxGL *mapView = viewRegistry[reactTag]; - if ([mapView isKindOfClass:[RCTMapboxGL class]]) { - NSDictionary *pointDict = options[@"point"]; - NSDictionary *rectDict = options[@"rect"]; - if ((!pointDict && !rectDict) || (pointDict && rectDict)) { - reject(@"invalid_arguments", @"queryRenderedFeatures(): one of 'point' or 'rect' is required.", nil); - return; - } - - NSArray> *features; - NSArray *styleLayerIdentifiersArray = options[@"layers"]; - NSSet *styleLayerIdentifiers; - if (styleLayerIdentifiersArray) { - styleLayerIdentifiers = [NSSet setWithArray:styleLayerIdentifiersArray]; - } - - if (pointDict) { - NSNumber *screenCoordX = pointDict[@"screenCoordX"]; - NSNumber *screenCoordY = pointDict[@"screenCoordY"]; - CGPoint point = CGPointMake(screenCoordX.floatValue, screenCoordY.floatValue); - features = [mapView visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers]; - } else { - NSNumber *left = rectDict[@"left"]; - NSNumber *top = rectDict[@"top"]; - NSNumber *right = rectDict[@"right"]; - NSNumber *bottom = rectDict[@"bottom"]; - CGFloat width = right.floatValue - left.floatValue; - CGFloat height = bottom.floatValue - top.floatValue; - CGRect rect = CGRectMake(left.floatValue, top.floatValue, width, height); - features = [mapView visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers]; - } - - NSMutableArray *geoJSONFeatures = [NSMutableArray arrayWithCapacity:features.count]; - for (id feature in features) { - NSDictionary *geoJSONGeometry = [self geoJSONGeometryFromMGLFeature:feature]; - NSDictionary *geoJSON = @{ @"type": @"Feature", - @"id": feature.identifier ? feature.identifier : [NSNull null], - @"properties": feature.attributes, - @"geometry": geoJSONGeometry }; - [geoJSONFeatures addObject:geoJSON]; - } - - resolve(geoJSONFeatures); - } - }]; -} - -- (NSDictionary*)geoJSONGeometryFromMGLFeature:(id )feature -{ - NSString *geometryType; - - if ([feature isKindOfClass:[MGLShapeCollectionFeature class]]) { - geometryType = @"GeometryCollection"; - MGLShapeCollectionFeature *shapeCollection = (MGLShapeCollectionFeature *) feature; - NSMutableArray *geometries = [[NSMutableArray alloc] init]; - for (MGLShape *shape in shapeCollection.shapes) { - [geometries addObject:[self geoJSONGeometryFromMGLFeature:shape]]; - } - return @{ @"type": geometryType, - @"geometries": geometries }; - } - - NSMutableArray *coordinates = [[NSMutableArray alloc] init]; - - if ([feature isKindOfClass:[MGLPointFeature class]]) { - geometryType = @"Point"; - coordinates = [[NSMutableArray alloc] initWithArray:@[@(feature.coordinate.longitude), @(feature.coordinate.latitude)]]; - } else if ([feature isKindOfClass:[MGLPolylineFeature class]]) { - geometryType = @"LineString"; - MGLPolylineFeature *polyline = (MGLPolylineFeature *)feature; - coordinates = polyline.coordinateArray; - } else if ([feature isKindOfClass:[MGLPolygonFeature class]]) { - geometryType = @"Polygon"; - MGLPolygonFeature *polygon = (MGLPolygonFeature *)feature; - coordinates = polygon.coordinateArray; - } else if ([feature isKindOfClass:[MGLMultiPolylineFeature class]]) { - geometryType = @"MultiLineString"; - MGLMultiPolylineFeature *multiPolyline = (MGLMultiPolylineFeature *)feature; - for (MGLPolyline *polyline in multiPolyline.polylines) { - [coordinates addObject:polyline.coordinateArray]; - } - } else if ([feature isKindOfClass:[MGLMultiPolygonFeature class]]) { - geometryType = @"MultiPolygon"; - MGLMultiPolygonFeature *multiPolygon = (MGLMultiPolygonFeature *)feature; - for (MGLPolygon *polygon in multiPolygon.polygons) { - [coordinates addObject:polygon.coordinateArray]; - } - } else if ([feature isKindOfClass:[MGLMultiPointFeature class]]) { - // this is checked last since MGLPolyline and MGLPolygon inherit from MGLMultiPoint - geometryType = @"MultiPoint"; - MGLMultiPointFeature *multiPoint = (MGLMultiPointFeature *)feature; - for (int index = 0; index < multiPoint.pointCount; index++) { - CLLocationCoordinate2D coord = multiPoint.coordinates[index]; - [coordinates addObject:[[NSMutableArray alloc] initWithArray:@[@(coord.longitude), @(coord.latitude)]]]; - } - } - - return @{ @"type": geometryType, - @"coordinates": coordinates }; -} - -@end