diff --git a/data/src/main/java/com/google/maps/android/data/geojson/GeoJsonLayer.kt b/data/src/main/java/com/google/maps/android/data/geojson/GeoJsonLayer.kt index 07c2c4442..e589ed926 100644 --- a/data/src/main/java/com/google/maps/android/data/geojson/GeoJsonLayer.kt +++ b/data/src/main/java/com/google/maps/android/data/geojson/GeoJsonLayer.kt @@ -16,6 +16,7 @@ package com.google.maps.android.data.geojson import android.content.Context +import android.graphics.Color import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLngBounds @@ -258,7 +259,10 @@ public class GeoJsonLayer : Layer { is com.google.maps.android.data.renderer.model.PointGeometry -> { val pointStyle = feature.pointStyle ?: mDefaultPointStyle com.google.maps.android.data.renderer.model.PointStyle( - color = 0, + // The renderer derives the marker's alpha from the color's alpha channel, so encode the + // legacy style's alpha into an otherwise-black color (hue 0 keeps the default marker look). + // A transparent color here (e.g. 0) would render the marker invisible. + color = Color.argb((pointStyle.getAlpha() * 255).toInt().coerceIn(0, 255), 0, 0, 0), anchorU = pointStyle.getAnchorU(), anchorV = pointStyle.getAnchorV(), heading = pointStyle.getRotation(), diff --git a/data/src/main/java/com/google/maps/android/data/kml/KmlLayer.kt b/data/src/main/java/com/google/maps/android/data/kml/KmlLayer.kt index 9cf5787b4..f6bcf221c 100644 --- a/data/src/main/java/com/google/maps/android/data/kml/KmlLayer.kt +++ b/data/src/main/java/com/google/maps/android/data/kml/KmlLayer.kt @@ -16,6 +16,7 @@ package com.google.maps.android.data.kml import android.content.Context +import android.graphics.Color import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLngBounds @@ -387,7 +388,12 @@ public class KmlLayer : Layer { when (modelGeometry) { is com.google.maps.android.data.renderer.model.PointGeometry -> { com.google.maps.android.data.renderer.model.PointStyle( - color = inline?.mMarkerColor?.toInt() ?: 0, + // mMarkerColor is a hue (0..360), not an ARGB color — convert it before handing it to the + // renderer, which derives the marker's hue and alpha from an ARGB value. Passing the raw + // hue (or 0) made the alpha channel 0 and rendered every KML point marker invisible. + color = + inline?.mMarkerColor?.let { hue -> Color.HSVToColor(floatArrayOf(hue, 1f, 1f)) } + ?: Color.BLACK, iconUrl = inline?.getIconUrl(), ) } diff --git a/data/src/test/java/com/google/maps/android/data/geojson/GeoJsonLayerMarkerVisibilityTest.kt b/data/src/test/java/com/google/maps/android/data/geojson/GeoJsonLayerMarkerVisibilityTest.kt new file mode 100644 index 000000000..7cec12a34 --- /dev/null +++ b/data/src/test/java/com/google/maps/android/data/geojson/GeoJsonLayerMarkerVisibilityTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.data.geojson + +import com.google.android.gms.maps.GoogleMap +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 io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkStatic +import org.json.JSONObject +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +/** + * Regression test for GeoJSON point markers rendered through the legacy [GeoJsonLayer] bridge: + * the bridge must not hand the renderer a fully transparent point color, which would make every + * point marker invisible (the renderer derives marker alpha from the style color's alpha channel). + */ +@RunWith(RobolectricTestRunner::class) +class GeoJsonLayerMarkerVisibilityTest { + @Before + fun setUp() { + mockkStatic(BitmapDescriptorFactory::class) + every { BitmapDescriptorFactory.defaultMarker(any()) } returns mockk() + } + + @After + fun tearDown() { + unmockkStatic(BitmapDescriptorFactory::class) + } + + @Test + fun pointFeature_isRenderedFullyOpaque() { + val mockMap = mockk(relaxed = true) + val mockMarker = mockk(relaxed = true) + val optionsSlot = slot() + every { mockMap.addMarker(capture(optionsSlot)) } returns mockMarker + + val geoJson = + """ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { "name": "A point" }, + "geometry": { "type": "Point", "coordinates": [-111.620, 41.942] } + } + ] + } + """.trimIndent() + + val layer = GeoJsonLayer(mockMap, JSONObject(geoJson)) + layer.addLayerToMap() + + assertEquals(1.0f, optionsSlot.captured.alpha, 0.001f) + } +} diff --git a/data/src/test/java/com/google/maps/android/data/kml/KmlLayerMarkerVisibilityTest.kt b/data/src/test/java/com/google/maps/android/data/kml/KmlLayerMarkerVisibilityTest.kt new file mode 100644 index 000000000..ae573a85a --- /dev/null +++ b/data/src/test/java/com/google/maps/android/data/kml/KmlLayerMarkerVisibilityTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.data.kml + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.google.android.gms.maps.GoogleMap +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 io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkStatic +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +/** + * Regression test for KML point markers rendered through the legacy [KmlLayer] bridge: the bridge + * must hand the renderer an ARGB color with a non-zero alpha channel. Passing the raw marker hue + * (or 0) as if it were ARGB made the derived marker alpha 0, so every KML point was invisible. + */ +@RunWith(RobolectricTestRunner::class) +class KmlLayerMarkerVisibilityTest { + @Before + fun setUp() { + mockkStatic(BitmapDescriptorFactory::class) + every { BitmapDescriptorFactory.defaultMarker(any()) } returns mockk() + } + + @After + fun tearDown() { + unmockkStatic(BitmapDescriptorFactory::class) + } + + @Test + fun pointPlacemark_isRenderedFullyOpaque() { + val mockMap = mockk(relaxed = true) + val mockMarker = mockk(relaxed = true) + val optionsSlot = slot() + every { mockMap.addMarker(capture(optionsSlot)) } returns mockMarker + + val kml = + """ + + + + + A point + + -111.620,41.942,0 + + + + + """.trimIndent() + + val context = ApplicationProvider.getApplicationContext() + val layer = KmlLayer(mockMap, kml.byteInputStream(), context) + layer.addLayerToMap() + + assertEquals(1.0f, optionsSlot.captured.alpha, 0.001f) + } +}