blob: 37e0ad0b0cf2caf934bc8168ccf5e954769c0f33 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "GoogleMapController.h"
#import "JsonConversions.h"
#pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations.
static NSDictionary* PositionToJson(GMSCameraPosition* position);
static NSDictionary* PointToJson(CGPoint point);
static NSArray* LocationToJson(CLLocationCoordinate2D position);
static CGPoint ToCGPoint(NSDictionary* json);
static GMSCameraPosition* ToOptionalCameraPosition(NSDictionary* json);
static GMSCoordinateBounds* ToOptionalBounds(NSArray* json);
static GMSCameraUpdate* ToCameraUpdate(NSArray* data);
static NSDictionary* GMSCoordinateBoundsToJson(GMSCoordinateBounds* bounds);
static void InterpretMapOptions(NSDictionary* data, id<FLTGoogleMapOptionsSink> sink);
static double ToDouble(NSNumber* data) { return [FLTGoogleMapJsonConversions toDouble:data]; }
@implementation FLTGoogleMapFactory {
NSObject<FlutterPluginRegistrar>* _registrar;
}
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
self = [super init];
if (self) {
_registrar = registrar;
}
return self;
}
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
return [FlutterStandardMessageCodec sharedInstance];
}
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args {
return [[FLTGoogleMapController alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
registrar:_registrar];
}
@end
@implementation FLTGoogleMapController {
GMSMapView* _mapView;
int64_t _viewId;
FlutterMethodChannel* _channel;
BOOL _trackCameraPosition;
NSObject<FlutterPluginRegistrar>* _registrar;
// Used for the temporary workaround for a bug that the camera is not properly positioned at
// initialization. https://github.com/flutter/flutter/issues/24806
// TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
// https://github.com/flutter/flutter/issues/27550
BOOL _cameraDidInitialSetup;
FLTMarkersController* _markersController;
FLTPolygonsController* _polygonsController;
FLTPolylinesController* _polylinesController;
FLTCirclesController* _circlesController;
}
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
registrar:(NSObject<FlutterPluginRegistrar>*)registrar {
if (self = [super init]) {
_viewId = viewId;
GMSCameraPosition* camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]);
_mapView = [GMSMapView mapWithFrame:frame camera:camera];
_mapView.accessibilityElementsHidden = NO;
_trackCameraPosition = NO;
InterpretMapOptions(args[@"options"], self);
NSString* channelName =
[NSString stringWithFormat:@"plugins.flutter.io/google_maps_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName
binaryMessenger:registrar.messenger];
__weak __typeof__(self) weakSelf = self;
[_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if (weakSelf) {
[weakSelf onMethodCall:call result:result];
}
}];
_mapView.delegate = weakSelf;
_registrar = registrar;
_cameraDidInitialSetup = NO;
_markersController = [[FLTMarkersController alloc] init:_channel
mapView:_mapView
registrar:registrar];
_polygonsController = [[FLTPolygonsController alloc] init:_channel
mapView:_mapView
registrar:registrar];
_polylinesController = [[FLTPolylinesController alloc] init:_channel
mapView:_mapView
registrar:registrar];
_circlesController = [[FLTCirclesController alloc] init:_channel
mapView:_mapView
registrar:registrar];
id markersToAdd = args[@"markersToAdd"];
if ([markersToAdd isKindOfClass:[NSArray class]]) {
[_markersController addMarkers:markersToAdd];
}
id polygonsToAdd = args[@"polygonsToAdd"];
if ([polygonsToAdd isKindOfClass:[NSArray class]]) {
[_polygonsController addPolygons:polygonsToAdd];
}
id polylinesToAdd = args[@"polylinesToAdd"];
if ([polylinesToAdd isKindOfClass:[NSArray class]]) {
[_polylinesController addPolylines:polylinesToAdd];
}
id circlesToAdd = args[@"circlesToAdd"];
if ([circlesToAdd isKindOfClass:[NSArray class]]) {
[_circlesController addCircles:circlesToAdd];
}
}
return self;
}
- (UIView*)view {
return _mapView;
}
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([call.method isEqualToString:@"map#show"]) {
[self showAtX:ToDouble(call.arguments[@"x"]) Y:ToDouble(call.arguments[@"y"])];
result(nil);
} else if ([call.method isEqualToString:@"map#hide"]) {
[self hide];
result(nil);
} else if ([call.method isEqualToString:@"camera#animate"]) {
[self animateWithCameraUpdate:ToCameraUpdate(call.arguments[@"cameraUpdate"])];
result(nil);
} else if ([call.method isEqualToString:@"camera#move"]) {
[self moveWithCameraUpdate:ToCameraUpdate(call.arguments[@"cameraUpdate"])];
result(nil);
} else if ([call.method isEqualToString:@"map#update"]) {
InterpretMapOptions(call.arguments[@"options"], self);
result(PositionToJson([self cameraPosition]));
} else if ([call.method isEqualToString:@"map#getVisibleRegion"]) {
if (_mapView != nil) {
GMSVisibleRegion visibleRegion = _mapView.projection.visibleRegion;
GMSCoordinateBounds* bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion];
result(GMSCoordinateBoundsToJson(bounds));
} else {
result([FlutterError errorWithCode:@"GoogleMap uninitialized"
message:@"getVisibleRegion called prior to map initialization"
details:nil]);
}
} else if ([call.method isEqualToString:@"map#getScreenCoordinate"]) {
if (_mapView != nil) {
CLLocationCoordinate2D location = [FLTGoogleMapJsonConversions toLocation:call.arguments];
CGPoint point = [_mapView.projection pointForCoordinate:location];
result(PointToJson(point));
} else {
result([FlutterError errorWithCode:@"GoogleMap uninitialized"
message:@"getScreenCoordinate called prior to map initialization"
details:nil]);
}
} else if ([call.method isEqualToString:@"map#getLatLng"]) {
if (_mapView != nil && call.arguments) {
CGPoint point = ToCGPoint(call.arguments);
CLLocationCoordinate2D latlng = [_mapView.projection coordinateForPoint:point];
result(LocationToJson(latlng));
} else {
result([FlutterError errorWithCode:@"GoogleMap uninitialized"
message:@"getLatLng called prior to map initialization"
details:nil]);
}
} else if ([call.method isEqualToString:@"map#waitForMap"]) {
result(nil);
} else if ([call.method isEqualToString:@"markers#update"]) {
id markersToAdd = call.arguments[@"markersToAdd"];
if ([markersToAdd isKindOfClass:[NSArray class]]) {
[_markersController addMarkers:markersToAdd];
}
id markersToChange = call.arguments[@"markersToChange"];
if ([markersToChange isKindOfClass:[NSArray class]]) {
[_markersController changeMarkers:markersToChange];
}
id markerIdsToRemove = call.arguments[@"markerIdsToRemove"];
if ([markerIdsToRemove isKindOfClass:[NSArray class]]) {
[_markersController removeMarkerIds:markerIdsToRemove];
}
result(nil);
} else if ([call.method isEqualToString:@"polygons#update"]) {
id polygonsToAdd = call.arguments[@"polygonsToAdd"];
if ([polygonsToAdd isKindOfClass:[NSArray class]]) {
[_polygonsController addPolygons:polygonsToAdd];
}
id polygonsToChange = call.arguments[@"polygonsToChange"];
if ([polygonsToChange isKindOfClass:[NSArray class]]) {
[_polygonsController changePolygons:polygonsToChange];
}
id polygonIdsToRemove = call.arguments[@"polygonIdsToRemove"];
if ([polygonIdsToRemove isKindOfClass:[NSArray class]]) {
[_polygonsController removePolygonIds:polygonIdsToRemove];
}
result(nil);
} else if ([call.method isEqualToString:@"polylines#update"]) {
id polylinesToAdd = call.arguments[@"polylinesToAdd"];
if ([polylinesToAdd isKindOfClass:[NSArray class]]) {
[_polylinesController addPolylines:polylinesToAdd];
}
id polylinesToChange = call.arguments[@"polylinesToChange"];
if ([polylinesToChange isKindOfClass:[NSArray class]]) {
[_polylinesController changePolylines:polylinesToChange];
}
id polylineIdsToRemove = call.arguments[@"polylineIdsToRemove"];
if ([polylineIdsToRemove isKindOfClass:[NSArray class]]) {
[_polylinesController removePolylineIds:polylineIdsToRemove];
}
result(nil);
} else if ([call.method isEqualToString:@"circles#update"]) {
id circlesToAdd = call.arguments[@"circlesToAdd"];
if ([circlesToAdd isKindOfClass:[NSArray class]]) {
[_circlesController addCircles:circlesToAdd];
}
id circlesToChange = call.arguments[@"circlesToChange"];
if ([circlesToChange isKindOfClass:[NSArray class]]) {
[_circlesController changeCircles:circlesToChange];
}
id circleIdsToRemove = call.arguments[@"circleIdsToRemove"];
if ([circleIdsToRemove isKindOfClass:[NSArray class]]) {
[_circlesController removeCircleIds:circleIdsToRemove];
}
result(nil);
} else if ([call.method isEqualToString:@"map#isCompassEnabled"]) {
NSNumber* isCompassEnabled = @(_mapView.settings.compassButton);
result(isCompassEnabled);
} else if ([call.method isEqualToString:@"map#isMapToolbarEnabled"]) {
NSNumber* isMapToolbarEnabled = [NSNumber numberWithBool:NO];
result(isMapToolbarEnabled);
} else if ([call.method isEqualToString:@"map#getMinMaxZoomLevels"]) {
NSArray* zoomLevels = @[ @(_mapView.minZoom), @(_mapView.maxZoom) ];
result(zoomLevels);
} else if ([call.method isEqualToString:@"map#getZoomLevel"]) {
result(@(_mapView.camera.zoom));
} else if ([call.method isEqualToString:@"map#isZoomGesturesEnabled"]) {
NSNumber* isZoomGesturesEnabled = @(_mapView.settings.zoomGestures);
result(isZoomGesturesEnabled);
} else if ([call.method isEqualToString:@"map#isTiltGesturesEnabled"]) {
NSNumber* isTiltGesturesEnabled = @(_mapView.settings.tiltGestures);
result(isTiltGesturesEnabled);
} else if ([call.method isEqualToString:@"map#isRotateGesturesEnabled"]) {
NSNumber* isRotateGesturesEnabled = @(_mapView.settings.rotateGestures);
result(isRotateGesturesEnabled);
} else if ([call.method isEqualToString:@"map#isScrollGesturesEnabled"]) {
NSNumber* isScrollGesturesEnabled = @(_mapView.settings.scrollGestures);
result(isScrollGesturesEnabled);
} else if ([call.method isEqualToString:@"map#isMyLocationButtonEnabled"]) {
NSNumber* isMyLocationButtonEnabled = @(_mapView.settings.myLocationButton);
result(isMyLocationButtonEnabled);
} else if ([call.method isEqualToString:@"map#isTrafficEnabled"]) {
NSNumber* isTrafficEnabled = @(_mapView.trafficEnabled);
result(isTrafficEnabled);
} else if ([call.method isEqualToString:@"map#isBuildingsEnabled"]) {
NSNumber* isBuildingsEnabled = @(_mapView.buildingsEnabled);
result(isBuildingsEnabled);
} else if ([call.method isEqualToString:@"map#setStyle"]) {
NSString* mapStyle = [call arguments];
NSString* error = [self setMapStyle:mapStyle];
if (error == nil) {
result(@[ @(YES) ]);
} else {
result(@[ @(NO), error ]);
}
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)showAtX:(CGFloat)x Y:(CGFloat)y {
_mapView.frame =
CGRectMake(x, y, CGRectGetWidth(_mapView.frame), CGRectGetHeight(_mapView.frame));
_mapView.hidden = NO;
}
- (void)hide {
_mapView.hidden = YES;
}
- (void)animateWithCameraUpdate:(GMSCameraUpdate*)cameraUpdate {
[_mapView animateWithCameraUpdate:cameraUpdate];
}
- (void)moveWithCameraUpdate:(GMSCameraUpdate*)cameraUpdate {
[_mapView moveCamera:cameraUpdate];
}
- (GMSCameraPosition*)cameraPosition {
if (_trackCameraPosition) {
return _mapView.camera;
} else {
return nil;
}
}
#pragma mark - FLTGoogleMapOptionsSink methods
- (void)setCamera:(GMSCameraPosition*)camera {
_mapView.camera = camera;
}
- (void)setCameraTargetBounds:(GMSCoordinateBounds*)bounds {
_mapView.cameraTargetBounds = bounds;
}
- (void)setCompassEnabled:(BOOL)enabled {
_mapView.settings.compassButton = enabled;
}
- (void)setIndoorEnabled:(BOOL)enabled {
_mapView.indoorEnabled = enabled;
}
- (void)setTrafficEnabled:(BOOL)enabled {
_mapView.trafficEnabled = enabled;
}
- (void)setBuildingsEnabled:(BOOL)enabled {
_mapView.buildingsEnabled = enabled;
}
- (void)setMapType:(GMSMapViewType)mapType {
_mapView.mapType = mapType;
}
- (void)setMinZoom:(float)minZoom maxZoom:(float)maxZoom {
[_mapView setMinZoom:minZoom maxZoom:maxZoom];
}
- (void)setPaddingTop:(float)top left:(float)left bottom:(float)bottom right:(float)right {
_mapView.padding = UIEdgeInsetsMake(top, left, bottom, right);
}
- (void)setRotateGesturesEnabled:(BOOL)enabled {
_mapView.settings.rotateGestures = enabled;
}
- (void)setScrollGesturesEnabled:(BOOL)enabled {
_mapView.settings.scrollGestures = enabled;
}
- (void)setTiltGesturesEnabled:(BOOL)enabled {
_mapView.settings.tiltGestures = enabled;
}
- (void)setTrackCameraPosition:(BOOL)enabled {
_trackCameraPosition = enabled;
}
- (void)setZoomGesturesEnabled:(BOOL)enabled {
_mapView.settings.zoomGestures = enabled;
}
- (void)setMyLocationEnabled:(BOOL)enabled {
_mapView.myLocationEnabled = enabled;
}
- (void)setMyLocationButtonEnabled:(BOOL)enabled {
_mapView.settings.myLocationButton = enabled;
}
- (NSString*)setMapStyle:(NSString*)mapStyle {
if (mapStyle == (id)[NSNull null] || mapStyle.length == 0) {
_mapView.mapStyle = nil;
return nil;
}
NSError* error;
GMSMapStyle* style = [GMSMapStyle styleWithJSONString:mapStyle error:&error];
if (!style) {
return [error localizedDescription];
} else {
_mapView.mapStyle = style;
return nil;
}
}
#pragma mark - GMSMapViewDelegate methods
- (void)mapView:(GMSMapView*)mapView willMove:(BOOL)gesture {
[_channel invokeMethod:@"camera#onMoveStarted" arguments:@{@"isGesture" : @(gesture)}];
}
- (void)mapView:(GMSMapView*)mapView didChangeCameraPosition:(GMSCameraPosition*)position {
if (!_cameraDidInitialSetup) {
// We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
// initialization. https://github.com/flutter/flutter/issues/24806
// This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
// still being investigated.
// TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
// https://github.com/flutter/flutter/issues/27550
_cameraDidInitialSetup = YES;
[mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]];
}
if (_trackCameraPosition) {
[_channel invokeMethod:@"camera#onMove" arguments:@{@"position" : PositionToJson(position)}];
}
}
- (void)mapView:(GMSMapView*)mapView idleAtCameraPosition:(GMSCameraPosition*)position {
[_channel invokeMethod:@"camera#onIdle" arguments:@{}];
}
- (BOOL)mapView:(GMSMapView*)mapView didTapMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
return [_markersController onMarkerTap:markerId];
}
- (void)mapView:(GMSMapView*)mapView didEndDraggingMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
[_markersController onMarkerDragEnd:markerId coordinate:marker.position];
}
- (void)mapView:(GMSMapView*)mapView didTapInfoWindowOfMarker:(GMSMarker*)marker {
NSString* markerId = marker.userData[0];
[_markersController onInfoWindowTap:markerId];
}
- (void)mapView:(GMSMapView*)mapView didTapOverlay:(GMSOverlay*)overlay {
NSString* overlayId = overlay.userData[0];
if ([_polylinesController hasPolylineWithId:overlayId]) {
[_polylinesController onPolylineTap:overlayId];
} else if ([_polygonsController hasPolygonWithId:overlayId]) {
[_polygonsController onPolygonTap:overlayId];
} else if ([_circlesController hasCircleWithId:overlayId]) {
[_circlesController onCircleTap:overlayId];
}
}
- (void)mapView:(GMSMapView*)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate {
[_channel invokeMethod:@"map#onTap" arguments:@{@"position" : LocationToJson(coordinate)}];
}
- (void)mapView:(GMSMapView*)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate {
[_channel invokeMethod:@"map#onLongPress" arguments:@{@"position" : LocationToJson(coordinate)}];
}
@end
#pragma mark - Implementations of JSON conversion functions.
static NSArray* LocationToJson(CLLocationCoordinate2D position) {
return @[ @(position.latitude), @(position.longitude) ];
}
static NSDictionary* PositionToJson(GMSCameraPosition* position) {
if (!position) {
return nil;
}
return @{
@"target" : LocationToJson([position target]),
@"zoom" : @([position zoom]),
@"bearing" : @([position bearing]),
@"tilt" : @([position viewingAngle]),
};
}
static NSDictionary* PointToJson(CGPoint point) {
return @{
@"x" : @((int)point.x),
@"y" : @((int)point.y),
};
}
static NSDictionary* GMSCoordinateBoundsToJson(GMSCoordinateBounds* bounds) {
if (!bounds) {
return nil;
}
return @{
@"southwest" : LocationToJson([bounds southWest]),
@"northeast" : LocationToJson([bounds northEast]),
};
}
static float ToFloat(NSNumber* data) { return [FLTGoogleMapJsonConversions toFloat:data]; }
static CLLocationCoordinate2D ToLocation(NSArray* data) {
return [FLTGoogleMapJsonConversions toLocation:data];
}
static int ToInt(NSNumber* data) { return [FLTGoogleMapJsonConversions toInt:data]; }
static BOOL ToBool(NSNumber* data) { return [FLTGoogleMapJsonConversions toBool:data]; }
static CGPoint ToPoint(NSArray* data) { return [FLTGoogleMapJsonConversions toPoint:data]; }
static GMSCameraPosition* ToCameraPosition(NSDictionary* data) {
return [GMSCameraPosition cameraWithTarget:ToLocation(data[@"target"])
zoom:ToFloat(data[@"zoom"])
bearing:ToDouble(data[@"bearing"])
viewingAngle:ToDouble(data[@"tilt"])];
}
static GMSCameraPosition* ToOptionalCameraPosition(NSDictionary* json) {
return json ? ToCameraPosition(json) : nil;
}
static CGPoint ToCGPoint(NSDictionary* json) {
double x = ToDouble(json[@"x"]);
double y = ToDouble(json[@"y"]);
return CGPointMake(x, y);
}
static GMSCoordinateBounds* ToBounds(NSArray* data) {
return [[GMSCoordinateBounds alloc] initWithCoordinate:ToLocation(data[0])
coordinate:ToLocation(data[1])];
}
static GMSCoordinateBounds* ToOptionalBounds(NSArray* data) {
return (data[0] == [NSNull null]) ? nil : ToBounds(data[0]);
}
static GMSMapViewType ToMapViewType(NSNumber* json) {
int value = ToInt(json);
return (GMSMapViewType)(value == 0 ? 5 : value);
}
static GMSCameraUpdate* ToCameraUpdate(NSArray* data) {
NSString* update = data[0];
if ([update isEqualToString:@"newCameraPosition"]) {
return [GMSCameraUpdate setCamera:ToCameraPosition(data[1])];
} else if ([update isEqualToString:@"newLatLng"]) {
return [GMSCameraUpdate setTarget:ToLocation(data[1])];
} else if ([update isEqualToString:@"newLatLngBounds"]) {
return [GMSCameraUpdate fitBounds:ToBounds(data[1]) withPadding:ToDouble(data[2])];
} else if ([update isEqualToString:@"newLatLngZoom"]) {
return [GMSCameraUpdate setTarget:ToLocation(data[1]) zoom:ToFloat(data[2])];
} else if ([update isEqualToString:@"scrollBy"]) {
return [GMSCameraUpdate scrollByX:ToDouble(data[1]) Y:ToDouble(data[2])];
} else if ([update isEqualToString:@"zoomBy"]) {
if (data.count == 2) {
return [GMSCameraUpdate zoomBy:ToFloat(data[1])];
} else {
return [GMSCameraUpdate zoomBy:ToFloat(data[1]) atPoint:ToPoint(data[2])];
}
} else if ([update isEqualToString:@"zoomIn"]) {
return [GMSCameraUpdate zoomIn];
} else if ([update isEqualToString:@"zoomOut"]) {
return [GMSCameraUpdate zoomOut];
} else if ([update isEqualToString:@"zoomTo"]) {
return [GMSCameraUpdate zoomTo:ToFloat(data[1])];
}
return nil;
}
static void InterpretMapOptions(NSDictionary* data, id<FLTGoogleMapOptionsSink> sink) {
NSArray* cameraTargetBounds = data[@"cameraTargetBounds"];
if (cameraTargetBounds) {
[sink setCameraTargetBounds:ToOptionalBounds(cameraTargetBounds)];
}
NSNumber* compassEnabled = data[@"compassEnabled"];
if (compassEnabled != nil) {
[sink setCompassEnabled:ToBool(compassEnabled)];
}
id indoorEnabled = data[@"indoorEnabled"];
if (indoorEnabled) {
[sink setIndoorEnabled:ToBool(indoorEnabled)];
}
id trafficEnabled = data[@"trafficEnabled"];
if (trafficEnabled) {
[sink setTrafficEnabled:ToBool(trafficEnabled)];
}
id buildingsEnabled = data[@"buildingsEnabled"];
if (buildingsEnabled) {
[sink setBuildingsEnabled:ToBool(buildingsEnabled)];
}
id mapType = data[@"mapType"];
if (mapType) {
[sink setMapType:ToMapViewType(mapType)];
}
NSArray* zoomData = data[@"minMaxZoomPreference"];
if (zoomData) {
float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : ToFloat(zoomData[0]);
float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : ToFloat(zoomData[1]);
[sink setMinZoom:minZoom maxZoom:maxZoom];
}
NSArray* paddingData = data[@"padding"];
if (paddingData) {
float top = (paddingData[0] == [NSNull null]) ? 0 : ToFloat(paddingData[0]);
float left = (paddingData[1] == [NSNull null]) ? 0 : ToFloat(paddingData[1]);
float bottom = (paddingData[2] == [NSNull null]) ? 0 : ToFloat(paddingData[2]);
float right = (paddingData[3] == [NSNull null]) ? 0 : ToFloat(paddingData[3]);
[sink setPaddingTop:top left:left bottom:bottom right:right];
}
NSNumber* rotateGesturesEnabled = data[@"rotateGesturesEnabled"];
if (rotateGesturesEnabled != nil) {
[sink setRotateGesturesEnabled:ToBool(rotateGesturesEnabled)];
}
NSNumber* scrollGesturesEnabled = data[@"scrollGesturesEnabled"];
if (scrollGesturesEnabled != nil) {
[sink setScrollGesturesEnabled:ToBool(scrollGesturesEnabled)];
}
NSNumber* tiltGesturesEnabled = data[@"tiltGesturesEnabled"];
if (tiltGesturesEnabled != nil) {
[sink setTiltGesturesEnabled:ToBool(tiltGesturesEnabled)];
}
NSNumber* trackCameraPosition = data[@"trackCameraPosition"];
if (trackCameraPosition != nil) {
[sink setTrackCameraPosition:ToBool(trackCameraPosition)];
}
NSNumber* zoomGesturesEnabled = data[@"zoomGesturesEnabled"];
if (zoomGesturesEnabled != nil) {
[sink setZoomGesturesEnabled:ToBool(zoomGesturesEnabled)];
}
NSNumber* myLocationEnabled = data[@"myLocationEnabled"];
if (myLocationEnabled != nil) {
[sink setMyLocationEnabled:ToBool(myLocationEnabled)];
}
NSNumber* myLocationButtonEnabled = data[@"myLocationButtonEnabled"];
if (myLocationButtonEnabled != nil) {
[sink setMyLocationButtonEnabled:ToBool(myLocationButtonEnabled)];
}
}