@lugg/maps 0.2.0-alpha.8 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +20 -19
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +290 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +99 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +13 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +157 -13
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +28 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolygonView.kt +85 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolygonViewManager.kt +100 -0
  10. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  11. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  12. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +698 -0
  13. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +60 -0
  14. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +89 -24
  15. package/android/src/main/java/com/luggmaps/events/LongPressEvent.kt +29 -0
  16. package/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt +41 -0
  17. package/android/src/main/java/com/luggmaps/events/MarkerPressEvent.kt +34 -0
  18. package/android/src/main/java/com/luggmaps/events/PolygonPressEvent.kt +12 -0
  19. package/android/src/main/java/com/luggmaps/events/PressEvent.kt +29 -0
  20. package/ios/LuggMapView.h +9 -0
  21. package/ios/LuggMapView.mm +343 -0
  22. package/ios/LuggMarkerView.h +13 -0
  23. package/ios/LuggMarkerView.mm +119 -0
  24. package/ios/LuggPolygonView.h +31 -0
  25. package/ios/LuggPolygonView.mm +151 -0
  26. package/ios/LuggPolylineView.h +4 -0
  27. package/ios/LuggPolylineView.mm +23 -0
  28. package/ios/core/AppleMapProvider.h +15 -0
  29. package/ios/core/AppleMapProvider.mm +1029 -0
  30. package/ios/core/GMSPolylineAnimator.h +3 -0
  31. package/ios/core/GMSPolylineAnimator.m +101 -15
  32. package/ios/core/GoogleMapProvider.h +13 -0
  33. package/ios/core/GoogleMapProvider.mm +689 -0
  34. package/ios/core/MKPolylineAnimator.h +4 -0
  35. package/ios/core/MKPolylineAnimator.m +167 -42
  36. package/ios/core/MapProviderDelegate.h +84 -0
  37. package/ios/core/PolylineAnimatorBase.h +24 -1
  38. package/ios/core/PolylineAnimatorBase.m +82 -19
  39. package/ios/events/LongPressEvent.h +29 -0
  40. package/ios/events/MarkerDragEvent.h +69 -0
  41. package/ios/events/MarkerPressEvent.h +29 -0
  42. package/ios/events/PolygonPressEvent.h +20 -0
  43. package/ios/events/PressEvent.h +29 -0
  44. package/ios/extensions/MKMapView+Zoom.h +2 -0
  45. package/ios/extensions/MKMapView+Zoom.m +14 -4
  46. package/lib/module/MapProvider.js +2 -4
  47. package/lib/module/MapProvider.js.map +1 -1
  48. package/lib/module/MapProvider.web.js +12 -7
  49. package/lib/module/MapProvider.web.js.map +1 -1
  50. package/lib/module/MapView.js +33 -16
  51. package/lib/module/MapView.js.map +1 -1
  52. package/lib/module/MapView.web.js +315 -217
  53. package/lib/module/MapView.web.js.map +1 -1
  54. package/lib/module/components/GeoJson.js +159 -0
  55. package/lib/module/components/GeoJson.js.map +1 -0
  56. package/lib/module/components/GeoJson.types.js +4 -0
  57. package/lib/module/components/GeoJson.types.js.map +1 -0
  58. package/lib/module/components/Marker.js +28 -4
  59. package/lib/module/components/Marker.js.map +1 -1
  60. package/lib/module/components/Marker.types.js +4 -0
  61. package/lib/module/components/Marker.types.js.map +1 -0
  62. package/lib/module/components/Marker.web.js +98 -27
  63. package/lib/module/components/Marker.web.js.map +1 -1
  64. package/lib/module/components/Polygon.js +46 -0
  65. package/lib/module/components/Polygon.js.map +1 -0
  66. package/lib/module/components/Polygon.types.js +4 -0
  67. package/lib/module/components/Polygon.types.js.map +1 -0
  68. package/lib/module/components/Polygon.web.js +87 -0
  69. package/lib/module/components/Polygon.web.js.map +1 -0
  70. package/lib/module/components/Polyline.js +17 -4
  71. package/lib/module/components/Polyline.js.map +1 -1
  72. package/lib/module/components/Polyline.types.js +4 -0
  73. package/lib/module/components/Polyline.types.js.map +1 -0
  74. package/lib/module/components/Polyline.web.js +108 -31
  75. package/lib/module/components/Polyline.web.js.map +1 -1
  76. package/lib/module/components/index.js +4 -2
  77. package/lib/module/components/index.js.map +1 -1
  78. package/lib/module/components/index.web.js +2 -0
  79. package/lib/module/components/index.web.js.map +1 -1
  80. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  81. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  82. package/lib/module/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  83. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  84. package/lib/module/index.js +4 -1
  85. package/lib/module/index.js.map +1 -1
  86. package/lib/module/index.web.js +4 -1
  87. package/lib/module/index.web.js.map +1 -1
  88. package/lib/typescript/src/MapProvider.d.ts +1 -1
  89. package/lib/typescript/src/MapProvider.d.ts.map +1 -1
  90. package/lib/typescript/src/MapProvider.web.d.ts +17 -1
  91. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  92. package/lib/typescript/src/MapView.d.ts +4 -4
  93. package/lib/typescript/src/MapView.d.ts.map +1 -1
  94. package/lib/typescript/src/MapView.types.d.ts +35 -9
  95. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  96. package/lib/typescript/src/MapView.web.d.ts +2 -11
  97. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  98. package/lib/typescript/src/components/GeoJson.d.ts +4 -0
  99. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -0
  100. package/lib/typescript/src/components/GeoJson.types.d.ts +79 -0
  101. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -0
  102. package/lib/typescript/src/components/Marker.d.ts +6 -33
  103. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  104. package/lib/typescript/src/components/Marker.types.d.ts +74 -0
  105. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -0
  106. package/lib/typescript/src/components/Marker.web.d.ts +2 -5
  107. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  108. package/lib/typescript/src/components/Polygon.d.ts +10 -0
  109. package/lib/typescript/src/components/Polygon.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Polygon.types.d.ts +33 -0
  111. package/lib/typescript/src/components/Polygon.types.d.ts.map +1 -0
  112. package/lib/typescript/src/components/Polygon.web.d.ts +3 -0
  113. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Polyline.d.ts +6 -21
  115. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Polyline.types.d.ts +53 -0
  117. package/lib/typescript/src/components/Polyline.types.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Polyline.web.d.ts +2 -5
  119. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  120. package/lib/typescript/src/components/index.d.ts +8 -2
  121. package/lib/typescript/src/components/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/index.web.d.ts +6 -2
  123. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  124. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +30 -4
  125. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  126. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +45 -1
  127. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  128. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts +18 -0
  129. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts.map +1 -0
  130. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  131. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  132. package/lib/typescript/src/index.d.ts +11 -3
  133. package/lib/typescript/src/index.d.ts.map +1 -1
  134. package/lib/typescript/src/index.web.d.ts +9 -1
  135. package/lib/typescript/src/index.web.d.ts.map +1 -1
  136. package/lib/typescript/src/types.d.ts +11 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -1
  138. package/package.json +10 -9
  139. package/src/MapProvider.tsx +1 -3
  140. package/src/MapProvider.web.tsx +12 -3
  141. package/src/MapView.tsx +32 -26
  142. package/src/MapView.types.ts +43 -9
  143. package/src/MapView.web.tsx +376 -244
  144. package/src/components/GeoJson.tsx +221 -0
  145. package/src/components/GeoJson.types.ts +107 -0
  146. package/src/components/Marker.tsx +45 -36
  147. package/src/components/Marker.types.ts +75 -0
  148. package/src/components/Marker.web.tsx +116 -28
  149. package/src/components/Polygon.tsx +52 -0
  150. package/src/components/Polygon.types.ts +33 -0
  151. package/src/components/Polygon.web.tsx +104 -0
  152. package/src/components/Polyline.tsx +23 -22
  153. package/src/components/Polyline.types.ts +55 -0
  154. package/src/components/Polyline.web.tsx +151 -37
  155. package/src/components/index.ts +16 -2
  156. package/src/components/index.web.ts +6 -2
  157. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  158. package/src/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  159. package/src/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  160. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  161. package/src/index.ts +28 -1
  162. package/src/index.web.ts +15 -1
  163. package/src/types.ts +13 -0
  164. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +0 -511
  165. package/ios/LuggAppleMapView.h +0 -16
  166. package/ios/LuggAppleMapView.mm +0 -592
  167. package/ios/LuggGoogleMapView.h +0 -13
  168. package/ios/LuggGoogleMapView.mm +0 -455
  169. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  170. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  171. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  172. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  173. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -0,0 +1,1029 @@
1
+ #import "AppleMapProvider.h"
2
+ #import "../LuggMarkerView.h"
3
+ #import "../LuggPolygonView.h"
4
+ #import "../LuggPolylineView.h"
5
+ #import "../extensions/MKMapView+Zoom.h"
6
+ #import "MKPolylineAnimator.h"
7
+
8
+ @interface AppleMarkerAnnotation : NSObject <MKAnnotation>
9
+ @property(nonatomic, assign) CLLocationCoordinate2D coordinate;
10
+ @property(nonatomic, copy, nullable) NSString *title;
11
+ @property(nonatomic, copy, nullable) NSString *subtitle;
12
+ @property(nonatomic, strong) LuggMarkerView *markerView;
13
+ @property(nonatomic, weak) MKAnnotationView *annotationView;
14
+ @end
15
+
16
+ @implementation AppleMarkerAnnotation
17
+ @end
18
+
19
+ @implementation LuggAppleMapViewContent
20
+ @end
21
+
22
+ @interface AppleMapProvider () <
23
+ LuggMarkerViewDelegate, LuggPolylineViewDelegate, LuggPolygonViewDelegate,
24
+ UIGestureRecognizerDelegate>
25
+ @end
26
+
27
+ @implementation AppleMapProvider {
28
+ LuggAppleMapViewContent *_mapView;
29
+ BOOL _isMapReady;
30
+ BOOL _isDragging;
31
+ double _minZoom;
32
+ double _maxZoom;
33
+ NSMapTable<id<MKOverlay>, LuggPolylineView *> *_overlayToPolylineMap;
34
+ NSMapTable<id<MKOverlay>, LuggPolygonView *> *_overlayToPolygonMap;
35
+ UITapGestureRecognizer *_tapGesture;
36
+ UILongPressGestureRecognizer *_longPressGesture;
37
+ // Edge insets animation
38
+ CADisplayLink *_edgeInsetsDisplayLink;
39
+ UIEdgeInsets _edgeInsetsFrom;
40
+ UIEdgeInsets _edgeInsetsTo;
41
+ CFTimeInterval _edgeInsetsAnimationStart;
42
+ CFTimeInterval _edgeInsetsAnimationDuration;
43
+ }
44
+
45
+ @synthesize delegate = _delegate;
46
+
47
+ - (instancetype)init {
48
+ if (self = [super init]) {
49
+ _overlayToPolylineMap = [NSMapTable strongToWeakObjectsMapTable];
50
+ _overlayToPolygonMap = [NSMapTable strongToWeakObjectsMapTable];
51
+ }
52
+ return self;
53
+ }
54
+
55
+ - (BOOL)isMapReady {
56
+ return _isMapReady;
57
+ }
58
+
59
+ - (MKMapView *)mapView {
60
+ return _mapView;
61
+ }
62
+
63
+ #pragma mark - MapProvider
64
+
65
+ - (void)initializeMapInView:(UIView *)wrapperView
66
+ initialCoordinate:(CLLocationCoordinate2D)coordinate
67
+ initialZoom:(double)zoom {
68
+ if (_mapView)
69
+ return;
70
+
71
+ _mapView = [[LuggAppleMapViewContent alloc] initWithFrame:wrapperView.bounds];
72
+ _mapView.autoresizingMask =
73
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
74
+ _mapView.delegate = self;
75
+ _mapView.insetsLayoutMarginsFromSafeArea = NO;
76
+
77
+ [self applyZoomRange];
78
+
79
+ _tapGesture =
80
+ [[UITapGestureRecognizer alloc] initWithTarget:self
81
+ action:@selector(handleTap:)];
82
+ _tapGesture.cancelsTouchesInView = NO;
83
+ _tapGesture.delegate = self;
84
+ [_mapView addGestureRecognizer:_tapGesture];
85
+
86
+ _longPressGesture = [[UILongPressGestureRecognizer alloc]
87
+ initWithTarget:self
88
+ action:@selector(handleLongPress:)];
89
+ _longPressGesture.delegate = self;
90
+ [_mapView addGestureRecognizer:_longPressGesture];
91
+
92
+ [_tapGesture requireGestureRecognizerToFail:_longPressGesture];
93
+
94
+ [wrapperView addSubview:_mapView];
95
+
96
+ MKCoordinateRegion region = [_mapView regionForCenterCoordinate:coordinate
97
+ zoomLevel:zoom];
98
+ [_mapView setRegion:region animated:NO];
99
+
100
+ _isMapReady = YES;
101
+
102
+ [_delegate mapProviderDidReady];
103
+ }
104
+
105
+ - (void)destroy {
106
+ [self stopEdgeInsetsAnimation];
107
+ if (_tapGesture) {
108
+ [_mapView removeGestureRecognizer:_tapGesture];
109
+ _tapGesture = nil;
110
+ }
111
+ if (_longPressGesture) {
112
+ [_mapView removeGestureRecognizer:_longPressGesture];
113
+ _longPressGesture = nil;
114
+ }
115
+ [_mapView removeFromSuperview];
116
+ _mapView = nil;
117
+ _isMapReady = NO;
118
+ }
119
+
120
+ #pragma mark - Props
121
+
122
+ - (void)setZoomEnabled:(BOOL)enabled {
123
+ _mapView.zoomEnabled = enabled;
124
+ }
125
+
126
+ - (void)setScrollEnabled:(BOOL)enabled {
127
+ _mapView.scrollEnabled = enabled;
128
+ }
129
+
130
+ - (void)setRotateEnabled:(BOOL)enabled {
131
+ _mapView.rotateEnabled = enabled;
132
+ }
133
+
134
+ - (void)setPitchEnabled:(BOOL)enabled {
135
+ _mapView.pitchEnabled = enabled;
136
+ }
137
+
138
+ - (void)setUserLocationEnabled:(BOOL)enabled {
139
+ _mapView.showsUserLocation = enabled;
140
+ }
141
+
142
+ - (void)setTheme:(NSInteger)theme {
143
+ switch (theme) {
144
+ case 1: // Dark
145
+ _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
146
+ break;
147
+ case 0: // Light
148
+ _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
149
+ break;
150
+ default: // System
151
+ _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
152
+ break;
153
+ }
154
+ }
155
+
156
+ - (void)setMinZoom:(double)minZoom {
157
+ _minZoom = minZoom;
158
+ [self applyZoomRange];
159
+ }
160
+
161
+ - (void)setMaxZoom:(double)maxZoom {
162
+ _maxZoom = maxZoom;
163
+ [self applyZoomRange];
164
+ }
165
+
166
+ - (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
167
+ oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets {
168
+ CGFloat oldOffsetX = (oldEdgeInsets.left - oldEdgeInsets.right) / 2.0;
169
+ CGFloat oldOffsetY = (oldEdgeInsets.top - oldEdgeInsets.bottom) / 2.0;
170
+ CGFloat newOffsetX = (edgeInsets.left - edgeInsets.right) / 2.0;
171
+ CGFloat newOffsetY = (edgeInsets.top - edgeInsets.bottom) / 2.0;
172
+
173
+ CGFloat deltaX = newOffsetX - oldOffsetX;
174
+ CGFloat deltaY = newOffsetY - oldOffsetY;
175
+
176
+ _mapView.layoutMargins = edgeInsets;
177
+
178
+ if (deltaX != 0 || deltaY != 0) {
179
+ CLLocationCoordinate2D currentCenter = _mapView.centerCoordinate;
180
+ CGPoint centerPoint = [_mapView convertCoordinate:currentCenter
181
+ toPointToView:_mapView];
182
+ CGPoint newPoint =
183
+ CGPointMake(centerPoint.x - deltaX, centerPoint.y - deltaY);
184
+ CLLocationCoordinate2D newCenter = [_mapView convertPoint:newPoint
185
+ toCoordinateFromView:_mapView];
186
+ [_mapView setCenterCoordinate:newCenter animated:NO];
187
+ }
188
+ }
189
+
190
+ - (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
191
+ oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets
192
+ duration:(double)duration {
193
+ [self stopEdgeInsetsAnimation];
194
+
195
+ if (duration != 0 && _mapView) {
196
+ double actualDuration = duration < 0 ? 0.3 : duration / 1000.0;
197
+ _edgeInsetsFrom = oldEdgeInsets;
198
+ _edgeInsetsTo = edgeInsets;
199
+ _edgeInsetsAnimationDuration = actualDuration;
200
+ _edgeInsetsAnimationStart = CACurrentMediaTime();
201
+
202
+ _edgeInsetsDisplayLink = [CADisplayLink
203
+ displayLinkWithTarget:self
204
+ selector:@selector(edgeInsetsAnimationTick:)];
205
+ [_edgeInsetsDisplayLink addToRunLoop:[NSRunLoop mainRunLoop]
206
+ forMode:NSRunLoopCommonModes];
207
+ } else {
208
+ [self setEdgeInsets:edgeInsets oldEdgeInsets:oldEdgeInsets];
209
+ }
210
+ }
211
+
212
+ - (void)edgeInsetsAnimationTick:(CADisplayLink *)displayLink {
213
+ CFTimeInterval elapsed = CACurrentMediaTime() - _edgeInsetsAnimationStart;
214
+ CGFloat progress = MIN(elapsed / _edgeInsetsAnimationDuration, 1.0);
215
+
216
+ // Ease out cubic
217
+ CGFloat t = 1.0 - (1.0 - progress) * (1.0 - progress) * (1.0 - progress);
218
+
219
+ UIEdgeInsets current = UIEdgeInsetsMake(
220
+ _edgeInsetsFrom.top + (_edgeInsetsTo.top - _edgeInsetsFrom.top) * t,
221
+ _edgeInsetsFrom.left + (_edgeInsetsTo.left - _edgeInsetsFrom.left) * t,
222
+ _edgeInsetsFrom.bottom +
223
+ (_edgeInsetsTo.bottom - _edgeInsetsFrom.bottom) * t,
224
+ _edgeInsetsFrom.right +
225
+ (_edgeInsetsTo.right - _edgeInsetsFrom.right) * t);
226
+
227
+ [self setEdgeInsets:current oldEdgeInsets:_mapView.layoutMargins];
228
+
229
+ if (progress >= 1.0) {
230
+ [self stopEdgeInsetsAnimation];
231
+ }
232
+ }
233
+
234
+ - (void)stopEdgeInsetsAnimation {
235
+ [_edgeInsetsDisplayLink invalidate];
236
+ _edgeInsetsDisplayLink = nil;
237
+ }
238
+
239
+ - (CLLocationDistance)cameraDistanceForZoomLevel:(double)zoomLevel {
240
+ return 128000000.0 / pow(2, zoomLevel);
241
+ }
242
+
243
+ - (void)applyZoomRange {
244
+ if (!_mapView)
245
+ return;
246
+
247
+ CLLocationDistance minDistance =
248
+ _maxZoom > 0 ? [self cameraDistanceForZoomLevel:_maxZoom] : 0;
249
+ CLLocationDistance maxDistance =
250
+ _minZoom > 0 ? [self cameraDistanceForZoomLevel:_minZoom] : -1;
251
+
252
+ MKMapCameraZoomRange *zoomRange = [[MKMapCameraZoomRange alloc]
253
+ initWithMinCenterCoordinateDistance:minDistance
254
+ maxCenterCoordinateDistance:maxDistance];
255
+ _mapView.cameraZoomRange = zoomRange;
256
+ }
257
+
258
+ - (LuggPolygonView *)hitTestPolygonAtPoint:(CGPoint)point {
259
+ CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point
260
+ toCoordinateFromView:_mapView];
261
+ MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoordinate);
262
+ CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
263
+
264
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
265
+ for (NSInteger i = overlays.count - 1; i >= 0; i--) {
266
+ id<MKOverlay> overlay = overlays[i];
267
+ if (![overlay isKindOfClass:[MKPolygon class]])
268
+ continue;
269
+
270
+ LuggPolygonView *polygonView = [_overlayToPolygonMap objectForKey:overlay];
271
+ if (!polygonView || !polygonView.tappable)
272
+ continue;
273
+
274
+ NSArray<CLLocation *> *coordinates = polygonView.coordinates;
275
+ if (coordinates.count == 0)
276
+ continue;
277
+
278
+ CGMutablePathRef path = CGPathCreateMutable();
279
+ for (NSUInteger j = 0; j < coordinates.count; j++) {
280
+ MKMapPoint mp = MKMapPointForCoordinate(coordinates[j].coordinate);
281
+ if (j == 0) {
282
+ CGPathMoveToPoint(path, NULL, mp.x, mp.y);
283
+ } else {
284
+ CGPathAddLineToPoint(path, NULL, mp.x, mp.y);
285
+ }
286
+ }
287
+ CGPathCloseSubpath(path);
288
+
289
+ for (NSArray<CLLocation *> *hole in polygonView.holes) {
290
+ for (NSUInteger j = 0; j < hole.count; j++) {
291
+ MKMapPoint mp = MKMapPointForCoordinate(hole[j].coordinate);
292
+ if (j == 0) {
293
+ CGPathMoveToPoint(path, NULL, mp.x, mp.y);
294
+ } else {
295
+ CGPathAddLineToPoint(path, NULL, mp.x, mp.y);
296
+ }
297
+ }
298
+ CGPathCloseSubpath(path);
299
+ }
300
+
301
+ BOOL contains = CGPathContainsPoint(path, NULL, mapPointAsCGP, YES);
302
+ CGPathRelease(path);
303
+
304
+ if (contains)
305
+ return polygonView;
306
+ }
307
+ return nil;
308
+ }
309
+
310
+ - (BOOL)hitTestAnnotationAtPoint:(CGPoint)point {
311
+ for (id<MKAnnotation> annotation in _mapView.annotations) {
312
+ MKAnnotationView *view = [_mapView viewForAnnotation:annotation];
313
+ if (!view)
314
+ continue;
315
+ CGPoint local = [view convertPoint:point fromView:_mapView];
316
+ if ([view pointInside:local withEvent:nil])
317
+ return YES;
318
+ }
319
+ return NO;
320
+ }
321
+
322
+ - (void)handleTap:(UITapGestureRecognizer *)gesture {
323
+ if (gesture.state != UIGestureRecognizerStateEnded)
324
+ return;
325
+
326
+ CGPoint point = [gesture locationInView:_mapView];
327
+
328
+ if ([self hitTestAnnotationAtPoint:point])
329
+ return;
330
+
331
+ LuggPolygonView *polygonView = [self hitTestPolygonAtPoint:point];
332
+ if (polygonView) {
333
+ [polygonView emitPressEvent];
334
+ } else {
335
+ CLLocationCoordinate2D coordinate = [_mapView convertPoint:point
336
+ toCoordinateFromView:_mapView];
337
+ [_delegate mapProviderDidPress:coordinate.latitude
338
+ longitude:coordinate.longitude
339
+ x:point.x
340
+ y:point.y];
341
+ }
342
+ }
343
+
344
+ - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
345
+ if (gesture.state != UIGestureRecognizerStateBegan)
346
+ return;
347
+
348
+ CGPoint point = [gesture locationInView:_mapView];
349
+ CLLocationCoordinate2D coordinate = [_mapView convertPoint:point
350
+ toCoordinateFromView:_mapView];
351
+ [_delegate mapProviderDidLongPress:coordinate.latitude
352
+ longitude:coordinate.longitude
353
+ x:point.x
354
+ y:point.y];
355
+ }
356
+
357
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
358
+ shouldRecognizeSimultaneouslyWithGestureRecognizer:
359
+ (UIGestureRecognizer *)otherGestureRecognizer {
360
+ return YES;
361
+ }
362
+
363
+ #pragma mark - MKMapViewDelegate
364
+
365
+ - (BOOL)isUserInteracting {
366
+ UIView *mapContainerView = _mapView.subviews.firstObject;
367
+ for (UIGestureRecognizer *gesture in mapContainerView.gestureRecognizers) {
368
+ if (gesture.state == UIGestureRecognizerStateBegan ||
369
+ gesture.state == UIGestureRecognizerStateChanged) {
370
+ return YES;
371
+ }
372
+ }
373
+ return NO;
374
+ }
375
+
376
+ - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
377
+ _isDragging = [self isUserInteracting];
378
+ if (_isDragging) {
379
+ for (LuggPolylineView *polylineView in _overlayToPolylineMap
380
+ .objectEnumerator) {
381
+ MKPolylineAnimator *renderer =
382
+ (MKPolylineAnimator *)polylineView.renderer;
383
+ [renderer pause];
384
+ }
385
+ }
386
+ }
387
+
388
+ - (void)mapViewDidChangeVisibleRegion:(MKMapView *)mapView {
389
+ [_delegate mapProviderDidMoveCamera:mapView.centerCoordinate.latitude
390
+ longitude:mapView.centerCoordinate.longitude
391
+ zoom:mapView.zoomLevel
392
+ gesture:_isDragging];
393
+ }
394
+
395
+ - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
396
+ BOOL wasDragging = _isDragging;
397
+ _isDragging = NO;
398
+ if (wasDragging) {
399
+ for (LuggPolylineView *polylineView in _overlayToPolylineMap
400
+ .objectEnumerator) {
401
+ MKPolylineAnimator *renderer =
402
+ (MKPolylineAnimator *)polylineView.renderer;
403
+ [renderer resume];
404
+ }
405
+ }
406
+ [_delegate mapProviderDidIdleCamera:mapView.centerCoordinate.latitude
407
+ longitude:mapView.centerCoordinate.longitude
408
+ zoom:mapView.zoomLevel
409
+ gesture:wasDragging];
410
+ }
411
+
412
+ - (MKAnnotationView *)mapView:(MKMapView *)mapView
413
+ viewForAnnotation:(id<MKAnnotation>)annotation {
414
+ if (![annotation isKindOfClass:[AppleMarkerAnnotation class]]) {
415
+ return nil;
416
+ }
417
+
418
+ AppleMarkerAnnotation *markerAnnotation = (AppleMarkerAnnotation *)annotation;
419
+ LuggMarkerView *markerView = markerAnnotation.markerView;
420
+
421
+ if (!markerView) {
422
+ return nil;
423
+ }
424
+
425
+ if (!markerView.hasCustomView) {
426
+ MKMarkerAnnotationView *markerAnnotationView =
427
+ [[MKMarkerAnnotationView alloc] initWithAnnotation:annotation
428
+ reuseIdentifier:nil];
429
+ markerAnnotationView.canShowCallout = YES;
430
+ markerAnnotationView.displayPriority = MKFeatureDisplayPriorityRequired;
431
+ markerAnnotationView.layer.zPosition = markerView.zIndex;
432
+ markerAnnotationView.zPriority = markerView.zIndex;
433
+ markerAnnotationView.draggable = markerView.draggable;
434
+ [self addCenterTapGesture:markerAnnotationView];
435
+ markerAnnotation.annotationView = markerAnnotationView;
436
+ return markerAnnotationView;
437
+ }
438
+
439
+ MKAnnotationView *annotationView =
440
+ [[MKAnnotationView alloc] initWithAnnotation:annotation
441
+ reuseIdentifier:nil];
442
+ annotationView.canShowCallout = YES;
443
+ annotationView.displayPriority = MKFeatureDisplayPriorityRequired;
444
+ annotationView.layer.zPosition = markerView.zIndex;
445
+ annotationView.zPriority = markerView.zIndex;
446
+ annotationView.draggable = markerView.draggable;
447
+ [self addCenterTapGesture:annotationView];
448
+
449
+ if (!markerView.rasterize) {
450
+ UIView *iconView = markerView.iconView;
451
+ [iconView removeFromSuperview];
452
+ [annotationView addSubview:iconView];
453
+ }
454
+
455
+ [self applyMarkerStyle:markerView annotationView:annotationView];
456
+ markerAnnotation.annotationView = annotationView;
457
+
458
+ return annotationView;
459
+ }
460
+
461
+ - (MKOverlayRenderer *)mapView:(MKMapView *)mapView
462
+ rendererForOverlay:(id<MKOverlay>)overlay {
463
+ if ([overlay isKindOfClass:[MKPolyline class]]) {
464
+ LuggPolylineView *polylineView =
465
+ [_overlayToPolylineMap objectForKey:overlay];
466
+ MKPolyline *polyline = (MKPolyline *)overlay;
467
+
468
+ if (polylineView) {
469
+ NSArray<UIColor *> *colors = polylineView.strokeColors;
470
+
471
+ MKPolylineAnimator *renderer =
472
+ [[MKPolylineAnimator alloc] initWithPolyline:polyline];
473
+ renderer.lineWidth = polylineView.strokeWidth;
474
+ renderer.strokeColor = colors.firstObject;
475
+ if (colors.count > 1) {
476
+ renderer.strokeColors = colors;
477
+ }
478
+ renderer.animatedOptions = polylineView.animatedOptions;
479
+ renderer.animated = polylineView.animated;
480
+ polylineView.renderer = renderer;
481
+ return renderer;
482
+ }
483
+
484
+ MKPolylineRenderer *renderer =
485
+ [[MKPolylineRenderer alloc] initWithPolyline:polyline];
486
+ return renderer;
487
+ }
488
+
489
+ if ([overlay isKindOfClass:[MKPolygon class]]) {
490
+ LuggPolygonView *polygonView = [_overlayToPolygonMap objectForKey:overlay];
491
+ MKPolygon *polygon = (MKPolygon *)overlay;
492
+
493
+ MKPolygonRenderer *renderer =
494
+ [[MKPolygonRenderer alloc] initWithPolygon:polygon];
495
+ if (polygonView) {
496
+ renderer.fillColor = polygonView.fillColor;
497
+ renderer.strokeColor = polygonView.strokeColor;
498
+ renderer.lineWidth = polygonView.strokeWidth;
499
+ polygonView.renderer = renderer;
500
+ }
501
+ return renderer;
502
+ }
503
+
504
+ return nil;
505
+ }
506
+
507
+ - (void)mapView:(MKMapView *)mapView
508
+ didSelectAnnotationView:(MKAnnotationView *)view {
509
+ if (![view.annotation isKindOfClass:[AppleMarkerAnnotation class]])
510
+ return;
511
+
512
+ AppleMarkerAnnotation *annotation = (AppleMarkerAnnotation *)view.annotation;
513
+ LuggMarkerView *markerView = annotation.markerView;
514
+
515
+ if (markerView) {
516
+ CGPoint point = [_mapView convertCoordinate:markerView.coordinate
517
+ toPointToView:_mapView];
518
+ [markerView emitPressEventWithPoint:point];
519
+ }
520
+ }
521
+
522
+ - (void)mapView:(MKMapView *)mapView
523
+ annotationView:(MKAnnotationView *)view
524
+ didChangeDragState:(MKAnnotationViewDragState)newState
525
+ fromOldState:(MKAnnotationViewDragState)oldState {
526
+ if (![view.annotation isKindOfClass:[AppleMarkerAnnotation class]])
527
+ return;
528
+
529
+ AppleMarkerAnnotation *annotation = (AppleMarkerAnnotation *)view.annotation;
530
+ LuggMarkerView *markerView = annotation.markerView;
531
+ if (!markerView)
532
+ return;
533
+
534
+ CLLocationCoordinate2D coord = annotation.coordinate;
535
+ CGPoint point = [_mapView convertCoordinate:coord toPointToView:_mapView];
536
+
537
+ switch (newState) {
538
+ case MKAnnotationViewDragStateStarting:
539
+ [markerView updateCoordinate:coord];
540
+ [markerView emitDragStartEventWithPoint:point];
541
+ [view setDragState:MKAnnotationViewDragStateDragging animated:YES];
542
+ break;
543
+ case MKAnnotationViewDragStateDragging:
544
+ [markerView updateCoordinate:coord];
545
+ [markerView emitDragChangeEventWithPoint:point];
546
+ break;
547
+ case MKAnnotationViewDragStateEnding:
548
+ [markerView updateCoordinate:coord];
549
+ [markerView emitDragEndEventWithPoint:point];
550
+ [view setDragState:MKAnnotationViewDragStateNone animated:YES];
551
+ break;
552
+ case MKAnnotationViewDragStateCanceling:
553
+ [view setDragState:MKAnnotationViewDragStateNone animated:YES];
554
+ break;
555
+ default:
556
+ break;
557
+ }
558
+ }
559
+
560
+ - (void)addCenterTapGesture:(MKAnnotationView *)view {
561
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
562
+ initWithTarget:self
563
+ action:@selector(handleAnnotationTap:)];
564
+ tap.cancelsTouchesInView = NO;
565
+ [view addGestureRecognizer:tap];
566
+ }
567
+
568
+ - (void)handleAnnotationTap:(UITapGestureRecognizer *)gesture {
569
+ MKAnnotationView *view = (MKAnnotationView *)gesture.view;
570
+ if ([view.annotation isKindOfClass:[AppleMarkerAnnotation class]]) {
571
+ AppleMarkerAnnotation *annotation =
572
+ (AppleMarkerAnnotation *)view.annotation;
573
+ [_mapView setCenterCoordinate:annotation.coordinate animated:YES];
574
+ }
575
+ }
576
+
577
+ #pragma mark - MarkerViewDelegate
578
+
579
+ - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
580
+ AppleMarkerAnnotation *annotation =
581
+ (AppleMarkerAnnotation *)markerView.marker;
582
+ if (annotation) {
583
+ [self updateAnnotationViewFrame:annotation];
584
+ }
585
+ }
586
+
587
+ - (void)markerViewDidUpdate:(LuggMarkerView *)markerView {
588
+ AppleMarkerAnnotation *annotation =
589
+ (AppleMarkerAnnotation *)markerView.marker;
590
+
591
+ if (!annotation)
592
+ return;
593
+
594
+ annotation.coordinate = markerView.coordinate;
595
+ annotation.title = markerView.title;
596
+ annotation.subtitle = markerView.markerDescription;
597
+
598
+ MKAnnotationView *annotationView = annotation.annotationView;
599
+ if (annotationView) {
600
+ annotationView.draggable = markerView.draggable;
601
+ annotationView.layer.zPosition = markerView.zIndex;
602
+ annotationView.zPriority = markerView.zIndex;
603
+ }
604
+
605
+ [self updateAnnotationViewFrame:annotation];
606
+ }
607
+
608
+ #pragma mark - PolylineViewDelegate
609
+
610
+ - (void)polylineViewDidUpdate:(LuggPolylineView *)polylineView {
611
+ [self syncPolylineView:polylineView];
612
+ }
613
+
614
+ #pragma mark - PolygonViewDelegate
615
+
616
+ - (void)polygonViewDidUpdate:(LuggPolygonView *)polygonView {
617
+ [self syncPolygonView:polygonView];
618
+ }
619
+
620
+ #pragma mark - Marker Management
621
+
622
+ - (void)addMarkerView:(LuggMarkerView *)markerView {
623
+ markerView.delegate = self;
624
+
625
+ AppleMarkerAnnotation *annotation = [[AppleMarkerAnnotation alloc] init];
626
+ annotation.markerView = markerView;
627
+ markerView.marker = annotation;
628
+
629
+ if (_mapView) {
630
+ [_mapView addAnnotation:annotation];
631
+ }
632
+
633
+ [self markerViewDidUpdate:markerView];
634
+ }
635
+
636
+ - (void)removeMarkerView:(LuggMarkerView *)markerView {
637
+ markerView.delegate = nil;
638
+
639
+ AppleMarkerAnnotation *annotation =
640
+ (AppleMarkerAnnotation *)markerView.marker;
641
+
642
+ if (annotation) {
643
+ annotation.annotationView.transform = CGAffineTransformIdentity;
644
+ annotation.markerView = nil;
645
+ annotation.annotationView = nil;
646
+ [_mapView removeAnnotation:annotation];
647
+ markerView.marker = nil;
648
+ }
649
+ [markerView resetIconViewTransform];
650
+ }
651
+
652
+ - (void)syncMarkerView:(LuggMarkerView *)markerView {
653
+ [self markerViewDidUpdate:markerView];
654
+ }
655
+
656
+ - (void)applyMarkerStyle:(LuggMarkerView *)markerView
657
+ annotationView:(MKAnnotationView *)annotationView {
658
+ annotationView.transform = CGAffineTransformIdentity;
659
+
660
+ UIView *iconView = markerView.iconView;
661
+ CGRect frame = iconView.frame;
662
+ if (frame.size.width <= 0 || frame.size.height <= 0)
663
+ return;
664
+
665
+ CGFloat scale = markerView.scale;
666
+ CGPoint anchor = markerView.anchor;
667
+
668
+ if (markerView.rasterize) {
669
+ annotationView.image = [markerView createScaledIconImage];
670
+ } else {
671
+ iconView.layer.anchorPoint = anchor;
672
+ iconView.transform = CGAffineTransformMakeScale(scale, scale);
673
+ iconView.frame =
674
+ CGRectMake(frame.size.width * (0.5 - anchor.x) * (scale - 1),
675
+ frame.size.height * (0.5 - anchor.y) * (scale - 1),
676
+ frame.size.width, frame.size.height);
677
+ }
678
+
679
+ annotationView.bounds =
680
+ CGRectMake(0, 0, frame.size.width * scale, frame.size.height * scale);
681
+ annotationView.centerOffset =
682
+ CGPointMake(frame.size.width * scale * (anchor.x - 0.5),
683
+ -frame.size.height * scale * (anchor.y - 0.5));
684
+ annotationView.transform =
685
+ CGAffineTransformMakeRotation(markerView.rotate * M_PI / 180.0);
686
+ }
687
+
688
+ - (void)updateAnnotationViewFrame:(AppleMarkerAnnotation *)annotation {
689
+ MKAnnotationView *annotationView = annotation.annotationView;
690
+ LuggMarkerView *markerView = annotation.markerView;
691
+
692
+ if (!annotationView || !markerView)
693
+ return;
694
+
695
+ [self applyMarkerStyle:markerView annotationView:annotationView];
696
+ }
697
+
698
+ #pragma mark - Polyline Management
699
+
700
+ - (void)addPolylineView:(LuggPolylineView *)polylineView {
701
+ polylineView.delegate = self;
702
+ [self addPolylineOverlayToMap:polylineView];
703
+ }
704
+
705
+ - (void)removePolylineView:(LuggPolylineView *)polylineView {
706
+ polylineView.delegate = nil;
707
+ MKPolyline *polyline = (MKPolyline *)polylineView.polyline;
708
+ if (polyline) {
709
+ [_overlayToPolylineMap removeObjectForKey:polyline];
710
+ [_mapView removeOverlay:polyline];
711
+ polylineView.polyline = nil;
712
+ }
713
+ }
714
+
715
+ - (void)syncPolylineView:(LuggPolylineView *)polylineView {
716
+ if (!_mapView)
717
+ return;
718
+
719
+ MKPolylineAnimator *renderer = (MKPolylineAnimator *)polylineView.renderer;
720
+ MKPolyline *oldPolyline = (MKPolyline *)polylineView.polyline;
721
+
722
+ NSArray<CLLocation *> *coordinates = polylineView.coordinates;
723
+ if (coordinates.count == 0) {
724
+ if (renderer) {
725
+ renderer.animated = NO;
726
+ }
727
+ if (oldPolyline) {
728
+ [_overlayToPolylineMap removeObjectForKey:oldPolyline];
729
+ [_mapView removeOverlay:oldPolyline];
730
+ polylineView.polyline = nil;
731
+ polylineView.renderer = nil;
732
+ }
733
+ return;
734
+ }
735
+
736
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(
737
+ sizeof(CLLocationCoordinate2D) * coordinates.count);
738
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
739
+ coords[i] = coordinates[i].coordinate;
740
+ }
741
+ MKPolyline *newPolyline =
742
+ [MKPolyline polylineWithCoordinates:coords count:coordinates.count];
743
+ free(coords);
744
+
745
+ polylineView.polyline = newPolyline;
746
+ [_overlayToPolylineMap setObject:polylineView forKey:newPolyline];
747
+
748
+ if (renderer && oldPolyline) {
749
+ [_overlayToPolylineMap removeObjectForKey:oldPolyline];
750
+ [_mapView removeOverlay:oldPolyline];
751
+ [self insertOverlay:newPolyline withZIndex:polylineView.zIndex];
752
+ [renderer updatePolyline:newPolyline];
753
+ renderer.lineWidth = polylineView.strokeWidth;
754
+ renderer.strokeColor = polylineView.strokeColors.firstObject;
755
+ renderer.strokeColors =
756
+ polylineView.strokeColors.count > 1 ? polylineView.strokeColors : nil;
757
+ renderer.animatedOptions = polylineView.animatedOptions;
758
+ renderer.animated = polylineView.animated;
759
+ return;
760
+ }
761
+
762
+ if (oldPolyline) {
763
+ [_overlayToPolylineMap removeObjectForKey:oldPolyline];
764
+ [_mapView removeOverlay:oldPolyline];
765
+ }
766
+ [self insertOverlay:newPolyline withZIndex:polylineView.zIndex];
767
+ }
768
+
769
+ - (void)addPolylineOverlayToMap:(LuggPolylineView *)polylineView {
770
+ if (!_mapView)
771
+ return;
772
+
773
+ NSArray<CLLocation *> *coordinates = polylineView.coordinates;
774
+ if (coordinates.count == 0)
775
+ return;
776
+
777
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(
778
+ sizeof(CLLocationCoordinate2D) * coordinates.count);
779
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
780
+ coords[i] = coordinates[i].coordinate;
781
+ }
782
+
783
+ MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords
784
+ count:coordinates.count];
785
+ free(coords);
786
+
787
+ polylineView.polyline = polyline;
788
+ [_overlayToPolylineMap setObject:polylineView forKey:polyline];
789
+ [self insertOverlay:polyline withZIndex:polylineView.zIndex];
790
+ }
791
+
792
+ #pragma mark - Polygon Management
793
+
794
+ - (void)addPolygonView:(LuggPolygonView *)polygonView {
795
+ polygonView.delegate = self;
796
+ [self addPolygonOverlayToMap:polygonView];
797
+ }
798
+
799
+ - (void)removePolygonView:(LuggPolygonView *)polygonView {
800
+ polygonView.delegate = nil;
801
+ MKPolygon *polygon = (MKPolygon *)polygonView.polygon;
802
+ if (polygon) {
803
+ [_overlayToPolygonMap removeObjectForKey:polygon];
804
+ [_mapView removeOverlay:polygon];
805
+ polygonView.polygon = nil;
806
+ }
807
+ }
808
+
809
+ - (void)syncPolygonView:(LuggPolygonView *)polygonView {
810
+ if (!_mapView)
811
+ return;
812
+
813
+ MKPolygon *oldPolygon = (MKPolygon *)polygonView.polygon;
814
+
815
+ NSArray<CLLocation *> *coordinates = polygonView.coordinates;
816
+ if (coordinates.count == 0) {
817
+ if (oldPolygon) {
818
+ [_overlayToPolygonMap removeObjectForKey:oldPolygon];
819
+ [_mapView removeOverlay:oldPolygon];
820
+ polygonView.polygon = nil;
821
+ polygonView.renderer = nil;
822
+ }
823
+ return;
824
+ }
825
+
826
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(
827
+ sizeof(CLLocationCoordinate2D) * coordinates.count);
828
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
829
+ coords[i] = coordinates[i].coordinate;
830
+ }
831
+ NSArray<MKPolygon *> *interiorPolygons =
832
+ [self interiorPolygonsFromHoles:polygonView.holes];
833
+ MKPolygon *newPolygon = [MKPolygon polygonWithCoordinates:coords
834
+ count:coordinates.count
835
+ interiorPolygons:interiorPolygons];
836
+ free(coords);
837
+
838
+ polygonView.polygon = newPolygon;
839
+ [_overlayToPolygonMap setObject:polygonView forKey:newPolygon];
840
+
841
+ MKPolygonRenderer *renderer = (MKPolygonRenderer *)polygonView.renderer;
842
+ if (renderer && oldPolygon) {
843
+ [_overlayToPolygonMap removeObjectForKey:oldPolygon];
844
+ [_mapView removeOverlay:oldPolygon];
845
+ [self insertOverlay:newPolygon withZIndex:polygonView.zIndex];
846
+ polygonView.renderer = nil;
847
+ return;
848
+ }
849
+
850
+ if (oldPolygon) {
851
+ [_overlayToPolygonMap removeObjectForKey:oldPolygon];
852
+ [_mapView removeOverlay:oldPolygon];
853
+ }
854
+ [self insertOverlay:newPolygon withZIndex:polygonView.zIndex];
855
+ }
856
+
857
+ - (void)addPolygonOverlayToMap:(LuggPolygonView *)polygonView {
858
+ if (!_mapView)
859
+ return;
860
+
861
+ NSArray<CLLocation *> *coordinates = polygonView.coordinates;
862
+ if (coordinates.count == 0)
863
+ return;
864
+
865
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(
866
+ sizeof(CLLocationCoordinate2D) * coordinates.count);
867
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
868
+ coords[i] = coordinates[i].coordinate;
869
+ }
870
+
871
+ NSArray<MKPolygon *> *interiorPolygons =
872
+ [self interiorPolygonsFromHoles:polygonView.holes];
873
+ MKPolygon *polygon = [MKPolygon polygonWithCoordinates:coords
874
+ count:coordinates.count
875
+ interiorPolygons:interiorPolygons];
876
+ free(coords);
877
+
878
+ polygonView.polygon = polygon;
879
+ [_overlayToPolygonMap setObject:polygonView forKey:polygon];
880
+ [self insertOverlay:polygon withZIndex:polygonView.zIndex];
881
+ }
882
+
883
+ - (NSArray<MKPolygon *> *)interiorPolygonsFromHoles:
884
+ (NSArray<NSArray<CLLocation *> *> *)holes {
885
+ if (holes.count == 0)
886
+ return @[];
887
+
888
+ NSMutableArray<MKPolygon *> *interiorPolygons = [NSMutableArray array];
889
+ for (NSArray<CLLocation *> *hole in holes) {
890
+ if (hole.count == 0)
891
+ continue;
892
+ CLLocationCoordinate2D *holeCoords = (CLLocationCoordinate2D *)malloc(
893
+ sizeof(CLLocationCoordinate2D) * hole.count);
894
+ for (NSUInteger i = 0; i < hole.count; i++) {
895
+ holeCoords[i] = hole[i].coordinate;
896
+ }
897
+ MKPolygon *interiorPolygon = [MKPolygon polygonWithCoordinates:holeCoords
898
+ count:hole.count];
899
+ free(holeCoords);
900
+ [interiorPolygons addObject:interiorPolygon];
901
+ }
902
+ return [interiorPolygons copy];
903
+ }
904
+
905
+ - (void)insertOverlay:(id<MKOverlay>)overlay withZIndex:(NSInteger)zIndex {
906
+ if (zIndex == 0) {
907
+ [_mapView addOverlay:overlay];
908
+ return;
909
+ }
910
+
911
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
912
+ NSInteger insertIndex = overlays.count;
913
+
914
+ for (NSInteger i = 0; i < overlays.count; i++) {
915
+ NSInteger existingZIndex = 0;
916
+ LuggPolylineView *existingPolylineView =
917
+ [_overlayToPolylineMap objectForKey:overlays[i]];
918
+ LuggPolygonView *existingPolygonView =
919
+ [_overlayToPolygonMap objectForKey:overlays[i]];
920
+ if (existingPolylineView) {
921
+ existingZIndex = existingPolylineView.zIndex;
922
+ } else if (existingPolygonView) {
923
+ existingZIndex = existingPolygonView.zIndex;
924
+ } else {
925
+ continue;
926
+ }
927
+ if (existingZIndex > zIndex) {
928
+ insertIndex = i;
929
+ break;
930
+ }
931
+ }
932
+
933
+ [_mapView insertOverlay:overlay atIndex:insertIndex];
934
+ }
935
+
936
+ #pragma mark - Lifecycle
937
+
938
+ - (void)pauseAnimations {
939
+ for (LuggPolylineView *polylineView in _overlayToPolylineMap
940
+ .objectEnumerator) {
941
+ MKPolylineAnimator *renderer = (MKPolylineAnimator *)polylineView.renderer;
942
+ [renderer pause];
943
+ }
944
+ }
945
+
946
+ - (void)resumeAnimations {
947
+ for (LuggPolylineView *polylineView in _overlayToPolylineMap
948
+ .objectEnumerator) {
949
+ MKPolylineAnimator *renderer = (MKPolylineAnimator *)polylineView.renderer;
950
+ [renderer resume];
951
+ }
952
+ }
953
+
954
+ #pragma mark - Commands
955
+
956
+ - (void)moveCamera:(double)latitude
957
+ longitude:(double)longitude
958
+ zoom:(double)zoom
959
+ duration:(double)duration {
960
+ if (!_mapView)
961
+ return;
962
+
963
+ double targetZoom = zoom > 0 ? zoom : _mapView.zoomLevel;
964
+
965
+ if (duration < 0) {
966
+ [_mapView
967
+ setCenterCoordinate:CLLocationCoordinate2DMake(latitude, longitude)
968
+ zoomLevel:targetZoom
969
+ animated:YES];
970
+ } else if (duration > 0) {
971
+ CLLocationCoordinate2D center =
972
+ CLLocationCoordinate2DMake(latitude, longitude);
973
+ MKCoordinateRegion region = [_mapView regionForCenterCoordinate:center
974
+ zoomLevel:targetZoom];
975
+ [UIView animateWithDuration:duration / 1000.0
976
+ animations:^{
977
+ [self->_mapView setRegion:region animated:NO];
978
+ }];
979
+ } else {
980
+ [_mapView
981
+ setCenterCoordinate:CLLocationCoordinate2DMake(latitude, longitude)
982
+ zoomLevel:targetZoom
983
+ animated:NO];
984
+ }
985
+ }
986
+
987
+ - (void)fitCoordinates:(NSArray *)coordinates
988
+ edgeInsetsTop:(double)edgeInsetsTop
989
+ edgeInsetsLeft:(double)edgeInsetsLeft
990
+ edgeInsetsBottom:(double)edgeInsetsBottom
991
+ edgeInsetsRight:(double)edgeInsetsRight
992
+ duration:(double)duration {
993
+ if (!_mapView || coordinates.count == 0)
994
+ return;
995
+
996
+ CLLocationCoordinate2D *coords = (CLLocationCoordinate2D *)malloc(
997
+ sizeof(CLLocationCoordinate2D) * coordinates.count);
998
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
999
+ NSDictionary *coord = coordinates[i];
1000
+ coords[i] = CLLocationCoordinate2DMake([coord[@"latitude"] doubleValue],
1001
+ [coord[@"longitude"] doubleValue]);
1002
+ }
1003
+
1004
+ MKMapRect mapRect = MKMapRectNull;
1005
+ for (NSUInteger i = 0; i < coordinates.count; i++) {
1006
+ MKMapPoint point = MKMapPointForCoordinate(coords[i]);
1007
+ MKMapRect pointRect = MKMapRectMake(point.x, point.y, 0, 0);
1008
+ mapRect = MKMapRectUnion(mapRect, pointRect);
1009
+ }
1010
+ free(coords);
1011
+
1012
+ UIEdgeInsets insets = UIEdgeInsetsMake(edgeInsetsTop, edgeInsetsLeft,
1013
+ edgeInsetsBottom, edgeInsetsRight);
1014
+
1015
+ if (duration < 0) {
1016
+ [_mapView setVisibleMapRect:mapRect edgePadding:insets animated:YES];
1017
+ } else if (duration > 0) {
1018
+ [UIView animateWithDuration:duration / 1000.0
1019
+ animations:^{
1020
+ [self->_mapView setVisibleMapRect:mapRect
1021
+ edgePadding:insets
1022
+ animated:NO];
1023
+ }];
1024
+ } else {
1025
+ [_mapView setVisibleMapRect:mapRect edgePadding:insets animated:NO];
1026
+ }
1027
+ }
1028
+
1029
+ @end