Skip to content

Commit 426e6fe

Browse files
committed
Handle more general geometries when converting to polygons
After fixing geometries, the result may not necessarily be `Polygonal` (`Polygon` or `MultiPolygon`) – it could be some other `Geometry` or `GeometryCollection` that contains `Polygonal` elements. So `GeometryUtil#toJtsPolygons()` needs to handle these more general cases so that we don't throw away valid geometry during pathfinding or vision.
1 parent 731b74b commit 426e6fe

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

common/src/main/java/net/rptools/lib/GeometryUtil.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Comparator;
2424
import java.util.List;
2525
import java.util.Objects;
26+
import java.util.function.Consumer;
2627
import org.apache.logging.log4j.LogManager;
2728
import org.apache.logging.log4j.Logger;
2829
import org.locationtech.jts.algorithm.InteriorPointArea;
@@ -33,6 +34,7 @@
3334
import org.locationtech.jts.geom.CoordinateArrays;
3435
import org.locationtech.jts.geom.Envelope;
3536
import org.locationtech.jts.geom.Geometry;
37+
import org.locationtech.jts.geom.GeometryCollection;
3638
import org.locationtech.jts.geom.GeometryFactory;
3739
import org.locationtech.jts.geom.LinearRing;
3840
import org.locationtech.jts.geom.Location;
@@ -201,33 +203,59 @@ public static Collection<Polygon> toJtsPolygons(Shape shape) {
201203
// Build the polygon...
202204
var polygon = island.toPolygon();
203205
// ... then make sure it is valid, fixing it if not.
204-
Geometry fixedPolygon;
206+
Geometry fixedGeometry;
205207
try {
206-
fixedPolygon = GeometryPrecisionReducer.reduce(GeometryFixer.fix(polygon), precisionModel);
208+
fixedGeometry = GeometryPrecisionReducer.reduce(GeometryFixer.fix(polygon), precisionModel);
207209
} catch (Throwable t) {
208210
log.error("Failure while reducing polygon", t);
209211
continue;
210212
}
211213

212-
switch (fixedPolygon) {
213-
case Polygon p -> polygons.add(p);
214-
case MultiPolygon mp -> {
215-
for (var n = 0; n < mp.getNumGeometries(); ++n) {
216-
polygons.add((Polygon) mp.getGeometryN(n));
214+
/*
215+
* Although `fixedGeometry` is usually a `Polygon` or a `MultiPolygon`, it _could_ be some
216+
* other `Geometry` or even an arbitrary `GeometryCollection`. We can only work with polygonal
217+
* elements, so we first need to flatten our geometry to a list and then pick out only the
218+
* polygons.
219+
*/
220+
221+
var flattened = new ArrayList<Geometry>();
222+
flatten(fixedGeometry, flattened::add);
223+
224+
for (var geometry : flattened) {
225+
if (geometry instanceof Polygon p) {
226+
if (!p.isEmpty()) {
227+
polygons.add(p);
217228
}
229+
} else {
230+
log.error(
231+
"Found unexpected geometry after fixing polygon: {}. Skipping", geometry.getClass());
218232
}
219-
default ->
220-
log.error(
221-
"Found unexpected geometry after fixing polygon: {}. Skipping",
222-
fixedPolygon.getClass());
223233
}
224234
}
225235

226-
polygons.removeIf(Polygon::isEmpty);
227-
228236
return polygons;
229237
}
230238

239+
/**
240+
* Recursively flattens a geometry by replacing any collection components with their children.
241+
*
242+
* <p>Any discovered non-collection geometry is added to {@code output}.
243+
*
244+
* @param geometry The geometry to flatten. If this is not a collection,
245+
* @param output The place to write the non-collection geometries.
246+
*/
247+
public static void flatten(Geometry geometry, Consumer<Geometry> output) {
248+
for (var n = 0; n < geometry.getNumGeometries(); ++n) {
249+
var child = geometry.getGeometryN(n);
250+
if (child instanceof GeometryCollection childCollection) {
251+
// Recurse on collections so we get their children.
252+
flatten(childCollection, output);
253+
} else {
254+
output.accept(child);
255+
}
256+
}
257+
}
258+
231259
public static Point2D coordinateToPoint2D(Coordinate coordinate) {
232260
return new Point2D.Double(coordinate.getX(), coordinate.getY());
233261
}

0 commit comments

Comments
 (0)