Skip to content

Commit d8818c2

Browse files
author
Spike Brehm
authored
Merge pull request react-native-maps#760 from boundlessgeo/overlayOnPress
Add onPress for polygons and polylines on iOS and Android
2 parents ab1ecd8 + b11e2af commit d8818c2

File tree

10 files changed

+169
-17
lines changed

10 files changed

+169
-17
lines changed

android/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public Object getFeature() {
102102
@Override
103103
public void addToMap(GoogleMap map) {
104104
polygon = map.addPolygon(getPolygonOptions());
105+
polygon.setClickable(true);
105106
}
106107

107108
@Override

android/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88

99
import com.facebook.react.bridge.ReactApplicationContext;
1010
import com.facebook.react.bridge.ReadableArray;
11+
import com.facebook.react.common.MapBuilder;
1112
import com.facebook.react.uimanager.ThemedReactContext;
1213
import com.facebook.react.uimanager.ViewGroupManager;
1314
import com.facebook.react.uimanager.annotations.ReactProp;
1415

16+
import java.util.HashMap;
17+
import java.util.Map;
18+
import javax.annotation.Nullable;
19+
1520
public class AirMapPolygonManager extends ViewGroupManager<AirMapPolygon> {
1621
private final DisplayMetrics metrics;
1722

@@ -67,4 +72,12 @@ public void setGeodesic(AirMapPolygon view, boolean geodesic) {
6772
public void setZIndex(AirMapPolygon view, float zIndex) {
6873
view.setZIndex(zIndex);
6974
}
75+
76+
@Override
77+
@Nullable
78+
public Map getExportedCustomDirectEventTypeConstants() {
79+
return MapBuilder.of(
80+
"onPress", MapBuilder.of("registrationName", "onPress")
81+
);
82+
}
7083
}

android/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public Object getFeature() {
9292
@Override
9393
public void addToMap(GoogleMap map) {
9494
polyline = map.addPolyline(getPolylineOptions());
95+
polyline.setClickable(true);
9596
}
9697

9798
@Override

android/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88

99
import com.facebook.react.bridge.ReactApplicationContext;
1010
import com.facebook.react.bridge.ReadableArray;
11+
import com.facebook.react.common.MapBuilder;
1112
import com.facebook.react.uimanager.ThemedReactContext;
1213
import com.facebook.react.uimanager.ViewGroupManager;
1314
import com.facebook.react.uimanager.annotations.ReactProp;
1415

16+
import java.util.HashMap;
17+
import java.util.Map;
18+
import javax.annotation.Nullable;
19+
1520
public class AirMapPolylineManager extends ViewGroupManager<AirMapPolyline> {
1621
private final DisplayMetrics metrics;
1722

@@ -62,4 +67,12 @@ public void setGeodesic(AirMapPolyline view, boolean geodesic) {
6267
public void setZIndex(AirMapPolyline view, float zIndex) {
6368
view.setZIndex(zIndex);
6469
}
70+
71+
@Override
72+
@Nullable
73+
public Map getExportedCustomDirectEventTypeConstants() {
74+
return MapBuilder.of(
75+
"onPress", MapBuilder.of("registrationName", "onPress")
76+
);
77+
}
6578
}

android/src/main/java/com/airbnb/android/react/maps/AirMapView.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import com.google.android.gms.maps.model.LatLng;
4040
import com.google.android.gms.maps.model.LatLngBounds;
4141
import com.google.android.gms.maps.model.Marker;
42+
import com.google.android.gms.maps.model.Polygon;
43+
import com.google.android.gms.maps.model.Polyline;
4244

4345
import java.util.ArrayList;
4446
import java.util.Arrays;
@@ -72,6 +74,8 @@ public class AirMapView extends MapView implements GoogleMap.InfoWindowAdapter,
7274

7375
private final List<AirMapFeature> features = new ArrayList<>();
7476
private final Map<Marker, AirMapMarker> markerMap = new HashMap<>();
77+
private final Map<Polyline, AirMapPolyline> polylineMap = new HashMap<>();
78+
private final Map<Polygon, AirMapPolygon> polygonMap = new HashMap<>();
7579
private final ScaleGestureDetector scaleDetector;
7680
private final GestureDetectorCompat gestureDetector;
7781
private final AirMapManager manager;
@@ -165,6 +169,24 @@ public boolean onMarkerClick(Marker marker) {
165169
}
166170
});
167171

172+
map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
173+
@Override
174+
public void onPolygonClick(Polygon polygon) {
175+
WritableMap event = makeClickEventData(polygon.getPoints().get(0));
176+
event.putString("action", "polygon-press");
177+
manager.pushEvent(polygonMap.get(polygon), "onPress", event);
178+
}
179+
});
180+
181+
map.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
182+
@Override
183+
public void onPolylineClick(Polyline polyline) {
184+
WritableMap event = makeClickEventData(polyline.getPoints().get(0));
185+
event.putString("action", "polyline-press");
186+
manager.pushEvent(polylineMap.get(polyline), "onPress", event);
187+
}
188+
});
189+
168190
map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
169191
@Override
170192
public void onInfoWindowClick(Marker marker) {
@@ -381,10 +403,14 @@ public void addFeature(View child, int index) {
381403
AirMapPolyline polylineView = (AirMapPolyline) child;
382404
polylineView.addToMap(map);
383405
features.add(index, polylineView);
406+
Polyline polyline = (Polyline) polylineView.getFeature();
407+
polylineMap.put(polyline, polylineView);
384408
} else if (child instanceof AirMapPolygon) {
385409
AirMapPolygon polygonView = (AirMapPolygon) child;
386410
polygonView.addToMap(map);
387411
features.add(index, polygonView);
412+
Polygon polygon = (Polygon) polygonView.getFeature();
413+
polygonMap.put(polygon, polygonView);
388414
} else if (child instanceof AirMapCircle) {
389415
AirMapCircle circleView = (AirMapCircle) child;
390416
circleView.addToMap(map);

ios/AirMaps/AIRMapManager.m

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ - (void)takeMapSnapshot:(AIRMap *)mapView
285285
[pin.image drawAtPoint:point];
286286
}
287287
}
288-
288+
289289
for (id <AIRMapSnapshot> overlay in mapView.overlays) {
290290
if ([overlay respondsToSelector:@selector(drawToSnapshot:context:)]) {
291291
[overlay drawToSnapshot:snapshot context:UIGraphicsGetCurrentContext()];
@@ -308,21 +308,75 @@ - (void)takeMapSnapshot:(AIRMap *)mapView
308308

309309
#pragma mark Gesture Recognizer Handlers
310310

311+
#define MAX_DISTANCE_PX 10.0f
311312
- (void)handleMapTap:(UITapGestureRecognizer *)recognizer {
312313
AIRMap *map = (AIRMap *)recognizer.view;
313-
if (!map.onPress) return;
314314

315-
CGPoint touchPoint = [recognizer locationInView:map];
316-
CLLocationCoordinate2D coord = [map convertPoint:touchPoint toCoordinateFromView:map];
315+
CGPoint tapPoint = [recognizer locationInView:map];
316+
CLLocationCoordinate2D tapCoordinate = [map convertPoint:tapPoint toCoordinateFromView:map];
317+
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoordinate);
318+
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
319+
320+
double maxMeters = [self metersFromPixel:MAX_DISTANCE_PX atPoint:tapPoint forMap:map];
321+
float nearestDistance = MAXFLOAT;
322+
AIRMapPolyline *nearestPolyline = nil;
323+
324+
for (id<MKOverlay> overlay in map.overlays) {
325+
if([overlay isKindOfClass:[AIRMapPolygon class]]){
326+
AIRMapPolygon *polygon = (AIRMapPolygon*) overlay;
327+
if (polygon.onPress) {
328+
CGMutablePathRef mpr = CGPathCreateMutable();
329+
330+
for(int i = 0; i < polygon.coordinates.count; i++) {
331+
AIRMapCoordinate *c = polygon.coordinates[i];
332+
MKMapPoint mp = MKMapPointForCoordinate(c.coordinate);
333+
if (i == 0) {
334+
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
335+
} else {
336+
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
337+
}
338+
}
339+
340+
if (CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE)) {
341+
id event = @{
342+
@"action": @"polygon-press",
343+
};
344+
polygon.onPress(event);
345+
}
346+
347+
CGPathRelease(mpr);
348+
}
349+
}
317350

351+
if([overlay isKindOfClass:[AIRMapPolyline class]]){
352+
AIRMapPolyline *polyline = (AIRMapPolyline*) overlay;
353+
if (polyline.onPress) {
354+
float distance = [self distanceOfPoint:MKMapPointForCoordinate(tapCoordinate)
355+
toPoly:overlay];
356+
if (distance < nearestDistance) {
357+
nearestDistance = distance;
358+
nearestPolyline = overlay;
359+
}
360+
}
361+
}
362+
}
363+
364+
if (nearestDistance <= maxMeters) {
365+
id event = @{
366+
@"action": @"polyline-press",
367+
};
368+
nearestPolyline.onPress(event);
369+
}
370+
371+
if (!map.onPress) return;
318372
map.onPress(@{
319373
@"coordinate": @{
320-
@"latitude": @(coord.latitude),
321-
@"longitude": @(coord.longitude),
374+
@"latitude": @(tapCoordinate.latitude),
375+
@"longitude": @(tapCoordinate.longitude),
322376
},
323377
@"position": @{
324-
@"x": @(touchPoint.x),
325-
@"y": @(touchPoint.y),
378+
@"x": @(tapPoint.x),
379+
@"y": @(tapPoint.y),
326380
},
327381
});
328382

@@ -609,4 +663,53 @@ - (void)_emitRegionChangeEvent:(AIRMap *)mapView continuous:(BOOL)continuous
609663
}
610664
}
611665

666+
/** Returns the distance of |pt| to |poly| in meters
667+
*
668+
*
669+
*/
670+
- (double)distanceOfPoint:(MKMapPoint)pt toPoly:(AIRMapPolyline *)poly
671+
{
672+
double distance = MAXFLOAT;
673+
for (int n = 0; n < poly.coordinates.count - 1; n++) {
674+
675+
MKMapPoint ptA = MKMapPointForCoordinate(poly.coordinates[n].coordinate);
676+
MKMapPoint ptB = MKMapPointForCoordinate(poly.coordinates[n + 1].coordinate);
677+
678+
double xDelta = ptB.x - ptA.x;
679+
double yDelta = ptB.y - ptA.y;
680+
681+
if (xDelta == 0.0 && yDelta == 0.0) {
682+
continue;
683+
}
684+
685+
double u = ((pt.x - ptA.x) * xDelta + (pt.y - ptA.y) * yDelta) / (xDelta * xDelta + yDelta * yDelta);
686+
MKMapPoint ptClosest;
687+
if (u < 0.0) {
688+
ptClosest = ptA;
689+
}
690+
else if (u > 1.0) {
691+
ptClosest = ptB;
692+
}
693+
else {
694+
ptClosest = MKMapPointMake(ptA.x + u * xDelta, ptA.y + u * yDelta);
695+
}
696+
697+
distance = MIN(distance, MKMetersBetweenMapPoints(ptClosest, pt));
698+
}
699+
700+
return distance;
701+
}
702+
703+
704+
/** Converts |px| to meters at location |pt| */
705+
- (double)metersFromPixel:(NSUInteger)px atPoint:(CGPoint)pt forMap:(AIRMap *)mapView
706+
{
707+
CGPoint ptB = CGPointMake(pt.x + px, pt.y);
708+
709+
CLLocationCoordinate2D coordA = [mapView convertPoint:pt toCoordinateFromView:mapView];
710+
CLLocationCoordinate2D coordB = [mapView convertPoint:ptB toCoordinateFromView:mapView];
711+
712+
return MKMetersBetweenMapPoints(MKMapPointForCoordinate(coordA), MKMapPointForCoordinate(coordB));
713+
}
714+
612715
@end

ios/AirMaps/AIRMapPolygon.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
@property (nonatomic, assign) CGLineJoin lineJoin;
3333
@property (nonatomic, assign) CGFloat lineDashPhase;
3434
@property (nonatomic, strong) NSArray <NSNumber *> *lineDashPattern;
35+
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
3536

3637
#pragma mark MKOverlay protocol
3738

ios/AirMaps/AIRMapPolygonManager.m

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ - (UIView *)view
4242
RCT_EXPORT_VIEW_PROPERTY(miterLimit, CGFloat)
4343
RCT_EXPORT_VIEW_PROPERTY(lineDashPhase, CGFloat)
4444
RCT_EXPORT_VIEW_PROPERTY(lineDashPattern, NSArray)
45+
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
4546

46-
// NOTE(lmr):
47-
// for now, onPress events for overlays will be left unimplemented. Seems it is possible with some work, but
48-
// it is difficult to achieve in both ios and android so I decided to leave it out.
49-
//RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
5047

5148
@end

ios/AirMaps/AIRMapPolyline.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
@property (nonatomic, assign) CGLineJoin lineJoin;
3232
@property (nonatomic, assign) CGFloat lineDashPhase;
3333
@property (nonatomic, strong) NSArray <NSNumber *> *lineDashPattern;
34+
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
3435

3536
#pragma mark MKOverlay protocol
3637

ios/AirMaps/AIRMapPolylineManager.m

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ - (UIView *)view
4141
RCT_EXPORT_VIEW_PROPERTY(miterLimit, CGFloat)
4242
RCT_EXPORT_VIEW_PROPERTY(lineDashPhase, CGFloat)
4343
RCT_EXPORT_VIEW_PROPERTY(lineDashPattern, NSArray)
44-
45-
// NOTE(lmr):
46-
// for now, onPress events for overlays will be left unimplemented. Seems it is possible with some work, but
47-
// it is difficult to achieve in both ios and android so I decided to leave it out.
48-
//RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
44+
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
4945

5046
@end

0 commit comments

Comments
 (0)