diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/RCTMGLPackage.java b/android/rctmgl/src/main/java/mapbox/rctmgl/RCTMGLPackage.java index 1b3546d..b8ac028 100644 --- a/android/rctmgl/src/main/java/mapbox/rctmgl/RCTMGLPackage.java +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/RCTMGLPackage.java @@ -7,10 +7,12 @@ 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; +import mapbox.rctmgl.components.RCTMGLMapViewManager; +import mapbox.rctmgl.modules.RCTMGLModule; + /** * Created by nickitaliano on 8/18/17. */ diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTAbstractEventEmitter.java b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTAbstractEventEmitter.java new file mode 100644 index 0000000..07b0f01 --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTAbstractEventEmitter.java @@ -0,0 +1,52 @@ +package mapbox.rctmgl.components; + +import android.view.ViewGroup; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.events.RCTEventEmitter; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import mapbox.rctmgl.events.IRCTMGLEvent; + +/** + * Created by nickitaliano on 8/23/17. + */ + +abstract public class RCTAbstractEventEmitter extends ViewGroupManager { + private ReactApplicationContext mRCTAppContext; + + public RCTAbstractEventEmitter(ReactApplicationContext reactApplicationContext) { + mRCTAppContext = reactApplicationContext; + } + + public void handleEvent(IRCTMGLEvent event) { + getEventEmitter().receiveEvent(event.getID(), event.getName(), event.toWritableMap()); + } + + @Nullable + @Override + public Map getExportedCustomDirectEventTypeConstants() { + Map events = customEvents(); + Map exportedEvents = new HashMap<>(); + + for (Map.Entry event : events.entrySet()) { + exportedEvents.put(event.getKey(), MapBuilder.of("registrationName", event.getValue())); + } + + return exportedEvents; + } + + public abstract Map customEvents(); + + private RCTEventEmitter getEventEmitter() { + return mRCTAppContext.getJSModule(RCTEventEmitter.class); + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapView.java b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapView.java new file mode 100644 index 0000000..c753f12 --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapView.java @@ -0,0 +1,163 @@ +package mapbox.rctmgl.components; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.util.Log; +import android.widget.RelativeLayout; + +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.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.services.commons.geojson.Point; + +import mapbox.rctmgl.utils.MGLGeoUtils; +import mapbox.rctmgl.events.RCTMGLMapClickEvent; +import mapbox.rctmgl.events.RCTMGLMapLongClickEvent; + +/** + * Created by nickitaliano on 8/18/17. + */ + +public class RCTMGLMapView extends RelativeLayout implements OnMapReadyCallback { + public static final String LOG_TAG = RCTMGLMapView.class.getSimpleName(); + + private RCTMGLMapViewManager mManager; + + private MapboxMap mMap; + private MapView mMapView; + + private String mStyleURL; + private boolean mAnimated; + private double mHeading; + private double mPitch; + private double mZoomLevel; + private Point mCenterCoordinate; + + public RCTMGLMapView(Context context, RCTMGLMapViewManager manager) { + super(context); + mManager = manager; + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + if (mMapView == null) { + Log.d(LOG_TAG, "Mapbox map is ready before our mapview is initialized!!!"); + return; + } + mMap = mapboxMap; + setOnClickListener(); + setOnLongClickListener(); + } + + public void setStyleURL(String styleURL) { + mStyleURL = styleURL; + + if (mMap != null) { + mMap.setStyleUrl(styleURL); + } + } + + public void setAnimated(boolean animated) { + mAnimated = animated; + updateCameraPositionIfNeeded(false); + } + + public void setHeading(double heading) { + mHeading = heading; + updateCameraPositionIfNeeded(false); + } + + public void setPitch(double pitch) { + mPitch = pitch; + updateCameraPositionIfNeeded(false); + } + + public void setZoomLevel(double zoomLevel) { + mZoomLevel = zoomLevel; + updateCameraPositionIfNeeded(false); + } + + public void setCenterCoordinate(Point centerCoordinate) { + mCenterCoordinate = centerCoordinate; + updateCameraPositionIfNeeded(true); + } + + public void makeView() { + if (mMapView != null) { + return; + } + buildMapView(); + } + + private void buildMapView() { + MapboxMapOptions options = new MapboxMapOptions(); + options.camera(buildCamera()); + + mMapView = new MapView(getContext(), options); + mManager.addView(this, mMapView, 0); + mMapView.setStyleUrl(mStyleURL); + mMapView.onCreate(null); + mMapView.getMapAsync(this); + } + + 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(MGLGeoUtils.pointToLatLng(mCenterCoordinate)); + } + + return builder.build(); + } + + private void setOnClickListener() { + final RCTMGLMapView view = this; + + mMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() { + @Override + public void onMapClick(@NonNull LatLng point) { + RCTMGLMapClickEvent event = new RCTMGLMapClickEvent(view); + event.setLatLng(point); + mManager.handleEvent(event); + } + }); + } + + private void setOnLongClickListener() { + final RCTMGLMapView view = this; + + mMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() { + @Override + public void onMapLongClick(@NonNull LatLng point) { + RCTMGLMapLongClickEvent event = new RCTMGLMapLongClickEvent(view); + event.setLatLng(point); + mManager.handleEvent(event); + } + }); + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapViewManager.java b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapViewManager.java new file mode 100644 index 0000000..b606cbb --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/components/RCTMGLMapViewManager.java @@ -0,0 +1,91 @@ +package mapbox.rctmgl.components; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.mapbox.services.commons.geojson.Point; + +import java.util.Map; + +import mapbox.rctmgl.utils.MGLGeoUtils; +import mapbox.rctmgl.utils.RCTMGLMapStyleURL; +import mapbox.rctmgl.events.RCTMGLEventNames; + +/** + * Created by nickitaliano on 8/18/17. + */ + +public class RCTMGLMapViewManager extends RCTAbstractEventEmitter { + public static final String REACT_CLASS = RCTMGLMapView.class.getSimpleName(); + + public RCTMGLMapViewManager(ReactApplicationContext context) { + super(context); + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + protected void onAfterUpdateTransaction(RCTMGLMapView mapView) { + super.onAfterUpdateTransaction(mapView); + mapView.makeView(); + } + + @Override + protected RCTMGLMapView createViewInstance(ThemedReactContext themedReactContext) { + return new RCTMGLMapView(themedReactContext, this); + } + + @ReactProp(name="styleURL") + public void setStyleURL(RCTMGLMapView mapView, String styleURL) { + RCTMGLMapStyleURL mapStyle = RCTMGLMapStyleURL.fromKey(styleURL); + + if (mapStyle != null) { + mapView.setStyleURL(mapStyle.getURL()); + return; + } + + mapView.setStyleURL(styleURL); + } + + @ReactProp(name="animated") + public void setAnimated(RCTMGLMapView mapView, boolean isAnimated) { + mapView.setAnimated(isAnimated); + } + + @ReactProp(name="heading") + public void setHeading(RCTMGLMapView mapView, double heading) { + mapView.setHeading(heading); + } + + @ReactProp(name="pitch") + public void setPitch(RCTMGLMapView mapView, double pitch) { + mapView.setPitch(pitch); + } + + @ReactProp(name="zoomLevel") + public void setZoomLevel(RCTMGLMapView mapView, double zoomLevel) { + mapView.setZoomLevel(zoomLevel); + } + + @ReactProp(name="centerCoordinate") + public void setCenterCoordinate(RCTMGLMapView mapView, ReadableMap map) { + Point centerCoordinate = MGLGeoUtils.readableMapToPoint(map); + + if (centerCoordinate != null) { + mapView.setCenterCoordinate(centerCoordinate); + } + } + + @Override + public Map customEvents() { + return MapBuilder.builder() + .put(RCTMGLEventNames.MAP_CLICK, "onPress") + .put(RCTMGLEventNames.MAP_LONG_CLICK,"onLongPress") + .build(); + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/events/IRCTMGLEvent.java b/android/rctmgl/src/main/java/mapbox/rctmgl/events/IRCTMGLEvent.java new file mode 100644 index 0000000..2bdf21e --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/events/IRCTMGLEvent.java @@ -0,0 +1,13 @@ +package mapbox.rctmgl.events; + +import com.facebook.react.bridge.WritableMap; + +/** + * Created by nickitaliano on 8/23/17. + */ + +public interface IRCTMGLEvent { + int getID(); + String getName(); + WritableMap toWritableMap(); +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLEventNames.java b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLEventNames.java new file mode 100644 index 0000000..2249be0 --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLEventNames.java @@ -0,0 +1,10 @@ +package mapbox.rctmgl.events; + +/** + * Created by nickitaliano on 8/24/17. + */ + +public class RCTMGLEventNames { + public static final String MAP_CLICK = "map_press"; + public static final String MAP_LONG_CLICK = "map_longpress"; +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapClickEvent.java b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapClickEvent.java new file mode 100644 index 0000000..929255d --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapClickEvent.java @@ -0,0 +1,40 @@ +package mapbox.rctmgl.events; + +import android.view.View; + +import com.facebook.react.bridge.WritableMap; +import com.mapbox.mapboxsdk.geometry.LatLng; + +import mapbox.rctmgl.utils.MGLGeoUtils; + +/** + * Created by nickitaliano on 8/23/17. + */ + +public class RCTMGLMapClickEvent implements IRCTMGLEvent { + private int mTagID; + private LatLng mTouchedLatLng; + + public RCTMGLMapClickEvent(View view) { + mTagID = view.getId(); + } + + public String getName() { + return RCTMGLEventNames.MAP_CLICK; + } + + public int getID() { + return mTagID; + } + + public WritableMap toWritableMap() { + return MGLGeoUtils.latLngToWritableMap(mTouchedLatLng); + } + + public void setLatLng(LatLng touchedLatLng) { + if (touchedLatLng == null) { + return; + } + mTouchedLatLng = touchedLatLng; + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapLongClickEvent.java b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapLongClickEvent.java new file mode 100644 index 0000000..6941956 --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/events/RCTMGLMapLongClickEvent.java @@ -0,0 +1,18 @@ +package mapbox.rctmgl.events; + +import android.view.View; + +/** + * Created by nickitaliano on 8/24/17. + */ + +public class RCTMGLMapLongClickEvent extends RCTMGLMapClickEvent { + public RCTMGLMapLongClickEvent(View view) { + super(view); + } + + @Override + public String getName() { + return RCTMGLEventNames.MAP_LONG_CLICK; + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/modules/RCTMGLModule.java b/android/rctmgl/src/main/java/mapbox/rctmgl/modules/RCTMGLModule.java new file mode 100644 index 0000000..2b59f7b --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/modules/RCTMGLModule.java @@ -0,0 +1,92 @@ +package mapbox.rctmgl.modules; + +import android.Manifest; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +import com.facebook.react.bridge.Arguments; +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.WritableMap; +import com.mapbox.mapboxsdk.Mapbox; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by nickitaliano on 8/18/17. + */ + +public class RCTMGLModule extends ReactContextBaseJavaModule { + public static final String REACT_CLASS = RCTMGLModule.class.getSimpleName(); + + private Handler mUiThreadHandler; + + public RCTMGLModule(ReactApplicationContext reactApplicationContext) { + super(reactApplicationContext); + mUiThreadHandler = new Handler(Looper.getMainLooper()); + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @ReactMethod + public void setAccessToken(final String accessToken) { + mUiThreadHandler.post(new Runnable() { + @Override + public void run() { + Mapbox.getInstance(getReactApplicationContext(), accessToken); + } + }); + } + + @ReactMethod + public void getAccessToken(Promise promise) { + WritableMap map = Arguments.createMap(); + map.putString("accessToken", Mapbox.getAccessToken()); + promise.resolve(map); + } + + @ReactMethod + public void requestPermissions(Promise promise) { + if (Build.VERSION.SDK_INT < 23) { + return; + } + + requestPermissionsIfNeeded(new String[]{ + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION + }); + } + + private void requestPermissionsIfNeeded(final String[] permissions) { + Activity curActivity = getCurrentActivity(); + if (curActivity == null) { + return; + } + + List permissionsToCheck = new ArrayList<>(); + for (String permission: permissions) { + int permissionCheck = ContextCompat.checkSelfPermission(curActivity, permission); + + if (permissionCheck != PackageManager.PERMISSION_GRANTED) { + permissionsToCheck.add(permission); + } + } + + int checkCount = permissionsToCheck.size(); + if (checkCount > 0) { + ActivityCompat.requestPermissions( + curActivity, permissionsToCheck.toArray(new String[checkCount]), 0); + } + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/utils/MGLGeoUtils.java b/android/rctmgl/src/main/java/mapbox/rctmgl/utils/MGLGeoUtils.java new file mode 100644 index 0000000..f382b74 --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/utils/MGLGeoUtils.java @@ -0,0 +1,66 @@ +package mapbox.rctmgl.utils; + +import com.facebook.react.bridge.Arguments; +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.bridge.WritableNativeArray; +import com.facebook.react.bridge.WritableNativeMap; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.services.android.telemetry.constants.GeoConstants; +import com.mapbox.services.commons.geojson.GeoJSON; +import com.mapbox.services.commons.geojson.Geometry; +import com.mapbox.services.commons.geojson.Point; +import com.mapbox.services.commons.models.Position; +import com.mapbox.services.commons.geojson.custom.GeometryDeserializer; + +import java.util.List; + +/** + * Created by nickitaliano on 8/18/17. + */ + +public class MGLGeoUtils { + public static WritableMap latLngToWritableMap(LatLng point) { + WritableMap map = Arguments.createMap(); + map.putString("type", "Point"); + + WritableArray coordinates = Arguments.createArray(); + coordinates.pushDouble(point.getLongitude()); + coordinates.pushDouble(point.getLatitude()); + map.putArray("coordinates", coordinates); + + return map; + } + + public static LatLng pointToLatLng(Point point) { + if (point == null) { + return null; + } + + Position position = point.getCoordinates(); + if (position == null) { + return null; + } + + return new LatLng(position.getLatitude(), position.getLongitude()); + } + + public static Point readableMapToPoint(ReadableMap map) { + final String type = map.getString("type"); + + if (!type.equals("Point")) { + return null; + } + + return Point.fromCoordinates(readableArrayToDoubleArray(map.getArray("coordinates"))); + } + + private static double[] readableArrayToDoubleArray(ReadableArray coordArray) { + if (coordArray == null && coordArray.size() >= 2) { + return null; + } + return new double[]{ coordArray.getDouble(0), coordArray.getDouble(1) }; + } +} diff --git a/android/rctmgl/src/main/java/mapbox/rctmgl/utils/RCTMGLMapStyleURL.java b/android/rctmgl/src/main/java/mapbox/rctmgl/utils/RCTMGLMapStyleURL.java new file mode 100644 index 0000000..db5048b --- /dev/null +++ b/android/rctmgl/src/main/java/mapbox/rctmgl/utils/RCTMGLMapStyleURL.java @@ -0,0 +1,41 @@ +package mapbox.rctmgl.utils; + +import com.mapbox.mapboxsdk.constants.Style; + +/** + * Created by nickitaliano on 8/21/17. + */ + + +public enum RCTMGLMapStyleURL { + Streets("mapbox-streets", Style.MAPBOX_STREETS), + Dark("mapbox-dark", Style.DARK), + Light("mapbox-light", Style.LIGHT), + Outdoors("mapbox-outdoors", Style.OUTDOORS), + Satellite("mapbox-satellite", Style.SATELLITE); + + private String mKey; + private String mURL; + + RCTMGLMapStyleURL(String key, String url) { + mKey = key; + mURL = url; + } + + private String getKey() { + return mKey; + } + + public String getURL() { + return mURL; + } + + public static RCTMGLMapStyleURL fromKey(String key) { + for (RCTMGLMapStyleURL mapStyle: RCTMGLMapStyleURL.values()) { + if (mapStyle.getKey().equals(key)) { + return mapStyle; + } + } + return null; + } +} diff --git a/ios/RCTMGL/RCTMGLMapView.h b/ios/RCTMGL/RCTMGLMapView.h index fe789ff..9bdc03d 100644 --- a/ios/RCTMGL/RCTMGLMapView.h +++ b/ios/RCTMGL/RCTMGLMapView.h @@ -19,4 +19,7 @@ @property (nonatomic, assign) double pitch; @property (nonatomic, assign) double reactZoomLevel; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; +@property (nonatomic, copy) RCTBubblingEventBlock onLongPress; + @end diff --git a/ios/RCTMGL/RCTMGLMapView.m b/ios/RCTMGL/RCTMGLMapView.m index e5c3bec..81172b2 100644 --- a/ios/RCTMGL/RCTMGLMapView.m +++ b/ios/RCTMGL/RCTMGLMapView.m @@ -68,7 +68,17 @@ self.zoomLevel = reactZoomLevel; } -- (NSURL*) getStyleURLFromKey:(NSString *)styleKey +- (void)setOnPress:(RCTBubblingEventBlock)onPress +{ + _onPress = onPress; +} + +- (void)setOnLongPress:(RCTBubblingEventBlock)onLongPress +{ + _onLongPress = onLongPress; +} + +- (NSURL*)getStyleURLFromKey:(NSString *)styleKey { NSString *styleURL = [_mapStyleURLS objectForKey:styleKey]; diff --git a/ios/RCTMGL/RCTMGLMapViewManager.h b/ios/RCTMGL/RCTMGLMapViewManager.h index 250ca64..f090b24 100644 --- a/ios/RCTMGL/RCTMGLMapViewManager.h +++ b/ios/RCTMGL/RCTMGLMapViewManager.h @@ -10,4 +10,7 @@ @interface RCTMGLMapViewManager : RCTViewManager +- (void)didTapMap:(UITapGestureRecognizer *)recognizer; +- (void)didLongPressMap:(UILongPressGestureRecognizer *)recognizer; + @end diff --git a/ios/RCTMGL/RCTMGLMapViewManager.m b/ios/RCTMGL/RCTMGLMapViewManager.m index e5e262c..f40c4d4 100644 --- a/ios/RCTMGL/RCTMGLMapViewManager.m +++ b/ios/RCTMGL/RCTMGLMapViewManager.m @@ -20,8 +20,14 @@ RCT_EXPORT_MODULE() - (UIView *)view { + // setup map gesture recongizers + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapMap:)]; + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPressMap:)]; + RCTMGLMapView *mapView = [RCTMGLMapView new]; mapView.delegate = self; + [mapView addGestureRecognizer:tap]; + [mapView addGestureRecognizer:longPress]; return mapView; } @@ -32,4 +38,38 @@ RCT_EXPORT_VIEW_PROPERTY(heading, double) RCT_EXPORT_VIEW_PROPERTY(pitch, double) RCT_REMAP_VIEW_PROPERTY(zoomLevel, reactZoomLevel, double) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLongPress, RCTBubblingEventBlock) + +- (void)didTapMap:(UITapGestureRecognizer *)recognizer +{ + RCTMGLMapView *mapView = (RCTMGLMapView*)recognizer.view; + + if (mapView == nil || mapView.onPress == nil) { + return; + } + + mapView.onPress([self convertXYPointToGeoJSONPoint:mapView atPoint:[recognizer locationInView:mapView]]); +} + +- (void)didLongPressMap:(UILongPressGestureRecognizer *)recognizer +{ + RCTMGLMapView *mapView = (RCTMGLMapView*)recognizer.view; + + if (mapView == nil || mapView.onPress == nil) { + return; + } + + mapView.onLongPress([self convertXYPointToGeoJSONPoint:mapView atPoint:[recognizer locationInView:mapView]]); +} + +- (NSDictionary*)convertXYPointToGeoJSONPoint:(RCTMGLMapView*)mapView atPoint:(CGPoint)point +{ + CLLocationCoordinate2D coord = [mapView convertPoint:point toCoordinateFromView:mapView]; + return @{ + @"type": @"Point", + @"coordinates": @[[NSNumber numberWithDouble:coord.longitude], [NSNumber numberWithDouble:coord.latitude]] + }; +} + @end diff --git a/javascript/components/MapView.js b/javascript/components/MapView.js index 7ae82d5..8bb4c37 100644 --- a/javascript/components/MapView.js +++ b/javascript/components/MapView.js @@ -56,6 +56,16 @@ class MapView extends React.Component { * Initial zoom level of map */ zoomLevel: PropTypes.number, + + /** + * Map press listener, gets called when a user presses the map + */ + onPress: PropTypes.func, + + /** + * Map long press listener, gets called when a user long presses the map + */ + onLongPress: PropTypes.func, }; static defaultProps = { @@ -67,8 +77,32 @@ class MapView extends React.Component { styleURL: MapView.StyleURL.Street, }; + constructor (props) { + super(props); + + this.onPress = this.onPress.bind(this); + this.onLongPress = this.onLongPress.bind(this); + } + + onPress (e) { + if (typeof this.props.onPress === 'function') { + this.props.onPress(e.nativeEvent); + } + } + + onLongPress (e) { + if (typeof this.props.onLongPress === 'function') { + this.props.onLongPress(e.nativeEvent); + } + } + render () { - return ; + return ( + + ); } }