@lugg/maps 1.0.0-beta.2 → 1.0.0-beta.21

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 (206) hide show
  1. package/README.md +3 -0
  2. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +9 -2
  3. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +4 -3
  4. package/android/src/main/java/com/luggmaps/LuggCircleView.kt +85 -0
  5. package/android/src/main/java/com/luggmaps/LuggCircleViewManager.kt +79 -0
  6. package/android/src/main/java/com/luggmaps/LuggGroundOverlayView.kt +81 -0
  7. package/android/src/main/java/com/luggmaps/LuggGroundOverlayViewManager.kt +82 -0
  8. package/android/src/main/java/com/luggmaps/LuggMapView.kt +90 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +45 -0
  10. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +27 -2
  11. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +30 -2
  12. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  13. package/android/src/main/java/com/luggmaps/LuggPackage.kt +4 -1
  14. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +3 -23
  15. package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
  16. package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
  17. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +737 -37
  18. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +15 -0
  19. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +65 -11
  20. package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
  21. package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
  22. package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
  23. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  24. package/ios/LuggCalloutView.h +1 -1
  25. package/ios/LuggCalloutView.mm +21 -11
  26. package/ios/LuggCircleView.h +31 -0
  27. package/ios/LuggCircleView.mm +133 -0
  28. package/ios/LuggGroundOverlayView.h +30 -0
  29. package/ios/LuggGroundOverlayView.mm +125 -0
  30. package/ios/LuggMapView.mm +254 -4
  31. package/ios/LuggMapWrapperView.h +5 -0
  32. package/ios/LuggMapWrapperView.mm +18 -0
  33. package/ios/LuggMarkerView.h +3 -0
  34. package/ios/LuggMarkerView.mm +25 -0
  35. package/ios/LuggTileOverlayView.h +31 -0
  36. package/ios/LuggTileOverlayView.mm +130 -0
  37. package/ios/core/AppleMapProvider.h +8 -0
  38. package/ios/core/AppleMapProvider.mm +663 -29
  39. package/ios/core/AppleStaticMapProvider.h +14 -0
  40. package/ios/core/AppleStaticMapProvider.mm +159 -0
  41. package/ios/core/GMSPolylineAnimator.m +46 -18
  42. package/ios/core/GoogleMapProvider.h +10 -0
  43. package/ios/core/GoogleMapProvider.mm +430 -51
  44. package/ios/core/GoogleStaticMapProvider.h +21 -0
  45. package/ios/core/GoogleStaticMapProvider.mm +213 -0
  46. package/ios/core/MKPolylineAnimator.m +67 -43
  47. package/ios/core/MapProviderDelegate.h +27 -1
  48. package/ios/core/StaticMapProviderBase.h +75 -0
  49. package/ios/core/StaticMapProviderBase.mm +845 -0
  50. package/ios/events/CameraIdleEvent.h +4 -2
  51. package/ios/events/CameraMoveEvent.h +4 -2
  52. package/ios/events/CirclePressEvent.h +22 -0
  53. package/ios/events/GroundOverlayPressEvent.h +22 -0
  54. package/ios/events/LongPressEvent.h +4 -2
  55. package/ios/events/MarkerDragEvent.h +12 -6
  56. package/ios/events/MarkerPressEvent.h +4 -2
  57. package/ios/events/PolygonPressEvent.h +4 -2
  58. package/ios/events/PressEvent.h +4 -2
  59. package/ios/events/ReadyEvent.h +4 -2
  60. package/ios/events/TileOverlayPressEvent.h +22 -0
  61. package/ios/extensions/MKMapView+Zoom.h +20 -0
  62. package/ios/extensions/MKMapView+Zoom.m +28 -18
  63. package/ios/extensions/UIView+Snapshot.h +16 -0
  64. package/ios/extensions/UIView+Snapshot.m +27 -0
  65. package/lib/module/MapProvider.web.js +4 -1
  66. package/lib/module/MapProvider.web.js.map +1 -1
  67. package/lib/module/MapView.js +19 -1
  68. package/lib/module/MapView.js.map +1 -1
  69. package/lib/module/MapView.web.js +30 -22
  70. package/lib/module/MapView.web.js.map +1 -1
  71. package/lib/module/components/Circle.js +46 -0
  72. package/lib/module/components/Circle.js.map +1 -0
  73. package/lib/module/components/Circle.types.js +4 -0
  74. package/lib/module/components/Circle.types.js.map +1 -0
  75. package/lib/module/components/Circle.web.js +79 -0
  76. package/lib/module/components/Circle.web.js.map +1 -0
  77. package/lib/module/components/GeoJson.js +3 -3
  78. package/lib/module/components/GeoJson.js.map +1 -1
  79. package/lib/module/components/GroundOverlay.js +45 -0
  80. package/lib/module/components/GroundOverlay.js.map +1 -0
  81. package/lib/module/components/GroundOverlay.types.js +4 -0
  82. package/lib/module/components/GroundOverlay.types.js.map +1 -0
  83. package/lib/module/components/GroundOverlay.web.js +77 -0
  84. package/lib/module/components/GroundOverlay.web.js.map +1 -0
  85. package/lib/module/components/Marker.js +22 -4
  86. package/lib/module/components/Marker.js.map +1 -1
  87. package/lib/module/components/Marker.web.js +23 -10
  88. package/lib/module/components/Marker.web.js.map +1 -1
  89. package/lib/module/components/Polygon.web.js +3 -3
  90. package/lib/module/components/Polygon.web.js.map +1 -1
  91. package/lib/module/components/Polyline.web.js +6 -12
  92. package/lib/module/components/Polyline.web.js.map +1 -1
  93. package/lib/module/components/TileOverlay.js +44 -0
  94. package/lib/module/components/TileOverlay.js.map +1 -0
  95. package/lib/module/components/TileOverlay.types.js +4 -0
  96. package/lib/module/components/TileOverlay.types.js.map +1 -0
  97. package/lib/module/components/TileOverlay.web.js +69 -0
  98. package/lib/module/components/TileOverlay.web.js.map +1 -0
  99. package/lib/module/components/index.js +3 -0
  100. package/lib/module/components/index.js.map +1 -1
  101. package/lib/module/components/index.web.js +3 -0
  102. package/lib/module/components/index.web.js.map +1 -1
  103. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  104. package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
  105. package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  106. package/lib/module/fabric/LuggMapViewNativeComponent.ts +16 -5
  107. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  108. package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  109. package/lib/module/index.js +3 -0
  110. package/lib/module/index.js.map +1 -1
  111. package/lib/module/index.web.js +3 -0
  112. package/lib/module/index.web.js.map +1 -1
  113. package/lib/typescript/src/MapProvider.web.d.ts +3 -2
  114. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  115. package/lib/typescript/src/MapView.d.ts +1 -1
  116. package/lib/typescript/src/MapView.d.ts.map +1 -1
  117. package/lib/typescript/src/MapView.types.d.ts +60 -1
  118. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  119. package/lib/typescript/src/MapView.web.d.ts +1 -1
  120. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  121. package/lib/typescript/src/components/Circle.d.ts +10 -0
  122. package/lib/typescript/src/components/Circle.d.ts.map +1 -0
  123. package/lib/typescript/src/components/Circle.types.d.ts +33 -0
  124. package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
  125. package/lib/typescript/src/components/Circle.web.d.ts +3 -0
  126. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
  127. package/lib/typescript/src/components/GeoJson.d.ts +2 -1
  128. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
  129. package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
  130. package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
  131. package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
  132. package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
  133. package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
  134. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
  135. package/lib/typescript/src/components/Marker.d.ts +6 -3
  136. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  137. package/lib/typescript/src/components/Marker.types.d.ts +18 -6
  138. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  139. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  140. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  141. package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
  142. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
  143. package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
  144. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  145. package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
  146. package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
  147. package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
  148. package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
  149. package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
  150. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
  151. package/lib/typescript/src/components/index.d.ts +7 -1
  152. package/lib/typescript/src/components/index.d.ts.map +1 -1
  153. package/lib/typescript/src/components/index.web.d.ts +6 -0
  154. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  155. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +1 -1
  156. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
  157. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
  158. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
  159. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
  160. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +13 -5
  161. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  162. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  163. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  164. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
  165. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
  166. package/lib/typescript/src/index.d.ts +9 -3
  167. package/lib/typescript/src/index.d.ts.map +1 -1
  168. package/lib/typescript/src/index.web.d.ts +7 -1
  169. package/lib/typescript/src/index.web.d.ts.map +1 -1
  170. package/lib/typescript/src/types.d.ts +0 -4
  171. package/lib/typescript/src/types.d.ts.map +1 -1
  172. package/package.json +17 -11
  173. package/src/MapProvider.web.tsx +5 -3
  174. package/src/MapView.tsx +19 -1
  175. package/src/MapView.types.ts +142 -1
  176. package/src/MapView.web.tsx +379 -340
  177. package/src/components/Circle.tsx +52 -0
  178. package/src/components/Circle.types.ts +33 -0
  179. package/src/components/Circle.web.tsx +95 -0
  180. package/src/components/GeoJson.tsx +3 -3
  181. package/src/components/GroundOverlay.tsx +47 -0
  182. package/src/components/GroundOverlay.types.ts +36 -0
  183. package/src/components/GroundOverlay.web.tsx +81 -0
  184. package/src/components/Marker.tsx +32 -6
  185. package/src/components/Marker.types.ts +19 -6
  186. package/src/components/Marker.web.tsx +196 -150
  187. package/src/components/Polygon.web.tsx +93 -89
  188. package/src/components/Polyline.web.tsx +235 -235
  189. package/src/components/TileOverlay.tsx +44 -0
  190. package/src/components/TileOverlay.types.ts +37 -0
  191. package/src/components/TileOverlay.web.tsx +97 -0
  192. package/src/components/index.ts +10 -0
  193. package/src/components/index.web.ts +9 -0
  194. package/src/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  195. package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
  196. package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  197. package/src/fabric/LuggMapViewNativeComponent.ts +16 -5
  198. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  199. package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  200. package/src/index.ts +12 -1
  201. package/src/index.web.ts +15 -0
  202. package/src/types.ts +0 -5
  203. package/plugin/build/withMapsAndroid.d.ts +0 -5
  204. package/plugin/build/withMapsAndroid.js +0 -15
  205. package/plugin/build/withMapsIOS.d.ts +0 -5
  206. package/plugin/build/withMapsIOS.js +0 -27
@@ -1,8 +1,17 @@
1
1
  #import "AppleMapProvider.h"
2
+
3
+ using facebook::react::LuggMapViewInsetAdjustment;
4
+ using facebook::react::LuggMapViewMapType;
5
+ using facebook::react::LuggMapViewPoiFilterMode;
6
+ using facebook::react::LuggMapViewTheme;
7
+
2
8
  #import "../LuggCalloutView.h"
9
+ #import "../LuggCircleView.h"
10
+ #import "../LuggGroundOverlayView.h"
3
11
  #import "../LuggMarkerView.h"
4
12
  #import "../LuggPolygonView.h"
5
13
  #import "../LuggPolylineView.h"
14
+ #import "../LuggTileOverlayView.h"
6
15
  #import "../extensions/MKMapView+Zoom.h"
7
16
  #import "LuggAnnotationView.h"
8
17
  #import "MKPolylineAnimator.h"
@@ -19,12 +28,90 @@
19
28
  @implementation AppleMarkerAnnotation
20
29
  @end
21
30
 
31
+ @interface GroundImageOverlay : NSObject <MKOverlay>
32
+ @property(nonatomic, assign) CLLocationCoordinate2D coordinate;
33
+ @property(nonatomic, assign) MKMapRect boundingMapRect;
34
+ @property(nonatomic, strong) UIImage *image;
35
+ @property(nonatomic, assign) CGFloat opacity;
36
+ @end
37
+
38
+ @implementation GroundImageOverlay
39
+ @end
40
+
41
+ @interface BoundedTileOverlay : MKTileOverlay
42
+ @property(nonatomic, assign) CLLocationCoordinate2D southwest;
43
+ @property(nonatomic, assign) CLLocationCoordinate2D northeast;
44
+ @end
45
+
46
+ @implementation BoundedTileOverlay
47
+
48
+ static double tileToLat(NSInteger y, NSInteger z) {
49
+ double n = M_PI - 2.0 * M_PI * y / pow(2.0, z);
50
+ return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
51
+ }
52
+
53
+ static double tileToLng(NSInteger x, NSInteger z) {
54
+ return x / pow(2.0, z) * 360.0 - 180.0;
55
+ }
56
+
57
+ - (void)loadTileAtPath:(MKTileOverlayPath)path
58
+ result:(void (^)(NSData *_Nullable, NSError *_Nullable))result {
59
+ double tileSW_lat = tileToLat(path.y + 1, path.z);
60
+ double tileNE_lat = tileToLat(path.y, path.z);
61
+ double tileSW_lng = tileToLng(path.x, path.z);
62
+ double tileNE_lng = tileToLng(path.x + 1, path.z);
63
+
64
+ if (tileNE_lat < self.southwest.latitude ||
65
+ tileSW_lat > self.northeast.latitude ||
66
+ tileNE_lng < self.southwest.longitude ||
67
+ tileSW_lng > self.northeast.longitude) {
68
+ result(nil, nil);
69
+ return;
70
+ }
71
+
72
+ [super loadTileAtPath:path result:result];
73
+ }
74
+
75
+ @end
76
+
77
+ @interface GroundImageOverlayRenderer : MKOverlayRenderer
78
+ @property(nonatomic, strong) UIImage *image;
79
+ @property(nonatomic, assign) CGFloat overlayOpacity;
80
+ @end
81
+
82
+ @implementation GroundImageOverlayRenderer
83
+
84
+ - (void)drawMapRect:(MKMapRect)mapRect
85
+ zoomScale:(MKZoomScale)zoomScale
86
+ inContext:(CGContextRef)context {
87
+ MKMapRect overlayRect = self.overlay.boundingMapRect;
88
+ CGRect drawRect = [self rectForMapRect:overlayRect];
89
+
90
+ CGContextSaveGState(context);
91
+ CGContextSetAlpha(context, self.overlayOpacity);
92
+
93
+ // Flip the context for UIImage drawing
94
+ CGContextTranslateCTM(context, drawRect.origin.x,
95
+ drawRect.origin.y + drawRect.size.height);
96
+ CGContextScaleCTM(context, 1.0, -1.0);
97
+
98
+ CGRect imageRect =
99
+ CGRectMake(0, 0, drawRect.size.width, drawRect.size.height);
100
+ CGContextDrawImage(context, imageRect, self.image.CGImage);
101
+
102
+ CGContextRestoreGState(context);
103
+ }
104
+
105
+ @end
106
+
22
107
  @implementation LuggAppleMapViewContent
23
108
  @end
24
109
 
25
110
  @interface AppleMapProvider () <
26
111
  LuggMarkerViewDelegate, LuggCalloutViewDelegate, LuggPolylineViewDelegate,
27
- LuggPolygonViewDelegate, UIGestureRecognizerDelegate>
112
+ LuggPolygonViewDelegate, LuggCircleViewDelegate,
113
+ LuggGroundOverlayViewDelegate, LuggTileOverlayViewDelegate,
114
+ UIGestureRecognizerDelegate>
28
115
  @end
29
116
 
30
117
  @implementation AppleMapProvider {
@@ -36,24 +123,40 @@
36
123
  double _maxZoom;
37
124
  NSMapTable<id<MKOverlay>, LuggPolylineView *> *_overlayToPolylineMap;
38
125
  NSMapTable<id<MKOverlay>, LuggPolygonView *> *_overlayToPolygonMap;
126
+ NSMapTable<id<MKOverlay>, LuggCircleView *> *_overlayToCircleMap;
127
+ NSMapTable<id<MKOverlay>, LuggGroundOverlayView *>
128
+ *_overlayToGroundOverlayMap;
129
+ NSMapTable<id<MKOverlay>, LuggTileOverlayView *> *_overlayToTileOverlayMap;
39
130
  UITapGestureRecognizer *_tapGesture;
40
131
  UILongPressGestureRecognizer *_longPressGesture;
41
132
  LuggMarkerView *_activeNonBubbledMarker;
42
133
  BOOL _isReselectingAnnotation;
134
+ BOOL _isProgrammaticSelect;
43
135
  // Edge insets animation
44
136
  CADisplayLink *_edgeInsetsDisplayLink;
45
137
  UIEdgeInsets _edgeInsetsFrom;
46
138
  UIEdgeInsets _edgeInsetsTo;
139
+ UIEdgeInsets _edgeInsetsCurrent;
47
140
  CFTimeInterval _edgeInsetsAnimationStart;
48
141
  CFTimeInterval _edgeInsetsAnimationDuration;
142
+ BOOL _poiEnabled;
143
+ LuggMapViewPoiFilterMode _poiFilterMode;
144
+ NSArray<NSString *> *_poiFilterCategories;
49
145
  }
50
146
 
51
147
  @synthesize delegate = _delegate;
148
+ @synthesize staticMode = _staticMode;
52
149
 
53
150
  - (instancetype)init {
54
151
  if (self = [super init]) {
55
152
  _overlayToPolylineMap = [NSMapTable strongToWeakObjectsMapTable];
56
153
  _overlayToPolygonMap = [NSMapTable strongToWeakObjectsMapTable];
154
+ _overlayToCircleMap = [NSMapTable strongToWeakObjectsMapTable];
155
+ _overlayToGroundOverlayMap = [NSMapTable strongToWeakObjectsMapTable];
156
+ _overlayToTileOverlayMap = [NSMapTable strongToWeakObjectsMapTable];
157
+ _poiEnabled = YES;
158
+ _poiFilterMode = LuggMapViewPoiFilterMode::Including;
159
+ _poiFilterCategories = @[];
57
160
  }
58
161
  return self;
59
162
  }
@@ -80,6 +183,10 @@
80
183
  _mapView.delegate = self;
81
184
  _mapView.insetsLayoutMarginsFromSafeArea = NO;
82
185
 
186
+ if (_staticMode) {
187
+ _mapView.userInteractionEnabled = NO;
188
+ }
189
+
83
190
  [self applyZoomRange];
84
191
 
85
192
  _tapGesture =
@@ -110,6 +217,11 @@
110
217
  }
111
218
 
112
219
  - (void)destroy {
220
+ [self destroyMapView];
221
+ _isMapReady = NO;
222
+ }
223
+
224
+ - (void)destroyMapView {
113
225
  [self dismissNonBubbledCallout];
114
226
  [self stopEdgeInsetsAnimation];
115
227
  if (_tapGesture) {
@@ -120,9 +232,9 @@
120
232
  [_mapView removeGestureRecognizer:_longPressGesture];
121
233
  _longPressGesture = nil;
122
234
  }
235
+ _mapView.delegate = nil;
123
236
  [_mapView removeFromSuperview];
124
237
  _mapView = nil;
125
- _isMapReady = NO;
126
238
  }
127
239
 
128
240
  #pragma mark - Props
@@ -143,19 +255,191 @@
143
255
  _mapView.pitchEnabled = enabled;
144
256
  }
145
257
 
258
+ - (void)setCompassEnabled:(BOOL)enabled {
259
+ _mapView.showsCompass = enabled;
260
+ }
261
+
146
262
  - (void)setUserLocationEnabled:(BOOL)enabled {
147
263
  _mapView.showsUserLocation = enabled;
148
264
  }
149
265
 
150
- - (void)setTheme:(NSInteger)theme {
266
+ static MKPointOfInterestCategory poiCategoryFromString(NSString *string) {
267
+ static NSDictionary<NSString *, MKPointOfInterestCategory> *map;
268
+ static dispatch_once_t onceToken;
269
+ dispatch_once(&onceToken, ^{
270
+ NSMutableDictionary<NSString *, MKPointOfInterestCategory> *m =
271
+ [NSMutableDictionary dictionaryWithDictionary:@{
272
+ @"airport" : MKPointOfInterestCategoryAirport,
273
+ @"amusement-park" : MKPointOfInterestCategoryAmusementPark,
274
+ @"aquarium" : MKPointOfInterestCategoryAquarium,
275
+ @"atm" : MKPointOfInterestCategoryATM,
276
+ @"bakery" : MKPointOfInterestCategoryBakery,
277
+ @"bank" : MKPointOfInterestCategoryBank,
278
+ @"beach" : MKPointOfInterestCategoryBeach,
279
+ @"brewery" : MKPointOfInterestCategoryBrewery,
280
+ @"cafe" : MKPointOfInterestCategoryCafe,
281
+ @"campground" : MKPointOfInterestCategoryCampground,
282
+ @"car-rental" : MKPointOfInterestCategoryCarRental,
283
+ @"ev-charger" : MKPointOfInterestCategoryEVCharger,
284
+ @"fire-station" : MKPointOfInterestCategoryFireStation,
285
+ @"fitness-center" : MKPointOfInterestCategoryFitnessCenter,
286
+ @"food-market" : MKPointOfInterestCategoryFoodMarket,
287
+ @"gas-station" : MKPointOfInterestCategoryGasStation,
288
+ @"hospital" : MKPointOfInterestCategoryHospital,
289
+ @"hotel" : MKPointOfInterestCategoryHotel,
290
+ @"laundry" : MKPointOfInterestCategoryLaundry,
291
+ @"library" : MKPointOfInterestCategoryLibrary,
292
+ @"marina" : MKPointOfInterestCategoryMarina,
293
+ @"movie-theater" : MKPointOfInterestCategoryMovieTheater,
294
+ @"museum" : MKPointOfInterestCategoryMuseum,
295
+ @"national-park" : MKPointOfInterestCategoryNationalPark,
296
+ @"nightlife" : MKPointOfInterestCategoryNightlife,
297
+ @"park" : MKPointOfInterestCategoryPark,
298
+ @"parking" : MKPointOfInterestCategoryParking,
299
+ @"pharmacy" : MKPointOfInterestCategoryPharmacy,
300
+ @"police" : MKPointOfInterestCategoryPolice,
301
+ @"post-office" : MKPointOfInterestCategoryPostOffice,
302
+ @"public-transport" : MKPointOfInterestCategoryPublicTransport,
303
+ @"restaurant" : MKPointOfInterestCategoryRestaurant,
304
+ @"restroom" : MKPointOfInterestCategoryRestroom,
305
+ @"school" : MKPointOfInterestCategorySchool,
306
+ @"stadium" : MKPointOfInterestCategoryStadium,
307
+ @"store" : MKPointOfInterestCategoryStore,
308
+ @"theater" : MKPointOfInterestCategoryTheater,
309
+ @"university" : MKPointOfInterestCategoryUniversity,
310
+ @"winery" : MKPointOfInterestCategoryWinery,
311
+ @"zoo" : MKPointOfInterestCategoryZoo,
312
+ }];
313
+ if (@available(iOS 18.0, *)) {
314
+ [m addEntriesFromDictionary:@{
315
+ @"animal-service" : MKPointOfInterestCategoryAnimalService,
316
+ @"automotive-repair" : MKPointOfInterestCategoryAutomotiveRepair,
317
+ @"baseball" : MKPointOfInterestCategoryBaseball,
318
+ @"basketball" : MKPointOfInterestCategoryBasketball,
319
+ @"beauty" : MKPointOfInterestCategoryBeauty,
320
+ @"bowling" : MKPointOfInterestCategoryBowling,
321
+ @"castle" : MKPointOfInterestCategoryCastle,
322
+ @"convention-center" : MKPointOfInterestCategoryConventionCenter,
323
+ @"distillery" : MKPointOfInterestCategoryDistillery,
324
+ @"fairground" : MKPointOfInterestCategoryFairground,
325
+ @"fishing" : MKPointOfInterestCategoryFishing,
326
+ @"fortress" : MKPointOfInterestCategoryFortress,
327
+ @"go-kart" : MKPointOfInterestCategoryGoKart,
328
+ @"golf" : MKPointOfInterestCategoryGolf,
329
+ @"hiking" : MKPointOfInterestCategoryHiking,
330
+ @"kayaking" : MKPointOfInterestCategoryKayaking,
331
+ @"landmark" : MKPointOfInterestCategoryLandmark,
332
+ @"mailbox" : MKPointOfInterestCategoryMailbox,
333
+ @"mini-golf" : MKPointOfInterestCategoryMiniGolf,
334
+ @"music-venue" : MKPointOfInterestCategoryMusicVenue,
335
+ @"national-monument" : MKPointOfInterestCategoryNationalMonument,
336
+ @"planetarium" : MKPointOfInterestCategoryPlanetarium,
337
+ @"rock-climbing" : MKPointOfInterestCategoryRockClimbing,
338
+ @"rv-park" : MKPointOfInterestCategoryRVPark,
339
+ @"skate-park" : MKPointOfInterestCategorySkatePark,
340
+ @"skating" : MKPointOfInterestCategorySkating,
341
+ @"skiing" : MKPointOfInterestCategorySkiing,
342
+ @"soccer" : MKPointOfInterestCategorySoccer,
343
+ @"spa" : MKPointOfInterestCategorySpa,
344
+ @"surfing" : MKPointOfInterestCategorySurfing,
345
+ @"swimming" : MKPointOfInterestCategorySwimming,
346
+ @"tennis" : MKPointOfInterestCategoryTennis,
347
+ @"volleyball" : MKPointOfInterestCategoryVolleyball,
348
+ }];
349
+ }
350
+ map = [m copy];
351
+ });
352
+ return map[string];
353
+ }
354
+
355
+ MKPointOfInterestFilter *_Nullable LuggPointOfInterestFilter(
356
+ BOOL poiEnabled, LuggMapViewPoiFilterMode filterMode,
357
+ NSArray<NSString *> *filterCategories) {
358
+ if (!poiEnabled) {
359
+ return MKPointOfInterestFilter.filterExcludingAllCategories;
360
+ }
361
+ if (filterCategories.count > 0) {
362
+ NSMutableArray<MKPointOfInterestCategory> *categories =
363
+ [NSMutableArray array];
364
+ for (NSString *name in filterCategories) {
365
+ MKPointOfInterestCategory category = poiCategoryFromString(name);
366
+ if (category) {
367
+ [categories addObject:category];
368
+ }
369
+ }
370
+ if (categories.count > 0) {
371
+ if (filterMode == LuggMapViewPoiFilterMode::Excluding) {
372
+ return [[MKPointOfInterestFilter alloc]
373
+ initExcludingCategories:categories];
374
+ }
375
+ return
376
+ [[MKPointOfInterestFilter alloc] initIncludingCategories:categories];
377
+ }
378
+ }
379
+ return nil;
380
+ }
381
+
382
+ - (void)applyPoiFilter {
383
+ _mapView.pointOfInterestFilter = LuggPointOfInterestFilter(
384
+ _poiEnabled, _poiFilterMode, _poiFilterCategories);
385
+ }
386
+
387
+ - (void)setInsetAdjustment:(LuggMapViewInsetAdjustment)insetAdjustment {
388
+ BOOL automatic = (insetAdjustment == LuggMapViewInsetAdjustment::Automatic);
389
+ if (_mapView.insetsLayoutMarginsFromSafeArea == automatic)
390
+ return;
391
+
392
+ CLLocationCoordinate2D center = _mapView.centerCoordinate;
393
+ _mapView.insetsLayoutMarginsFromSafeArea = automatic;
394
+
395
+ dispatch_async(dispatch_get_main_queue(), ^{
396
+ [self->_mapView setCenterCoordinate:center animated:NO];
397
+ });
398
+ }
399
+
400
+ - (void)setPoiEnabled:(BOOL)enabled {
401
+ _poiEnabled = enabled;
402
+ [self applyPoiFilter];
403
+ }
404
+
405
+ - (void)setPoiFilterMode:(LuggMapViewPoiFilterMode)mode {
406
+ _poiFilterMode = mode;
407
+ [self applyPoiFilter];
408
+ }
409
+
410
+ - (void)setPoiFilterCategories:(NSArray<NSString *> *)categories {
411
+ _poiFilterCategories = categories;
412
+ [self applyPoiFilter];
413
+ }
414
+
415
+ MKMapType LuggMKMapTypeFromMapType(LuggMapViewMapType mapType) {
416
+ switch (mapType) {
417
+ case LuggMapViewMapType::Satellite:
418
+ return MKMapTypeSatellite;
419
+ case LuggMapViewMapType::Terrain:
420
+ return MKMapTypeStandard;
421
+ case LuggMapViewMapType::Hybrid:
422
+ return MKMapTypeHybrid;
423
+ case LuggMapViewMapType::MutedStandard:
424
+ return MKMapTypeMutedStandard;
425
+ default:
426
+ return MKMapTypeStandard;
427
+ }
428
+ }
429
+
430
+ - (void)setMapType:(LuggMapViewMapType)mapType {
431
+ _mapView.mapType = LuggMKMapTypeFromMapType(mapType);
432
+ }
433
+
434
+ - (void)setTheme:(LuggMapViewTheme)theme {
151
435
  switch (theme) {
152
- case 1: // Dark
436
+ case LuggMapViewTheme::Dark:
153
437
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
154
438
  break;
155
- case 0: // Light
439
+ case LuggMapViewTheme::Light:
156
440
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
157
441
  break;
158
- default: // System
442
+ default:
159
443
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
160
444
  break;
161
445
  }
@@ -182,6 +466,7 @@
182
466
  CGFloat deltaY = newOffsetY - oldOffsetY;
183
467
 
184
468
  _mapView.layoutMargins = edgeInsets;
469
+ _edgeInsetsCurrent = edgeInsets;
185
470
 
186
471
  if (deltaX != 0 || deltaY != 0) {
187
472
  CLLocationCoordinate2D currentCenter = _mapView.centerCoordinate;
@@ -232,7 +517,7 @@
232
517
  _edgeInsetsFrom.right +
233
518
  (_edgeInsetsTo.right - _edgeInsetsFrom.right) * t);
234
519
 
235
- [self setEdgeInsets:current oldEdgeInsets:_mapView.layoutMargins];
520
+ [self setEdgeInsets:current oldEdgeInsets:_edgeInsetsCurrent];
236
521
 
237
522
  if (progress >= 1.0) {
238
523
  [self stopEdgeInsetsAnimation];
@@ -315,6 +600,55 @@
315
600
  return nil;
316
601
  }
317
602
 
603
+ - (LuggCircleView *)hitTestCircleAtPoint:(CGPoint)point {
604
+ CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point
605
+ toCoordinateFromView:_mapView];
606
+ CLLocation *tapLocation =
607
+ [[CLLocation alloc] initWithLatitude:tapCoordinate.latitude
608
+ longitude:tapCoordinate.longitude];
609
+
610
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
611
+ for (NSInteger i = overlays.count - 1; i >= 0; i--) {
612
+ id<MKOverlay> overlay = overlays[i];
613
+ if (![overlay isKindOfClass:[MKCircle class]])
614
+ continue;
615
+
616
+ LuggCircleView *circleView = [_overlayToCircleMap objectForKey:overlay];
617
+ if (!circleView || !circleView.tappable)
618
+ continue;
619
+
620
+ CLLocation *center =
621
+ [[CLLocation alloc] initWithLatitude:circleView.center.latitude
622
+ longitude:circleView.center.longitude];
623
+ CLLocationDistance distance = [tapLocation distanceFromLocation:center];
624
+ if (distance <= circleView.radius)
625
+ return circleView;
626
+ }
627
+ return nil;
628
+ }
629
+
630
+ - (LuggGroundOverlayView *)hitTestGroundOverlayAtPoint:(CGPoint)point {
631
+ CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point
632
+ toCoordinateFromView:_mapView];
633
+
634
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
635
+ for (NSInteger i = overlays.count - 1; i >= 0; i--) {
636
+ id<MKOverlay> overlay = overlays[i];
637
+ if (![overlay isKindOfClass:[GroundImageOverlay class]])
638
+ continue;
639
+
640
+ LuggGroundOverlayView *view =
641
+ [_overlayToGroundOverlayMap objectForKey:overlay];
642
+ if (!view || !view.tappable)
643
+ continue;
644
+
645
+ MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoordinate);
646
+ if (MKMapRectContainsPoint(overlay.boundingMapRect, mapPoint))
647
+ return view;
648
+ }
649
+ return nil;
650
+ }
651
+
318
652
  - (BOOL)hitTestAnnotationAtPoint:(CGPoint)point {
319
653
  for (id<MKAnnotation> annotation in _mapView.annotations) {
320
654
  MKAnnotationView *view = [_mapView viewForAnnotation:annotation];
@@ -349,6 +683,19 @@
349
683
  if ([self hitTestAnnotationAtPoint:point])
350
684
  return;
351
685
 
686
+ LuggGroundOverlayView *groundOverlayView =
687
+ [self hitTestGroundOverlayAtPoint:point];
688
+ if (groundOverlayView) {
689
+ [groundOverlayView emitPressEvent];
690
+ return;
691
+ }
692
+
693
+ LuggCircleView *circleView = [self hitTestCircleAtPoint:point];
694
+ if (circleView) {
695
+ [circleView emitPressEvent];
696
+ return;
697
+ }
698
+
352
699
  LuggPolygonView *polygonView = [self hitTestPolygonAtPoint:point];
353
700
  if (polygonView) {
354
701
  [polygonView emitPressEvent];
@@ -501,7 +848,8 @@
501
848
  renderer.strokeColors = colors;
502
849
  }
503
850
  renderer.animatedOptions = polylineView.animatedOptions;
504
- renderer.animated = polylineView.animated;
851
+ // Static maps snapshot an arbitrary frame; render the full polyline
852
+ renderer.animated = polylineView.animated && !_staticMode;
505
853
  polylineView.renderer = renderer;
506
854
  return renderer;
507
855
  }
@@ -526,6 +874,37 @@
526
874
  return renderer;
527
875
  }
528
876
 
877
+ if ([overlay isKindOfClass:[MKCircle class]]) {
878
+ LuggCircleView *circleView = [_overlayToCircleMap objectForKey:overlay];
879
+ MKCircle *circle = (MKCircle *)overlay;
880
+
881
+ MKCircleRenderer *renderer =
882
+ [[MKCircleRenderer alloc] initWithCircle:circle];
883
+ if (circleView) {
884
+ renderer.fillColor = circleView.fillColor;
885
+ renderer.strokeColor = circleView.strokeColor;
886
+ renderer.lineWidth = circleView.strokeWidth;
887
+ circleView.renderer = renderer;
888
+ }
889
+ return renderer;
890
+ }
891
+
892
+ if ([overlay isKindOfClass:[GroundImageOverlay class]]) {
893
+ GroundImageOverlay *groundOverlay = (GroundImageOverlay *)overlay;
894
+ GroundImageOverlayRenderer *renderer =
895
+ [[GroundImageOverlayRenderer alloc] initWithOverlay:overlay];
896
+ renderer.image = groundOverlay.image;
897
+ renderer.overlayOpacity = groundOverlay.opacity;
898
+ return renderer;
899
+ }
900
+
901
+ if ([overlay isKindOfClass:[MKTileOverlay class]]) {
902
+ MKTileOverlay *tileOverlay = (MKTileOverlay *)overlay;
903
+ MKTileOverlayRenderer *renderer =
904
+ [[MKTileOverlayRenderer alloc] initWithTileOverlay:tileOverlay];
905
+ return renderer;
906
+ }
907
+
529
908
  return nil;
530
909
  }
531
910
 
@@ -538,10 +917,6 @@
538
917
  LuggMarkerView *markerView = annotation.markerView;
539
918
 
540
919
  if (markerView) {
541
- CGPoint point = [_mapView convertCoordinate:markerView.coordinate
542
- toPointToView:_mapView];
543
- [markerView emitPressEventWithPoint:point];
544
-
545
920
  LuggCalloutView *calloutView = markerView.calloutView;
546
921
  if (calloutView && !calloutView.bubbled && calloutView.hasCustomContent) {
547
922
  [self showNonBubbledCallout:markerView];
@@ -587,10 +962,7 @@
587
962
  }
588
963
 
589
964
  _activeNonBubbledMarker = markerView;
590
- dispatch_async(dispatch_get_main_queue(), ^{
591
- [self positionNonBubbledCallout];
592
- contentView.hidden = NO;
593
- });
965
+ [self positionNonBubbledCallout];
594
966
  }
595
967
 
596
968
  - (void)calloutViewDidUpdate:(LuggCalloutView *)calloutView {
@@ -607,26 +979,26 @@
607
979
  if (contentSize.width <= 0 || contentSize.height <= 0)
608
980
  return;
609
981
 
610
- CGPoint anchor = calloutView.anchor;
982
+ CGPoint offset = calloutView.offset;
611
983
 
612
984
  AppleMarkerAnnotation *annotation =
613
985
  (AppleMarkerAnnotation *)_activeNonBubbledMarker.marker;
614
986
  MKAnnotationView *annotationView = annotation.annotationView;
615
987
 
616
988
  if (annotationView && contentView.superview == annotationView) {
617
- CGPoint center = CGPointMake(annotationView.bounds.size.width / 2.0 +
618
- contentSize.width * (0.5 - anchor.x),
619
- annotationView.bounds.size.height / 2.0 +
620
- contentSize.height * (0.5 - anchor.y));
989
+ CGPoint center =
990
+ CGPointMake(annotationView.bounds.size.width / 2.0 + offset.x,
991
+ -contentSize.height / 2.0 + offset.y);
621
992
  contentView.center = center;
622
993
  } else {
623
994
  CGPoint point =
624
995
  [_mapView convertCoordinate:_activeNonBubbledMarker.coordinate
625
996
  toPointToView:_wrapperView];
626
- contentView.center =
627
- CGPointMake(point.x + contentSize.width * (0.5 - anchor.x),
628
- point.y + contentSize.height * (0.5 - anchor.y));
997
+ contentView.center = CGPointMake(
998
+ point.x + offset.x, point.y - contentSize.height / 2.0 + offset.y);
629
999
  }
1000
+
1001
+ contentView.hidden = NO;
630
1002
  }
631
1003
 
632
1004
  - (void)dismissNonBubbledCallout {
@@ -697,7 +1069,17 @@
697
1069
  if ([view.annotation isKindOfClass:[AppleMarkerAnnotation class]]) {
698
1070
  AppleMarkerAnnotation *annotation =
699
1071
  (AppleMarkerAnnotation *)view.annotation;
700
- [_mapView setCenterCoordinate:annotation.coordinate animated:YES];
1072
+ LuggMarkerView *markerView = annotation.markerView;
1073
+
1074
+ if (markerView) {
1075
+ CGPoint point = [_mapView convertCoordinate:markerView.coordinate
1076
+ toPointToView:_mapView];
1077
+ [markerView emitPressEventWithPoint:point];
1078
+
1079
+ if (markerView.centerOnPress) {
1080
+ [_mapView setCenterCoordinate:annotation.coordinate animated:YES];
1081
+ }
1082
+ }
701
1083
  }
702
1084
  }
703
1085
 
@@ -722,6 +1104,42 @@
722
1104
  calloutView.hasCustomContent ? calloutView.contentView : nil;
723
1105
  }
724
1106
 
1107
+ - (void)showCalloutForMarkerView:(LuggMarkerView *)markerView {
1108
+ LuggCalloutView *calloutView = markerView.calloutView;
1109
+ _isProgrammaticSelect = YES;
1110
+
1111
+ if (calloutView && calloutView.hasCustomContent) {
1112
+ [self dismissNonBubbledCallout];
1113
+ AppleMarkerAnnotation *annotation =
1114
+ (AppleMarkerAnnotation *)markerView.marker;
1115
+ if (!calloutView.bubbled) {
1116
+ [self showNonBubbledCallout:markerView];
1117
+ } else if (annotation) {
1118
+ [_mapView selectAnnotation:annotation animated:YES];
1119
+ }
1120
+ _isProgrammaticSelect = NO;
1121
+ return;
1122
+ }
1123
+
1124
+ if (markerView.title.length > 0) {
1125
+ AppleMarkerAnnotation *annotation =
1126
+ (AppleMarkerAnnotation *)markerView.marker;
1127
+ if (annotation) {
1128
+ [_mapView selectAnnotation:annotation animated:YES];
1129
+ }
1130
+ }
1131
+ _isProgrammaticSelect = NO;
1132
+ }
1133
+
1134
+ - (void)hideCalloutForMarkerView:(LuggMarkerView *)markerView {
1135
+ [self dismissNonBubbledCallout];
1136
+ AppleMarkerAnnotation *annotation =
1137
+ (AppleMarkerAnnotation *)markerView.marker;
1138
+ if (annotation) {
1139
+ [_mapView deselectAnnotation:annotation animated:YES];
1140
+ }
1141
+ }
1142
+
725
1143
  #pragma mark - MarkerViewDelegate
726
1144
 
727
1145
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
@@ -766,6 +1184,24 @@
766
1184
  [self syncPolygonView:polygonView];
767
1185
  }
768
1186
 
1187
+ #pragma mark - CircleViewDelegate
1188
+
1189
+ - (void)circleViewDidUpdate:(LuggCircleView *)circleView {
1190
+ [self syncCircleView:circleView];
1191
+ }
1192
+
1193
+ #pragma mark - GroundOverlayViewDelegate
1194
+
1195
+ - (void)groundOverlayViewDidUpdate:(LuggGroundOverlayView *)groundOverlayView {
1196
+ [self syncGroundOverlayView:groundOverlayView];
1197
+ }
1198
+
1199
+ #pragma mark - TileOverlayViewDelegate
1200
+
1201
+ - (void)tileOverlayViewDidUpdate:(LuggTileOverlayView *)tileOverlayView {
1202
+ [self syncTileOverlayView:tileOverlayView];
1203
+ }
1204
+
769
1205
  #pragma mark - Marker Management
770
1206
 
771
1207
  - (void)addMarkerView:(LuggMarkerView *)markerView {
@@ -942,7 +1378,7 @@
942
1378
  renderer.strokeColors =
943
1379
  polylineView.strokeColors.count > 1 ? polylineView.strokeColors : nil;
944
1380
  renderer.animatedOptions = polylineView.animatedOptions;
945
- renderer.animated = polylineView.animated;
1381
+ renderer.animated = polylineView.animated && !_staticMode;
946
1382
  return;
947
1383
  }
948
1384
 
@@ -1089,6 +1525,196 @@
1089
1525
  return [interiorPolygons copy];
1090
1526
  }
1091
1527
 
1528
+ #pragma mark - Circle Management
1529
+
1530
+ - (void)addCircleView:(LuggCircleView *)circleView {
1531
+ circleView.delegate = self;
1532
+ [self addCircleOverlayToMap:circleView];
1533
+ }
1534
+
1535
+ - (void)removeCircleView:(LuggCircleView *)circleView {
1536
+ circleView.delegate = nil;
1537
+ MKCircle *circle = (MKCircle *)circleView.circle;
1538
+ if (circle) {
1539
+ [_overlayToCircleMap removeObjectForKey:circle];
1540
+ [_mapView removeOverlay:circle];
1541
+ circleView.circle = nil;
1542
+ }
1543
+ }
1544
+
1545
+ - (void)syncCircleView:(LuggCircleView *)circleView {
1546
+ if (!_mapView)
1547
+ return;
1548
+
1549
+ MKCircle *oldCircle = (MKCircle *)circleView.circle;
1550
+
1551
+ MKCircle *newCircle = [MKCircle circleWithCenterCoordinate:circleView.center
1552
+ radius:circleView.radius];
1553
+
1554
+ circleView.circle = newCircle;
1555
+ [_overlayToCircleMap setObject:circleView forKey:newCircle];
1556
+
1557
+ MKCircleRenderer *renderer = (MKCircleRenderer *)circleView.renderer;
1558
+ if (renderer && oldCircle) {
1559
+ [_overlayToCircleMap removeObjectForKey:oldCircle];
1560
+ [_mapView removeOverlay:oldCircle];
1561
+ [self insertOverlay:newCircle withZIndex:circleView.zIndex];
1562
+ circleView.renderer = nil;
1563
+ return;
1564
+ }
1565
+
1566
+ if (oldCircle) {
1567
+ [_overlayToCircleMap removeObjectForKey:oldCircle];
1568
+ [_mapView removeOverlay:oldCircle];
1569
+ }
1570
+ [self insertOverlay:newCircle withZIndex:circleView.zIndex];
1571
+ }
1572
+
1573
+ - (void)addCircleOverlayToMap:(LuggCircleView *)circleView {
1574
+ if (!_mapView)
1575
+ return;
1576
+
1577
+ MKCircle *circle = [MKCircle circleWithCenterCoordinate:circleView.center
1578
+ radius:circleView.radius];
1579
+
1580
+ circleView.circle = circle;
1581
+ [_overlayToCircleMap setObject:circleView forKey:circle];
1582
+ [self insertOverlay:circle withZIndex:circleView.zIndex];
1583
+ }
1584
+
1585
+ #pragma mark - Ground Overlay Management
1586
+
1587
+ - (void)addGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1588
+ groundOverlayView.delegate = self;
1589
+ [self syncGroundOverlayView:groundOverlayView];
1590
+ }
1591
+
1592
+ - (void)removeGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1593
+ groundOverlayView.delegate = nil;
1594
+ GroundImageOverlay *overlay = (GroundImageOverlay *)groundOverlayView.overlay;
1595
+ if (overlay) {
1596
+ [_overlayToGroundOverlayMap removeObjectForKey:overlay];
1597
+ [_mapView removeOverlay:overlay];
1598
+ groundOverlayView.overlay = nil;
1599
+ }
1600
+ }
1601
+
1602
+ - (void)syncGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1603
+ if (!_mapView)
1604
+ return;
1605
+
1606
+ NSString *imageUri = groundOverlayView.imageUri;
1607
+ if (imageUri.length == 0)
1608
+ return;
1609
+
1610
+ // Remove old overlay
1611
+ GroundImageOverlay *oldOverlay =
1612
+ (GroundImageOverlay *)groundOverlayView.overlay;
1613
+ if (oldOverlay) {
1614
+ [_overlayToGroundOverlayMap removeObjectForKey:oldOverlay];
1615
+ [_mapView removeOverlay:oldOverlay];
1616
+ groundOverlayView.overlay = nil;
1617
+ }
1618
+
1619
+ NSURL *url = [NSURL URLWithString:imageUri];
1620
+ if (!url)
1621
+ return;
1622
+
1623
+ __weak __typeof(self) weakSelf = self;
1624
+ __weak LuggGroundOverlayView *weakOverlayView = groundOverlayView;
1625
+ dispatch_async(
1626
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1627
+ NSData *data = [NSData dataWithContentsOfURL:url];
1628
+ UIImage *image = data ? [UIImage imageWithData:data] : nil;
1629
+ dispatch_async(dispatch_get_main_queue(), ^{
1630
+ [weakSelf addGroundOverlayToMap:weakOverlayView image:image];
1631
+ });
1632
+ });
1633
+ }
1634
+
1635
+ - (void)addGroundOverlayToMap:(LuggGroundOverlayView *)groundOverlayView
1636
+ image:(UIImage *)image {
1637
+ if (!_mapView || !groundOverlayView || !image)
1638
+ return;
1639
+
1640
+ MKMapPoint sw = MKMapPointForCoordinate(groundOverlayView.southwest);
1641
+ MKMapPoint ne = MKMapPointForCoordinate(groundOverlayView.northeast);
1642
+
1643
+ MKMapRect mapRect = MKMapRectMake(MIN(sw.x, ne.x), MIN(sw.y, ne.y),
1644
+ fabs(ne.x - sw.x), fabs(ne.y - sw.y));
1645
+
1646
+ CLLocationCoordinate2D center =
1647
+ CLLocationCoordinate2DMake((groundOverlayView.northeast.latitude +
1648
+ groundOverlayView.southwest.latitude) /
1649
+ 2.0,
1650
+ (groundOverlayView.northeast.longitude +
1651
+ groundOverlayView.southwest.longitude) /
1652
+ 2.0);
1653
+
1654
+ GroundImageOverlay *overlay = [[GroundImageOverlay alloc] init];
1655
+ overlay.coordinate = center;
1656
+ overlay.boundingMapRect = mapRect;
1657
+ overlay.image = image;
1658
+ overlay.opacity = groundOverlayView.opacity;
1659
+
1660
+ groundOverlayView.overlay = overlay;
1661
+ [_overlayToGroundOverlayMap setObject:groundOverlayView forKey:overlay];
1662
+ [self insertOverlay:overlay withZIndex:groundOverlayView.zIndex];
1663
+ }
1664
+
1665
+ #pragma mark - Tile Overlay Management
1666
+
1667
+ - (void)addTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1668
+ tileOverlayView.delegate = self;
1669
+ [self syncTileOverlayView:tileOverlayView];
1670
+ }
1671
+
1672
+ - (void)removeTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1673
+ tileOverlayView.delegate = nil;
1674
+ MKTileOverlay *overlay = (MKTileOverlay *)tileOverlayView.overlay;
1675
+ if (overlay) {
1676
+ [_overlayToTileOverlayMap removeObjectForKey:overlay];
1677
+ [_mapView removeOverlay:overlay];
1678
+ tileOverlayView.overlay = nil;
1679
+ }
1680
+ }
1681
+
1682
+ - (void)syncTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1683
+ if (!_mapView)
1684
+ return;
1685
+
1686
+ NSString *urlTemplate = tileOverlayView.urlTemplate;
1687
+ if (urlTemplate.length == 0)
1688
+ return;
1689
+
1690
+ // Remove old overlay
1691
+ MKTileOverlay *oldOverlay = (MKTileOverlay *)tileOverlayView.overlay;
1692
+ if (oldOverlay) {
1693
+ [_overlayToTileOverlayMap removeObjectForKey:oldOverlay];
1694
+ [_mapView removeOverlay:oldOverlay];
1695
+ tileOverlayView.overlay = nil;
1696
+ }
1697
+
1698
+ MKTileOverlay *tileOverlay;
1699
+ if (tileOverlayView.hasBounds) {
1700
+ BoundedTileOverlay *bounded =
1701
+ [[BoundedTileOverlay alloc] initWithURLTemplate:urlTemplate];
1702
+ bounded.southwest = tileOverlayView.southwest;
1703
+ bounded.northeast = tileOverlayView.northeast;
1704
+ tileOverlay = bounded;
1705
+ } else {
1706
+ tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
1707
+ }
1708
+
1709
+ tileOverlay.tileSize =
1710
+ CGSizeMake(tileOverlayView.tileSize, tileOverlayView.tileSize);
1711
+ tileOverlay.canReplaceMapContent = NO;
1712
+
1713
+ tileOverlayView.overlay = tileOverlay;
1714
+ [_overlayToTileOverlayMap setObject:tileOverlayView forKey:tileOverlay];
1715
+ [_mapView addOverlay:tileOverlay level:MKOverlayLevelAboveRoads];
1716
+ }
1717
+
1092
1718
  - (void)insertOverlay:(id<MKOverlay>)overlay withZIndex:(NSInteger)zIndex {
1093
1719
  if (zIndex == 0) {
1094
1720
  [_mapView addOverlay:overlay];
@@ -1104,10 +1730,18 @@
1104
1730
  [_overlayToPolylineMap objectForKey:overlays[i]];
1105
1731
  LuggPolygonView *existingPolygonView =
1106
1732
  [_overlayToPolygonMap objectForKey:overlays[i]];
1733
+ LuggCircleView *existingCircleView =
1734
+ [_overlayToCircleMap objectForKey:overlays[i]];
1735
+ LuggGroundOverlayView *existingGroundOverlayView =
1736
+ [_overlayToGroundOverlayMap objectForKey:overlays[i]];
1107
1737
  if (existingPolylineView) {
1108
1738
  existingZIndex = existingPolylineView.zIndex;
1109
1739
  } else if (existingPolygonView) {
1110
1740
  existingZIndex = existingPolygonView.zIndex;
1741
+ } else if (existingCircleView) {
1742
+ existingZIndex = existingCircleView.zIndex;
1743
+ } else if (existingGroundOverlayView) {
1744
+ existingZIndex = existingGroundOverlayView.zIndex;
1111
1745
  } else {
1112
1746
  continue;
1113
1747
  }
@@ -1157,11 +1791,11 @@
1157
1791
  } else if (duration > 0) {
1158
1792
  CLLocationCoordinate2D center =
1159
1793
  CLLocationCoordinate2DMake(latitude, longitude);
1160
- MKCoordinateRegion region = [_mapView regionForCenterCoordinate:center
1161
- zoomLevel:targetZoom];
1794
+ MKMapCamera *camera = [_mapView cameraForCenterCoordinate:center
1795
+ zoomLevel:targetZoom];
1162
1796
  [UIView animateWithDuration:duration / 1000.0
1163
1797
  animations:^{
1164
- [self->_mapView setRegion:region animated:NO];
1798
+ [self->_mapView setCamera:camera animated:NO];
1165
1799
  }];
1166
1800
  } else {
1167
1801
  [_mapView