1// 2// AIRGoogleMapManager.m 3// AirMaps 4// 5// Created by Gil Birman on 9/1/16. 6// 7 8#ifdef HAVE_GOOGLE_MAPS 9 10 11#import "AIRGoogleMapManager.h" 12#import <React/RCTViewManager.h> 13#import <React/RCTBridge.h> 14#import <React/RCTUIManager.h> 15#import <React/RCTConvert+CoreLocation.h> 16#import <React/RCTEventDispatcher.h> 17#import <React/RCTViewManager.h> 18#import <React/RCTConvert.h> 19#import <React/UIView+React.h> 20#import "RCTConvert+GMSMapViewType.h" 21#import "AIRGoogleMap.h" 22#import "AIRMapMarker.h" 23#import "AIRMapPolyline.h" 24#import "AIRMapPolygon.h" 25#import "AIRMapCircle.h" 26#import "SMCalloutView.h" 27#import "AIRGoogleMapMarker.h" 28#import "RCTConvert+AirMap.h" 29 30#import <MapKit/MapKit.h> 31#import <QuartzCore/QuartzCore.h> 32 33static NSString *const RCTMapViewKey = @"MapView"; 34 35 36@interface AIRGoogleMapManager() <GMSMapViewDelegate> 37{ 38 BOOL didCallOnMapReady; 39} 40@end 41 42@implementation AIRGoogleMapManager 43 44RCT_EXPORT_MODULE() 45 46- (UIView *)view 47{ 48 [GMSServices setMetalRendererEnabled:YES]; 49 50 AIRGoogleMap *map = [AIRGoogleMap new]; 51 map.bridge = self.bridge; 52 map.delegate = self; 53 map.isAccessibilityElement = NO; 54 map.accessibilityElementsHidden = NO; 55 map.settings.consumesGesturesInView = NO; 56 57 UIPanGestureRecognizer *drag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapDrag:)]; 58 [drag setMinimumNumberOfTouches:1]; 59 [drag setMaximumNumberOfTouches:1]; 60 [map addGestureRecognizer:drag]; 61 62 UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapDrag:)]; 63 [map addGestureRecognizer:pinch]; 64 65 return map; 66} 67 68RCT_EXPORT_VIEW_PROPERTY(isAccessibilityElement, BOOL) 69RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString) 70RCT_EXPORT_VIEW_PROPERTY(initialCamera, GMSCameraPosition) 71RCT_REMAP_VIEW_PROPERTY(camera, cameraProp, GMSCameraPosition) 72RCT_EXPORT_VIEW_PROPERTY(initialRegion, MKCoordinateRegion) 73RCT_EXPORT_VIEW_PROPERTY(region, MKCoordinateRegion) 74RCT_EXPORT_VIEW_PROPERTY(showsBuildings, BOOL) 75RCT_EXPORT_VIEW_PROPERTY(showsCompass, BOOL) 76//RCT_EXPORT_VIEW_PROPERTY(showsScale, BOOL) // Not supported by GoogleMaps 77RCT_EXPORT_VIEW_PROPERTY(showsTraffic, BOOL) 78RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL) 79RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL) 80RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) 81RCT_EXPORT_VIEW_PROPERTY(scrollDuringRotateOrZoomEnabled, BOOL) 82RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL) 83RCT_EXPORT_VIEW_PROPERTY(zoomTapEnabled, BOOL) 84RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL) 85RCT_EXPORT_VIEW_PROPERTY(showsMyLocationButton, BOOL) 86RCT_EXPORT_VIEW_PROPERTY(showsIndoors, BOOL) 87RCT_EXPORT_VIEW_PROPERTY(showsIndoorLevelPicker, BOOL) 88RCT_EXPORT_VIEW_PROPERTY(customMapStyleString, NSString) 89RCT_EXPORT_VIEW_PROPERTY(mapPadding, UIEdgeInsets) 90RCT_REMAP_VIEW_PROPERTY(paddingAdjustmentBehavior, paddingAdjustmentBehaviorString, NSString) 91RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTBubblingEventBlock) 92RCT_EXPORT_VIEW_PROPERTY(onMapLoaded, RCTBubblingEventBlock) 93RCT_EXPORT_VIEW_PROPERTY(onKmlReady, RCTBubblingEventBlock) 94RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) 95RCT_EXPORT_VIEW_PROPERTY(onLongPress, RCTBubblingEventBlock) 96RCT_EXPORT_VIEW_PROPERTY(onPanDrag, RCTBubblingEventBlock) 97RCT_EXPORT_VIEW_PROPERTY(onUserLocationChange, RCTBubblingEventBlock) 98RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) 99RCT_EXPORT_VIEW_PROPERTY(onMarkerPress, RCTDirectEventBlock) 100RCT_EXPORT_VIEW_PROPERTY(onRegionChange, RCTDirectEventBlock) 101RCT_EXPORT_VIEW_PROPERTY(onRegionChangeComplete, RCTDirectEventBlock) 102RCT_EXPORT_VIEW_PROPERTY(onPoiClick, RCTDirectEventBlock) 103RCT_EXPORT_VIEW_PROPERTY(onIndoorLevelActivated, RCTDirectEventBlock) 104RCT_EXPORT_VIEW_PROPERTY(onIndoorBuildingFocused, RCTDirectEventBlock) 105RCT_EXPORT_VIEW_PROPERTY(mapType, GMSMapViewType) 106RCT_EXPORT_VIEW_PROPERTY(minZoomLevel, CGFloat) 107RCT_EXPORT_VIEW_PROPERTY(maxZoomLevel, CGFloat) 108RCT_EXPORT_VIEW_PROPERTY(kmlSrc, NSString) 109 110RCT_EXPORT_METHOD(getCamera:(nonnull NSNumber *)reactTag 111 resolver: (RCTPromiseResolveBlock)resolve 112 rejecter:(RCTPromiseRejectBlock)reject) 113{ 114 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 115 id view = viewRegistry[reactTag]; 116 if (![view isKindOfClass:[AIRGoogleMap class]]) { 117 reject(@"Invalid argument", [NSString stringWithFormat:@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view], NULL); 118 } else { 119 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 120 resolve(@{ 121 @"center": @{ 122 @"latitude": @(mapView.camera.target.latitude), 123 @"longitude": @(mapView.camera.target.longitude), 124 }, 125 @"pitch": @(mapView.camera.viewingAngle), 126 @"heading": @(mapView.camera.bearing), 127 @"zoom": @(mapView.camera.zoom), 128 }); 129 } 130 }]; 131} 132 133RCT_EXPORT_METHOD(setCamera:(nonnull NSNumber *)reactTag 134 camera:(id)json) 135{ 136 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 137 id view = viewRegistry[reactTag]; 138 if (![view isKindOfClass:[AIRGoogleMap class]]) { 139 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 140 } else { 141 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 142 GMSCameraPosition *camera = [RCTConvert GMSCameraPositionWithDefaults:json existingCamera:[mapView camera]]; 143 [mapView setCamera:camera]; 144 } 145 }]; 146} 147 148 149RCT_EXPORT_METHOD(animateCamera:(nonnull NSNumber *)reactTag 150 withCamera:(id)json 151 withDuration:(CGFloat)duration) 152{ 153 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 154 id view = viewRegistry[reactTag]; 155 if (![view isKindOfClass:[AIRGoogleMap class]]) { 156 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 157 } else { 158 [CATransaction begin]; 159 [CATransaction setAnimationDuration:duration/1000]; 160 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 161 GMSCameraPosition *camera = [RCTConvert GMSCameraPositionWithDefaults:json existingCamera:[mapView camera]]; 162 [mapView animateToCameraPosition:camera]; 163 [CATransaction commit]; 164 } 165 }]; 166} 167 168RCT_EXPORT_METHOD(animateToRegion:(nonnull NSNumber *)reactTag 169 withRegion:(MKCoordinateRegion)region 170 withDuration:(CGFloat)duration) 171{ 172 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 173 id view = viewRegistry[reactTag]; 174 if (![view isKindOfClass:[AIRGoogleMap class]]) { 175 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 176 } else { 177 // Core Animation must be used to control the animation's duration 178 // See http://stackoverflow.com/a/15663039/171744 179 [CATransaction begin]; 180 [CATransaction setAnimationDuration:duration/1000]; 181 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 182 GMSCameraPosition *camera = [AIRGoogleMap makeGMSCameraPositionFromMap:mapView andMKCoordinateRegion:region]; 183 [mapView animateToCameraPosition:camera]; 184 [CATransaction commit]; 185 } 186 }]; 187} 188 189RCT_EXPORT_METHOD(fitToElements:(nonnull NSNumber *)reactTag 190 edgePadding:(nonnull NSDictionary *)edgePadding 191 animated:(BOOL)animated) 192{ 193 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 194 id view = viewRegistry[reactTag]; 195 if (![view isKindOfClass:[AIRGoogleMap class]]) { 196 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 197 } else { 198 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 199 200 CLLocationCoordinate2D myLocation = ((AIRGoogleMapMarker *)(mapView.markers.firstObject)).realMarker.position; 201 GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:myLocation coordinate:myLocation]; 202 203 for (AIRGoogleMapMarker *marker in mapView.markers) 204 bounds = [bounds includingCoordinate:marker.realMarker.position]; 205 206 GMSCameraUpdate* cameraUpdate; 207 208 if ([edgePadding count] != 0) { 209 // Set Map viewport 210 CGFloat top = [RCTConvert CGFloat:edgePadding[@"top"]]; 211 CGFloat right = [RCTConvert CGFloat:edgePadding[@"right"]]; 212 CGFloat bottom = [RCTConvert CGFloat:edgePadding[@"bottom"]]; 213 CGFloat left = [RCTConvert CGFloat:edgePadding[@"left"]]; 214 215 cameraUpdate = [GMSCameraUpdate fitBounds:bounds withEdgeInsets:UIEdgeInsetsMake(top, left, bottom, right)]; 216 } else { 217 cameraUpdate = [GMSCameraUpdate fitBounds:bounds withPadding:55.0f]; 218 } 219 if (animated) { 220 [mapView animateWithCameraUpdate: cameraUpdate]; 221 } else { 222 [mapView moveCamera: cameraUpdate]; 223 } 224 } 225 }]; 226} 227 228RCT_EXPORT_METHOD(fitToSuppliedMarkers:(nonnull NSNumber *)reactTag 229 markers:(nonnull NSArray *)markers 230 edgePadding:(nonnull NSDictionary *)edgePadding 231 animated:(BOOL)animated) 232{ 233 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 234 id view = viewRegistry[reactTag]; 235 if (![view isKindOfClass:[AIRGoogleMap class]]) { 236 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 237 } else { 238 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 239 240 NSPredicate *filterMarkers = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { 241 AIRGoogleMapMarker *marker = (AIRGoogleMapMarker *)evaluatedObject; 242 return [marker isKindOfClass:[AIRGoogleMapMarker class]] && [markers containsObject:marker.identifier]; 243 }]; 244 245 NSArray *filteredMarkers = [mapView.markers filteredArrayUsingPredicate:filterMarkers]; 246 247 CLLocationCoordinate2D myLocation = ((AIRGoogleMapMarker *)(filteredMarkers.firstObject)).realMarker.position; 248 GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:myLocation coordinate:myLocation]; 249 250 for (AIRGoogleMapMarker *marker in filteredMarkers) 251 bounds = [bounds includingCoordinate:marker.realMarker.position]; 252 253 // Set Map viewport 254 CGFloat top = [RCTConvert CGFloat:edgePadding[@"top"]]; 255 CGFloat right = [RCTConvert CGFloat:edgePadding[@"right"]]; 256 CGFloat bottom = [RCTConvert CGFloat:edgePadding[@"bottom"]]; 257 CGFloat left = [RCTConvert CGFloat:edgePadding[@"left"]]; 258 259 GMSCameraUpdate* cameraUpdate = [GMSCameraUpdate fitBounds:bounds withEdgeInsets:UIEdgeInsetsMake(top, left, bottom, right)]; 260 if (animated) { 261 [mapView animateWithCameraUpdate:cameraUpdate 262 ]; 263 } else { 264 [mapView moveCamera: cameraUpdate]; 265 } 266 } 267 }]; 268} 269 270RCT_EXPORT_METHOD(fitToCoordinates:(nonnull NSNumber *)reactTag 271 coordinates:(nonnull NSArray<AIRMapCoordinate *> *)coordinates 272 edgePadding:(nonnull NSDictionary *)edgePadding 273 animated:(BOOL)animated) 274{ 275 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 276 id view = viewRegistry[reactTag]; 277 if (![view isKindOfClass:[AIRGoogleMap class]]) { 278 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 279 } else { 280 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 281 282 CLLocationCoordinate2D myLocation = coordinates.firstObject.coordinate; 283 GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:myLocation coordinate:myLocation]; 284 285 for (AIRMapCoordinate *coordinate in coordinates) 286 bounds = [bounds includingCoordinate:coordinate.coordinate]; 287 288 // Set Map viewport 289 CGFloat top = [RCTConvert CGFloat:edgePadding[@"top"]]; 290 CGFloat right = [RCTConvert CGFloat:edgePadding[@"right"]]; 291 CGFloat bottom = [RCTConvert CGFloat:edgePadding[@"bottom"]]; 292 CGFloat left = [RCTConvert CGFloat:edgePadding[@"left"]]; 293 294 GMSCameraUpdate *cameraUpdate = [GMSCameraUpdate fitBounds:bounds withEdgeInsets:UIEdgeInsetsMake(top, left, bottom, right)]; 295 296 if (animated) { 297 [mapView animateWithCameraUpdate: cameraUpdate]; 298 } else { 299 [mapView moveCamera: cameraUpdate]; 300 } 301 } 302 }]; 303} 304 305RCT_EXPORT_METHOD(takeSnapshot:(nonnull NSNumber *)reactTag 306 withWidth:(nonnull NSNumber *)width 307 withHeight:(nonnull NSNumber *)height 308 withRegion:(MKCoordinateRegion)region 309 format:(nonnull NSString *)format 310 quality:(nonnull NSNumber *)quality 311 result:(nonnull NSString *)result 312 withCallback:(RCTResponseSenderBlock)callback) 313{ 314 NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970]; 315 NSString *pathComponent = [NSString stringWithFormat:@"Documents/snapshot-%.20lf.%@", timeStamp, format]; 316 NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent: pathComponent]; 317 318 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 319 id view = viewRegistry[reactTag]; 320 if (![view isKindOfClass:[AIRGoogleMap class]]) { 321 RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view); 322 } else { 323 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 324 325 // TODO: currently we are ignoring width, height, region 326 327 UIGraphicsBeginImageContextWithOptions(mapView.frame.size, YES, 0.0f); 328 [mapView.layer renderInContext:UIGraphicsGetCurrentContext()]; 329 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 330 331 NSData *data; 332 if ([format isEqualToString:@"png"]) { 333 data = UIImagePNGRepresentation(image); 334 335 } 336 else if([format isEqualToString:@"jpg"]) { 337 data = UIImageJPEGRepresentation(image, quality.floatValue); 338 } 339 340 if ([result isEqualToString:@"file"]) { 341 [data writeToFile:filePath atomically:YES]; 342 callback(@[[NSNull null], filePath]); 343 } 344 else if ([result isEqualToString:@"base64"]) { 345 callback(@[[NSNull null], [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]]); 346 } 347 } 348 UIGraphicsEndImageContext(); 349 }]; 350} 351 352RCT_EXPORT_METHOD(pointForCoordinate:(nonnull NSNumber *)reactTag 353 coordinate:(NSDictionary *)coordinate 354 resolver: (RCTPromiseResolveBlock)resolve 355 rejecter:(RCTPromiseRejectBlock)reject) 356{ 357 CLLocationCoordinate2D coord = 358 CLLocationCoordinate2DMake( 359 [coordinate[@"latitude"] doubleValue], 360 [coordinate[@"longitude"] doubleValue] 361 ); 362 363 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 364 id view = viewRegistry[reactTag]; 365 if (![view isKindOfClass:[AIRGoogleMap class]]) { 366 RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view); 367 } else { 368 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 369 370 CGPoint touchPoint = [mapView.projection pointForCoordinate:coord]; 371 372 resolve(@{ 373 @"x": @(touchPoint.x), 374 @"y": @(touchPoint.y), 375 }); 376 } 377 }]; 378} 379 380RCT_EXPORT_METHOD(coordinateForPoint:(nonnull NSNumber *)reactTag 381 point:(NSDictionary *)point 382 resolver: (RCTPromiseResolveBlock)resolve 383 rejecter:(RCTPromiseRejectBlock)reject) 384{ 385 CGPoint pt = CGPointMake( 386 [point[@"x"] doubleValue], 387 [point[@"y"] doubleValue] 388 ); 389 390 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 391 id view = viewRegistry[reactTag]; 392 if (![view isKindOfClass:[AIRGoogleMap class]]) { 393 RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view); 394 } else { 395 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 396 397 CLLocationCoordinate2D coordinate = [mapView.projection coordinateForPoint:pt]; 398 399 resolve(@{ 400 @"latitude": @(coordinate.latitude), 401 @"longitude": @(coordinate.longitude), 402 }); 403 } 404 }]; 405} 406 407RCT_EXPORT_METHOD(getMarkersFrames:(nonnull NSNumber *)reactTag 408 onlyVisible:(BOOL)onlyVisible 409 resolver: (RCTPromiseResolveBlock)resolve 410 rejecter:(RCTPromiseRejectBlock)reject) 411{ 412 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 413 id view = viewRegistry[reactTag]; 414 if (![view isKindOfClass:[AIRGoogleMap class]]) { 415 RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view); 416 } else { 417 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 418 resolve([mapView getMarkersFramesWithOnlyVisible:onlyVisible]); 419 } 420 }]; 421} 422 423RCT_EXPORT_METHOD(getMapBoundaries:(nonnull NSNumber *)reactTag 424 resolver:(RCTPromiseResolveBlock)resolve 425 rejecter:(RCTPromiseRejectBlock)reject) 426{ 427 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 428 id view = viewRegistry[reactTag]; 429 if (![view isKindOfClass:[AIRGoogleMap class]]) { 430 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 431 } else { 432 NSArray *boundingBox = [view getMapBoundaries]; 433 434 resolve(@{ 435 @"northEast" : @{ 436 @"longitude" : boundingBox[0][0], 437 @"latitude" : boundingBox[0][1] 438 }, 439 @"southWest" : @{ 440 @"longitude" : boundingBox[1][0], 441 @"latitude" : boundingBox[1][1] 442 } 443 }); 444 } 445 }]; 446} 447 448RCT_EXPORT_METHOD(setMapBoundaries:(nonnull NSNumber *)reactTag 449 northEast:(CLLocationCoordinate2D)northEast 450 southWest:(CLLocationCoordinate2D)southWest) 451{ 452 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 453 id view = viewRegistry[reactTag]; 454 if (![view isKindOfClass:[AIRGoogleMap class]]) { 455 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 456 } else { 457 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 458 459 GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:northEast coordinate:southWest]; 460 461 mapView.cameraTargetBounds = bounds; 462 } 463 }]; 464} 465 466RCT_EXPORT_METHOD(setIndoorActiveLevelIndex:(nonnull NSNumber *)reactTag 467 levelIndex:(NSInteger) levelIndex) 468{ 469 [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) { 470 id view = viewRegistry[reactTag]; 471 if (![view isKindOfClass:[AIRGoogleMap class]]) { 472 RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view); 473 } else { 474 AIRGoogleMap *mapView = (AIRGoogleMap *)view; 475 if (!mapView.indoorDisplay) { 476 return; 477 } 478 if ( levelIndex < [mapView.indoorDisplay.activeBuilding.levels count]) { 479 mapView.indoorDisplay.activeLevel = mapView.indoorDisplay.activeBuilding.levels[levelIndex]; 480 } 481 } 482 }]; 483 } 484 485+ (BOOL)requiresMainQueueSetup { 486 return YES; 487} 488 489- (NSDictionary *)constantsToExport { 490 return @{ @"legalNotice": [GMSServices openSourceLicenseInfo] }; 491} 492 493- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture{ 494 self.isGesture = gesture; 495} 496 497- (void)mapViewDidStartTileRendering:(GMSMapView *)mapView { 498 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 499 [googleMapView didPrepareMap]; 500} 501 502- (void)mapViewDidFinishTileRendering:(GMSMapView *)mapView { 503 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 504 [googleMapView mapViewDidFinishTileRendering]; 505} 506 507- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { 508 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 509 return [googleMapView didTapMarker:marker]; 510} 511 512- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSPolygon *)polygon { 513 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 514 [googleMapView didTapPolygon:polygon]; 515} 516 517- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { 518 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 519 [googleMapView didTapAtCoordinate:coordinate]; 520} 521 522- (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { 523 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 524 [googleMapView didLongPressAtCoordinate:coordinate]; 525} 526 527- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { 528 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 529 [googleMapView didChangeCameraPosition:position isGesture:self.isGesture]; 530} 531 532- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position { 533 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 534 [googleMapView idleAtCameraPosition:position isGesture:self.isGesture]; 535} 536 537- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker { 538 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 539 return [aMarker.fakeMarker markerInfoWindow];} 540 541- (UIView *)mapView:(GMSMapView *)mapView markerInfoContents:(GMSMarker *)marker { 542 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 543 return [aMarker.fakeMarker markerInfoContents]; 544} 545 546- (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { 547 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 548 [aMarker.fakeMarker didTapInfoWindowOfMarker:aMarker]; 549} 550 551- (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker { 552 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 553 [aMarker.fakeMarker didBeginDraggingMarker:aMarker]; 554} 555 556- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { 557 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 558 [aMarker.fakeMarker didEndDraggingMarker:aMarker]; 559} 560 561- (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { 562 AIRGMSMarker *aMarker = (AIRGMSMarker *)marker; 563 [aMarker.fakeMarker didDragMarker:aMarker]; 564} 565 566- (void)mapView:(GMSMapView *)mapView 567 didTapPOIWithPlaceID:(NSString *)placeID 568 name:(NSString *)name 569 location:(CLLocationCoordinate2D)location { 570 AIRGoogleMap *googleMapView = (AIRGoogleMap *)mapView; 571 [googleMapView didTapPOIWithPlaceID:placeID name:name location:location]; 572} 573 574#pragma mark Gesture Recognizer Handlers 575 576- (void)handleMapDrag:(UIPanGestureRecognizer*)recognizer { 577 AIRGoogleMap *map = (AIRGoogleMap *)recognizer.view; 578 if (!map.onPanDrag) return; 579 580 CGPoint touchPoint = [recognizer locationInView:map]; 581 CLLocationCoordinate2D coord = [map.projection coordinateForPoint:touchPoint]; 582 map.onPanDrag(@{ 583 @"coordinate": @{ 584 @"latitude": @(coord.latitude), 585 @"longitude": @(coord.longitude), 586 }, 587 @"position": @{ 588 @"x": @(touchPoint.x), 589 @"y": @(touchPoint.y), 590 }, 591 }); 592 593} 594 595@end 596 597#endif 598