react-native-google-maps-plus 1.7.0-dev.9 → 1.7.0

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 (46) hide show
  1. package/android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt +120 -124
  2. package/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt +33 -0
  3. package/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt +27 -11
  4. package/android/src/main/java/com/rngooglemapsplus/extensions/BitmapExtension.kt +35 -0
  5. package/android/src/main/java/com/rngooglemapsplus/extensions/LatLngBoundsExtension.kt +31 -0
  6. package/android/src/main/java/com/rngooglemapsplus/extensions/MapObjectTagExtensions.kt +84 -0
  7. package/ios/GoogleMapViewImpl.swift +81 -64
  8. package/ios/MapMarkerBuilder.swift +55 -2
  9. package/ios/RNGoogleMapsPlusView.swift +20 -10
  10. package/ios/extensions/MapObjectTag+Extension.swift +93 -0
  11. package/ios/extensions/UIImage+Extension.swift +45 -0
  12. package/lib/module/types.js.map +1 -1
  13. package/lib/typescript/src/RNGoogleMapsPlusView.nitro.d.ts +12 -10
  14. package/lib/typescript/src/RNGoogleMapsPlusView.nitro.d.ts.map +1 -1
  15. package/lib/typescript/src/types.d.ts +1 -0
  16. package/lib/typescript/src/types.d.ts.map +1 -1
  17. package/nitrogen/generated/android/RNGoogleMapsPlusOnLoad.cpp +4 -4
  18. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +75 -0
  19. package/nitrogen/generated/android/c++/JFunc_void_std__string_RNLatLng.hpp +77 -0
  20. package/nitrogen/generated/android/c++/JHybridRNGoogleMapsPlusViewSpec.cpp +100 -92
  21. package/nitrogen/generated/android/c++/JHybridRNGoogleMapsPlusViewSpec.hpp +22 -20
  22. package/nitrogen/generated/android/c++/JRNMarker.hpp +7 -3
  23. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/{Func_void_std__optional_std__string_.kt → Func_void_std__string.kt} +12 -12
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/{Func_void_std__optional_std__string__RNLatLng.kt → Func_void_std__string_RNLatLng.kt} +12 -12
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/HybridRNGoogleMapsPlusViewSpec.kt +38 -30
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngooglemapsplus/RNMarker.kt +6 -3
  27. package/nitrogen/generated/ios/RNGoogleMapsPlus-Swift-Cxx-Bridge.cpp +16 -8
  28. package/nitrogen/generated/ios/RNGoogleMapsPlus-Swift-Cxx-Bridge.hpp +58 -36
  29. package/nitrogen/generated/ios/c++/HybridRNGoogleMapsPlusViewSpecSwift.hpp +32 -20
  30. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +6 -6
  31. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  32. package/nitrogen/generated/ios/swift/Func_void_std__string_RNLatLng.swift +47 -0
  33. package/nitrogen/generated/ios/swift/HybridRNGoogleMapsPlusViewSpec.swift +12 -10
  34. package/nitrogen/generated/ios/swift/HybridRNGoogleMapsPlusViewSpec_cxx.swift +142 -180
  35. package/nitrogen/generated/ios/swift/RNMarker.swift +24 -1
  36. package/nitrogen/generated/shared/c++/HybridRNGoogleMapsPlusViewSpec.cpp +2 -0
  37. package/nitrogen/generated/shared/c++/HybridRNGoogleMapsPlusViewSpec.hpp +22 -20
  38. package/nitrogen/generated/shared/c++/RNMarker.hpp +6 -2
  39. package/nitrogen/generated/shared/c++/views/HybridRNGoogleMapsPlusViewComponent.cpp +20 -20
  40. package/nitrogen/generated/shared/c++/views/HybridRNGoogleMapsPlusViewComponent.hpp +10 -10
  41. package/package.json +1 -1
  42. package/src/RNGoogleMapsPlusView.nitro.ts +14 -10
  43. package/src/types.ts +1 -0
  44. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__string_.hpp +0 -76
  45. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__string__RNLatLng.hpp +0 -78
  46. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string__RNLatLng.swift +0 -54
@@ -0,0 +1,35 @@
1
+ package com.rngooglemapsplus.extensions
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.util.Base64
6
+ import android.util.Size
7
+ import androidx.core.graphics.scale
8
+ import java.io.ByteArrayOutputStream
9
+ import java.io.File
10
+ import java.io.FileOutputStream
11
+
12
+ fun Bitmap.encode(
13
+ context: Context,
14
+ targetSize: Size?,
15
+ format: String,
16
+ compressFormat: Bitmap.CompressFormat,
17
+ quality: Double,
18
+ asFile: Boolean,
19
+ ): String? =
20
+ try {
21
+ targetSize?.let { scale(it.width, it.height) }
22
+ val output = ByteArrayOutputStream()
23
+ compress(compressFormat, (quality * 100).toInt().coerceIn(0, 100), output)
24
+ val bytes = output.toByteArray()
25
+
26
+ if (asFile) {
27
+ val file = File(context.cacheDir, "snapshot_${System.currentTimeMillis()}.$format")
28
+ FileOutputStream(file).use { it.write(bytes) }
29
+ file.absolutePath
30
+ } else {
31
+ "data:image/$format;base64," + Base64.encodeToString(bytes, Base64.NO_WRAP)
32
+ }
33
+ } catch (_: Exception) {
34
+ null
35
+ }
@@ -1,10 +1,41 @@
1
1
  package com.rngooglemapsplus.extensions
2
2
 
3
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
4
+ import com.google.android.gms.maps.model.LatLng
3
5
  import com.google.android.gms.maps.model.LatLngBounds
4
6
  import com.rngooglemapsplus.RNLatLngBounds
7
+ import com.rngooglemapsplus.RNMapPadding
5
8
 
6
9
  fun LatLngBounds.toRnLatLngBounds(): RNLatLngBounds =
7
10
  RNLatLngBounds(
8
11
  northeast = northeast.toRnLatLng(),
9
12
  southwest = southwest.toRnLatLng(),
10
13
  )
14
+
15
+ fun LatLngBounds.withPaddingPixels(
16
+ mapWidthPx: Int,
17
+ mapHeightPx: Int,
18
+ padding: RNMapPadding,
19
+ ): LatLngBounds {
20
+ val latSpan = northeast.latitude - southwest.latitude
21
+ val lngSpan = northeast.longitude - southwest.longitude
22
+ if (latSpan == 0.0 && lngSpan == 0.0) return this
23
+
24
+ val latPerPixel = if (mapHeightPx != 0) latSpan / mapHeightPx else 0.0
25
+ val lngPerPixel = if (mapWidthPx != 0) lngSpan / mapWidthPx else 0.0
26
+
27
+ val builder = LatLngBounds.builder()
28
+ builder.include(
29
+ LatLng(
30
+ northeast.latitude + (padding.top.dpToPx() * latPerPixel),
31
+ northeast.longitude + (padding.right.dpToPx() * lngPerPixel),
32
+ ),
33
+ )
34
+ builder.include(
35
+ LatLng(
36
+ southwest.latitude - (padding.bottom.dpToPx() * latPerPixel),
37
+ southwest.longitude - (padding.left.dpToPx() * lngPerPixel),
38
+ ),
39
+ )
40
+ return builder.build()
41
+ }
@@ -0,0 +1,84 @@
1
+ import android.util.Log
2
+ import com.google.android.gms.maps.model.Circle
3
+ import com.google.android.gms.maps.model.Marker
4
+ import com.google.android.gms.maps.model.Polygon
5
+ import com.google.android.gms.maps.model.Polyline
6
+ import com.rngooglemapsplus.RNMarkerSvg
7
+
8
+ sealed class MapObjectTag(
9
+ open val id: String,
10
+ )
11
+
12
+ data class MarkerTag(
13
+ override val id: String,
14
+ val iconSvg: RNMarkerSvg? = null,
15
+ ) : MapObjectTag(id)
16
+
17
+ data class PolylineTag(
18
+ override val id: String,
19
+ ) : MapObjectTag(id)
20
+
21
+ data class PolygonTag(
22
+ override val id: String,
23
+ ) : MapObjectTag(id)
24
+
25
+ data class CircleTag(
26
+ override val id: String,
27
+ ) : MapObjectTag(id)
28
+
29
+ val Marker.tagData: MarkerTag
30
+ get() =
31
+ (tag as? MarkerTag) ?: run {
32
+ Log.w("MapTag", "Marker without tag detected at $position")
33
+ val fallback = MarkerTag(id = "unknown")
34
+ tag = fallback
35
+ fallback
36
+ }
37
+
38
+ val Marker.idTag: String
39
+ get() = tagData.id
40
+
41
+ var Polyline.tagData: PolylineTag
42
+ get() =
43
+ (tag as? PolylineTag) ?: run {
44
+ Log.w("MapTag", "Polyline without tag detected")
45
+ val fallback = PolylineTag(id = "unknown")
46
+ tag = fallback
47
+ fallback
48
+ }
49
+ set(value) {
50
+ tag = value
51
+ }
52
+
53
+ val Polyline.idTag: String
54
+ get() = tagData.id
55
+
56
+ var Polygon.tagData: PolygonTag
57
+ get() =
58
+ (tag as? PolygonTag) ?: run {
59
+ Log.w("MapTag", "Polygon without tag detected")
60
+ val fallback = PolygonTag(id = "unknown")
61
+ tag = fallback
62
+ fallback
63
+ }
64
+ set(value) {
65
+ tag = value
66
+ }
67
+
68
+ val Polygon.idTag: String
69
+ get() = tagData.id
70
+
71
+ var Circle.tagData: CircleTag
72
+ get() =
73
+ (tag as? CircleTag) ?: run {
74
+ Log.w("MapTag", "Circle without tag detected")
75
+ val fallback = CircleTag(id = "unknown")
76
+ tag = fallback
77
+ fallback
78
+ }
79
+ set(value) {
80
+ tag = value
81
+ }
82
+
83
+ val Circle.idTag: String
84
+ get() = tagData.id
@@ -267,24 +267,43 @@ GMSIndoorDisplayDelegate {
267
267
  var onMapPress: ((RNLatLng) -> Void)?
268
268
  var onMapLongPress: ((RNLatLng) -> Void)?
269
269
  var onPoiPress: ((String, String, RNLatLng) -> Void)?
270
- var onMarkerPress: ((String?) -> Void)?
271
- var onPolylinePress: ((String?) -> Void)?
272
- var onPolygonPress: ((String?) -> Void)?
273
- var onCirclePress: ((String?) -> Void)?
274
- var onMarkerDragStart: ((String?, RNLatLng) -> Void)?
275
- var onMarkerDrag: ((String?, RNLatLng) -> Void)?
276
- var onMarkerDragEnd: ((String?, RNLatLng) -> Void)?
270
+ var onMarkerPress: ((String) -> Void)?
271
+ var onPolylinePress: ((String) -> Void)?
272
+ var onPolygonPress: ((String) -> Void)?
273
+ var onCirclePress: ((String) -> Void)?
274
+ var onMarkerDragStart: ((String, RNLatLng) -> Void)?
275
+ var onMarkerDrag: ((String, RNLatLng) -> Void)?
276
+ var onMarkerDragEnd: ((String, RNLatLng) -> Void)?
277
277
  var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Void)?
278
278
  var onIndoorLevelActivated: ((RNIndoorLevel) -> Void)?
279
- var onInfoWindowPress: ((String?) -> Void)?
280
- var onInfoWindowClose: ((String?) -> Void)?
281
- var onInfoWindowLongPress: ((String?) -> Void)?
279
+ var onInfoWindowPress: ((String) -> Void)?
280
+ var onInfoWindowClose: ((String) -> Void)?
281
+ var onInfoWindowLongPress: ((String) -> Void)?
282
282
  var onMyLocationPress: ((RNLocation) -> Void)?
283
283
  var onMyLocationButtonPress: ((Bool) -> Void)?
284
284
  var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)?
285
285
  var onCameraChange: ((RNRegion, RNCamera, Bool) -> Void)?
286
286
  var onCameraChangeComplete: ((RNRegion, RNCamera, Bool) -> Void)?
287
287
 
288
+ @MainActor
289
+ func showMarkerInfoWindow(id: String) {
290
+ onMain {
291
+ guard let marker = self.markersById[id] else { return }
292
+ self.mapView?.selectedMarker = nil
293
+ self.mapView?.selectedMarker = marker
294
+ }
295
+ }
296
+
297
+ @MainActor
298
+ func hideMarkerInfoWindow(id: String) {
299
+ onMain {
300
+ guard let marker = self.markersById[id] else { return }
301
+ if self.mapView?.selectedMarker == marker {
302
+ self.mapView?.selectedMarker = nil
303
+ }
304
+ }
305
+ }
306
+
288
307
  @MainActor
289
308
  func setCamera(camera: GMSCameraPosition, animated: Bool, durationMs: Double) {
290
309
  if animated {
@@ -380,42 +399,16 @@ GMSIndoorDisplayDelegate {
380
399
  mapView.layer.render(in: ctx.cgContext)
381
400
  }
382
401
 
383
- var finalImage = image
384
-
385
- size.map {
386
- UIGraphicsBeginImageContextWithOptions($0, false, 0.0)
387
- image.draw(in: CGRect(origin: .zero, size: $0))
388
- finalImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
389
- UIGraphicsEndImageContext()
390
- }
391
-
392
- let data: Data?
393
- switch imageFormat {
394
- case .jpeg:
395
- data = finalImage.jpegData(compressionQuality: quality)
396
- case .png:
397
- data = finalImage.pngData()
398
- }
399
-
400
- guard let imageData = data else {
401
- promise.resolve(withResult: nil)
402
- return
403
- }
404
-
405
- if resultIsFile {
406
- let filename =
407
- "map_snapshot_\(Int(Date().timeIntervalSince1970)).\(format)"
408
- let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
409
- .appendingPathComponent(filename)
410
- do {
411
- try imageData.write(to: fileURL)
412
- promise.resolve(withResult: fileURL.path)
413
- } catch {
414
- promise.resolve(withResult: nil)
415
- }
402
+ if let result = image.encode(
403
+ targetSize: size,
404
+ format: format,
405
+ imageFormat: imageFormat,
406
+ quality: quality,
407
+ resultIsFile: resultIsFile
408
+ ) {
409
+ promise.resolve(withResult: result)
416
410
  } else {
417
- let base64 = imageData.base64EncodedString()
418
- promise.resolve(withResult: "data:image/\(format);base64,\(base64)")
411
+ promise.resolve(withResult: nil)
419
412
  }
420
413
  }
421
414
 
@@ -434,14 +427,19 @@ GMSIndoorDisplayDelegate {
434
427
 
435
428
  @MainActor
436
429
  private func addMarkerInternal(id: String, marker: GMSMarker) {
437
- marker.userData = id
438
430
  marker.map = mapView
439
431
  markersById[id] = marker
440
432
  }
441
433
 
442
434
  @MainActor
443
435
  func updateMarker(id: String, block: @escaping (GMSMarker) -> Void) {
444
- markersById[id].map { block($0) }
436
+ markersById[id].map {
437
+ block($0)
438
+ if let mapView, mapView.selectedMarker == $0 {
439
+ mapView.selectedMarker = nil
440
+ mapView.selectedMarker = $0
441
+ }
442
+ }
445
443
  }
446
444
 
447
445
  @MainActor
@@ -468,8 +466,8 @@ GMSIndoorDisplayDelegate {
468
466
 
469
467
  @MainActor
470
468
  private func addPolylineInternal(id: String, polyline: GMSPolyline) {
469
+ polyline.tagData = PolylineTag(id: id)
471
470
  polyline.map = mapView
472
- polyline.userData = id
473
471
  polylinesById[id] = polyline
474
472
  }
475
473
 
@@ -502,8 +500,8 @@ GMSIndoorDisplayDelegate {
502
500
 
503
501
  @MainActor
504
502
  private func addPolygonInternal(id: String, polygon: GMSPolygon) {
503
+ polygon.tagData = PolygonTag(id: id)
505
504
  polygon.map = mapView
506
- polygon.userData = id
507
505
  polygonsById[id] = polygon
508
506
  }
509
507
 
@@ -536,8 +534,8 @@ GMSIndoorDisplayDelegate {
536
534
 
537
535
  @MainActor
538
536
  private func addCircleInternal(id: String, circle: GMSCircle) {
537
+ circle.tagData = CircleTag(id: id)
539
538
  circle.map = mapView
540
- circle.userData = id
541
539
  circlesById[id] = circle
542
540
  }
543
541
 
@@ -576,7 +574,10 @@ GMSIndoorDisplayDelegate {
576
574
 
577
575
  @MainActor
578
576
  func removeHeatmap(id: String) {
579
- heatmapsById.removeValue(forKey: id).map { $0.map = nil }
577
+ heatmapsById.removeValue(forKey: id).map {
578
+ $0.clearTileCache()
579
+ $0.map = nil
580
+ }
580
581
  }
581
582
 
582
583
  @MainActor
@@ -610,6 +611,7 @@ GMSIndoorDisplayDelegate {
610
611
  geometries: parser.placemarks
611
612
  )
612
613
  renderer.render()
614
+ kmlLayerById[id] = renderer
613
615
  }
614
616
  }
615
617
 
@@ -641,16 +643,23 @@ GMSIndoorDisplayDelegate {
641
643
  urlTileOverlay: GMSURLTileLayer
642
644
  ) {
643
645
  urlTileOverlay.map = mapView
646
+ urlTileOverlays[id] = urlTileOverlay
644
647
  }
645
648
 
646
649
  @MainActor
647
650
  func removeUrlTileOverlay(id: String) {
648
- urlTileOverlays.removeValue(forKey: id).map { $0.map = nil }
651
+ urlTileOverlays.removeValue(forKey: id).map {
652
+ $0.clearTileCache()
653
+ $0.map = nil
654
+ }
649
655
  }
650
656
 
651
657
  @MainActor
652
658
  func clearUrlTileOverlay() {
653
- urlTileOverlays.values.forEach { $0.map = nil }
659
+ urlTileOverlays.values.forEach {
660
+ $0.clearTileCache()
661
+ $0.map = nil
662
+ }
654
663
  urlTileOverlays.removeAll()
655
664
  pendingUrlTileOverlays.removeAll()
656
665
  }
@@ -778,8 +787,7 @@ GMSIndoorDisplayDelegate {
778
787
 
779
788
  func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
780
789
  onMain {
781
- mapView.selectedMarker = marker
782
- self.onMarkerPress?(marker.userData as? String, )
790
+ self.onMarkerPress?(marker.idTag)
783
791
  }
784
792
  return uiSettings?.consumeOnMarkerPress ?? false
785
793
  }
@@ -788,13 +796,13 @@ GMSIndoorDisplayDelegate {
788
796
  onMain {
789
797
  switch overlay {
790
798
  case let circle as GMSCircle:
791
- self.onCirclePress?(circle.userData as? String, )
799
+ self.onCirclePress?(circle.idTag)
792
800
 
793
801
  case let polygon as GMSPolygon:
794
- self.onPolygonPress?(polygon.userData as? String, )
802
+ self.onPolygonPress?(polygon.idTag)
795
803
 
796
804
  case let polyline as GMSPolyline:
797
- self.onPolylinePress?(polyline.userData as? String, )
805
+ self.onPolylinePress?(polyline.idTag)
798
806
 
799
807
  default:
800
808
  break
@@ -805,7 +813,7 @@ GMSIndoorDisplayDelegate {
805
813
  func mapView(_ mapView: GMSMapView, didBeginDragging marker: GMSMarker) {
806
814
  onMain {
807
815
  self.onMarkerDragStart?(
808
- marker.userData as? String,
816
+ marker.idTag,
809
817
  marker.position.toRNLatLng()
810
818
  )
811
819
  }
@@ -814,7 +822,7 @@ GMSIndoorDisplayDelegate {
814
822
  func mapView(_ mapView: GMSMapView, didDrag marker: GMSMarker) {
815
823
  onMain {
816
824
  self.onMarkerDrag?(
817
- marker.userData as? String,
825
+ marker.idTag,
818
826
  marker.position.toRNLatLng()
819
827
  )
820
828
  }
@@ -823,7 +831,7 @@ GMSIndoorDisplayDelegate {
823
831
  func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
824
832
  onMain {
825
833
  self.onMarkerDragEnd?(
826
- marker.userData as? String,
834
+ marker.idTag,
827
835
  marker.position.toRNLatLng()
828
836
  )
829
837
  }
@@ -857,13 +865,13 @@ GMSIndoorDisplayDelegate {
857
865
 
858
866
  func mapView(_ mapView: GMSMapView, didTapInfoWindowOf marker: GMSMarker) {
859
867
  onMain {
860
- self.onInfoWindowPress?(marker.userData as? String)
868
+ self.onInfoWindowPress?(marker.idTag)
861
869
  }
862
870
  }
863
871
 
864
872
  func mapView(_ mapView: GMSMapView, didCloseInfoWindowOf marker: GMSMarker) {
865
873
  onMain {
866
- self.onInfoWindowClose?(marker.userData as? String)
874
+ self.onInfoWindowClose?(marker.idTag)
867
875
  }
868
876
  }
869
877
 
@@ -872,7 +880,7 @@ GMSIndoorDisplayDelegate {
872
880
  didLongPressInfoWindowOf marker: GMSMarker
873
881
  ) {
874
882
  onMain {
875
- self.onInfoWindowLongPress?(marker.userData as? String)
883
+ self.onInfoWindowLongPress?(marker.idTag)
876
884
  }
877
885
  }
878
886
 
@@ -893,4 +901,13 @@ GMSIndoorDisplayDelegate {
893
901
  }
894
902
  return uiSettings?.consumeOnMyLocationButtonPress ?? false
895
903
  }
904
+
905
+ func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
906
+ return markerBuilder.buildInfoWindow(iconSvg: marker.tagData.iconSvg)
907
+ }
908
+
909
+ func mapView(_ mapView: GMSMapView, markerInfoContents marker: GMSMarker)
910
+ -> UIView? {
911
+ return nil
912
+ }
896
913
  }
@@ -15,12 +15,14 @@ final class MapMarkerBuilder {
15
15
  let marker = GMSMarker(
16
16
  position: m.coordinate.toCLLocationCoordinate2D()
17
17
  )
18
- marker.userData = m.id
19
18
  marker.tracksViewChanges = true
20
19
  marker.icon = icon
21
20
  m.title.map { marker.title = $0 }
22
21
  m.snippet.map { marker.snippet = $0 }
23
- m.opacity.map { marker.iconView?.alpha = CGFloat($0) }
22
+ m.opacity.map {
23
+ marker.opacity = Float($0)
24
+ marker.iconView?.alpha = CGFloat($0)
25
+ }
24
26
  m.flat.map { marker.isFlat = $0 }
25
27
  m.draggable.map { marker.isDraggable = $0 }
26
28
  m.rotation.map { marker.rotation = $0 }
@@ -35,6 +37,11 @@ final class MapMarkerBuilder {
35
37
  }
36
38
  m.zIndex.map { marker.zIndex = Int32($0) }
37
39
 
40
+ marker.tagData = MarkerTag(
41
+ id: m.id,
42
+ iconSvg: m.infoWindowIconSvg
43
+ )
44
+
38
45
  onMainAsync { [weak marker] in
39
46
  try? await Task.sleep(nanoseconds: 250_000_000)
40
47
  marker?.tracksViewChanges = false
@@ -120,6 +127,12 @@ final class MapMarkerBuilder {
120
127
  y: next.infoWindowAnchor?.y ?? 0
121
128
  )
122
129
  }
130
+
131
+ m.tagData = MarkerTag(
132
+ id: next.id,
133
+ iconSvg: next.infoWindowIconSvg
134
+ )
135
+
123
136
  }
124
137
  }
125
138
 
@@ -177,6 +190,46 @@ final class MapMarkerBuilder {
177
190
  iconCache.removeAllObjects()
178
191
  }
179
192
 
193
+ @MainActor
194
+ func buildInfoWindow(iconSvg: RNMarkerSvg?) -> UIImageView? {
195
+ guard let iconSvg = iconSvg else {
196
+ return nil
197
+ }
198
+
199
+ guard let data = iconSvg.svgString.data(using: .utf8),
200
+ let svgImg = SVGKImage(data: data)
201
+ else {
202
+ return nil
203
+ }
204
+
205
+ let size = CGSize(
206
+ width: max(1, CGFloat(iconSvg.width)),
207
+ height: max(1, CGFloat(iconSvg.height))
208
+ )
209
+
210
+ svgImg.size = size
211
+
212
+ guard let base = svgImg.uiImage else {
213
+ return nil
214
+ }
215
+
216
+ let fmt = UIGraphicsImageRendererFormat.default()
217
+ fmt.opaque = false
218
+ fmt.scale = UIScreen.main.scale
219
+ let renderer = UIGraphicsImageRenderer(size: size, format: fmt)
220
+
221
+ let finalImage = renderer.image { _ in
222
+ base.draw(in: CGRect(origin: .zero, size: size))
223
+ }
224
+
225
+ let imageView = UIImageView(image: finalImage)
226
+ imageView.frame = CGRect(origin: .zero, size: size)
227
+ imageView.contentMode = .scaleAspectFit
228
+ imageView.backgroundColor = .clear
229
+
230
+ return imageView
231
+ }
232
+
180
233
  @MainActor
181
234
  private func renderUIImage(_ m: RNMarker, _ scale: CGFloat) async -> UIImage? {
182
235
  guard let iconSvg = m.iconSvg,
@@ -349,31 +349,31 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec {
349
349
  didSet { impl.onPoiPress = onPoiPress }
350
350
  }
351
351
  @MainActor
352
- var onMarkerPress: ((String?) -> Void)? {
352
+ var onMarkerPress: ((String) -> Void)? {
353
353
  didSet { impl.onMarkerPress = onMarkerPress }
354
354
  }
355
355
  @MainActor
356
- var onPolylinePress: ((String?) -> Void)? {
356
+ var onPolylinePress: ((String) -> Void)? {
357
357
  didSet { impl.onPolylinePress = onPolylinePress }
358
358
  }
359
359
  @MainActor
360
- var onPolygonPress: ((String?) -> Void)? {
360
+ var onPolygonPress: ((String) -> Void)? {
361
361
  didSet { impl.onPolygonPress = onPolygonPress }
362
362
  }
363
363
  @MainActor
364
- var onCirclePress: ((String?) -> Void)? {
364
+ var onCirclePress: ((String) -> Void)? {
365
365
  didSet { impl.onCirclePress = onCirclePress }
366
366
  }
367
367
  @MainActor
368
- var onMarkerDragStart: ((String?, RNLatLng) -> Void)? {
368
+ var onMarkerDragStart: ((String, RNLatLng) -> Void)? {
369
369
  didSet { impl.onMarkerDragStart = onMarkerDragStart }
370
370
  }
371
371
  @MainActor
372
- var onMarkerDrag: ((String?, RNLatLng) -> Void)? {
372
+ var onMarkerDrag: ((String, RNLatLng) -> Void)? {
373
373
  didSet { impl.onMarkerDrag = onMarkerDrag }
374
374
  }
375
375
  @MainActor
376
- var onMarkerDragEnd: ((String?, RNLatLng) -> Void)? {
376
+ var onMarkerDragEnd: ((String, RNLatLng) -> Void)? {
377
377
  didSet { impl.onMarkerDragEnd = onMarkerDragEnd }
378
378
  }
379
379
  @MainActor
@@ -385,15 +385,15 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec {
385
385
  didSet { impl.onIndoorLevelActivated = onIndoorLevelActivated }
386
386
  }
387
387
  @MainActor
388
- var onInfoWindowPress: ((String?) -> Void)? {
388
+ var onInfoWindowPress: ((String) -> Void)? {
389
389
  didSet { impl.onInfoWindowPress = onInfoWindowPress }
390
390
  }
391
391
  @MainActor
392
- var onInfoWindowClose: ((String?) -> Void)? {
392
+ var onInfoWindowClose: ((String) -> Void)? {
393
393
  didSet { impl.onInfoWindowClose = onInfoWindowClose }
394
394
  }
395
395
  @MainActor
396
- var onInfoWindowLongPress: ((String?) -> Void)? {
396
+ var onInfoWindowLongPress: ((String) -> Void)? {
397
397
  didSet { impl.onInfoWindowLongPress = onInfoWindowLongPress }
398
398
  }
399
399
  @MainActor
@@ -417,6 +417,16 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec {
417
417
  didSet { impl.onCameraChangeComplete = onCameraChangeComplete }
418
418
  }
419
419
 
420
+ @MainActor
421
+ func showMarkerInfoWindow(id: String) {
422
+ impl.showMarkerInfoWindow(id: id);
423
+ }
424
+
425
+ @MainActor
426
+ func hideMarkerInfoWindow(id: String) {
427
+ impl.hideMarkerInfoWindow(id: id);
428
+ }
429
+
420
430
  @MainActor
421
431
  func setCamera(camera: RNCamera, animated: Bool?, durationMs: Double?) {
422
432
  let cam = camera.toGMSCameraPosition(current: impl.currentCamera)
@@ -0,0 +1,93 @@
1
+ import GoogleMaps
2
+ import Foundation
3
+
4
+ protocol MapObjectTag {
5
+ var id: String { get }
6
+ }
7
+
8
+ struct MarkerTag: MapObjectTag {
9
+ let id: String
10
+ let iconSvg: RNMarkerSvg?
11
+ init(id: String, iconSvg: RNMarkerSvg? = nil) {
12
+ self.id = id
13
+ self.iconSvg = iconSvg
14
+ }
15
+ }
16
+
17
+ struct PolylineTag: MapObjectTag { let id: String }
18
+ struct PolygonTag: MapObjectTag { let id: String }
19
+ struct CircleTag: MapObjectTag { let id: String }
20
+
21
+ extension GMSMarker {
22
+ var tagData: MarkerTag {
23
+ get {
24
+ if let tag = userData as? MarkerTag {
25
+ return tag
26
+ } else {
27
+ print("[MapTag] Marker without tag detected at \(position)")
28
+ let fallback = MarkerTag(id: "unknown")
29
+ userData = fallback
30
+ return fallback
31
+ }
32
+ }
33
+ set {
34
+ userData = newValue
35
+ }
36
+ }
37
+
38
+ var idTag: String { tagData.id }
39
+ }
40
+
41
+ extension GMSPolyline {
42
+ var tagData: PolylineTag {
43
+ get {
44
+ if let tag = userData as? PolylineTag {
45
+ return tag
46
+ } else {
47
+ print("[MapTag] Polyline without tag detected")
48
+ let fallback = PolylineTag(id: "unknown")
49
+ userData = fallback
50
+ return fallback
51
+ }
52
+ }
53
+ set { userData = newValue }
54
+ }
55
+
56
+ var idTag: String { tagData.id }
57
+ }
58
+
59
+ extension GMSPolygon {
60
+ var tagData: PolygonTag {
61
+ get {
62
+ if let tag = userData as? PolygonTag {
63
+ return tag
64
+ } else {
65
+ print("[MapTag] Polygon without tag detected")
66
+ let fallback = PolygonTag(id: "unknown")
67
+ userData = fallback
68
+ return fallback
69
+ }
70
+ }
71
+ set { userData = newValue }
72
+ }
73
+
74
+ var idTag: String { tagData.id }
75
+ }
76
+
77
+ extension GMSCircle {
78
+ var tagData: CircleTag {
79
+ get {
80
+ if let tag = userData as? CircleTag {
81
+ return tag
82
+ } else {
83
+ print("[MapTag] Circle without tag detected")
84
+ let fallback = CircleTag(id: "unknown")
85
+ userData = fallback
86
+ return fallback
87
+ }
88
+ }
89
+ set { userData = newValue }
90
+ }
91
+
92
+ var idTag: String { tagData.id }
93
+ }