Skip to content

Commit 7072a20

Browse files
committed
fix: apply polygon styling to GeoJSON MultiPolygon features
GeoJsonMapper matched MultiGeometry in the LineString branch, so a MultiPolygon feature carrying simplestyle properties was given a LineStyle. MapViewRenderer then casts the feature style with 'as? PolygonStyle' when rendering each member polygon, gets null, and draws the polygons with SDK defaults — the feature's fill and stroke colors were silently dropped. Style a multi-geometry whose members are all (recursively) polygons — i.e. a GeoJSON MultiPolygon — with PolygonStyle; mixed collections and MultiLineStrings keep the previous line styling.
1 parent 0147954 commit 7072a20

2 files changed

Lines changed: 103 additions & 4 deletions

File tree

data/src/main/java/com/google/maps/android/data/renderer/mapper/GeoJsonMapper.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ object GeoJsonMapper {
9191
val finalProperties = if (id != null) featureProperties + ("id" to id) else featureProperties
9292

9393
val style = properties?.let { props ->
94-
when (geometry) {
95-
is LineString, is MultiGeometry -> { // MultiGeometry could contain lines
94+
when {
95+
geometry is LineString || (geometry is MultiGeometry && !geometry.isPolygonal()) -> {
96+
// MultiGeometry could contain lines
9697
val strokeColor = props["stroke"]?.let { parseColor(it) }
9798
val strokeWidth = props["stroke-width"]?.toFloatOrNull()
9899
if (strokeColor != null || strokeWidth != null) {
@@ -102,7 +103,7 @@ object GeoJsonMapper {
102103
)
103104
} else null
104105
}
105-
is ModelPolygon -> {
106+
geometry is ModelPolygon || geometry is MultiGeometry -> {
106107
val strokeColor = props["stroke"]?.let { parseColor(it) }
107108
val strokeWidth = props["stroke-width"]?.toFloatOrNull()
108109
val fillColor = props["fill"]?.let { parseColor(it) }
@@ -125,7 +126,7 @@ object GeoJsonMapper {
125126
)
126127
} else null
127128
}
128-
is PointGeometry -> {
129+
geometry is PointGeometry -> {
129130
// TODO: Marker styling (marker-color, marker-size, marker-symbol)
130131
null
131132
}
@@ -136,6 +137,14 @@ object GeoJsonMapper {
136137
return Feature(geometry, style = style, properties = finalProperties)
137138
}
138139

140+
/**
141+
* True for a Polygon or a multi-geometry whose members are all (recursively) polygons — i.e. a
142+
* GeoJSON MultiPolygon — which per the simplestyle-spec carries fill styling rather than line styling.
143+
*/
144+
private fun Geometry.isPolygonal(): Boolean =
145+
this is ModelPolygon ||
146+
(this is MultiGeometry && geometries.isNotEmpty() && geometries.all { it.isPolygonal() })
147+
139148
private fun parseColor(colorString: String): Int? {
140149
if (colorString.startsWith("#")) {
141150
// Handle hex color

data/src/test/java/com/google/maps/android/data/renderer/mapper/GeoJsonStylingTest.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,94 @@ class GeoJsonStylingTest {
113113
// Verify stroke width
114114
assertEquals(3.0f, style.width!!, 0.001f)
115115
}
116+
117+
@Test
118+
fun `parse styled multipolygon applies polygon style`() {
119+
val geoJson =
120+
"""
121+
{
122+
"type": "Feature",
123+
"properties": {
124+
"stroke": "#ff0000",
125+
"stroke-width": 2.0,
126+
"fill": "#00ff00",
127+
"fill-opacity": 0.25
128+
},
129+
"geometry": {
130+
"type": "MultiPolygon",
131+
"coordinates": [
132+
[
133+
[
134+
[100.0, 0.0],
135+
[101.0, 0.0],
136+
[101.0, 1.0],
137+
[100.0, 0.0]
138+
]
139+
],
140+
[
141+
[
142+
[102.0, 2.0],
143+
[103.0, 2.0],
144+
[103.0, 3.0],
145+
[102.0, 2.0]
146+
]
147+
]
148+
]
149+
}
150+
}
151+
""".trimIndent()
152+
153+
val parser = GeoJsonParser()
154+
val geoJsonObject = parser.parse(ByteArrayInputStream(geoJson.toByteArray()))!!
155+
val layer = GeoJsonMapper.toLayer(geoJsonObject)
156+
val feature = layer.features.first()
157+
158+
// A MultiPolygon must carry polygon styling (fill), not line styling.
159+
assertTrue(feature.style is PolygonStyle)
160+
val style = feature.style as PolygonStyle
161+
162+
assertEquals(0xFFFF0000.toInt(), style.strokeColor)
163+
assertEquals(2.0f, style.strokeWidth, 0.001f)
164+
165+
// Fill: green with 0.25 opacity (0.25 * 255 = 63 = 0x3F)
166+
val expectedFillColor = (0x3F shl 24) or 0x00FF00
167+
assertEquals(expectedFillColor, style.fillColor)
168+
}
169+
170+
@Test
171+
fun `parse styled multilinestring keeps line style`() {
172+
val geoJson =
173+
"""
174+
{
175+
"type": "Feature",
176+
"properties": {
177+
"stroke": "#0000ff",
178+
"stroke-width": 3.0
179+
},
180+
"geometry": {
181+
"type": "MultiLineString",
182+
"coordinates": [
183+
[
184+
[100.0, 0.0],
185+
[101.0, 1.0]
186+
],
187+
[
188+
[102.0, 2.0],
189+
[103.0, 3.0]
190+
]
191+
]
192+
}
193+
}
194+
""".trimIndent()
195+
196+
val parser = GeoJsonParser()
197+
val geoJsonObject = parser.parse(ByteArrayInputStream(geoJson.toByteArray()))!!
198+
val layer = GeoJsonMapper.toLayer(geoJsonObject)
199+
val feature = layer.features.first()
200+
201+
assertTrue(feature.style is LineStyle)
202+
val style = feature.style as LineStyle
203+
assertEquals(0xFF0000FF.toInt(), style.color)
204+
assertEquals(3.0f, style.width!!, 0.001f)
205+
}
116206
}

0 commit comments

Comments
 (0)