diff --git a/android/build.gradle b/android/build.gradle index b9d8673..7e3f97c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -136,6 +136,6 @@ dependencies { implementation "com.google.android.gms:play-services-base:${getExtOrDefault('googlePlayServicesBaseVersion')}" implementation "com.google.android.gms:play-services-maps:${getExtOrDefault('googlePlayServicesMapsVersion')}" implementation "com.google.android.gms:play-services-location:${getExtOrDefault('googlePlayServicesLocationVersion')}" - implementation 'com.google.maps.android:android-maps-utils:3.10.0' + implementation "com.google.maps.android:android-maps-utils:${getExtOrDefault('mapsUtilsVersion')}" implementation 'com.caverock:androidsvg-aar:1.4' } diff --git a/android/gradle.properties b/android/gradle.properties index 9da52ae..14981ef 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,6 +3,7 @@ RNGoogleMapsPlus_minSdkVersion=26 RNGoogleMapsPlus_targetSdkVersion=36 RNGoogleMapsPlus_compileSdkVersion=36 RNGoogleMapsPlus_ndkVersion=27.1.12297006 -RNGoogleMapsPlus_googlePlayServicesBaseVersion=18.8.0 +RNGoogleMapsPlus_googlePlayServicesBaseVersion=18.9.0 RNGoogleMapsPlus_googlePlayServicesMapsVersion=19.2.0 RNGoogleMapsPlus_googlePlayServicesLocationVersion=21.3.0 +RNGoogleMapsPlus_mapsUtilsVersion=3.19.0 diff --git a/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt index 6079629..6c06f49 100644 --- a/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt +++ b/android/src/main/java/com/rngooglemapsplus/MapCircleBuilder.kt @@ -4,6 +4,7 @@ import android.graphics.Color import com.facebook.react.uimanager.PixelUtil.dpToPx import com.google.android.gms.maps.model.Circle import com.google.android.gms.maps.model.CircleOptions +import com.rngooglemapsplus.extensions.centerEquals import com.rngooglemapsplus.extensions.onUi import com.rngooglemapsplus.extensions.toColor import com.rngooglemapsplus.extensions.toLatLng @@ -25,9 +26,7 @@ class MapCircleBuilder { next: RNCircle, circle: Circle, ) = onUi { - if (prev.center.latitude != next.center.latitude || - prev.center.longitude != next.center.longitude - ) { + if (!prev.centerEquals(next)) { circle.center = next.center.toLatLng() } diff --git a/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt index aaa924d..bb618e5 100644 --- a/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt +++ b/android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt @@ -19,6 +19,10 @@ import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.MarkerOptions +import com.rngooglemapsplus.extensions.anchorEquals +import com.rngooglemapsplus.extensions.coordinatesEquals +import com.rngooglemapsplus.extensions.infoWindowAnchorEquals +import com.rngooglemapsplus.extensions.markerInfoWindowStyleEquals import com.rngooglemapsplus.extensions.markerStyleEquals import com.rngooglemapsplus.extensions.onUi import com.rngooglemapsplus.extensions.styleHash @@ -73,7 +77,10 @@ class MapMarkerBuilder( val height = (svg.documentHeight.takeIf { it > 0 } ?: 128f).toInt() createBitmap(width, height).apply { - Canvas(this).also(svg::renderToCanvas) + density = context.resources.displayMetrics.densityDpi + Canvas(this).also { + svg.renderToCanvas(it) + } } } @@ -94,10 +101,12 @@ class MapMarkerBuilder( val innerSvg = SVG.getFromString(svgText) val w = innerSvg.documentWidth.takeIf { it > 0 } ?: 128f val h = innerSvg.documentHeight.takeIf { it > 0 } ?: 128f - val bmp = createBitmap(w.toInt(), h.toInt()) - val canvas = Canvas(bmp) - innerSvg.renderToCanvas(canvas) - bmp + createBitmap(w.toInt(), h.toInt()).apply { + density = context.resources.displayMetrics.densityDpi + Canvas(this).also { + innerSvg.renderToCanvas(it) + } + } } else { conn.inputStream.use { BitmapFactory.decodeStream(it) } } @@ -169,51 +178,39 @@ class MapMarkerBuilder( next: RNMarker, marker: Marker, ) = onUi { - if (prev.coordinate.latitude != next.coordinate.latitude || - prev.coordinate.longitude != next.coordinate.longitude - ) { + if (!prev.coordinatesEquals(next)) { marker.position = next.coordinate.toLatLng() } if (!prev.markerStyleEquals(next)) { - buildIconAsync(marker.id, next) { icon -> + buildIconAsync(next) { icon -> marker.setIcon(icon) - if (prev.infoWindowAnchor?.x != next.infoWindowAnchor?.x || - prev.infoWindowAnchor?.y != next.infoWindowAnchor?.y - ) { - marker.setInfoWindowAnchor( - (next.infoWindowAnchor?.x ?: 0.5f).toFloat(), - (next.infoWindowAnchor?.y ?: 0f).toFloat(), - ) - } - - if (prev.anchor?.x != next.anchor?.x || - prev.anchor?.y != next.anchor?.y - ) { + if (!prev.anchorEquals(next)) { marker.setAnchor( (next.anchor?.x ?: 0.5f).toFloat(), (next.anchor?.y ?: 1.0f).toFloat(), ) } + if (!prev.infoWindowAnchorEquals(next)) { + marker.setInfoWindowAnchor( + (next.infoWindowAnchor?.x ?: 0.5f).toFloat(), + (next.infoWindowAnchor?.y ?: 0f).toFloat(), + ) + } } } else { - if (prev.infoWindowAnchor?.x != next.infoWindowAnchor?.x || - prev.infoWindowAnchor?.y != next.infoWindowAnchor?.y - ) { - marker.setInfoWindowAnchor( - (next.infoWindowAnchor?.x ?: 0.5f).toFloat(), - (next.infoWindowAnchor?.y ?: 0f).toFloat(), - ) - } - - if (prev.anchor?.x != next.anchor?.x || - prev.anchor?.y != next.anchor?.y - ) { + if (!prev.anchorEquals(next)) { marker.setAnchor( (next.anchor?.x ?: 0.5f).toFloat(), (next.anchor?.y ?: 1.0f).toFloat(), ) } + if (!prev.infoWindowAnchorEquals(next)) { + marker.setInfoWindowAnchor( + (next.infoWindowAnchor?.x ?: 0.5f).toFloat(), + (next.infoWindowAnchor?.y ?: 0f).toFloat(), + ) + } } if (prev.title != next.title) { @@ -244,17 +241,16 @@ class MapMarkerBuilder( marker.zIndex = next.zIndex?.toFloat() ?: 0f } - if (prev.infoWindowIconSvg != next.infoWindowIconSvg) { + if (!prev.markerInfoWindowStyleEquals(next)) { marker.tag = MarkerTag(id = next.id, iconSvg = next.infoWindowIconSvg) } } fun buildIconAsync( - id: String, m: RNMarker, onReady: (BitmapDescriptor?) -> Unit, ) { - jobsById[id]?.cancel() + jobsById[m.id]?.cancel() m.iconSvg ?: return onReady(null) @@ -283,11 +279,11 @@ class MapMarkerBuilder( iconCache.evictAll() } catch (_: Throwable) { } finally { - jobsById.remove(id) + jobsById.remove(m.id) } } - jobsById[id] = job + jobsById[m.id] = job } fun cancelIconJob(id: String) { @@ -337,27 +333,28 @@ class MapMarkerBuilder( coroutineContext.ensureActive() val svg = SVG.getFromString(m.iconSvg.svgString) - coroutineContext.ensureActive() - svg.setDocumentWidth(m.iconSvg.width.dpToPx()) - svg.setDocumentHeight(m.iconSvg.height.dpToPx()) + val wPx = + m.iconSvg.width + .dpToPx() + .toInt() + val hPx = + m.iconSvg.height + .dpToPx() + .toInt() coroutineContext.ensureActive() - bmp = - createBitmap( - m.iconSvg.width - .dpToPx() - .toInt(), - m.iconSvg.height - .dpToPx() - .toInt(), - Bitmap.Config.ARGB_8888, - ) + svg.setDocumentWidth(wPx.toFloat()) + svg.setDocumentHeight(hPx.toFloat()) coroutineContext.ensureActive() - val canvas = Canvas(bmp) - svg.renderToCanvas(canvas) + bmp = + createBitmap(wPx, hPx, Bitmap.Config.ARGB_8888).apply { + density = context.resources.displayMetrics.densityDpi + Canvas(this).also { + svg.renderToCanvas(it) + } + } - coroutineContext.ensureActive() return bmp } catch (t: Throwable) { try { diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt b/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt index b8079bd..c3f0f87 100644 --- a/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt +++ b/android/src/main/java/com/rngooglemapsplus/MapPolygonBuilder.kt @@ -4,9 +4,12 @@ import android.graphics.Color import com.facebook.react.uimanager.PixelUtil.dpToPx import com.google.android.gms.maps.model.Polygon import com.google.android.gms.maps.model.PolygonOptions +import com.rngooglemapsplus.extensions.coordinatesEquals +import com.rngooglemapsplus.extensions.holesEquals import com.rngooglemapsplus.extensions.onUi import com.rngooglemapsplus.extensions.toColor import com.rngooglemapsplus.extensions.toLatLng +import com.rngooglemapsplus.extensions.toMapsPolygonHoles class MapPolygonBuilder { fun build(poly: RNPolygon): PolygonOptions = @@ -32,32 +35,12 @@ class MapPolygonBuilder { next: RNPolygon, poly: Polygon, ) = onUi { - val coordsChanged = - prev.coordinates.size != next.coordinates.size || - !prev.coordinates.zip(next.coordinates).all { (a, b) -> - a.latitude == b.latitude && a.longitude == b.longitude - } - - if (coordsChanged) { + if (!prev.coordinatesEquals(next)) { poly.points = next.coordinates.map { it.toLatLng() } } - val prevHoles = prev.holes?.toList() ?: emptyList() - val nextHoles = next.holes?.toList() ?: emptyList() - val holesChanged = - prevHoles.size != nextHoles.size || - !prevHoles.zip(nextHoles).all { (ha, hb) -> - ha.coordinates.size == hb.coordinates.size && - ha.coordinates.zip(hb.coordinates).all { (a, b) -> - a.latitude == b.latitude && a.longitude == b.longitude - } - } - - if (holesChanged) { - poly.holes = - nextHoles.map { hole -> - hole.coordinates.map { it.toLatLng() } - } + if (!prev.holesEquals(next)) { + poly.holes = next.holes?.toMapsPolygonHoles() ?: emptyList() } if (prev.fillColor != next.fillColor) { diff --git a/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt b/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt index 681682a..abe3958 100644 --- a/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt +++ b/android/src/main/java/com/rngooglemapsplus/MapPolylineBuilder.kt.kt @@ -2,16 +2,14 @@ package com.rngooglemapsplus import android.graphics.Color import com.facebook.react.uimanager.PixelUtil.dpToPx -import com.google.android.gms.maps.model.ButtCap -import com.google.android.gms.maps.model.Cap -import com.google.android.gms.maps.model.JointType import com.google.android.gms.maps.model.Polyline import com.google.android.gms.maps.model.PolylineOptions -import com.google.android.gms.maps.model.RoundCap -import com.google.android.gms.maps.model.SquareCap +import com.rngooglemapsplus.extensions.coordinatesEquals import com.rngooglemapsplus.extensions.onUi import com.rngooglemapsplus.extensions.toColor import com.rngooglemapsplus.extensions.toLatLng +import com.rngooglemapsplus.extensions.toMapJointType +import com.rngooglemapsplus.extensions.toMapLineCap class MapPolylineBuilder { fun build(pl: RNPolyline): PolylineOptions = @@ -21,10 +19,10 @@ class MapPolylineBuilder { } pl.width?.let { width(it.dpToPx()) } pl.lineCap?.let { - startCap(mapLineCap(it)) - endCap(mapLineCap(it)) + startCap(it.toMapLineCap()) + endCap(it.toMapLineCap()) } - pl.lineJoin?.let { jointType(mapLineJoin(it)) } + pl.lineJoin?.let { jointType(it.toMapJointType()) } pl.color?.let { color(it.toColor()) } pl.geodesic?.let { geodesic(it) } pl.pressable?.let { clickable(it) } @@ -36,13 +34,7 @@ class MapPolylineBuilder { next: RNPolyline, polyline: Polyline, ) = onUi { - val coordsChanged = - prev.coordinates.size != next.coordinates.size || - !prev.coordinates.zip(next.coordinates).all { (a, b) -> - a.latitude == b.latitude && a.longitude == b.longitude - } - - if (coordsChanged) { + if (!prev.coordinatesEquals(next)) { polyline.points = next.coordinates.map { it.toLatLng() } } @@ -50,17 +42,13 @@ class MapPolylineBuilder { polyline.width = next.width?.dpToPx() ?: 1f } - val newCap = mapLineCap(next.lineCap ?: RNLineCapType.BUTT) - val prevCap = mapLineCap(prev.lineCap ?: RNLineCapType.BUTT) - if (newCap != prevCap) { - polyline.startCap = newCap - polyline.endCap = newCap + if (prev.lineCap != next.lineCap) { + polyline.startCap = next.lineCap.toMapLineCap() + polyline.endCap = next.lineCap.toMapLineCap() } - val newJoin = mapLineJoin(next.lineJoin ?: RNLineJoinType.MITER) - val prevJoin = mapLineJoin(prev.lineJoin ?: RNLineJoinType.MITER) - if (newJoin != prevJoin) { - polyline.jointType = newJoin + if (prev.lineJoin != next.lineJoin) { + polyline.jointType = next.lineJoin.toMapJointType() } if (prev.color != next.color) { @@ -79,19 +67,4 @@ class MapPolylineBuilder { polyline.zIndex = next.zIndex?.toFloat() ?: 0f } } - - private fun mapLineCap(type: RNLineCapType?): Cap = - when (type) { - RNLineCapType.ROUND -> RoundCap() - RNLineCapType.SQUARE -> SquareCap() - else -> ButtCap() - } - - private fun mapLineJoin(type: RNLineJoinType?): Int = - when (type) { - RNLineJoinType.ROUND -> JointType.ROUND - RNLineJoinType.BEVEL -> JointType.BEVEL - RNLineJoinType.MITER -> JointType.DEFAULT - null -> JointType.DEFAULT - } } diff --git a/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt b/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt index 556f385..acb7aa0 100644 --- a/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt +++ b/android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt @@ -12,6 +12,7 @@ import com.rngooglemapsplus.extensions.markerEquals import com.rngooglemapsplus.extensions.polygonEquals import com.rngooglemapsplus.extensions.polylineEquals import com.rngooglemapsplus.extensions.toCameraPosition +import com.rngooglemapsplus.extensions.toColor import com.rngooglemapsplus.extensions.toCompressFormat import com.rngooglemapsplus.extensions.toFileExtension import com.rngooglemapsplus.extensions.toGoogleMapType @@ -48,6 +49,7 @@ class RNGoogleMapsPlusView( initialProps?.mapId?.let { mapId(it) } initialProps?.liteMode?.let { liteMode(it) } initialProps?.camera?.let { camera(it.toCameraPosition(current = null)) } + initialProps?.backgroundColor?.let { backgroundColor(it.toColor()) } } view.initMapView(options) } @@ -149,7 +151,7 @@ class RNGoogleMapsPlusView( val prev = prevById[id] when { prev == null -> - markerBuilder.buildIconAsync(id, next) { icon -> + markerBuilder.buildIconAsync(next) { icon -> view.addMarker( id, markerBuilder.build(next, icon), diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNLineCapTypeExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNLineCapTypeExtension.kt new file mode 100644 index 0000000..77042aa --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNLineCapTypeExtension.kt @@ -0,0 +1,14 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.maps.model.ButtCap +import com.google.android.gms.maps.model.Cap +import com.google.android.gms.maps.model.RoundCap +import com.google.android.gms.maps.model.SquareCap +import com.rngooglemapsplus.RNLineCapType + +fun RNLineCapType?.toMapLineCap(): Cap = + when (this) { + RNLineCapType.ROUND -> RoundCap() + RNLineCapType.SQUARE -> SquareCap() + else -> ButtCap() + } diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNLineJoinTypeExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNLineJoinTypeExtension.kt new file mode 100644 index 0000000..0251e96 --- /dev/null +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNLineJoinTypeExtension.kt @@ -0,0 +1,12 @@ +package com.rngooglemapsplus.extensions + +import com.google.android.gms.maps.model.JointType +import com.rngooglemapsplus.RNLineJoinType + +fun RNLineJoinType?.toMapJointType(): Int = + when (this) { + RNLineJoinType.ROUND -> JointType.ROUND + RNLineJoinType.BEVEL -> JointType.BEVEL + RNLineJoinType.MITER -> JointType.DEFAULT + null -> JointType.DEFAULT + } diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt index 8de84e4..7af8902 100644 --- a/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNMapCircleExtension.kt @@ -3,12 +3,18 @@ package com.rngooglemapsplus.extensions import com.rngooglemapsplus.RNCircle fun RNCircle.circleEquals(b: RNCircle): Boolean { + if (!centerEquals(b)) return false if (zIndex != b.zIndex) return false if (pressable != b.pressable) return false - if (center != b.center) return false if (radius != b.radius) return false if (strokeWidth != b.strokeWidth) return false if (strokeColor != b.strokeColor) return false if (fillColor != b.fillColor) return false return true } + +fun RNCircle.centerEquals(b: RNCircle): Boolean { + if (center.latitude != b.center.latitude) return false + if (center.longitude != b.center.longitude) return false + return true +} diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt index 6da266d..fa2025d 100644 --- a/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNMarkerExtension.kt @@ -2,24 +2,61 @@ package com.rngooglemapsplus.extensions import com.rngooglemapsplus.RNMarker -fun RNMarker.markerEquals(b: RNMarker): Boolean = - id == b.id && - zIndex == b.zIndex && - coordinate == b.coordinate && - anchor == b.anchor && - showInfoWindow == b.showInfoWindow && - title == b.title && - snippet == b.snippet && - opacity == b.opacity && - flat == b.flat && - draggable == b.draggable && - rotation == b.rotation && - infoWindowAnchor == b.infoWindowAnchor && - markerStyleEquals(b) - -fun RNMarker.markerStyleEquals(b: RNMarker): Boolean = iconSvg == b.iconSvg +fun RNMarker.markerEquals(b: RNMarker): Boolean { + if (id != b.id) return false + if (zIndex != b.zIndex) return false + if (!coordinatesEquals(b)) return false + if (!anchorEquals(b)) return false + if (!infoWindowAnchorEquals(b)) return false + if (title != b.title) return false + if (snippet != b.snippet) return false + if (opacity != b.opacity) return false + if (flat != b.flat) return false + if (draggable != b.draggable) return false + if (rotation != b.rotation) return false + if (!markerInfoWindowStyleEquals(b)) return false + if (!markerStyleEquals(b)) return false + + return true +} + +fun RNMarker.coordinatesEquals(b: RNMarker): Boolean { + if (coordinate.latitude != b.coordinate.latitude) return false + if (coordinate.longitude != b.coordinate.longitude) return false + return true +} + +fun RNMarker.anchorEquals(b: RNMarker): Boolean { + if (anchor?.x != b.anchor?.x) return false + if (anchor?.y != b.anchor?.y) return false + return true +} + +fun RNMarker.infoWindowAnchorEquals(b: RNMarker): Boolean { + if (infoWindowAnchor?.x != b.infoWindowAnchor?.x) return false + if (infoWindowAnchor?.y != b.infoWindowAnchor?.y) return false + return true +} + +fun RNMarker.markerInfoWindowStyleEquals(b: RNMarker): Boolean { + if (infoWindowIconSvg?.width != b.infoWindowIconSvg?.width) return false + if (infoWindowIconSvg?.height != b.infoWindowIconSvg?.height) return false + if (infoWindowIconSvg?.svgString != b.infoWindowIconSvg?.svgString) return false + + return true +} + +fun RNMarker.markerStyleEquals(b: RNMarker): Boolean { + if (iconSvg?.width != b.iconSvg?.width) return false + if (iconSvg?.height != b.iconSvg?.height) return false + if (iconSvg?.svgString != b.iconSvg?.svgString) return false + + return true +} fun RNMarker.styleHash(): Int = arrayOf( - iconSvg, + iconSvg?.width, + iconSvg?.height, + iconSvg?.svgString, ).contentHashCode() diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt index 4814d4b..347d7d3 100644 --- a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolygonExtension.kt @@ -1,6 +1,8 @@ package com.rngooglemapsplus.extensions +import com.google.android.gms.maps.model.LatLng import com.rngooglemapsplus.RNPolygon +import com.rngooglemapsplus.RNPolygonHole fun RNPolygon.polygonEquals(b: RNPolygon): Boolean { if (zIndex != b.zIndex) return false @@ -9,7 +11,12 @@ fun RNPolygon.polygonEquals(b: RNPolygon): Boolean { if (fillColor != b.fillColor) return false if (strokeColor != b.strokeColor) return false if (geodesic != b.geodesic) return false - if (!holes.contentEquals(b.holes)) return false + if (!coordinatesEquals(b)) return false + if (!holesEquals(b)) return false + return true +} + +fun RNPolygon.coordinatesEquals(b: RNPolygon): Boolean { val ac = coordinates val bc = b.coordinates if (ac.size != bc.size) return false @@ -20,3 +27,26 @@ fun RNPolygon.polygonEquals(b: RNPolygon): Boolean { } return true } + +fun RNPolygon.holesEquals(b: RNPolygon): Boolean { + if (holes?.size != b.holes?.size) return false + if (holes != null && b.holes != null) { + for (i in holes.indices) { + val ah = holes[i] + val bh = b.holes[i] + + if (ah.coordinates.size != bh.coordinates.size) return false + for (j in ah.coordinates.indices) { + val p = ah.coordinates[j] + val q = bh.coordinates[j] + if (p.latitude != q.latitude || p.longitude != q.longitude) return false + } + } + } + return true +} + +fun Array.toMapsPolygonHoles(): List> = + this.map { hole -> + hole.coordinates.map { it.toLatLng() } + } diff --git a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt index 0b7cdc6..38edbdb 100644 --- a/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt +++ b/android/src/main/java/com/rngooglemapsplus/extensions/RNPolylineExtension.kt @@ -5,11 +5,16 @@ import com.rngooglemapsplus.RNPolyline fun RNPolyline.polylineEquals(b: RNPolyline): Boolean { if (zIndex != b.zIndex) return false if (pressable != b.pressable) return false - if ((width ?: 0.0) != (b.width ?: 0.0)) return false + if (width != b.width) return false if (lineCap != b.lineCap) return false if (lineJoin != b.lineJoin) return false if (geodesic != b.geodesic) return false if (color != b.color) return false + if (!coordinatesEquals(b)) return false + return true +} + +fun RNPolyline.coordinatesEquals(b: RNPolyline): Boolean { val ac = coordinates val bc = b.coordinates if (ac.size != bc.size) return false diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b75ae1d..3916a0e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -2504,7 +2504,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNGoogleMapsPlus (1.6.0): + - RNGoogleMapsPlus (1.6.2): - boost - DoubleConversion - fast_float @@ -3120,7 +3120,7 @@ SPEC CHECKSUMS: ReactCodegen: 0bce2d209e2e802589f4c5ff76d21618200e74cb ReactCommon: 801eff8cb9c940c04d3a89ce399c343ee3eff654 RNGestureHandler: 332c71177cd5c856673c7e7875fa3f92ba5a5cc2 - RNGoogleMapsPlus: b08637cb23bb9592c6e12d1497148e9fd98c35f2 + RNGoogleMapsPlus: 2e51359c4ec90a88a315950cfd8d055c1615d7df RNReanimated: 8f0185df21f0dea34ee8c9611ba88c17a290ed9a RNScreens: 98771ad898d1c0528fc8139606bbacf5a2e9d237 RNWorklets: ab618bf7d1c7fd2cb793b9f0f39c3e29274b3ebf diff --git a/example/src/components/MapWrapper.tsx b/example/src/components/MapWrapper.tsx index 1f0bf1b..81415ce 100644 --- a/example/src/components/MapWrapper.tsx +++ b/example/src/components/MapWrapper.tsx @@ -111,11 +111,7 @@ export default function MapWrapper(props: Props) { { - props.mapRef.current = ref; - }, - }} + hybridRef={wrapCallback((ref) => (props.mapRef.current = ref))} initialProps={props.initialProps ?? initialProps} uiSettings={props.uiSettings ?? uiSettings} myLocationEnabled={props.myLocationEnabled ?? true} diff --git a/example/src/components/maptConfigDialog/validator.ts b/example/src/components/maptConfigDialog/validator.ts index 7c22f09..40cb540 100644 --- a/example/src/components/maptConfigDialog/validator.ts +++ b/example/src/components/maptConfigDialog/validator.ts @@ -44,6 +44,8 @@ export function unionWithValues( return wrapped as any; } +export const RNCustomMapStyleValidator = string(); + export const RNLatLngValidator = object({ latitude: number(), longitude: number(), @@ -317,6 +319,7 @@ export const RNBasicMapConfigValidator = object({ mapId: optional(string()), liteMode: optional(boolean()), camera: optional(RNCameraValidator), + backgroundColor: optional(string()), }) ), uiSettings: optional(RNMapUiSettingsValidator), diff --git a/example/src/screens/BasicMapScreen.tsx b/example/src/screens/BasicMapScreen.tsx index 8f144b8..85bc51a 100644 --- a/example/src/screens/BasicMapScreen.tsx +++ b/example/src/screens/BasicMapScreen.tsx @@ -8,9 +8,11 @@ import { useNavigation } from '@react-navigation/native'; import { RNBasicMapConfigValidator } from '../components/maptConfigDialog/validator'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useHeaderButton } from '../hooks/useHeaderButton'; +import { useAppTheme } from '../hooks/useAppTheme'; export default function BasicMapScreen() { const mapRef = useRef(null); + const theme = useAppTheme(); const layout = useSafeAreaInsets(); const navigation = useNavigation(); const [init, setInit] = useState(false); @@ -22,6 +24,7 @@ export default function BasicMapScreen() { center: { latitude: 37.7749, longitude: -122.4194 }, zoom: 12, }, + backgroundColor: theme.bgAccent, }, uiSettings: { allGesturesEnabled: true, diff --git a/example/src/screens/CirclesScreen.tsx b/example/src/screens/CirclesScreen.tsx index f3be49e..5527dd4 100644 --- a/example/src/screens/CirclesScreen.tsx +++ b/example/src/screens/CirclesScreen.tsx @@ -26,7 +26,7 @@ export default function CirclesScreen() { visible={dialogVisible} title="Edit circle" - initialData={makeCircle(1)} + initialData={circles ? circles[0]! : makeCircle(1)} validator={RNCircleValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setCircles([c])} diff --git a/example/src/screens/HeatmapScreen.tsx b/example/src/screens/HeatmapScreen.tsx index e7b683a..565d31c 100644 --- a/example/src/screens/HeatmapScreen.tsx +++ b/example/src/screens/HeatmapScreen.tsx @@ -26,7 +26,7 @@ export default function HeatmapScreen() { visible={dialogVisible} title="Edit heatmap" - initialData={makeHeatmap(1)} + initialData={heatmaps ? heatmaps[0]! : makeHeatmap(1)} validator={RNHeatmapValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setHeatmaps([c])} diff --git a/example/src/screens/KmlLayerScreen.tsx b/example/src/screens/KmlLayerScreen.tsx index cd4f376..35852ad 100644 --- a/example/src/screens/KmlLayerScreen.tsx +++ b/example/src/screens/KmlLayerScreen.tsx @@ -28,7 +28,9 @@ export default function KmlLayerScreen() { visible={dialogVisible} title="Edit KML layer" - initialData={{ id: '1', kmlString: kmlString }} + initialData={ + kmlLayers ? kmlLayers[0]! : { id: '1', kmlString: kmlString } + } validator={RNKMLayerValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setKmlLayers([c])} diff --git a/example/src/screens/MarkersScreen.tsx b/example/src/screens/MarkersScreen.tsx index 0c240c2..f308f83 100644 --- a/example/src/screens/MarkersScreen.tsx +++ b/example/src/screens/MarkersScreen.tsx @@ -51,7 +51,7 @@ export default function MarkersScreen() { visible={dialogVisible} title="Edit marker" - initialData={makeMarker(1)} + initialData={markers ? markers[0]! : makeMarker(1)} validator={RNMarkerValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setMarkers([c])} diff --git a/example/src/screens/PolygonsScreen.tsx b/example/src/screens/PolygonsScreen.tsx index 3096e64..64008e4 100644 --- a/example/src/screens/PolygonsScreen.tsx +++ b/example/src/screens/PolygonsScreen.tsx @@ -26,7 +26,7 @@ export default function PolygonsScreen() { visible={dialogVisible} title="Edit polygon" - initialData={makePolygon(1)} + initialData={polygons ? polygons[0]! : makePolygon(1)} validator={RNPolygonValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setPolygons([c])} diff --git a/example/src/screens/PolylinesScreen.tsx b/example/src/screens/PolylinesScreen.tsx index f50c96c..234c0da 100644 --- a/example/src/screens/PolylinesScreen.tsx +++ b/example/src/screens/PolylinesScreen.tsx @@ -28,7 +28,7 @@ export default function PolylinesScreen() { visible={dialogVisible} title="Edit polyline" - initialData={makePolyline(1)} + initialData={polylines ? polylines[0]! : makePolyline(1)} validator={RNPolylineValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setPolylines([c])} diff --git a/example/src/screens/SvgMarkersScreen.tsx b/example/src/screens/SvgMarkersScreen.tsx index b1d42d6..8daf6a6 100644 --- a/example/src/screens/SvgMarkersScreen.tsx +++ b/example/src/screens/SvgMarkersScreen.tsx @@ -77,7 +77,7 @@ const MARKER_P_IMAGE_REMOTE_SVG = ` ${buildText('F')} @@ -88,7 +88,7 @@ const MARKER_P_IMAGE_REMOTE_PNG = ` ${buildText('G')} @@ -100,7 +100,7 @@ const MARKER_P_IMAGE_REMOTE_JPG = ` ${buildText('H')} diff --git a/example/src/screens/UrlTileOverlay.tsx b/example/src/screens/UrlTileOverlay.tsx index 65883c1..961ce49 100644 --- a/example/src/screens/UrlTileOverlay.tsx +++ b/example/src/screens/UrlTileOverlay.tsx @@ -32,7 +32,9 @@ export default function UrlTileOverlay() { visible={dialogVisible} title="Edit KML layer" - initialData={makeUrlTileOverlay(1)} + initialData={ + urlTileOverlays ? urlTileOverlays[0]! : makeUrlTileOverlay(1) + } validator={RNUrlTileOverlayValidator} onClose={() => setDialogVisible(false)} onSave={(c) => setUrlTileOverlays([c])} diff --git a/example/src/types/basicMapConfig.ts b/example/src/types/basicMapConfig.ts index 065f5b2..71f1154 100644 --- a/example/src/types/basicMapConfig.ts +++ b/example/src/types/basicMapConfig.ts @@ -13,6 +13,7 @@ export type RNBasicMapConfig = { mapId?: string; liteMode?: boolean; camera?: RNCamera; + backgroundColor?: string; }; uiSettings?: RNMapUiSettings; myLocationEnabled?: boolean; diff --git a/ios/MapCircleBuilder.swift b/ios/MapCircleBuilder.swift index 2c70cbe..4dbe5a8 100644 --- a/ios/MapCircleBuilder.swift +++ b/ios/MapCircleBuilder.swift @@ -17,13 +17,12 @@ final class MapCircleBuilder { @MainActor func update(_ prev: RNCircle, _ next: RNCircle, _ c: GMSCircle) { - if prev.center.latitude != next.center.latitude - || prev.center.longitude != next.center.longitude { + if !prev.centerEquals(next) { c.position = next.center.toCLLocationCoordinate2D() } if prev.radius != next.radius { - c.radius = next.radius ?? 0 + c.radius = next.radius } if prev.fillColor != next.fillColor { diff --git a/ios/MapMarkerBuilder.swift b/ios/MapMarkerBuilder.swift index a27ecb1..666aeb3 100644 --- a/ios/MapMarkerBuilder.swift +++ b/ios/MapMarkerBuilder.swift @@ -15,7 +15,6 @@ final class MapMarkerBuilder { let marker = GMSMarker( position: m.coordinate.toCLLocationCoordinate2D() ) - marker.tracksViewChanges = true marker.icon = icon m.title.map { marker.title = $0 } m.snippet.map { marker.snippet = $0 } @@ -42,65 +41,28 @@ final class MapMarkerBuilder { iconSvg: m.infoWindowIconSvg ) - onMainAsync { [weak marker] in - try? await Task.sleep(nanoseconds: 250_000_000) - marker?.tracksViewChanges = false - } - return marker } @MainActor func update(_ prev: RNMarker, _ next: RNMarker, _ m: GMSMarker) { - if prev.coordinate.latitude != next.coordinate.latitude - || prev.coordinate.longitude != next.coordinate.longitude { + if !prev.coordinateEquals(next) { m.position = next.coordinate.toCLLocationCoordinate2D() } - if prev.title != next.title { - m.title = next.title - } - - if prev.snippet != next.snippet { - m.snippet = next.snippet - } - - if prev.opacity != next.opacity { - let opacity = Float(next.opacity ?? 1) - m.opacity = opacity - m.iconView?.alpha = CGFloat(opacity) - } - - if prev.flat != next.flat { - m.isFlat = next.flat ?? false - } - - if prev.draggable != next.draggable { - m.isDraggable = next.draggable ?? false - } - - if prev.rotation != next.rotation { - m.rotation = next.rotation ?? 0 - } - - if prev.zIndex != next.zIndex { - m.zIndex = Int32(next.zIndex ?? 0) - } - if !prev.markerStyleEquals(next) { - buildIconAsync(next.id, next) { img in + buildIconAsync(next) { img in m.tracksViewChanges = true m.icon = img - if prev.anchor?.x != next.anchor?.x || prev.anchor?.y != next.anchor?.y { + if !prev.anchorEquals(next) { m.groundAnchor = CGPoint( x: next.anchor?.x ?? 0.5, y: next.anchor?.y ?? 1 ) } - if prev.infoWindowAnchor?.x != next.infoWindowAnchor?.x - || prev.infoWindowAnchor?.y != next.infoWindowAnchor?.y { + if !prev.infoWindowAnchorEquals(next) { m.infoWindowAnchor = CGPoint( x: next.infoWindowAnchor?.x ?? 0.5, y: next.infoWindowAnchor?.y ?? 0 @@ -113,36 +75,77 @@ final class MapMarkerBuilder { } } } else { - if prev.anchor?.x != next.anchor?.x || prev.anchor?.y != next.anchor?.y { + if !prev.anchorEquals(next) { m.groundAnchor = CGPoint( x: next.anchor?.x ?? 0.5, y: next.anchor?.y ?? 1 ) } - if prev.infoWindowAnchor?.x != next.infoWindowAnchor?.x - || prev.infoWindowAnchor?.y != next.infoWindowAnchor?.y { + if !prev.infoWindowAnchorEquals(next) { m.infoWindowAnchor = CGPoint( x: next.infoWindowAnchor?.x ?? 0.5, y: next.infoWindowAnchor?.y ?? 0 ) } + } + var tracksInfoWindowChanges = false + + if prev.title != next.title { + tracksInfoWindowChanges = true + m.title = next.title + } + + if prev.snippet != next.snippet { + tracksInfoWindowChanges = true + m.snippet = next.snippet + } + + if(tracksInfoWindowChanges) { + m.tracksInfoWindowChanges = true + onMainAsync { [weak m] in + try? await Task.sleep(nanoseconds: 250_000_000) + m?.tracksInfoWindowChanges = false + } + } + + if prev.opacity != next.opacity { + let opacity = Float(next.opacity ?? 1) + m.opacity = opacity + m.iconView?.alpha = CGFloat(opacity) + } + + if prev.flat != next.flat { + m.isFlat = next.flat ?? false + } + + if prev.draggable != next.draggable { + m.isDraggable = next.draggable ?? false + } + + if prev.rotation != next.rotation { + m.rotation = next.rotation ?? 0 + } + + if prev.zIndex != next.zIndex { + m.zIndex = Int32(next.zIndex ?? 0) + } + + if !prev.markerInfoWindowStyleEquals(next) { m.tagData = MarkerTag( id: next.id, iconSvg: next.infoWindowIconSvg ) - } } @MainActor func buildIconAsync( - _ id: String, _ m: RNMarker, onReady: @escaping (UIImage?) -> Void ) { - tasks[id]?.cancel() + tasks[m.id]?.cancel() if m.iconSvg == nil { onReady(nil) @@ -157,7 +160,7 @@ final class MapMarkerBuilder { let task = Task(priority: .userInitiated) { [weak self] in guard let self else { return } - defer { self.tasks.removeValue(forKey: id) } + defer { self.tasks.removeValue(forKey: m.id) } let scale = UIScreen.main.scale let img = await self.renderUIImage(m, scale) @@ -171,7 +174,7 @@ final class MapMarkerBuilder { } } - tasks[id] = task + tasks[m.id] = task } @MainActor diff --git a/ios/MapPolygonBuilder.swift b/ios/MapPolygonBuilder.swift index d74696b..a5d6bb5 100644 --- a/ios/MapPolygonBuilder.swift +++ b/ios/MapPolygonBuilder.swift @@ -3,13 +3,7 @@ import GoogleMaps final class MapPolygonBuilder { @MainActor func build(_ p: RNPolygon) -> GMSPolygon { - let path = GMSMutablePath() - p.coordinates.forEach { - path.add( - $0.toCLLocationCoordinate2D() - ) - } - + let path = p.coordinates.toGMSPath() let pg = GMSPolygon(path: path) p.fillColor.map { pg.fillColor = $0.toUIColor() } @@ -17,13 +11,7 @@ final class MapPolygonBuilder { p.strokeWidth.map { pg.strokeWidth = CGFloat($0) } p.pressable.map { pg.isTappable = $0 } p.geodesic.map { pg.geodesic = $0 } - p.holes.map { - pg.holes = $0.map { hole in - let path = GMSMutablePath() - hole.coordinates.forEach { path.add($0.toCLLocationCoordinate2D()) } - return path - } - } + pg.holes = p.holes.toMapPolygonHoles() p.zIndex.map { pg.zIndex = Int32($0) } return pg @@ -31,35 +19,12 @@ final class MapPolygonBuilder { @MainActor func update(_ prev: RNPolygon, _ next: RNPolygon, _ pg: GMSPolygon) { - let coordsChanged = - prev.coordinates.count != next.coordinates.count - || !zip(prev.coordinates, next.coordinates).allSatisfy { - $0.latitude == $1.latitude && $0.longitude == $1.longitude - } - - if coordsChanged { - let path = GMSMutablePath() - next.coordinates.forEach { path.add($0.toCLLocationCoordinate2D()) } - pg.path = path + if !prev.coordinatesEquals(next) { + pg.path = next.coordinates.toGMSPath() } - let prevHoles = prev.holes ?? [] - let nextHoles = next.holes ?? [] - let holesChanged = - prevHoles.count != nextHoles.count - || !zip(prevHoles, nextHoles).allSatisfy { a, b in - a.coordinates.count == b.coordinates.count - && zip(a.coordinates, b.coordinates).allSatisfy { - $0.latitude == $1.latitude && $0.longitude == $1.longitude - } - } - - if holesChanged { - pg.holes = nextHoles.map { hole in - let path = GMSMutablePath() - hole.coordinates.forEach { path.add($0.toCLLocationCoordinate2D()) } - return path - } + if !prev.holesEquals(next) { + pg.holes = next.holes.toMapPolygonHoles() } if prev.fillColor != next.fillColor { diff --git a/ios/MapPolylineBuilder.swift b/ios/MapPolylineBuilder.swift index cb2ae8a..45087a1 100644 --- a/ios/MapPolylineBuilder.swift +++ b/ios/MapPolylineBuilder.swift @@ -25,16 +25,8 @@ final class MapPolylineBuilder { @MainActor func update(_ prev: RNPolyline, _ next: RNPolyline, _ pl: GMSPolyline) { - let coordsChanged = - prev.coordinates.count != next.coordinates.count - || !zip(prev.coordinates, next.coordinates).allSatisfy { - $0.latitude == $1.latitude && $0.longitude == $1.longitude - } - - if coordsChanged { - let path = GMSMutablePath() - next.coordinates.forEach { path.add($0.toCLLocationCoordinate2D()) } - pl.path = path + if !prev.coordinatesEquals(next) { + pl.path = next.coordinates.toGMSPath() } if prev.width != next.width { diff --git a/ios/RNGoogleMapsPlusView.swift b/ios/RNGoogleMapsPlusView.swift index 4fa4fc5..cef7c5d 100644 --- a/ios/RNGoogleMapsPlusView.swift +++ b/ios/RNGoogleMapsPlusView.swift @@ -41,6 +41,9 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { initialProps?.camera.map { options.camera = $0.toGMSCameraPosition(current: nil) } + initialProps?.backgroundColor.map { + options.backgroundColor = $0.toUIColor() + } impl.initMapView(googleMapOptions: options) } } @@ -143,7 +146,7 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec { } } } else { - self.markerBuilder.buildIconAsync(next.id, next) { icon in + self.markerBuilder.buildIconAsync(next) { icon in let marker = self.markerBuilder.build(next, icon: icon) self.impl.addMarker(id: id, marker: marker) } diff --git a/ios/extensions/RNCircle+Extension.swift b/ios/extensions/RNCircle+Extension.swift index 8d2557b..4fe843d 100644 --- a/ios/extensions/RNCircle+Extension.swift +++ b/ios/extensions/RNCircle+Extension.swift @@ -2,10 +2,19 @@ import GoogleMaps extension RNCircle { func circleEquals(_ b: RNCircle) -> Bool { - zIndex == b.zIndex && pressable == b.pressable - && center.latitude == b.center.latitude - && center.longitude == b.center.longitude && radius == b.radius - && strokeWidth == b.strokeWidth && strokeColor == b.strokeColor - && fillColor == b.fillColor + if zIndex != b.zIndex { return false } + if pressable != b.pressable { return false } + if !centerEquals(b) { return false } + if radius != b.radius { return false } + if strokeWidth != b.strokeWidth { return false } + if strokeColor != b.strokeColor { return false } + if fillColor != b.fillColor { return false } + return true + } + + func centerEquals(_ b: RNCircle) -> Bool { + if center.latitude != b.center.latitude { return false } + if center.longitude != b.center.longitude { return false } + return true } } diff --git a/ios/extensions/RNLatLng+Extension.swift b/ios/extensions/RNLatLng+Extension.swift index 36f7e66..dd13c01 100644 --- a/ios/extensions/RNLatLng+Extension.swift +++ b/ios/extensions/RNLatLng+Extension.swift @@ -1,7 +1,18 @@ import CoreLocation +import GoogleMaps extension RNLatLng { func toCLLocationCoordinate2D() -> CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } } + +extension Array where Element == RNLatLng { + func toGMSPath() -> GMSPath { + let path = GMSMutablePath() + for coord in self { + path.add(coord.toCLLocationCoordinate2D()) + } + return path + } +} diff --git a/ios/extensions/RNLineCapType+Extension.swift b/ios/extensions/RNLineCapType+Extension.swift new file mode 100644 index 0000000..53db3b6 --- /dev/null +++ b/ios/extensions/RNLineCapType+Extension.swift @@ -0,0 +1,10 @@ +extension RNLineCapType { + func toCGLineCap() -> CGLineCap { + switch self { + case .round: return .round + case .square: return .square + default: return .butt + } + } +} + diff --git a/ios/extensions/RNLineJoinType+Extension.swift b/ios/extensions/RNLineJoinType+Extension.swift new file mode 100644 index 0000000..6621ac0 --- /dev/null +++ b/ios/extensions/RNLineJoinType+Extension.swift @@ -0,0 +1,11 @@ +extension RNLineJoinType { + func toCGLineJoin() -> CGLineJoin { + switch self { + case .round: return .round + case .bevel: return .bevel + default: return .miter + } + } + +} + diff --git a/ios/extensions/RNMarker+Extension.swift b/ios/extensions/RNMarker+Extension.swift index a7fd2a6..7041259 100644 --- a/ios/extensions/RNMarker+Extension.swift +++ b/ios/extensions/RNMarker+Extension.swift @@ -2,21 +2,52 @@ import GoogleMaps extension RNMarker { func markerEquals(_ b: RNMarker) -> Bool { - id == b.id && zIndex == b.zIndex - && coordinate.latitude == b.coordinate.latitude - && coordinate.longitude == b.coordinate.longitude - && anchor?.x == b.anchor?.x && anchor?.y == b.anchor?.y - && showInfoWindow == b.showInfoWindow && title == b.title - && snippet == b.snippet && opacity == b.opacity && flat == b.flat - && draggable == b.draggable && rotation == b.rotation - && infoWindowAnchor?.x == b.infoWindowAnchor?.x - && infoWindowAnchor?.y == b.infoWindowAnchor?.y - && markerStyleEquals(b) + if id != b.id { return false } + if zIndex != b.zIndex { return false } + if !coordinateEquals(b) { return false } + if !anchorEquals(b) { return false } + if title != b.title { return false } + if snippet != b.snippet { return false } + if opacity != b.opacity { return false } + if flat != b.flat { return false } + if draggable != b.draggable { return false } + if rotation != b.rotation { return false } + if !infoWindowAnchorEquals(b) { return false } + if !markerInfoWindowStyleEquals(b) { return false } + if !markerStyleEquals(b) { return false } + return true + } + + func coordinateEquals(_ b: RNMarker) -> Bool { + if coordinate.latitude != b.coordinate.latitude { return false } + if coordinate.longitude != b.coordinate.longitude { return false } + return true + } + + func anchorEquals(_ b: RNMarker) -> Bool { + if anchor?.x != b.anchor?.x { return false } + if anchor?.y != b.anchor?.y { return false } + return true + } + + func infoWindowAnchorEquals(_ b: RNMarker) -> Bool { + if infoWindowAnchor?.x != b.infoWindowAnchor?.x { return false } + if infoWindowAnchor?.y != b.infoWindowAnchor?.y { return false } + return true + } + + func markerInfoWindowStyleEquals(_ b: RNMarker) -> Bool { + if infoWindowIconSvg?.width != b.infoWindowIconSvg?.width { return false } + if infoWindowIconSvg?.height != b.infoWindowIconSvg?.height { return false } + if infoWindowIconSvg?.svgString != b.infoWindowIconSvg?.svgString { return false } + return true } func markerStyleEquals(_ b: RNMarker) -> Bool { - iconSvg?.width == b.iconSvg?.width && iconSvg?.height == b.iconSvg?.height - && iconSvg?.svgString == b.iconSvg?.svgString + if iconSvg?.width != b.iconSvg?.width { return false } + if iconSvg?.height != b.iconSvg?.height { return false } + if iconSvg?.svgString != b.iconSvg?.svgString { return false } + return true } func styleHash() -> NSNumber { diff --git a/ios/extensions/RNPolygon+Extension.swift.swift b/ios/extensions/RNPolygon+Extension.swift.swift index 0d743d9..1123c95 100644 --- a/ios/extensions/RNPolygon+Extension.swift.swift +++ b/ios/extensions/RNPolygon+Extension.swift.swift @@ -2,32 +2,48 @@ import GoogleMaps extension RNPolygon { func polygonEquals(_ b: RNPolygon) -> Bool { - guard zIndex == b.zIndex, - pressable == b.pressable, - strokeWidth == b.strokeWidth, - fillColor == b.fillColor, - strokeColor == b.strokeColor, - geodesic == b.geodesic, - coordinates.count == b.coordinates.count, - holes?.count == b.holes?.count - else { return false } - - for i in 0.. Bool { + if coordinates.count != b.coordinates.count { return false } + + for (a, c) in zip(coordinates, b.coordinates) { + if a.latitude != c.latitude || a.longitude != c.longitude { return false } } - for i in 0..<(holes?.count ?? 0) { - let ha = holes![i] - let hb = b.holes![i] - if ha.coordinates.count != hb.coordinates.count { return false } + return true + } + + func holesEquals(_ b: RNPolygon) -> Bool { + if let holes = holes, let bHoles = b.holes { + if holes.count != bHoles.count { return false } - for j in 0.. [GMSPath]? { + guard let holes = self else { return nil } + return holes.map { hole in + let path = GMSMutablePath() + hole.coordinates.forEach { coord in + path.add(coord.toCLLocationCoordinate2D()) + } + return path + } + } +} diff --git a/ios/extensions/RNPolyline+Extension.swift.swift b/ios/extensions/RNPolyline+Extension.swift.swift index 205d58f..4779841 100644 --- a/ios/extensions/RNPolyline+Extension.swift.swift +++ b/ios/extensions/RNPolyline+Extension.swift.swift @@ -2,37 +2,26 @@ import GoogleMaps extension RNPolyline { func polylineEquals(_ b: RNPolyline) -> Bool { - guard zIndex == b.zIndex, - (width ?? 0) == (b.width ?? 0), - lineCap == b.lineCap, - lineJoin == b.lineJoin, - color == b.color, - geodesic == b.geodesic, - coordinates.count == b.coordinates.count - else { return false } + if zIndex != b.zIndex { return false } + if width != b.width { return false } + if lineCap != b.lineCap { return false } + if lineJoin != b.lineJoin { return false } + if color != b.color { return false } + if geodesic != b.geodesic { return false } + if !coordinatesEquals(b) { return false } - for i in 0.. CGLineCap { - switch t { - case .round: return .round - case .square: return .square - default: return .butt - } - } + func coordinatesEquals(_ b: RNPolyline) -> Bool { + if coordinates.count != b.coordinates.count { return false } - private func mapLineJoin(_ t: RNLineJoinType) -> CGLineJoin { - switch t { - case .round: return .round - case .bevel: return .bevel - default: return .miter + for (a, c) in zip(coordinates, b.coordinates) { + if a.latitude != c.latitude || a.longitude != c.longitude { + return false + } } + + return true } } diff --git a/src/types.ts b/src/types.ts index d7d941f..b1a51a1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,7 @@ export type RNInitialProps = { mapId?: string; liteMode?: boolean; camera?: RNCamera; + backgroundColor?: string; }; export type RNMapUiSettings = { @@ -162,7 +163,6 @@ export type RNMarker = { zIndex?: number; coordinate: RNLatLng; anchor?: RNPosition; - showInfoWindow?: boolean; title?: string; snippet?: string; opacity?: number;