Skip to content

Commit 4b5adf9

Browse files
authored
[GH-2525] bug: Make ST_Force3D return multipolygons even if it's a single polygon inside (#2526)
1 parent 3796f0e commit 4b5adf9

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

common/src/main/java/org/apache/sedona/common/utils/GeometryForce3DTransformer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,16 @@ public static Geometry transform(Geometry geometry, double zValue) {
4949
GeometryForce3DTransformer transformer = new GeometryForce3DTransformer(zValue);
5050
return transformer.transform(geometry);
5151
}
52+
53+
protected Geometry transformMultiPolygon(MultiPolygon geom, Geometry parent) {
54+
// Transform each polygon individually to avoid recursion
55+
Polygon[] transformedPolygons = new Polygon[geom.getNumGeometries()];
56+
for (int i = 0; i < geom.getNumGeometries(); i++) {
57+
Polygon polygon = (Polygon) geom.getGeometryN(i);
58+
Geometry transformed = super.transform(polygon);
59+
transformedPolygons[i] = (Polygon) transformed;
60+
}
61+
// Always return as MultiPolygon, even if there's only one polygon
62+
return geom.getFactory().createMultiPolygon(transformedPolygons);
63+
}
5264
}

common/src/test/java/org/apache/sedona/common/FunctionsTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,24 @@ public void force3DHybridGeomCollectionDefaultValue() {
24602460
wktWriter3D.write(actualGeometryCollection.getGeometryN(0).getGeometryN(2)));
24612461
}
24622462

2463+
@Test
2464+
public void force3DMultiPolygonWithSinglePolygon() {
2465+
// Test that a MultiPolygon with a single polygon remains a MultiPolygon after force3D
2466+
Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(0, 0, 10, 0, 10, 10, 0, 10, 0, 0));
2467+
MultiPolygon multiPolygon = GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {polygon});
2468+
2469+
Geometry forced = Functions.force3D(multiPolygon, 5.0);
2470+
2471+
assertTrue(forced instanceof MultiPolygon);
2472+
assertEquals(1, ((MultiPolygon) forced).getNumGeometries());
2473+
2474+
Polygon forcedPolygon = (Polygon) ((MultiPolygon) forced).getGeometryN(0);
2475+
Coordinate[] coords = forcedPolygon.getCoordinates();
2476+
for (Coordinate coord : coords) {
2477+
assertEquals(5.0, coord.getZ(), 0.0001);
2478+
}
2479+
}
2480+
24632481
@Test
24642482
public void makeLine() {
24652483
Point point1 = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0));

spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3033,6 +3033,17 @@ class functionTestScala
30333033
}
30343034
}
30353035

3036+
it("should pass ST_Force3D with MultiPolygon containing single polygon") {
3037+
// Test that a MultiPolygon with a single polygon remains a MultiPolygon after force3D
3038+
val df = sparkSession.sql(
3039+
"SELECT ST_AsText(ST_Force3D(ST_GeomFromWKT('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))'), 5.0)) AS geom")
3040+
val actual = df.take(1)(0).get(0).asInstanceOf[String]
3041+
// Should still be MULTIPOLYGON, not POLYGON
3042+
assertTrue(actual.startsWith("MULTIPOLYGON"))
3043+
assertTrue(actual.contains("Z"))
3044+
assertTrue(actual.contains("5"))
3045+
}
3046+
30363047
it("Should pass ST_Force3DZ") {
30373048
val geomTestCases = Map(
30383049
("'LINESTRING (0 1, 1 0, 2 0)'") -> ("'LINESTRING Z(0 1 1, 1 0 1, 2 0 1)'", "'LINESTRING Z(0 1 0, 1 0 0, 2 0 0)'"),

0 commit comments

Comments
 (0)