Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import com.google.android.gms.maps.model.Polyline
import com.google.android.gms.maps.model.PolylineOptions
import com.google.android.gms.maps.model.TileOverlay
import com.google.android.gms.maps.model.TileOverlayOptions
import com.google.maps.android.data.kml.KmlLayer
import com.rngooglemapsplus.extensions.toGooglePriority
import com.rngooglemapsplus.extensions.toLocationErrorCode
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets

class GoogleMapsViewImpl(
val reactContext: ThemedReactContext,
Expand All @@ -55,12 +58,14 @@ class GoogleMapsViewImpl(
private val pendingPolygons = mutableListOf<Pair<String, PolygonOptions>>()
private val pendingCircles = mutableListOf<Pair<String, CircleOptions>>()
private val pendingHeatmaps = mutableListOf<Pair<String, TileOverlayOptions>>()
private val pendingKmlLayers = mutableListOf<Pair<String, String>>()

private val markersById = mutableMapOf<String, Marker>()
private val polylinesById = mutableMapOf<String, Polyline>()
private val polygonsById = mutableMapOf<String, Polygon>()
private val circlesById = mutableMapOf<String, Circle>()
private val heatmapsById = mutableMapOf<String, TileOverlay>()
private val kmlLayersById = mutableMapOf<String, KmlLayer>()

private var cameraMoveReason = -1
private var lastSubmittedLocation: Location? = null
Expand Down Expand Up @@ -343,6 +348,13 @@ class GoogleMapsViewImpl(
}
pendingHeatmaps.clear()
}

if (pendingKmlLayers.isNotEmpty()) {
pendingKmlLayers.forEach { (id, string) ->
internalAddKmlLayer(id, string)
}
pendingKmlLayers.clear()
}
}

var uiSettings: RNMapUiSettings? = null
Expand Down Expand Up @@ -825,6 +837,50 @@ class GoogleMapsViewImpl(
pendingHeatmaps.clear()
}

fun addKmlLayer(
id: String,
kmlString: String,
) {
if (googleMap == null) {
pendingKmlLayers.add(id to kmlString)
return
}
onUi {
kmlLayersById.remove(id)?.removeLayerFromMap()
}
internalAddKmlLayer(id, kmlString)
}

private fun internalAddKmlLayer(
id: String,
kmlString: String,
) {
onUi {
try {
val inputStream = ByteArrayInputStream(kmlString.toByteArray(StandardCharsets.UTF_8))
val layer = KmlLayer(googleMap, inputStream, context)
kmlLayersById[id] = layer
layer.addLayerToMap()
} catch (e: Exception) {
// / ignore
}
}
}

fun removeKmlLayer(id: String) {
onUi {
kmlLayersById.remove(id)?.removeLayerFromMap()
}
}

fun clearKmlLayer() {
onUi {
kmlLayersById.values.forEach { it.removeLayerFromMap() }
}
kmlLayersById.clear()
pendingKmlLayers.clear()
}

fun destroyInternal() {
onUi {
markerBuilder.cancelAllJobs()
Expand All @@ -833,6 +889,7 @@ class GoogleMapsViewImpl(
clearPolygons()
clearCircles()
clearHeatmaps()
clearKmlLayer()
locationHandler.stop()
googleMap?.apply {
setOnCameraMoveStartedListener(null)
Expand Down
14 changes: 14 additions & 0 deletions android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,20 @@ class RNGoogleMapsPlusView(
}
}

override var kmlLayers: Array<RNKMLayer>? = null
set(value) {
if (field.contentEquals(value)) return
val prevById = field?.associateBy { it.id } ?: emptyMap()
val nextById = value?.associateBy { it.id } ?: emptyMap()
field = value
(prevById.keys - nextById.keys).forEach { id ->
view.removeKmlLayer(id)
}
nextById.forEach { (id, next) ->
view.addKmlLayer(id, next.kmlString)
}
}

override var locationConfig: RNLocationConfig? = null
set(value) {
if (field == value) return
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2358,7 +2358,7 @@ PODS:
- React-perflogger (= 0.82.0)
- React-utils (= 0.82.0)
- SocketRocket
- RNGoogleMapsPlus (1.1.0-dev.2):
- RNGoogleMapsPlus (1.1.0-dev.5):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2713,7 +2713,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: c5c4f5280e4ae0f9f4a739c64c4260fe0b3edaf1
ReactCodegen: 3873d7ac09960375f7845384ff47d53e478462dc
ReactCommon: f5527f5d97a9957ab46eb5db78875d3579e03b97
RNGoogleMapsPlus: 9b638ea84ab0231430a8b5a109a20130ad4a722a
RNGoogleMapsPlus: cdea400ea1e69740d91e07dbb5882d93be4c0a77
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SVGKit: 1ad7513f8c74d9652f94ed64ddecda1a23864dea
Yoga: ce55ebb197c21e22b6700cd36e3f36b7ec26e6f8
Expand Down
75 changes: 75 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,78 @@ const silverMapStyle: RNMapStyleElement[] = [
},
];

const kmlString = `
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Example KML Data</name>
<description>Example with marker, polygon and circle shifted further northeast of San Francisco</description>

<Placemark>
<name>Center Point</name>
<Point>
<coordinates>-122.4156,37.7781,0</coordinates>
</Point>
</Placemark>

<Placemark>
<name>Example Polygon</name>
<Style>
<LineStyle>
<color>ff0000ff</color>
<width>2</width>
</LineStyle>
<PolyStyle>
<color>7d00ff00</color>
</PolyStyle>
</Style>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-122.4206,37.7826,0
-122.4106,37.7826,0
-122.4106,37.7746,0
-122.4206,37.7746,0
-122.4206,37.7826,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>

<Placemark>
<name>Approximate Circle</name>
<Style>
<LineStyle>
<color>ffff0000</color>
<width>2</width>
</LineStyle>
<PolyStyle>
<color>3dff0000</color>
</PolyStyle>
</Style>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-122.4156,37.7801,0
-122.4136,37.7801,0
-122.4136,37.7761,0
-122.4156,37.7761,0
-122.4176,37.7761,0
-122.4176,37.7801,0
-122.4156,37.7801,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>

</Document>
</kml>
`.trim();

function makeSvgIcon(width: number, height: number): string {
const color = randomColor();
return `
Expand Down Expand Up @@ -369,6 +441,8 @@ export default function App() {
Array.from({ length: 1 }, (_, i) => makeHeatmap(i + 1))
);

const [kmlLayers] = useState([{ id: '21', zIndex: 1, kmlString }]);

useEffect(() => {
if (!stressTest) return;

Expand Down Expand Up @@ -543,6 +617,7 @@ export default function App() {
polylines={polylines}
circles={circles}
heatmaps={heatmaps}
kmlLayers={kmlLayers}
/>

<ScrollView style={styles.scrollView}>
Expand Down
44 changes: 44 additions & 0 deletions ios/GoogleMapViewImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
private var pendingPolygons: [(id: String, polygon: GMSPolygon)] = []
private var pendingCircles: [(id: String, circle: GMSCircle)] = []
private var pendingHeatmaps: [(id: String, heatmap: GMUHeatmapTileLayer)] = []
private var pendingKmlLayers: [(id: String, kmlString: String)] = []

private var markersById: [String: GMSMarker] = [:]
private var polylinesById: [String: GMSPolyline] = [:]
private var polygonsById: [String: GMSPolygon] = [:]
private var circlesById: [String: GMSCircle] = [:]
private var heatmapsById: [String: GMUHeatmapTileLayer] = [:]
private var kmlLayerById: [String: GMUGeometryRenderer] = [:]

private var cameraMoveReasonIsGesture: Bool = false
private var lastSubmittedCameraPosition: GMSCameraPosition?
Expand Down Expand Up @@ -177,6 +179,12 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
}
pendingHeatmaps.removeAll()
}
if !pendingKmlLayers.isEmpty {
pendingKmlLayers.forEach {
addKmlLayerInternal(id: $0.id, kmlString: $0.kmlString)
}
pendingKmlLayers.removeAll()
}
}

var currentCamera: GMSCameraPosition? {
Expand Down Expand Up @@ -524,6 +532,42 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
pendingHeatmaps.removeAll()
}

@MainActor
func addKmlLayer(id: String, kmlString: String) {
if mapView == nil {
pendingKmlLayers.append((id, kmlString))
return
}
kmlLayerById.removeValue(forKey: id).map { $0.clear() }
addKmlLayerInternal(id: id, kmlString: kmlString)
}

@MainActor
private func addKmlLayerInternal(id: String, kmlString: String) {
guard let data = kmlString.data(using: .utf8) else { return }
let parser = GMUKMLParser(data: data)
parser.parse()
mapView.map { mapView in
let renderer = GMUGeometryRenderer(
map: mapView,
geometries: parser.placemarks
)
renderer.render()
}
}

@MainActor
func removeKmlLayer(id: String) {
kmlLayerById.removeValue(forKey: id).map { $0.clear() }
}

@MainActor
func clearKmlLayers() {
kmlLayerById.values.forEach { $0.clear() }
kmlLayerById.removeAll()
pendingKmlLayers.removeAll()
}

func deinitInternal() {
markerBuilder.cancelAllIconTasks()
clearMarkers()
Expand Down
21 changes: 21 additions & 0 deletions ios/RNGoogleMapsPlusView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,27 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec {
}
}

@MainActor
var kmlLayers: [RNKMLayer]? {
didSet {
let prevById = Dictionary(
(oldValue ?? []).map { ($0.id, $0) },
uniquingKeysWith: { _, new in new }
)
let nextById = Dictionary(
(kmlLayers ?? []).map { ($0.id, $0) },
uniquingKeysWith: { _, new in new }
)

let removed = Set(prevById.keys).subtracting(nextById.keys)
removed.forEach { impl.removeKmlLayer(id: $0) }

for (id, next) in nextById {
impl.addKmlLayer(id: id, kmlString: next.kmlString)
}
}
}

@MainActor var locationConfig: RNLocationConfig? {
didSet {
impl.locationConfig = locationConfig
Expand Down
2 changes: 2 additions & 0 deletions src/RNGoogleMapsPlusView.nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
RNLocationConfig,
RNMapZoomConfig,
RNHeatmap,
RNKMLayer,
} from './types';

export interface RNGoogleMapsPlusViewProps extends HybridViewProps {
Expand All @@ -42,6 +43,7 @@ export interface RNGoogleMapsPlusViewProps extends HybridViewProps {
polylines?: RNPolyline[];
circles?: RNCircle[];
heatmaps?: RNHeatmap[];
kmlLayers?: RNKMLayer[];
locationConfig?: RNLocationConfig;
onMapError?: (error: RNMapErrorCode) => void;
onMapReady?: (ready: boolean) => void;
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ export type RNHeatmapGradient = {
colorMapSize: number;
};

export type RNKMLayer = {
id: string;
kmlString: string;
};

export type RNLocationConfig = {
android?: RNAndroidLocationConfig;
ios?: RNIOSLocationConfig;
Expand Down
Loading