From 49284e700df748511817c5f83c4dfb1fb1aa7938 Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Mon, 15 Feb 2021 14:19:51 +0100 Subject: [PATCH 01/11] correct parameter order on assertEquals, avoiding confusion when tests fails Signed-off-by: Rui Brito --- .../java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index 64d5935dad..cd8e04efb5 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -138,7 +138,7 @@ private void runTest(String wkt, int srid, boolean encodeCRS, String expectedGeo geoJsonWriter.setEncodeCRS(encodeCRS); String json = this.geoJsonWriter.write(geom); json = json.replace('"', '\''); - assertEquals(json, expectedGeojson); + assertEquals(expectedGeojson, json); } } From 3cdc88aad06cd9fa81416fce6171111d42622fdd Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Mon, 15 Feb 2021 14:21:20 +0100 Subject: [PATCH 02/11] fix tests that would fail the new right hand rule Signed-off-by: Rui Brito --- .../org/locationtech/jts/io/geojson/GeoJsonWriterTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index cd8e04efb5..a32239e1b0 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -87,8 +87,8 @@ public void testMultiLineString() throws ParseException { } public void testMultiPolygon() throws ParseException { - runTest("MULTIPOLYGON ( ((0 0, 100 0, 100 100, 0 100, 0 0), (1 1, 1 10, 10 10, 10 1, 1 1) ), ((200 200, 200 250, 250 250, 250 200, 200 200)) )", - "{'type':'MultiPolygon','coordinates':[[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]],[[[200,200],[200,250],[250,250],[250,200],[200,200]]]]}" + runTest("MULTIPOLYGON ( ((0 0, 100 0, 100 100, 0 100, 0 0), (1 1, 1 10, 10 10, 10 1, 1 1) ), ((200 200, 250 200, 250 250, 200 250, 200 200)) )", + "{'type':'MultiPolygon','coordinates':[[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]],[[[200,200],[250,200],[250,250],[200,250],[200,200]]]]}" ); } From 0054db67c2e40a4ac0c7c43af4b953010df1fd6e Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Mon, 15 Feb 2021 14:21:43 +0100 Subject: [PATCH 03/11] right hand rules tests for polygon with exterior and polygon with hole Signed-off-by: Rui Brito --- .../locationtech/jts/io/geojson/GeoJsonWriterTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index a32239e1b0..dc8c5e79a7 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -76,6 +76,16 @@ public void testPolygonWithHole() throws ParseException { "{'type':'Polygon','coordinates':[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]]}"); } + public void testPolygonRightHandRule() throws ParseException { + runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))", + "{'type':'Polygon','coordinates':[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]]]}"); + } + + public void testPolygonWithHoleRightHandRule() throws ParseException { + runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (1 1, 10 1, 10 10, 1 10, 1 1) )", + "{'type':'Polygon','coordinates':[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]]}"); + } + public void testMultiPoint() throws ParseException { runTest("MULTIPOINT ((0 0), (1 4), (100 200))", "{'type':'MultiPoint','coordinates':[[0.0,0.0],[1,4],[100,200]]}"); From f6fd350f17afeded969760fbbe664f9ed304bab1 Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Mon, 15 Feb 2021 14:22:04 +0100 Subject: [PATCH 04/11] enforce right hand rule when writing the geojson string Signed-off-by: Rui Brito --- .../jts/io/geojson/GeoJsonWriter.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index c369ab0235..5496559e49 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -21,10 +21,12 @@ import org.json.simple.JSONAware; import org.json.simple.JSONObject; +import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Geometry; 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; @@ -215,8 +217,9 @@ private List makeJsonAware(Polygon poly) { ArrayList result = new ArrayList(); { - final String jsonString = getJsonString(poly.getExteriorRing() - .getCoordinateSequence()); + final String jsonString = getJsonString( + getCoordinateSequenceWthRightHandRuleEnforcement(poly.getExteriorRing(), true) + ); result.add(new JSONAware() { public String toJSONString() { @@ -225,8 +228,9 @@ public String toJSONString() { }); } for (int i = 0; i < poly.getNumInteriorRing(); i++) { - final String jsonString = getJsonString(poly.getInteriorRingN(i) - .getCoordinateSequence()); + final String jsonString = getJsonString( + getCoordinateSequenceWthRightHandRuleEnforcement(poly.getInteriorRingN(i), false) + ); result.add(new JSONAware() { public String toJSONString() { @@ -238,6 +242,24 @@ public String toJSONString() { return result; } + /** + * See [RFC 7946] for more context. + * A linear ring MUST follow the right-hand rule with respect to the + * area it bounds, i.e., exterior rings are counterclockwise, and + * holes are clockwise. + * */ + private CoordinateSequence getCoordinateSequenceWthRightHandRuleEnforcement(LinearRing ring, boolean isExteriorRing) { + final boolean isRingClockWise = !Orientation.isCCW(ring.getCoordinateSequence()); + + final LinearRing rightHandRuleRing; + if (isExteriorRing) { + rightHandRuleRing = isRingClockWise? ring.reverse() : ring; + } else { + rightHandRuleRing = isRingClockWise? ring : ring.reverse(); + } + return rightHandRuleRing.getCoordinateSequence(); + } + private List makeJsonAware(GeometryCollection geometryCollection) { ArrayList list = new ArrayList( From e4f26a10202f9373d651521b37f11e3f8ee8f0ae Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Wed, 17 Feb 2021 13:55:24 +0100 Subject: [PATCH 05/11] creation of a TransformationUtils class that holds the right hand rule logic Signed-off-by: Rui Brito --- .../jts/io/geojson/TransformationUtils.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java new file mode 100644 index 0000000000..394edf06f9 --- /dev/null +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java @@ -0,0 +1,82 @@ +package org.locationtech.jts.io.geojson; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.*; + +public class TransformationUtils { + + /** + * Transforms a geometry using the Right Hand Rule specifications defined + * in the latest GeoJSON specification. + * See RFC-7946 Specification for more context. + * + * @param geometry to be transformed + * @return Geometry under the Right Hand Rule specifications + */ + public static Geometry enforceRightHandRuleOrientation(final Geometry geometry) { + + if (geometry instanceof MultiPolygon) { + MultiPolygon multiPolygon = (MultiPolygon) geometry; + + Polygon[] polygons = new Polygon[0]; + for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { + final Geometry polygon = multiPolygon.getGeometryN(i); + polygons[i] = ((Polygon) enforceRightHandRuleOrientation(polygon)); + } + + return new GeometryFactory().createMultiPolygon(polygons); + + } else if (geometry instanceof Polygon) { + return enforceRightHandRuleOrientation((Polygon) geometry); + + } else { + return geometry; + } + } + + /** + * Transforms a polygon using the Right Hand Rule specifications defined + * in the latest GeoJSON specification. + * See RFC-7946 Specification for more context. + * + * @param polygon to be transformed + * @return Polygon under the Right Hand Rule specifications + */ + public static Polygon enforceRightHandRuleOrientation(Polygon polygon) { + LinearRing exteriorRing = polygon.getExteriorRing(); + LinearRing exteriorRingEnforced = enforceRightHandRuleOrientation(exteriorRing, true); + + LinearRing[] interiorRings = new LinearRing[0]; + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + interiorRings[i] = enforceRightHandRuleOrientation(polygon.getInteriorRingN(i), false); + } + + return new GeometryFactory(polygon.getPrecisionModel(), polygon.getSRID()) + .createPolygon(exteriorRingEnforced, interiorRings); + } + + /** + * Transforms a polygon using the Right Hand Rule specifications defined + * in the latest GeoJSON specification. + * A linear ring MUST follow the right-hand rule with respect to the + * area it bounds, i.e., exterior rings are counterclockwise, and + * holes are clockwise. + * + * See RFC 7946 Specification for more context. + * + * @param ring the LinearRing, a constraint specific to Polygons + * @param isExteriorRing true if the LinearRing is the exterior polygon ring, the one that defiens the boundary + * @return LinearRing under the Right Hand Rule specifications + */ + public static LinearRing enforceRightHandRuleOrientation(LinearRing ring, boolean isExteriorRing) { + final boolean isRingClockWise = !Orientation.isCCW(ring.getCoordinateSequence()); + + final LinearRing rightHandRuleRing; + if (isExteriorRing) { + rightHandRuleRing = isRingClockWise? ring.reverse() : ring; + } else { + rightHandRuleRing = isRingClockWise? ring : ring.reverse(); + } + return rightHandRuleRing; + } +} From aa689ba7aa3d005e1692b0afe8e4d00e469fe620 Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Wed, 17 Feb 2021 13:58:01 +0100 Subject: [PATCH 06/11] added isRHREnforced option to GeoJsonWriter class which invokes the transformation functions Signed-off-by: Rui Brito --- .../jts/io/geojson/GeoJsonWriter.java | 65 +++++++++---------- .../jts/io/geojson/GeoJsonWriterTest.java | 19 ++++-- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index 5496559e49..e5b5e615e4 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -11,22 +11,12 @@ */ package org.locationtech.jts.io.geojson; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - import org.json.simple.JSONAware; import org.json.simple.JSONObject; -import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Geometry; 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; @@ -34,6 +24,14 @@ import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.util.Assert; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + /** * Writes {@link Geometry}s as JSON fragments in GeoJSON format. @@ -61,6 +59,7 @@ public class GeoJsonWriter { private double scale; private boolean isEncodeCRS = true; + private boolean isRHREnforced = false; /** * Constructs a GeoJsonWriter instance. @@ -90,6 +89,16 @@ public void setEncodeCRS(boolean isEncodeCRS) { this.isEncodeCRS = isEncodeCRS; } + /** + * Sets whether the GeoJSON should be output following the RFC7946 Right Hand Rule. + * See RFC 7946 Specification for more context. + * + * @param isRHREnforced true if the GeoJSON should be output following the RFC7946 Right Hand Rule + */ + public void setEnforceRightHandRule(boolean isRHREnforced) { + this.isRHREnforced = isRHREnforced; + } + /** * Writes a {@link Geometry} in GeoJson format to a String. * @@ -160,6 +169,10 @@ public String toJSONString() { } else if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; + if (isRHREnforced) { + polygon = (Polygon) TransformationUtils.enforceRightHandRuleOrientation(polygon); + } + result.put(GeoJsonConstants.NAME_COORDINATES, makeJsonAware(polygon)); } else if (geometry instanceof MultiPoint) { @@ -175,6 +188,10 @@ public String toJSONString() { } else if (geometry instanceof MultiPolygon) { MultiPolygon multiPolygon = (MultiPolygon) geometry; + if (isRHREnforced) { + multiPolygon = (MultiPolygon) TransformationUtils.enforceRightHandRuleOrientation(multiPolygon); + } + result.put(GeoJsonConstants.NAME_COORDINATES, makeJsonAware(multiPolygon)); } else if (geometry instanceof GeometryCollection) { @@ -217,9 +234,8 @@ private List makeJsonAware(Polygon poly) { ArrayList result = new ArrayList(); { - final String jsonString = getJsonString( - getCoordinateSequenceWthRightHandRuleEnforcement(poly.getExteriorRing(), true) - ); + final String jsonString = getJsonString(poly.getExteriorRing() + .getCoordinateSequence()); result.add(new JSONAware() { public String toJSONString() { @@ -228,9 +244,8 @@ public String toJSONString() { }); } for (int i = 0; i < poly.getNumInteriorRing(); i++) { - final String jsonString = getJsonString( - getCoordinateSequenceWthRightHandRuleEnforcement(poly.getInteriorRingN(i), false) - ); + final String jsonString = getJsonString(poly.getInteriorRingN(i) + .getCoordinateSequence()); result.add(new JSONAware() { public String toJSONString() { @@ -242,24 +257,6 @@ public String toJSONString() { return result; } - /** - * See [RFC 7946] for more context. - * A linear ring MUST follow the right-hand rule with respect to the - * area it bounds, i.e., exterior rings are counterclockwise, and - * holes are clockwise. - * */ - private CoordinateSequence getCoordinateSequenceWthRightHandRuleEnforcement(LinearRing ring, boolean isExteriorRing) { - final boolean isRingClockWise = !Orientation.isCCW(ring.getCoordinateSequence()); - - final LinearRing rightHandRuleRing; - if (isExteriorRing) { - rightHandRuleRing = isRingClockWise? ring.reverse() : ring; - } else { - rightHandRuleRing = isRingClockWise? ring : ring.reverse(); - } - return rightHandRuleRing.getCoordinateSequence(); - } - private List makeJsonAware(GeometryCollection geometryCollection) { ArrayList list = new ArrayList( diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index dc8c5e79a7..18cc41f8da 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -77,12 +77,12 @@ public void testPolygonWithHole() throws ParseException { } public void testPolygonRightHandRule() throws ParseException { - runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))", + runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))", true, "{'type':'Polygon','coordinates':[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]]]}"); } public void testPolygonWithHoleRightHandRule() throws ParseException { - runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (1 1, 10 1, 10 10, 1 10, 1 1) )", + runTest("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (1 1, 10 1, 10 10, 1 10, 1 1) )", true, "{'type':'Polygon','coordinates':[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]]}"); } @@ -97,8 +97,8 @@ public void testMultiLineString() throws ParseException { } public void testMultiPolygon() throws ParseException { - runTest("MULTIPOLYGON ( ((0 0, 100 0, 100 100, 0 100, 0 0), (1 1, 1 10, 10 10, 10 1, 1 1) ), ((200 200, 250 200, 250 250, 200 250, 200 200)) )", - "{'type':'MultiPolygon','coordinates':[[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]],[[[200,200],[250,200],[250,250],[200,250],[200,200]]]]}" + runTest("MULTIPOLYGON ( ((0 0, 100 0, 100 100, 0 100, 0 0), (1 1, 1 10, 10 10, 10 1, 1 1) ), ((200 200, 200 250, 250 250, 250 200, 200 200)) )", + "{'type':'MultiPolygon','coordinates':[[[[0.0,0.0],[100,0.0],[100,100],[0.0,100],[0.0,0.0]],[[1,1],[1,10],[10,10],[10,1],[1,1]]],[[[200,200],[200,250],[250,250],[250,200],[200,200]]]]}" ); } @@ -135,17 +135,22 @@ private void runTest(String wkt) throws ParseException { } private void runTest(String wkt, String expectedGeojson) throws ParseException { - runTest(wkt, 0, false, expectedGeojson); + runTest(wkt, 0, false, false, expectedGeojson); } private void runTest(String wkt, int srid, String expectedGeojson) throws ParseException { - runTest(wkt, srid, true, expectedGeojson); + runTest(wkt, srid, true, false, expectedGeojson); } - private void runTest(String wkt, int srid, boolean encodeCRS, String expectedGeojson) throws ParseException { + private void runTest(String wkt, boolean enforceRHR, String expectedGeojson) throws ParseException { + runTest(wkt, 0, false, enforceRHR, expectedGeojson); + } + + private void runTest(String wkt, int srid, boolean encodeCRS, boolean enforceRHR, String expectedGeojson) throws ParseException { Geometry geom = read(wkt); geom.setSRID(srid); geoJsonWriter.setEncodeCRS(encodeCRS); + geoJsonWriter.setEnforceRightHandRule(enforceRHR); String json = this.geoJsonWriter.write(geom); json = json.replace('"', '\''); assertEquals(expectedGeojson, json); From 63b4708f52fdb28045422af30d7ecd54b93ce2f3 Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Wed, 17 Feb 2021 14:22:20 +0100 Subject: [PATCH 07/11] i was getting index out of bounds with an implementation that uses arrays so I switched to use list Signed-off-by: Rui Brito --- .../jts/io/geojson/TransformationUtils.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java index 394edf06f9..254bff9e31 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java @@ -3,6 +3,9 @@ import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.*; +import java.util.ArrayList; +import java.util.List; + public class TransformationUtils { /** @@ -18,13 +21,13 @@ public static Geometry enforceRightHandRuleOrientation(final Geometry geometry) if (geometry instanceof MultiPolygon) { MultiPolygon multiPolygon = (MultiPolygon) geometry; - Polygon[] polygons = new Polygon[0]; + List polygons = new ArrayList<>(); for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { final Geometry polygon = multiPolygon.getGeometryN(i); - polygons[i] = ((Polygon) enforceRightHandRuleOrientation(polygon)); + polygons.add((Polygon) enforceRightHandRuleOrientation(polygon)); } - return new GeometryFactory().createMultiPolygon(polygons); + return new GeometryFactory().createMultiPolygon(polygons.toArray(new Polygon[0])); } else if (geometry instanceof Polygon) { return enforceRightHandRuleOrientation((Polygon) geometry); @@ -46,13 +49,13 @@ public static Polygon enforceRightHandRuleOrientation(Polygon polygon) { LinearRing exteriorRing = polygon.getExteriorRing(); LinearRing exteriorRingEnforced = enforceRightHandRuleOrientation(exteriorRing, true); - LinearRing[] interiorRings = new LinearRing[0]; + List interiorRings = new ArrayList<>(); for (int i = 0; i < polygon.getNumInteriorRing(); i++) { - interiorRings[i] = enforceRightHandRuleOrientation(polygon.getInteriorRingN(i), false); + interiorRings.add(enforceRightHandRuleOrientation(polygon.getInteriorRingN(i), false)); } return new GeometryFactory(polygon.getPrecisionModel(), polygon.getSRID()) - .createPolygon(exteriorRingEnforced, interiorRings); + .createPolygon(exteriorRingEnforced, interiorRings.toArray(new LinearRing[0])); } /** From 46505720d36a3b4d9881d3dbd8adca41451fb1cd Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Thu, 18 Feb 2021 14:15:09 +0100 Subject: [PATCH 08/11] resolved potential aliases errors && renamed right hand rule methods to CCW Signed-off-by: Rui Brito --- .../jts/io/geojson/GeoJsonWriter.java | 18 +++++++-------- ...Utils.java => OrientationTransformer.java} | 22 +++++++++---------- .../jts/io/geojson/GeoJsonWriterTest.java | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) rename modules/io/common/src/main/java/org/locationtech/jts/io/geojson/{TransformationUtils.java => OrientationTransformer.java} (77%) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index e5b5e615e4..0b570348e3 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -59,7 +59,7 @@ public class GeoJsonWriter { private double scale; private boolean isEncodeCRS = true; - private boolean isRHREnforced = false; + private boolean isEnforceCCW = false; /** * Constructs a GeoJsonWriter instance. @@ -90,13 +90,13 @@ public void setEncodeCRS(boolean isEncodeCRS) { } /** - * Sets whether the GeoJSON should be output following the RFC7946 Right Hand Rule. + * Sets whether the GeoJSON should be output following counter-clocked orientation aka Right Hand Rule defined in RFC7946 * See RFC 7946 Specification for more context. * - * @param isRHREnforced true if the GeoJSON should be output following the RFC7946 Right Hand Rule + * @param isRHREnforced true if the GeoJSON should be output following the RFC7946 counter-clocked orientation aka Right Hand Rule */ - public void setEnforceRightHandRule(boolean isRHREnforced) { - this.isRHREnforced = isRHREnforced; + public void setEnforceCCW(boolean isRHREnforced) { + this.isEnforceCCW = isRHREnforced; } /** @@ -169,8 +169,8 @@ public String toJSONString() { } else if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; - if (isRHREnforced) { - polygon = (Polygon) TransformationUtils.enforceRightHandRuleOrientation(polygon); + if (isEnforceCCW) { + polygon = (Polygon) OrientationTransformer.transformCCW(polygon); } result.put(GeoJsonConstants.NAME_COORDINATES, makeJsonAware(polygon)); @@ -188,8 +188,8 @@ public String toJSONString() { } else if (geometry instanceof MultiPolygon) { MultiPolygon multiPolygon = (MultiPolygon) geometry; - if (isRHREnforced) { - multiPolygon = (MultiPolygon) TransformationUtils.enforceRightHandRuleOrientation(multiPolygon); + if (isEnforceCCW) { + multiPolygon = (MultiPolygon) OrientationTransformer.transformCCW(multiPolygon); } result.put(GeoJsonConstants.NAME_COORDINATES, makeJsonAware(multiPolygon)); diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java similarity index 77% rename from modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java rename to modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java index 254bff9e31..b2e2937ab0 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/TransformationUtils.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; -public class TransformationUtils { +public class OrientationTransformer { /** * Transforms a geometry using the Right Hand Rule specifications defined @@ -16,7 +16,7 @@ public class TransformationUtils { * @param geometry to be transformed * @return Geometry under the Right Hand Rule specifications */ - public static Geometry enforceRightHandRuleOrientation(final Geometry geometry) { + public static Geometry transformCCW(final Geometry geometry) { if (geometry instanceof MultiPolygon) { MultiPolygon multiPolygon = (MultiPolygon) geometry; @@ -24,13 +24,13 @@ public static Geometry enforceRightHandRuleOrientation(final Geometry geometry) List polygons = new ArrayList<>(); for (int i = 0; i < multiPolygon.getNumGeometries(); i++) { final Geometry polygon = multiPolygon.getGeometryN(i); - polygons.add((Polygon) enforceRightHandRuleOrientation(polygon)); + polygons.add((Polygon) transformCCW(polygon)); } return new GeometryFactory().createMultiPolygon(polygons.toArray(new Polygon[0])); } else if (geometry instanceof Polygon) { - return enforceRightHandRuleOrientation((Polygon) geometry); + return transformCCW((Polygon) geometry); } else { return geometry; @@ -45,13 +45,13 @@ public static Geometry enforceRightHandRuleOrientation(final Geometry geometry) * @param polygon to be transformed * @return Polygon under the Right Hand Rule specifications */ - public static Polygon enforceRightHandRuleOrientation(Polygon polygon) { + public static Polygon transformCCW(Polygon polygon) { LinearRing exteriorRing = polygon.getExteriorRing(); - LinearRing exteriorRingEnforced = enforceRightHandRuleOrientation(exteriorRing, true); + LinearRing exteriorRingEnforced = transformCCW(exteriorRing, true); List interiorRings = new ArrayList<>(); for (int i = 0; i < polygon.getNumInteriorRing(); i++) { - interiorRings.add(enforceRightHandRuleOrientation(polygon.getInteriorRingN(i), false)); + interiorRings.add(transformCCW(polygon.getInteriorRingN(i), false)); } return new GeometryFactory(polygon.getPrecisionModel(), polygon.getSRID()) @@ -68,17 +68,17 @@ public static Polygon enforceRightHandRuleOrientation(Polygon polygon) { * See RFC 7946 Specification for more context. * * @param ring the LinearRing, a constraint specific to Polygons - * @param isExteriorRing true if the LinearRing is the exterior polygon ring, the one that defiens the boundary + * @param isExteriorRing true if the LinearRing is the exterior polygon ring, the one that defines the boundary * @return LinearRing under the Right Hand Rule specifications */ - public static LinearRing enforceRightHandRuleOrientation(LinearRing ring, boolean isExteriorRing) { + public static LinearRing transformCCW(LinearRing ring, boolean isExteriorRing) { final boolean isRingClockWise = !Orientation.isCCW(ring.getCoordinateSequence()); final LinearRing rightHandRuleRing; if (isExteriorRing) { - rightHandRuleRing = isRingClockWise? ring.reverse() : ring; + rightHandRuleRing = isRingClockWise? ring.reverse() : (LinearRing) ring.copy(); } else { - rightHandRuleRing = isRingClockWise? ring : ring.reverse(); + rightHandRuleRing = isRingClockWise? (LinearRing) ring.copy() : ring.reverse(); } return rightHandRuleRing; } diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index 18cc41f8da..e1ac98c1a6 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -150,7 +150,7 @@ private void runTest(String wkt, int srid, boolean encodeCRS, boolean enforceRHR Geometry geom = read(wkt); geom.setSRID(srid); geoJsonWriter.setEncodeCRS(encodeCRS); - geoJsonWriter.setEnforceRightHandRule(enforceRHR); + geoJsonWriter.setEnforceCCW(enforceRHR); String json = this.geoJsonWriter.write(geom); json = json.replace('"', '\''); assertEquals(expectedGeojson, json); From 6aa05b4fdd3d6edc841862714b6b95d3a2bbcd9b Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Thu, 18 Feb 2021 14:15:49 +0100 Subject: [PATCH 09/11] renamed right hand rule method args to CCW Signed-off-by: Rui Brito --- .../java/org/locationtech/jts/io/geojson/GeoJsonWriter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index 0b570348e3..07a650269c 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -93,10 +93,10 @@ public void setEncodeCRS(boolean isEncodeCRS) { * Sets whether the GeoJSON should be output following counter-clocked orientation aka Right Hand Rule defined in RFC7946 * See RFC 7946 Specification for more context. * - * @param isRHREnforced true if the GeoJSON should be output following the RFC7946 counter-clocked orientation aka Right Hand Rule + * @param enforceCCW true if the GeoJSON should be output following the RFC7946 counter-clocked orientation aka Right Hand Rule */ - public void setEnforceCCW(boolean isRHREnforced) { - this.isEnforceCCW = isRHREnforced; + public void setEnforceCCW(boolean enforceCCW) { + this.isEnforceCCW = enforceCCW; } /** From 666a775e13b4b6fa900d2443c6a854d7ca34fabb Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Fri, 19 Feb 2021 10:12:44 +0100 Subject: [PATCH 10/11] extended javadoc information Signed-off-by: Rui Brito --- .../java/org/locationtech/jts/io/geojson/GeoJsonWriter.java | 4 ++-- .../locationtech/jts/io/geojson/OrientationTransformer.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index 07a650269c..e82f436dac 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -90,10 +90,10 @@ public void setEncodeCRS(boolean isEncodeCRS) { } /** - * Sets whether the GeoJSON should be output following counter-clocked orientation aka Right Hand Rule defined in RFC7946 + * Sets whether the GeoJSON should be output following counter-clockwise orientation aka Right Hand Rule defined in RFC7946 * See RFC 7946 Specification for more context. * - * @param enforceCCW true if the GeoJSON should be output following the RFC7946 counter-clocked orientation aka Right Hand Rule + * @param enforceCCW true if the GeoJSON should be output following the RFC7946 counter-clockwise orientation aka Right Hand Rule */ public void setEnforceCCW(boolean enforceCCW) { this.isEnforceCCW = enforceCCW; diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java index b2e2937ab0..89fa053cf8 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/OrientationTransformer.java @@ -6,6 +6,9 @@ import java.util.ArrayList; import java.util.List; +/** + * Utilities to modify the ring orientation of polygonal geometries. + */ public class OrientationTransformer { /** From d027011cc3e99c7a447af1c1c70f0c68fc86af58 Mon Sep 17 00:00:00 2001 From: Rui Brito Date: Mon, 22 Feb 2021 09:40:51 +0100 Subject: [PATCH 11/11] renamed arguments for the sake of brevity Signed-off-by: Rui Brito --- .../locationtech/jts/io/geojson/GeoJsonWriter.java | 12 ++++++------ .../jts/io/geojson/GeoJsonWriterTest.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java index e82f436dac..cdaee6d58e 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonWriter.java @@ -59,7 +59,7 @@ public class GeoJsonWriter { private double scale; private boolean isEncodeCRS = true; - private boolean isEnforceCCW = false; + private boolean isForceCCW = false; /** * Constructs a GeoJsonWriter instance. @@ -93,10 +93,10 @@ public void setEncodeCRS(boolean isEncodeCRS) { * Sets whether the GeoJSON should be output following counter-clockwise orientation aka Right Hand Rule defined in RFC7946 * See RFC 7946 Specification for more context. * - * @param enforceCCW true if the GeoJSON should be output following the RFC7946 counter-clockwise orientation aka Right Hand Rule + * @param isForceCCW true if the GeoJSON should be output following the RFC7946 counter-clockwise orientation aka Right Hand Rule */ - public void setEnforceCCW(boolean enforceCCW) { - this.isEnforceCCW = enforceCCW; + public void setForceCCW(boolean isForceCCW) { + this.isForceCCW = isForceCCW; } /** @@ -169,7 +169,7 @@ public String toJSONString() { } else if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; - if (isEnforceCCW) { + if (isForceCCW) { polygon = (Polygon) OrientationTransformer.transformCCW(polygon); } @@ -188,7 +188,7 @@ public String toJSONString() { } else if (geometry instanceof MultiPolygon) { MultiPolygon multiPolygon = (MultiPolygon) geometry; - if (isEnforceCCW) { + if (isForceCCW) { multiPolygon = (MultiPolygon) OrientationTransformer.transformCCW(multiPolygon); } diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java index e1ac98c1a6..1c3c8565b7 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonWriterTest.java @@ -150,7 +150,7 @@ private void runTest(String wkt, int srid, boolean encodeCRS, boolean enforceRHR Geometry geom = read(wkt); geom.setSRID(srid); geoJsonWriter.setEncodeCRS(encodeCRS); - geoJsonWriter.setEnforceCCW(enforceRHR); + geoJsonWriter.setForceCCW(enforceRHR); String json = this.geoJsonWriter.write(geom); json = json.replace('"', '\''); assertEquals(expectedGeojson, json);