@lugg/maps 0.2.0-alpha.1 → 0.2.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +17 -4
  2. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +30 -8
  3. package/android/src/main/java/com/luggmaps/LuggGoogleMapViewManager.kt +10 -2
  4. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +7 -1
  5. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +6 -0
  6. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +7 -0
  7. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +6 -0
  8. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +89 -43
  9. package/ios/LuggAppleMapView.mm +45 -5
  10. package/ios/LuggGoogleMapView.mm +18 -6
  11. package/ios/LuggMarkerView.h +2 -0
  12. package/ios/LuggMarkerView.mm +13 -0
  13. package/ios/LuggPolylineView.h +1 -0
  14. package/ios/LuggPolylineView.mm +6 -1
  15. package/ios/core/GMSPolylineAnimator.m +85 -36
  16. package/ios/core/MKPolylineAnimator.m +63 -32
  17. package/lib/module/MapProvider.js +13 -0
  18. package/lib/module/MapProvider.js.map +1 -0
  19. package/lib/module/MapProvider.types.js +4 -0
  20. package/lib/module/MapProvider.types.js.map +1 -0
  21. package/lib/module/MapProvider.web.js +14 -0
  22. package/lib/module/MapProvider.web.js.map +1 -0
  23. package/lib/module/MapView.js +8 -2
  24. package/lib/module/MapView.js.map +1 -1
  25. package/lib/module/MapView.web.js +235 -0
  26. package/lib/module/MapView.web.js.map +1 -0
  27. package/lib/module/components/Marker.js +4 -1
  28. package/lib/module/components/Marker.js.map +1 -1
  29. package/lib/module/components/Marker.web.js +25 -0
  30. package/lib/module/components/Marker.web.js.map +1 -0
  31. package/lib/module/components/Polyline.js +5 -2
  32. package/lib/module/components/Polyline.js.map +1 -1
  33. package/lib/module/components/Polyline.web.js +169 -0
  34. package/lib/module/components/Polyline.web.js.map +1 -0
  35. package/lib/module/components/index.js +2 -2
  36. package/lib/module/components/index.js.map +1 -1
  37. package/lib/module/components/index.web.js +5 -0
  38. package/lib/module/components/index.web.js.map +1 -0
  39. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +4 -1
  40. package/lib/module/fabric/LuggGoogleMapViewNativeComponent.ts +4 -1
  41. package/lib/module/index.js +3 -2
  42. package/lib/module/index.js.map +1 -1
  43. package/lib/module/index.web.js +6 -0
  44. package/lib/module/index.web.js.map +1 -0
  45. package/lib/typescript/src/MapProvider.d.ts +8 -0
  46. package/lib/typescript/src/MapProvider.d.ts.map +1 -0
  47. package/lib/typescript/src/MapProvider.types.d.ts +16 -0
  48. package/lib/typescript/src/MapProvider.types.d.ts.map +1 -0
  49. package/lib/typescript/src/MapProvider.web.d.ts +3 -0
  50. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -0
  51. package/lib/typescript/src/MapView.d.ts.map +1 -1
  52. package/lib/typescript/src/MapView.types.d.ts +1 -2
  53. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  54. package/lib/typescript/src/MapView.web.d.ts +3 -0
  55. package/lib/typescript/src/MapView.web.d.ts.map +1 -0
  56. package/lib/typescript/src/components/Marker.d.ts +4 -0
  57. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  58. package/lib/typescript/src/components/Marker.web.d.ts +3 -0
  59. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -0
  60. package/lib/typescript/src/components/Polyline.d.ts +4 -0
  61. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  62. package/lib/typescript/src/components/Polyline.web.d.ts +3 -0
  63. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -0
  64. package/lib/typescript/src/components/index.web.d.ts +5 -0
  65. package/lib/typescript/src/components/index.web.d.ts.map +1 -0
  66. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +1 -1
  67. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +1 -1
  68. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts +1 -1
  69. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +1 -1
  70. package/lib/typescript/src/index.d.ts +3 -1
  71. package/lib/typescript/src/index.d.ts.map +1 -1
  72. package/lib/typescript/src/index.web.d.ts +7 -0
  73. package/lib/typescript/src/index.web.d.ts.map +1 -0
  74. package/package.json +15 -2
  75. package/src/MapProvider.tsx +10 -0
  76. package/src/MapProvider.types.ts +16 -0
  77. package/src/MapProvider.web.tsx +6 -0
  78. package/src/MapView.tsx +11 -2
  79. package/src/MapView.types.ts +1 -2
  80. package/src/MapView.web.tsx +283 -0
  81. package/src/components/Marker.tsx +6 -2
  82. package/src/components/Marker.web.tsx +24 -0
  83. package/src/components/Polyline.tsx +6 -1
  84. package/src/components/Polyline.web.tsx +209 -0
  85. package/src/components/index.web.ts +4 -0
  86. package/src/fabric/LuggAppleMapViewNativeComponent.ts +4 -1
  87. package/src/fabric/LuggGoogleMapViewNativeComponent.ts +4 -1
  88. package/src/index.ts +8 -1
  89. package/src/index.web.ts +17 -0
package/README.md CHANGED
@@ -1,9 +1,6 @@
1
1
  # @lugg/maps
2
2
 
3
- React Native Fabric maps library for iOS and Android.
4
-
5
- - Google Maps (iOS & Android)
6
- - Apple Maps (iOS only)
3
+ Universal maps for your React Native apps 📍
7
4
 
8
5
  ## Installation
9
6
 
@@ -56,6 +53,22 @@ Add your Google Maps API key to `AndroidManifest.xml`:
56
53
  </application>
57
54
  ```
58
55
 
56
+ ### Web
57
+
58
+ Wrap your app with `MapProvider` and pass your Google Maps API key:
59
+
60
+ ```tsx
61
+ import { MapProvider } from '@lugg/maps';
62
+
63
+ function App() {
64
+ return (
65
+ <MapProvider apiKey="YOUR_WEB_API_KEY">
66
+ {/* Your app */}
67
+ </MapProvider>
68
+ );
69
+ }
70
+ ```
71
+
59
72
  ## Usage
60
73
 
61
74
  ```tsx
@@ -1,7 +1,6 @@
1
1
  package com.luggmaps
2
2
 
3
3
  import android.annotation.SuppressLint
4
- import android.util.Log
5
4
  import android.view.View
6
5
  import android.view.ViewGroup
7
6
  import com.facebook.react.uimanager.PixelUtil.dpToPx
@@ -103,7 +102,6 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
103
102
  override fun removeViewAt(index: Int) {
104
103
  val view = getChildAt(index)
105
104
  if (view is LuggMarkerView) {
106
- Log.d(TAG, "removing markerView: ${view.name}")
107
105
  view.marker?.remove()
108
106
  view.marker = null
109
107
  } else if (view is LuggPolylineView) {
@@ -123,7 +121,6 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
123
121
  }
124
122
 
125
123
  fun onDropViewInstance() {
126
- Log.d(TAG, "dropping mapView instance")
127
124
  pendingMarkerViews.clear()
128
125
  pendingPolylineViews.clear()
129
126
  polylineAnimators.values.forEach { it.destroy() }
@@ -282,6 +279,7 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
282
279
  title = markerView.title
283
280
  snippet = markerView.description
284
281
  setAnchor(markerView.anchorX, markerView.anchorY)
282
+ zIndex = markerView.zIndex
285
283
  if (!markerView.hasCustomView) {
286
284
  iconView = null
287
285
  }
@@ -291,7 +289,6 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
291
289
  private fun processPendingMarkers() {
292
290
  if (googleMap == null) return
293
291
 
294
- Log.d(TAG, "processing pending markers ${pendingMarkerViews.size}")
295
292
  pendingMarkerViews.forEach { addMarkerViewToMap(it) }
296
293
  pendingMarkerViews.clear()
297
294
  }
@@ -311,15 +308,14 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
311
308
  .position(position)
312
309
  .title(markerView.title)
313
310
  .snippet(markerView.description)
314
- .collisionBehavior(CollisionBehavior.REQUIRED)
315
311
 
316
- Log.d(TAG, "adding marker: ${markerView.name} customview: ${markerView.hasCustomView}")
317
312
  if (markerView.hasCustomView) {
318
313
  options.iconView(iconView)
319
314
  }
320
315
 
321
316
  val marker = map.addMarker(options) as AdvancedMarker
322
317
  marker.setAnchor(markerView.anchorX, markerView.anchorY)
318
+ marker.zIndex = markerView.zIndex
323
319
 
324
320
  markerView.marker = marker
325
321
  }
@@ -342,6 +338,7 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
342
338
  }
343
339
 
344
340
  polylineView.polyline?.width = polylineView.strokeWidth.dpToPx()
341
+ polylineView.polyline?.zIndex = polylineView.zIndex
345
342
 
346
343
  polylineAnimators[polylineView]?.apply {
347
344
  coordinates = polylineView.coordinates
@@ -364,6 +361,7 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
364
361
 
365
362
  val options = PolylineOptions()
366
363
  .width(polylineView.strokeWidth.dpToPx())
364
+ .zIndex(polylineView.zIndex)
367
365
 
368
366
  val polyline = map.addPolyline(options)
369
367
  polylineView.polyline = polyline
@@ -466,7 +464,14 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
466
464
  }
467
465
  }
468
466
 
469
- fun fitCoordinates(coordinates: List<LatLng>, padding: Int, duration: Int) {
467
+ fun fitCoordinates(
468
+ coordinates: List<LatLng>,
469
+ paddingTop: Int,
470
+ paddingLeft: Int,
471
+ paddingBottom: Int,
472
+ paddingRight: Int,
473
+ duration: Int
474
+ ) {
470
475
  val map = googleMap ?: return
471
476
  if (coordinates.isEmpty()) return
472
477
 
@@ -474,12 +479,29 @@ class LuggGoogleMapView(private val reactContext: ThemedReactContext) :
474
479
  coordinates.forEach { boundsBuilder.include(it) }
475
480
  val bounds = boundsBuilder.build()
476
481
 
477
- val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding.toFloat().dpToPx().toInt())
482
+ val top = paddingTop.toFloat().dpToPx().toInt()
483
+ val left = paddingLeft.toFloat().dpToPx().toInt()
484
+ val bottom = paddingBottom.toFloat().dpToPx().toInt()
485
+ val right = paddingRight.toFloat().dpToPx().toInt()
486
+
487
+ // Set padding before camera update, then restore after
488
+ map.setPadding(
489
+ this.paddingLeft + left,
490
+ this.paddingTop + top,
491
+ this.paddingRight + right,
492
+ this.paddingBottom + bottom
493
+ )
494
+
495
+ val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0)
496
+
478
497
  when {
479
498
  duration < 0 -> map.animateCamera(cameraUpdate)
480
499
  duration > 0 -> map.animateCamera(cameraUpdate, duration, null)
481
500
  else -> map.moveCamera(cameraUpdate)
482
501
  }
502
+
503
+ // Restore base padding
504
+ map.setPadding(this.paddingLeft, this.paddingTop, this.paddingRight, this.paddingBottom)
483
505
  }
484
506
 
485
507
  // endregion
@@ -144,7 +144,15 @@ class LuggGoogleMapViewManager :
144
144
  view.moveCamera(latitude, longitude, zoom, duration.toInt())
145
145
  }
146
146
 
147
- override fun fitCoordinates(view: LuggGoogleMapView, coordinates: ReadableArray?, padding: Double, duration: Double) {
147
+ override fun fitCoordinates(
148
+ view: LuggGoogleMapView,
149
+ coordinates: ReadableArray?,
150
+ paddingTop: Double,
151
+ paddingLeft: Double,
152
+ paddingBottom: Double,
153
+ paddingRight: Double,
154
+ duration: Double
155
+ ) {
148
156
  val coords = mutableListOf<LatLng>()
149
157
  coordinates?.let {
150
158
  for (i in 0 until it.size()) {
@@ -154,7 +162,7 @@ class LuggGoogleMapViewManager :
154
162
  coords.add(LatLng(lat, lng))
155
163
  }
156
164
  }
157
- view.fitCoordinates(coords, padding.toInt(), duration.toInt())
165
+ view.fitCoordinates(coords, paddingTop.toInt(), paddingLeft.toInt(), paddingBottom.toInt(), paddingRight.toInt(), duration.toInt())
158
166
  }
159
167
 
160
168
  companion object {
@@ -1,7 +1,6 @@
1
1
  package com.luggmaps
2
2
 
3
3
  import android.content.Context
4
- import android.util.Log
5
4
  import android.view.View
6
5
  import androidx.core.view.isNotEmpty
7
6
  import com.facebook.react.views.view.ReactViewGroup
@@ -34,6 +33,9 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
34
33
  var anchorY: Float = 1.0f
35
34
  private set
36
35
 
36
+ var zIndex: Float = 0f
37
+ private set
38
+
37
39
  var didLayout: Boolean = false
38
40
  private set
39
41
 
@@ -114,6 +116,10 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) {
114
116
  anchorY = y.toFloat()
115
117
  }
116
118
 
119
+ fun setZIndex(zIndex: Float) {
120
+ this.zIndex = zIndex
121
+ }
122
+
117
123
  fun setName(name: String?) {
118
124
  this.name = name
119
125
  }
@@ -63,6 +63,12 @@ class LuggMarkerViewManager :
63
63
  }
64
64
  }
65
65
 
66
+ @ReactProp(name = "zIndex", defaultFloat = 0f)
67
+ override fun setZIndex(view: LuggMarkerView, zIndex: Float) {
68
+ super.setZIndex(view, zIndex)
69
+ view.setZIndex(zIndex)
70
+ }
71
+
66
72
  companion object {
67
73
  const val NAME = "LuggMarkerView"
68
74
  }
@@ -25,6 +25,9 @@ class LuggPolylineView(context: Context) : ReactViewGroup(context) {
25
25
  var animated: Boolean = false
26
26
  private set
27
27
 
28
+ var zIndex: Float = 0f
29
+ private set
30
+
28
31
  var cachedSpans: List<StyleSpan>? = null
29
32
  private set
30
33
 
@@ -55,6 +58,10 @@ class LuggPolylineView(context: Context) : ReactViewGroup(context) {
55
58
  animated = value
56
59
  }
57
60
 
61
+ fun setZIndex(value: Float) {
62
+ zIndex = value
63
+ }
64
+
58
65
  fun getOrCreateSpans(): List<StyleSpan> {
59
66
  cachedSpans?.let { return it }
60
67
 
@@ -65,6 +65,12 @@ class LuggPolylineViewManager :
65
65
  view.setAnimated(value)
66
66
  }
67
67
 
68
+ @ReactProp(name = "zIndex", defaultFloat = 0f)
69
+ override fun setZIndex(view: LuggPolylineView, value: Float) {
70
+ super.setZIndex(view, value)
71
+ view.setZIndex(value)
72
+ }
73
+
68
74
  companion object {
69
75
  const val NAME = "LuggPolylineView"
70
76
  }
@@ -2,18 +2,24 @@ 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
10
11
  import kotlin.math.floor
11
- import kotlin.math.max
12
12
  import kotlin.math.min
13
13
 
14
14
  class PolylineAnimator {
15
15
  var polyline: Polyline? = null
16
16
  var coordinates: List<LatLng> = emptyList()
17
+ set(value) {
18
+ field = value
19
+ if (animated && animator != null) {
20
+ computeCumulativeDistances()
21
+ }
22
+ }
17
23
  var strokeColors: List<Int> = listOf(Color.BLACK)
18
24
  var strokeWidth: Float = 1f
19
25
 
@@ -31,6 +37,8 @@ class PolylineAnimator {
31
37
 
32
38
  private var animator: ValueAnimator? = null
33
39
  private var animationProgress: Float = 0f
40
+ private var cumulativeDistances: FloatArray = floatArrayOf()
41
+ private var totalLength: Float = 0f
34
42
 
35
43
  fun update() {
36
44
  if (animated) return
@@ -50,8 +58,10 @@ class PolylineAnimator {
50
58
  private fun startAnimation() {
51
59
  if (animator != null) return
52
60
 
61
+ computeCumulativeDistances()
62
+
53
63
  animator = ValueAnimator.ofFloat(0f, 2.15f).apply {
54
- duration = 3650 // ~1.75s per phase * 2 + pause
64
+ duration = 2150
55
65
  repeatCount = ValueAnimator.INFINITE
56
66
  interpolator = LinearInterpolator()
57
67
  addUpdateListener { animation ->
@@ -62,6 +72,58 @@ class PolylineAnimator {
62
72
  }
63
73
  }
64
74
 
75
+ private fun computeCumulativeDistances() {
76
+ if (coordinates.size < 2) {
77
+ cumulativeDistances = floatArrayOf(0f)
78
+ totalLength = 0f
79
+ return
80
+ }
81
+
82
+ val distances = FloatArray(coordinates.size)
83
+ distances[0] = 0f
84
+ var total = 0f
85
+
86
+ for (i in 1 until coordinates.size) {
87
+ val prev = coordinates[i - 1]
88
+ val curr = coordinates[i]
89
+ val results = FloatArray(1)
90
+ Location.distanceBetween(prev.latitude, prev.longitude, curr.latitude, curr.longitude, results)
91
+ total += results[0]
92
+ distances[i] = total
93
+ }
94
+
95
+ cumulativeDistances = distances
96
+ totalLength = total
97
+ }
98
+
99
+ private fun indexForDistance(distance: Float): Int {
100
+ for (i in 1 until cumulativeDistances.size) {
101
+ if (cumulativeDistances[i] >= distance) {
102
+ return i - 1
103
+ }
104
+ }
105
+ return (cumulativeDistances.size - 2).coerceAtLeast(0)
106
+ }
107
+
108
+ private fun coordinateAtDistance(distance: Float): LatLng {
109
+ if (distance <= 0f) return coordinates.first()
110
+ if (distance >= totalLength) return coordinates.last()
111
+
112
+ val idx = indexForDistance(distance)
113
+ val segStart = cumulativeDistances[idx]
114
+ val segEnd = cumulativeDistances[idx + 1]
115
+ val segLength = segEnd - segStart
116
+
117
+ val t = if (segLength > 0) (distance - segStart) / segLength else 0f
118
+ val c1 = coordinates[idx]
119
+ val c2 = coordinates[idx + 1]
120
+
121
+ return LatLng(
122
+ c1.latitude + (c2.latitude - c1.latitude) * t,
123
+ c1.longitude + (c2.longitude - c1.longitude) * t
124
+ )
125
+ }
126
+
65
127
  private fun stopAnimation() {
66
128
  animator?.cancel()
67
129
  animator = null
@@ -69,71 +131,55 @@ class PolylineAnimator {
69
131
 
70
132
  private fun updateAnimatedPolyline() {
71
133
  val poly = polyline ?: return
72
- if (coordinates.size < 2) {
134
+ if (coordinates.size < 2 || totalLength <= 0f) {
73
135
  poly.points = coordinates
74
136
  return
75
137
  }
76
138
 
77
- val segmentCount = coordinates.size - 1
78
139
  val progress = min(animationProgress, 2f)
79
140
 
80
- val headPos: Float
81
- val tailPos: Float
141
+ val headDist: Float
142
+ val tailDist: Float
82
143
 
83
144
  if (progress <= 1f) {
84
- tailPos = 0f
85
- headPos = progress * segmentCount
145
+ tailDist = 0f
146
+ headDist = progress * totalLength
86
147
  } else {
87
148
  val shrinkProgress = progress - 1f
88
- tailPos = shrinkProgress * segmentCount
89
- headPos = segmentCount.toFloat()
149
+ tailDist = shrinkProgress * totalLength
150
+ headDist = totalLength
90
151
  }
91
152
 
92
- if (headPos <= tailPos || coordinates.isEmpty()) {
153
+ if (headDist <= tailDist) {
93
154
  poly.setSpans(emptyList())
94
155
  poly.points = listOf(coordinates.firstOrNull() ?: LatLng(0.0, 0.0))
95
156
  return
96
157
  }
97
158
 
98
- val startIndex = floor(tailPos).toInt()
99
- val endIndex = kotlin.math.ceil(headPos.toDouble()).toInt()
100
- val visibleLength = headPos - tailPos
159
+ val visibleLength = headDist - tailDist
160
+ val startIndex = indexForDistance(tailDist)
161
+ val endIndex = indexForDistance(headDist)
101
162
 
102
163
  val points = mutableListOf<LatLng>()
103
164
  val spans = mutableListOf<StyleSpan>()
104
165
 
105
- for (i in startIndex..minOf(endIndex, coordinates.size - 1)) {
106
- var coord = coordinates[i]
107
-
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
- }
166
+ points.add(coordinateAtDistance(tailDist))
117
167
 
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
- }
168
+ for (i in (startIndex + 1)..endIndex) {
169
+ points.add(coordinates[i])
170
+ }
127
171
 
128
- points.add(coord)
172
+ val endCoord = coordinateAtDistance(headDist)
173
+ val lastAdded = points.lastOrNull()
174
+ if (lastAdded == null || endCoord.latitude != lastAdded.latitude || endCoord.longitude != lastAdded.longitude) {
175
+ points.add(endCoord)
176
+ }
129
177
 
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
- }
178
+ for (i in 0 until (points.size - 1)) {
179
+ val segMidDist = tailDist + visibleLength * (i + 0.5f) / (points.size - 1)
180
+ val gradientPos = (segMidDist - tailDist) / visibleLength
181
+ val color = colorAtGradientPosition(gradientPos)
182
+ spans.add(StyleSpan(StrokeStyle.colorBuilder(color).build()))
137
183
  }
138
184
 
139
185
  poly.points = points
@@ -161,6 +161,10 @@ using namespace luggmaps::events;
161
161
  _maxZoom = viewProps.maxZoom;
162
162
  [self applyZoomRange];
163
163
 
164
+ _mapView.layoutMargins =
165
+ UIEdgeInsetsMake(viewProps.padding.top, viewProps.padding.left,
166
+ viewProps.padding.bottom, viewProps.padding.right);
167
+
164
168
  [_mapWrapperView addSubview:_mapView];
165
169
 
166
170
  [self setCameraWithLatitude:viewProps.initialCoordinate.latitude
@@ -298,7 +302,7 @@ using namespace luggmaps::events;
298
302
  free(coords);
299
303
 
300
304
  polylineView.polyline = polyline;
301
- [_mapView addOverlay:polyline];
305
+ [self insertOverlay:polyline withZIndex:polylineView.zIndex];
302
306
  }
303
307
 
304
308
  - (void)syncPolylineView:(LuggPolylineView *)polylineView {
@@ -312,6 +316,9 @@ using namespace luggmaps::events;
312
316
  // Build new polyline from coordinates
313
317
  NSArray<CLLocation *> *coordinates = polylineView.coordinates;
314
318
  if (coordinates.count == 0) {
319
+ if (renderer) {
320
+ renderer.animated = NO;
321
+ }
315
322
  if (oldPolyline) {
316
323
  [_mapView removeOverlay:oldPolyline];
317
324
  polylineView.polyline = nil;
@@ -333,6 +340,8 @@ using namespace luggmaps::events;
333
340
 
334
341
  // If we have an existing renderer, update it in place
335
342
  if (renderer && oldPolyline) {
343
+ [_mapView removeOverlay:oldPolyline];
344
+ [self insertOverlay:newPolyline withZIndex:polylineView.zIndex];
336
345
  [renderer updatePolyline:newPolyline];
337
346
  renderer.lineWidth = polylineView.strokeWidth;
338
347
  renderer.strokeColor = polylineView.strokeColors.firstObject;
@@ -346,7 +355,28 @@ using namespace luggmaps::events;
346
355
  if (oldPolyline) {
347
356
  [_mapView removeOverlay:oldPolyline];
348
357
  }
349
- [_mapView addOverlay:newPolyline];
358
+ [self insertOverlay:newPolyline withZIndex:polylineView.zIndex];
359
+ }
360
+
361
+ - (void)insertOverlay:(id<MKOverlay>)overlay withZIndex:(NSInteger)zIndex {
362
+ if (zIndex == 0) {
363
+ [_mapView addOverlay:overlay];
364
+ return;
365
+ }
366
+
367
+ NSArray<id<MKOverlay>> *overlays = _mapView.overlays;
368
+ NSInteger insertIndex = overlays.count;
369
+
370
+ for (NSInteger i = 0; i < overlays.count; i++) {
371
+ LuggPolylineView *existingPolylineView =
372
+ [self findPolylineViewForOverlay:overlays[i]];
373
+ if (existingPolylineView && existingPolylineView.zIndex > zIndex) {
374
+ insertIndex = i;
375
+ break;
376
+ }
377
+ }
378
+
379
+ [_mapView insertOverlay:overlay atIndex:insertIndex];
350
380
  }
351
381
 
352
382
  - (LuggPolylineView *)findPolylineViewForOverlay:(id<MKOverlay>)overlay {
@@ -384,6 +414,12 @@ using namespace luggmaps::events;
384
414
  annotation.title = markerView.title;
385
415
  annotation.subtitle = markerView.markerDescription;
386
416
 
417
+ MKAnnotationView *annotationView = annotation.annotationView;
418
+ if (annotationView) {
419
+ annotationView.layer.zPosition = markerView.zIndex;
420
+ annotationView.zPriority = markerView.zIndex;
421
+ }
422
+
387
423
  [self updateAnnotationViewFrame:annotation];
388
424
  }
389
425
 
@@ -438,7 +474,8 @@ using namespace luggmaps::events;
438
474
  reuseIdentifier:nil];
439
475
  annotationView.canShowCallout = YES;
440
476
  annotationView.displayPriority = MKFeatureDisplayPriorityRequired;
441
- annotationView.collisionMode = MKAnnotationViewCollisionModeNone;
477
+ annotationView.layer.zPosition = markerView.zIndex;
478
+ annotationView.zPriority = markerView.zIndex;
442
479
 
443
480
  UIView *iconView = markerView.iconView;
444
481
  [iconView removeFromSuperview];
@@ -523,7 +560,10 @@ using namespace luggmaps::events;
523
560
  }
524
561
 
525
562
  - (void)fitCoordinates:(NSArray *)coordinates
526
- padding:(double)padding
563
+ paddingTop:(double)paddingTop
564
+ paddingLeft:(double)paddingLeft
565
+ paddingBottom:(double)paddingBottom
566
+ paddingRight:(double)paddingRight
527
567
  duration:(double)duration {
528
568
  if (!_mapView || coordinates.count == 0) {
529
569
  return;
@@ -546,7 +586,7 @@ using namespace luggmaps::events;
546
586
  free(coords);
547
587
 
548
588
  UIEdgeInsets edgePadding =
549
- UIEdgeInsetsMake(padding, padding, padding, padding);
589
+ UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight);
550
590
 
551
591
  if (duration < 0) {
552
592
  [_mapView setVisibleMapRect:mapRect edgePadding:edgePadding animated:YES];
@@ -168,6 +168,10 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
168
168
  [_mapView setMinZoom:_mapView.minZoom maxZoom:(float)viewProps.maxZoom];
169
169
  }
170
170
 
171
+ _mapView.padding =
172
+ UIEdgeInsetsMake(viewProps.padding.top, viewProps.padding.left,
173
+ viewProps.padding.bottom, viewProps.padding.right);
174
+
171
175
  [_mapWrapperView addSubview:_mapView];
172
176
 
173
177
  _isMapReady = YES;
@@ -238,14 +242,15 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
238
242
  marker.position = markerView.coordinate;
239
243
  marker.title = markerView.title;
240
244
  marker.snippet = markerView.markerDescription;
241
- marker.groundAnchor = markerView.anchor;
242
-
245
+ marker.zIndex = (int)markerView.zIndex;
243
246
  if (markerView.hasCustomView) {
244
247
  UIView *iconView = markerView.iconView;
245
248
  [iconView removeFromSuperview];
246
249
  marker.iconView = iconView;
250
+ marker.groundAnchor = markerView.anchor;
247
251
  } else {
248
252
  marker.iconView = nil;
253
+ marker.groundAnchor = CGPointMake(0.5, 1);
249
254
  }
250
255
  }
251
256
 
@@ -273,13 +278,13 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
273
278
  marker.position = markerView.coordinate;
274
279
  marker.title = markerView.title;
275
280
  marker.snippet = markerView.markerDescription;
276
- marker.collisionBehavior = GMSCollisionBehaviorRequired;
277
281
 
278
282
  if (markerView.hasCustomView) {
279
283
  marker.iconView = iconView;
284
+ marker.groundAnchor = markerView.anchor;
280
285
  }
281
286
 
282
- marker.groundAnchor = markerView.anchor;
287
+ marker.zIndex = (int)markerView.zIndex;
283
288
  marker.map = _mapView;
284
289
 
285
290
  markerView.marker = marker;
@@ -302,6 +307,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
302
307
 
303
308
  GMSPolyline *polyline = (GMSPolyline *)polylineView.polyline;
304
309
  polyline.strokeWidth = polylineView.strokeWidth;
310
+ polyline.zIndex = (int)polylineView.zIndex;
305
311
 
306
312
  GMSPolylineAnimator *animator =
307
313
  [_polylineAnimators objectForKey:polylineView];
@@ -331,6 +337,7 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
331
337
 
332
338
  GMSPolyline *polyline = [GMSPolyline polylineWithPath:[GMSMutablePath path]];
333
339
  polyline.strokeWidth = polylineView.strokeWidth;
340
+ polyline.zIndex = (int)polylineView.zIndex;
334
341
  polyline.map = _mapView;
335
342
  polylineView.polyline = polyline;
336
343
 
@@ -406,7 +413,10 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
406
413
  }
407
414
 
408
415
  - (void)fitCoordinates:(NSArray *)coordinates
409
- padding:(double)padding
416
+ paddingTop:(double)paddingTop
417
+ paddingLeft:(double)paddingLeft
418
+ paddingBottom:(double)paddingBottom
419
+ paddingRight:(double)paddingRight
410
420
  duration:(double)duration {
411
421
  if (!_mapView || coordinates.count == 0) {
412
422
  return;
@@ -419,8 +429,10 @@ static NSString *const kDemoMapId = @"DEMO_MAP_ID";
419
429
  bounds = [bounds includingCoordinate:CLLocationCoordinate2DMake(lat, lng)];
420
430
  }
421
431
 
432
+ UIEdgeInsets edgePadding =
433
+ UIEdgeInsetsMake(paddingTop, paddingLeft, paddingBottom, paddingRight);
422
434
  GMSCameraUpdate *cameraUpdate = [GMSCameraUpdate fitBounds:bounds
423
- withPadding:padding];
435
+ withEdgeInsets:edgePadding];
424
436
 
425
437
  if (duration < 0) {
426
438
  [_mapView animateWithCameraUpdate:cameraUpdate];
@@ -14,10 +14,12 @@ 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;
21
23
  @property(nonatomic, readonly) BOOL hasCustomView;
22
24
  @property(nonatomic, readonly) BOOL didLayout;
23
25
  @property(nonatomic, readonly) UIView *iconView;