From fdb2e28a279ee24aadcaece38f92d9141b6ec51a Mon Sep 17 00:00:00 2001 From: Michael Carleton Date: Tue, 30 Dec 2025 21:35:53 +0000 Subject: [PATCH 1/2] add extractor API generics --- .../jts/geom/util/GeometryExtracter.java | 116 ++++++++--- .../jts/geom/util/LineStringExtracter.java | 121 +++++------ .../geom/util/LinearComponentExtracter.java | 195 ++++++++++-------- .../jts/geom/util/PointExtracter.java | 97 ++++----- .../jts/geom/util/PolygonExtracter.java | 97 +++++---- .../jts/geom/util/PolygonalExtracter.java | 97 +++++---- .../jts/operation/valid/IsSimpleOp.java | 2 +- .../jts/geom/util/GeometryExtracterTest.java | 21 ++ 8 files changed, 415 insertions(+), 331 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java index cde0cf1f6a..986aa1e84a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java @@ -12,6 +12,7 @@ package org.locationtech.jts.geom.util; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Geometry; @@ -33,14 +34,55 @@ public class GeometryExtracter implements GeometryFilter { - /** - * Extracts the components of type clz from a {@link Geometry} - * and adds them to the provided {@link List}. - * - * @param geom the geometry from which to extract - * @param list the list to add the extracted elements to - * @deprecated Use {@link GeometryExtracter#extract(Geometry, String, List)} - */ + + /** + * Extracts the components of type {@code clz} from a {@link Geometry}, returns + * them in a new {@link List}, and optionally also adds them to {@code out}. + *

+ * This method is intentionally NOT named {@code extract(...)} to avoid overload + * ambiguity with the legacy JTS {@code extract(Geometry, String, List)} / + * {@code extract(Geometry, Class, List)} methods. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param clz the class of the components to extract (must not be {@code null}) + * @param out optional collection to also write extracted components into (may + * be {@code null}) + * @param the component type + * @return a new modifiable list of extracted components + */ + public static List extractByClass(Geometry geom, Class clz, Collection out) { + List result = new ArrayList(); + if (geom == null || geom.isEmpty()) + return result; + + geom.apply(new GeometryExtracter(clz, result)); + if (out != null) + out.addAll(result); + + return result; + } + + /** + * Extracts the components of type {@code clz} from a {@link Geometry} and + * returns them in a new {@link List}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param clz the class of the components to extract (must not be {@code null}) + * @param the component type + * @return a new modifiable list of extracted components + */ + public static List extractByClass(Geometry geom, Class clz) { + return extractByClass(geom, clz, null); + } + + /** + * Extracts the components of type clz from a {@link Geometry} and adds + * them to the provided {@link List}. + * + * @param geom the geometry from which to extract + * @param list the list to add the extracted elements to + * @deprecated Use {@link GeometryExtracter#extract(Geometry, String, List)} + */ public static List extract(Geometry geom, Class clz, List list) { return extract(geom, toGeometryType(clz), list); @@ -109,44 +151,50 @@ public static List extract(Geometry geom, String geometryType) return extract(geom, geometryType, new ArrayList()); } - private String geometryType; - private List comps; - - /** - * Constructs a filter with a list in which to store the elements found. - * - * @param clz the class of the components to extract (null means all types) - * @param comps the list to extract into - * @deprecated - */ - public GeometryExtracter(Class clz, List comps) + private final String geometryType; // legacy string-based matching mode + private final Class componentClass; // class-based matching mode + private final Collection comps; + + public GeometryExtracter(String geometryType, List comps) { - this.geometryType = toGeometryType(clz); + this.geometryType = geometryType; + this.componentClass = null; this.comps = comps; } - + /** - * Constructs a filter with a list in which to store the elements found. - * - * @param geometryType Geometry type to extract (null means all types) + * Constructs a filter matching by component class. + * + * @param componentClass the class of the components to extract (null means all types) * @param comps the list to extract into */ - public GeometryExtracter(String geometryType, List comps) + public GeometryExtracter(Class componentClass, List comps) { - this.geometryType = geometryType; + this.geometryType = null; + this.componentClass = componentClass; this.comps = comps; } - + + @Override + public void filter(Geometry geom) + { + if (geom == null || geom.isEmpty()) return; + + if (componentClass != null) { + if (componentClass.isInstance(geom)) comps.add(geom); + return; + } + + // legacy string-based behaviour + if (geometryType == null || isOfType(geom, geometryType)) { + comps.add(geom); + } + } + protected static boolean isOfType(Geometry geom, String geometryType) { if (geom.getGeometryType() == geometryType) return true; if (geometryType == Geometry.TYPENAME_LINESTRING - && geom.getGeometryType() == Geometry.TYPENAME_LINEARRING) return true; + && geom.getGeometryType() == Geometry.TYPENAME_LINEARRING) return true; return false; } - - public void filter(Geometry geom) { - if (geometryType == null || isOfType(geom, geometryType)) - comps.add(geom); - } - } diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/LineStringExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/LineStringExtracter.java index 5782a0f900..6161c1a4c4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/LineStringExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/LineStringExtracter.java @@ -11,82 +11,75 @@ */ package org.locationtech.jts.geom.util; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFilter; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiLineString; /** - * Extracts all the {@link LineString} elements from a {@link Geometry}. - * + * Extracts {@link LineString} components from a {@link Geometry}. + *

+ * This class implements {@link GeometryFilter} so it can be passed to + * {@link Geometry#apply(GeometryFilter)}. + * * @version 1.7 - * @see GeometryExtracter + * @see org.locationtech.jts.geom.util.GeometryExtracter GeometryExtracter */ -public class LineStringExtracter - implements GeometryFilter -{ - /** - * Extracts the {@link LineString} elements from a single {@link Geometry} - * and adds them to the provided {@link List}. - * - * @param geom the geometry from which to extract - * @param lines the list to add the extracted LineStrings to - * @return the list argument - */ - public static List getLines(Geometry geom, List lines) - { - if (geom instanceof LineString) { - lines.add(geom); - } - else if (geom instanceof GeometryCollection) { - geom.apply(new LineStringExtracter(lines)); - } - // skip non-LineString elemental geometries - - return lines; - } +public final class LineStringExtracter implements GeometryFilter { + /** + * Extracts the {@link LineString} elements from a single {@link Geometry} and + * adds them to the provided {@link Collection}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param out an optional collection to add the extracted LineStrings to (may + * be {@code null}) + * @return a new modifiable {@link List} containing the extracted LineStrings + */ + public static List getLines(Geometry geom, Collection out) { + return GeometryExtracter.extractByClass(geom, LineString.class, out); + } - /** - * Extracts the {@link LineString} elements from a single {@link Geometry} - * and returns them in a {@link List}. - * - * @param geom the geometry from which to extract - * @return a list containing the linear elements - */ - public static List getLines(Geometry geom) - { - return getLines(geom, new ArrayList()); - } + /** + * Extracts the {@link LineString} elements from a single {@link Geometry} and + * returns them in a {@link List}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @return a new modifiable {@link List} containing the linear elements + */ + public static List getLines(Geometry geom) { + return GeometryExtracter.extractByClass(geom, LineString.class); + } - /** - * Extracts the {@link LineString} elements from a single {@link Geometry} - * and returns them as either a {@link LineString} or {@link MultiLineString}. - * - * @param geom the geometry from which to extract - * @return a linear geometry - */ - public static Geometry getGeometry(Geometry geom) - { - return geom.getFactory().buildGeometry(getLines(geom)); - } + /** + * Extracts the {@link LineString} elements from a single {@link Geometry} and + * returns them as either a {@link LineString} or {@link MultiLineString} (or an + * empty geometry if none are present). + * + * @param geom the geometry from which to extract + * @return a linear geometry + */ + public static Geometry getGeometry(Geometry geom) { + return geom.getFactory().buildGeometry(getLines(geom)); + } - private List comps; - - /** - * Constructs a filter with a list in which to store the elements found. - */ - public LineStringExtracter(List comps) - { - this.comps = comps; - } + private final Collection comps; - public void filter(Geometry geom) - { - if (geom instanceof LineString) comps.add(geom); - } + /** + * Constructs a filter with a collection in which to store {@link LineString}s + * found. + * + * @param comps the collection in which to store LineStrings found + */ + public LineStringExtracter(Collection comps) { + this.comps = comps; + } -} + @Override + public void filter(Geometry geom) { + if (geom instanceof LineString) + comps.add((LineString) geom); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java index 2975952d30..fb9d62aa34 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; import org.locationtech.jts.geom.Geometry; @@ -22,50 +21,49 @@ import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.MultiLineString; - /** * Extracts all the 1-dimensional ({@link LineString}) components from a {@link Geometry}. * For polygonal geometries, this will extract all the component {@link LinearRing}s. - * If desired, LinearRings can be forced to be returned as LineStrings. + *

+ * If desired, {@link LinearRing}s can be forced to be returned as {@link LineString}s. * * @version 1.7 */ -public class LinearComponentExtracter - implements GeometryComponentFilter +public class LinearComponentExtracter implements GeometryComponentFilter { /** - * Extracts the linear components from a single {@link Geometry} + * Extracts the linear components from a collection of {@link Geometry}s * and adds them to the provided {@link Collection}. * * @param geoms the collection of geometries from which to extract linear components - * @param lines the collection to add the extracted linear components to - * @return the collection of linear components (LineStrings or LinearRings) + * @param out the collection to add the extracted linear components to + * @return the {@code out} collection of linear components (LineStrings or LinearRings) */ - public static Collection getLines(Collection geoms, Collection lines) + public static Collection getLines( + Collection geoms, + Collection out) { - for (Iterator i = geoms.iterator(); i.hasNext(); ) { - Geometry g = (Geometry) i.next(); - getLines(g, lines); - } - return lines; + return getLines(geoms, out, false); } /** - * Extracts the linear components from a single {@link Geometry} + * Extracts the linear components from a collection of {@link Geometry}s * and adds them to the provided {@link Collection}. * - * @param geoms the Collection of geometries from which to extract linear components - * @param lines the collection to add the extracted linear components to - * @param forceToLineString true if LinearRings should be converted to LineStrings - * @return the collection of linear components (LineStrings or LinearRings) + * @param geoms the collection of geometries from which to extract linear components + * @param out the collection to add the extracted linear components to + * @param forceToLineString true if {@link LinearRing}s should be converted to {@link LineString}s + * @return the {@code out} collection of linear components (LineStrings or LinearRings) */ - public static Collection getLines(Collection geoms, Collection lines, boolean forceToLineString) + public static Collection getLines( + Collection geoms, + Collection out, + boolean forceToLineString) { - for (Iterator i = geoms.iterator(); i.hasNext(); ) { - Geometry g = (Geometry) i.next(); - getLines(g, lines, forceToLineString); - } - return lines; + for (Geometry g : geoms) { + getLines(g, out, forceToLineString); + } + return out; } /** @@ -73,136 +71,151 @@ public static Collection getLines(Collection geoms, Collection lines, boolean fo * and adds them to the provided {@link Collection}. * * @param geom the geometry from which to extract linear components - * @param lines the Collection to add the extracted linear components to - * @return the Collection of linear components (LineStrings or LinearRings) + * @param out the collection to add the extracted linear components to + * @return the {@code out} collection of linear components (LineStrings or LinearRings) */ - public static Collection getLines(Geometry geom, Collection lines) + public static Collection getLines( + Geometry geom, + Collection out) { - if (geom instanceof LineString) { - lines.add(geom); - } - else { - geom.apply(new LinearComponentExtracter(lines)); - } - return lines; + return getLines(geom, out, false); } /** * Extracts the linear components from a single {@link Geometry} * and adds them to the provided {@link Collection}. * - * @param geom the geometry from which to extract linear components - * @param lines the Collection to add the extracted linear components to - * @param forceToLineString true if LinearRings should be converted to LineStrings - * @return the Collection of linear components (LineStrings or LinearRings) + * @param geom the geometry from which to extract linear components + * @param out the collection to add the extracted linear components to + * @param forceToLineString true if {@link LinearRing}s should be converted to {@link LineString}s + * @return the {@code out} collection of linear components (LineStrings or LinearRings) */ - public static Collection getLines(Geometry geom, Collection lines, boolean forceToLineString) + public static Collection getLines( + Geometry geom, + Collection out, + boolean forceToLineString) { - geom.apply(new LinearComponentExtracter(lines, forceToLineString)); - return lines; + if (geom == null || geom.isEmpty()) return out; + + // Fast-path for single LineString inputs when not forcing ring conversion. + // (If forcing, we still run the filter so LinearRing gets converted.) + if (!forceToLineString && geom instanceof LineString) { + out.add((LineString) geom); + return out; + } + + geom.apply(new LinearComponentExtracter(out, forceToLineString)); + return out; } /** * Extracts the linear components from a single geometry. - * If more than one geometry is to be processed, it is more - * efficient to create a single {@link LinearComponentExtracter} instance - * and pass it to multiple geometries. + * If more than one geometry is to be processed, it is more efficient to create a single + * {@link LinearComponentExtracter} instance and pass it to multiple geometries. * * @param geom the geometry from which to extract linear components - * @return the list of linear components + * @return a new modifiable list of linear components */ - public static List getLines(Geometry geom) + public static List getLines(Geometry geom) { return getLines(geom, false); } /** * Extracts the linear components from a single geometry. - * If more than one geometry is to be processed, it is more - * efficient to create a single {@link LinearComponentExtracter} instance - * and pass it to multiple geometries. + * If more than one geometry is to be processed, it is more efficient to create a single + * {@link LinearComponentExtracter} instance and pass it to multiple geometries. * - * @param geom the geometry from which to extract linear components - * @param forceToLineString true if LinearRings should be converted to LineStrings - * @return the list of linear components + * @param geom the geometry from which to extract linear components + * @param forceToLineString true if {@link LinearRing}s should be converted to {@link LineString}s + * @return a new modifiable list of linear components */ - public static List getLines(Geometry geom, boolean forceToLineString) + public static List getLines(Geometry geom, boolean forceToLineString) { - List lines = new ArrayList(); - geom.apply(new LinearComponentExtracter(lines, forceToLineString)); + List lines = new ArrayList(); + getLines(geom, lines, forceToLineString); return lines; } /** * Extracts the linear components from a single {@link Geometry} - * and returns them as either a {@link LineString} or {@link MultiLineString}. - * + * and returns them as either a {@link LineString} or {@link MultiLineString} + * (or an empty geometry if none are present). + * * @param geom the geometry from which to extract - * @return a linear geometry + * @return a linear geometry built from the extracted components */ public static Geometry getGeometry(Geometry geom) { return geom.getFactory().buildGeometry(getLines(geom)); } - /** * Extracts the linear components from a single {@link Geometry} - * and returns them as either a {@link LineString} or {@link MultiLineString}. - * - * @param geom the geometry from which to extract - * @param forceToLineString true if LinearRings should be converted to LineStrings - * @return a linear geometry + * and returns them as either a {@link LineString} or {@link MultiLineString} + * (or an empty geometry if none are present). + * + * @param geom the geometry from which to extract + * @param forceToLineString true if {@link LinearRing}s should be converted to {@link LineString}s + * @return a linear geometry built from the extracted components */ public static Geometry getGeometry(Geometry geom, boolean forceToLineString) { return geom.getFactory().buildGeometry(getLines(geom, forceToLineString)); } - - private Collection lines; + private final Collection lines; private boolean isForcedToLineString = false; - + /** - * Constructs a LineExtracterFilter with a list in which to store LineStrings found. + * Constructs a filter with a collection in which to store linear components found. + * + * @param lines the collection in which to store linear components found */ - public LinearComponentExtracter(Collection lines) + public LinearComponentExtracter(Collection lines) { - this.lines = lines; + this(lines, false); } /** - * Constructs a LineExtracterFilter with a list in which to store LineStrings found. + * Constructs a filter with a collection in which to store linear components found. + * + * @param lines the collection in which to store linear components found + * @param isForcedToLineString true if {@link LinearRing}s should be converted to {@link LineString}s */ - public LinearComponentExtracter(Collection lines, boolean isForcedToLineString) + public LinearComponentExtracter(Collection lines, boolean isForcedToLineString) { this.lines = lines; this.isForcedToLineString = isForcedToLineString; } /** - * Indicates that LinearRing components should be - * converted to pure LineStrings. - * - * @param isForcedToLineString true if LinearRings should be converted to LineStrings + * Indicates that {@link LinearRing} components should be converted to pure {@link LineString}s. + * + * @param isForcedToLineString true if {@link LinearRing}s should be converted to {@link LineString}s */ public void setForceToLineString(boolean isForcedToLineString) { - this.isForcedToLineString = isForcedToLineString; + this.isForcedToLineString = isForcedToLineString; } - + + @Override public void filter(Geometry geom) { - if (isForcedToLineString && geom instanceof LinearRing) { - LineString line = geom.getFactory().createLineString( ((LinearRing) geom).getCoordinateSequence()); - lines.add(line); - return; - } - // if not being forced, and this is a linear component - if (geom instanceof LineString) - lines.add(geom); - - // else this is not a linear component, so skip it - } + if (geom == null || geom.isEmpty()) return; -} + if (isForcedToLineString && geom instanceof LinearRing) { + LinearRing ring = (LinearRing) geom; + LineString line = geom.getFactory().createLineString(ring.getCoordinateSequence()); + lines.add(line); + return; + } + + // If not being forced, and this is a linear component (includes LinearRing) + if (geom instanceof LineString) { + lines.add((LineString) geom); + } + + // else: not a linear component, so skip it + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/PointExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/PointExtracter.java index ca0432cccb..f14f183701 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/PointExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/PointExtracter.java @@ -11,70 +11,59 @@ */ package org.locationtech.jts.geom.util; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFilter; import org.locationtech.jts.geom.Point; - /** - * Extracts all the 0-dimensional ({@link Point}) components from a {@link Geometry}. - * + * Extracts all the 0-dimensional ({@link Point}) components from a + * {@link Geometry}. + * * @version 1.7 - * @see GeometryExtracter + * @see org.locationtech.jts.geom.util.GeometryExtracter GeometryExtracter */ -public class PointExtracter - implements GeometryFilter -{ - /** - * Extracts the {@link Point} elements from a single {@link Geometry} - * and adds them to the provided {@link List}. - * - * @param geom the geometry from which to extract - * @param list the list to add the extracted elements to - */ - public static List getPoints(Geometry geom, List list) - { - if (geom instanceof Point) { - list.add(geom); - } - else if (geom instanceof GeometryCollection) { - geom.apply(new PointExtracter(list)); - } - // skip non-Polygonal elemental geometries - - return list; - } +public final class PointExtracter implements GeometryFilter { + /** + * Extracts the {@link Point} elements from a single {@link Geometry} and adds + * them to the provided {@link Collection}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param out an optional collection to add the extracted points to (may be + * {@code null}) + * @return a new modifiable {@link List} containing the extracted points + */ + public static List getPoints(Geometry geom, Collection out) { + return GeometryExtracter.extractByClass(geom, Point.class, out); + } - /** - * Extracts the {@link Point} elements from a single {@link Geometry} - * and returns them in a {@link List}. - * - * @param geom the geometry from which to extract - */ - public static List getPoints(Geometry geom) { - if (geom instanceof Point) { - return Collections.singletonList(geom); - } - return getPoints(geom, new ArrayList()); - } + /** + * Extracts the {@link Point} elements from a single {@link Geometry} and + * returns them in a {@link List}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @return a new modifiable {@link List} containing the extracted points + */ + public static List getPoints(Geometry geom) { + return GeometryExtracter.extractByClass(geom, Point.class); + } - private List pts; - /** - * Constructs a PointExtracterFilter with a list in which to store Points found. - */ - public PointExtracter(List pts) - { - this.pts = pts; - } + private final Collection comps; - public void filter(Geometry geom) - { - if (geom instanceof Point) pts.add(geom); - } + /** + * Constructs a filter with a collection in which to store {@link Point}s found. + * + * @param comps the collection in which to store points found + */ + public PointExtracter(Collection comps) { + this.comps = comps; + } -} + @Override + public void filter(Geometry geom) { + if (geom instanceof Point) + comps.add((Point) geom); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonExtracter.java index 686038ae1e..a229e706b4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonExtracter.java @@ -11,66 +11,63 @@ */ package org.locationtech.jts.geom.util; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFilter; import org.locationtech.jts.geom.Polygon; /** - * Extracts all the {@link Polygon} elements from a {@link Geometry}. - * + * Extracts {@link Polygon} components from a {@link Geometry}. + *

+ * This class implements {@link GeometryFilter} so it can be passed to + * {@link Geometry#apply(GeometryFilter)}. + * * @version 1.7 - * @see GeometryExtracter + * @see org.locationtech.jts.geom.util.GeometryExtracter GeometryExtracter */ -public class PolygonExtracter - implements GeometryFilter -{ - /** - * Extracts the {@link Polygon} elements from a single {@link Geometry} - * and adds them to the provided {@link List}. - * - * @param geom the geometry from which to extract - * @param list the list to add the extracted elements to - */ - public static List getPolygons(Geometry geom, List list) - { - if (geom instanceof Polygon) { - list.add(geom); - } - else if (geom instanceof GeometryCollection) { - geom.apply(new PolygonExtracter(list)); - } - // skip non-Polygonal elemental geometries - - return list; - } +public final class PolygonExtracter implements GeometryFilter { + + /** + * Extracts the {@link Polygon} elements from a single {@link Geometry} and adds + * them to the provided {@link Collection}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param out an optional collection to add the extracted polygons to (may be + * {@code null}) + * @return a new modifiable {@link List} containing the extracted polygons + */ + public static List getPolygons(Geometry geom, Collection out) { + return GeometryExtracter.extractByClass(geom, Polygon.class, out); + } - /** - * Extracts the {@link Polygon} elements from a single {@link Geometry} - * and returns them in a {@link List}. - * - * @param geom the geometry from which to extract - */ - public static List getPolygons(Geometry geom) - { - return getPolygons(geom, new ArrayList()); - } + /** + * Extracts the {@link Polygon} elements from a single {@link Geometry} and + * returns them in a {@link List}. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @return a new modifiable {@link List} containing the extracted polygons + */ + public static List getPolygons(Geometry geom) { + return GeometryExtracter.extractByClass(geom, Polygon.class); + } - private List comps; - /** - * Constructs a PolygonExtracterFilter with a list in which to store Polygons found. - */ - public PolygonExtracter(List comps) - { - this.comps = comps; - } + private final Collection comps; - public void filter(Geometry geom) - { - if (geom instanceof Polygon) comps.add(geom); - } + /** + * Constructs a filter with a collection in which to store {@link Polygon}s + * found. + * + * @param comps the collection in which to store polygons found + */ + public PolygonExtracter(Collection comps) { + this.comps = comps; + } -} + @Override + public void filter(Geometry geom) { + if (geom instanceof Polygon) + comps.add((Polygon) geom); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java index 42b892f4bb..9887c32b59 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java @@ -12,48 +12,71 @@ package org.locationtech.jts.geom.util; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.GeometryFilter; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; /** - * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a {@link Geometry}. + * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a + * {@link Geometry}. + *

+ * This class implements {@link GeometryFilter} so it can be passed to + * {@link Geometry#apply(GeometryFilter)}. + * + * @version 1.7 + * @see org.locationtech.jts.geom.util.GeometryExtracter GeometryExtracter */ -public class PolygonalExtracter -{ - /** - * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a {@link Geometry} - * and adds them to the provided list. - * - * @param geom the geometry from which to extract - * @param list the list to add the extracted elements to - */ - public static List getPolygonals(Geometry geom, List list) - { - if (geom instanceof Polygon || geom instanceof MultiPolygon) { - list.add(geom); - } - else if (geom instanceof GeometryCollection) { - for (int i = 0; i < geom.getNumGeometries(); i++) { - getPolygonals(geom.getGeometryN(i), list); - } - } - // skip non-Polygonal elemental geometries - return list; - } - - /** - * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a {@link Geometry} - * and returns them in a list. - * - * @param geom the geometry from which to extract - */ - public static List getPolygonals(Geometry geom) - { - return getPolygonals(geom, new ArrayList()); - } - -} +public final class PolygonalExtracter implements GeometryFilter { + + /** + * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a + * {@link Geometry} and adds them to the provided collection. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @param out the collection to add the extracted elements to + * @return the {@code out} collection + */ + public static Collection getPolygonals(Geometry geom, Collection out) { + if (geom == null || geom.isEmpty()) + return out; + + geom.apply(new PolygonalExtracter(out)); + return out; + } + + /** + * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a + * {@link Geometry} and returns them in a list. + * + * @param geom the geometry from which to extract (may be {@code null}) + * @return a new modifiable list containing polygonal elements + */ + public static List getPolygonals(Geometry geom) { + List result = new ArrayList(); + getPolygonals(geom, result); + return result; + } + + private final Collection comps; + + /** + * Constructs a filter with a collection in which to store polygonal elements + * found. + * + * @param comps the collection in which to store polygonal elements found + */ + public PolygonalExtracter(Collection comps) { + this.comps = comps; + } + + @Override + public void filter(Geometry geom) { + if (geom instanceof Polygon || geom instanceof MultiPolygon) { + comps.add(geom); + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java index df548320ab..1c2e573787 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java @@ -233,7 +233,7 @@ private boolean isSimpleMultiPoint(MultiPoint mp) private boolean isSimplePolygonal(Geometry geom) { boolean isSimple = true; - List rings = LinearComponentExtracter.getLines(geom); + List rings = LinearComponentExtracter.getLines(geom); for (Geometry ring : rings) { if (! isSimpleLinearGeometry(ring)) { diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryExtracterTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryExtracterTest.java index bd1b8a4845..027061b848 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryExtracterTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryExtracterTest.java @@ -1,8 +1,12 @@ package org.locationtech.jts.geom.util; +import java.util.ArrayList; import java.util.List; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; @@ -30,6 +34,23 @@ public void testExtract() throws ParseException { // verify that nested geometries are extracted List points = GeometryExtracter.extract(gc, Geometry.TYPENAME_POINT); assertEquals(2, points.size()); + + List byClassLines = GeometryExtracter.extractByClass(gc, LineString.class); + assertEquals(3, byClassLines.size()); + + // verify LinearRing.class only extracts rings + List byClassRings = GeometryExtracter.extractByClass(gc, LinearRing.class); + assertEquals(1, byClassRings.size()); + + // verify Point.class extracts nested points + List byClassPoints = GeometryExtracter.extractByClass(gc, Point.class); + assertEquals(2, byClassPoints.size()); + + // verify "write into user-provided collection" works with a supertype + List out = new ArrayList(); + List returned = GeometryExtracter.extractByClass(gc, Point.class, out); + assertEquals(2, returned.size()); + assertEquals(2, out.size()); } } From 8c82867663b30898a7804c104dfecc0b9b45cff3 Mon Sep 17 00:00:00 2001 From: Michael Carleton Date: Tue, 30 Dec 2025 22:33:56 +0000 Subject: [PATCH 2/2] dont filter out empty geoms! --- .../jts/geom/util/GeometryExtracter.java | 4 ++-- .../jts/geom/util/LinearComponentExtracter.java | 4 ++-- .../jts/geom/util/PolygonalExtracter.java | 14 +++++++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java index 986aa1e84a..581a14c6f4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryExtracter.java @@ -52,7 +52,7 @@ public class GeometryExtracter */ public static List extractByClass(Geometry geom, Class clz, Collection out) { List result = new ArrayList(); - if (geom == null || geom.isEmpty()) + if (geom == null) return result; geom.apply(new GeometryExtracter(clz, result)); @@ -178,7 +178,7 @@ public GeometryExtracter(Class componentClass, List comps) @Override public void filter(Geometry geom) { - if (geom == null || geom.isEmpty()) return; + if (geom == null) return; if (componentClass != null) { if (componentClass.isInstance(geom)) comps.add(geom); diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java index fb9d62aa34..aa20de2599 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/LinearComponentExtracter.java @@ -95,7 +95,7 @@ public static Collection getLines( Collection out, boolean forceToLineString) { - if (geom == null || geom.isEmpty()) return out; + if (geom == null) return out; // Fast-path for single LineString inputs when not forcing ring conversion. // (If forcing, we still run the filter so LinearRing gets converted.) @@ -202,7 +202,7 @@ public void setForceToLineString(boolean isForcedToLineString) @Override public void filter(Geometry geom) { - if (geom == null || geom.isEmpty()) return; + if (geom == null) return; if (isForcedToLineString && geom instanceof LinearRing) { LinearRing ring = (LinearRing) geom; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java index 9887c32b59..10f24bbcd4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/PolygonalExtracter.java @@ -16,6 +16,7 @@ import java.util.List; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFilter; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; @@ -41,10 +42,17 @@ public final class PolygonalExtracter implements GeometryFilter { * @return the {@code out} collection */ public static Collection getPolygonals(Geometry geom, Collection out) { - if (geom == null || geom.isEmpty()) + if (geom == null) return out; - geom.apply(new PolygonalExtracter(out)); + if (geom instanceof Polygon || geom instanceof MultiPolygon) { + out.add(geom); + } else if (geom instanceof GeometryCollection) { + for (int i = 0; i < geom.getNumGeometries(); i++) { + getPolygonals(geom.getGeometryN(i), out); + } + } + // skip non-polygonal elemental geometries return out; } @@ -52,7 +60,7 @@ public static Collection getPolygonals(Geometry geom, Collecti * Extracts the {@link Polygon} and {@link MultiPolygon} elements from a * {@link Geometry} and returns them in a list. * - * @param geom the geometry from which to extract (may be {@code null}) + * @param geom the geometry from which to extract * @return a new modifiable list containing polygonal elements */ public static List getPolygonals(Geometry geom) {