@lugg/maps 0.2.0-alpha.3 → 0.2.0-alpha.30

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 (124) hide show
  1. package/README.md +21 -3
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +265 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +75 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +6 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +136 -14
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +21 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  10. package/android/src/main/java/com/luggmaps/{LuggGoogleMapView.kt → core/GoogleMapProvider.kt} +251 -241
  11. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +55 -0
  12. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +163 -52
  13. package/ios/LuggMapView.h +9 -0
  14. package/ios/LuggMapView.mm +312 -0
  15. package/ios/LuggMarkerView.h +9 -0
  16. package/ios/LuggMarkerView.mm +79 -0
  17. package/ios/LuggPolylineView.h +4 -0
  18. package/ios/LuggPolylineView.mm +23 -0
  19. package/ios/core/AppleMapProvider.h +15 -0
  20. package/ios/core/AppleMapProvider.mm +664 -0
  21. package/ios/core/GMSPolylineAnimator.h +3 -0
  22. package/ios/core/GMSPolylineAnimator.m +175 -40
  23. package/ios/core/GoogleMapProvider.h +13 -0
  24. package/ios/core/GoogleMapProvider.mm +513 -0
  25. package/ios/core/MKPolylineAnimator.h +4 -0
  26. package/ios/core/MKPolylineAnimator.m +224 -68
  27. package/ios/core/MapProviderDelegate.h +72 -0
  28. package/ios/core/PolylineAnimatorBase.h +24 -1
  29. package/ios/core/PolylineAnimatorBase.m +82 -19
  30. package/ios/extensions/MKMapView+Zoom.h +2 -0
  31. package/ios/extensions/MKMapView+Zoom.m +14 -4
  32. package/lib/module/MapProvider.js +13 -0
  33. package/lib/module/MapProvider.js.map +1 -0
  34. package/lib/module/MapProvider.types.js +4 -0
  35. package/lib/module/MapProvider.types.js.map +1 -0
  36. package/lib/module/MapProvider.web.js +20 -0
  37. package/lib/module/MapProvider.web.js.map +1 -0
  38. package/lib/module/MapView.js +29 -16
  39. package/lib/module/MapView.js.map +1 -1
  40. package/lib/module/MapView.web.js +290 -0
  41. package/lib/module/MapView.web.js.map +1 -0
  42. package/lib/module/components/Marker.js +19 -2
  43. package/lib/module/components/Marker.js.map +1 -1
  44. package/lib/module/components/Marker.web.js +33 -0
  45. package/lib/module/components/Marker.web.js.map +1 -0
  46. package/lib/module/components/Polyline.js +17 -4
  47. package/lib/module/components/Polyline.js.map +1 -1
  48. package/lib/module/components/Polyline.web.js +229 -0
  49. package/lib/module/components/Polyline.web.js.map +1 -0
  50. package/lib/module/components/index.js +2 -2
  51. package/lib/module/components/index.js.map +1 -1
  52. package/lib/module/components/index.web.js +5 -0
  53. package/lib/module/components/index.web.js.map +1 -0
  54. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  55. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  56. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  57. package/lib/module/index.js +4 -2
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/index.web.js +7 -0
  60. package/lib/module/index.web.js.map +1 -0
  61. package/lib/typescript/src/MapProvider.d.ts +8 -0
  62. package/lib/typescript/src/MapProvider.d.ts.map +1 -0
  63. package/lib/typescript/src/MapProvider.types.d.ts +16 -0
  64. package/lib/typescript/src/MapProvider.types.d.ts.map +1 -0
  65. package/lib/typescript/src/MapProvider.web.d.ts +11 -0
  66. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -0
  67. package/lib/typescript/src/MapView.d.ts +4 -4
  68. package/lib/typescript/src/MapView.d.ts.map +1 -1
  69. package/lib/typescript/src/MapView.types.d.ts +23 -7
  70. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  71. package/lib/typescript/src/MapView.web.d.ts +3 -0
  72. package/lib/typescript/src/MapView.web.d.ts.map +1 -0
  73. package/lib/typescript/src/components/Marker.d.ts +25 -1
  74. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  75. package/lib/typescript/src/components/Marker.web.d.ts +3 -0
  76. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -0
  77. package/lib/typescript/src/components/Polyline.d.ts +36 -1
  78. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  79. package/lib/typescript/src/components/Polyline.web.d.ts +3 -0
  80. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -0
  81. package/lib/typescript/src/components/index.d.ts +4 -2
  82. package/lib/typescript/src/components/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/index.web.d.ts +5 -0
  84. package/lib/typescript/src/components/index.web.d.ts.map +1 -0
  85. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +8 -4
  86. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  87. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +4 -1
  88. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  89. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  90. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  91. package/lib/typescript/src/index.d.ts +8 -3
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/index.web.d.ts +10 -0
  94. package/lib/typescript/src/index.web.d.ts.map +1 -0
  95. package/lib/typescript/src/types.d.ts +4 -0
  96. package/lib/typescript/src/types.d.ts.map +1 -1
  97. package/package.json +17 -3
  98. package/src/MapProvider.tsx +10 -0
  99. package/src/MapProvider.types.ts +16 -0
  100. package/src/MapProvider.web.tsx +14 -0
  101. package/src/MapView.tsx +28 -26
  102. package/src/MapView.types.ts +24 -7
  103. package/src/MapView.web.tsx +364 -0
  104. package/src/components/Marker.tsx +50 -4
  105. package/src/components/Marker.web.tsx +33 -0
  106. package/src/components/Polyline.tsx +51 -2
  107. package/src/components/Polyline.web.tsx +287 -0
  108. package/src/components/index.ts +8 -2
  109. package/src/components/index.web.ts +4 -0
  110. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  111. package/src/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  112. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  113. package/src/index.ts +18 -2
  114. package/src/index.web.ts +20 -0
  115. package/src/types.ts +5 -0
  116. package/ios/LuggAppleMapView.h +0 -16
  117. package/ios/LuggAppleMapView.mm +0 -580
  118. package/ios/LuggGoogleMapView.h +0 -13
  119. package/ios/LuggGoogleMapView.mm +0 -454
  120. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  121. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  122. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  123. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  124. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -0,0 +1,55 @@
1
+ package com.luggmaps.core
2
+
3
+ import android.view.View
4
+ import com.luggmaps.LuggMarkerView
5
+ import com.luggmaps.LuggPolylineView
6
+
7
+ data class EdgeInsets(val top: Int = 0, val left: Int = 0, val bottom: Int = 0, val right: Int = 0)
8
+
9
+ interface MapProviderDelegate {
10
+ fun mapProviderDidReady()
11
+ fun mapProviderDidMoveCamera(latitude: Double, longitude: Double, zoom: Float, gesture: Boolean)
12
+ fun mapProviderDidIdleCamera(latitude: Double, longitude: Double, zoom: Float, gesture: Boolean)
13
+ }
14
+
15
+ interface MapProvider {
16
+ var delegate: MapProviderDelegate?
17
+ val isMapReady: Boolean
18
+
19
+ fun initializeMap(wrapperView: View, latitude: Double, longitude: Double, zoom: Float)
20
+ fun destroy()
21
+
22
+ // Props
23
+ fun setZoomEnabled(enabled: Boolean)
24
+ fun setScrollEnabled(enabled: Boolean)
25
+ fun setRotateEnabled(enabled: Boolean)
26
+ fun setPitchEnabled(enabled: Boolean)
27
+ fun setUserLocationEnabled(enabled: Boolean)
28
+ fun setUserLocationButtonEnabled(enabled: Boolean)
29
+ fun setTheme(value: String)
30
+ fun setMinZoom(zoom: Double)
31
+ fun setMaxZoom(zoom: Double)
32
+ fun setEdgeInsets(edgeInsets: EdgeInsets)
33
+ fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int)
34
+
35
+ // Children
36
+ fun addMarkerView(markerView: LuggMarkerView)
37
+ fun removeMarkerView(markerView: LuggMarkerView)
38
+ fun addPolylineView(polylineView: LuggPolylineView)
39
+ fun removePolylineView(polylineView: LuggPolylineView)
40
+
41
+ // Lifecycle
42
+ fun pauseAnimations()
43
+ fun resumeAnimations()
44
+
45
+ // Commands
46
+ fun moveCamera(latitude: Double, longitude: Double, zoom: Double, duration: Int)
47
+ fun fitCoordinates(
48
+ coordinates: List<Any>,
49
+ edgeInsetsTop: Int,
50
+ edgeInsetsLeft: Int,
51
+ edgeInsetsBottom: Int,
52
+ edgeInsetsRight: Int,
53
+ duration: Int
54
+ )
55
+ }
@@ -2,20 +2,39 @@ package com.luggmaps.core
2
2
 
3
3
  import android.animation.ValueAnimator
4
4
  import android.graphics.Color
5
+ import android.location.Location
5
6
  import android.view.animation.LinearInterpolator
6
7
  import com.google.android.gms.maps.model.LatLng
7
8
  import com.google.android.gms.maps.model.Polyline
8
9
  import com.google.android.gms.maps.model.StrokeStyle
9
10
  import com.google.android.gms.maps.model.StyleSpan
11
+ import com.luggmaps.AnimatedOptions
10
12
  import kotlin.math.floor
11
- import kotlin.math.max
12
13
  import kotlin.math.min
13
14
 
14
15
  class PolylineAnimator {
16
+ companion object {
17
+ private const val MAX_ANIMATION_SPANS = 16
18
+ }
19
+
15
20
  var polyline: Polyline? = null
16
21
  var coordinates: List<LatLng> = emptyList()
22
+ set(value) {
23
+ field = value
24
+ if (animated && animator != null) {
25
+ computeCumulativeDistances()
26
+ }
27
+ }
17
28
  var strokeColors: List<Int> = listOf(Color.BLACK)
18
29
  var strokeWidth: Float = 1f
30
+ var animatedOptions: AnimatedOptions = AnimatedOptions()
31
+ set(value) {
32
+ if (field == value) return
33
+ field = value
34
+ if (animated) {
35
+ restartAnimation()
36
+ }
37
+ }
19
38
 
20
39
  var animated: Boolean = false
21
40
  set(value) {
@@ -31,6 +50,25 @@ class PolylineAnimator {
31
50
 
32
51
  private var animator: ValueAnimator? = null
33
52
  private var animationProgress: Float = 0f
53
+ private var cumulativeDistances: FloatArray = floatArrayOf()
54
+ private var totalLength: Float = 0f
55
+
56
+ // Reusable collections to avoid per-frame allocations
57
+ private val reusablePoints = ArrayList<LatLng>()
58
+ private val reusableSpans = ArrayList<StyleSpan>()
59
+
60
+ private fun restartAnimation() {
61
+ stopAnimation()
62
+ startAnimation()
63
+ }
64
+
65
+ private fun applyEasing(t: Float): Float =
66
+ when (animatedOptions.easing) {
67
+ "easeIn" -> t * t
68
+ "easeOut" -> t * (2 - t)
69
+ "easeInOut" -> if (t < 0.5f) 2 * t * t else -1 + (4 - 2 * t) * t
70
+ else -> t
71
+ }
34
72
 
35
73
  fun update() {
36
74
  if (animated) return
@@ -50,8 +88,14 @@ class PolylineAnimator {
50
88
  private fun startAnimation() {
51
89
  if (animator != null) return
52
90
 
53
- animator = ValueAnimator.ofFloat(0f, 2.15f).apply {
54
- duration = 3650 // ~1.75s per phase * 2 + pause
91
+ computeCumulativeDistances()
92
+
93
+ val trailLength = animatedOptions.trailLength.coerceIn(0.01f, 1f)
94
+ val maxProgress = if (trailLength < 1f) 1f else 2.15f
95
+
96
+ animator = ValueAnimator.ofFloat(0f, maxProgress).apply {
97
+ duration = (animatedOptions.duration * maxProgress).toLong()
98
+ startDelay = animatedOptions.delay
55
99
  repeatCount = ValueAnimator.INFINITE
56
100
  interpolator = LinearInterpolator()
57
101
  addUpdateListener { animation ->
@@ -62,83 +106,150 @@ class PolylineAnimator {
62
106
  }
63
107
  }
64
108
 
109
+ private fun computeCumulativeDistances() {
110
+ if (coordinates.size < 2) {
111
+ cumulativeDistances = floatArrayOf(0f)
112
+ totalLength = 0f
113
+ return
114
+ }
115
+
116
+ val distances = FloatArray(coordinates.size)
117
+ distances[0] = 0f
118
+ var total = 0f
119
+
120
+ for (i in 1 until coordinates.size) {
121
+ val prev = coordinates[i - 1]
122
+ val curr = coordinates[i]
123
+ val results = FloatArray(1)
124
+ Location.distanceBetween(prev.latitude, prev.longitude, curr.latitude, curr.longitude, results)
125
+ total += results[0]
126
+ distances[i] = total
127
+ }
128
+
129
+ cumulativeDistances = distances
130
+ totalLength = total
131
+ }
132
+
133
+ private fun indexForDistance(distance: Float): Int {
134
+ var left = 0
135
+ var right = cumulativeDistances.size - 1
136
+
137
+ while (left < right) {
138
+ val mid = (left + right + 1) / 2
139
+ if (cumulativeDistances[mid] <= distance) {
140
+ left = mid
141
+ } else {
142
+ right = mid - 1
143
+ }
144
+ }
145
+
146
+ return left.coerceAtMost(cumulativeDistances.size - 2).coerceAtLeast(0)
147
+ }
148
+
149
+ private fun coordinateAtDistance(distance: Float): LatLng {
150
+ if (distance <= 0f) return coordinates.first()
151
+ if (distance >= totalLength) return coordinates.last()
152
+
153
+ val idx = indexForDistance(distance)
154
+ val segStart = cumulativeDistances[idx]
155
+ val segEnd = cumulativeDistances[idx + 1]
156
+ val segLength = segEnd - segStart
157
+
158
+ val t = if (segLength > 0) (distance - segStart) / segLength else 0f
159
+ val c1 = coordinates[idx]
160
+ val c2 = coordinates[idx + 1]
161
+
162
+ // Reuse existing coordinate if no interpolation needed
163
+ if (t == 0f) return c1
164
+
165
+ return LatLng(
166
+ c1.latitude + (c2.latitude - c1.latitude) * t,
167
+ c1.longitude + (c2.longitude - c1.longitude) * t
168
+ )
169
+ }
170
+
65
171
  private fun stopAnimation() {
66
172
  animator?.cancel()
67
173
  animator = null
68
174
  }
69
175
 
176
+ fun pause() {
177
+ animator?.pause()
178
+ }
179
+
180
+ fun resume() {
181
+ animator?.resume()
182
+ }
183
+
70
184
  private fun updateAnimatedPolyline() {
71
185
  val poly = polyline ?: return
72
- if (coordinates.size < 2) {
186
+ if (coordinates.size < 2 || totalLength <= 0f) {
73
187
  poly.points = coordinates
74
188
  return
75
189
  }
76
190
 
77
- val segmentCount = coordinates.size - 1
78
- val progress = min(animationProgress, 2f)
191
+ val trailLength = animatedOptions.trailLength.coerceIn(0.01f, 1f)
192
+ val maxProgress = if (trailLength < 1f) 1f else 2f
193
+ val rawProgress = min(animationProgress, maxProgress)
194
+ val progress = applyEasing(rawProgress / maxProgress) * maxProgress
79
195
 
80
- val headPos: Float
81
- val tailPos: Float
196
+ val headDist: Float
197
+ val tailDist: Float
82
198
 
83
- if (progress <= 1f) {
84
- tailPos = 0f
85
- headPos = progress * segmentCount
199
+ if (trailLength < 1f) {
200
+ headDist = progress * totalLength
201
+ tailDist = maxOf(0f, headDist - totalLength * trailLength)
202
+ } else if (progress <= 1f) {
203
+ tailDist = 0f
204
+ headDist = progress * totalLength
86
205
  } else {
87
206
  val shrinkProgress = progress - 1f
88
- tailPos = shrinkProgress * segmentCount
89
- headPos = segmentCount.toFloat()
207
+ tailDist = shrinkProgress * totalLength
208
+ headDist = totalLength
90
209
  }
91
210
 
92
- if (headPos <= tailPos || coordinates.isEmpty()) {
93
- poly.setSpans(emptyList())
211
+ if (headDist <= tailDist) {
94
212
  poly.points = listOf(coordinates.firstOrNull() ?: LatLng(0.0, 0.0))
213
+ poly.color = strokeColors.firstOrNull() ?: Color.BLACK
95
214
  return
96
215
  }
97
216
 
98
- val startIndex = floor(tailPos).toInt()
99
- val endIndex = kotlin.math.ceil(headPos.toDouble()).toInt()
100
- val visibleLength = headPos - tailPos
217
+ val visibleLength = headDist - tailDist
218
+ val startIndex = indexForDistance(tailDist)
219
+ val endIndex = indexForDistance(headDist)
101
220
 
102
- val points = mutableListOf<LatLng>()
103
- val spans = mutableListOf<StyleSpan>()
221
+ reusablePoints.clear()
104
222
 
105
- for (i in startIndex..minOf(endIndex, coordinates.size - 1)) {
106
- var coord = coordinates[i]
223
+ reusablePoints.add(coordinateAtDistance(tailDist))
107
224
 
108
- // Interpolate tail
109
- if (i == startIndex && tailPos > startIndex.toFloat() && i + 1 < coordinates.size) {
110
- val t = tailPos - startIndex
111
- val next = coordinates[i + 1]
112
- coord = LatLng(
113
- coord.latitude + (next.latitude - coord.latitude) * t,
114
- coord.longitude + (next.longitude - coord.longitude) * t
115
- )
116
- }
225
+ for (i in (startIndex + 1)..endIndex) {
226
+ reusablePoints.add(coordinates[i])
227
+ }
117
228
 
118
- // Interpolate head
119
- if (i == endIndex && headPos < endIndex.toFloat() && i > 0) {
120
- val t = headPos - (endIndex - 1)
121
- val prev = coordinates[i - 1]
122
- coord = LatLng(
123
- prev.latitude + (coordinates[i].latitude - prev.latitude) * t,
124
- prev.longitude + (coordinates[i].longitude - prev.longitude) * t
125
- )
126
- }
229
+ val endCoord = coordinateAtDistance(headDist)
230
+ val lastAdded = reusablePoints.lastOrNull()
231
+ if (lastAdded == null || endCoord.latitude != lastAdded.latitude || endCoord.longitude != lastAdded.longitude) {
232
+ reusablePoints.add(endCoord)
233
+ }
127
234
 
128
- points.add(coord)
235
+ if (reusablePoints.size < 2) return
129
236
 
130
- if (i < endIndex && i < segmentCount) {
131
- val segStartPos = max(i.toFloat(), tailPos)
132
- val segEndPos = min((i + 1).toFloat(), headPos)
133
- val gradientMid = ((segStartPos + segEndPos) / 2f - tailPos) / visibleLength
134
- val color = colorAtGradientPosition(gradientMid)
135
- spans.add(StyleSpan(StrokeStyle.colorBuilder(color).build()))
136
- }
137
- }
237
+ poly.points = ArrayList(reusablePoints)
138
238
 
139
- poly.points = points
140
- if (spans.isNotEmpty()) {
141
- poly.setSpans(spans)
239
+ if (strokeColors.size <= 1) {
240
+ poly.color = strokeColors.firstOrNull() ?: Color.BLACK
241
+ } else {
242
+ val segmentCount = reusablePoints.size - 1
243
+ val spanCount = min(segmentCount, MAX_ANIMATION_SPANS)
244
+ val segmentsPerSpan = segmentCount.toDouble() / spanCount
245
+
246
+ reusableSpans.clear()
247
+ for (i in 0 until spanCount) {
248
+ val gradientPos = (i + 0.5f) / spanCount
249
+ val color = colorAtGradientPosition(gradientPos)
250
+ reusableSpans.add(StyleSpan(StrokeStyle.colorBuilder(color).build(), segmentsPerSpan))
251
+ }
252
+ poly.setSpans(reusableSpans)
142
253
  }
143
254
  }
144
255
 
@@ -0,0 +1,9 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface LuggMapView : RCTViewComponentView
7
+ @end
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,312 @@
1
+ #import "LuggMapView.h"
2
+ #import "LuggMapWrapperView.h"
3
+ #import "LuggMarkerView.h"
4
+ #import "LuggPolylineView.h"
5
+ #import "core/AppleMapProvider.h"
6
+ #import "core/GoogleMapProvider.h"
7
+ #import "core/MapProviderDelegate.h"
8
+ #import "events/CameraIdleEvent.h"
9
+ #import "events/CameraMoveEvent.h"
10
+ #import "events/ReadyEvent.h"
11
+
12
+ #import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
13
+ #import <react/renderer/components/RNMapsSpec/EventEmitters.h>
14
+ #import <react/renderer/components/RNMapsSpec/Props.h>
15
+ #import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
16
+
17
+ #import "RCTFabricComponentsPlugins.h"
18
+
19
+ using namespace facebook::react;
20
+ using namespace luggmaps::events;
21
+
22
+ @interface LuggMapView () <RCTLuggMapViewViewProtocol, MapProviderDelegate>
23
+ @end
24
+
25
+ @implementation LuggMapView {
26
+ id<MapProvider> _provider;
27
+ LuggMapWrapperView *_mapWrapperView;
28
+ LuggMapViewProvider _providerType;
29
+ NSString *_mapId;
30
+ BOOL _initialized;
31
+ // Cached props to apply after content creation
32
+ BOOL _zoomEnabled;
33
+ BOOL _scrollEnabled;
34
+ BOOL _rotateEnabled;
35
+ BOOL _pitchEnabled;
36
+ BOOL _userLocationEnabled;
37
+ LuggMapViewTheme _theme;
38
+ double _minZoom;
39
+ double _maxZoom;
40
+ UIEdgeInsets _edgeInsets;
41
+ UIEdgeInsets _oldEdgeInsets;
42
+ }
43
+
44
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
45
+ return concreteComponentDescriptorProvider<LuggMapViewComponentDescriptor>();
46
+ }
47
+
48
+ - (instancetype)initWithFrame:(CGRect)frame {
49
+ if (self = [super initWithFrame:frame]) {
50
+ static const auto defaultProps = std::make_shared<const LuggMapViewProps>();
51
+ _props = defaultProps;
52
+
53
+ _initialized = NO;
54
+ _mapId = @"";
55
+ _providerType = LuggMapViewProvider::Google;
56
+ _zoomEnabled = YES;
57
+ _scrollEnabled = YES;
58
+ _rotateEnabled = YES;
59
+ _pitchEnabled = YES;
60
+ _userLocationEnabled = NO;
61
+ _theme = LuggMapViewTheme::System;
62
+ _edgeInsets = UIEdgeInsetsZero;
63
+ }
64
+ return self;
65
+ }
66
+
67
+ #pragma mark - View Lifecycle
68
+
69
+ - (void)mountChildComponentView:
70
+ (UIView<RCTComponentViewProtocol> *)childComponentView
71
+ index:(NSInteger)index {
72
+ [super mountChildComponentView:childComponentView index:index];
73
+
74
+ if ([childComponentView isKindOfClass:[LuggMapWrapperView class]]) {
75
+ _mapWrapperView = (LuggMapWrapperView *)childComponentView;
76
+ } else if ([childComponentView isKindOfClass:[LuggMarkerView class]]) {
77
+ LuggMarkerView *markerView = (LuggMarkerView *)childComponentView;
78
+ if (_provider) {
79
+ [_provider addMarkerView:markerView];
80
+ }
81
+ } else if ([childComponentView isKindOfClass:[LuggPolylineView class]]) {
82
+ LuggPolylineView *polylineView = (LuggPolylineView *)childComponentView;
83
+ if (_provider) {
84
+ [_provider addPolylineView:polylineView];
85
+ }
86
+ }
87
+ }
88
+
89
+ - (void)unmountChildComponentView:
90
+ (UIView<RCTComponentViewProtocol> *)childComponentView
91
+ index:(NSInteger)index {
92
+ if ([childComponentView isKindOfClass:[LuggMarkerView class]]) {
93
+ LuggMarkerView *markerView = (LuggMarkerView *)childComponentView;
94
+ if (_provider) {
95
+ [_provider removeMarkerView:markerView];
96
+ }
97
+ } else if ([childComponentView isKindOfClass:[LuggPolylineView class]]) {
98
+ LuggPolylineView *polylineView = (LuggPolylineView *)childComponentView;
99
+ if (_provider) {
100
+ [_provider removePolylineView:polylineView];
101
+ }
102
+ }
103
+
104
+ [super unmountChildComponentView:childComponentView index:index];
105
+ }
106
+
107
+ - (void)didMoveToWindow {
108
+ [super didMoveToWindow];
109
+ if (self.window) {
110
+ if (!_provider && _mapWrapperView) {
111
+ [self initializeProvider];
112
+ }
113
+ [_provider resumeAnimations];
114
+ } else {
115
+ [_provider pauseAnimations];
116
+ }
117
+ }
118
+
119
+ - (void)prepareForRecycle {
120
+ [super prepareForRecycle];
121
+
122
+ [_provider destroy];
123
+ _provider = nil;
124
+ _mapWrapperView = nil;
125
+ _initialized = NO;
126
+ }
127
+
128
+ #pragma mark - Provider Initialization
129
+
130
+ - (void)initializeProvider {
131
+ if (_provider || !_mapWrapperView)
132
+ return;
133
+
134
+ const auto &viewProps =
135
+ *std::static_pointer_cast<LuggMapViewProps const>(_props);
136
+
137
+ if (_providerType == LuggMapViewProvider::Apple) {
138
+ _provider = [[AppleMapProvider alloc] init];
139
+ } else {
140
+ GoogleMapProvider *google = [[GoogleMapProvider alloc] init];
141
+ google.mapId = _mapId;
142
+ _provider = google;
143
+ }
144
+
145
+ _provider.delegate = self;
146
+
147
+ CLLocationCoordinate2D coordinate =
148
+ CLLocationCoordinate2DMake(viewProps.initialCoordinate.latitude,
149
+ viewProps.initialCoordinate.longitude);
150
+
151
+ [_provider initializeMapInView:_mapWrapperView
152
+ initialCoordinate:coordinate
153
+ initialZoom:viewProps.initialZoom];
154
+
155
+ // Apply cached props after map view is created
156
+ [self applyProps];
157
+ [_provider setEdgeInsets:_edgeInsets oldEdgeInsets:UIEdgeInsetsZero];
158
+
159
+ _initialized = YES;
160
+
161
+ // Flush any children that were mounted before content was created
162
+ for (UIView *subview in self.subviews) {
163
+ if ([subview isKindOfClass:[LuggMarkerView class]]) {
164
+ [_provider addMarkerView:(LuggMarkerView *)subview];
165
+ } else if ([subview isKindOfClass:[LuggPolylineView class]]) {
166
+ [_provider addPolylineView:(LuggPolylineView *)subview];
167
+ }
168
+ }
169
+ }
170
+
171
+ #pragma mark - MapProviderDelegate
172
+
173
+ - (void)mapProviderDidReady {
174
+ ReadyEvent::emit<LuggMapViewEventEmitter>(_eventEmitter);
175
+ }
176
+
177
+ - (void)mapProviderDidMoveCamera:(double)latitude
178
+ longitude:(double)longitude
179
+ zoom:(double)zoom
180
+ gesture:(BOOL)gesture {
181
+ CameraMoveEvent{latitude, longitude, zoom, static_cast<bool>(gesture)}
182
+ .emit<LuggMapViewEventEmitter>(_eventEmitter);
183
+ }
184
+
185
+ - (void)mapProviderDidIdleCamera:(double)latitude
186
+ longitude:(double)longitude
187
+ zoom:(double)zoom
188
+ gesture:(BOOL)gesture {
189
+ CameraIdleEvent{latitude, longitude, zoom, static_cast<bool>(gesture)}
190
+ .emit<LuggMapViewEventEmitter>(_eventEmitter);
191
+ }
192
+
193
+ #pragma mark - Property Setters
194
+
195
+ - (void)applyProps {
196
+ [_provider setZoomEnabled:_zoomEnabled];
197
+ [_provider setScrollEnabled:_scrollEnabled];
198
+ [_provider setRotateEnabled:_rotateEnabled];
199
+ [_provider setPitchEnabled:_pitchEnabled];
200
+ [_provider setUserLocationEnabled:_userLocationEnabled];
201
+ [_provider setTheme:(NSInteger)_theme];
202
+ [_provider setMinZoom:_minZoom];
203
+ [_provider setMaxZoom:_maxZoom];
204
+ }
205
+
206
+ - (void)updateProps:(Props::Shared const &)props
207
+ oldProps:(Props::Shared const &)oldProps {
208
+ const auto &newViewProps =
209
+ *std::static_pointer_cast<LuggMapViewProps const>(props);
210
+ const auto &prevViewProps =
211
+ *std::static_pointer_cast<LuggMapViewProps const>(_props);
212
+
213
+ _providerType = newViewProps.provider;
214
+
215
+ NSString *newMapId =
216
+ [NSString stringWithUTF8String:newViewProps.mapId.c_str()];
217
+ if (newMapId.length > 0) {
218
+ _mapId = newMapId;
219
+ }
220
+
221
+ if (newViewProps.zoomEnabled != prevViewProps.zoomEnabled) {
222
+ _zoomEnabled = newViewProps.zoomEnabled;
223
+ [_provider setZoomEnabled:_zoomEnabled];
224
+ }
225
+ if (newViewProps.scrollEnabled != prevViewProps.scrollEnabled) {
226
+ _scrollEnabled = newViewProps.scrollEnabled;
227
+ [_provider setScrollEnabled:_scrollEnabled];
228
+ }
229
+ if (newViewProps.rotateEnabled != prevViewProps.rotateEnabled) {
230
+ _rotateEnabled = newViewProps.rotateEnabled;
231
+ [_provider setRotateEnabled:_rotateEnabled];
232
+ }
233
+ if (newViewProps.pitchEnabled != prevViewProps.pitchEnabled) {
234
+ _pitchEnabled = newViewProps.pitchEnabled;
235
+ [_provider setPitchEnabled:_pitchEnabled];
236
+ }
237
+ if (newViewProps.userLocationEnabled != prevViewProps.userLocationEnabled) {
238
+ _userLocationEnabled = newViewProps.userLocationEnabled;
239
+ [_provider setUserLocationEnabled:_userLocationEnabled];
240
+ }
241
+ if (newViewProps.minZoom != prevViewProps.minZoom) {
242
+ _minZoom = newViewProps.minZoom;
243
+ [_provider setMinZoom:_minZoom];
244
+ }
245
+ if (newViewProps.maxZoom != prevViewProps.maxZoom) {
246
+ _maxZoom = newViewProps.maxZoom;
247
+ [_provider setMaxZoom:_maxZoom];
248
+ }
249
+ if (newViewProps.theme != prevViewProps.theme) {
250
+ _theme = newViewProps.theme;
251
+ [_provider setTheme:(NSInteger)_theme];
252
+ }
253
+
254
+ UIEdgeInsets newEdgeInsets = UIEdgeInsetsMake(
255
+ newViewProps.edgeInsets.top, newViewProps.edgeInsets.left,
256
+ newViewProps.edgeInsets.bottom, newViewProps.edgeInsets.right);
257
+ if (!UIEdgeInsetsEqualToEdgeInsets(newEdgeInsets, _edgeInsets)) {
258
+ _oldEdgeInsets = _edgeInsets;
259
+ _edgeInsets = newEdgeInsets;
260
+ [_provider setEdgeInsets:_edgeInsets oldEdgeInsets:_oldEdgeInsets];
261
+ }
262
+
263
+ [super updateProps:props oldProps:oldProps];
264
+ }
265
+
266
+ #pragma mark - Commands
267
+
268
+ - (void)moveCamera:(double)latitude
269
+ longitude:(double)longitude
270
+ zoom:(double)zoom
271
+ duration:(double)duration {
272
+ [_provider moveCamera:latitude
273
+ longitude:longitude
274
+ zoom:zoom
275
+ duration:duration];
276
+ }
277
+
278
+ - (void)fitCoordinates:(NSArray *)coordinates
279
+ edgeInsetsTop:(double)edgeInsetsTop
280
+ edgeInsetsLeft:(double)edgeInsetsLeft
281
+ edgeInsetsBottom:(double)edgeInsetsBottom
282
+ edgeInsetsRight:(double)edgeInsetsRight
283
+ duration:(double)duration {
284
+ [_provider fitCoordinates:coordinates
285
+ edgeInsetsTop:edgeInsetsTop
286
+ edgeInsetsLeft:edgeInsetsLeft
287
+ edgeInsetsBottom:edgeInsetsBottom
288
+ edgeInsetsRight:edgeInsetsRight
289
+ duration:duration];
290
+ }
291
+
292
+ - (void)setEdgeInsets:(double)top
293
+ left:(double)left
294
+ bottom:(double)bottom
295
+ right:(double)right
296
+ duration:(double)duration {
297
+ UIEdgeInsets oldInsets = _edgeInsets;
298
+ _edgeInsets = UIEdgeInsetsMake(top, left, bottom, right);
299
+ [_provider setEdgeInsets:_edgeInsets
300
+ oldEdgeInsets:oldInsets
301
+ duration:duration];
302
+ }
303
+
304
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
305
+ RCTLuggMapViewHandleCommand(self, commandName, args);
306
+ }
307
+
308
+ Class<RCTComponentViewProtocol> LuggMapViewCls(void) {
309
+ return LuggMapView.class;
310
+ }
311
+
312
+ @end
@@ -14,16 +14,25 @@ NS_ASSUME_NONNULL_BEGIN
14
14
 
15
15
  @interface LuggMarkerView : RCTViewComponentView
16
16
 
17
+ @property(nonatomic, readonly, nullable) NSString *name;
17
18
  @property(nonatomic, readonly) CLLocationCoordinate2D coordinate;
18
19
  @property(nonatomic, readonly, nullable) NSString *title;
19
20
  @property(nonatomic, readonly, nullable) NSString *markerDescription;
20
21
  @property(nonatomic, readonly) CGPoint anchor;
22
+ @property(nonatomic, readonly) NSInteger zIndex;
23
+ @property(nonatomic, readonly) CLLocationDegrees rotate;
24
+ @property(nonatomic, readonly) CGFloat scale;
25
+ @property(nonatomic, readonly) BOOL rasterize;
21
26
  @property(nonatomic, readonly) BOOL hasCustomView;
22
27
  @property(nonatomic, readonly) BOOL didLayout;
23
28
  @property(nonatomic, readonly) UIView *iconView;
24
29
  @property(nonatomic, weak, nullable) id<LuggMarkerViewDelegate> delegate;
25
30
  @property(nonatomic, strong, nullable) NSObject *marker;
26
31
 
32
+ - (nullable UIImage *)createIconImage;
33
+ - (nullable UIImage *)createScaledIconImage;
34
+ - (void)resetIconViewTransform;
35
+
27
36
  @end
28
37
 
29
38
  NS_ASSUME_NONNULL_END