@lugg/maps 1.0.0-beta.1 → 1.0.0-beta.11

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 (178) 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 +49 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +31 -0
  10. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +28 -0
  11. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  12. package/android/src/main/java/com/luggmaps/LuggPackage.kt +4 -1
  13. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +1 -22
  14. package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
  15. package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
  16. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +451 -17
  17. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +14 -0
  18. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +27 -9
  19. package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
  20. package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
  21. package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
  22. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  23. package/ios/LuggCalloutView.h +1 -1
  24. package/ios/LuggCalloutView.mm +21 -11
  25. package/ios/LuggCircleView.h +31 -0
  26. package/ios/LuggCircleView.mm +133 -0
  27. package/ios/LuggGroundOverlayView.h +30 -0
  28. package/ios/LuggGroundOverlayView.mm +125 -0
  29. package/ios/LuggMapView.mm +84 -2
  30. package/ios/LuggMarkerView.h +3 -0
  31. package/ios/LuggMarkerView.mm +23 -0
  32. package/ios/LuggTileOverlayView.h +31 -0
  33. package/ios/LuggTileOverlayView.mm +130 -0
  34. package/ios/core/AppleMapProvider.mm +697 -37
  35. package/ios/core/GMSPolylineAnimator.m +6 -3
  36. package/ios/core/GoogleMapProvider.mm +400 -22
  37. package/ios/core/MapProviderDelegate.h +20 -1
  38. package/ios/events/CirclePressEvent.h +20 -0
  39. package/ios/events/GroundOverlayPressEvent.h +20 -0
  40. package/ios/events/TileOverlayPressEvent.h +20 -0
  41. package/lib/module/MapProvider.web.js +4 -1
  42. package/lib/module/MapProvider.web.js.map +1 -1
  43. package/lib/module/MapView.js +12 -1
  44. package/lib/module/MapView.js.map +1 -1
  45. package/lib/module/MapView.web.js +21 -18
  46. package/lib/module/MapView.web.js.map +1 -1
  47. package/lib/module/components/Circle.js +46 -0
  48. package/lib/module/components/Circle.js.map +1 -0
  49. package/lib/module/components/Circle.types.js +4 -0
  50. package/lib/module/components/Circle.types.js.map +1 -0
  51. package/lib/module/components/Circle.web.js +79 -0
  52. package/lib/module/components/Circle.web.js.map +1 -0
  53. package/lib/module/components/GeoJson.js +3 -3
  54. package/lib/module/components/GeoJson.js.map +1 -1
  55. package/lib/module/components/GroundOverlay.js +45 -0
  56. package/lib/module/components/GroundOverlay.js.map +1 -0
  57. package/lib/module/components/GroundOverlay.types.js +4 -0
  58. package/lib/module/components/GroundOverlay.types.js.map +1 -0
  59. package/lib/module/components/GroundOverlay.web.js +77 -0
  60. package/lib/module/components/GroundOverlay.web.js.map +1 -0
  61. package/lib/module/components/Marker.js +22 -4
  62. package/lib/module/components/Marker.js.map +1 -1
  63. package/lib/module/components/Marker.web.js +23 -10
  64. package/lib/module/components/Marker.web.js.map +1 -1
  65. package/lib/module/components/Polygon.web.js +3 -3
  66. package/lib/module/components/Polygon.web.js.map +1 -1
  67. package/lib/module/components/Polyline.web.js +6 -12
  68. package/lib/module/components/Polyline.web.js.map +1 -1
  69. package/lib/module/components/TileOverlay.js +44 -0
  70. package/lib/module/components/TileOverlay.js.map +1 -0
  71. package/lib/module/components/TileOverlay.types.js +4 -0
  72. package/lib/module/components/TileOverlay.types.js.map +1 -0
  73. package/lib/module/components/TileOverlay.web.js +69 -0
  74. package/lib/module/components/TileOverlay.web.js.map +1 -0
  75. package/lib/module/components/index.js +3 -0
  76. package/lib/module/components/index.js.map +1 -1
  77. package/lib/module/components/index.web.js +3 -0
  78. package/lib/module/components/index.web.js.map +1 -1
  79. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  80. package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
  81. package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  82. package/lib/module/fabric/LuggMapViewNativeComponent.ts +13 -5
  83. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  84. package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  85. package/lib/module/index.js +3 -0
  86. package/lib/module/index.js.map +1 -1
  87. package/lib/module/index.web.js +3 -0
  88. package/lib/module/index.web.js.map +1 -1
  89. package/lib/typescript/src/MapProvider.web.d.ts +3 -2
  90. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  91. package/lib/typescript/src/MapView.d.ts +1 -1
  92. package/lib/typescript/src/MapView.d.ts.map +1 -1
  93. package/lib/typescript/src/MapView.types.d.ts +36 -1
  94. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  95. package/lib/typescript/src/MapView.web.d.ts +1 -1
  96. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  97. package/lib/typescript/src/components/Circle.d.ts +10 -0
  98. package/lib/typescript/src/components/Circle.d.ts.map +1 -0
  99. package/lib/typescript/src/components/Circle.types.d.ts +33 -0
  100. package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
  101. package/lib/typescript/src/components/Circle.web.d.ts +3 -0
  102. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
  103. package/lib/typescript/src/components/GeoJson.d.ts +2 -1
  104. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
  105. package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
  106. package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
  107. package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
  108. package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
  109. package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
  110. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
  111. package/lib/typescript/src/components/Marker.d.ts +6 -3
  112. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  113. package/lib/typescript/src/components/Marker.types.d.ts +18 -6
  114. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  115. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  116. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  117. package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
  118. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
  119. package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
  120. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  121. package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
  122. package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
  123. package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
  124. package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
  125. package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
  126. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
  127. package/lib/typescript/src/components/index.d.ts +7 -1
  128. package/lib/typescript/src/components/index.d.ts.map +1 -1
  129. package/lib/typescript/src/components/index.web.d.ts +6 -0
  130. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  131. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +1 -1
  132. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
  133. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
  134. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
  135. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
  136. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +10 -5
  137. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  138. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  139. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  140. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
  141. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
  142. package/lib/typescript/src/index.d.ts +9 -3
  143. package/lib/typescript/src/index.d.ts.map +1 -1
  144. package/lib/typescript/src/index.web.d.ts +7 -1
  145. package/lib/typescript/src/index.web.d.ts.map +1 -1
  146. package/lib/typescript/src/types.d.ts +0 -4
  147. package/lib/typescript/src/types.d.ts.map +1 -1
  148. package/package.json +5 -2
  149. package/src/MapProvider.web.tsx +5 -3
  150. package/src/MapView.tsx +12 -1
  151. package/src/MapView.types.ts +118 -1
  152. package/src/MapView.web.tsx +378 -345
  153. package/src/components/Circle.tsx +52 -0
  154. package/src/components/Circle.types.ts +33 -0
  155. package/src/components/Circle.web.tsx +95 -0
  156. package/src/components/GeoJson.tsx +3 -3
  157. package/src/components/GroundOverlay.tsx +47 -0
  158. package/src/components/GroundOverlay.types.ts +36 -0
  159. package/src/components/GroundOverlay.web.tsx +81 -0
  160. package/src/components/Marker.tsx +32 -6
  161. package/src/components/Marker.types.ts +19 -6
  162. package/src/components/Marker.web.tsx +196 -150
  163. package/src/components/Polygon.web.tsx +93 -89
  164. package/src/components/Polyline.web.tsx +235 -235
  165. package/src/components/TileOverlay.tsx +44 -0
  166. package/src/components/TileOverlay.types.ts +37 -0
  167. package/src/components/TileOverlay.web.tsx +97 -0
  168. package/src/components/index.ts +10 -0
  169. package/src/components/index.web.ts +9 -0
  170. package/src/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  171. package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
  172. package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  173. package/src/fabric/LuggMapViewNativeComponent.ts +13 -5
  174. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  175. package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  176. package/src/index.ts +12 -1
  177. package/src/index.web.ts +15 -0
  178. package/src/types.ts +0 -5
@@ -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"
@@ -13,17 +22,96 @@
13
22
  @property(nonatomic, copy, nullable) NSString *subtitle;
14
23
  @property(nonatomic, strong) LuggMarkerView *markerView;
15
24
  @property(nonatomic, weak) MKAnnotationView *annotationView;
25
+ @property(nonatomic, copy, nullable) dispatch_block_t pendingScaleAnimation;
16
26
  @end
17
27
 
18
28
  @implementation AppleMarkerAnnotation
19
29
  @end
20
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
+
21
107
  @implementation LuggAppleMapViewContent
22
108
  @end
23
109
 
24
110
  @interface AppleMapProvider () <
25
111
  LuggMarkerViewDelegate, LuggCalloutViewDelegate, LuggPolylineViewDelegate,
26
- LuggPolygonViewDelegate, UIGestureRecognizerDelegate>
112
+ LuggPolygonViewDelegate, LuggCircleViewDelegate,
113
+ LuggGroundOverlayViewDelegate, LuggTileOverlayViewDelegate,
114
+ UIGestureRecognizerDelegate>
27
115
  @end
28
116
 
29
117
  @implementation AppleMapProvider {
@@ -35,16 +123,25 @@
35
123
  double _maxZoom;
36
124
  NSMapTable<id<MKOverlay>, LuggPolylineView *> *_overlayToPolylineMap;
37
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;
38
130
  UITapGestureRecognizer *_tapGesture;
39
131
  UILongPressGestureRecognizer *_longPressGesture;
40
132
  LuggMarkerView *_activeNonBubbledMarker;
41
133
  BOOL _isReselectingAnnotation;
134
+ BOOL _isProgrammaticSelect;
42
135
  // Edge insets animation
43
136
  CADisplayLink *_edgeInsetsDisplayLink;
44
137
  UIEdgeInsets _edgeInsetsFrom;
45
138
  UIEdgeInsets _edgeInsetsTo;
139
+ UIEdgeInsets _edgeInsetsCurrent;
46
140
  CFTimeInterval _edgeInsetsAnimationStart;
47
141
  CFTimeInterval _edgeInsetsAnimationDuration;
142
+ BOOL _poiEnabled;
143
+ LuggMapViewPoiFilterMode _poiFilterMode;
144
+ NSArray<NSString *> *_poiFilterCategories;
48
145
  }
49
146
 
50
147
  @synthesize delegate = _delegate;
@@ -53,6 +150,12 @@
53
150
  if (self = [super init]) {
54
151
  _overlayToPolylineMap = [NSMapTable strongToWeakObjectsMapTable];
55
152
  _overlayToPolygonMap = [NSMapTable strongToWeakObjectsMapTable];
153
+ _overlayToCircleMap = [NSMapTable strongToWeakObjectsMapTable];
154
+ _overlayToGroundOverlayMap = [NSMapTable strongToWeakObjectsMapTable];
155
+ _overlayToTileOverlayMap = [NSMapTable strongToWeakObjectsMapTable];
156
+ _poiEnabled = YES;
157
+ _poiFilterMode = LuggMapViewPoiFilterMode::Including;
158
+ _poiFilterCategories = @[];
56
159
  }
57
160
  return self;
58
161
  }
@@ -146,15 +249,181 @@
146
249
  _mapView.showsUserLocation = enabled;
147
250
  }
148
251
 
149
- - (void)setTheme:(NSInteger)theme {
252
+ static MKPointOfInterestCategory poiCategoryFromString(NSString *string) {
253
+ static NSDictionary<NSString *, MKPointOfInterestCategory> *map;
254
+ static dispatch_once_t onceToken;
255
+ dispatch_once(&onceToken, ^{
256
+ NSMutableDictionary<NSString *, MKPointOfInterestCategory> *m =
257
+ [NSMutableDictionary dictionaryWithDictionary:@{
258
+ @"airport" : MKPointOfInterestCategoryAirport,
259
+ @"amusement-park" : MKPointOfInterestCategoryAmusementPark,
260
+ @"aquarium" : MKPointOfInterestCategoryAquarium,
261
+ @"atm" : MKPointOfInterestCategoryATM,
262
+ @"bakery" : MKPointOfInterestCategoryBakery,
263
+ @"bank" : MKPointOfInterestCategoryBank,
264
+ @"beach" : MKPointOfInterestCategoryBeach,
265
+ @"brewery" : MKPointOfInterestCategoryBrewery,
266
+ @"cafe" : MKPointOfInterestCategoryCafe,
267
+ @"campground" : MKPointOfInterestCategoryCampground,
268
+ @"car-rental" : MKPointOfInterestCategoryCarRental,
269
+ @"ev-charger" : MKPointOfInterestCategoryEVCharger,
270
+ @"fire-station" : MKPointOfInterestCategoryFireStation,
271
+ @"fitness-center" : MKPointOfInterestCategoryFitnessCenter,
272
+ @"food-market" : MKPointOfInterestCategoryFoodMarket,
273
+ @"gas-station" : MKPointOfInterestCategoryGasStation,
274
+ @"hospital" : MKPointOfInterestCategoryHospital,
275
+ @"hotel" : MKPointOfInterestCategoryHotel,
276
+ @"laundry" : MKPointOfInterestCategoryLaundry,
277
+ @"library" : MKPointOfInterestCategoryLibrary,
278
+ @"marina" : MKPointOfInterestCategoryMarina,
279
+ @"movie-theater" : MKPointOfInterestCategoryMovieTheater,
280
+ @"museum" : MKPointOfInterestCategoryMuseum,
281
+ @"national-park" : MKPointOfInterestCategoryNationalPark,
282
+ @"nightlife" : MKPointOfInterestCategoryNightlife,
283
+ @"park" : MKPointOfInterestCategoryPark,
284
+ @"parking" : MKPointOfInterestCategoryParking,
285
+ @"pharmacy" : MKPointOfInterestCategoryPharmacy,
286
+ @"police" : MKPointOfInterestCategoryPolice,
287
+ @"post-office" : MKPointOfInterestCategoryPostOffice,
288
+ @"public-transport" : MKPointOfInterestCategoryPublicTransport,
289
+ @"restaurant" : MKPointOfInterestCategoryRestaurant,
290
+ @"restroom" : MKPointOfInterestCategoryRestroom,
291
+ @"school" : MKPointOfInterestCategorySchool,
292
+ @"stadium" : MKPointOfInterestCategoryStadium,
293
+ @"store" : MKPointOfInterestCategoryStore,
294
+ @"theater" : MKPointOfInterestCategoryTheater,
295
+ @"university" : MKPointOfInterestCategoryUniversity,
296
+ @"winery" : MKPointOfInterestCategoryWinery,
297
+ @"zoo" : MKPointOfInterestCategoryZoo,
298
+ }];
299
+ if (@available(iOS 18.0, *)) {
300
+ [m addEntriesFromDictionary:@{
301
+ @"animal-service" : MKPointOfInterestCategoryAnimalService,
302
+ @"automotive-repair" : MKPointOfInterestCategoryAutomotiveRepair,
303
+ @"baseball" : MKPointOfInterestCategoryBaseball,
304
+ @"basketball" : MKPointOfInterestCategoryBasketball,
305
+ @"beauty" : MKPointOfInterestCategoryBeauty,
306
+ @"bowling" : MKPointOfInterestCategoryBowling,
307
+ @"castle" : MKPointOfInterestCategoryCastle,
308
+ @"convention-center" : MKPointOfInterestCategoryConventionCenter,
309
+ @"distillery" : MKPointOfInterestCategoryDistillery,
310
+ @"fairground" : MKPointOfInterestCategoryFairground,
311
+ @"fishing" : MKPointOfInterestCategoryFishing,
312
+ @"fortress" : MKPointOfInterestCategoryFortress,
313
+ @"go-kart" : MKPointOfInterestCategoryGoKart,
314
+ @"golf" : MKPointOfInterestCategoryGolf,
315
+ @"hiking" : MKPointOfInterestCategoryHiking,
316
+ @"kayaking" : MKPointOfInterestCategoryKayaking,
317
+ @"landmark" : MKPointOfInterestCategoryLandmark,
318
+ @"mailbox" : MKPointOfInterestCategoryMailbox,
319
+ @"mini-golf" : MKPointOfInterestCategoryMiniGolf,
320
+ @"music-venue" : MKPointOfInterestCategoryMusicVenue,
321
+ @"national-monument" : MKPointOfInterestCategoryNationalMonument,
322
+ @"planetarium" : MKPointOfInterestCategoryPlanetarium,
323
+ @"rock-climbing" : MKPointOfInterestCategoryRockClimbing,
324
+ @"rv-park" : MKPointOfInterestCategoryRVPark,
325
+ @"skate-park" : MKPointOfInterestCategorySkatePark,
326
+ @"skating" : MKPointOfInterestCategorySkating,
327
+ @"skiing" : MKPointOfInterestCategorySkiing,
328
+ @"soccer" : MKPointOfInterestCategorySoccer,
329
+ @"spa" : MKPointOfInterestCategorySpa,
330
+ @"surfing" : MKPointOfInterestCategorySurfing,
331
+ @"swimming" : MKPointOfInterestCategorySwimming,
332
+ @"tennis" : MKPointOfInterestCategoryTennis,
333
+ @"volleyball" : MKPointOfInterestCategoryVolleyball,
334
+ }];
335
+ }
336
+ map = [m copy];
337
+ });
338
+ return map[string];
339
+ }
340
+
341
+ - (void)applyPoiFilter {
342
+ if (!_poiEnabled) {
343
+ _mapView.pointOfInterestFilter =
344
+ MKPointOfInterestFilter.filterExcludingAllCategories;
345
+ return;
346
+ }
347
+ if (_poiFilterCategories.count > 0) {
348
+ NSMutableArray<MKPointOfInterestCategory> *categories =
349
+ [NSMutableArray array];
350
+ for (NSString *name in _poiFilterCategories) {
351
+ MKPointOfInterestCategory category = poiCategoryFromString(name);
352
+ if (category) {
353
+ [categories addObject:category];
354
+ }
355
+ }
356
+ if (categories.count > 0) {
357
+ if (_poiFilterMode == LuggMapViewPoiFilterMode::Excluding) {
358
+ _mapView.pointOfInterestFilter = [[MKPointOfInterestFilter alloc]
359
+ initExcludingCategories:categories];
360
+ } else {
361
+ _mapView.pointOfInterestFilter = [[MKPointOfInterestFilter alloc]
362
+ initIncludingCategories:categories];
363
+ }
364
+ return;
365
+ }
366
+ }
367
+ _mapView.pointOfInterestFilter = nil;
368
+ }
369
+
370
+ - (void)setInsetAdjustment:(LuggMapViewInsetAdjustment)insetAdjustment {
371
+ BOOL automatic = (insetAdjustment == LuggMapViewInsetAdjustment::Automatic);
372
+ if (_mapView.insetsLayoutMarginsFromSafeArea == automatic)
373
+ return;
374
+
375
+ CLLocationCoordinate2D center = _mapView.centerCoordinate;
376
+ _mapView.insetsLayoutMarginsFromSafeArea = automatic;
377
+
378
+ dispatch_async(dispatch_get_main_queue(), ^{
379
+ [self->_mapView setCenterCoordinate:center animated:NO];
380
+ });
381
+ }
382
+
383
+ - (void)setPoiEnabled:(BOOL)enabled {
384
+ _poiEnabled = enabled;
385
+ [self applyPoiFilter];
386
+ }
387
+
388
+ - (void)setPoiFilterMode:(LuggMapViewPoiFilterMode)mode {
389
+ _poiFilterMode = mode;
390
+ [self applyPoiFilter];
391
+ }
392
+
393
+ - (void)setPoiFilterCategories:(NSArray<NSString *> *)categories {
394
+ _poiFilterCategories = categories;
395
+ [self applyPoiFilter];
396
+ }
397
+
398
+ - (void)setMapType:(LuggMapViewMapType)mapType {
399
+ switch (mapType) {
400
+ case LuggMapViewMapType::Satellite:
401
+ _mapView.mapType = MKMapTypeSatellite;
402
+ break;
403
+ case LuggMapViewMapType::Terrain:
404
+ _mapView.mapType = MKMapTypeStandard;
405
+ break;
406
+ case LuggMapViewMapType::Hybrid:
407
+ _mapView.mapType = MKMapTypeHybrid;
408
+ break;
409
+ case LuggMapViewMapType::MutedStandard:
410
+ _mapView.mapType = MKMapTypeMutedStandard;
411
+ break;
412
+ default:
413
+ _mapView.mapType = MKMapTypeStandard;
414
+ break;
415
+ }
416
+ }
417
+
418
+ - (void)setTheme:(LuggMapViewTheme)theme {
150
419
  switch (theme) {
151
- case 1: // Dark
420
+ case LuggMapViewTheme::Dark:
152
421
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
153
422
  break;
154
- case 0: // Light
423
+ case LuggMapViewTheme::Light:
155
424
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
156
425
  break;
157
- default: // System
426
+ default:
158
427
  _mapView.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
159
428
  break;
160
429
  }
@@ -181,6 +450,7 @@
181
450
  CGFloat deltaY = newOffsetY - oldOffsetY;
182
451
 
183
452
  _mapView.layoutMargins = edgeInsets;
453
+ _edgeInsetsCurrent = edgeInsets;
184
454
 
185
455
  if (deltaX != 0 || deltaY != 0) {
186
456
  CLLocationCoordinate2D currentCenter = _mapView.centerCoordinate;
@@ -231,7 +501,7 @@
231
501
  _edgeInsetsFrom.right +
232
502
  (_edgeInsetsTo.right - _edgeInsetsFrom.right) * t);
233
503
 
234
- [self setEdgeInsets:current oldEdgeInsets:_mapView.layoutMargins];
504
+ [self setEdgeInsets:current oldEdgeInsets:_edgeInsetsCurrent];
235
505
 
236
506
  if (progress >= 1.0) {
237
507
  [self stopEdgeInsetsAnimation];
@@ -314,6 +584,55 @@
314
584
  return nil;
315
585
  }
316
586
 
587
+ - (LuggCircleView *)hitTestCircleAtPoint:(CGPoint)point {
588
+ CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point
589
+ toCoordinateFromView:_mapView];
590
+ CLLocation *tapLocation =
591
+ [[CLLocation alloc] initWithLatitude:tapCoordinate.latitude
592
+ longitude:tapCoordinate.longitude];
593
+
594
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
595
+ for (NSInteger i = overlays.count - 1; i >= 0; i--) {
596
+ id<MKOverlay> overlay = overlays[i];
597
+ if (![overlay isKindOfClass:[MKCircle class]])
598
+ continue;
599
+
600
+ LuggCircleView *circleView = [_overlayToCircleMap objectForKey:overlay];
601
+ if (!circleView || !circleView.tappable)
602
+ continue;
603
+
604
+ CLLocation *center =
605
+ [[CLLocation alloc] initWithLatitude:circleView.center.latitude
606
+ longitude:circleView.center.longitude];
607
+ CLLocationDistance distance = [tapLocation distanceFromLocation:center];
608
+ if (distance <= circleView.radius)
609
+ return circleView;
610
+ }
611
+ return nil;
612
+ }
613
+
614
+ - (LuggGroundOverlayView *)hitTestGroundOverlayAtPoint:(CGPoint)point {
615
+ CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point
616
+ toCoordinateFromView:_mapView];
617
+
618
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
619
+ for (NSInteger i = overlays.count - 1; i >= 0; i--) {
620
+ id<MKOverlay> overlay = overlays[i];
621
+ if (![overlay isKindOfClass:[GroundImageOverlay class]])
622
+ continue;
623
+
624
+ LuggGroundOverlayView *view =
625
+ [_overlayToGroundOverlayMap objectForKey:overlay];
626
+ if (!view || !view.tappable)
627
+ continue;
628
+
629
+ MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoordinate);
630
+ if (MKMapRectContainsPoint(overlay.boundingMapRect, mapPoint))
631
+ return view;
632
+ }
633
+ return nil;
634
+ }
635
+
317
636
  - (BOOL)hitTestAnnotationAtPoint:(CGPoint)point {
318
637
  for (id<MKAnnotation> annotation in _mapView.annotations) {
319
638
  MKAnnotationView *view = [_mapView viewForAnnotation:annotation];
@@ -348,6 +667,19 @@
348
667
  if ([self hitTestAnnotationAtPoint:point])
349
668
  return;
350
669
 
670
+ LuggGroundOverlayView *groundOverlayView =
671
+ [self hitTestGroundOverlayAtPoint:point];
672
+ if (groundOverlayView) {
673
+ [groundOverlayView emitPressEvent];
674
+ return;
675
+ }
676
+
677
+ LuggCircleView *circleView = [self hitTestCircleAtPoint:point];
678
+ if (circleView) {
679
+ [circleView emitPressEvent];
680
+ return;
681
+ }
682
+
351
683
  LuggPolygonView *polygonView = [self hitTestPolygonAtPoint:point];
352
684
  if (polygonView) {
353
685
  [polygonView emitPressEvent];
@@ -525,6 +857,37 @@
525
857
  return renderer;
526
858
  }
527
859
 
860
+ if ([overlay isKindOfClass:[MKCircle class]]) {
861
+ LuggCircleView *circleView = [_overlayToCircleMap objectForKey:overlay];
862
+ MKCircle *circle = (MKCircle *)overlay;
863
+
864
+ MKCircleRenderer *renderer =
865
+ [[MKCircleRenderer alloc] initWithCircle:circle];
866
+ if (circleView) {
867
+ renderer.fillColor = circleView.fillColor;
868
+ renderer.strokeColor = circleView.strokeColor;
869
+ renderer.lineWidth = circleView.strokeWidth;
870
+ circleView.renderer = renderer;
871
+ }
872
+ return renderer;
873
+ }
874
+
875
+ if ([overlay isKindOfClass:[GroundImageOverlay class]]) {
876
+ GroundImageOverlay *groundOverlay = (GroundImageOverlay *)overlay;
877
+ GroundImageOverlayRenderer *renderer =
878
+ [[GroundImageOverlayRenderer alloc] initWithOverlay:overlay];
879
+ renderer.image = groundOverlay.image;
880
+ renderer.overlayOpacity = groundOverlay.opacity;
881
+ return renderer;
882
+ }
883
+
884
+ if ([overlay isKindOfClass:[MKTileOverlay class]]) {
885
+ MKTileOverlay *tileOverlay = (MKTileOverlay *)overlay;
886
+ MKTileOverlayRenderer *renderer =
887
+ [[MKTileOverlayRenderer alloc] initWithTileOverlay:tileOverlay];
888
+ return renderer;
889
+ }
890
+
528
891
  return nil;
529
892
  }
530
893
 
@@ -537,10 +900,6 @@
537
900
  LuggMarkerView *markerView = annotation.markerView;
538
901
 
539
902
  if (markerView) {
540
- CGPoint point = [_mapView convertCoordinate:markerView.coordinate
541
- toPointToView:_mapView];
542
- [markerView emitPressEventWithPoint:point];
543
-
544
903
  LuggCalloutView *calloutView = markerView.calloutView;
545
904
  if (calloutView && !calloutView.bubbled && calloutView.hasCustomContent) {
546
905
  [self showNonBubbledCallout:markerView];
@@ -572,6 +931,7 @@
572
931
  [contentView removeFromSuperview];
573
932
 
574
933
  contentView.userInteractionEnabled = YES;
934
+ contentView.hidden = YES;
575
935
  calloutView.delegate = self;
576
936
 
577
937
  AppleMarkerAnnotation *annotation =
@@ -602,26 +962,26 @@
602
962
  if (contentSize.width <= 0 || contentSize.height <= 0)
603
963
  return;
604
964
 
605
- CGPoint anchor = calloutView.anchor;
965
+ CGPoint offset = calloutView.offset;
606
966
 
607
967
  AppleMarkerAnnotation *annotation =
608
968
  (AppleMarkerAnnotation *)_activeNonBubbledMarker.marker;
609
969
  MKAnnotationView *annotationView = annotation.annotationView;
610
970
 
611
971
  if (annotationView && contentView.superview == annotationView) {
612
- CGPoint center = CGPointMake(annotationView.bounds.size.width / 2.0 +
613
- contentSize.width * (0.5 - anchor.x),
614
- annotationView.bounds.size.height / 2.0 +
615
- contentSize.height * (0.5 - anchor.y));
972
+ CGPoint center =
973
+ CGPointMake(annotationView.bounds.size.width / 2.0 + offset.x,
974
+ -contentSize.height / 2.0 + offset.y);
616
975
  contentView.center = center;
617
976
  } else {
618
977
  CGPoint point =
619
978
  [_mapView convertCoordinate:_activeNonBubbledMarker.coordinate
620
979
  toPointToView:_wrapperView];
621
- contentView.center =
622
- CGPointMake(point.x + contentSize.width * (0.5 - anchor.x),
623
- point.y + contentSize.height * (0.5 - anchor.y));
980
+ contentView.center = CGPointMake(
981
+ point.x + offset.x, point.y - contentSize.height / 2.0 + offset.y);
624
982
  }
983
+
984
+ contentView.hidden = NO;
625
985
  }
626
986
 
627
987
  - (void)dismissNonBubbledCallout {
@@ -692,7 +1052,17 @@
692
1052
  if ([view.annotation isKindOfClass:[AppleMarkerAnnotation class]]) {
693
1053
  AppleMarkerAnnotation *annotation =
694
1054
  (AppleMarkerAnnotation *)view.annotation;
695
- [_mapView setCenterCoordinate:annotation.coordinate animated:YES];
1055
+ LuggMarkerView *markerView = annotation.markerView;
1056
+
1057
+ if (markerView) {
1058
+ CGPoint point = [_mapView convertCoordinate:markerView.coordinate
1059
+ toPointToView:_mapView];
1060
+ [markerView emitPressEventWithPoint:point];
1061
+
1062
+ if (markerView.centerOnPress) {
1063
+ [_mapView setCenterCoordinate:annotation.coordinate animated:YES];
1064
+ }
1065
+ }
696
1066
  }
697
1067
  }
698
1068
 
@@ -717,6 +1087,42 @@
717
1087
  calloutView.hasCustomContent ? calloutView.contentView : nil;
718
1088
  }
719
1089
 
1090
+ - (void)showCalloutForMarkerView:(LuggMarkerView *)markerView {
1091
+ LuggCalloutView *calloutView = markerView.calloutView;
1092
+ _isProgrammaticSelect = YES;
1093
+
1094
+ if (calloutView && calloutView.hasCustomContent) {
1095
+ [self dismissNonBubbledCallout];
1096
+ AppleMarkerAnnotation *annotation =
1097
+ (AppleMarkerAnnotation *)markerView.marker;
1098
+ if (!calloutView.bubbled) {
1099
+ [self showNonBubbledCallout:markerView];
1100
+ } else if (annotation) {
1101
+ [_mapView selectAnnotation:annotation animated:YES];
1102
+ }
1103
+ _isProgrammaticSelect = NO;
1104
+ return;
1105
+ }
1106
+
1107
+ if (markerView.title.length > 0) {
1108
+ AppleMarkerAnnotation *annotation =
1109
+ (AppleMarkerAnnotation *)markerView.marker;
1110
+ if (annotation) {
1111
+ [_mapView selectAnnotation:annotation animated:YES];
1112
+ }
1113
+ }
1114
+ _isProgrammaticSelect = NO;
1115
+ }
1116
+
1117
+ - (void)hideCalloutForMarkerView:(LuggMarkerView *)markerView {
1118
+ [self dismissNonBubbledCallout];
1119
+ AppleMarkerAnnotation *annotation =
1120
+ (AppleMarkerAnnotation *)markerView.marker;
1121
+ if (annotation) {
1122
+ [_mapView deselectAnnotation:annotation animated:YES];
1123
+ }
1124
+ }
1125
+
720
1126
  #pragma mark - MarkerViewDelegate
721
1127
 
722
1128
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView {
@@ -761,6 +1167,24 @@
761
1167
  [self syncPolygonView:polygonView];
762
1168
  }
763
1169
 
1170
+ #pragma mark - CircleViewDelegate
1171
+
1172
+ - (void)circleViewDidUpdate:(LuggCircleView *)circleView {
1173
+ [self syncCircleView:circleView];
1174
+ }
1175
+
1176
+ #pragma mark - GroundOverlayViewDelegate
1177
+
1178
+ - (void)groundOverlayViewDidUpdate:(LuggGroundOverlayView *)groundOverlayView {
1179
+ [self syncGroundOverlayView:groundOverlayView];
1180
+ }
1181
+
1182
+ #pragma mark - TileOverlayViewDelegate
1183
+
1184
+ - (void)tileOverlayViewDidUpdate:(LuggTileOverlayView *)tileOverlayView {
1185
+ [self syncTileOverlayView:tileOverlayView];
1186
+ }
1187
+
764
1188
  #pragma mark - Marker Management
765
1189
 
766
1190
  - (void)addMarkerView:(LuggMarkerView *)markerView {
@@ -784,6 +1208,10 @@
784
1208
  (AppleMarkerAnnotation *)markerView.marker;
785
1209
 
786
1210
  if (annotation) {
1211
+ if (annotation.pendingScaleAnimation) {
1212
+ dispatch_block_cancel(annotation.pendingScaleAnimation);
1213
+ annotation.pendingScaleAnimation = nil;
1214
+ }
787
1215
  annotation.annotationView.transform = CGAffineTransformIdentity;
788
1216
  annotation.markerView = nil;
789
1217
  annotation.annotationView = nil;
@@ -802,31 +1230,65 @@
802
1230
  annotationView.transform = CGAffineTransformIdentity;
803
1231
 
804
1232
  UIView *iconView = markerView.iconView;
805
- CGRect frame = iconView.frame;
806
- if (frame.size.width <= 0 || frame.size.height <= 0)
1233
+ iconView.transform = CGAffineTransformIdentity;
1234
+
1235
+ // Clean up iconView from in-progress rasterize animation
1236
+ if (markerView.rasterize && [iconView superview] == annotationView) {
1237
+ [iconView removeFromSuperview];
1238
+ iconView.layer.anchorPoint = CGPointMake(0.5, 0.5);
1239
+ }
1240
+
1241
+ CGSize size = iconView.bounds.size;
1242
+ if (size.width <= 0 || size.height <= 0)
807
1243
  return;
808
1244
 
809
1245
  CGFloat scale = markerView.scale;
810
1246
  CGPoint anchor = markerView.anchor;
1247
+ CGFloat scaledWidth = size.width * scale;
1248
+ CGFloat scaledHeight = size.height * scale;
811
1249
 
812
- if (markerView.rasterize) {
813
- annotationView.image = [markerView createScaledIconImage];
814
- } else {
815
- iconView.layer.anchorPoint = anchor;
816
- iconView.transform = CGAffineTransformMakeScale(scale, scale);
817
- iconView.frame =
818
- CGRectMake(frame.size.width * (0.5 - anchor.x) * (scale - 1),
819
- frame.size.height * (0.5 - anchor.y) * (scale - 1),
820
- frame.size.width, frame.size.height);
821
- }
822
-
823
- annotationView.bounds =
824
- CGRectMake(0, 0, frame.size.width * scale, frame.size.height * scale);
825
- annotationView.centerOffset =
826
- CGPointMake(frame.size.width * scale * (anchor.x - 0.5),
827
- -frame.size.height * scale * (anchor.y - 0.5));
1250
+ AppleMarkerAnnotation *annotation =
1251
+ (AppleMarkerAnnotation *)markerView.marker;
1252
+
1253
+ // Cancel any pending rasterize
1254
+ if (annotation.pendingScaleAnimation) {
1255
+ dispatch_block_cancel(annotation.pendingScaleAnimation);
1256
+ annotation.pendingScaleAnimation = nil;
1257
+ }
1258
+
1259
+ // Use live iconView during rapid updates (skip expensive rasterize per frame)
1260
+ if (markerView.rasterize && [iconView superview] != annotationView) {
1261
+ annotationView.image = nil;
1262
+ [annotationView addSubview:iconView];
1263
+ }
1264
+
1265
+ iconView.layer.anchorPoint = anchor;
1266
+ iconView.bounds = CGRectMake(0, 0, size.width, size.height);
1267
+ iconView.center =
1268
+ CGPointMake(scaledWidth * anchor.x, scaledHeight * anchor.y);
1269
+ iconView.transform = CGAffineTransformMakeScale(scale, scale);
1270
+
1271
+ annotationView.bounds = CGRectMake(0, 0, scaledWidth, scaledHeight);
1272
+ annotationView.centerOffset = CGPointMake(scaledWidth * (0.5 - anchor.x),
1273
+ scaledHeight * (0.5 - anchor.y));
828
1274
  annotationView.transform =
829
1275
  CGAffineTransformMakeRotation(markerView.rotate * M_PI / 180.0);
1276
+
1277
+ // Debounce: rasterize once updates settle
1278
+ if (markerView.rasterize) {
1279
+ dispatch_block_t rasterizeBlock =
1280
+ dispatch_block_create((dispatch_block_flags_t)0, ^{
1281
+ annotation.pendingScaleAnimation = nil;
1282
+ annotationView.image = [markerView createScaledIconImage];
1283
+ [iconView removeFromSuperview];
1284
+ [markerView resetIconViewTransform];
1285
+ });
1286
+
1287
+ annotation.pendingScaleAnimation = rasterizeBlock;
1288
+ dispatch_after(
1289
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(150 * NSEC_PER_MSEC)),
1290
+ dispatch_get_main_queue(), rasterizeBlock);
1291
+ }
830
1292
  }
831
1293
 
832
1294
  - (void)updateAnnotationViewFrame:(AppleMarkerAnnotation *)annotation {
@@ -1046,6 +1508,196 @@
1046
1508
  return [interiorPolygons copy];
1047
1509
  }
1048
1510
 
1511
+ #pragma mark - Circle Management
1512
+
1513
+ - (void)addCircleView:(LuggCircleView *)circleView {
1514
+ circleView.delegate = self;
1515
+ [self addCircleOverlayToMap:circleView];
1516
+ }
1517
+
1518
+ - (void)removeCircleView:(LuggCircleView *)circleView {
1519
+ circleView.delegate = nil;
1520
+ MKCircle *circle = (MKCircle *)circleView.circle;
1521
+ if (circle) {
1522
+ [_overlayToCircleMap removeObjectForKey:circle];
1523
+ [_mapView removeOverlay:circle];
1524
+ circleView.circle = nil;
1525
+ }
1526
+ }
1527
+
1528
+ - (void)syncCircleView:(LuggCircleView *)circleView {
1529
+ if (!_mapView)
1530
+ return;
1531
+
1532
+ MKCircle *oldCircle = (MKCircle *)circleView.circle;
1533
+
1534
+ MKCircle *newCircle = [MKCircle circleWithCenterCoordinate:circleView.center
1535
+ radius:circleView.radius];
1536
+
1537
+ circleView.circle = newCircle;
1538
+ [_overlayToCircleMap setObject:circleView forKey:newCircle];
1539
+
1540
+ MKCircleRenderer *renderer = (MKCircleRenderer *)circleView.renderer;
1541
+ if (renderer && oldCircle) {
1542
+ [_overlayToCircleMap removeObjectForKey:oldCircle];
1543
+ [_mapView removeOverlay:oldCircle];
1544
+ [self insertOverlay:newCircle withZIndex:circleView.zIndex];
1545
+ circleView.renderer = nil;
1546
+ return;
1547
+ }
1548
+
1549
+ if (oldCircle) {
1550
+ [_overlayToCircleMap removeObjectForKey:oldCircle];
1551
+ [_mapView removeOverlay:oldCircle];
1552
+ }
1553
+ [self insertOverlay:newCircle withZIndex:circleView.zIndex];
1554
+ }
1555
+
1556
+ - (void)addCircleOverlayToMap:(LuggCircleView *)circleView {
1557
+ if (!_mapView)
1558
+ return;
1559
+
1560
+ MKCircle *circle = [MKCircle circleWithCenterCoordinate:circleView.center
1561
+ radius:circleView.radius];
1562
+
1563
+ circleView.circle = circle;
1564
+ [_overlayToCircleMap setObject:circleView forKey:circle];
1565
+ [self insertOverlay:circle withZIndex:circleView.zIndex];
1566
+ }
1567
+
1568
+ #pragma mark - Ground Overlay Management
1569
+
1570
+ - (void)addGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1571
+ groundOverlayView.delegate = self;
1572
+ [self syncGroundOverlayView:groundOverlayView];
1573
+ }
1574
+
1575
+ - (void)removeGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1576
+ groundOverlayView.delegate = nil;
1577
+ GroundImageOverlay *overlay = (GroundImageOverlay *)groundOverlayView.overlay;
1578
+ if (overlay) {
1579
+ [_overlayToGroundOverlayMap removeObjectForKey:overlay];
1580
+ [_mapView removeOverlay:overlay];
1581
+ groundOverlayView.overlay = nil;
1582
+ }
1583
+ }
1584
+
1585
+ - (void)syncGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView {
1586
+ if (!_mapView)
1587
+ return;
1588
+
1589
+ NSString *imageUri = groundOverlayView.imageUri;
1590
+ if (imageUri.length == 0)
1591
+ return;
1592
+
1593
+ // Remove old overlay
1594
+ GroundImageOverlay *oldOverlay =
1595
+ (GroundImageOverlay *)groundOverlayView.overlay;
1596
+ if (oldOverlay) {
1597
+ [_overlayToGroundOverlayMap removeObjectForKey:oldOverlay];
1598
+ [_mapView removeOverlay:oldOverlay];
1599
+ groundOverlayView.overlay = nil;
1600
+ }
1601
+
1602
+ NSURL *url = [NSURL URLWithString:imageUri];
1603
+ if (!url)
1604
+ return;
1605
+
1606
+ __weak __typeof(self) weakSelf = self;
1607
+ __weak LuggGroundOverlayView *weakOverlayView = groundOverlayView;
1608
+ dispatch_async(
1609
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1610
+ NSData *data = [NSData dataWithContentsOfURL:url];
1611
+ UIImage *image = data ? [UIImage imageWithData:data] : nil;
1612
+ dispatch_async(dispatch_get_main_queue(), ^{
1613
+ [weakSelf addGroundOverlayToMap:weakOverlayView image:image];
1614
+ });
1615
+ });
1616
+ }
1617
+
1618
+ - (void)addGroundOverlayToMap:(LuggGroundOverlayView *)groundOverlayView
1619
+ image:(UIImage *)image {
1620
+ if (!_mapView || !groundOverlayView || !image)
1621
+ return;
1622
+
1623
+ MKMapPoint sw = MKMapPointForCoordinate(groundOverlayView.southwest);
1624
+ MKMapPoint ne = MKMapPointForCoordinate(groundOverlayView.northeast);
1625
+
1626
+ MKMapRect mapRect = MKMapRectMake(MIN(sw.x, ne.x), MIN(sw.y, ne.y),
1627
+ fabs(ne.x - sw.x), fabs(ne.y - sw.y));
1628
+
1629
+ CLLocationCoordinate2D center =
1630
+ CLLocationCoordinate2DMake((groundOverlayView.northeast.latitude +
1631
+ groundOverlayView.southwest.latitude) /
1632
+ 2.0,
1633
+ (groundOverlayView.northeast.longitude +
1634
+ groundOverlayView.southwest.longitude) /
1635
+ 2.0);
1636
+
1637
+ GroundImageOverlay *overlay = [[GroundImageOverlay alloc] init];
1638
+ overlay.coordinate = center;
1639
+ overlay.boundingMapRect = mapRect;
1640
+ overlay.image = image;
1641
+ overlay.opacity = groundOverlayView.opacity;
1642
+
1643
+ groundOverlayView.overlay = overlay;
1644
+ [_overlayToGroundOverlayMap setObject:groundOverlayView forKey:overlay];
1645
+ [self insertOverlay:overlay withZIndex:groundOverlayView.zIndex];
1646
+ }
1647
+
1648
+ #pragma mark - Tile Overlay Management
1649
+
1650
+ - (void)addTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1651
+ tileOverlayView.delegate = self;
1652
+ [self syncTileOverlayView:tileOverlayView];
1653
+ }
1654
+
1655
+ - (void)removeTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1656
+ tileOverlayView.delegate = nil;
1657
+ MKTileOverlay *overlay = (MKTileOverlay *)tileOverlayView.overlay;
1658
+ if (overlay) {
1659
+ [_overlayToTileOverlayMap removeObjectForKey:overlay];
1660
+ [_mapView removeOverlay:overlay];
1661
+ tileOverlayView.overlay = nil;
1662
+ }
1663
+ }
1664
+
1665
+ - (void)syncTileOverlayView:(LuggTileOverlayView *)tileOverlayView {
1666
+ if (!_mapView)
1667
+ return;
1668
+
1669
+ NSString *urlTemplate = tileOverlayView.urlTemplate;
1670
+ if (urlTemplate.length == 0)
1671
+ return;
1672
+
1673
+ // Remove old overlay
1674
+ MKTileOverlay *oldOverlay = (MKTileOverlay *)tileOverlayView.overlay;
1675
+ if (oldOverlay) {
1676
+ [_overlayToTileOverlayMap removeObjectForKey:oldOverlay];
1677
+ [_mapView removeOverlay:oldOverlay];
1678
+ tileOverlayView.overlay = nil;
1679
+ }
1680
+
1681
+ MKTileOverlay *tileOverlay;
1682
+ if (tileOverlayView.hasBounds) {
1683
+ BoundedTileOverlay *bounded =
1684
+ [[BoundedTileOverlay alloc] initWithURLTemplate:urlTemplate];
1685
+ bounded.southwest = tileOverlayView.southwest;
1686
+ bounded.northeast = tileOverlayView.northeast;
1687
+ tileOverlay = bounded;
1688
+ } else {
1689
+ tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
1690
+ }
1691
+
1692
+ tileOverlay.tileSize =
1693
+ CGSizeMake(tileOverlayView.tileSize, tileOverlayView.tileSize);
1694
+ tileOverlay.canReplaceMapContent = NO;
1695
+
1696
+ tileOverlayView.overlay = tileOverlay;
1697
+ [_overlayToTileOverlayMap setObject:tileOverlayView forKey:tileOverlay];
1698
+ [_mapView addOverlay:tileOverlay level:MKOverlayLevelAboveRoads];
1699
+ }
1700
+
1049
1701
  - (void)insertOverlay:(id<MKOverlay>)overlay withZIndex:(NSInteger)zIndex {
1050
1702
  if (zIndex == 0) {
1051
1703
  [_mapView addOverlay:overlay];
@@ -1061,10 +1713,18 @@
1061
1713
  [_overlayToPolylineMap objectForKey:overlays[i]];
1062
1714
  LuggPolygonView *existingPolygonView =
1063
1715
  [_overlayToPolygonMap objectForKey:overlays[i]];
1716
+ LuggCircleView *existingCircleView =
1717
+ [_overlayToCircleMap objectForKey:overlays[i]];
1718
+ LuggGroundOverlayView *existingGroundOverlayView =
1719
+ [_overlayToGroundOverlayMap objectForKey:overlays[i]];
1064
1720
  if (existingPolylineView) {
1065
1721
  existingZIndex = existingPolylineView.zIndex;
1066
1722
  } else if (existingPolygonView) {
1067
1723
  existingZIndex = existingPolygonView.zIndex;
1724
+ } else if (existingCircleView) {
1725
+ existingZIndex = existingCircleView.zIndex;
1726
+ } else if (existingGroundOverlayView) {
1727
+ existingZIndex = existingGroundOverlayView.zIndex;
1068
1728
  } else {
1069
1729
  continue;
1070
1730
  }