Skip to content

Commit

Permalink
Support Map.Overlay for iOS (#2022)
Browse files Browse the repository at this point in the history
* Support Map.Overlay for iOS

* change using image in example

* add overlay support to Google Maps API for iOS

* documented limitation in Overlay for iOS

* fix MKMapRectMake params from 'bottom to top' to 'top to bottom'

* remove limitation
  • Loading branch information
smellman authored and rborn committed Feb 20, 2018
1 parent 53b374b commit 42ff69a
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 5 deletions.
2 changes: 1 addition & 1 deletion docs/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ type LatLng {
latitude: Number,
longitude: Number,
}
```
```
8 changes: 4 additions & 4 deletions example/examples/ImageOverlayWithURL.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const OVERLAY_TOP_LEFT_COORDINATE1 = [35.68184060244454, 139.76531982421875];
const OVERLAY_BOTTOM_RIGHT_COORDINATE1 = [35.679609609368576, 139.76806640625];
const IMAGE_URL1 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51613.png';
// 116423, 51614, 17
const OVERLAY_TOP_LEFT_COORDINATE2 = [35.679609609368576, 139.76531982421875];
const OVERLAY_BOTTOM_RIGHT_COORDINATE2 = [35.67737855391474, 139.76806640625];
const IMAGE_URL2 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51614.png';
// 116423, 51615, 17
const OVERLAY_TOP_LEFT_COORDINATE2 = [35.67737855391474, 139.76531982421875];
const OVERLAY_BOTTOM_RIGHT_COORDINATE2 = [35.67514743608467, 139.76806640625];
const IMAGE_URL2 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51615.png';

export default class ImageOverlayWithURL extends Component {

Expand Down
1 change: 1 addition & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
@property (nonatomic, strong) NSMutableArray *polylines;
@property (nonatomic, strong) NSMutableArray *circles;
@property (nonatomic, strong) NSMutableArray *tiles;
@property (nonatomic, strong) NSMutableArray *overlays;

@property (nonatomic, assign) BOOL showsBuildings;
@property (nonatomic, assign) BOOL showsTraffic;
Expand Down
10 changes: 10 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "AIRGoogleMapPolyline.h"
#import "AIRGoogleMapCircle.h"
#import "AIRGoogleMapUrlTile.h"
#import "AIRGoogleMapOverlay.h"
#import <GoogleMaps/GoogleMaps.h>
#import <MapKit/MapKit.h>
#import <React/UIView+React.h>
Expand Down Expand Up @@ -50,6 +51,7 @@ - (instancetype)init
_polylines = [NSMutableArray array];
_circles = [NSMutableArray array];
_tiles = [NSMutableArray array];
_overlays = [NSMutableArray array];
_initialRegion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0.0, 0.0), MKCoordinateSpanMake(0.0, 0.0));
_region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0.0, 0.0), MKCoordinateSpanMake(0.0, 0.0));
_initialRegionSetOnLoad = false;
Expand Down Expand Up @@ -99,6 +101,10 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
AIRGoogleMapUrlTile *tile = (AIRGoogleMapUrlTile*)subview;
tile.tileLayer.map = self;
[self.tiles addObject:tile];
} else if ([subview isKindOfClass:[AIRGoogleMapOverlay class]]) {
AIRGoogleMapOverlay *overlay = (AIRGoogleMapOverlay*)subview;
overlay.overlay.map = self;
[self.overlays addObject:overlay];
} else {
NSArray<id<RCTComponent>> *childSubviews = [subview reactSubviews];
for (int i = 0; i < childSubviews.count; i++) {
Expand Down Expand Up @@ -135,6 +141,10 @@ - (void)removeReactSubview:(id<RCTComponent>)subview {
AIRGoogleMapUrlTile *tile = (AIRGoogleMapUrlTile*)subview;
tile.tileLayer.map = nil;
[self.tiles removeObject:tile];
} else if ([subview isKindOfClass:[AIRGoogleMapOverlay class]]) {
AIRGoogleMapOverlay *overlay = (AIRGoogleMapOverlay*)subview;
overlay.overlay.map = nil;
[self.overlays removeObject:overlay];
} else {
NSArray<id<RCTComponent>> *childSubviews = [subview reactSubviews];
for (int i = 0; i < childSubviews.count; i++) {
Expand Down
23 changes: 23 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapOverlay.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// AIRGoogleMapOverlay.h
//
// Created by Taro Matsuzawa on 5/3/17.
//

#import <Foundation/Foundation.h>
#import <GoogleMaps/GoogleMaps.h>
#import <React/RCTBridge.h>
#import "AIRMapCoordinate.h"
#import "AIRGoogleMap.h"

@interface AIRGoogleMapOverlay : UIView

@property (nonatomic, strong) GMSGroundOverlay *overlay;
@property (nonatomic, copy) NSString *imageSrc;
@property (nonatomic, strong, readonly) UIImage *overlayImage;
@property (nonatomic, copy) NSArray *boundsRect;
@property (nonatomic, readonly) GMSCoordinateBounds *overlayBounds;

@property (nonatomic, weak) RCTBridge *bridge;

@end
76 changes: 76 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapOverlay.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// AIRGoogleMapOverlay.m
// Created by Nick Italiano on 3/5/17.
//

#import "AIRGoogleMapOverlay.h"

#import <React/RCTEventDispatcher.h>
#import <React/RCTImageLoader.h>
#import <React/RCTUtils.h>
#import <React/UIView+React.h>

@interface AIRGoogleMapOverlay()
@property (nonatomic, strong, readwrite) UIImage *overlayImage;
@property (nonatomic, readwrite) GMSCoordinateBounds *overlayBounds;
@end

@implementation AIRGoogleMapOverlay {
RCTImageLoaderCancellationBlock _reloadImageCancellationBlock;
CLLocationCoordinate2D _southWest;
CLLocationCoordinate2D _northEast;
}

- (instancetype)init
{
if ((self = [super init])) {
_overlay = [[GMSGroundOverlay alloc] init];
}
return self;
}

- (void)setImageSrc:(NSString *)imageSrc
{
NSLog(@">>> SET IMAGESRC: %@", imageSrc);
_imageSrc = imageSrc;

if (_reloadImageCancellationBlock) {
_reloadImageCancellationBlock();
_reloadImageCancellationBlock = nil;
}

__weak typeof(self) weakSelf = self;
_reloadImageCancellationBlock = [_bridge.imageLoader loadImageWithURLRequest:[RCTConvert NSURLRequest:_imageSrc]
size:weakSelf.bounds.size
scale:RCTScreenScale()
clipped:YES
resizeMode:RCTResizeModeCenter
progressBlock:nil
partialLoadBlock:nil
completionBlock:^(NSError *error, UIImage *image) {
if (error) {
NSLog(@"%@", error);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@">>> IMAGE: %@", image);
weakSelf.overlayImage = image;
weakSelf.overlay.icon = image;
});
}];

}

- (void)setBoundsRect:(NSArray *)boundsRect
{
_boundsRect = boundsRect;

_southWest = CLLocationCoordinate2DMake([boundsRect[1][0] doubleValue], [boundsRect[0][1] doubleValue]);
_northEast = CLLocationCoordinate2DMake([boundsRect[0][0] doubleValue], [boundsRect[1][1] doubleValue]);

_overlayBounds = [[GMSCoordinateBounds alloc] initWithCoordinate:_southWest
coordinate:_northEast];

_overlay.bounds = _overlayBounds;
}

@end
10 changes: 10 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapOverlayManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// AIRGoogleMapOverlayManager.h
// Created by Taro Matsuzawa on 3/5/17.
//

#import <Foundation/Foundation.h>
#import <React/RCTViewManager.h>

@interface AIRGoogleMapOverlayManager : RCTViewManager
@end
22 changes: 22 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapOverlayManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#import "AIRGoogleMapOverlayManager.h"
#import "AIRGoogleMapOverlay.h"

@interface AIRGoogleMapOverlayManager()

@end

@implementation AIRGoogleMapOverlayManager

RCT_EXPORT_MODULE()

- (UIView *)view
{
AIRGoogleMapOverlay *overlay = [AIRGoogleMapOverlay new];
overlay.bridge = self.bridge;
return overlay;
}

RCT_REMAP_VIEW_PROPERTY(bounds, boundsRect, NSArray)
RCT_REMAP_VIEW_PROPERTY(image, imageSrc, NSString)

@end
18 changes: 18 additions & 0 deletions lib/ios/AirMaps.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
1125B2F21C4AD445007D0023 /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1125B2F11C4AD445007D0023 /* SMCalloutView.m */; };
19DABC7F1E7C9D3C00F41150 /* RCTConvert+AirMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 19DABC7E1E7C9D3C00F41150 /* RCTConvert+AirMap.m */; };
2163AA501FEAEDD100BBEC95 /* AIRMapPolylineRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2163AA4F1FEAEDD100BBEC95 /* AIRMapPolylineRenderer.m */; };
53D31636202E723B00B55447 /* AIRMapOverlayManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D31635202E723B00B55447 /* AIRMapOverlayManager.m */; };
53D3163A202E72FC00B55447 /* AIRMapOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D31639202E72FC00B55447 /* AIRMapOverlay.m */; };
53D3163D202E734F00B55447 /* AIRMapOverlayRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D3163C202E734F00B55447 /* AIRMapOverlayRenderer.m */; };
628F81201FD16DF80058313A /* AIRMapLocalTile.m in Sources */ = {isa = PBXBuildFile; fileRef = 628F811F1FD16DF80058313A /* AIRMapLocalTile.m */; };
628F81231FD16EFA0058313A /* AIRMapLocalTileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 628F81221FD16EFA0058313A /* AIRMapLocalTileManager.m */; };
62AEC4D41FD5A0AA003225E0 /* AIRMapLocalTileOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AEC4D31FD5A0AA003225E0 /* AIRMapLocalTileOverlay.m */; };
Expand Down Expand Up @@ -76,6 +79,12 @@
19DABC7E1E7C9D3C00F41150 /* RCTConvert+AirMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+AirMap.m"; sourceTree = "<group>"; };
2163AA4E1FEAEDD100BBEC95 /* AIRMapPolylineRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIRMapPolylineRenderer.h; sourceTree = "<group>"; };
2163AA4F1FEAEDD100BBEC95 /* AIRMapPolylineRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AIRMapPolylineRenderer.m; sourceTree = "<group>"; };
53D31635202E723B00B55447 /* AIRMapOverlayManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AIRMapOverlayManager.m; sourceTree = "<group>"; };
53D31637202E725E00B55447 /* AIRMapOverlayManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIRMapOverlayManager.h; sourceTree = "<group>"; };
53D31638202E72D500B55447 /* AIRMapOverlay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIRMapOverlay.h; sourceTree = "<group>"; };
53D31639202E72FC00B55447 /* AIRMapOverlay.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AIRMapOverlay.m; sourceTree = "<group>"; };
53D3163B202E732300B55447 /* AIRMapOverlayRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIRMapOverlayRenderer.h; sourceTree = "<group>"; };
53D3163C202E734F00B55447 /* AIRMapOverlayRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AIRMapOverlayRenderer.m; sourceTree = "<group>"; };
628F811E1FD16D780058313A /* AIRMapLocalTile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIRMapLocalTile.h; sourceTree = "<group>"; };
628F811F1FD16DF80058313A /* AIRMapLocalTile.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AIRMapLocalTile.m; sourceTree = "<group>"; };
628F81211FD16EAB0058313A /* AIRMapLocalTileManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AIRMapLocalTileManager.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -158,6 +167,12 @@
628F811F1FD16DF80058313A /* AIRMapLocalTile.m */,
628F81211FD16EAB0058313A /* AIRMapLocalTileManager.h */,
628F81221FD16EFA0058313A /* AIRMapLocalTileManager.m */,
53D31638202E72D500B55447 /* AIRMapOverlay.h */,
53D31639202E72FC00B55447 /* AIRMapOverlay.m */,
53D31637202E725E00B55447 /* AIRMapOverlayManager.h */,
53D31635202E723B00B55447 /* AIRMapOverlayManager.m */,
53D3163B202E732300B55447 /* AIRMapOverlayRenderer.h */,
53D3163C202E734F00B55447 /* AIRMapOverlayRenderer.m */,
);
path = AirMaps;
sourceTree = "<group>";
Expand Down Expand Up @@ -222,13 +237,16 @@
1125B2E31C4AD3DA007D0023 /* AIRMapPolygon.m in Sources */,
1125B2E41C4AD3DA007D0023 /* AIRMapPolygonManager.m in Sources */,
1125B2DB1C4AD3DA007D0023 /* AIRMapCallout.m in Sources */,
53D31636202E723B00B55447 /* AIRMapOverlayManager.m in Sources */,
1125B2E01C4AD3DA007D0023 /* AIRMapManager.m in Sources */,
1125B2E61C4AD3DA007D0023 /* AIRMapPolylineManager.m in Sources */,
1125B2DD1C4AD3DA007D0023 /* AIRMapCircle.m in Sources */,
19DABC7F1E7C9D3C00F41150 /* RCTConvert+AirMap.m in Sources */,
1125B2E51C4AD3DA007D0023 /* AIRMapPolyline.m in Sources */,
DA6C263E1C9E324A0035349F /* AIRMapUrlTileManager.m in Sources */,
53D3163A202E72FC00B55447 /* AIRMapOverlay.m in Sources */,
628F81201FD16DF80058313A /* AIRMapLocalTile.m in Sources */,
53D3163D202E734F00B55447 /* AIRMapOverlayRenderer.m in Sources */,
1125B2DA1C4AD3DA007D0023 /* AIRMap.m in Sources */,
1125B2DF1C4AD3DA007D0023 /* AIRMapCoordinate.m in Sources */,
1125B2F21C4AD445007D0023 /* SMCalloutView.m in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions lib/ios/AirMaps/AIRMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#import <QuartzCore/QuartzCore.h>
#import "AIRMapUrlTile.h"
#import "AIRMapLocalTile.h"
#import "AIRMapOverlay.h"

const CLLocationDegrees AIRMapDefaultSpan = 0.005;
const NSTimeInterval AIRMapRegionChangeObserveInterval = 0.1;
Expand Down Expand Up @@ -122,6 +123,9 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
} else if ([subview isKindOfClass:[AIRMapLocalTile class]]) {
((AIRMapLocalTile *)subview).map = self;
[self addOverlay:(id<MKOverlay>)subview];
} else if ([subview isKindOfClass:[AIRMapOverlay class]]) {
((AIRMapOverlay *)subview).map = self;
[self addOverlay:(id<MKOverlay>)subview];
} else {
NSArray<id<RCTComponent>> *childSubviews = [subview reactSubviews];
for (int i = 0; i < childSubviews.count; i++) {
Expand Down Expand Up @@ -149,6 +153,8 @@ - (void)removeReactSubview:(id<RCTComponent>)subview {
[self removeOverlay:(id <MKOverlay>) subview];
} else if ([subview isKindOfClass:[AIRMapLocalTile class]]) {
[self removeOverlay:(id <MKOverlay>) subview];
} else if ([subview isKindOfClass:[AIRMapOverlay class]]) {
[self removeOverlay:(id <MKOverlay>) subview];
} else {
NSArray<id<RCTComponent>> *childSubviews = [subview reactSubviews];
for (int i = 0; i < childSubviews.count; i++) {
Expand Down
21 changes: 21 additions & 0 deletions lib/ios/AirMaps/AIRMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "AIRMapLocalTile.h"
#import "AIRMapSnapshot.h"
#import "RCTConvert+AirMap.h"
#import "AIRMapOverlay.h"

#import <MapKit/MapKit.h>

Expand Down Expand Up @@ -456,6 +457,24 @@ - (void)handleMapTap:(UITapGestureRecognizer *)recognizer {
}
}
}

if ([overlay isKindOfClass:[AIRMapOverlay class]]) {
AIRMapOverlay *imageOverlay = (AIRMapOverlay*) overlay;
if (MKMapRectContainsPoint(imageOverlay.boundingMapRect, mapPoint)) {
if (imageOverlay.onPress) {
id event = @{
@"action": @"image-overlay-press",
@"name": imageOverlay.name ?: @"unknown",
@"coordinate": @{
@"latitude": @(imageOverlay.coordinate.latitude),
@"longitude": @(imageOverlay.coordinate.longitude)
}
};
imageOverlay.onPress(event);
}
}
}

}

if (nearestDistance <= maxMeters) {
Expand Down Expand Up @@ -537,6 +556,8 @@ - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOv
return ((AIRMapUrlTile *)overlay).renderer;
} else if ([overlay isKindOfClass:[AIRMapLocalTile class]]) {
return ((AIRMapLocalTile *)overlay).renderer;
} else if ([overlay isKindOfClass:[AIRMapOverlay class]]) {
return ((AIRMapOverlay *)overlay).renderer;
} else if([overlay isKindOfClass:[MKTileOverlay class]]) {
return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
} else {
Expand Down
36 changes: 36 additions & 0 deletions lib/ios/AirMaps/AIRMapOverlay.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#import "AIRMapCallout.h"

#import <MapKit/MapKit.h>
#import <UIKit/UIKit.h>

#import "RCTConvert+AirMap.h"
#import <React/RCTComponent.h>
#import "AIRMap.h"
#import "AIRMapOverlayRenderer.h"

@class RCTBridge;

@interface AIRMapOverlay : UIView <MKOverlay>

@property (nonatomic, strong) AIRMapOverlayRenderer *renderer;
@property (nonatomic, weak) AIRMap *map;
@property (nonatomic, weak) RCTBridge *bridge;

@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) NSString *imageSrc;
@property (nonatomic, strong, readonly) UIImage *overlayImage;
@property (nonatomic, copy) NSArray *boundsRect;
@property (nonatomic, assign) NSInteger rotation;
@property (nonatomic, assign) CGFloat transparency;
@property (nonatomic, assign) NSInteger zIndex;

@property (nonatomic, copy) RCTBubblingEventBlock onPress;

#pragma mark MKOverlay protocol

@property(nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property(nonatomic, readonly) MKMapRect boundingMapRect;
- (BOOL)intersectsMapRect:(MKMapRect)mapRect;
- (BOOL)canReplaceMapContent;

@end
Loading

0 comments on commit 42ff69a

Please sign in to comment.