2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-03-10 01:05:10 +00:00
|
|
|
|
|
|
|
#import "RCTMapManager.h"
|
|
|
|
|
|
|
|
#import "RCTBridge.h"
|
2015-04-15 00:51:28 +00:00
|
|
|
#import "RCTConvert+CoreLocation.h"
|
|
|
|
#import "RCTConvert+MapKit.h"
|
2015-03-10 01:05:10 +00:00
|
|
|
#import "RCTEventDispatcher.h"
|
|
|
|
#import "RCTMap.h"
|
2015-03-26 09:58:06 +00:00
|
|
|
#import "UIView+React.h"
|
2015-06-25 16:07:19 +00:00
|
|
|
#import "RCTPointAnnotation.h"
|
|
|
|
|
|
|
|
#import <MapKit/MapKit.h>
|
2015-03-10 01:05:10 +00:00
|
|
|
|
2015-04-15 00:51:28 +00:00
|
|
|
static NSString *const RCTMapViewKey = @"MapView";
|
2015-03-26 04:29:28 +00:00
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
@interface RCTMapManager() <MKMapViewDelegate>
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation RCTMapManager
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
- (UIView *)view
|
|
|
|
{
|
2015-08-17 14:35:34 +00:00
|
|
|
RCTMap *map = [RCTMap new];
|
2015-03-10 01:05:10 +00:00
|
|
|
map.delegate = self;
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(maxDelta, CGFloat)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
|
2015-06-11 17:46:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType)
|
2015-06-25 16:07:19 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(annotations, RCTPointAnnotationArray)
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
|
|
|
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
|
2015-04-16 12:29:06 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
|
|
|
|
{
|
|
|
|
[view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES];
|
|
|
|
}
|
2015-03-10 01:05:10 +00:00
|
|
|
|
|
|
|
#pragma mark MKMapViewDelegate
|
|
|
|
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (mapView.onPress && [view.annotation isKindOfClass:[RCTPointAnnotation class]]) {
|
2015-06-25 16:07:19 +00:00
|
|
|
|
|
|
|
RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation;
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
mapView.onPress(@{
|
|
|
|
@"action": @"annotation-click",
|
|
|
|
@"annotation": @{
|
|
|
|
@"id": annotation.identifier,
|
|
|
|
@"title": annotation.title ?: @"",
|
|
|
|
@"subtitle": annotation.subtitle ?: @"",
|
|
|
|
@"latitude": @(annotation.coordinate.latitude),
|
|
|
|
@"longitude": @(annotation.coordinate.longitude)
|
|
|
|
}
|
|
|
|
});
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 23:19:40 +00:00
|
|
|
- (MKAnnotationView *)mapView:(__unused MKMapView *)mapView viewForAnnotation:(RCTPointAnnotation *)annotation
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
|
2015-06-25 16:07:19 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
MKPinAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"RCTAnnotation"];
|
|
|
|
|
|
|
|
annotationView.canShowCallout = true;
|
|
|
|
annotationView.animatesDrop = annotation.animateDrop;
|
|
|
|
|
|
|
|
annotationView.leftCalloutAccessoryView = nil;
|
|
|
|
if (annotation.hasLeftCallout) {
|
|
|
|
annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
|
|
|
|
}
|
|
|
|
|
|
|
|
annotationView.rightCalloutAccessoryView = nil;
|
|
|
|
if (annotation.hasRightCallout) {
|
|
|
|
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
|
|
|
|
}
|
|
|
|
|
|
|
|
return annotationView;
|
|
|
|
}
|
|
|
|
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
|
2015-06-25 16:07:19 +00:00
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (mapView.onPress) {
|
2015-06-25 16:07:19 +00:00
|
|
|
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
// Pass to js
|
|
|
|
RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation;
|
|
|
|
mapView.onPress(@{
|
|
|
|
@"side": (control == view.leftCalloutAccessoryView) ? @"left" : @"right",
|
2015-06-25 16:07:19 +00:00
|
|
|
@"action": @"callout-click",
|
|
|
|
@"annotationId": annotation.identifier
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
});
|
|
|
|
}
|
2015-06-25 16:07:19 +00:00
|
|
|
}
|
|
|
|
|
2015-03-10 01:05:10 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location
|
|
|
|
{
|
|
|
|
if (mapView.followUserLocation) {
|
|
|
|
MKCoordinateRegion region;
|
|
|
|
region.span.latitudeDelta = RCTMapDefaultSpan;
|
|
|
|
region.span.longitudeDelta = RCTMapDefaultSpan;
|
|
|
|
region.center = location.coordinate;
|
|
|
|
[mapView setRegion:region animated:YES];
|
|
|
|
|
|
|
|
// Move to user location only for the first time it loads up.
|
|
|
|
mapView.followUserLocation = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView regionWillChangeAnimated:(__unused BOOL)animated
|
2015-03-10 01:05:10 +00:00
|
|
|
{
|
|
|
|
[self _regionChanged:mapView];
|
|
|
|
|
2015-04-21 17:19:18 +00:00
|
|
|
mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTMapRegionChangeObserveInterval
|
|
|
|
target:self
|
|
|
|
selector:@selector(_onTick:)
|
|
|
|
userInfo:@{ RCTMapViewKey: mapView }
|
|
|
|
repeats:YES];
|
|
|
|
|
|
|
|
[[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (void)mapView:(RCTMap *)mapView regionDidChangeAnimated:(__unused BOOL)animated
|
2015-03-10 01:05:10 +00:00
|
|
|
{
|
|
|
|
[mapView.regionChangeObserveTimer invalidate];
|
|
|
|
mapView.regionChangeObserveTimer = nil;
|
2015-03-13 16:10:57 +00:00
|
|
|
|
|
|
|
[self _regionChanged:mapView];
|
2015-04-15 00:51:28 +00:00
|
|
|
|
|
|
|
// Don't send region did change events until map has
|
2015-05-07 23:20:08 +00:00
|
|
|
// started rendering, as these won't represent the final location
|
|
|
|
if (mapView.hasStartedRendering) {
|
2015-04-15 00:51:28 +00:00
|
|
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-05-07 23:20:08 +00:00
|
|
|
- (void)mapViewWillStartRenderingMap:(RCTMap *)mapView
|
2015-04-15 00:51:28 +00:00
|
|
|
{
|
2015-05-07 23:20:08 +00:00
|
|
|
mapView.hasStartedRendering = YES;
|
2015-03-13 16:10:57 +00:00
|
|
|
[self _emitRegionChangeEvent:mapView continuous:NO];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark Private
|
|
|
|
|
|
|
|
- (void)_onTick:(NSTimer *)timer
|
|
|
|
{
|
2015-04-15 00:51:28 +00:00
|
|
|
[self _regionChanged:timer.userInfo[RCTMapViewKey]];
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_regionChanged:(RCTMap *)mapView
|
|
|
|
{
|
|
|
|
BOOL needZoom = NO;
|
|
|
|
CGFloat newLongitudeDelta = 0.0f;
|
|
|
|
MKCoordinateRegion region = mapView.region;
|
|
|
|
// On iOS 7, it's possible that we observe invalid locations during initialization of the map.
|
|
|
|
// Filter those out.
|
|
|
|
if (!CLLocationCoordinate2DIsValid(region.center)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Calculation on float is not 100% accurate. If user zoom to max/min and then move, it's likely the map will auto zoom to max/min from time to time.
|
|
|
|
// So let's try to make map zoom back to 99% max or 101% min so that there are some buffer that moving the map won't constantly hitting the max/min bound.
|
|
|
|
if (mapView.maxDelta > FLT_EPSILON && region.span.longitudeDelta > mapView.maxDelta) {
|
|
|
|
needZoom = YES;
|
|
|
|
newLongitudeDelta = mapView.maxDelta * (1 - RCTMapZoomBoundBuffer);
|
|
|
|
} else if (mapView.minDelta > FLT_EPSILON && region.span.longitudeDelta < mapView.minDelta) {
|
|
|
|
needZoom = YES;
|
|
|
|
newLongitudeDelta = mapView.minDelta * (1 + RCTMapZoomBoundBuffer);
|
|
|
|
}
|
|
|
|
if (needZoom) {
|
|
|
|
region.span.latitudeDelta = region.span.latitudeDelta / region.span.longitudeDelta * newLongitudeDelta;
|
|
|
|
region.span.longitudeDelta = newLongitudeDelta;
|
|
|
|
mapView.region = region;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Continously observe region changes
|
|
|
|
[self _emitRegionChangeEvent:mapView continuous:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_emitRegionChangeEvent:(RCTMap *)mapView continuous:(BOOL)continuous
|
|
|
|
{
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
if (mapView.onChange) {
|
|
|
|
MKCoordinateRegion region = mapView.region;
|
|
|
|
if (!CLLocationCoordinate2DIsValid(region.center)) {
|
|
|
|
return;
|
2015-03-26 04:29:28 +00:00
|
|
|
}
|
Added mechanism for directly mapping JS event handlers to blocks
Summary:
Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher.
This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names.
The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g.
RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock)
If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether).
2015-09-02 12:58:10 +00:00
|
|
|
|
|
|
|
#define FLUSH_NAN(value) (isnan(value) ? 0 : value)
|
|
|
|
|
|
|
|
mapView.onChange(@{
|
|
|
|
@"continuous": @(continuous),
|
|
|
|
@"region": @{
|
|
|
|
@"latitude": @(FLUSH_NAN(region.center.latitude)),
|
|
|
|
@"longitude": @(FLUSH_NAN(region.center.longitude)),
|
|
|
|
@"latitudeDelta": @(FLUSH_NAN(region.span.latitudeDelta)),
|
|
|
|
@"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-03-10 01:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|