This is an automated email from the ASF dual-hosted git repository.
jsorel pushed a commit to branch feat/image2polygon
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/feat/image2polygon by this
push:
new 5d7e5f99e6 Fix AWT Shape to JTS Geometry adding unwanted NaN Z values
5d7e5f99e6 is described below
commit 5d7e5f99e6db35f9554f13c404a97a1fd50da306
Author: jsorel <[email protected]>
AuthorDate: Fri Apr 18 08:39:25 2025 +0200
Fix AWT Shape to JTS Geometry adding unwanted NaN Z values
---
.../sis/geometry/wrapper/jts/ShapeConverter.java | 190 +++++++++++++++++----
1 file changed, 160 insertions(+), 30 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/ShapeConverter.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/ShapeConverter.java
index 2b268caf74..8b9d7548c0 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/ShapeConverter.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/ShapeConverter.java
@@ -27,6 +27,15 @@ import java.util.Comparator;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.apache.sis.referencing.privy.AbstractShape;
+import org.locationtech.jts.geom.CoordinateSequence;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.util.GeometryFixer;
@@ -268,43 +277,164 @@ abstract class ShapeConverter {
}
flush(false);
final int count = geometries.size();
+
+ Geometry result;
if (count == 1) {
- return geometries.get(0);
- }
- switch (geometryType) {
- case 0: return factory.createEmpty(DIMENSION);
- default: return
factory.createGeometryCollection(GeometryFactory.toGeometryArray (geometries));
- case POINT: return factory.createMultiPoint
(GeometryFactory.toPointArray (geometries));
- case LINESTRING: return factory.createMultiLineString
(GeometryFactory.toLineStringArray(geometries));
- case POLYGON: {
- /*
- * Java2D shapes and JTS geometries differ in their way to
fill interior.
- * Java2D fills the resulting contour based on visual winding
rules.
- * JTS has a system where outer shell and holes are clearly
separated.
- * We would need to draw contours as Java2D for computing JTS
equivalent,
- * but it would require a lot of work. In the meantime, the
SymDifference
- * operation is what behave the most like EVEN_ODD or NON_ZERO
winding rules.
- */
-
- //sort by area, bigger geometries are the outter rings
- Collections.sort(geometries, (Geometry o1, Geometry o2) ->
java.lang.Double.compare(o2.getArea(), o1.getArea()));
- Geometry result = geometries.get(0);
- for (int i=1; i<count; i++) {
- Geometry other = geometries.get(i);
- if (result.intersects(other)) {
- //ring is a hole
- result = result.symDifference(other);
- } else {
- //ring is a separate polygon
- result = result.union(other);
+ result = geometries.get(0);
+ } else {
+ switch (geometryType) {
+ case 0:
+ result = factory.createEmpty(DIMENSION);
+ break;
+ default:
+ result =
factory.createGeometryCollection(GeometryFactory.toGeometryArray (geometries));
+ break;
+ case POINT:
+ result = factory.createMultiPoint
(GeometryFactory.toPointArray (geometries));
+ break;
+ case LINESTRING:
+ result = factory.createMultiLineString
(GeometryFactory.toLineStringArray(geometries));
+ break;
+ case POLYGON:
+ /*
+ * Java2D shapes and JTS geometries differ in their way to
fill interior.
+ * Java2D fills the resulting contour based on visual
winding rules.
+ * JTS has a system where outer shell and holes are
clearly separated.
+ * We would need to draw contours as Java2D for computing
JTS equivalent,
+ * but it would require a lot of work. In the meantime,
the SymDifference
+ * operation is what behave the most like EVEN_ODD or
NON_ZERO winding rules.
+ */
+
+ //sort by area, bigger geometries are the outter rings
+ Collections.sort(geometries, (Geometry o1, Geometry o2) ->
java.lang.Double.compare(o2.getArea(), o1.getArea()));
+ result = geometries.get(0);
+ for (int i=1; i<count; i++) {
+ Geometry other = geometries.get(i);
+ if (result.intersects(other)) {
+ //ring is a hole
+ result = result.symDifference(other);
+ } else {
+ //ring is a separate polygon
+ result = result.union(other);
+ }
}
-
+ break;
+ }
+ }
+
+ return enforce2D(result);
+ }
+
+ /**
+ * JTS has the bad habit of expending the dimension of CoordinateSequence
+ * from 2D to 3D adding NaN Z values.
+ * Since we do not want any Z ordinates, we have to check and fix those.
+ *
+ * @param geometry to fix
+ */
+ private <T extends Geometry> T enforce2D(T geometry) {
+ if (geometry instanceof Point) {
+ final Point pt = (Point) geometry;
+ final CoordinateSequence cs = pt.getCoordinateSequence();
+ final CoordinateSequence cs2d = enforce2D(cs);
+ return (T) (cs2d != cs ? factory.createPoint(cs2d) : geometry);
+ } else if (geometry instanceof LinearRing) {
+ final LinearRing ls = (LinearRing) geometry;
+ final CoordinateSequence cs = ls.getCoordinateSequence();
+ final CoordinateSequence cs2d = enforce2D(cs);
+ return (T) (cs2d != cs ? factory.createLinearRing(cs2d) :
geometry);
+ } else if (geometry instanceof LineString) {
+ final LineString ls = (LineString) geometry;
+ final CoordinateSequence cs = ls.getCoordinateSequence();
+ final CoordinateSequence cs2d = enforce2D(cs);
+ return (T) (cs2d != cs ? factory.createLineString(cs2d) :
geometry);
+ } else if (geometry instanceof MultiLineString) {
+ final MultiLineString ml = (MultiLineString) geometry;
+ boolean changed = false;
+ final LineString[] news = new LineString[ml.getNumGeometries()];
+ for (int i = 0; i < news.length; i++) {
+ news[i] = (LineString) ml.getGeometryN(i);
+ LineString cp = enforce2D(news[i]);
+ if (cp != news[i]) {
+ news[i] = cp;
+ changed = true;
+ }
+ }
+ return (T) (changed ? factory.createMultiLineString(news) :
geometry);
+ } else if (geometry instanceof Polygon) {
+ final Polygon pl = (Polygon) geometry;
+ boolean changed = false;
+ final LinearRing exterior = pl.getExteriorRing();
+ final LinearRing copy = enforce2D(exterior);
+ if (exterior != copy) {
+ changed = true;
+ }
+
+ final LinearRing[] news = new LinearRing[pl.getNumInteriorRing()];
+ for (int i = 0; i < news.length; i++) {
+ news[i] = pl.getInteriorRingN(i);
+ LinearRing cp = enforce2D(news[i]);
+ if (cp != news[i]) {
+ news[i] = cp;
+ changed = true;
}
- return result;
}
+ return (T) (changed ? factory.createPolygon(copy, news) :
geometry);
+ } else if (geometry instanceof MultiPoint) {
+ final MultiPoint ml = (MultiPoint) geometry;
+ boolean changed = false;
+ final Point[] news = new Point[ml.getNumGeometries()];
+ for (int i = 0; i < news.length; i++) {
+ news[i] = (Point) ml.getGeometryN(i);
+ Point cp = enforce2D(news[i]);
+ if (cp != news[i]) {
+ news[i] = cp;
+ changed = true;
+ }
+ }
+ return (T) (changed ? factory.createMultiPoint(news) : geometry);
+ } else if (geometry instanceof MultiPolygon) {
+ final MultiPolygon ml = (MultiPolygon) geometry;
+ boolean changed = false;
+ final Polygon[] news = new Polygon[ml.getNumGeometries()];
+ for (int i = 0; i < news.length; i++) {
+ news[i] = (Polygon) ml.getGeometryN(i);
+ Polygon cp = enforce2D(news[i]);
+ if (cp != news[i]) {
+ news[i] = cp;
+ changed = true;
+ }
+ }
+ return (T) (changed ? factory.createMultiPolygon(news) : geometry);
+ } else if (geometry instanceof GeometryCollection) {
+ final GeometryCollection ml = (GeometryCollection) geometry;
+ boolean changed = false;
+ final Geometry[] news = new Geometry[ml.getNumGeometries()];
+ for (int i = 0; i < news.length; i++) {
+ news[i] = ml.getGeometryN(i);
+ Geometry cp = enforce2D(news[i]);
+ if (cp != news[i]) {
+ news[i] = cp;
+ changed = true;
+ }
+ }
+ return (T) (changed ? factory.createGeometryCollection(news) :
geometry);
+ } else {
+ throw new UnsupportedOperationException("Unexpected JTS geometry
type " + geometry.getGeometryType());
}
}
+ private CoordinateSequence enforce2D(CoordinateSequence cs) {
+ if (cs.getDimension() == 2) return cs;
+ final int size = cs.size();
+ final CoordinateSequence copy =
factory.getCoordinateSequenceFactory().create(size, 2);
+ for (int i = 0; i < size; i++) {
+ copy.setOrdinate(i, 0, cs.getX(i));
+ copy.setOrdinate(i, 1, cs.getY(i));
+ }
+ return copy;
+ }
+
/**
* Copies current coordinates in a new JTS geometry,
* then resets {@link #length} to 0 in preparation for the next geometry.