@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
@@ -0,0 +1,213 @@
1
+ #import "GoogleStaticMapProvider.h"
2
+
3
+ #import "../extensions/UIView+Snapshot.h"
4
+ #import "GoogleMapProvider.h"
5
+
6
+ // Static maps churn through GMSMapViews as list rows warm up, and both
7
+ // creating one and tearing one down are expensive main-thread operations
8
+ // (renderer setup/teardown). Detached map views are pooled and reused so a
9
+ // warmup usually only costs a camera move plus tile load. Keyed by map ID,
10
+ // which is fixed at GMSMapView creation.
11
+ static const NSUInteger kStaticMapViewPoolCapacity = 6;
12
+
13
+ static NSMutableDictionary<NSString *, NSMutableArray<GMSMapView *> *> *
14
+ StaticMapViewPool(void) {
15
+ static NSMutableDictionary<NSString *, NSMutableArray<GMSMapView *> *> *pool;
16
+ static dispatch_once_t onceToken;
17
+ dispatch_once(&onceToken, ^{
18
+ pool = [NSMutableDictionary dictionary];
19
+ [[NSNotificationCenter defaultCenter]
20
+ addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
21
+ object:nil
22
+ queue:[NSOperationQueue mainQueue]
23
+ usingBlock:^(NSNotification *notification) {
24
+ [pool removeAllObjects];
25
+ }];
26
+ });
27
+ return pool;
28
+ }
29
+
30
+ static GMSMapView *_Nullable DequeueStaticMapView(NSString *mapId) {
31
+ NSMutableArray<GMSMapView *> *views = StaticMapViewPool()[mapId];
32
+ GMSMapView *mapView = views.lastObject;
33
+ [views removeLastObject];
34
+ return mapView;
35
+ }
36
+
37
+ static void EnqueueStaticMapView(NSString *mapId, GMSMapView *mapView) {
38
+ NSMutableDictionary<NSString *, NSMutableArray<GMSMapView *> *> *pool =
39
+ StaticMapViewPool();
40
+ NSMutableArray<GMSMapView *> *views = pool[mapId];
41
+ if (!views) {
42
+ views = [NSMutableArray array];
43
+ pool[mapId] = views;
44
+ }
45
+ if (views.count >= kStaticMapViewPoolCapacity)
46
+ return;
47
+ [views addObject:mapView];
48
+ }
49
+
50
+ @implementation GoogleStaticMapProvider {
51
+ GMSMapView *_warmupMapView;
52
+ BOOL _tilesRendered;
53
+ BOOL _swapScheduled;
54
+ }
55
+
56
+ #pragma mark - StaticMapProviderBase
57
+
58
+ - (MKMapRect)mapRectForSize:(CGSize)size {
59
+ // GMSMapView shows the world 256 * 2^zoom points wide, centered on the
60
+ // camera target - the same web mercator MKMapPoint uses, so overlays
61
+ // project onto the warmed-up tiles exactly
62
+ double zoom =
63
+ MAX(MIN(self.zoom, (double)kGMSMaxZoomLevel), (double)kGMSMinZoomLevel);
64
+ double mapPointsPerPoint = MKMapSizeWorld.width / (256.0 * pow(2.0, zoom));
65
+ double width = size.width * mapPointsPerPoint;
66
+ double height = size.height * mapPointsPerPoint;
67
+ MKMapPoint center = MKMapPointForCoordinate(self.coordinate);
68
+ return MKMapRectMake(center.x - width / 2, center.y - height / 2, width,
69
+ height);
70
+ }
71
+
72
+ - (UIView *)newDefaultMarkerOverlayView {
73
+ return
74
+ [[UIImageView alloc] initWithImage:[GMSMarker markerImageWithColor:nil]];
75
+ }
76
+
77
+ // Inverse of mapRectForSize
78
+ - (double)zoomForMapPointsPerPoint:(double)mapPointsPerPoint {
79
+ double zoom = log2(MKMapSizeWorld.width / (256.0 * mapPointsPerPoint));
80
+ return MAX(MIN(zoom, (double)kGMSMaxZoomLevel), (double)kGMSMinZoomLevel);
81
+ }
82
+
83
+ - (void)renderBaseMap {
84
+ if (_warmupMapView) {
85
+ // Retry a swap that couldn't run (e.g. view was off-window)
86
+ [self scheduleSwap];
87
+ return;
88
+ }
89
+ [self startWarmup];
90
+ }
91
+
92
+ - (void)cancelBaseRender {
93
+ [self destroyWarmupMapView];
94
+ }
95
+
96
+ #pragma mark - Warmup
97
+
98
+ // "" resolves to the demo map ID (see LuggGMSMapIDFromString), so both
99
+ // share a pool bucket
100
+ - (NSString *)poolKey {
101
+ return self.mapId.length > 0 ? self.mapId : @"DEMO_MAP_ID";
102
+ }
103
+
104
+ - (void)startWarmup {
105
+ UIView *wrapperView = self.wrapperView;
106
+ if (!wrapperView)
107
+ return;
108
+
109
+ _tilesRendered = NO;
110
+
111
+ GMSMapView *mapView = DequeueStaticMapView([self poolKey]);
112
+ if (mapView) {
113
+ mapView.frame = wrapperView.bounds;
114
+ [mapView moveCamera:[GMSCameraUpdate setTarget:self.coordinate
115
+ zoom:(float)self.zoom]];
116
+ } else {
117
+ GMSCameraPosition *camera =
118
+ [GMSCameraPosition cameraWithLatitude:self.coordinate.latitude
119
+ longitude:self.coordinate.longitude
120
+ zoom:(float)self.zoom];
121
+
122
+ GMSMapViewOptions *options = [[GMSMapViewOptions alloc] init];
123
+ options.frame = wrapperView.bounds;
124
+ options.camera = camera;
125
+ options.mapID = LuggGMSMapIDFromString(self.mapId);
126
+
127
+ mapView = [[GMSMapView alloc] initWithOptions:options];
128
+ }
129
+
130
+ mapView.autoresizingMask =
131
+ UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
132
+ mapView.delegate = self;
133
+ mapView.userInteractionEnabled = NO;
134
+ mapView.preferredFrameRate = kGMSFrameRateConservative;
135
+ mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever;
136
+ // Frames the camera like the inset-shifted projection (and clears stale
137
+ // padding on pooled views)
138
+ mapView.padding = self.edgeInsets;
139
+ mapView.mapType = LuggGMSMapTypeFromMapType(self.mapType);
140
+ mapView.overrideUserInterfaceStyle = LuggInterfaceStyleFromTheme(self.theme);
141
+
142
+ _warmupMapView = mapView;
143
+ [wrapperView insertSubview:mapView atIndex:0];
144
+ }
145
+
146
+ - (void)destroyWarmupMapView {
147
+ if (!_warmupMapView)
148
+ return;
149
+ _warmupMapView.delegate = nil;
150
+ [_warmupMapView removeFromSuperview];
151
+ EnqueueStaticMapView([self poolKey], _warmupMapView);
152
+ _warmupMapView = nil;
153
+ }
154
+
155
+ // Once tiles are fully rendered, swap the live map with its image and
156
+ // release the map view. The image render and map teardown are expensive,
157
+ // so they run outside scroll tracking; the live map keeps displaying (and
158
+ // loading tiles) until then.
159
+ - (void)scheduleSwap {
160
+ if (_swapScheduled || !_warmupMapView || !_tilesRendered ||
161
+ !_warmupMapView.window)
162
+ return;
163
+
164
+ _swapScheduled = YES;
165
+ __weak GoogleStaticMapProvider *weakSelf = self;
166
+ [[NSRunLoop mainRunLoop] performInModes:@[ NSDefaultRunLoopMode ]
167
+ block:^{
168
+ GoogleStaticMapProvider *strongSelf =
169
+ weakSelf;
170
+ if (!strongSelf)
171
+ return;
172
+ strongSelf->_swapScheduled = NO;
173
+ [strongSelf performSwap];
174
+ }];
175
+ }
176
+
177
+ - (void)performSwap {
178
+ // Tiles can invalidate (or the view leave the window) between scheduling
179
+ // and this runloop pass; the next tile callback or resumeAnimations
180
+ // retries
181
+ if (!_warmupMapView || !_tilesRendered || !_warmupMapView.window)
182
+ return;
183
+
184
+ UIImageView *imageView = [_warmupMapView lugg_snapshotImageView];
185
+ if (!imageView)
186
+ return;
187
+
188
+ [self displayBaseImage:imageView.image fromCache:NO];
189
+ [self destroyWarmupMapView];
190
+ }
191
+
192
+ #pragma mark - GMSMapViewDelegate
193
+
194
+ - (void)mapViewDidStartTileRendering:(GMSMapView *)mapView {
195
+ _tilesRendered = NO;
196
+ }
197
+
198
+ - (void)mapViewDidFinishTileRendering:(GMSMapView *)mapView {
199
+ _tilesRendered = YES;
200
+ // The visible warmup map is fully rendered; show the overlays now
201
+ // instead of waiting for the swap, which is deferred while scrolling
202
+ [self revealOverlays];
203
+ [self scheduleSwap];
204
+ }
205
+
206
+ - (void)mapViewSnapshotReady:(GMSMapView *)mapView {
207
+ // Stable per the SDK: tiles loaded, labels and overlays rendered
208
+ _tilesRendered = YES;
209
+ [self revealOverlays];
210
+ [self scheduleSwap];
211
+ }
212
+
213
+ @end
@@ -35,8 +35,9 @@
35
35
  NSArray<NSNumber *> *_cumulativeDistances;
36
36
  CGFloat _totalLength;
37
37
  CGColorSpaceRef _colorSpace;
38
- RGBAComponents *_colorCache;
39
- NSUInteger _colorCacheCount;
38
+ // Immutable snapshot read by background draws; rebuilt (never mutated)
39
+ // on the main thread
40
+ NSData *_colorCacheData;
40
41
  }
41
42
 
42
43
  - (id)initWithPolyline:(MKPolyline *)polyline {
@@ -61,7 +62,6 @@
61
62
 
62
63
  - (void)dealloc {
63
64
  [self stopAnimation];
64
- free(_colorCache);
65
65
  if (_colorSpace) {
66
66
  CGColorSpaceRelease(_colorSpace);
67
67
  _colorSpace = NULL;
@@ -123,20 +123,25 @@
123
123
  _displayLink.paused = NO;
124
124
  }
125
125
 
126
- - (NSUInteger)indexForDistance:(CGFloat)distance {
126
+ - (NSUInteger)indexForDistance:(CGFloat)distance
127
+ inDistances:(NSArray<NSNumber *> *)distances {
128
+ if (distances.count < 2) {
129
+ return 0;
130
+ }
131
+
127
132
  NSUInteger left = 0;
128
- NSUInteger right = _cumulativeDistances.count - 1;
133
+ NSUInteger right = distances.count - 1;
129
134
 
130
135
  while (left < right) {
131
136
  NSUInteger mid = (left + right + 1) / 2;
132
- if (_cumulativeDistances[mid].doubleValue <= distance) {
137
+ if (distances[mid].doubleValue <= distance) {
133
138
  left = mid;
134
139
  } else {
135
140
  right = mid - 1;
136
141
  }
137
142
  }
138
143
 
139
- return MIN(left, _cumulativeDistances.count - 2);
144
+ return MIN(left, distances.count - 2);
140
145
  }
141
146
 
142
147
  - (CGFloat)applyEasing:(CGFloat)t {
@@ -208,44 +213,52 @@
208
213
  }
209
214
 
210
215
  - (void)rebuildColorCache {
211
- free(_colorCache);
212
- _colorCache = NULL;
213
- _colorCacheCount = _strokeColors.count;
214
- if (_colorCacheCount == 0) return;
215
-
216
- _colorCache = (RGBAComponents *)malloc(sizeof(RGBAComponents) * _colorCacheCount);
217
- for (NSUInteger i = 0; i < _colorCacheCount; i++) {
218
- [_strokeColors[i] getRed:&_colorCache[i].r
219
- green:&_colorCache[i].g
220
- blue:&_colorCache[i].b
221
- alpha:&_colorCache[i].a];
216
+ NSUInteger count = _strokeColors.count;
217
+ if (count == 0) {
218
+ _colorCacheData = nil;
219
+ return;
220
+ }
221
+
222
+ NSMutableData *data =
223
+ [NSMutableData dataWithLength:sizeof(RGBAComponents) * count];
224
+ RGBAComponents *cache = (RGBAComponents *)data.mutableBytes;
225
+ for (NSUInteger i = 0; i < count; i++) {
226
+ [_strokeColors[i] getRed:&cache[i].r
227
+ green:&cache[i].g
228
+ blue:&cache[i].b
229
+ alpha:&cache[i].a];
222
230
  }
231
+ _colorCacheData = data;
223
232
  }
224
233
 
225
234
  - (void)colorAtGradientPosition:(CGFloat)position rgba:(RGBAComponents *)out {
226
- if (_colorCacheCount == 0) {
235
+ NSData *cacheData = _colorCacheData;
236
+ const RGBAComponents *cache = (const RGBAComponents *)cacheData.bytes;
237
+ NSUInteger count = cacheData.length / sizeof(RGBAComponents);
238
+
239
+ if (count == 0) {
227
240
  CGFloat r, g, b, a;
228
241
  [self.strokeColor getRed:&r green:&g blue:&b alpha:&a];
229
242
  *out = (RGBAComponents){r, g, b, a};
230
243
  return;
231
244
  }
232
- if (_colorCacheCount == 1) {
233
- *out = _colorCache[0];
245
+ if (count == 1) {
246
+ *out = cache[0];
234
247
  return;
235
248
  }
236
249
 
237
250
  position = MAX(0, MIN(1, position));
238
- CGFloat scaledPos = position * (_colorCacheCount - 1);
251
+ CGFloat scaledPos = position * (count - 1);
239
252
  NSUInteger index = (NSUInteger)scaledPos;
240
253
  CGFloat t = scaledPos - index;
241
254
 
242
- if (index >= _colorCacheCount - 1) {
243
- *out = _colorCache[_colorCacheCount - 1];
255
+ if (index >= count - 1) {
256
+ *out = cache[count - 1];
244
257
  return;
245
258
  }
246
259
 
247
- RGBAComponents c1 = _colorCache[index];
248
- RGBAComponents c2 = _colorCache[index + 1];
260
+ RGBAComponents c1 = cache[index];
261
+ RGBAComponents c2 = cache[index + 1];
249
262
  out->r = c1.r + (c2.r - c1.r) * t;
250
263
  out->g = c1.g + (c2.g - c1.g) * t;
251
264
  out->b = c1.b + (c2.b - c1.b) * t;
@@ -272,13 +285,20 @@
272
285
  CGContextSetLineCap(context, self.lineCap);
273
286
  CGContextSetLineJoin(context, self.lineJoin);
274
287
 
275
- NSUInteger segmentCount = _polyline.pointCount - 1;
276
- if (segmentCount == 0) {
288
+ // drawMapRect: runs on MapKit's background render queues while
289
+ // updatePolyline: swaps these ivars on the main thread; snapshot them so
290
+ // one frame draws from a consistent pair
291
+ MKPolyline *polyline = _polyline;
292
+ NSArray<NSNumber *> *distances = _cumulativeDistances;
293
+ CGFloat totalLength = _totalLength;
294
+
295
+ if (polyline.pointCount < 2) {
277
296
  return;
278
297
  }
298
+ NSUInteger segmentCount = polyline.pointCount - 1;
279
299
 
280
300
  // Snake animation: grow from start, then shrink from start
281
- if (_animated && _polyline.pointCount > 1 && _totalLength > 0) {
301
+ if (_animated && distances.count > 1 && totalLength > 0) {
282
302
  CGFloat trailLength = MAX(0.01, MIN(1.0, _animatedOptions.trailLength));
283
303
  CGFloat maxProgress = (trailLength < 1.0) ? 1.0 : 2.0;
284
304
  CGFloat progress = MIN(_animationProgress, maxProgress);
@@ -287,15 +307,15 @@
287
307
  CGFloat headDist, tailDist;
288
308
 
289
309
  if (trailLength < 1.0) {
290
- headDist = easedProgress * _totalLength;
291
- tailDist = MAX(0, headDist - _totalLength * trailLength);
310
+ headDist = easedProgress * totalLength;
311
+ tailDist = MAX(0, headDist - totalLength * trailLength);
292
312
  } else if (easedProgress <= 1.0) {
293
313
  tailDist = 0;
294
- headDist = easedProgress * _totalLength;
314
+ headDist = easedProgress * totalLength;
295
315
  } else {
296
316
  CGFloat shrinkProgress = easedProgress - 1.0;
297
- tailDist = shrinkProgress * _totalLength;
298
- headDist = _totalLength;
317
+ tailDist = shrinkProgress * totalLength;
318
+ headDist = totalLength;
299
319
  }
300
320
 
301
321
  if (headDist <= tailDist) {
@@ -303,19 +323,23 @@
303
323
  }
304
324
 
305
325
  CGFloat visibleLength = headDist - tailDist;
306
- NSUInteger startIndex = [self indexForDistance:tailDist];
307
- NSUInteger endIndex = [self indexForDistance:headDist];
326
+ NSUInteger startIndex = [self indexForDistance:tailDist inDistances:distances];
327
+ NSUInteger endIndex = [self indexForDistance:headDist inDistances:distances];
328
+
329
+ // distances may lag behind the polyline (or vice versa) for a frame;
330
+ // never index past either
331
+ NSUInteger maxSegment = MIN(segmentCount, distances.count - 1);
308
332
 
309
- for (NSUInteger i = startIndex; i <= endIndex && i < segmentCount; i++) {
310
- CGFloat segStartDist = _cumulativeDistances[i].doubleValue;
311
- CGFloat segEndDist = _cumulativeDistances[i + 1].doubleValue;
333
+ for (NSUInteger i = startIndex; i <= endIndex && i < maxSegment; i++) {
334
+ CGFloat segStartDist = distances[i].doubleValue;
335
+ CGFloat segEndDist = distances[i + 1].doubleValue;
312
336
 
313
337
  if (segEndDist <= tailDist || segStartDist >= headDist) {
314
338
  continue;
315
339
  }
316
340
 
317
- CGPoint segStart = [self pointForMapPoint:_polyline.points[i]];
318
- CGPoint segEnd = [self pointForMapPoint:_polyline.points[i + 1]];
341
+ CGPoint segStart = [self pointForMapPoint:polyline.points[i]];
342
+ CGPoint segEnd = [self pointForMapPoint:polyline.points[i + 1]];
319
343
 
320
344
  CGPoint drawStart = segStart;
321
345
  CGPoint drawEnd = segEnd;
@@ -368,8 +392,8 @@
368
392
  // Static gradient rendering
369
393
  if (_strokeColors && _strokeColors.count > 1) {
370
394
  for (NSUInteger i = 0; i < segmentCount; i++) {
371
- CGPoint startPoint = [self pointForMapPoint:_polyline.points[i]];
372
- CGPoint endPoint = [self pointForMapPoint:_polyline.points[i + 1]];
395
+ CGPoint startPoint = [self pointForMapPoint:polyline.points[i]];
396
+ CGPoint endPoint = [self pointForMapPoint:polyline.points[i + 1]];
373
397
 
374
398
  CGFloat gradientStart = (CGFloat)i / segmentCount;
375
399
  CGFloat gradientEnd = (CGFloat)(i + 1) / segmentCount;
@@ -1,14 +1,20 @@
1
1
  #import <CoreLocation/CoreLocation.h>
2
2
  #import <UIKit/UIKit.h>
3
+ #import <react/renderer/components/RNMapsSpec/Props.h>
3
4
 
4
5
  NS_ASSUME_NONNULL_BEGIN
5
6
 
6
7
  @class LuggMarkerView;
7
8
  @class LuggPolylineView;
8
9
  @class LuggPolygonView;
10
+ @class LuggCircleView;
11
+ @class LuggGroundOverlayView;
12
+ @class LuggTileOverlayView;
9
13
 
10
14
  @protocol MapProviderDelegate <NSObject>
11
15
  - (void)mapProviderDidReady;
16
+ - (void)mapProviderDidFinishStaticSnapshot;
17
+ - (void)mapProviderDidCaptureStaticImage:(UIImage *)image;
12
18
  - (void)mapProviderDidMoveCamera:(double)latitude
13
19
  longitude:(double)longitude
14
20
  zoom:(double)zoom
@@ -32,18 +38,26 @@ NS_ASSUME_NONNULL_BEGIN
32
38
  @property(nonatomic, weak, nullable) id<MapProviderDelegate> delegate;
33
39
  @property(nonatomic, readonly) BOOL isMapReady;
34
40
 
41
+ // Non-interactive static map. Must be set before initializeMapInView.
42
+ // Once fully rendered, the live map is replaced with a snapshot image.
43
+ @property(nonatomic, assign) BOOL staticMode;
44
+
35
45
  - (void)initializeMapInView:(UIView *)wrapperView
36
46
  initialCoordinate:(CLLocationCoordinate2D)coordinate
37
47
  initialZoom:(double)zoom;
38
48
  - (void)destroy;
39
49
 
40
50
  // Props
51
+ - (void)setInsetAdjustment:
52
+ (facebook::react::LuggMapViewInsetAdjustment)insetAdjustment;
41
53
  - (void)setZoomEnabled:(BOOL)enabled;
42
54
  - (void)setScrollEnabled:(BOOL)enabled;
43
55
  - (void)setRotateEnabled:(BOOL)enabled;
44
56
  - (void)setPitchEnabled:(BOOL)enabled;
57
+ - (void)setCompassEnabled:(BOOL)enabled;
45
58
  - (void)setUserLocationEnabled:(BOOL)enabled;
46
- - (void)setTheme:(NSInteger)theme;
59
+ - (void)setMapType:(facebook::react::LuggMapViewMapType)mapType;
60
+ - (void)setTheme:(facebook::react::LuggMapViewTheme)theme;
47
61
  - (void)setMinZoom:(double)minZoom;
48
62
  - (void)setMaxZoom:(double)maxZoom;
49
63
  - (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
@@ -51,6 +65,9 @@ NS_ASSUME_NONNULL_BEGIN
51
65
  - (void)setEdgeInsets:(UIEdgeInsets)edgeInsets
52
66
  oldEdgeInsets:(UIEdgeInsets)oldEdgeInsets
53
67
  duration:(double)duration;
68
+ - (void)setPoiEnabled:(BOOL)enabled;
69
+ - (void)setPoiFilterMode:(facebook::react::LuggMapViewPoiFilterMode)mode;
70
+ - (void)setPoiFilterCategories:(NSArray<NSString *> *)categories;
54
71
 
55
72
  // Children
56
73
  - (void)addMarkerView:(LuggMarkerView *)markerView;
@@ -62,6 +79,15 @@ NS_ASSUME_NONNULL_BEGIN
62
79
  - (void)addPolygonView:(LuggPolygonView *)polygonView;
63
80
  - (void)removePolygonView:(LuggPolygonView *)polygonView;
64
81
  - (void)syncPolygonView:(LuggPolygonView *)polygonView;
82
+ - (void)addCircleView:(LuggCircleView *)circleView;
83
+ - (void)removeCircleView:(LuggCircleView *)circleView;
84
+ - (void)syncCircleView:(LuggCircleView *)circleView;
85
+ - (void)addGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView;
86
+ - (void)removeGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView;
87
+ - (void)syncGroundOverlayView:(LuggGroundOverlayView *)groundOverlayView;
88
+ - (void)addTileOverlayView:(LuggTileOverlayView *)tileOverlayView;
89
+ - (void)removeTileOverlayView:(LuggTileOverlayView *)tileOverlayView;
90
+ - (void)syncTileOverlayView:(LuggTileOverlayView *)tileOverlayView;
65
91
 
66
92
  // Lifecycle
67
93
  - (void)pauseAnimations;
@@ -0,0 +1,75 @@
1
+ #import "MapProviderDelegate.h"
2
+ #import <MapKit/MapKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ /// Projects a coordinate into view points for a static map covering mapRect
7
+ CGPoint LuggStaticPointForCoordinate(MKMapRect mapRect, CGSize size,
8
+ CLLocationCoordinate2D coordinate);
9
+
10
+ /**
11
+ * Shared behavior for static (non-interactive) map providers - the iOS
12
+ * equivalent of Android's lite mode. The base map renders asynchronously
13
+ * (subclass-specific), so static maps keep loading while the user scrolls
14
+ * without blocking the main thread.
15
+ *
16
+ * Markers are live views positioned over the base map via a mercator
17
+ * projection (mirroring Android's live markers on lite maps), so async
18
+ * marker content (e.g. remote images) still updates when it loads.
19
+ * Polylines, polygons, circles and ground overlays are drawn into a
20
+ * single CoreGraphics overlay view. All overlays stay hidden over the
21
+ * placeholder and reveal together with the base map. Tile overlays are
22
+ * not supported.
23
+ */
24
+ @interface StaticMapProviderBase : NSObject <MapProvider>
25
+
26
+ /// Previously captured base map for the same cache key; when set, the base
27
+ /// render is skipped and the image is shown immediately.
28
+ @property(nonatomic, strong, nullable) UIImage *cachedBaseImage;
29
+
30
+ // State for subclasses
31
+ @property(nonatomic, readonly, nullable) UIView *wrapperView;
32
+ @property(nonatomic, readonly) CLLocationCoordinate2D coordinate;
33
+ @property(nonatomic, readonly) double zoom;
34
+ @property(nonatomic, readonly) MKMapRect mapRect;
35
+ @property(nonatomic, readonly) CGSize projectedSize;
36
+ @property(nonatomic, readonly) UIEdgeInsets edgeInsets;
37
+ @property(nonatomic, readonly) BOOL projectionReady;
38
+ @property(nonatomic, readonly) facebook::react::LuggMapViewMapType mapType;
39
+ @property(nonatomic, readonly) facebook::react::LuggMapViewTheme theme;
40
+
41
+ /// Shows the base image below the overlays, emits the static snapshot
42
+ /// delegate callbacks and marks the base render done.
43
+ - (void)displayBaseImage:(UIImage *)image fromCache:(BOOL)fromCache;
44
+
45
+ /// Shows the marker and shape overlays (idempotent). Called automatically
46
+ /// when the base image displays; subclasses whose base map is already
47
+ /// visible earlier (e.g. a live warmup map) can call it sooner.
48
+ - (void)revealOverlays;
49
+
50
+ #pragma mark - Subclass hooks
51
+
52
+ /// The map rect the base render covers for the given view size; must match
53
+ /// the SDK's camera framing so overlays align with the base map exactly.
54
+ - (MKMapRect)mapRectForSize:(CGSize)size;
55
+
56
+ /// Overlay view shown for markers without a custom child view.
57
+ - (UIView *)newDefaultMarkerOverlayView;
58
+
59
+ /// Zoom level at which the base map renders at the given scale (map points
60
+ /// per view point) for the current coordinate and view size; the inverse of
61
+ /// mapRectForSize. Used by fitCoordinates.
62
+ - (double)zoomForMapPointsPerPoint:(double)mapPointsPerPoint;
63
+
64
+ /// Starts rendering the base map for the current projection (guaranteed
65
+ /// projectionReady, no cached image). Called after mount (once initial
66
+ /// props have applied), after a resize, and from resumeAnimations to retry
67
+ /// a failed or cancelled render (e.g. device back online).
68
+ - (void)renderBaseMap;
69
+
70
+ /// Cancels an in-flight base render (resize or teardown).
71
+ - (void)cancelBaseRender;
72
+
73
+ @end
74
+
75
+ NS_ASSUME_NONNULL_END