@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,10 +1,15 @@
1
1
  #import "LuggMapView.h"
2
+ #import "LuggCircleView.h"
3
+ #import "LuggGroundOverlayView.h"
2
4
  #import "LuggMapWrapperView.h"
3
5
  #import "LuggMarkerView.h"
4
6
  #import "LuggPolygonView.h"
5
7
  #import "LuggPolylineView.h"
8
+ #import "LuggTileOverlayView.h"
6
9
  #import "core/AppleMapProvider.h"
10
+ #import "core/AppleStaticMapProvider.h"
7
11
  #import "core/GoogleMapProvider.h"
12
+ #import "core/GoogleStaticMapProvider.h"
8
13
  #import "core/MapProviderDelegate.h"
9
14
  #import "events/CameraIdleEvent.h"
10
15
  #import "events/CameraMoveEvent.h"
@@ -22,6 +27,19 @@
22
27
  using namespace facebook::react;
23
28
  using namespace luggmaps::events;
24
29
 
30
+ // Static snapshots cached across view recycling, keyed by the user-provided
31
+ // staticKey plus the map settings that affect appearance.
32
+ static NSCache<NSString *, UIImage *> *StaticSnapshotCache(void) {
33
+ static NSCache<NSString *, UIImage *> *cache;
34
+ static dispatch_once_t onceToken;
35
+ dispatch_once(&onceToken, ^{
36
+ cache = [[NSCache alloc] init];
37
+ cache.countLimit = 100;
38
+ cache.totalCostLimit = 64 * 1024 * 1024; // bytes
39
+ });
40
+ return cache;
41
+ }
42
+
25
43
  @interface LuggMapView () <RCTLuggMapViewViewProtocol, MapProviderDelegate>
26
44
  @end
27
45
 
@@ -36,12 +54,21 @@ using namespace luggmaps::events;
36
54
  BOOL _scrollEnabled;
37
55
  BOOL _rotateEnabled;
38
56
  BOOL _pitchEnabled;
57
+ BOOL _compassEnabled;
58
+ BOOL _staticMode;
59
+ NSString *_staticKey;
60
+ BOOL _staticSnapshotDone;
39
61
  BOOL _userLocationEnabled;
62
+ LuggMapViewMapType _mapType;
40
63
  LuggMapViewTheme _theme;
64
+ BOOL _poiEnabled;
65
+ LuggMapViewPoiFilterMode _poiFilterMode;
66
+ NSArray<NSString *> *_poiFilterCategories;
41
67
  double _minZoom;
42
68
  double _maxZoom;
43
69
  UIEdgeInsets _edgeInsets;
44
70
  UIEdgeInsets _oldEdgeInsets;
71
+ LuggMapViewInsetAdjustment _insetAdjustment;
45
72
  }
46
73
 
47
74
  + (ComponentDescriptorProvider)componentDescriptorProvider {
@@ -60,9 +87,17 @@ using namespace luggmaps::events;
60
87
  _scrollEnabled = YES;
61
88
  _rotateEnabled = YES;
62
89
  _pitchEnabled = YES;
90
+ _compassEnabled = YES;
91
+ _staticMode = NO;
92
+ _staticKey = @"";
63
93
  _userLocationEnabled = NO;
94
+ _poiEnabled = NO;
95
+ _poiFilterMode = LuggMapViewPoiFilterMode::Including;
96
+ _poiFilterCategories = @[];
97
+ _mapType = LuggMapViewMapType::Standard;
64
98
  _theme = LuggMapViewTheme::System;
65
99
  _edgeInsets = UIEdgeInsetsZero;
100
+ _insetAdjustment = LuggMapViewInsetAdjustment::Never;
66
101
  }
67
102
  return self;
68
103
  }
@@ -91,6 +126,23 @@ using namespace luggmaps::events;
91
126
  if (_provider) {
92
127
  [_provider addPolygonView:polygonView];
93
128
  }
129
+ } else if ([childComponentView isKindOfClass:[LuggCircleView class]]) {
130
+ LuggCircleView *circleView = (LuggCircleView *)childComponentView;
131
+ if (_provider) {
132
+ [_provider addCircleView:circleView];
133
+ }
134
+ } else if ([childComponentView isKindOfClass:[LuggGroundOverlayView class]]) {
135
+ LuggGroundOverlayView *groundOverlayView =
136
+ (LuggGroundOverlayView *)childComponentView;
137
+ if (_provider) {
138
+ [_provider addGroundOverlayView:groundOverlayView];
139
+ }
140
+ } else if ([childComponentView isKindOfClass:[LuggTileOverlayView class]]) {
141
+ LuggTileOverlayView *tileOverlayView =
142
+ (LuggTileOverlayView *)childComponentView;
143
+ if (_provider) {
144
+ [_provider addTileOverlayView:tileOverlayView];
145
+ }
94
146
  }
95
147
  }
96
148
 
@@ -112,6 +164,23 @@ using namespace luggmaps::events;
112
164
  if (_provider) {
113
165
  [_provider removePolygonView:polygonView];
114
166
  }
167
+ } else if ([childComponentView isKindOfClass:[LuggCircleView class]]) {
168
+ LuggCircleView *circleView = (LuggCircleView *)childComponentView;
169
+ if (_provider) {
170
+ [_provider removeCircleView:circleView];
171
+ }
172
+ } else if ([childComponentView isKindOfClass:[LuggGroundOverlayView class]]) {
173
+ LuggGroundOverlayView *groundOverlayView =
174
+ (LuggGroundOverlayView *)childComponentView;
175
+ if (_provider) {
176
+ [_provider removeGroundOverlayView:groundOverlayView];
177
+ }
178
+ } else if ([childComponentView isKindOfClass:[LuggTileOverlayView class]]) {
179
+ LuggTileOverlayView *tileOverlayView =
180
+ (LuggTileOverlayView *)childComponentView;
181
+ if (_provider) {
182
+ [_provider removeTileOverlayView:tileOverlayView];
183
+ }
115
184
  }
116
185
 
117
186
  [super unmountChildComponentView:childComponentView index:index];
@@ -121,17 +190,118 @@ using namespace luggmaps::events;
121
190
  [super didMoveToWindow];
122
191
  if (self.window) {
123
192
  if (!_provider && _mapWrapperView) {
124
- [self initializeProvider];
193
+ if (_staticMode) {
194
+ [self startStaticContent];
195
+ } else {
196
+ [self initializeProvider];
197
+ }
125
198
  }
126
199
  [_provider resumeAnimations];
127
200
  } else {
201
+ // A static map mid-warmup may hold an expensive live map view. Destroy
202
+ // it off-window and re-render on return instead of letting paused maps
203
+ // pile up across screen switches and all resume at once
204
+ if (_staticMode && _provider && !_staticSnapshotDone) {
205
+ [_provider destroy];
206
+ _provider = nil;
207
+ _initialized = NO;
208
+ }
128
209
  [_provider pauseAnimations];
129
210
  }
130
211
  }
131
212
 
213
+ // Shown while the map warms up, until the live map or snapshot covers it
214
+ - (void)applyStaticPlaceholderBackground {
215
+ const auto &viewProps =
216
+ *std::static_pointer_cast<LuggMapViewProps const>(_props);
217
+ if (viewProps.backgroundColor) {
218
+ // A user-provided background acts as the placeholder
219
+ return;
220
+ }
221
+
222
+ switch (_theme) {
223
+ case LuggMapViewTheme::Dark:
224
+ _mapWrapperView.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
225
+ break;
226
+ case LuggMapViewTheme::Light:
227
+ _mapWrapperView.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
228
+ break;
229
+ default:
230
+ _mapWrapperView.overrideUserInterfaceStyle =
231
+ UIUserInterfaceStyleUnspecified;
232
+ break;
233
+ }
234
+ _mapWrapperView.backgroundColor = UIColor.secondarySystemBackgroundColor;
235
+ }
236
+
237
+ - (nullable NSString *)staticSnapshotCacheKey {
238
+ if (_staticKey.length == 0 || !_mapWrapperView)
239
+ return nil;
240
+
241
+ CGSize size = _mapWrapperView.bounds.size;
242
+ if (size.width <= 0 || size.height <= 0)
243
+ return nil;
244
+
245
+ // 'system' theme snapshots depend on the current appearance
246
+ NSInteger style = _theme == LuggMapViewTheme::System
247
+ ? (NSInteger)self.traitCollection.userInterfaceStyle
248
+ : -(NSInteger)_theme;
249
+
250
+ // Camera is part of the key so a reused staticKey with a different
251
+ // location/zoom can't serve a stale snapshot. The provider's camera (when
252
+ // available) reflects imperative camera changes, so a re-rendered
253
+ // snapshot is keyed on what it actually shows.
254
+ const auto &viewProps =
255
+ *std::static_pointer_cast<LuggMapViewProps const>(_props);
256
+ double latitude = viewProps.initialCoordinate.latitude;
257
+ double longitude = viewProps.initialCoordinate.longitude;
258
+ double zoom = viewProps.initialZoom;
259
+ if ([_provider isKindOfClass:[StaticMapProviderBase class]]) {
260
+ StaticMapProviderBase *staticProvider = (StaticMapProviderBase *)_provider;
261
+ latitude = staticProvider.coordinate.latitude;
262
+ longitude = staticProvider.coordinate.longitude;
263
+ zoom = staticProvider.zoom;
264
+ }
265
+
266
+ return [NSString
267
+ stringWithFormat:@"%@|%d|%@|%d|%ld|%.0fx%.0f|%.6f,%.6f,%.2f|%@",
268
+ _staticKey, (int)_providerType, _mapId, (int)_mapType,
269
+ (long)style, size.width, size.height, latitude,
270
+ longitude, zoom, NSStringFromUIEdgeInsets(_edgeInsets)];
271
+ }
272
+
273
+ - (void)startStaticContent {
274
+ [self applyStaticPlaceholderBackground];
275
+
276
+ // Both static providers render fully async: the placeholder shows until
277
+ // the snapshotter (Apple) or a brief live-map warmup (Google) delivers
278
+ // the base map, which reveals together with the marker/shape overlays
279
+ [self initializeProvider];
280
+ }
281
+
282
+ // staticKey identifies the map's content; when it changes (e.g. a recycled
283
+ // list row bound to a new item), any live warmup or displayed snapshot is
284
+ // stale and the static lifecycle restarts. Without this, an in-flight
285
+ // warmup caches the old item's map under the new key.
286
+ - (void)resetStaticContent {
287
+ _staticSnapshotDone = NO;
288
+
289
+ if (_provider) {
290
+ [_provider destroy];
291
+ _provider = nil;
292
+ _initialized = NO;
293
+ }
294
+
295
+ if (self.window) {
296
+ [self startStaticContent];
297
+ }
298
+ }
299
+
132
300
  - (void)prepareForRecycle {
133
301
  [super prepareForRecycle];
134
302
 
303
+ _staticSnapshotDone = NO;
304
+
135
305
  [_provider destroy];
136
306
  _provider = nil;
137
307
  _mapWrapperView = nil;
@@ -148,7 +318,12 @@ using namespace luggmaps::events;
148
318
  *std::static_pointer_cast<LuggMapViewProps const>(_props);
149
319
 
150
320
  if (_providerType == LuggMapViewProvider::Apple) {
151
- _provider = [[AppleMapProvider alloc] init];
321
+ _provider = _staticMode ? [[AppleStaticMapProvider alloc] init]
322
+ : [[AppleMapProvider alloc] init];
323
+ } else if (_staticMode) {
324
+ GoogleStaticMapProvider *google = [[GoogleStaticMapProvider alloc] init];
325
+ google.mapId = _mapId;
326
+ _provider = google;
152
327
  } else {
153
328
  GoogleMapProvider *google = [[GoogleMapProvider alloc] init];
154
329
  google.mapId = _mapId;
@@ -156,6 +331,7 @@ using namespace luggmaps::events;
156
331
  }
157
332
 
158
333
  _provider.delegate = self;
334
+ _provider.staticMode = _staticMode;
159
335
 
160
336
  CLLocationCoordinate2D coordinate =
161
337
  CLLocationCoordinate2DMake(viewProps.initialCoordinate.latitude,
@@ -165,6 +341,16 @@ using namespace luggmaps::events;
165
341
  initialCoordinate:coordinate
166
342
  initialZoom:viewProps.initialZoom];
167
343
 
344
+ // After initializeMapInView so the cache key reads the provider's camera;
345
+ // the base render is async and picks up the cached image
346
+ if (_staticMode) {
347
+ NSString *cacheKey = [self staticSnapshotCacheKey];
348
+ if (cacheKey) {
349
+ ((StaticMapProviderBase *)_provider).cachedBaseImage =
350
+ [StaticSnapshotCache() objectForKey:cacheKey];
351
+ }
352
+ }
353
+
168
354
  // Apply cached props after map view is created
169
355
  [self applyProps];
170
356
  [_provider setEdgeInsets:_edgeInsets oldEdgeInsets:UIEdgeInsetsZero];
@@ -179,6 +365,12 @@ using namespace luggmaps::events;
179
365
  [_provider addPolylineView:(LuggPolylineView *)subview];
180
366
  } else if ([subview isKindOfClass:[LuggPolygonView class]]) {
181
367
  [_provider addPolygonView:(LuggPolygonView *)subview];
368
+ } else if ([subview isKindOfClass:[LuggCircleView class]]) {
369
+ [_provider addCircleView:(LuggCircleView *)subview];
370
+ } else if ([subview isKindOfClass:[LuggGroundOverlayView class]]) {
371
+ [_provider addGroundOverlayView:(LuggGroundOverlayView *)subview];
372
+ } else if ([subview isKindOfClass:[LuggTileOverlayView class]]) {
373
+ [_provider addTileOverlayView:(LuggTileOverlayView *)subview];
182
374
  }
183
375
  }
184
376
  }
@@ -189,6 +381,19 @@ using namespace luggmaps::events;
189
381
  ReadyEvent::emit<LuggMapViewEventEmitter>(_eventEmitter);
190
382
  }
191
383
 
384
+ - (void)mapProviderDidFinishStaticSnapshot {
385
+ _staticSnapshotDone = YES;
386
+ }
387
+
388
+ - (void)mapProviderDidCaptureStaticImage:(UIImage *)image {
389
+ NSString *key = [self staticSnapshotCacheKey];
390
+ if (key) {
391
+ NSUInteger cost = (NSUInteger)(image.size.width * image.scale *
392
+ image.size.height * image.scale * 4);
393
+ [StaticSnapshotCache() setObject:image forKey:key cost:cost];
394
+ }
395
+ }
396
+
192
397
  - (void)mapProviderDidMoveCamera:(double)latitude
193
398
  longitude:(double)longitude
194
399
  zoom:(double)zoom
@@ -228,10 +433,16 @@ using namespace luggmaps::events;
228
433
  [_provider setScrollEnabled:_scrollEnabled];
229
434
  [_provider setRotateEnabled:_rotateEnabled];
230
435
  [_provider setPitchEnabled:_pitchEnabled];
436
+ [_provider setCompassEnabled:_compassEnabled];
231
437
  [_provider setUserLocationEnabled:_userLocationEnabled];
232
- [_provider setTheme:(NSInteger)_theme];
438
+ [_provider setMapType:_mapType];
439
+ [_provider setTheme:_theme];
440
+ [_provider setPoiEnabled:_poiEnabled];
441
+ [_provider setPoiFilterMode:_poiFilterMode];
442
+ [_provider setPoiFilterCategories:_poiFilterCategories];
233
443
  [_provider setMinZoom:_minZoom];
234
444
  [_provider setMaxZoom:_maxZoom];
445
+ [_provider setInsetAdjustment:_insetAdjustment];
235
446
  }
236
447
 
237
448
  - (void)updateProps:(Props::Shared const &)props
@@ -242,6 +453,12 @@ using namespace luggmaps::events;
242
453
  *std::static_pointer_cast<LuggMapViewProps const>(_props);
243
454
 
244
455
  _providerType = newViewProps.provider;
456
+ _staticMode = newViewProps.staticMode;
457
+
458
+ NSString *newStaticKey =
459
+ [NSString stringWithUTF8String:newViewProps.staticKey.c_str()];
460
+ BOOL staticKeyChanged = ![newStaticKey isEqualToString:_staticKey];
461
+ _staticKey = newStaticKey;
245
462
 
246
463
  NSString *newMapId =
247
464
  [NSString stringWithUTF8String:newViewProps.mapId.c_str()];
@@ -265,10 +482,34 @@ using namespace luggmaps::events;
265
482
  _pitchEnabled = newViewProps.pitchEnabled;
266
483
  [_provider setPitchEnabled:_pitchEnabled];
267
484
  }
485
+ if (newViewProps.compassEnabled != prevViewProps.compassEnabled) {
486
+ _compassEnabled = newViewProps.compassEnabled;
487
+ [_provider setCompassEnabled:_compassEnabled];
488
+ }
268
489
  if (newViewProps.userLocationEnabled != prevViewProps.userLocationEnabled) {
269
490
  _userLocationEnabled = newViewProps.userLocationEnabled;
270
491
  [_provider setUserLocationEnabled:_userLocationEnabled];
271
492
  }
493
+ if (newViewProps.poiEnabled != prevViewProps.poiEnabled) {
494
+ _poiEnabled = newViewProps.poiEnabled;
495
+ [_provider setPoiEnabled:_poiEnabled];
496
+ }
497
+ if (newViewProps.poiFilterMode != prevViewProps.poiFilterMode) {
498
+ _poiFilterMode = newViewProps.poiFilterMode;
499
+ [_provider setPoiFilterMode:_poiFilterMode];
500
+ }
501
+ if (newViewProps.poiFilterCategories != prevViewProps.poiFilterCategories) {
502
+ NSMutableArray<NSString *> *categories = [NSMutableArray array];
503
+ for (const auto &category : newViewProps.poiFilterCategories) {
504
+ [categories addObject:[NSString stringWithUTF8String:category.c_str()]];
505
+ }
506
+ _poiFilterCategories = [categories copy];
507
+ [_provider setPoiFilterCategories:_poiFilterCategories];
508
+ }
509
+ if (newViewProps.mapType != prevViewProps.mapType) {
510
+ _mapType = newViewProps.mapType;
511
+ [_provider setMapType:_mapType];
512
+ }
272
513
  if (newViewProps.minZoom != prevViewProps.minZoom) {
273
514
  _minZoom = newViewProps.minZoom;
274
515
  [_provider setMinZoom:_minZoom];
@@ -279,7 +520,11 @@ using namespace luggmaps::events;
279
520
  }
280
521
  if (newViewProps.theme != prevViewProps.theme) {
281
522
  _theme = newViewProps.theme;
282
- [_provider setTheme:(NSInteger)_theme];
523
+ [_provider setTheme:_theme];
524
+ }
525
+ if (newViewProps.insetAdjustment != prevViewProps.insetAdjustment) {
526
+ _insetAdjustment = newViewProps.insetAdjustment;
527
+ [_provider setInsetAdjustment:_insetAdjustment];
283
528
  }
284
529
 
285
530
  UIEdgeInsets newEdgeInsets = UIEdgeInsetsMake(
@@ -292,6 +537,11 @@ using namespace luggmaps::events;
292
537
  }
293
538
 
294
539
  [super updateProps:props oldProps:oldProps];
540
+
541
+ // After super so the snapshot cache key sees the new camera props
542
+ if (staticKeyChanged && _staticMode && _mapWrapperView) {
543
+ [self resetStaticContent];
544
+ }
295
545
  }
296
546
 
297
547
  #pragma mark - Commands
@@ -4,6 +4,11 @@
4
4
  NS_ASSUME_NONNULL_BEGIN
5
5
 
6
6
  @interface LuggMapWrapperView : RCTViewComponentView
7
+
8
+ /// Called after layout; lets the static snapshotter start (or restart)
9
+ /// once the wrapper has a real size, since a snapshot can't autoresize
10
+ @property(nonatomic, copy, nullable) void (^layoutHandler)(void);
11
+
7
12
  @end
8
13
 
9
14
  NS_ASSUME_NONNULL_END
@@ -29,6 +29,24 @@ using namespace facebook::react;
29
29
  return self;
30
30
  }
31
31
 
32
+ - (void)layoutSubviews {
33
+ [super layoutSubviews];
34
+ if (self.layoutHandler) {
35
+ self.layoutHandler();
36
+ }
37
+ }
38
+
39
+ - (void)prepareForRecycle {
40
+ [super prepareForRecycle];
41
+
42
+ // Static placeholder styling is applied outside of props
43
+ // (LuggMapView applyStaticPlaceholderBackground) and would otherwise
44
+ // leak into the next map that reuses this recycled view
45
+ self.backgroundColor = nil;
46
+ self.overrideUserInterfaceStyle = UIUserInterfaceStyleUnspecified;
47
+ self.layoutHandler = nil;
48
+ }
49
+
32
50
  Class<RCTComponentViewProtocol> LuggMapWrapperViewCls(void) {
33
51
  return LuggMapWrapperView.class;
34
52
  }
@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN
11
11
  @optional
12
12
  - (void)markerViewDidLayout:(LuggMarkerView *)markerView;
13
13
  - (void)markerViewDidUpdate:(LuggMarkerView *)markerView;
14
+ - (void)showCalloutForMarkerView:(LuggMarkerView *)markerView;
15
+ - (void)hideCalloutForMarkerView:(LuggMarkerView *)markerView;
14
16
  @end
15
17
 
16
18
  @interface LuggMarkerView : RCTViewComponentView
@@ -24,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
24
26
  @property(nonatomic, readonly) CLLocationDegrees rotate;
25
27
  @property(nonatomic, readonly) CGFloat scale;
26
28
  @property(nonatomic, readonly) BOOL rasterize;
29
+ @property(nonatomic, readonly) BOOL centerOnPress;
27
30
  @property(nonatomic, readonly) BOOL draggable;
28
31
  @property(nonatomic, readonly) BOOL hasCustomView;
29
32
  @property(nonatomic, readonly) BOOL didLayout;
@@ -26,6 +26,7 @@ using namespace luggmaps::events;
26
26
  CLLocationDegrees _rotate;
27
27
  CGFloat _scale;
28
28
  BOOL _rasterize;
29
+ BOOL _centerOnPress;
29
30
  BOOL _draggable;
30
31
  BOOL _didLayout;
31
32
  UIView *_iconView;
@@ -49,6 +50,7 @@ using namespace luggmaps::events;
49
50
  _rotate = 0;
50
51
  _scale = 1;
51
52
  _rasterize = YES;
53
+ _centerOnPress = YES;
52
54
  _didLayout = NO;
53
55
 
54
56
  _iconView = [[UIView alloc] init];
@@ -78,6 +80,7 @@ using namespace luggmaps::events;
78
80
  _rotate = newViewProps.rotate;
79
81
  _scale = newViewProps.scale;
80
82
  _rasterize = newViewProps.rasterize;
83
+ _centerOnPress = newViewProps.centerOnPress;
81
84
  _draggable = newViewProps.draggable;
82
85
  }
83
86
 
@@ -174,6 +177,10 @@ using namespace luggmaps::events;
174
177
  return _rasterize;
175
178
  }
176
179
 
180
+ - (BOOL)centerOnPress {
181
+ return _centerOnPress;
182
+ }
183
+
177
184
  - (BOOL)draggable {
178
185
  return _draggable;
179
186
  }
@@ -280,6 +287,24 @@ using namespace luggmaps::events;
280
287
  _iconView.layer.anchorPoint = CGPointMake(0.5, 0.5);
281
288
  _iconView.frame = CGRectMake(0, 0, _iconView.bounds.size.width,
282
289
  _iconView.bounds.size.height);
290
+ // Static providers hide the icon view until positioned
291
+ _iconView.hidden = NO;
292
+ }
293
+
294
+ - (void)showCallout {
295
+ if ([self.delegate respondsToSelector:@selector(showCalloutForMarkerView:)]) {
296
+ [self.delegate showCalloutForMarkerView:self];
297
+ }
298
+ }
299
+
300
+ - (void)hideCallout {
301
+ if ([self.delegate respondsToSelector:@selector(hideCalloutForMarkerView:)]) {
302
+ [self.delegate hideCalloutForMarkerView:self];
303
+ }
304
+ }
305
+
306
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
307
+ RCTLuggMarkerViewHandleCommand(self, commandName, args);
283
308
  }
284
309
 
285
310
  - (void)prepareForRecycle {
@@ -0,0 +1,31 @@
1
+ #import <CoreLocation/CoreLocation.h>
2
+ #import <React/RCTViewComponentView.h>
3
+ #import <UIKit/UIKit.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @class LuggTileOverlayView;
8
+
9
+ @protocol LuggTileOverlayViewDelegate <NSObject>
10
+ @optional
11
+ - (void)tileOverlayViewDidUpdate:(LuggTileOverlayView *)tileOverlayView;
12
+ @end
13
+
14
+ @interface LuggTileOverlayView : RCTViewComponentView
15
+
16
+ @property(nonatomic, readonly) NSString *urlTemplate;
17
+ @property(nonatomic, readonly) NSInteger tileSize;
18
+ @property(nonatomic, readonly) CGFloat opacity;
19
+ @property(nonatomic, readonly) BOOL hasBounds;
20
+ @property(nonatomic, readonly) CLLocationCoordinate2D northeast;
21
+ @property(nonatomic, readonly) CLLocationCoordinate2D southwest;
22
+ @property(nonatomic, readonly) NSInteger zIndex;
23
+ @property(nonatomic, readonly) BOOL tappable;
24
+ @property(nonatomic, weak, nullable) id<LuggTileOverlayViewDelegate> delegate;
25
+ @property(nonatomic, strong, nullable) NSObject *overlay;
26
+
27
+ - (void)emitPressEvent;
28
+
29
+ @end
30
+
31
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,130 @@
1
+ #import "LuggTileOverlayView.h"
2
+ #import "events/TileOverlayPressEvent.h"
3
+
4
+ #import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
5
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
6
+ #import <react/renderer/components/RNMapsSpec/Props.h>
7
+ #import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
8
+
9
+ #import "RCTFabricComponentsPlugins.h"
10
+
11
+ using namespace facebook::react;
12
+ using namespace luggmaps::events;
13
+
14
+ @interface LuggTileOverlayView () <RCTLuggTileOverlayViewViewProtocol>
15
+ @end
16
+
17
+ @implementation LuggTileOverlayView {
18
+ NSString *_urlTemplate;
19
+ NSInteger _tileSize;
20
+ CGFloat _opacity;
21
+ BOOL _hasBounds;
22
+ CLLocationCoordinate2D _northeast;
23
+ CLLocationCoordinate2D _southwest;
24
+ NSInteger _zIndex;
25
+ BOOL _tappable;
26
+ }
27
+
28
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
29
+ return concreteComponentDescriptorProvider<
30
+ LuggTileOverlayViewComponentDescriptor>();
31
+ }
32
+
33
+ - (instancetype)initWithFrame:(CGRect)frame {
34
+ if (self = [super initWithFrame:frame]) {
35
+ static const auto defaultProps =
36
+ std::make_shared<const LuggTileOverlayViewProps>();
37
+ _props = defaultProps;
38
+
39
+ _urlTemplate = @"";
40
+ _tileSize = 256;
41
+ _opacity = 1.0;
42
+
43
+ self.hidden = YES;
44
+ }
45
+
46
+ return self;
47
+ }
48
+
49
+ - (void)updateProps:(Props::Shared const &)props
50
+ oldProps:(Props::Shared const &)oldProps {
51
+ [super updateProps:props oldProps:oldProps];
52
+ const auto &newViewProps =
53
+ *std::static_pointer_cast<LuggTileOverlayViewProps const>(props);
54
+
55
+ _urlTemplate =
56
+ [NSString stringWithUTF8String:newViewProps.urlTemplate.c_str()];
57
+ _tileSize = newViewProps.tileSize;
58
+ _opacity = newViewProps.opacity;
59
+
60
+ auto ne = newViewProps.bounds.northeast;
61
+ auto sw = newViewProps.bounds.southwest;
62
+ _hasBounds = (ne.latitude != 0 || ne.longitude != 0 || sw.latitude != 0 ||
63
+ sw.longitude != 0);
64
+ if (_hasBounds) {
65
+ _northeast = CLLocationCoordinate2DMake(ne.latitude, ne.longitude);
66
+ _southwest = CLLocationCoordinate2DMake(sw.latitude, sw.longitude);
67
+ }
68
+
69
+ _zIndex = newViewProps.zIndex.value_or(0);
70
+ _tappable = newViewProps.tappable;
71
+ }
72
+
73
+ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask {
74
+ [super finalizeUpdates:updateMask];
75
+
76
+ if (updateMask & RNComponentViewUpdateMaskProps) {
77
+ if ([self.delegate
78
+ respondsToSelector:@selector(tileOverlayViewDidUpdate:)]) {
79
+ [self.delegate tileOverlayViewDidUpdate:self];
80
+ }
81
+ }
82
+ }
83
+
84
+ - (NSString *)urlTemplate {
85
+ return _urlTemplate;
86
+ }
87
+
88
+ - (NSInteger)tileSize {
89
+ return _tileSize;
90
+ }
91
+
92
+ - (CGFloat)opacity {
93
+ return _opacity;
94
+ }
95
+
96
+ - (BOOL)hasBounds {
97
+ return _hasBounds;
98
+ }
99
+
100
+ - (CLLocationCoordinate2D)northeast {
101
+ return _northeast;
102
+ }
103
+
104
+ - (CLLocationCoordinate2D)southwest {
105
+ return _southwest;
106
+ }
107
+
108
+ - (NSInteger)zIndex {
109
+ return _zIndex;
110
+ }
111
+
112
+ - (BOOL)tappable {
113
+ return _tappable;
114
+ }
115
+
116
+ - (void)emitPressEvent {
117
+ TileOverlayPressEvent::emit<LuggTileOverlayViewEventEmitter>(_eventEmitter);
118
+ }
119
+
120
+ - (void)prepareForRecycle {
121
+ [super prepareForRecycle];
122
+ self.overlay = nil;
123
+ self.delegate = nil;
124
+ }
125
+
126
+ Class<RCTComponentViewProtocol> LuggTileOverlayViewCls(void) {
127
+ return LuggTileOverlayView.class;
128
+ }
129
+
130
+ @end
@@ -3,6 +3,14 @@
3
3
 
4
4
  NS_ASSUME_NONNULL_BEGIN
5
5
 
6
+ /// POI filter for the given props, shared by the live map view and the
7
+ /// static snapshotter. Returns nil for "show everything".
8
+ MKPointOfInterestFilter *_Nullable LuggPointOfInterestFilter(
9
+ BOOL poiEnabled, facebook::react::LuggMapViewPoiFilterMode filterMode,
10
+ NSArray<NSString *> *filterCategories);
11
+
12
+ MKMapType LuggMKMapTypeFromMapType(facebook::react::LuggMapViewMapType mapType);
13
+
6
14
  @interface LuggAppleMapViewContent : MKMapView
7
15
  @end
8
16