From 63c7466a2fcc0acea6f50254520ffde12bcb198e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 May 2021 13:05:45 -0700 Subject: [PATCH 001/275] Add TestBuilder Legend stats display Signed-off-by: Martin Davis --- .../testbuilder/GeometryEditPanel.java | 1 + .../testbuilder/GeometryViewStylePanel.java | 11 +++++- .../testbuilder/geom/GeometryUtil.java | 11 +++--- .../testbuilder/ui/render/LegendElement.java | 35 ++++++++++++++----- .../testbuilder/ui/render/ViewStyle.java | 10 ++++++ 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java index cebdf538dd..16270fce63 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java @@ -625,6 +625,7 @@ public void render(Graphics2D g2) if (viewStyle.isLegendEnabled()) { legendElement.setBorderEnabled(viewStyle.isLegendBorderEnabled()); + legendElement.setStatsEnabled(viewStyle.isLegendStatsEnabled()); legendElement.setBorderColor(viewStyle.getBorderColor()); legendElement.setFill(viewStyle.getLegendFill()); legendElement.paint(tbModel.getLayersLegend(), g2); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java index 6fb5d1003f..5f8329b0f5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java @@ -27,6 +27,7 @@ public class GeometryViewStylePanel extends LabelComponentsPanel { private JCheckBox cbViewBorder; private JPanel ctlBorderClr; private JPanel ctlLegendFillClr; + private JCheckBox cbLegendStats; public GeometryViewStylePanel() { try { @@ -93,7 +94,14 @@ public void colorChanged(Color clr) { } } ); - addRow("Legend", cbLegend, "Border", cbLegendBorder, ctlLegendFillClr ); + cbLegendStats = new JCheckBox(); + cbLegendStats.setSelected(viewStyle.isLegendStatsEnabled()); + cbLegendStats.setAlignmentX(Component.LEFT_ALIGNMENT); + cbLegendStats.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + updateView(); } + }); + addRow("Legend", cbLegend, ctlLegendFillClr, "Border", cbLegendBorder, "Stats", cbLegendStats ); cbViewBorder = new JCheckBox(); cbViewBorder.setSelected(viewStyle.isBorderEnabled()); @@ -157,6 +165,7 @@ private void updateView() { viewStyle.setTitle(txtTitle.getText()); viewStyle.setLegendEnabled(cbLegend.isSelected()); viewStyle.setLegendBorderEnabled(cbLegendBorder.isSelected()); + viewStyle.setLegendStatsEnabled(cbLegendStats.isSelected()); viewStyle.setLegendFill(ctlLegendFillClr.getBackground()); JTSTestBuilder.controller().setViewStyle(viewStyle); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java index ba7fe09cd2..874b6ad596 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java @@ -21,15 +21,16 @@ public static String structureSummary(Geometry g) { String structure = ""; if (g instanceof Polygon) { - structure = ((Polygon) g).getNumInteriorRing() + " holes" ; + int nHoles = ((Polygon) g).getNumInteriorRing(); + if (nHoles > 0) structure = nHoles + " holes, " ; } - else if (g instanceof GeometryCollection) - structure = g.getNumGeometries() + " elements"; + String size = ""; + if (g instanceof GeometryCollection) + size = " [ " + g.getNumGeometries() + " ]"; return g.getGeometryType().toUpperCase() - + " - " + structure - + (structure.length() > 0 ? ", " : "") + + size + " - " + structure + g.getNumPoints() + " pts"; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java index eba3bc21c8..db7795d9b6 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java @@ -22,6 +22,7 @@ import org.locationtech.jts.awt.FontGlyphReader; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jtstest.testbuilder.geom.GeometryUtil; import org.locationtech.jtstest.testbuilder.model.Layer; import org.locationtech.jtstest.testbuilder.ui.Viewport; @@ -41,6 +42,7 @@ public class LegendElement { private int borderSize = 1; private boolean isBorderEnabled; + private boolean isStatsEnabled = false; private Color borderColor; @@ -54,6 +56,10 @@ public void setBorderEnabled(boolean isBorderEnabled) { this.isBorderEnabled = isBorderEnabled; } + public void setStatsEnabled(boolean isEnabled) { + this.isStatsEnabled = isEnabled; + } + public void setBorder(int borderSize) { this.borderSize = borderSize; } @@ -85,15 +91,26 @@ private void drawEntries(List layerList, Rectangle box, Graphics2D g) { for (int i = 0; i < n; i++) { // draw layer name int nameY = baseY + (i + 1) * lineHeight; - Layer layer = layerList.get(i); - String name = layer.getName(); - g.setPaint(NAME_CLR); - g.drawString(name, nameX, nameY); - - int swatchX = nameX - SWATCH_SIZE - SWATCH_MARGIN; - int swatchY = nameY - DEFAULT_FONT_SIZE + 2; - drawSwatch(layer, swatchX, swatchY, g); + drawEntry(layerList.get(i), nameX, nameY, g); + } + } + + private void drawEntry(Layer layer, int nameX, int nameY, Graphics2D g) { + String name = getDescription(layer); + g.setPaint(NAME_CLR); + g.drawString(name, nameX, nameY); + + int swatchX = nameX - SWATCH_SIZE - SWATCH_MARGIN; + int swatchY = nameY - DEFAULT_FONT_SIZE + 2; + drawSwatch(layer, swatchX, swatchY, g); + } + + private String getDescription(Layer layer) { + String desc = layer.getName(); + if (isStatsEnabled) { + desc += " -- " + GeometryUtil.structureSummary(layer.getGeometry()); } + return desc; } private void drawSwatch(Layer layer, int x, int y, Graphics2D g) { @@ -215,7 +232,7 @@ private Rectangle computeBox(List layerList, Graphics2D g) { private int entryWidth(List layerList, Graphics2D g2) { int width = 0; for (Layer layer : layerList) { - String s = layer.getName(); + String s = getDescription(layer); int nameWidth = (int) g2.getFontMetrics().getStringBounds(s, g2).getWidth(); if (nameWidth > width) width = nameWidth; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java index db9dfbe53d..db9de2e29d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java @@ -29,6 +29,7 @@ public class ViewStyle { private boolean isLegendEnabled = false; private boolean isLegendBorderEnabled = true; + private boolean isLegendStatsEnabled; private Color legendFillClr = Color.WHITE; private boolean isTitleEnabled = false; @@ -44,6 +45,7 @@ public class ViewStyle { private boolean isOffsetResult = false; + public ViewStyle() { } @@ -139,4 +141,12 @@ public boolean isOffsetResult() { return isOffsetResult; } + public void setLegendStatsEnabled(boolean isEabled) { + this.isLegendStatsEnabled = isEabled; + } + + public boolean isLegendStatsEnabled() { + return isLegendStatsEnabled; + } + } From 8298604fae5b514f6750fc7c1ed84f14f9f9c6bb Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 17 May 2021 09:40:44 -0700 Subject: [PATCH 002/275] Fix LinearRing constructor error message. Signed-off-by: Martin Davis --- .../src/main/java/org/locationtech/jts/geom/LinearRing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java b/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java index 6c6c5a431f..da44edebed 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java @@ -94,7 +94,7 @@ private void validateConstruction() { } if (getCoordinateSequence().size() >= 1 && getCoordinateSequence().size() < MINIMUM_VALID_SIZE) { throw new IllegalArgumentException("Invalid number of points in LinearRing (found " - + getCoordinateSequence().size() + " - must be 0 or >= 4)"); + + getCoordinateSequence().size() + " - must be 0 or >= " + MINIMUM_VALID_SIZE + ")"); } } From fd65e7b73da5660573317aaf94b629de9d392678 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 17 May 2021 10:34:46 -0700 Subject: [PATCH 003/275] Improve IsSimpleOp (#717) Signed-off-by: Martin Davis --- .../jtstest/function/ValidationFunctions.java | 22 +- .../jtstest/testbuilder/ValidPanel.java | 7 +- .../org/locationtech/jts/geom/Geometry.java | 6 +- .../jts/operation/IsSimpleOp.java | 2 + .../jts/operation/valid/IsSimpleOp.java | 430 ++++++++++++++++++ .../operation/{ => valid}/IsSimpleTest.java | 75 +-- .../resources/testxml/general/TestSimple.xml | 34 +- 7 files changed, 533 insertions(+), 43 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java rename modules/core/src/test/java/org/locationtech/jts/operation/{ => valid}/IsSimpleTest.java (55%) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java index 88f087b6fa..a4aa8e6ee0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java @@ -15,14 +15,16 @@ import java.util.ArrayList; import java.util.List; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateArrays; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygonal; import org.locationtech.jts.geom.util.GeometryFixer; import org.locationtech.jts.geom.util.LinearComponentExtracter; import org.locationtech.jts.operation.overlayng.OverlayNG; import org.locationtech.jts.operation.overlayng.OverlayNGRobust; import org.locationtech.jts.operation.polygonize.Polygonizer; +import org.locationtech.jts.operation.valid.IsSimpleOp; import org.locationtech.jts.operation.valid.IsValidOp; import org.locationtech.jts.operation.valid.TopologyValidationError; @@ -91,9 +93,27 @@ public static Geometry makeValid(Geometry geom) { public static Geometry fixInvalid(Geometry geom) { return GeometryFixer.fix(geom); } + public static Geometry fixInvalidKeepCollapse(Geometry geom) { GeometryFixer fixer = new GeometryFixer(geom); fixer.setKeepCollapsed(true); return fixer.getResult(); } + + public static boolean isSimple(Geometry geom) { + return IsSimpleOp.isSimple(geom); + } + + public static Geometry nonSimpleAllPoints(Geometry geom) { + IsSimpleOp op = new IsSimpleOp(geom); + op.setFindAllLocations(true); + List pts = op.getNonSimpleLocations(); + return geom.getFactory().createMultiPointFromCoords(CoordinateArrays.toCoordinateArray(pts)); + } + + public static Geometry nonSimplePoint(Geometry geom) { + IsSimpleOp op = new IsSimpleOp(geom); + Coordinate pt = op.getNonSimpleLocation(); + return geom.getFactory().createPoint(pt); + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java index dcf71d6bc5..d6354137b9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java @@ -18,7 +18,6 @@ import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; -import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.util.Vector; @@ -31,8 +30,8 @@ import javax.swing.SwingConstants; import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.io.*; -import org.locationtech.jts.operation.*; +import org.locationtech.jts.io.WKTWriter; +import org.locationtech.jts.operation.valid.IsSimpleOp; import org.locationtech.jts.operation.valid.IsValidOp; import org.locationtech.jts.operation.valid.TopologyValidationError; import org.locationtech.jtstest.testbuilder.event.ValidPanelEvent; @@ -200,7 +199,7 @@ void btnSimple_actionPerformed(ActionEvent e) } String msg = isSimple ? "" - : "Self-intersection at " + WKTWriter.toPoint(nonSimpleLoc); + : "Non-simple intersection at " + WKTWriter.toPoint(nonSimpleLoc); taInvalidMsg.setText(msg); txtIsValid.setText(isSimple ? "Y" : "N"); setMarkPoint(nonSimpleLoc); diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java index ca43a4cdf7..eb2e135825 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java @@ -18,19 +18,15 @@ import org.locationtech.jts.algorithm.Centroid; import org.locationtech.jts.algorithm.ConvexHull; import org.locationtech.jts.algorithm.InteriorPoint; -import org.locationtech.jts.geom.util.GeometryCollectionMapper; -import org.locationtech.jts.geom.util.GeometryMapper; import org.locationtech.jts.io.WKTWriter; -import org.locationtech.jts.operation.IsSimpleOp; import org.locationtech.jts.operation.buffer.BufferOp; import org.locationtech.jts.operation.distance.DistanceOp; import org.locationtech.jts.operation.linemerge.LineMerger; -import org.locationtech.jts.operation.overlay.OverlayOp; -import org.locationtech.jts.operation.overlay.snap.SnapIfNeededOverlayOp; import org.locationtech.jts.operation.predicate.RectangleContains; import org.locationtech.jts.operation.predicate.RectangleIntersects; import org.locationtech.jts.operation.relate.RelateOp; import org.locationtech.jts.operation.union.UnaryUnionOp; +import org.locationtech.jts.operation.valid.IsSimpleOp; import org.locationtech.jts.operation.valid.IsValidOp; import org.locationtech.jts.util.Assert; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java index fb55e6f68d..9b7e64f712 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java @@ -79,6 +79,8 @@ * @see BoundaryNodeRule * * @version 1.7 + * + * @deprecated Replaced by org.locationtech.jts.operation.valid.IsSimpleOp */ public class IsSimpleOp { 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 new file mode 100644 index 0000000000..12281bfdf8 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsSimpleOp.java @@ -0,0 +1,430 @@ + + +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.locationtech.jts.algorithm.BoundaryNodeRule; +import org.locationtech.jts.algorithm.LineIntersector; +import org.locationtech.jts.algorithm.RobustLineIntersector; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.Lineal; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.MultiPoint; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygonal; +import org.locationtech.jts.geom.util.LinearComponentExtracter; +import org.locationtech.jts.noding.BasicSegmentString; +import org.locationtech.jts.noding.MCIndexNoder; +import org.locationtech.jts.noding.SegmentIntersector; +import org.locationtech.jts.noding.SegmentString; + +/** + * Tests whether a Geometry is simple as defined by the OGC SFS specification. + *

+ * Simplicity is defined for each {@link Geometry} type as follows: + *

    + *
  • Point geometries are simple. + *
  • MultiPoint geometries are simple if every point is unique + *
  • LineString geometries are simple if they do not self-intersect at interior points + * (i.e. points other than the endpoints). + *
  • MultiLineString geometries are simple if + * their elements are simple and they intersect only at points + * which are boundary points of both elements. + * (The notion of boundary points can be user-specified - see below). + *
  • Polygonal geometries have no definition of simplicity. + * The isSimple code checks if all polygon rings are simple. + * (Note: this means that isSimple cannot be used to test + * for all self-intersections in Polygons. + * In order to check if a Polygonal geometry has self-intersections, + * use {@link Geometry#isValid()}). + *
  • GeometryCollection geometries are simple if all their elements are simple. + *
  • Empty geometries are simple + *
+ * For {@link Lineal} geometries the evaluation of simplicity + * can be customized by supplying a {@link BoundaryNodeRule} + * to define how boundary points are determined. + * The default is the SFS-standard {@link BoundaryNodeRule#MOD2_BOUNDARY_RULE}. + *

+ * Note that under the Mod-2 rule, closed LineStrings (rings) + * have no boundary. + * This means that an intersection at their endpoints makes the geometry non-simple. + * If it is required to test whether a set of LineStrings touch + * only at their endpoints, use {@link BoundaryNodeRule#ENDPOINT_BOUNDARY_RULE}. + * For example, this can be used to validate that a collection of lines + * form a topologically valid linear network. + *

+ * By default this class finds a single non-simple location. + * To find all non-simple locations, set {@link #setFindAllLocations(boolean)} + * before calling {@link #isSimple(), and retrieve the locations + * via {@link #getNonSimpleLocations(). + * This can be used to find all intersection points in a linear network. + * + * @see BoundaryNodeRule + * @see Geometry#isValid() + * + * @version 1.7 + */ +public class IsSimpleOp +{ + /** + * Tests whether a geometry is simple. + * + * @param geom the geometry to test + * @return true if the geometry is simple + */ + public static boolean isSimple(Geometry geom) { + IsSimpleOp op = new IsSimpleOp(geom); + return op.isSimple(); + } + + /** + * Gets a non-simple location in a geometry, if any. + * + * @param geom the input geometry + * @return a non-simple location, or null if the geometry is simple + */ + public static Coordinate getNonSimpleLcation(Geometry geom) { + IsSimpleOp op = new IsSimpleOp(geom); + return op.getNonSimpleLocation(); + } + + private Geometry inputGeom; + private boolean isClosedEndpointsInInterior = true; + private boolean isFindAllLocations; + + private boolean isSimple = false; + private List nonSimplePts; + + /** + * Creates a simplicity checker using the default SFS Mod-2 Boundary Node Rule + * + * @param geom the geometry to test + */ + public IsSimpleOp(Geometry geom) { + this(geom, BoundaryNodeRule.MOD2_BOUNDARY_RULE); + } + + /** + * Creates a simplicity checker using a given {@link BoundaryNodeRule} + * + * @param geom the geometry to test + * @param boundaryNodeRule the boundary node rule to use. + */ + public IsSimpleOp(Geometry geom, BoundaryNodeRule boundaryNodeRule) + { + this.inputGeom = geom; + isClosedEndpointsInInterior = ! boundaryNodeRule.isInBoundary(2); + } + + /** + * Sets whether all non-simple intersection points + * will be found. + * + * @param isFindAll whether to find all non-simple points + */ + public void setFindAllLocations(boolean isFindAll) { + this.isFindAllLocations = isFindAll; + } + + /** + * Tests whether the geometry is simple. + * + * @return true if the geometry is simple + */ + public boolean isSimple() + { + compute(); + return isSimple; + } + + /** + * Gets the coordinate for an location where the geometry + * fails to be simple. + * (i.e. where it has a non-boundary self-intersection). + * + * @return a coordinate for the location of the non-boundary self-intersection + * or null if the geometry is simple + */ + public Coordinate getNonSimpleLocation() + { + compute(); + if (nonSimplePts.size() == 0) return null; + return nonSimplePts.get(0); + } + + /** + * Gets all non-simple intersection locations. + * + * @return a list of the coordinates of non-simple locations + */ + public List getNonSimpleLocations() + { + compute(); + return nonSimplePts; + } + + private void compute() { + if (nonSimplePts != null) return; + nonSimplePts = new ArrayList(); + isSimple = computeSimple(inputGeom); + } + + private boolean computeSimple(Geometry geom) + { + if (geom.isEmpty()) return true; + if (geom instanceof Point) return true; + if (geom instanceof LineString) return isSimpleLinearGeometry(geom); + if (geom instanceof MultiLineString) return isSimpleLinearGeometry(geom); + if (geom instanceof MultiPoint) return isSimpleMultiPoint((MultiPoint) geom); + if (geom instanceof Polygonal) return isSimplePolygonal(geom); + if (geom instanceof GeometryCollection) return isSimpleGeometryCollection(geom); + // all other geometry types are simple by definition + return true; + } + + private boolean isSimpleMultiPoint(MultiPoint mp) + { + if (mp.isEmpty()) return true; + Set points = new HashSet(); + for (int i = 0; i < mp.getNumGeometries(); i++) { + Point pt = (Point) mp.getGeometryN(i); + Coordinate p = pt.getCoordinate(); + if (points.contains(p)) { + nonSimplePts.add(p); + return false; + } + points.add(p); + } + return true; + } + + /** + * Computes simplicity for polygonal geometries. + * Polygonal geometries are simple if and only if + * all of their component rings are simple. + * + * @param geom a Polygonal geometry + * @return true if the geometry is simple + */ + private boolean isSimplePolygonal(Geometry geom) + { + List rings = LinearComponentExtracter.getLines(geom); + for (Geometry ring : rings) { + if (! isSimpleLinearGeometry(ring)) + return false; + } + return true; + } + + /** + * Semantics for GeometryCollection is + * simple iff all components are simple. + * + * @param geom + * @return true if the geometry is simple + */ + private boolean isSimpleGeometryCollection(Geometry geom) + { + for (int i = 0; i < geom.getNumGeometries(); i++ ) { + Geometry comp = geom.getGeometryN(i); + if (! computeSimple(comp)) + return false; + } + return true; + } + + private boolean isSimpleLinearGeometry(Geometry geom) + { + if (geom.isEmpty()) return true; + List segStrings = extractSegmentStrings(geom); + NonSimpleIntersectionFinder segInt = new NonSimpleIntersectionFinder(isClosedEndpointsInInterior, isFindAllLocations, nonSimplePts); + MCIndexNoder noder = new MCIndexNoder(); + noder.setSegmentIntersector(segInt); + noder.computeNodes(segStrings); + if (segInt.hasIntersection()) { + return false; + } + return true; + } + + private static List extractSegmentStrings(Geometry geom) { + List segStrings = new ArrayList(); + for (int i = 0; i < geom.getNumGeometries(); i++) { + LineString line = (LineString) geom.getGeometryN(i); + SegmentString ss = new BasicSegmentString(line.getCoordinates(), null); + segStrings.add(ss); + } + return segStrings; + } + + private static class NonSimpleIntersectionFinder + implements SegmentIntersector + { + private boolean isClosedEndpointsInInterior; + private boolean isFindAll = false; + + LineIntersector li = new RobustLineIntersector(); + private List intersectionPts; + + private boolean hasInteriorInt; + private boolean hasInteriorVertexInt; + private boolean hasEqualSegments; + private boolean hasInteriorEndpointInt; + + public NonSimpleIntersectionFinder(boolean isClosedEndpointsInInterior, boolean isFindAll, List intersectionPts) { + this.isClosedEndpointsInInterior =isClosedEndpointsInInterior; + this.isFindAll = isFindAll; + this.intersectionPts = intersectionPts; + } + + /** + * Tests whether an intersection was found. + * + * @return true if an intersection was found + */ + public boolean hasIntersection() + { + return intersectionPts.size() > 0; + } + + @Override + public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { + + // don't test a segment with itself + boolean isSameSegString = ss0 == ss1; + boolean isSameSegment = isSameSegString && segIndex0 == segIndex1; + if (isSameSegment) return; + + Coordinate p00 = ss0.getCoordinate(segIndex0); + Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); + Coordinate p10 = ss1.getCoordinate(segIndex1); + Coordinate p11 = ss1.getCoordinate(segIndex1 + 1); + + boolean hasInt = findIntersection(ss0, segIndex0, ss1, segIndex1, + p00, p01, p10, p11); + + if (hasInt) { + // found an intersection! + intersectionPts.add(li.getIntersection(0)); + } + } + + private boolean findIntersection(SegmentString ss0, int segIndex0, + SegmentString ss1, int segIndex1, + Coordinate p00, Coordinate p01, Coordinate p10, Coordinate p11) { + + li.computeIntersection(p00, p01, p10, p11); + if (! li.hasIntersection()) return false; + + /** + * Check for an intersection in the interior of a segment. + */ + hasInteriorInt = li.isInteriorIntersection(); + if (hasInteriorInt) return true; + + /** + * Check for equal segments (which will produce two intersection points). + * These also intersect in interior points, so are non-simple. + * (This is not triggered by zero-length segments, since they + * are filtered out by the MC index). + */ + hasEqualSegments = li.getIntersectionNum() >= 2; + if (hasEqualSegments) return true; + + /** + * Following tests assume non-adjacent segments. + */ + boolean isSameSegString = ss0 == ss1; + boolean isAdjacentSegment = isSameSegString && Math.abs(segIndex1 - segIndex0) <= 1; + if (isAdjacentSegment) return false; + + /** + * At this point there is a single intersection point + * which is a vertex in each segString. + * Classify them as endpoints or interior + */ + boolean isIntersectionEndpt0 = isIntersectionEndpoint(ss0, segIndex0, li, 0); + boolean isIntersectionEndpt1 = isIntersectionEndpoint(ss1, segIndex1, li, 1); + + hasInteriorVertexInt = ! (isIntersectionEndpt0 && isIntersectionEndpt1); + if (hasInteriorVertexInt) return true; + + /** + * Both intersection vertices must be endpoints. + * Final check is if one or both of them is interior due + * to being endpoint of a closed ring. + * This only applies to different lines + * (which avoids reporting ring endpoints). + */ + if (isClosedEndpointsInInterior && !isSameSegString) { + hasInteriorEndpointInt = ss0.isClosed() || ss1.isClosed(); + if (hasInteriorEndpointInt) return true; + } + return false; + } + + /** + * Tests whether an intersection vertex is an endpoint of a segment string. + * + * @param ss the segmentString + * @param ssIndex index of segment in segmentString + * @param li the line intersector + * @param liSegmentIndex index of segment in intersector + * @return true if the intersection vertex is an endpoint + */ + private static boolean isIntersectionEndpoint(SegmentString ss, int ssIndex, + LineIntersector li, int liSegmentIndex) { + int vertexIndex = intersectionVertexIndex(li, liSegmentIndex); + /** + * If the vertex is the first one of the segment, check if it is the start endpoint. + * Otherwise check if it is the end endpoint. + */ + if (vertexIndex == 0) { + return ssIndex == 0; + } + else { + return ssIndex + 2 == ss.size(); + } + } + + /** + * Finds the vertex index in a segment of an intersection + * which is known to be a vertex. + * + * @param li the line intersector + * @param segmentIndex the intersection segment index + * @return the vertex index (0 or 1) in the segment vertex of the intersection point + */ + private static int intersectionVertexIndex(LineIntersector li, int segmentIndex) { + Coordinate intPt = li.getIntersection(0); + Coordinate endPt0 = li.getEndpoint(segmentIndex, 0); + return intPt.equals2D(endPt0) ? 0 : 1; + } + + @Override + public boolean isDone() { + if (isFindAll) return false; + return intersectionPts.size() > 0; + } + + } + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/IsSimpleTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java similarity index 55% rename from modules/core/src/test/java/org/locationtech/jts/operation/IsSimpleTest.java rename to modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java index 11e9c5e979..a07806f6aa 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/IsSimpleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Vivid Solutions. + * Copyright (c) 2021 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -9,18 +9,17 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ -package org.locationtech.jts.operation; +package org.locationtech.jts.operation.valid; + +import java.util.List; import org.locationtech.jts.algorithm.BoundaryNodeRule; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateArrays; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.io.ParseException; -import org.locationtech.jts.io.WKTReader; -import junit.framework.TestCase; import junit.textui.TestRunner; - +import test.jts.GeometryTestCase; /** * Tests {@link IsSimpleOp} with different {@link BoundaryNodeRule}s. @@ -29,7 +28,7 @@ * @version 1.7 */ public class IsSimpleTest - extends TestCase + extends GeometryTestCase { private static final double TOLERANCE = 0.00005; @@ -37,9 +36,6 @@ public static void main(String args[]) { TestRunner.run(IsSimpleTest.class); } - private GeometryFactory fact = new GeometryFactory(); - private WKTReader rdr = new WKTReader(fact); - public IsSimpleTest(String name) { super(name); @@ -51,9 +47,9 @@ public IsSimpleTest(String name) */ public void test2TouchAtEndpoint() throws Exception { String a = "MULTILINESTRING((0 1, 1 1, 2 1), (0 0, 1 0, 2 1))"; - runIsSimpleTest(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true, + checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true, new Coordinate(2, 1)); - runIsSimpleTest(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true, + checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true, new Coordinate(2, 1)); } @@ -66,17 +62,17 @@ public void test3TouchAtEndpoint() throws Exception { String a = "MULTILINESTRING ((0 1, 1 1, 2 1), (0 0, 1 0, 2 1), (0 2, 1 2, 2 1))"; // rings are simple under all rules - runIsSimpleTest(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true, + checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true, new Coordinate(2, 1)); - runIsSimpleTest(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true, + checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true, new Coordinate(2, 1)); } public void testCross() throws Exception { String a = "MULTILINESTRING ((20 120, 120 20), (20 20, 120 120))"; - runIsSimpleTest(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, false, + checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, false, new Coordinate(70, 70)); - runIsSimpleTest(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, false, + checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, false, new Coordinate(70, 70)); } @@ -87,9 +83,9 @@ public void testMultiLineStringWithRingTouchAtEndpoint() String a = "MULTILINESTRING ((100 100, 20 20, 200 20, 100 100), (100 200, 100 100))"; // under Mod-2, the ring has no boundary, so the line intersects the interior ==> not simple - runIsSimpleTest(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, false, new Coordinate(100, 100) ); + checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, false, new Coordinate(100, 100) ); // under Endpoint, the ring has a boundary point, so the line does NOT intersect the interior ==> simple - runIsSimpleTest(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true ); + checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true ); } public void testRing() @@ -98,35 +94,50 @@ public void testRing() String a = "LINESTRING (100 100, 20 20, 200 20, 100 100)"; // rings are simple under all rules - runIsSimpleTest(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true ); - runIsSimpleTest(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true ); + checkIsSimple(a, BoundaryNodeRule.MOD2_BOUNDARY_RULE, true ); + checkIsSimple(a, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE, true ); } + public void testLinesAll() { + checkIsSimpleAll("MULTILINESTRING ((10 20, 90 20), (10 30, 90 30), (50 40, 50 10))", + BoundaryNodeRule.MOD2_BOUNDARY_RULE, + "MULTIPOINT((50 20), (50 30))"); + } - private void runIsSimpleTest(String wkt, BoundaryNodeRule bnRule, boolean expectedResult) - throws ParseException + private void checkIsSimple(String wkt, BoundaryNodeRule bnRule, boolean expectedResult) { - runIsSimpleTest(wkt, bnRule, expectedResult, null); + checkIsSimple(wkt, bnRule, expectedResult, null); } - private void runIsSimpleTest(String wkt, BoundaryNodeRule bnRule, boolean expectedResult, - Coordinate expectedLocation) - throws ParseException + private void checkIsSimple(String wkt, BoundaryNodeRule bnRule, boolean expectedResult, Coordinate expectedLocation) { - Geometry g = rdr.read(wkt); + Geometry g = read(wkt); IsSimpleOp op = new IsSimpleOp(g, bnRule); - boolean isSimple = false; - isSimple = op.isSimple(); + boolean isSimple = op.isSimple(); Coordinate nonSimpleLoc = op.getNonSimpleLocation(); - // if geom is not simple, should have a valid location +// if geom is not simple, should have a valid location assertTrue(isSimple || nonSimpleLoc != null); assertTrue(expectedResult == isSimple); - if (! isSimple && expectedLocation != null) { + if ( !isSimple && expectedLocation != null ) { assertTrue(expectedLocation.distance(nonSimpleLoc) < TOLERANCE); } } + private void checkIsSimpleAll(String wkt, BoundaryNodeRule bnRule, + String wktExpectedPts) + { + Geometry g = read(wkt); + IsSimpleOp op = new IsSimpleOp(g, bnRule); + op.setFindAllLocations(true); + op.isSimple(); + List nonSimpleCoords = op.getNonSimpleLocations(); + Geometry nsPts = g.getFactory().createMultiPointFromCoords(CoordinateArrays.toCoordinateArray(nonSimpleCoords)); + + Geometry expectedPts = read(wktExpectedPts); + checkEqual(expectedPts, nsPts); + } + } diff --git a/modules/tests/src/test/resources/testxml/general/TestSimple.xml b/modules/tests/src/test/resources/testxml/general/TestSimple.xml index 6eac455baf..b5e8333124 100644 --- a/modules/tests/src/test/resources/testxml/general/TestSimple.xml +++ b/modules/tests/src/test/resources/testxml/general/TestSimple.xml @@ -73,6 +73,18 @@ + + L - non-simple, two equal segments (out-and-back) + + LINESTRING (10 10, 20 20, 10 10) + + + + false + + + + L - non-simple, interior intersection at vertices @@ -122,7 +134,7 @@ - L - simple, intersection at Bdy/Bdy + non-vertex + L - non-simple, intersection at Bdy/Bdy + non-vertex LINESTRING (80 80, 20 20, 20 80, 140 80, 140 140, 80 80) @@ -241,6 +253,18 @@ + + mL - non-simple: two equal lines + + MULTILINESTRING ((0 0, 100 100), (100 100, 0 0)) + + + + false + + + + LR - valid ring @@ -293,6 +317,14 @@ + + A - polygon with equal segments + POLYGON ((50 90, 90 90, 90 50, 50 50, 10 10, 50 50, 50 90)) + + false + + + A - empty From 462d6c288162920bd128e6fe75f3ece0db4d00ff Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 17 May 2021 10:37:32 -0700 Subject: [PATCH 004/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 52ae323948..d24dc41855 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -21,9 +21,14 @@ Distributions for older JTS versions can be obtained at the *Release Date: TBD* +### API Changes + +* Move `IsSimpleOp` to `org.locationtech.jts.operation.valid` package (#717) + ### Functionality Improvements * Add `GeometryFixer` class (#704) +* Improve design and performance of `IsSimpleOp` (#717) ### Bug Fixes @@ -32,6 +37,7 @@ Distributions for older JTS versions can be obtained at the * Fix `WKTReader` and `WKTWriter` handling of collections with all empty elements (#702) * Fix `HalfEdge.prev()` method (#703) * Fix `BufferOp` to remove invalid elements caused by inverted ring curves (#706) +* Fix `IsSimpleOp` duplicate lines bug (#716) From 6b65b6e50d1bead1dc5a9a0886c272f6975b3ff5 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 19 May 2021 10:42:39 -0700 Subject: [PATCH 005/275] Minor refactor of IsSimpleOp to improve locality Signed-off-by: Martin Davis --- .../jts/operation/valid/IsSimpleOp.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 12281bfdf8..0640ae470a 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 @@ -313,13 +313,7 @@ public void processIntersections(SegmentString ss0, int segIndex0, SegmentString boolean isSameSegment = isSameSegString && segIndex0 == segIndex1; if (isSameSegment) return; - Coordinate p00 = ss0.getCoordinate(segIndex0); - Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); - Coordinate p10 = ss1.getCoordinate(segIndex1); - Coordinate p11 = ss1.getCoordinate(segIndex1 + 1); - - boolean hasInt = findIntersection(ss0, segIndex0, ss1, segIndex1, - p00, p01, p10, p11); + boolean hasInt = findIntersection(ss0, segIndex0, ss1, segIndex1); if (hasInt) { // found an intersection! @@ -328,8 +322,12 @@ public void processIntersections(SegmentString ss0, int segIndex0, SegmentString } private boolean findIntersection(SegmentString ss0, int segIndex0, - SegmentString ss1, int segIndex1, - Coordinate p00, Coordinate p01, Coordinate p10, Coordinate p11) { + SegmentString ss1, int segIndex1) { + + Coordinate p00 = ss0.getCoordinate(segIndex0); + Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); + Coordinate p10 = ss1.getCoordinate(segIndex1); + Coordinate p11 = ss1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); if (! li.hasIntersection()) return false; From 0b4e09eb47a37b9afd97e5a212f105580775ad2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gediminas=20Rim=C5=A1a?= Date: Thu, 20 May 2021 18:44:52 +0300 Subject: [PATCH 006/275] Minor javadoc improvements (#719) * Javadoc: Updated Geometry#buffer javadoc to refer to non-deprecated constants * Javadoc: typo fixes * Javadoc: replaced references to deprecated CGAlgorithms#computeOrientation with Orientation#index Signed-off-by: grimsa --- .../testbuilder/io/shapefile/PolygonHandler.java | 2 +- .../java/org/locationtech/jts/algorithm/Angle.java | 4 ++-- .../jts/algorithm/construct/LargestEmptyCircle.java | 2 +- .../algorithm/construct/MaximumInscribedCircle.java | 2 +- .../java/org/locationtech/jts/geom/Geometry.java | 13 +++++++------ .../java/org/locationtech/jts/geom/LineSegment.java | 4 ++-- .../java/org/locationtech/jts/geom/Triangle.java | 8 ++++---- .../jts/geomgraph/index/MonotoneChainIndexer.java | 2 +- .../org/locationtech/jts/operation/IsSimpleOp.java | 6 +++--- .../jts/operation/overlay/PolygonBuilder.java | 2 +- .../jts/operation/overlayng/EdgeKey.java | 2 +- .../jts/operation/overlayng/OverlayEdgeRing.java | 2 +- .../jts/operation/polygonize/EdgeRing.java | 2 +- .../jts/operation/polygonize/HoleAssigner.java | 2 +- .../jts/operation/predicate/RectangleContains.java | 2 +- .../locationtech/jts/operation/valid/IsValidOp.java | 2 +- .../locationtech/jts/planargraph/DirectedEdge.java | 4 ++-- .../jts/algorithm/OrientationIndexTest.java | 2 +- 18 files changed, 32 insertions(+), 31 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java index 527327a0fe..7c684e834c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java @@ -68,7 +68,7 @@ boolean pointInList(Coordinate testPoint, Coordinate[] pointList) { p = pointList[t]; if ( (testPoint.x == p.x) && (testPoint.y == p.y) && - ((testPoint.getZ() == p.getZ()) || (!(testPoint.getZ() == testPoint.getZ())) ) //nan test; x!=x iff x is nan + ((testPoint.getZ() == p.getZ()) || (!(testPoint.getZ() == testPoint.getZ())) ) //nan test; x!=x if x is nan ) { return true; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java index 9bcb6b400f..3a45511b7d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java @@ -104,7 +104,7 @@ public static double angle(Coordinate p) { */ public static boolean isAcute(Coordinate p0, Coordinate p1, Coordinate p2) { - // relies on fact that A dot B is positive iff A ang B is acute + // relies on fact that A dot B is positive if A ang B is acute double dx0 = p0.x - p1.x; double dy0 = p0.y - p1.y; double dx1 = p2.x - p1.x; @@ -126,7 +126,7 @@ public static boolean isAcute(Coordinate p0, Coordinate p1, Coordinate p2) */ public static boolean isObtuse(Coordinate p0, Coordinate p1, Coordinate p2) { - // relies on fact that A dot B is negative iff A ang B is obtuse + // relies on fact that A dot B is negative if A ang B is obtuse double dx0 = p0.x - p1.x; double dy0 = p0.y - p1.y; double dx1 = p2.x - p1.x; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/LargestEmptyCircle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/LargestEmptyCircle.java index c1c085d6da..4f35f975b5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/LargestEmptyCircle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/LargestEmptyCircle.java @@ -395,7 +395,7 @@ public double getY() { } /** - * A cell is greater iff its maximum distance is larger. + * A cell is greater if its maximum distance is larger. */ public int compareTo(Cell o) { return (int) (o.maxDist - this.maxDist); diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java index 51cec7c327..515f67c761 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java @@ -332,7 +332,7 @@ public double getY() { } /** - * A cell is greater iff its maximum possible distance is larger. + * A cell is greater if its maximum possible distance is larger. */ public int compareTo(Cell o) { return (int) (o.maxDist - this.maxDist); diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java index eb2e135825..e6e434dc8e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Geometry.java @@ -20,6 +20,7 @@ import org.locationtech.jts.algorithm.InteriorPoint; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.operation.buffer.BufferOp; +import org.locationtech.jts.operation.buffer.BufferParameters; import org.locationtech.jts.operation.distance.DistanceOp; import org.locationtech.jts.operation.linemerge.LineMerger; import org.locationtech.jts.operation.predicate.RectangleContains; @@ -398,9 +399,9 @@ public PrecisionModel getPrecisionModel() { * tests for this condition and reports false if it is not met. * (This is a looser test than checking for validity). *

  • Linear rings have the same semantics. - *
  • Linear geometries are simple iff they do not self-intersect at points + *
  • Linear geometries are simple if they do not self-intersect at points * other than boundary points. - *
  • Zero-dimensional geometries (points) are simple iff they have no + *
  • Zero-dimensional geometries (points) are simple if they have no * repeated points. *
  • Empty Geometrys are always simple. * @@ -1227,9 +1228,9 @@ public Geometry buffer(double distance, int quadrantSegments) { * The end cap style specifies the buffer geometry that will be * created at the ends of linestrings. The styles provided are: *
      - *
    • BufferOp.CAP_ROUND - (default) a semi-circle - *
    • BufferOp.CAP_BUTT - a straight line perpendicular to the end segment - *
    • BufferOp.CAP_SQUARE - a half-square + *
    • {@link BufferParameters#CAP_ROUND} - (default) a semi-circle + *
    • {@link BufferParameters#CAP_FLAT} - a straight line perpendicular to the end segment + *
    • {@link BufferParameters#CAP_SQUARE} - a half-square *
    *

    * The buffer operation always returns a polygonal result. The negative or @@ -1391,7 +1392,7 @@ public Geometry difference(Geometry other) } /** - * Computes a Geometry representing the closure of the point-set + * Computes a Geometry representing the closure of the point-set * which is the union of the points in this Geometry which are not * contained in the other Geometry, * with the points in the other Geometry not contained in this diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java index 0201b586d4..ad63994695 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java @@ -168,7 +168,7 @@ public int orientationIndex(LineSegment seg) /** * Determines the orientation index of a {@link Coordinate} relative to this segment. - * The orientation index is as defined in {@link Orientation#computeOrientation}. + * The orientation index is as defined in {@link Orientation#index(Coordinate, Coordinate, Coordinate)}. * * @param p the coordinate to compare * @@ -176,7 +176,7 @@ public int orientationIndex(LineSegment seg) * @return -1 (RIGHT) if p is to the right of this segment * @return 0 (COLLINEAR) if p is collinear with this segment * - * @see Orientation#computeOrientation(Coordinate, Coordinate, Coordinate) + * @see Orientation#index(Coordinate, Coordinate, Coordinate) */ public int orientationIndex(Coordinate p) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java index 89fcef12cc..baa4fae5fe 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java @@ -26,9 +26,9 @@ public class Triangle { /** - * Tests whether a triangle is acute. A triangle is acute iff all interior + * Tests whether a triangle is acute. A triangle is acute if all interior * angles are acute. This is a strict test - right triangles will return - * false A triangle which is not acute is either right or obtuse. + * false. A triangle which is not acute is either right or obtuse. *

    * Note: this implementation is not robust for angles very close to 90 * degrees. @@ -476,9 +476,9 @@ public Coordinate inCentre() } /** - * Tests whether this triangle is acute. A triangle is acute iff all interior + * Tests whether this triangle is acute. A triangle is acute if all interior * angles are acute. This is a strict test - right triangles will return - * false A triangle which is not acute is either right or obtuse. + * false. A triangle which is not acute is either right or obtuse. *

    * Note: this implementation is not robust for angles very close to 90 * degrees. diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java index 921c414e7a..d6e4fa0a30 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java @@ -26,7 +26,7 @@ * MonotoneChains are a way of partitioning the segments of an edge to * allow for fast searching of intersections. * Specifically, a sequence of contiguous line segments - * is a monotone chain iff all the vectors defined by the oriented segments + * is a monotone chain if all the vectors defined by the oriented segments * lies in the same quadrant. *

    * Monotone Chains have the following useful properties: diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java index 9b7e64f712..0015467355 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java @@ -56,7 +56,7 @@ * for (invalid) self-intersections in Polygons. * In order to check if a Polygonal geometry has self-intersections, * use {@link Geometry#isValid()}). - *

  • Linear geometries are simple iff they do not self-intersect at interior points + *
  • Linear geometries are simple if they do not self-intersect at interior points * (i.e. points other than boundary points). * This is equivalent to saying that no two linear components satisfy the SFS {@link Geometry#touches(Geometry)} * predicate. @@ -180,7 +180,7 @@ public boolean isSimple(MultiLineString geom) } /** - * A MultiPoint is simple iff it has no repeated points + * A MultiPoint is simple if it has no repeated points * @deprecated use isSimple() */ public boolean isSimple(MultiPoint mp) @@ -225,7 +225,7 @@ private boolean isSimplePolygonal(Geometry geom) /** * Semantics for GeometryCollection is - * simple iff all components are simple. + * simple if all components are simple. * * @param geom * @return true if the geometry is simple diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/PolygonBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/PolygonBuilder.java index 9a02e185db..721d439e4d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/PolygonBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/PolygonBuilder.java @@ -222,7 +222,7 @@ private void placeFreeHoles(List shellList, List freeHoleList) * The innermost enclosing ring is the smallest enclosing ring. * The algorithm used depends on the fact that: *
    - * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * ring A contains ring B if envelope(ring A) contains envelope(ring B) *
    * This routine is only safe to use if the chosen point of the hole * is known to be properly contained in a shell diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeKey.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeKey.java index 8c77138aa7..13d898874c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeKey.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/EdgeKey.java @@ -19,7 +19,7 @@ * A key for sorting and comparing edges in a noded arrangement. * Relies on the fact that in a correctly noded arrangement * edges are identical (up to direction) - * iff they have their first segment in common. + * if they have their first segment in common. * * @author mdavis * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.java index e36528500e..82fb871dd1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdgeRing.java @@ -135,7 +135,7 @@ private Coordinate[] getCoordinates() * The innermost enclosing ring is the smallest enclosing ring. * The algorithm used depends on the fact that: *
    - * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * ring A contains ring B if envelope(ring A) contains envelope(ring B) *
    * This routine is only safe to use if the chosen point of the hole * is known to be properly contained in a shell diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java index 1d09e69ad9..84778c5eb7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java @@ -50,7 +50,7 @@ class EdgeRing { * The innermost enclosing ring is the smallest enclosing ring. * The algorithm used depends on the fact that: *
    - * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * ring A contains ring B if envelope(ring A) contains envelope(ring B) *
    * This routine is only safe to use if the chosen point of the hole * is known to be properly contained in a shell diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/HoleAssigner.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/HoleAssigner.java index 76666c32c9..ebbb2c9538 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/HoleAssigner.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/HoleAssigner.java @@ -90,7 +90,7 @@ private List queryOverlappingShells(Envelope ringEnv) { * The innermost enclosing ring is the smallest enclosing ring. * The algorithm used depends on the fact that: *
    - * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * ring A contains ring B if envelope(ring A) contains envelope(ring B) *
    * This routine is only safe to use if the chosen point of the hole * is known to be properly contained in a shell diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleContains.java b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleContains.java index ae7e2bd745..1e8ec2aa8e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleContains.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleContains.java @@ -105,7 +105,7 @@ private boolean isPointContainedInBoundary(Point point) private boolean isPointContainedInBoundary(Coordinate pt) { /** - * contains = false iff the point is properly contained in the rectangle. + * contains = false if the point is properly contained in the rectangle. * * This code assumes that the point lies in the rectangle envelope */ diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 1ca4aa86c2..066d4c692b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -60,7 +60,7 @@ public static boolean isValid(Geometry geom) /** * Checks whether a coordinate is valid for processing. - * Coordinates are valid iff their x and y ordinates are in the + * Coordinates are valid if their x and y ordinates are in the * range of the floating point representation. * * @param coord the coordinate to validate diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java index e5336be4ad..682c03bc0e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java @@ -164,7 +164,7 @@ public boolean isRemoved() *
  • first compare the quadrants. If the quadrants are different, it it * trivial to determine which vector is "greater". *
  • if the vectors lie in the same quadrant, the robust - * {@link Orientation#computeOrientation(Coordinate, Coordinate, Coordinate)} + * {@link Orientation#index(Coordinate, Coordinate, Coordinate)} * function can be used to decide the relative orientation of the vectors. * */ @@ -185,7 +185,7 @@ public int compareTo(Object obj) *
  • first compare the quadrants. If the quadrants are different, it it * trivial to determine which vector is "greater". *
  • if the vectors lie in the same quadrant, the robust - * {@link Orientation#computeOrientation(Coordinate, Coordinate, Coordinate)} + * {@link Orientation#index(Coordinate, Coordinate, Coordinate)} * function can be used to decide the relative orientation of the vectors. * */ diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexTest.java index db89532e71..07e3ac0768 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexTest.java @@ -20,7 +20,7 @@ import junit.textui.TestRunner; /** - * Tests CGAlgorithms.computeOrientation + * Tests Orientation.index * @version 1.7 */ public class OrientationIndexTest From e570d57b5dc555cbdf91430bdc295b20b05553e9 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 20 May 2021 13:04:06 -0700 Subject: [PATCH 007/275] Javadoc for STRtree. Signed-off-by: Martin Davis --- .../locationtech/jts/index/strtree/STRtree.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java index 9bcaf0781f..3594107d24 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java @@ -27,14 +27,16 @@ /** - * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. - * For two-dimensional spatial data. + * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. + * For two-dimensional spatial data. *

    - * The STR packed R-tree is simple to implement and maximizes space - * utilization; that is, as many leaves as possible are filled to capacity. - * Overlap between nodes is far less than in a basic R-tree. However, once the - * tree has been built (explicitly or on the first call to #query), items may - * not be added or removed. + * The STR packed R-tree is simple to implement and maximizes space + * utilization; that is, as many leaves as possible are filled to capacity. + * Overlap between nodes is far less than in a basic R-tree. + * However, the index is semi-static; once the tree has been built + * (which happens automatically upon the first query), items may + * not be added. + * Items may be removed from the tree using {@link #remove(Envelope, Object)}. *

    * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. * Spatial Databases With Application To GIS. From 480301f3b66b676a201dfaf71db0c46328e3ada4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gediminas=20Rim=C5=A1a?= Date: Fri, 21 May 2021 18:15:43 +0300 Subject: [PATCH 008/275] Fix Angle#interiorAngle method (#721) (#723) Signed-off-by: grimsa --- doc/JTS_Version_History.md | 1 + .../org/locationtech/jts/algorithm/Angle.java | 2 +- .../locationtech/jts/algorithm/AngleTest.java | 67 ++++++++++++++++--- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index d24dc41855..6f5da74b54 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -38,6 +38,7 @@ Distributions for older JTS versions can be obtained at the * Fix `HalfEdge.prev()` method (#703) * Fix `BufferOp` to remove invalid elements caused by inverted ring curves (#706) * Fix `IsSimpleOp` duplicate lines bug (#716) +* Fix `Angle.interiorAngle` to produce interior angle correctly (#721) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java index 3a45511b7d..311d0ccac4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java @@ -198,7 +198,7 @@ public static double interiorAngle(Coordinate p0, Coordinate p1, Coordinate p2) { double anglePrev = Angle.angle(p1, p0); double angleNext = Angle.angle(p1, p2); - return Math.abs(angleNext - anglePrev); + return normalizePositive(angleNext - anglePrev); } /** diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java index 632cd95356..7fe8cbba12 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java @@ -16,8 +16,15 @@ import junit.framework.TestCase; import junit.textui.TestRunner; +import org.locationtech.jts.geom.CoordinateSequenceFactory; +import org.locationtech.jts.geom.CoordinateSequences; +import org.locationtech.jts.geom.CoordinateXY; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.shape.random.RandomPointsBuilder; - +import java.util.Arrays; /** * @version 1.7 @@ -32,7 +39,7 @@ public static void main(String args[]) { public AngleTest(String name) { super(name); } - public void testAngle() throws Exception + public void testAngle() { assertEquals(Angle.angle(new Coordinate(10,0)), 0.0, TOLERANCE); assertEquals(Angle.angle(new Coordinate(10,10)), Math.PI/4, TOLERANCE); @@ -43,7 +50,7 @@ public void testAngle() throws Exception assertEquals(Angle.angle(new Coordinate(-10,-10)), -0.75*Math.PI, TOLERANCE); } - public void testIsAcute() throws Exception + public void testIsAcute() { assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(5,10)), true); assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(5,-10)), true); @@ -52,11 +59,9 @@ public void testIsAcute() throws Exception assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(-5,10)), false); assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(-5,-10)), false); - - } - public void testNormalizePositive() throws Exception + public void testNormalizePositive() { assertEquals(Angle.normalizePositive(0.0), 0.0, TOLERANCE); @@ -75,10 +80,9 @@ public void testNormalizePositive() throws Exception assertEquals(Angle.normalizePositive(2.5*Math.PI), 0.5*Math.PI, TOLERANCE); assertEquals(Angle.normalizePositive(3*Math.PI), Math.PI, TOLERANCE); assertEquals(Angle.normalizePositive(4 * Math.PI), 0.0, TOLERANCE); - } - public void testNormalize() throws Exception + public void testNormalize() { assertEquals(Angle.normalize(0.0), 0.0, TOLERANCE); @@ -97,9 +101,52 @@ public void testNormalize() throws Exception assertEquals(Angle.normalize(2.5*Math.PI), 0.5*Math.PI, TOLERANCE); assertEquals(Angle.normalize(3*Math.PI), Math.PI, TOLERANCE); assertEquals(Angle.normalize(4 * Math.PI), 0.0, TOLERANCE); - - } + public void testInteriorAngle() { + Coordinate p1 = new CoordinateXY(1, 2); + Coordinate p2 = new CoordinateXY(3, 2); + Coordinate p3 = new CoordinateXY(2, 1); + + // Tests all interior angles of a triangle "POLYGON ((1 2, 3 2, 2 1, 1 2))" + assertEquals(45, Math.toDegrees(Angle.interiorAngle(p1, p2, p3)), 0.01); + assertEquals(90, Math.toDegrees(Angle.interiorAngle(p2, p3, p1)), 0.01); + assertEquals(45, Math.toDegrees(Angle.interiorAngle(p3, p1, p2)), 0.01); + // Tests interior angles greater than 180 degrees + assertEquals(315, Math.toDegrees(Angle.interiorAngle(p3, p2, p1)), 0.01); + assertEquals(270, Math.toDegrees(Angle.interiorAngle(p1, p3, p2)), 0.01); + assertEquals(315, Math.toDegrees(Angle.interiorAngle(p2, p1, p3)), 0.01); + } + /** + * Tests interior angle calculation using a number of random triangles + */ + public void testInteriorAngle_randomTriangles() { + GeometryFactory geometryFactory = new GeometryFactory(); + CoordinateSequenceFactory coordinateSequenceFactory = geometryFactory.getCoordinateSequenceFactory(); + for (int i = 0; i < 100; i++){ + RandomPointsBuilder builder = new RandomPointsBuilder(); + builder.setNumPoints(3); + Geometry threeRandomPoints = builder.getGeometry(); + Polygon triangle = geometryFactory.createPolygon( + CoordinateSequences.ensureValidRing( + coordinateSequenceFactory, + coordinateSequenceFactory.create(threeRandomPoints.getCoordinates()) + ) + ); + // Triangle coordinates in clockwise order + Coordinate[] c = Orientation.isCCW(triangle.getCoordinates()) + ? triangle.reverse().getCoordinates() + : triangle.getCoordinates(); + double sumOfInteriorAngles = Angle.interiorAngle(c[0], c[1], c[2]) + + Angle.interiorAngle(c[1], c[2], c[0]) + + Angle.interiorAngle(c[2], c[0], c[1]); + assertEquals( + i + ": The sum of the angles of a triangle is not equal to two right angles for points: " + Arrays.toString(c), + Math.PI, + sumOfInteriorAngles, + 0.01 + ); + } + } } From 232a0aa8c3c892bb826d50a1d0c2b9dd9082e36e Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:46:43 +0200 Subject: [PATCH 009/275] Null checks should not be used with "instanceof" (#725) (cherry picked from commit 3ace5a666985fabd78510d8b880e3851ded8fd13) --- .../src/main/java/org/locationtech/jts/io/gml2/GMLHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLHandler.java b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLHandler.java index 8623a51bdd..a10a31364e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLHandler.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLHandler.java @@ -227,7 +227,7 @@ public void startElement(String uri, String localName, String qName, */ public void setDocumentLocator(Locator locator) { this.locator = locator; - if (delegate != null && delegate instanceof ContentHandler) + if (delegate instanceof ContentHandler) ((ContentHandler) delegate).setDocumentLocator(locator); } From 18e77f9c0d92befab72db175dfe4337971a50ab1 Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:46:58 +0200 Subject: [PATCH 010/275] Public constants must be static final not only final (#726) (cherry picked from commit 0be307acb9bd4708d12ba30713ffb8afe2fc9572) --- .../locationtech/jtstest/testbuilder/InspectorPanel.java | 8 ++++---- .../org/locationtech/jts/geomgraph/DirectedEdgeStar.java | 4 ++-- .../main/java/org/locationtech/jts/io/gml2/GMLWriter.java | 2 +- .../main/java/org/locationtech/jts/io/kml/KMLWriter.java | 2 +- .../operation/overlay/ConsistentPolygonRingChecker.java | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java index 8578d0b08f..7b8a00aeb7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java @@ -34,10 +34,10 @@ public class InspectorPanel extends TestBuilderPanel { private static final int BOX_SPACER = 5; - private final ImageIcon downIcon = AppIcons.DOWN; - private final ImageIcon upIcon = AppIcons.UP; - private final ImageIcon zoomIcon = AppIcons.ZOOM; - private final ImageIcon copyIcon = AppIcons.COPY; + private static final ImageIcon downIcon = AppIcons.DOWN; + private static final ImageIcon upIcon = AppIcons.UP; + private static final ImageIcon zoomIcon = AppIcons.ZOOM; + private static final ImageIcon copyIcon = AppIcons.COPY; GeometryTreePanel geomTreePanel; diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java index c7d1e931e6..3f1ef6e32c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java @@ -166,8 +166,8 @@ private List getResultAreaEdges() return resultAreaEdgeList; } - private final int SCANNING_FOR_INCOMING = 1; - private final int LINKING_TO_OUTGOING = 2; + private static final int SCANNING_FOR_INCOMING = 1; + private static final int LINKING_TO_OUTGOING = 2; /** * Traverse the star of DirectedEdges, linking the included edges together. * To link two dirEdges, the next pointer for an incoming dirEdge diff --git a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLWriter.java b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLWriter.java index 8defb2ae96..cdaa3021ec 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLWriter.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLWriter.java @@ -54,7 +54,7 @@ * @author Martin Davis */ public class GMLWriter { - private final String INDENT = " "; + private static final String INDENT = " "; private int startingIndentIndex = 0; diff --git a/modules/core/src/main/java/org/locationtech/jts/io/kml/KMLWriter.java b/modules/core/src/main/java/org/locationtech/jts/io/kml/KMLWriter.java index a57695e0e0..cd1fd37859 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/kml/KMLWriter.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/kml/KMLWriter.java @@ -93,7 +93,7 @@ public static String writeGeometry(Geometry geometry, double z, int precision, return writer.write(geometry); } - private final int INDENT_SIZE = 2; + private static final int INDENT_SIZE = 2; private static final String COORDINATE_SEPARATOR = ","; private static final String TUPLE_SEPARATOR = " "; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/ConsistentPolygonRingChecker.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/ConsistentPolygonRingChecker.java index 7fafa4315d..4e1f5483eb 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/ConsistentPolygonRingChecker.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/ConsistentPolygonRingChecker.java @@ -91,8 +91,8 @@ private boolean isPotentialResultAreaEdge(DirectedEdge de, int opCode) return false; } - private final int SCANNING_FOR_INCOMING = 1; - private final int LINKING_TO_OUTGOING = 2; + private static final int SCANNING_FOR_INCOMING = 1; + private static final int LINKING_TO_OUTGOING = 2; private void testLinkResultDirectedEdges(DirectedEdgeStar deStar, int opCode) { From bf8a0cb2a5f93f2671b59a089fe67d146846a57b Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:54:53 +0200 Subject: [PATCH 011/275] Removed redundant boolean literals (#727) (cherry picked from commit 8d115d2f3faf944fc45613fb9c242aa7e89652be) --- .../locationtech/jts/operation/linemerge/LineSequencer.java | 4 ++-- .../java/org/locationtech/jts/operation/overlayng/Edge.java | 2 +- .../jts/operation/predicate/RectangleIntersects.java | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineSequencer.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineSequencer.java index 5a24c1ae8c..c620aa36ce 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineSequencer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineSequencer.java @@ -380,11 +380,11 @@ private List orient(List seq) // test end edge before start edge, to make result stable // (ie. if both are good starts, pick the actual start - if (endEdge.getToNode().getDegree() == 1 && endEdge.getEdgeDirection() == false) { + if (endEdge.getToNode().getDegree() == 1 && !endEdge.getEdgeDirection()) { hasObviousStartNode = true; flipSeq = true; } - if (startEdge.getFromNode().getDegree() == 1 && startEdge.getEdgeDirection() == true) { + if (startEdge.getFromNode().getDegree() == 1 && startEdge.getEdgeDirection()) { hasObviousStartNode = true; flipSeq = false; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/Edge.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/Edge.java index 70b139916a..90b12a57b3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/Edge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/Edge.java @@ -109,7 +109,7 @@ public boolean direction() { throw new IllegalStateException("Edge direction cannot be determined because endpoints are equal"); } - return cmp == -1 ? true : false; + return cmp == -1; } /** diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java index d18b9f1989..aa52b17ce1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java @@ -181,7 +181,7 @@ protected void visit(Geometry element) protected boolean isDone() { - return intersects == true; + return intersects; } } @@ -248,7 +248,7 @@ protected void visit(Geometry geom) protected boolean isDone() { - return containsPoint == true; + return containsPoint; } } @@ -336,6 +336,6 @@ private void checkIntersectionWithSegments(LineString testLine) protected boolean isDone() { - return hasIntersection == true; + return hasIntersection; } } From fd93df1f3058e81b8339b8903ce09d8e1f17e27c Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:55:05 +0200 Subject: [PATCH 012/275] toString used on String (#728) (cherry picked from commit 0430b808e78238d3301bf2149faeac408ab3c7db) --- .../src/main/java/org/locationtech/jtstest/util/StringUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/util/StringUtil.java b/modules/tests/src/main/java/org/locationtech/jtstest/util/StringUtil.java index 051e48c029..77782d36db 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/util/StringUtil.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/util/StringUtil.java @@ -229,7 +229,7 @@ public static List fromCommaDelimitedString(String s) { ArrayList result = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(s, ","); while (tokenizer.hasMoreTokens()) { - result.add(tokenizer.nextToken().toString().trim()); + result.add(tokenizer.nextToken().trim()); } return result; } From 747fcbd7c9f9aa1d54aa08b16af126ecde48f1c2 Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:55:17 +0200 Subject: [PATCH 013/275] Use try/resource for stream (#729) (cherry picked from commit fc730205e8ce9c12ded3aeea5c764d7d24ebc216) --- .../locationtech/jtstest/util/FileUtil.java | 60 ++++++++++--------- .../jtstest/testrunner/TestReader.java | 11 ++-- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/FileUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/util/FileUtil.java index 4e39c311b7..31928292ce 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/util/FileUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/FileUtil.java @@ -62,11 +62,12 @@ public static boolean directoryExists(String directoryName) { public static List getContents(String textFileName) throws FileNotFoundException, IOException { List contents = new Vector(); FileReader fileReader = new FileReader(textFileName); - BufferedReader bufferedReader = new BufferedReader(fileReader); - String line = bufferedReader.readLine(); - while (line != null) { - contents.add(line); - line = bufferedReader.readLine(); + try(BufferedReader bufferedReader = new BufferedReader(fileReader)){ + String line = bufferedReader.readLine(); + while (line != null) { + contents.add(line); + line = bufferedReader.readLine(); + } } return contents; } @@ -89,11 +90,12 @@ public static String readText(File file) String thisLine; StringBuffer strb = new StringBuffer(""); - FileInputStream fin = new FileInputStream(file); - BufferedReader br = new BufferedReader(new InputStreamReader(fin)); - while ((thisLine = br.readLine()) != null) { - strb.append(thisLine + "\r\n"); - } + try(FileInputStream fin = new FileInputStream(file)){ + BufferedReader br = new BufferedReader(new InputStreamReader(fin)); + while ((thisLine = br.readLine()) != null) { + strb.append(thisLine + "\r\n"); + } + } String result = strb.toString(); return result; } @@ -102,12 +104,12 @@ public static String readText(File file) * Saves the String with the given filename */ public static void setContents(String textFileName, String contents) throws IOException { - FileWriter fileWriter = new FileWriter(textFileName, false); - BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); - bufferedWriter.write(contents); - bufferedWriter.flush(); - bufferedWriter.close(); - fileWriter.close(); + try(FileWriter fileWriter = new FileWriter(textFileName, false)){ + try(BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)){ + bufferedWriter.write(contents); + bufferedWriter.flush(); + } + } } /** @@ -115,19 +117,19 @@ public static void setContents(String textFileName, String contents) throws IOEx * Posted by Mark Thornton on Usenet. */ public static void copyFile(File source, File destination) throws IOException { - RandomAccessFile out = new RandomAccessFile(destination, "rw"); - //Tell the OS in advance how big the file will be. This may reduce fragmentation - out.setLength(source.length()); - //copy the content - FileInputStream in = new FileInputStream(source); - byte[] buffer = new byte[16384]; - while (true) { - int n = in.read(buffer); - if (n == -1) - break; - out.write(buffer, 0, n); + try(RandomAccessFile out = new RandomAccessFile(destination, "rw")){ + //Tell the OS in advance how big the file will be. This may reduce fragmentation + out.setLength(source.length()); + //copy the content + try(FileInputStream in = new FileInputStream(source)){ + byte[] buffer = new byte[16384]; + while (true) { + int n = in.read(buffer); + if (n == -1) + break; + out.write(buffer, 0, n); + } + } } - in.close(); - out.close(); } } diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java index b0147691e1..cb97371f94 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java @@ -587,11 +587,12 @@ private File absoluteWktFile(File wktFile, TestRun testRun) { public static List getContents(String textFileName) throws FileNotFoundException, IOException { List contents = new Vector(); FileReader fileReader = new FileReader(textFileName); - BufferedReader bufferedReader = new BufferedReader(fileReader); - String line = bufferedReader.readLine(); - while (line != null) { - contents.add(line); - line = bufferedReader.readLine(); + try(BufferedReader bufferedReader = new BufferedReader(fileReader)){ + String line = bufferedReader.readLine(); + while (line != null) { + contents.add(line); + line = bufferedReader.readLine(); + } } return contents; } From 212ebe2d05f015c3b5afea2ad48d99c37461693f Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:55:45 +0200 Subject: [PATCH 014/275] Typo error: check identical expression. (#730) (cherry picked from commit 0bb019105af6e25a7e3702fffff25be128847d5b) --- .../main/java/org/locationtech/jts/geom/GeometryOverlay.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java index 862804feac..0d81f0e24a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryOverlay.java @@ -140,8 +140,8 @@ static Geometry symDifference(Geometry a, Geometry b) static Geometry union(Geometry a, Geometry b) { // handle empty geometry cases - if (a.isEmpty() || a.isEmpty()) { - if (b.isEmpty() && b.isEmpty()) + if (a.isEmpty() || b.isEmpty()) { + if (a.isEmpty() && b.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.UNION, a, b, a.getFactory()); // special case: if either input is empty ==> other input From fd5aebbb0532344d7c5009221a899a97d13312dd Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:57:33 +0200 Subject: [PATCH 015/275] Removed unused imports (#731) (cherry picked from commit 09099feb12784536a3592d9d6cb1c6d7de50aecb) --- .../jtstest/cmd/GeometryOutput.java | 1 - .../jtstest/function/BoundaryFunctions.java | 4 ---- .../function/BufferByUnionFunctions.java | 9 -------- .../jtstest/function/DiffFunctions.java | 1 - .../jtstest/function/NodingFunctions.java | 9 -------- .../OverlayCommonBitsRemovedFunctions.java | 1 - .../function/OverlayNGSRFunctions.java | 1 - .../function/OverlayNGTestFunctions.java | 5 ---- .../function/PolygonOverlayFunctions.java | 1 - .../jtstest/function/SortingFunctions.java | 1 - .../function/TriangulationFunctions.java | 1 - .../geomfunction/BaseGeometryFunction.java | 1 - .../RepeaterGeometryFunction.java | 2 -- .../jtstest/testbuilder/AppCursors.java | 1 - .../jtstest/testbuilder/CommandPanel.java | 1 - .../testbuilder/GeometryEditPanel.java | 1 - .../testbuilder/GeometryViewStylePanel.java | 1 - .../jtstest/testbuilder/InfoPanel.java | 4 ---- .../jtstest/testbuilder/InspectorPanel.java | 1 - .../testbuilder/JTSTestBuilderFrame.java | 23 ------------------- .../testbuilder/JTSTestBuilderToolBar.java | 1 - .../jtstest/testbuilder/RelatePanel.java | 1 - .../jtstest/testbuilder/ResultWKTPanel.java | 2 -- .../jtstest/testbuilder/StatsPanel.java | 1 - .../testbuilder/TestBuilderDialogs.java | 1 - .../jtstest/testbuilder/WKTPanel.java | 4 +--- .../controller/JTSTestBuilderController.java | 6 ----- .../testbuilder/geom/GeometryLocation.java | 1 - .../testbuilder/io/JavaTestWriter.java | 1 - .../jtstest/testbuilder/io/PNGWriter.java | 15 ------------ .../io/shapefile/PolygonHandler.java | 1 - .../testbuilder/model/TestCaseEdit.java | 1 - .../ui/style/LineLabelBaseline.java | 1 - .../ui/style/PolygonStructureStyle.java | 5 ---- .../ui/style/SegmentIndexStyle.java | 1 - .../ui/style/VertexLabelStyle.java | 1 - .../testbuilder/ui/tools/BasicTool.java | 5 ---- .../testbuilder/ui/tools/BoxBandTool.java | 2 -- .../testbuilder/ui/tools/EditVertexTool.java | 1 - .../testbuilder/ui/tools/IndicatorTool.java | 6 ----- .../testbuilder/ui/tools/InfoTool.java | 2 -- .../jtstest/testbuilder/ui/tools/PanTool.java | 2 -- .../jtstest/util/io/MultiFormatReader.java | 2 -- .../jts/algorithm/InteriorPoint.java | 1 - .../jts/algorithm/MinimumBoundingCircle.java | 1 - .../jts/geom/CoordinateSequences.java | 1 - .../jts/geom/GeometryCollection.java | 2 -- .../locationtech/jts/io/OrdinateFormat.java | 1 - .../locationtech/jts/io/WKTFileReader.java | 1 - .../jts/noding/NodingValidator.java | 1 - .../jts/noding/ValidatingNoder.java | 2 -- .../jts/operation/BoundaryOp.java | 1 - .../buffer/OffsetSegmentGenerator.java | 2 -- .../jts/operation/overlayng/OverlayEdge.java | 1 - .../jts/operation/union/OverlapUnion.java | 1 - .../jts/shape/fractal/HilbertCode.java | 4 ---- .../locationtech/jts/util/IntArrayList.java | 1 - .../org/locationtech/jts/util/StringUtil.java | 2 -- .../jtsexample/geom/BasicExample.java | 1 - .../jtslab/IteratedOverlayFunctions.java | 1 - .../edgeray/EdgeRayIntersectionArea.java | 1 - .../geomop/NormalizedGeometryMatcher.java | 1 - .../locationtech/jtstest/testrunner/Test.java | 1 - 63 files changed, 1 insertion(+), 158 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java index 265342d282..db095b103f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java @@ -6,7 +6,6 @@ import org.locationtech.jts.io.WKBWriter; import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.locationtech.jts.io.gml2.GMLWriter; -import org.locationtech.jtstest.testbuilder.geom.GeometryUtil; import org.locationtech.jtstest.testbuilder.io.SVGTestWriter; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/BoundaryFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/BoundaryFunctions.java index b8a76b6790..5149d5e1c8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/BoundaryFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/BoundaryFunctions.java @@ -12,11 +12,7 @@ package org.locationtech.jtstest.function; import org.locationtech.jts.algorithm.BoundaryNodeRule; -import org.locationtech.jts.algorithm.MinimumBoundingCircle; -import org.locationtech.jts.algorithm.MinimumDiameter; -import org.locationtech.jts.densify.Densifier; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.OctagonalEnvelope; import org.locationtech.jts.operation.BoundaryOp; public class BoundaryFunctions { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java index e1020af3b3..d3a991c987 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java @@ -15,19 +15,10 @@ import java.util.Iterator; import java.util.List; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryCollectionIterator; import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.util.LinearComponentExtracter; -import org.locationtech.jts.noding.SegmentString; -import org.locationtech.jts.operation.buffer.BufferInputLineSimplifier; -import org.locationtech.jts.operation.buffer.BufferOp; -import org.locationtech.jts.operation.buffer.BufferParameters; -import org.locationtech.jts.operation.buffer.OffsetCurveSetBuilder; -import org.locationtech.jts.operation.buffer.validate.BufferResultValidator; import org.locationtech.jtstest.geomfunction.Metadata; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java index a8025c291d..95ec07abf0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java index 534683cbb6..d99ff4109a 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/NodingFunctions.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import org.locationtech.jts.algorithm.LineIntersector; @@ -23,23 +22,15 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateArrays; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.PrecisionModel; -import org.locationtech.jts.geom.util.LinearComponentExtracter; -import org.locationtech.jts.noding.BasicSegmentString; import org.locationtech.jts.noding.FastNodingValidator; import org.locationtech.jts.noding.NodingIntersectionFinder; import org.locationtech.jts.noding.IntersectionAdder; import org.locationtech.jts.noding.MCIndexNoder; -import org.locationtech.jts.noding.NodedSegmentString; import org.locationtech.jts.noding.Noder; -import org.locationtech.jts.noding.ScaledNoder; import org.locationtech.jts.noding.SegmentStringUtil; import org.locationtech.jts.noding.snap.SnappingNoder; -import org.locationtech.jts.noding.snapround.GeometryNoder; -import org.locationtech.jts.noding.snapround.MCIndexSnapRounder; import org.locationtech.jts.noding.snapround.SnapRoundingNoder; -import org.locationtech.jts.precision.GeometryPrecisionReducer; import org.locationtech.jtstest.geomfunction.Metadata; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayCommonBitsRemovedFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayCommonBitsRemovedFunctions.java index 522afd4ff5..de6893f15b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayCommonBitsRemovedFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayCommonBitsRemovedFunctions.java @@ -13,7 +13,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.precision.CommonBitsOp; -import org.locationtech.jts.precision.EnhancedPrecisionOp; public class OverlayCommonBitsRemovedFunctions { public static Geometry intersection(Geometry a, Geometry b) { return op().intersection(a, b); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGSRFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGSRFunctions.java index 91f87e79db..3c4c94740b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGSRFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGSRFunctions.java @@ -22,7 +22,6 @@ import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.geom.util.LineStringExtracter; import org.locationtech.jts.geom.util.PolygonExtracter; -import org.locationtech.jts.operation.overlayng.CoverageUnion; import org.locationtech.jts.operation.overlayng.OverlayNG; import org.locationtech.jts.operation.overlayng.PrecisionReducer; import org.locationtech.jts.operation.overlayng.UnaryUnionNG; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java index c62e993679..ee037acc1c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OverlayNGTestFunctions.java @@ -16,7 +16,6 @@ import java.util.List; -import static org.locationtech.jts.operation.overlayng.OverlayNG.DIFFERENCE; import static org.locationtech.jts.operation.overlayng.OverlayNG.SYMDIFFERENCE; import org.locationtech.jts.algorithm.LineIntersector; @@ -26,7 +25,6 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.MultiPolygon; -import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.noding.IntersectionAdder; @@ -36,9 +34,6 @@ import org.locationtech.jts.operation.overlayng.RingClipper; import org.locationtech.jts.operation.overlayng.LineLimiter; import org.locationtech.jts.operation.overlayng.OverlayNG; -import org.locationtech.jts.operation.overlayng.PrecisionUtil; -import org.locationtech.jts.operation.union.UnaryUnionOp; -import org.locationtech.jts.operation.union.UnionStrategy; public class OverlayNGTestFunctions { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java index dd39215497..d94f2892b5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java @@ -13,7 +13,6 @@ package org.locationtech.jtstest.function; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import org.locationtech.jts.algorithm.locate.SimplePointInAreaLocator; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java index 2b4711e1f0..645bb4f48d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/SortingFunctions.java @@ -20,7 +20,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.util.GeometryExtracter; import org.locationtech.jts.shape.fractal.HilbertCode; import org.locationtech.jts.shape.fractal.MortonCode; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulationFunctions.java index 15592b558f..39b3fafa8b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulationFunctions.java @@ -14,7 +14,6 @@ import org.locationtech.jts.geom.*; import org.locationtech.jts.triangulate.*; import org.locationtech.jts.triangulate.quadedge.LocateFailureException; -import org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision; import org.locationtech.jtstest.util.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java index 006d8e1dc4..d4c08076b7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java @@ -11,7 +11,6 @@ */ package org.locationtech.jtstest.geomfunction; -import java.lang.reflect.Method; import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.util.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java index 5bbb6297e5..93a6e692da 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/RepeaterGeometryFunction.java @@ -15,8 +15,6 @@ import java.util.List; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.util.GeometryMapper; -import org.locationtech.jts.geom.util.GeometryMapper.MapOp; import org.locationtech.jtstest.function.FunctionsUtil; import org.locationtech.jtstest.util.ClassUtil; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppCursors.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppCursors.java index 66eeffd35e..f9cbda3671 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppCursors.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppCursors.java @@ -15,7 +15,6 @@ import java.awt.Cursor; import java.awt.Toolkit; -import javax.swing.ImageIcon; public class AppCursors { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/CommandPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/CommandPanel.java index fda800bef1..d4f6bd87c6 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/CommandPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/CommandPanel.java @@ -35,7 +35,6 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Document; -import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.testbuilder.controller.CommandController; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java index 16270fce63..02537978c5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java @@ -19,7 +19,6 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; -import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.event.ComponentEvent; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java index 5f8329b0f5..949a33b6a1 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java @@ -6,7 +6,6 @@ import java.awt.event.ActionEvent; import javax.swing.JCheckBox; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InfoPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InfoPanel.java index a80e2d04e0..409730b7f7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InfoPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InfoPanel.java @@ -13,16 +13,12 @@ import java.awt.*; -import java.awt.event.*; import javax.swing.BorderFactory; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jtstest.testbuilder.geom.*; import org.locationtech.jtstest.testbuilder.model.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java index 7b8a00aeb7..7240e59a2d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java @@ -15,7 +15,6 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; -import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.util.Comparator; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java index e6eac2abee..49f275ab34 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java @@ -13,28 +13,23 @@ import java.awt.AWTEvent; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridLayout; -import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.WindowEvent; import java.io.File; import java.util.Iterator; import java.util.List; -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; -import javax.swing.border.BevelBorder; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; @@ -42,36 +37,18 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.util.LinearComponentExtracter; -import org.locationtech.jts.io.ParseException; import org.locationtech.jts.util.Assert; -import org.locationtech.jtstest.clean.CleanDuplicatePoints; import org.locationtech.jtstest.testbuilder.controller.ResultController; import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelListener; -import org.locationtech.jtstest.testbuilder.io.HtmlWriter; -import org.locationtech.jtstest.testbuilder.io.JavaTestWriter; import org.locationtech.jtstest.testbuilder.io.XMLTestWriter; import org.locationtech.jtstest.testbuilder.model.DisplayParameters; import org.locationtech.jtstest.testbuilder.model.GeometryEvent; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; import org.locationtech.jtstest.testbuilder.model.TestCaseEdit; -import org.locationtech.jtstest.testbuilder.ui.ImageUtil; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; import org.locationtech.jtstest.testbuilder.ui.dnd.FileDrop; -import org.locationtech.jtstest.testbuilder.ui.tools.DeleteVertexTool; -import org.locationtech.jtstest.testbuilder.ui.tools.EditVertexTool; -import org.locationtech.jtstest.testbuilder.ui.tools.ExtractComponentTool; -import org.locationtech.jtstest.testbuilder.ui.tools.InfoTool; -import org.locationtech.jtstest.testbuilder.ui.tools.LineStringTool; -import org.locationtech.jtstest.testbuilder.ui.tools.PanTool; -import org.locationtech.jtstest.testbuilder.ui.tools.PointTool; import org.locationtech.jtstest.testbuilder.ui.tools.RectangleTool; -import org.locationtech.jtstest.testbuilder.ui.tools.StreamPolygonTool; -import org.locationtech.jtstest.testbuilder.ui.tools.Tool; -import org.locationtech.jtstest.testbuilder.ui.tools.ZoomTool; -import org.locationtech.jtstest.testrunner.GuiUtil; -import org.locationtech.jtstest.util.FileUtil; import org.locationtech.jtstest.util.StringUtil; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderToolBar.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderToolBar.java index 65fbf24e74..09e79a7f39 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderToolBar.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderToolBar.java @@ -27,7 +27,6 @@ import javax.swing.SwingConstants; import org.locationtech.jtstest.testbuilder.controller.JTSTestBuilderController; -import org.locationtech.jtstest.testbuilder.ui.SwingUtil; public class JTSTestBuilderToolBar { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java index a31abb87a0..2a479a2ba3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java @@ -14,7 +14,6 @@ package org.locationtech.jtstest.testbuilder; -import java.awt.event.ActionEvent; import javax.swing.border.Border; import javax.swing.border.TitledBorder; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ResultWKTPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ResultWKTPanel.java index 91bf75ed71..790e38469f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ResultWKTPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ResultWKTPanel.java @@ -16,13 +16,11 @@ import java.awt.Color; import java.awt.Font; import java.awt.GridLayout; -import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.Box; -import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java index 31d32b565b..7edd893f06 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java @@ -13,7 +13,6 @@ import java.awt.BorderLayout; -import java.awt.SystemColor; import javax.swing.BorderFactory; import javax.swing.JPanel; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java index 680f8a23f8..44db16e0d8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java @@ -11,7 +11,6 @@ import org.locationtech.jtstest.testbuilder.io.HtmlSvgTestWriter; import org.locationtech.jtstest.testbuilder.io.HtmlWriter; import org.locationtech.jtstest.testbuilder.io.JavaTestWriter; -import org.locationtech.jtstest.testbuilder.io.SVGTestWriter; import org.locationtech.jtstest.testbuilder.io.XMLTestWriter; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java index 1c84ee0f2b..2bcdd5eb1f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java @@ -19,7 +19,6 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; -import java.awt.SystemColor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -28,9 +27,8 @@ import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonGroup; -import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JLabel; + import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java index 596d7d0a4a..306e180489 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java @@ -12,23 +12,18 @@ package org.locationtech.jtstest.testbuilder.controller; -import java.awt.event.ActionEvent; -import java.io.IOException; import javax.swing.JFileChooser; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.util.LinearComponentExtracter; -import org.locationtech.jts.io.ParseException; import org.locationtech.jtstest.testbuilder.TestBuilderDialogs; import org.locationtech.jtstest.clean.CleanDuplicatePoints; import org.locationtech.jtstest.testbuilder.GeometryEditPanel; import org.locationtech.jtstest.testbuilder.JTSTestBuilder; import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; import org.locationtech.jtstest.testbuilder.JTSTestBuilderToolBar; -import org.locationtech.jtstest.testbuilder.model.DisplayParameters; import org.locationtech.jtstest.testbuilder.model.GeometryEditModel; import org.locationtech.jtstest.testbuilder.model.LayerList; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; @@ -46,7 +41,6 @@ import org.locationtech.jtstest.testbuilder.ui.tools.StreamPolygonTool; import org.locationtech.jtstest.testbuilder.ui.tools.Tool; import org.locationtech.jtstest.testbuilder.ui.tools.ZoomTool; -import org.locationtech.jtstest.util.io.MultiFormatReader; public class JTSTestBuilderController diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryLocation.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryLocation.java index 2ee283bab2..b8cca998b0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryLocation.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryLocation.java @@ -14,7 +14,6 @@ import org.locationtech.jts.geom.*; import org.locationtech.jts.io.WKTWriter; -import org.locationtech.jts.util.NumberUtil; /** * Models the location of a point on a Geometry diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/JavaTestWriter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/JavaTestWriter.java index da20d0cf56..5735828dee 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/JavaTestWriter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/JavaTestWriter.java @@ -15,7 +15,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.WKTWriter; -import org.locationtech.jtstest.test.TestCaseList; import org.locationtech.jtstest.test.Testable; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; import org.locationtech.jtstest.util.StringUtil; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java index 26636473e4..025c9e90bb 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java @@ -18,15 +18,7 @@ import javax.imageio.ImageIO; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; import javax.swing.BorderFactory; import javax.swing.JFrame; @@ -34,16 +26,9 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.util.Assert; -import org.locationtech.jtstest.test.TestCaseList; -import org.locationtech.jtstest.test.Testable; -import org.locationtech.jtstest.testbuilder.BusyDialog; import org.locationtech.jtstest.testbuilder.GeometryEditPanel; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; import org.locationtech.jtstest.testbuilder.model.TestCaseEdit; -import org.locationtech.jtstest.testrunner.BooleanResult; -import org.locationtech.jtstest.testrunner.Test; -import org.locationtech.jtstest.util.FileUtil; -import org.locationtech.jtstest.util.StringUtil; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java index 7c684e834c..574f102c5b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java @@ -31,7 +31,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.PrecisionModel; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java index 0f8a6c66ff..64e6cbdcce 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java @@ -17,7 +17,6 @@ import org.locationtech.jts.io.ParseException; import org.locationtech.jtstest.test.TestCase; import org.locationtech.jtstest.test.Testable; -import org.locationtech.jtstest.testbuilder.JTSTestBuilder; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java index 15e56d4b23..5437750efa 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java @@ -4,7 +4,6 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.LineSegment; import org.locationtech.jts.geom.LineString; -import org.locationtech.jtstest.testbuilder.geom.EnvelopeClipper; import org.locationtech.jtstest.testbuilder.geom.SegmentClipper; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/PolygonStructureStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/PolygonStructureStyle.java index 6d61f32aa4..4743769ca3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/PolygonStructureStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/PolygonStructureStyle.java @@ -13,12 +13,7 @@ package org.locationtech.jtstest.testbuilder.ui.style; import java.awt.*; -import java.awt.geom.*; - -import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jtstest.testbuilder.ui.ColorUtil; import org.locationtech.jtstest.testbuilder.ui.Viewport; import org.locationtech.jtstest.testbuilder.ui.render.GeometryPainter; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/SegmentIndexStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/SegmentIndexStyle.java index 39e6fc969a..5c0084034d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/SegmentIndexStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/SegmentIndexStyle.java @@ -17,7 +17,6 @@ import org.locationtech.jts.awt.FontGlyphReader; import org.locationtech.jts.geom.Quadrant; -import org.locationtech.jts.noding.Octant; import org.locationtech.jtstest.testbuilder.ui.Viewport; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/VertexLabelStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/VertexLabelStyle.java index 86fa274ef4..ab4cc35b68 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/VertexLabelStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/VertexLabelStyle.java @@ -21,7 +21,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.OrdinateFormat; -import org.locationtech.jts.io.WKTWriter; import org.locationtech.jtstest.testbuilder.ui.GraphicsUtil; import org.locationtech.jtstest.testbuilder.ui.Viewport; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BasicTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BasicTool.java index a9fc4a7aca..1dcdced188 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BasicTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BasicTool.java @@ -15,19 +15,14 @@ import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.geom.Point2D; -import org.locationtech.jts.awt.FontGlyphReader; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.PrecisionModel; -import org.locationtech.jtstest.*; import org.locationtech.jtstest.testbuilder.AppConstants; import org.locationtech.jtstest.testbuilder.GeometryEditPanel; import org.locationtech.jtstest.testbuilder.JTSTestBuilder; -import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; import org.locationtech.jtstest.testbuilder.model.*; import org.locationtech.jtstest.testbuilder.ui.Viewport; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java index 6bf54b235a..a5af4c75d7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java @@ -25,9 +25,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jtstest.testbuilder.GeometryEditPanel; import org.locationtech.jtstest.testbuilder.JTSTestBuilder; -import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/EditVertexTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/EditVertexTool.java index 107ed91c1b..3cc0922c38 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/EditVertexTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/EditVertexTool.java @@ -20,7 +20,6 @@ import org.locationtech.jts.awt.GeometryCollectionShape; import org.locationtech.jts.geom.*; import org.locationtech.jtstest.testbuilder.AppCursors; -import org.locationtech.jtstest.testbuilder.IconLoader; import org.locationtech.jtstest.testbuilder.geom.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/IndicatorTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/IndicatorTool.java index fd819839c3..37e51eba64 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/IndicatorTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/IndicatorTool.java @@ -14,15 +14,9 @@ import java.awt.*; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.awt.geom.Point2D; import org.locationtech.jts.awt.FontGlyphReader; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jtstest.*; import org.locationtech.jtstest.testbuilder.AppConstants; -import org.locationtech.jtstest.testbuilder.model.*; public abstract class IndicatorTool extends BasicTool diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/InfoTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/InfoTool.java index 38989c992f..356cbb2737 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/InfoTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/InfoTool.java @@ -13,9 +13,7 @@ import java.awt.event.MouseEvent; -import org.locationtech.jtstest.*; import org.locationtech.jtstest.testbuilder.JTSTestBuilder; -import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/PanTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/PanTool.java index 96ef44d9ad..2babd52786 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/PanTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/PanTool.java @@ -12,11 +12,9 @@ package org.locationtech.jtstest.testbuilder.ui.tools; import java.awt.Cursor; -import java.awt.Toolkit; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; -import org.locationtech.jtstest.*; import org.locationtech.jtstest.testbuilder.AppCursors; import org.locationtech.jtstest.testbuilder.GeometryEditPanel; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatReader.java b/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatReader.java index 3adc41bd52..3a9a296062 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatReader.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/io/MultiFormatReader.java @@ -13,11 +13,9 @@ import java.io.IOException; -import javax.xml.parsers.*; import org.locationtech.jts.geom.*; import org.locationtech.jts.io.*; -import org.locationtech.jts.io.geojson.GeoJsonReader; import org.locationtech.jts.io.gml2.*; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java index 215ccfa5a8..47b411c26b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPoint.java @@ -16,7 +16,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; -import org.locationtech.jts.geom.GeometryComponentFilter; import org.locationtech.jts.geom.GeometryFilter; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java index 0f66b53285..6226c379b2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java @@ -14,7 +14,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateArrays; -import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Triangle; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java index 3217a8678b..9ede9eabd3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java @@ -12,7 +12,6 @@ package org.locationtech.jts.geom; import org.locationtech.jts.io.OrdinateFormat; -import org.locationtech.jts.util.NumberUtil; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java index 6d062f3a91..62ca39105c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java @@ -13,9 +13,7 @@ */ package org.locationtech.jts.geom; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.TreeSet; import org.locationtech.jts.util.Assert; diff --git a/modules/core/src/main/java/org/locationtech/jts/io/OrdinateFormat.java b/modules/core/src/main/java/org/locationtech/jts/io/OrdinateFormat.java index 3c5c1b8899..bf256f55a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/OrdinateFormat.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/OrdinateFormat.java @@ -13,7 +13,6 @@ package org.locationtech.jts.io; import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.Locale; diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java index b787cc6fed..c3764f7bea 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java @@ -17,7 +17,6 @@ import java.io.FileReader; import java.io.IOException; import java.io.Reader; -import java.io.StreamTokenizer; import java.util.ArrayList; import java.util.List; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java index d78b9cc958..ee5bcb25ee 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java @@ -19,7 +19,6 @@ import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.util.Debug; /** * Validates that a collection of {@link SegmentString}s is correctly noded. diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java index 94302d8a78..921b7cdc29 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java @@ -2,8 +2,6 @@ import java.util.Collection; -import org.locationtech.jts.algorithm.LineIntersector; -import org.locationtech.jts.algorithm.RobustLineIntersector; /** * A wrapper for {@link Noder}s which validates diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/BoundaryOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/BoundaryOp.java index e4f165f76e..ee8a138e88 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/BoundaryOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/BoundaryOp.java @@ -20,7 +20,6 @@ import org.locationtech.jts.algorithm.BoundaryNodeRule; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateArrays; -import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Dimension; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index 395a60e790..c6d607fe57 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -12,10 +12,8 @@ package org.locationtech.jts.operation.buffer; import org.locationtech.jts.algorithm.Angle; -import org.locationtech.jts.algorithm.HCoordinate; import org.locationtech.jts.algorithm.Intersection; import org.locationtech.jts.algorithm.LineIntersector; -import org.locationtech.jts.algorithm.NotRepresentableException; import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdge.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdge.java index 322e9bb35a..972ffd9f08 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayEdge.java @@ -12,7 +12,6 @@ package org.locationtech.jts.operation.overlayng; import java.util.Comparator; -import java.util.List; import org.locationtech.jts.edgegraph.HalfEdge; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/union/OverlapUnion.java b/modules/core/src/main/java/org/locationtech/jts/operation/union/OverlapUnion.java index 9112503dd1..23cb7da0d1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/union/OverlapUnion.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/union/OverlapUnion.java @@ -24,7 +24,6 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineSegment; -import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.geom.util.GeometryCombiner; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/fractal/HilbertCode.java b/modules/core/src/main/java/org/locationtech/jts/shape/fractal/HilbertCode.java index 97e8c9ccfd..93b753d52d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/fractal/HilbertCode.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/fractal/HilbertCode.java @@ -13,10 +13,6 @@ package org.locationtech.jts.shape.fractal; import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineSegment; -import org.locationtech.jts.shape.GeometricShapeBuilder; /** * Encodes points as the index along finite planar Hilbert curves. diff --git a/modules/core/src/main/java/org/locationtech/jts/util/IntArrayList.java b/modules/core/src/main/java/org/locationtech/jts/util/IntArrayList.java index 67435551cc..63eeaa9179 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/IntArrayList.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/IntArrayList.java @@ -12,7 +12,6 @@ package org.locationtech.jts.util; import java.util.Arrays; -import java.util.Objects; /** * An extendable array of primitive int values. diff --git a/modules/core/src/main/java/org/locationtech/jts/util/StringUtil.java b/modules/core/src/main/java/org/locationtech/jts/util/StringUtil.java index f8d08963ae..500660d0a6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/StringUtil.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/StringUtil.java @@ -17,8 +17,6 @@ import java.io.LineNumberReader; import java.io.PrintStream; import java.io.StringReader; -import java.text.DecimalFormat; -import java.text.NumberFormat; import java.util.ArrayList; import org.locationtech.jts.io.OrdinateFormat; diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java index 3c62dd68cc..7e24e790e5 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java @@ -15,7 +15,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.WKTReader; /** diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java index 16741741f2..168c543783 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; -import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java index 4dfaa14d9c..df9035602a 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java @@ -4,7 +4,6 @@ import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; -import org.locationtech.jts.algorithm.locate.SimplePointInAreaLocator; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Geometry; diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/NormalizedGeometryMatcher.java b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/NormalizedGeometryMatcher.java index 837ee81281..8d180c31e5 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/NormalizedGeometryMatcher.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/NormalizedGeometryMatcher.java @@ -13,7 +13,6 @@ package org.locationtech.jtstest.geomop; import org.locationtech.jts.geom.*; -import org.locationtech.jtstest.testrunner.GeometryResult; public class NormalizedGeometryMatcher diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Test.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Test.java index 36386e3aba..d163f5a02a 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Test.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Test.java @@ -12,7 +12,6 @@ package org.locationtech.jtstest.testrunner; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.locationtech.jts.geom.Geometry; From 4117aeec735f4dea20d154aafc713ce93e9fab5e Mon Sep 17 00:00:00 2001 From: Matteo Baccan Date: Fri, 21 May 2021 18:57:45 +0200 Subject: [PATCH 016/275] Correct boolean compare (#732) (cherry picked from commit 8d3b5f0c29ac8575501b328c4c0321eee30898b0) --- .../jtstest/testbuilder/controller/ResultController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java index eb4f8e3d8b..420f856906 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java @@ -115,7 +115,7 @@ private void resultLogEntry(GeometryFunctionInvocation function, String timeStri String funTimeLine = function.getSignature() + " : " + timeString; String entry = funTimeLine; String resultDesc = GeometryFunctionInvocation.toString(result); - if (resultDesc != null & resultDesc.length() < 40) entry += "\n ==> " + resultDesc; + if (resultDesc != null && resultDesc.length() < 40) entry += "\n ==> " + resultDesc; JTSTestBuilder.controller().displayInfo(entry, false); } From 15c015934e66072ef519b8538b09e78dd6451e23 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 24 May 2021 22:51:05 -0700 Subject: [PATCH 017/275] Add TestBuilder Validation.isSimpleEndpoints function Signed-off-by: Martin Davis --- .../jtstest/function/ValidationFunctions.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java index a4aa8e6ee0..64d4ddd583 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; +import org.locationtech.jts.algorithm.BoundaryNodeRule; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateArrays; import org.locationtech.jts.geom.Geometry; @@ -27,6 +28,7 @@ import org.locationtech.jts.operation.valid.IsSimpleOp; import org.locationtech.jts.operation.valid.IsValidOp; import org.locationtech.jts.operation.valid.TopologyValidationError; +import org.locationtech.jtstest.geomfunction.Metadata; public class ValidationFunctions @@ -104,6 +106,7 @@ public static boolean isSimple(Geometry geom) { return IsSimpleOp.isSimple(geom); } + @Metadata(description="Finds all non-simple points using the OGC Mod-2 Boundary Node Rule") public static Geometry nonSimpleAllPoints(Geometry geom) { IsSimpleOp op = new IsSimpleOp(geom); op.setFindAllLocations(true); @@ -111,9 +114,20 @@ public static Geometry nonSimpleAllPoints(Geometry geom) { return geom.getFactory().createMultiPointFromCoords(CoordinateArrays.toCoordinateArray(pts)); } + @Metadata(description="Find a non-simple point") public static Geometry nonSimplePoint(Geometry geom) { IsSimpleOp op = new IsSimpleOp(geom); Coordinate pt = op.getNonSimpleLocation(); return geom.getFactory().createPoint(pt); } + + @Metadata(description="Finds all non-simple points using the Endpoint Boundary Node Rule") + public static Geometry nonSimpleEndpoints(Geometry geom) { + IsSimpleOp op = new IsSimpleOp(geom, BoundaryNodeRule.ENDPOINT_BOUNDARY_RULE); + op.setFindAllLocations(true); + List pts = op.getNonSimpleLocations(); + return geom.getFactory().createMultiPointFromCoords(CoordinateArrays.toCoordinateArray(pts)); + } + + } From 93d804df9553e7c6cb888daee279e24841777c07 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 4 Jun 2021 15:35:51 -0700 Subject: [PATCH 018/275] Improve BufferByUnion functions Signed-off-by: Martin Davis --- .../function/BufferByUnionFunctions.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java index d3a991c987..b8df792399 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/BufferByUnionFunctions.java @@ -19,6 +19,8 @@ import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryCollectionIterator; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; import org.locationtech.jtstest.geomfunction.Metadata; @@ -59,17 +61,29 @@ public static Geometry bufferBySegments(Geometry g, double distance) return g.union(segBuf); } - public static Geometry bufferByChains(Geometry g, double distance, - @Metadata(title="Max Chain Size") + public static Geometry bufferBySections(Geometry g, double distance, + @Metadata(title="Section Size") int maxChainSize) { if (maxChainSize <= 0) - throw new IllegalArgumentException("Maximum Chain Size must be specified as an input parameter"); + throw new IllegalArgumentException("Section Size must be specified as an input parameter"); Geometry segs = LineHandlingFunctions.extractChains(g, maxChainSize); double posDist = Math.abs(distance); Geometry segBuf = bufferByComponents(segs, posDist); if (distance < 0.0) - return g.difference(segBuf); - return g.union(segBuf); + return OverlayNGRobust.overlay(g, segBuf, OverlayNG.DIFFERENCE); + return OverlayNGRobust.overlay(g, segBuf, OverlayNG.UNION); + } + + public static Geometry sectionBuffers(Geometry g, double distance, + @Metadata(title="Section Size") + int maxChainSize) + { + if (maxChainSize <= 0) + throw new IllegalArgumentException("Section Size must be specified as an input parameter"); + Geometry segs = LineHandlingFunctions.extractChains(g, maxChainSize); + double posDist = Math.abs(distance); + Geometry segBuf = componentBuffers(segs, posDist); + return segBuf; } } From 0f7f19f64c838935cb2e3c73280258d1c4bf7618 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 4 Jun 2021 15:41:17 -0700 Subject: [PATCH 019/275] Fix IsValid for LinearRings (#737) Signed-off-by: Martin Davis --- .../jts/operation/valid/IsValidOp.java | 14 +++++++++++--- .../jts/operation/valid/IsValidTest.java | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 066d4c692b..2d0f6e2c04 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -407,11 +407,15 @@ private void checkNoSelfIntersectingRings(GeometryGraph graph) private void checkNoSelfIntersectingRing(EdgeIntersectionList eiList) { Set nodeSet = new TreeSet(); - boolean isFirst = true; for (Iterator i = eiList.iterator(); i.hasNext(); ) { EdgeIntersection ei = (EdgeIntersection) i.next(); - if (isFirst) { - isFirst = false; + /** + * Do not count start point, so start/end node is not counted as a self-intersection. + * Another segment with a node in same location will still trigger an invalid error. + * (Note that the edgeIntersectionList may not contain the start/end node, + * due to noding short-circuiting.) + */ + if (isStartNode(ei)) { continue; } if (nodeSet.contains(ei.coord)) { @@ -426,6 +430,10 @@ private void checkNoSelfIntersectingRing(EdgeIntersectionList eiList) } } + private static boolean isStartNode(EdgeIntersection ei) { + return ei.getSegmentIndex() == 0 && ei.getDistance() == 0.0; + } + /** * Tests that each hole is inside the polygon shell. * This routine assumes that the holes have previously been tested diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index 2818eca5cd..4d39010a3b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -65,5 +65,23 @@ public void testLineString() throws Exception { g.isValid(); assertTrue(true); //No exception thrown [Jon Aquino] } + + public void testLinearRingTriangle() throws Exception { + Geometry g = reader.read( + "LINEARRING (100 100, 150 200, 200 100, 100 100)"); + assertTrue(g.isValid()); + } + + public void testLinearRingSelfCrossing() throws Exception { + Geometry g = reader.read( + "LINEARRING (150 100, 300 300, 100 300, 350 100, 150 100)"); + assertTrue(! g.isValid()); + } + + public void testLinearRingSelfCrossing2() throws Exception { + Geometry g = reader.read( + "LINEARRING (0 0, 100 100, 100 0, 0 100, 0 0)"); + assertTrue(! g.isValid()); + } } From e6cf60254f7b01b78453debccd6e1b05c0addbf1 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 4 Jun 2021 15:44:14 -0700 Subject: [PATCH 020/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 6f5da74b54..4f7035ee6a 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -39,6 +39,7 @@ Distributions for older JTS versions can be obtained at the * Fix `BufferOp` to remove invalid elements caused by inverted ring curves (#706) * Fix `IsSimpleOp` duplicate lines bug (#716) * Fix `Angle.interiorAngle` to produce interior angle correctly (#721) +* Fix `IsValidOp` to correctly report invalidity for certain kinds of LinearRings (#737) From 745b94e0456b0309c2abb5fed5a2c66fe691e78b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 8 Jun 2021 17:07:56 -0700 Subject: [PATCH 021/275] Fix GeometryPrecisionReducer to allow keeping collapsed components (#738) Signed-off-by: Martin Davis --- .../precision/GeometryPrecisionReducer.java | 12 ++- .../PointwisePrecisionReducerTransformer.java | 61 +++++++++++ .../PrecisionReducerTransformer.java | 102 +++++++++--------- .../GeometryPrecisionReducerTest.java | 52 +++------ 4 files changed, 138 insertions(+), 89 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index 70b4ea9212..f635ecdcae 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -20,7 +20,7 @@ /** * Reduces the precision of a {@link Geometry} * according to the supplied {@link PrecisionModel}, - * ensuring that the result is valid (unless specified otherwise). + * ensuring that the result is valid, unless specified otherwise. *

    * By default the reduced result is topologically valid * (i.e. {@link Geometry#isValid()} is true). @@ -42,7 +42,7 @@ * By default the geometry precision model is not changed. * This can be overridden by using {@link #setChangePrecisionModel(boolean)}. *

    - * Normally collapsed components (e.g. lines collapsing to a point) + * Normally, collapsed components (e.g. lines collapsing to a point) * are not included in the result. * This behavior can be changed by using {@link #setRemoveCollapsedComponents(boolean)}. * @@ -149,7 +149,13 @@ public void setPointwise(boolean isPointwise) */ public Geometry reduce(Geometry geom) { - Geometry reduced = PrecisionReducerTransformer.reduce(geom, targetPM, isPointwise); + Geometry reduced; + if (isPointwise) { + reduced = PointwisePrecisionReducerTransformer.reduce(geom, targetPM); + } + else { + reduced = PrecisionReducerTransformer.reduce(geom, targetPM, removeCollapsed); + } // TODO: incorporate this in the Transformer above if (changePrecisionModel) { diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java b/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java new file mode 100644 index 0000000000..b0a08968fb --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.precision; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.util.GeometryTransformer; + +/** + * A transformer to reduce the precision of a geometry pointwise. + * + * @author mdavis + * + */ +class PointwisePrecisionReducerTransformer extends GeometryTransformer { + + public static Geometry reduce(Geometry geom, PrecisionModel targetPM) { + PointwisePrecisionReducerTransformer trans = new PointwisePrecisionReducerTransformer(targetPM); + return trans.transform(geom); + } + + private PrecisionModel targetPM; + + PointwisePrecisionReducerTransformer(PrecisionModel targetPM) { + this.targetPM = targetPM; + } + + protected CoordinateSequence transformCoordinates( + CoordinateSequence coordinates, Geometry parent) { + if (coordinates.size() == 0) + return null; + + Coordinate[] coordsReduce = reducePointwise(coordinates); + return factory.getCoordinateSequenceFactory().create(coordsReduce); + } + + private Coordinate[] reducePointwise(CoordinateSequence coordinates) { + Coordinate[] coordReduce = new Coordinate[coordinates.size()]; + // copy coordinates and reduce + for (int i = 0; i < coordinates.size(); i++) { + Coordinate coord = coordinates.getCoordinate(i).copy(); + targetPM.makePrecise(coord); + coordReduce[i]= coord; + } + return coordReduce; + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java b/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java index 398532de7e..c9facb368a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.precision; import org.locationtech.jts.geom.Coordinate; @@ -12,27 +23,30 @@ import org.locationtech.jts.geom.util.GeometryTransformer; import org.locationtech.jts.operation.overlayng.PrecisionReducer; +/** + * A transformer to reduce the precision of geometry in a + * topologically valid way. + * Repeated points are removed. + * If geometry elements collapse below their valid length, + * they may be removed + * by specifying isRemoveCollapsedtrue. + * + * @author mdavis + * + */ class PrecisionReducerTransformer extends GeometryTransformer { - public static Geometry reduce(Geometry geom, PrecisionModel targetPM) { - return reduce(geom, targetPM, false); - } - - public static Geometry reduce(Geometry geom, PrecisionModel targetPM, boolean isPointwise) { - PrecisionReducerTransformer trans = new PrecisionReducerTransformer(targetPM, isPointwise); + public static Geometry reduce(Geometry geom, PrecisionModel targetPM, boolean isRemoveCollapsed) { + PrecisionReducerTransformer trans = new PrecisionReducerTransformer(targetPM, isRemoveCollapsed); return trans.transform(geom); } private PrecisionModel targetPM; - private boolean isPointwise = false; - - PrecisionReducerTransformer(PrecisionModel targetPM) { - this(targetPM, false); - } + private boolean isRemoveCollapsed = false; - PrecisionReducerTransformer(PrecisionModel targetPM, boolean isPointwise) { + PrecisionReducerTransformer(PrecisionModel targetPM, boolean isRemoveCollapsed) { this.targetPM = targetPM; - this.isPointwise = isPointwise; + this.isRemoveCollapsed = isRemoveCollapsed; } protected CoordinateSequence transformCoordinates( @@ -40,17 +54,11 @@ protected CoordinateSequence transformCoordinates( if (coordinates.size() == 0) return null; - Coordinate[] coordsReduce; - if (isPointwise) { - coordsReduce = reducePointwise(coordinates); - } - else { - coordsReduce = reduceCompress(coordinates); - } + Coordinate[] coordsReduce = reduceCompress(coordinates); /** - * Check to see if the removal of repeated points collapsed the coordinate - * List to an invalid length for the type of the parent geometry. It is not + * Check if the removal of repeated points collapsed the coordinate + * list to an invalid length for the type of the parent geometry. It is not * necessary to check for Point collapses, since the coordinate list can * never collapse to less than one point. If the length is invalid, return * the full-length coordinate array first computed, or null if collapses are @@ -61,16 +69,32 @@ protected CoordinateSequence transformCoordinates( if (parent instanceof LineString) minLength = 2; if (parent instanceof LinearRing) - minLength = 4; + minLength = LinearRing.MINIMUM_VALID_SIZE; - // collapse - return null so parent is removed or empty + /** + * Handle collapse. If specified return null so parent geometry is removed or empty, + * otherwise extend to required length. + */ if (coordsReduce.length < minLength) { - return null; + if (isRemoveCollapsed) { + return null; + } + coordsReduce = extend(coordsReduce, minLength); } - return factory.getCoordinateSequenceFactory().create(coordsReduce); } + private Coordinate[] extend(Coordinate[] coords, int minLength) { + if (coords.length >= minLength) + return coords; + Coordinate[] exCoords = new Coordinate[minLength]; + for (int i = 0; i < exCoords.length; i++) { + int iSrc = i < coords.length ? i : coords.length - 1; + exCoords[i] = coords[iSrc].copy(); + } + return exCoords; + } + private Coordinate[] reduceCompress(CoordinateSequence coordinates) { CoordinateList noRepeatCoordList = new CoordinateList(); // copy coordinates and reduce @@ -79,40 +103,16 @@ private Coordinate[] reduceCompress(CoordinateSequence coordinates) { targetPM.makePrecise(coord); noRepeatCoordList.add(coord, false); } - // remove repeated points, to simplify returned geometry as much as possible + // remove repeated points, to simplify geometry as much as possible Coordinate[] noRepeatCoords = noRepeatCoordList.toCoordinateArray(); return noRepeatCoords; } - private Coordinate[] reducePointwise(CoordinateSequence coordinates) { - Coordinate[] coordReduce = new Coordinate[coordinates.size()]; - // copy coordinates and reduce - for (int i = 0; i < coordinates.size(); i++) { - Coordinate coord = coordinates.getCoordinate(i).copy(); - targetPM.makePrecise(coord); - coordReduce[i]= coord; - } - return coordReduce; - } - protected Geometry transformPolygon(Polygon geom, Geometry parent) { - if (isPointwise) { - Geometry trans = super.transformPolygon(geom, parent); - /** - * For some reason the base transformer may return non-polygonal geoms here. - * Check this and return an empty polygon instead. - */ - if (trans instanceof Polygon) - return trans; - return factory.createPolygon(); - } return reduceArea(geom); } protected Geometry transformMultiPolygon(MultiPolygon geom, Geometry parent) { - if (isPointwise) { - return super.transformMultiPolygon(geom, parent); - } return reduceArea(geom); } diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java index d691f022f1..34b313862f 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java @@ -29,9 +29,6 @@ public class GeometryPrecisionReducerTest { private PrecisionModel pmFloat = new PrecisionModel(); private PrecisionModel pmFixed1 = new PrecisionModel(1); - private GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pmFixed1); - private GeometryPrecisionReducer reducerKeepCollapse - = new GeometryPrecisionReducer(pmFixed1); private GeometryFactory gfFloat = new GeometryFactory(pmFloat, 0); WKTReader reader = new WKTReader(gfFloat); @@ -43,7 +40,6 @@ public static void main(String args[]) { public GeometryPrecisionReducerTest(String name) { super(name); - reducerKeepCollapse.setRemoveCollapsedComponents(false); } public void testSquare() @@ -93,21 +89,21 @@ public void testPolygonHasValidResult() public void testLine() throws Exception { - checkReduceExact("LINESTRING ( 0 0, 0 1.4 )", + checkReduce("LINESTRING ( 0 0, 0 1.4 )", "LINESTRING (0 0, 0 1)"); } public void testLineNotNoded() throws Exception { - checkReduceExact("LINESTRING(1 1, 3 3, 9 9, 5.1 5, 2.1 2)", + checkReduce("LINESTRING(1 1, 3 3, 9 9, 5.1 5, 2.1 2)", "LINESTRING(1 1, 3 3, 9 9, 5 5, 2 2)"); } public void testLineRemoveCollapse() throws Exception { - checkReduceExact("LINESTRING ( 0 0, 0 .4 )", + checkReduce("LINESTRING ( 0 0, 0 .4 )", "LINESTRING EMPTY"); } @@ -115,10 +111,10 @@ public void testLineRemoveCollapse() * Disabled for now. * @throws Exception */ - public void xtestLineKeepCollapse() + public void testLineKeepCollapse() throws Exception { - checkReduceExactSameFactory(reducerKeepCollapse, + checkReduceKeepCollapse(1, "LINESTRING ( 0 0, 0 .4 )", "LINESTRING ( 0 0, 0 0 )"); } @@ -126,14 +122,14 @@ public void xtestLineKeepCollapse() public void testPoint() throws Exception { - checkReduceExact("POINT(1.1 4.9)", + checkReduce("POINT(1.1 4.9)", "POINT(1 5)"); } public void testMultiPoint() throws Exception { - checkReduceExact("MULTIPOINT( (1.1 4.9),(1.2 4.8), (3.3 6.6))", + checkReduce("MULTIPOINT( (1.1 4.9),(1.2 4.8), (3.3 6.6))", "MULTIPOINT((1 5), (1 5), (3 7))"); } @@ -185,8 +181,6 @@ public void testPolgonWithCollapsedPointPointwise() throws Exception { //======================================= - - private void checkReducePointwise(String wkt, String wktExpected) { Geometry g = read(wkt); Geometry gExpected = read(wktExpected); @@ -194,46 +188,34 @@ private void checkReducePointwise(String wkt, String wktExpected) { assertEqualsExactAndHasSameFactory(gExpected, gReduce); } - private void assertEqualsExactAndHasSameFactory(Geometry expected, Geometry actual) { checkEqual(expected, actual); assertTrue("Factories are not the same", expected.getFactory() == actual.getFactory()); } - - - - private void checkReduceExact(String wkt, String wktExpected) { - checkReduceExactSameFactory(reducer, wkt, wktExpected); - } - private void checkReduceExactSameFactory(GeometryPrecisionReducer reducer, + private void checkReduceKeepCollapse( + double scaleFactor, String wkt, String wktExpected) { - Geometry g = read(wkt); - Geometry expected = read(wktExpected); - Geometry actual = reducer.reduce(g); - assertTrue(actual.equalsExact(expected)); - assertTrue(expected.getFactory() == expected.getFactory()); - } - - private void checkReduceExact(double scaleFactor, String wkt, String wktExpected) { - PrecisionModel pm = new PrecisionModel(scaleFactor); - GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); - checkReduceExactSameFactory(reducer, wkt, wktExpected); - } - - private void checkReduce(double scaleFactor, String wkt, String wktExpected) { PrecisionModel pm = new PrecisionModel(scaleFactor); GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); + reducer.setRemoveCollapsedComponents(false); checkReduce(reducer, wkt, wktExpected); } private void checkReduce( String wkt, String wktExpected) { + checkReduce(1, wkt, wktExpected); + } + + private void checkReduce(double scaleFactor, String wkt, String wktExpected) { + PrecisionModel pm = new PrecisionModel(scaleFactor); + GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); checkReduce(reducer, wkt, wktExpected); } + private void checkReduce( GeometryPrecisionReducer reducer, String wkt, From a3a5a99c61b5d2b5690e397a3dd41fe0f08e042c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 8 Jun 2021 17:09:28 -0700 Subject: [PATCH 022/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 4f7035ee6a..9cbdaa54df 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -40,6 +40,7 @@ Distributions for older JTS versions can be obtained at the * Fix `IsSimpleOp` duplicate lines bug (#716) * Fix `Angle.interiorAngle` to produce interior angle correctly (#721) * Fix `IsValidOp` to correctly report invalidity for certain kinds of LinearRings (#737) +* Fix `GeometryPrecisionReducer` to support the "keep collapsed components" semantics (#738) From a2f377fe1c47815f0382e64a5c7d025372576896 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 9 Jun 2021 16:16:02 -0700 Subject: [PATCH 023/275] Fix VoronoiDiagramBuilder to respect clip envelope (#740) Signed-off-by: Martin Davis --- .../jts/triangulate/VoronoiDiagramBuilder.java | 13 ++++++------- .../jts/triangulate/VoronoiDiagramBuilderTest.java | 7 +++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilder.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilder.java index 922a49b8a1..37dc9d29e5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilder.java @@ -109,22 +109,21 @@ private void create() { if (subdiv != null) return; - Envelope siteEnv = DelaunayTriangulationBuilder.envelope(siteCoords); diagramEnv = clipEnv; if (diagramEnv == null) { /** - * If no user-provided clip env, - * create one which encloses all the sites, - * with a buffer around the edges. + * If no user-provided clip envelope, + * use one which encloses all the sites, + * with a 50% buffer around the edges. */ - diagramEnv = siteEnv; - // add a buffer around the sites envelope + diagramEnv = DelaunayTriangulationBuilder.envelope(siteCoords); + // add a 50% buffer around the sites envelope double expandBy = diagramEnv.getDiameter(); diagramEnv.expandBy(expandBy); } List vertices = DelaunayTriangulationBuilder.toVertices(siteCoords); - subdiv = new QuadEdgeSubdivision(siteEnv, tolerance); + subdiv = new QuadEdgeSubdivision(diagramEnv, tolerance); IncrementalDelaunayTriangulator triangulator = new IncrementalDelaunayTriangulator(subdiv); triangulator.insertSites(vertices); } diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilderTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilderTest.java index 8851c1efde..d2b338ba31 100644 --- a/modules/core/src/test/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/VoronoiDiagramBuilderTest.java @@ -19,6 +19,13 @@ public void testClipEnvelope() { assertTrue(voronoi.getEnvelopeInternal().equals(clip.getEnvelopeInternal())); } + public void testClipEnvelopeBig() { + Geometry sites = read("MULTIPOINT ((50 100), (50 50), (100 50), (100 100))"); + Geometry clip = read("POLYGON ((-1000 1000, 1000 1000, 1000 -1000, -1000 -1000, -1000 1000))"); + Geometry voronoi = voronoiDiagram(sites, clip); + assertTrue(voronoi.getEnvelopeInternal().equals(clip.getEnvelopeInternal())); + } + private static final double TRIANGULATION_TOLERANCE = 0.0; public static Geometry voronoiDiagram(Geometry sitesGeom, Geometry clipGeom) From c476a94091af5b5cb6515dd5ed6b9dd95e29d93c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 9 Jun 2021 16:17:15 -0700 Subject: [PATCH 024/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 9cbdaa54df..db47ac5eb2 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -41,6 +41,7 @@ Distributions for older JTS versions can be obtained at the * Fix `Angle.interiorAngle` to produce interior angle correctly (#721) * Fix `IsValidOp` to correctly report invalidity for certain kinds of LinearRings (#737) * Fix `GeometryPrecisionReducer` to support the "keep collapsed components" semantics (#738) +* Fix `VoronoiDiagramBuilder` to respect user-provided clip envelope (#740) From ef703c69aeb721311733a4fa799b7f7fe4298fa4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 15 Jun 2021 12:33:41 -0700 Subject: [PATCH 025/275] Add JTSOp version number Signed-off-by: Martin Davis --- .../src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java index 6dc6471a6a..20823c3fea 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.List; +import org.locationtech.jts.JTSVersion; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.WKTConstants; import org.locationtech.jtstest.cmd.JTSOpRunner.OpParams; @@ -179,6 +180,7 @@ private static CommandLine createCmdLine() { }; private void printHelp(boolean showFunctions) { + System.out.println("JTSOp -- Version " + JTSVersion.CURRENT_VERSION); for (String s : helpDoc) { System.out.println(s); } From 2d86d66570adb3a8de0535d70e710cbe35755cce Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 15 Jun 2021 15:51:56 -0700 Subject: [PATCH 026/275] Improve JTSOp geometry summary Signed-off-by: Martin Davis --- .../jtstest/cmd/GeometryOutput.java | 56 +++++++++++++++++-- .../locationtech/jtstest/cmd/JTSOpRunner.java | 4 +- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java index db095b103f..4ad264bc3d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java @@ -3,6 +3,13 @@ import java.util.List; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.LineString; +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.io.WKBWriter; import org.locationtech.jts.io.geojson.GeoJsonWriter; import org.locationtech.jts.io.gml2.GMLWriter; @@ -69,17 +76,18 @@ public static String writeGeometrySummary(String label, } public static String writeGeometrySummary(String label, - List g) + List geoms) { - if (g == null) return ""; - int nVert = getNumPoints(g); - return writeGeometrySummary(label, g.size(), nVert); + if (geoms == null) return ""; + int nVert = getNumPoints(geoms); + String geomTypes = getTypesSummary(geoms); + return writeGeometrySummary(label, geoms.size(), geomTypes, nVert); } public static String writeGeometrySummary(String label, - int numGeoms, int numVert) + int numGeoms, String geomTypes, int numVert) { - return String.format("%s : %d geometries, %d vertices", label, numGeoms, numVert); + return String.format("%s : %d %s, %d vertices", label, numGeoms, geomTypes, numVert); } private static int getNumPoints(List geoms) { @@ -90,4 +98,40 @@ private static int getNumPoints(List geoms) { return n; } + private static String getTypesSummary(List geoms) { + + int numPoint = 0; + int numMultiPoint = 0; + int numLineString = 0; + int numMultiLineString = 0; + int numPolygon = 0; + int numMultiPolygon = 0; + int numGeometryCollection = 0; + + for (Geometry g : geoms ) { + if (g instanceof Point) numPoint++; + else if (g instanceof MultiPoint) numMultiPoint++; + else if (g instanceof LineString) numLineString++; + else if (g instanceof MultiLineString) numMultiLineString++; + else if (g instanceof Polygon) numPolygon++; + else if (g instanceof MultiPolygon) numMultiPolygon++; + else if (g instanceof GeometryCollection) numGeometryCollection++; + } + StringBuilder sb = new StringBuilder(); + addName("Point", numPoint, sb); + addName("MultiPoint", numMultiPoint, sb); + addName("LineString", numLineString, sb); + addName("MultiLineString", numMultiLineString, sb); + addName("Polygon", numPolygon, sb); + addName("MultiPolygon", numMultiPolygon, sb); + addName("GeometryCollection", numGeometryCollection, sb); + return sb.toString(); + } + + private static void addName(String name, int num, StringBuilder sb) { + if (num <= 0) return; + if (sb.length() > 0) sb.append("/"); + sb.append(name); + if (num > 1) sb.append("s"); + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java index 5ddc52a330..f62e8ac180 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java @@ -269,8 +269,8 @@ private void executeFunction() { executeFunctionOverA(fun); if (isVerbose || isTime) { - out.println("\nOperations: " + opCount - + " Total Time: " + Stopwatch.getTimeString( totalTime )); + out.println("\nOperation " + func.getCategory() + "." + func.getName() + ": " + opCount + + " invocations - Total Time: " + Stopwatch.getTimeString( totalTime )); } } From a24242a87209b65f4f15d435c9514f40eb2c4649 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 15 Jun 2021 23:06:41 -0700 Subject: [PATCH 027/275] Change TestBuilder View offset to Layer shift control Signed-off-by: Martin Davis --- .../jtstest/testbuilder/AppStrings.java | 2 + .../testbuilder/GeometryEditPanel.java | 50 ++++++++++++------- .../testbuilder/GeometryViewStylePanel.java | 12 ----- .../jtstest/testbuilder/LayerStylePanel.java | 17 ++++++- .../jtstest/testbuilder/model/LayerList.java | 16 +++++- .../testbuilder/model/TestBuilderModel.java | 4 +- .../testbuilder/ui/render/ViewStyle.java | 12 ----- .../testbuilder/ui/style/LayerStyle.java | 10 ++++ 8 files changed, 76 insertions(+), 47 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java index 65d8cd06a5..b8701c4dcf 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java @@ -87,5 +87,7 @@ public class AppStrings { public static final String TIP_STYLE_SYMBOL = "Vertex Symbol"; + public static final String TIP_STYLE_SHIFT = "Shift layer display"; + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java index 02537978c5..d26904e15d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java @@ -567,11 +567,12 @@ public Renderer getRenderer() class GeometryEditPanelRenderer implements Renderer { - private static final double RESULT_OFFSET_GUTTER_FACTOR = 0.3; + private static final double LAYER_SHIFT_GUTTER_FACTOR = 0.3; private GeometryStretcherView stretchView = null; private Renderer currentRenderer = null; private boolean isRevealingTopology = false; - private boolean isRenderingStretchVertices = false; + private boolean isRenderingStretchVertices = false; + private double layerShiftX; public GeometryEditPanelRenderer() { @@ -607,6 +608,7 @@ public void render(Graphics2D g2) if (viewStyle.isBorderEnabled()) { drawBorder(g2, viewStyle.getBorderColor()); } + layerShiftX = computeLayerShift(tbModel.getLayersAll()); renderLayersTheme(tbModel.getLayersBase(), g2); renderLayersCore(getLayerList(), g2); @@ -636,7 +638,27 @@ public void render(Graphics2D g2) titleElement.setTitle(viewStyle.getTitle()); titleElement.paint(g2); } - + } + + private double computeLayerShift(LayerList lyrList) { + Envelope envBase = new Envelope(); + Envelope envShifted = new Envelope(); + boolean hasShifted = false; + int n = lyrList.size(); + for (int i = 0; i < n; i++) { + Layer layer = lyrList.getLayer(i); + if (layer.getLayerStyle().isShifted()) { + envShifted.expandToInclude(layerEnvelope(layer)); + hasShifted = true; + } + else { + envBase.expandToInclude(layerEnvelope(layer)); + } + } + if (! hasShifted) + return 0; + double offsetX = envBase.getMaxX() - envShifted.getMinX(); + return (1 + LAYER_SHIFT_GUTTER_FACTOR) * offsetX; } private void renderLayersCore(LayerList layerList, Graphics2D g) @@ -669,24 +691,8 @@ private Renderer createRendererCore(Layer layer, int i) { new StaticGeometryContainer(stretchView.getStretchedGeometry(i)), viewport); } - if (viewStyle.isOffsetResult() - && i == LayerList.LYR_RESULT) { - double resultOffset = computeResultOffset(); - return new LayerRenderer(layer, - new StaticGeometryContainer(offsetGeometry(layer.getGeometry(), resultOffset)), - viewport); - } return createRenderer(layer); } - - private double computeResultOffset() { - LayerList lyrList = getLayerList(); - Envelope envAB = layerEnvelope(lyrList.getLayer(LayerList.LYR_A)); - envAB.expandToInclude(layerEnvelope(lyrList.getLayer(LayerList.LYR_A))); - Envelope envResult = layerEnvelope(lyrList.getLayer(LayerList.LYR_RESULT)); - double offsetX = envAB.getMaxX() - envResult.getMinX(); - return (1 + RESULT_OFFSET_GUTTER_FACTOR) * offsetX; - } private Envelope layerEnvelope(Layer lyr) { if (lyr.hasGeometry()) return lyr.getGeometry().getEnvelopeInternal(); @@ -700,6 +706,12 @@ private Geometry offsetGeometry(Geometry geom, double offsetX) { } private Renderer createRenderer(Layer layer) { + if (layerShiftX > 0 + && layer.getLayerStyle().isShifted()) { + return new LayerRenderer(layer, + new StaticGeometryContainer(offsetGeometry(layer.getGeometry(), layerShiftX)), + viewport); + } return new LayerRenderer(layer, viewport); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java index 949a33b6a1..5a4cba41d8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java @@ -15,7 +15,6 @@ public class GeometryViewStylePanel extends LabelComponentsPanel { JTSTestBuilderFrame tbFrame; private JCheckBox cbGrid; - private JCheckBox cbOffsetResult; private JCheckBox cbLegend; private JCheckBox cbTitle; private JTextField txtTitle; @@ -119,16 +118,6 @@ public void colorChanged(Color clr) { } ); addRow("Border", cbViewBorder, ctlBorderClr); - - //-------------------------------------------------- - cbOffsetResult = new JCheckBox(); - cbOffsetResult.setSelected(viewStyle.isOffsetResult()); - cbOffsetResult.setAlignmentX(Component.LEFT_ALIGNMENT); - cbOffsetResult.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(ActionEvent e) { - updateView(); } - }); - addRow("Offset Result", cbOffsetResult); //-------------------------------------------------- cbGrid = new JCheckBox(); @@ -154,7 +143,6 @@ public void colorChanged(Color clr) { private void updateView() { ViewStyle viewStyle = new ViewStyle(); viewStyle.setGridEnabled(cbGrid.isSelected()); - viewStyle.setOffsetResult(cbOffsetResult.isSelected()); viewStyle.setBorderEnabled(cbViewBorder.isSelected()); viewStyle.setBorderColor(ctlBorderClr.getBackground()); viewStyle.setBackground(ctlBackgroundClr.getBackground()); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java index 6ce025ac33..84f2e5a4e9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java @@ -50,6 +50,7 @@ public class LayerStylePanel extends JPanel { private Layer layer; private JLabel title; + private JCheckBox cbShift; private JPanel stylePanel; private int rowIndex; private JCheckBox cbDashed; @@ -103,6 +104,7 @@ public void setLayer(Layer layer, boolean isModifiable) { txtName.setEditable(isModifiable); txtName.setFocusable(isModifiable); + cbShift.setSelected(layer.getLayerStyle().isShifted()); cbVertex.setSelected(layer.getLayerStyle().isVertices()); cbVertexLabel.setSelected(layer.getLayerStyle().isVertexLabels()); setVertexSymbol(comboVertexSymbol, layer.getLayerStyle().getVertexSymbol()); @@ -180,7 +182,20 @@ private JPanel stylePanel() { txtName.setMaximumSize(new Dimension(100,20)); txtName.setPreferredSize(new Dimension(100,20)); txtName.setMinimumSize(new Dimension(100,20)); - addRow("Name", txtName); + + cbShift = new JCheckBox(); + cbShift.setToolTipText(AppStrings.TIP_STYLE_SHIFT); + cbShift.setAlignmentX(Component.LEFT_ALIGNMENT); + cbShift.setText("Shift"); + cbShift.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + if (layer == null) return; + layer.getLayerStyle().setShift(cbShift.isSelected()); + JTSTestBuilder.controller().geometryViewChanged(); + } + }); + + addRow("Name", txtName, cbShift); txtName.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java index 26f1759ef5..2040b98d4f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java @@ -26,12 +26,20 @@ public class LayerList { - public static LayerList createInternal() { + public static LayerList createFixed() { LayerList list = new LayerList(); list.initFixed(); return list; } + public static LayerList create(LayerList l1, LayerList l2, LayerList l3) { + LayerList list = new LayerList(); + list.add(l1); + list.add(l2); + list.add(l3); + return list; + } + public static final int LYR_A = 0; public static final int LYR_B = 1; public static final int LYR_RESULT = 2; @@ -46,8 +54,8 @@ void initFixed() { layer.add(new Layer("A")); layer.add(new Layer("B")); layer.add(new Layer("Result")); - } + public int size() { return layer.size(); } public Layer getLayer(int i) @@ -153,6 +161,10 @@ public void addBottom(Layer lyr) { layer.add(lyr); } + public void add(LayerList lyrList) { + layer.addAll(lyrList.layer); + } + public void moveUp(Layer lyr) { int i = layer.indexOf(lyr); if (i <= 0) return; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestBuilderModel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestBuilderModel.java index a990b54df4..6f39c81820 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestBuilderModel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestBuilderModel.java @@ -47,7 +47,7 @@ public class TestBuilderModel private GeometryFactory geometryFactory = null; private GeometryEditModel geomEditModel; - private LayerList layerList = LayerList.createInternal(); + private LayerList layerList = LayerList.createFixed(); private LayerList layerListTop = new LayerList(); private LayerList layerListBase = new LayerList(); @@ -90,10 +90,12 @@ public String getResultDisplayString(Geometry g) } public LayerList getLayers() { return layerList; } + public LayerList getLayersAll() { return LayerList.create(layerListTop,layerList,layerListBase) ; } public LayerList getLayersTop() { return layerListTop; } public LayerList getLayersBase() { return layerListBase; } + public List getLayersLegend() { List layers = new ArrayList(); addLegendLayers(layerList, layers); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java index db9de2e29d..152b6be1b4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java @@ -42,10 +42,6 @@ public class ViewStyle { private boolean isBorderEnabled; private Color borderClr = Color.GRAY; - private boolean isOffsetResult = false; - - - public ViewStyle() { } @@ -133,14 +129,6 @@ public Color getBorderColor() { return borderClr; } - public void setOffsetResult(boolean isOffsetResult) { - this.isOffsetResult = isOffsetResult; - } - - public boolean isOffsetResult() { - return isOffsetResult; - } - public void setLegendStatsEnabled(boolean isEabled) { this.isLegendStatsEnabled = isEabled; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java index 99d587d7b6..ad388b2aba 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java @@ -50,6 +50,8 @@ public class LayerStyle implements Style { private SegmentIndexStyle segIndexStyle; + private boolean isShift; + public LayerStyle(BasicStyle geomStyle) { this.geomStyle = geomStyle; initDecorators(geomStyle); @@ -274,6 +276,14 @@ public boolean isSegIndex() { return decoratorStyle.isEnabled(segIndexStyle); } + public void setShift(boolean isShift) { + this.isShift = isShift; + } + + public boolean isShifted() { + return isShift; + } + static Geometry offsetLine(Geometry geom, double distance) { BufferParameters bufParams = new BufferParameters(); From 0b6656f7b6fa5f41ee1451ed6e42912f609429d5 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 16 Jun 2021 09:41:09 -0700 Subject: [PATCH 028/275] Improve TestBuilder layer shifting code Signed-off-by: Martin Davis --- .../testbuilder/GeometryEditPanel.java | 31 ++++++++----------- .../jtstest/testbuilder/model/Layer.java | 6 ++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java index d26904e15d..10a8db07f1 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java @@ -641,24 +641,24 @@ public void render(Graphics2D g2) } private double computeLayerShift(LayerList lyrList) { - Envelope envBase = new Envelope(); - Envelope envShifted = new Envelope(); - boolean hasShifted = false; + Envelope envBase = computeLayersEnv(lyrList, false); + Envelope envShifted = computeLayersEnv(lyrList, true); + if (envShifted.isNull()) + return 0; + double offsetX = envBase.getMaxX() - envShifted.getMinX(); + return (1 + LAYER_SHIFT_GUTTER_FACTOR) * offsetX; + } + + private Envelope computeLayersEnv(LayerList lyrList, boolean isShifted) { + Envelope env = new Envelope(); int n = lyrList.size(); for (int i = 0; i < n; i++) { Layer layer = lyrList.getLayer(i); - if (layer.getLayerStyle().isShifted()) { - envShifted.expandToInclude(layerEnvelope(layer)); - hasShifted = true; - } - else { - envBase.expandToInclude(layerEnvelope(layer)); + if (isShifted == layer.getLayerStyle().isShifted()) { + env.expandToInclude(layer.getEnvelope()); } } - if (! hasShifted) - return 0; - double offsetX = envBase.getMaxX() - envShifted.getMinX(); - return (1 + LAYER_SHIFT_GUTTER_FACTOR) * offsetX; + return env; } private void renderLayersCore(LayerList layerList, Graphics2D g) @@ -694,11 +694,6 @@ private Renderer createRendererCore(Layer layer, int i) { return createRenderer(layer); } - private Envelope layerEnvelope(Layer lyr) { - if (lyr.hasGeometry()) return lyr.getGeometry().getEnvelopeInternal(); - return new Envelope(); - } - private Geometry offsetGeometry(Geometry geom, double offsetX) { if (geom == null) return null; AffineTransformation trans = AffineTransformation.translationInstance(offsetX, 0); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java index bc695b9f40..5e78d84270 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java @@ -12,6 +12,7 @@ package org.locationtech.jtstest.testbuilder.model; +import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.testbuilder.geom.GeometryUtil; import org.locationtech.jtstest.testbuilder.ui.style.BasicStyle; @@ -90,6 +91,11 @@ public Geometry getGeometry() return geomCont.getGeometry(); } + public Envelope getEnvelope() { + if (hasGeometry()) return getGeometry().getEnvelopeInternal(); + return new Envelope(); + } + public boolean hasGeometry() { if (geomCont == null) return false; return null != geomCont.getGeometry(); From 09f4589eb51a42d2f2f98827d3197881ad5f7ac5 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 16 Jun 2021 10:43:45 -0700 Subject: [PATCH 029/275] Add TestBuilder prototype TintBandStyle Signed-off-by: Martin Davis --- .../ui/render/GeometryPainter.java | 3 - .../testbuilder/ui/style/LayerStyle.java | 5 ++ .../testbuilder/ui/style/TintBandStyle.java | 63 +++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/TintBandStyle.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/GeometryPainter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/GeometryPainter.java index d92e746a7e..13ce754289 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/GeometryPainter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/GeometryPainter.java @@ -12,13 +12,11 @@ package org.locationtech.jtstest.testbuilder.ui.render; -//import java.awt.*; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.Stroke; -import java.awt.geom.GeneralPath; import org.locationtech.jts.awt.PointShapeFactory; import org.locationtech.jts.awt.ShapeWriter; @@ -26,7 +24,6 @@ import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; -import org.locationtech.jtstest.*; import org.locationtech.jtstest.testbuilder.AppConstants; import org.locationtech.jtstest.testbuilder.ui.Viewport; import org.locationtech.jtstest.testbuilder.ui.style.Style; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java index ad388b2aba..6afbb16bbb 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LayerStyle.java @@ -52,6 +52,8 @@ public class LayerStyle implements Style { private boolean isShift; + //private TintBandStyle tintBandStyle; + public LayerStyle(BasicStyle geomStyle) { this.geomStyle = geomStyle; initDecorators(geomStyle); @@ -101,8 +103,11 @@ private void initDecorators(BasicStyle style) structureStyle = new PolygonStructureStyle(ColorUtil.opaque(style.getLineColor())); segIndexStyle = new SegmentIndexStyle(ColorUtil.opaque(style.getLineColor().darker())); + //tintBandStyle = new TintBandStyle(); + // order is important here StyleList styleList = new StyleList(); + //styleList.add(tintBandStyle); styleList.add(vertexLabelStyle); styleList.add(vertexStyle); styleList.add(endPointsStyle); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/TintBandStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/TintBandStyle.java new file mode 100644 index 0000000000..406abd374a --- /dev/null +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/TintBandStyle.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jtstest.testbuilder.ui.style; + +import java.awt.Color; +import java.awt.Graphics2D; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; +import org.locationtech.jtstest.testbuilder.ui.Viewport; +import org.locationtech.jtstest.testbuilder.ui.render.GeometryPainter; + + +/** + * WIP + * + * Idea: draw inside buffer instead of band - avoids need for different op. + * + * @author mdavis + * + */ +public class TintBandStyle implements Style +{ + private static final Color TINT_BAND_SHADE = new Color(255,255,255, 100); + + public TintBandStyle() { + } + + public void paint(Geometry geom, Viewport viewport, Graphics2D g2d) + { + if (! (geom instanceof Polygon)) + return; + + Geometry band = computeBand((Polygon) geom, 10); + if (band == null) + return; + GeometryPainter.paint(band, viewport, g2d, null, TINT_BAND_SHADE); + } + + private Geometry computeBand(Polygon poly, double dist) { + try { + Geometry insideBuffer = poly.buffer(-dist); + return OverlayNGRobust.overlay(poly, insideBuffer, OverlayNG.DIFFERENCE); + } + catch (TopologyException ex) { + return null; + } + } + +} From 71bd03c69a78b2e43bcf04dbaa87ef969483767d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Jun 2021 11:17:04 -0700 Subject: [PATCH 030/275] Improve IsValidOp (#743) --- .../jtstest/function/ValidationFunctions.java | 2 +- .../jts/operation/valid/AreaNode.java | 135 ++++ .../operation/valid/AreaTopologyAnalyzer.java | 281 +++++++ .../valid/ConnectedInteriorTester.java | 234 ------ .../operation/valid/ConsistentAreaTester.java | 149 ---- .../valid/IndexedNestedHoleTester.java | 104 +++ .../valid/IndexedNestedRingTester.java | 157 ---- .../valid/InvalidIntersectionFinder.java | 210 +++++ .../jts/operation/valid/IsValidOp.java | 729 +++++++++--------- .../jts/operation/valid/PolygonRing.java | 451 +++++++++++ .../jts/operation/valid/package.html | 3 +- .../jts/operation/valid/AreaNodeTest.java | 48 ++ .../jts/operation/valid/IsValidTest.java | 72 +- .../valid/SimpleNestedRingTester.java | 80 -- ...st.java => ValidSelfTouchingRingTest.java} | 74 +- .../valid/QuadtreeNestedRingTester.java | 100 --- .../valid/SweeplineNestedRingTester.java | 115 --- .../resources/testxml/general/TestValid.xml | 719 +++++++---------- 18 files changed, 1980 insertions(+), 1683 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/ConnectedInteriorTester.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/ConsistentAreaTester.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedRingTester.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java delete mode 100644 modules/core/src/test/java/org/locationtech/jts/operation/valid/SimpleNestedRingTester.java rename modules/core/src/test/java/org/locationtech/jts/operation/valid/{ValidSelfTouchingRingFormingHoleTest.java => ValidSelfTouchingRingTest.java} (66%) delete mode 100644 modules/core/src/test/java/test/jts/perf/operation/valid/QuadtreeNestedRingTester.java delete mode 100644 modules/core/src/test/java/test/jts/perf/operation/valid/SweeplineNestedRingTester.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java index 64d4ddd583..94f3dd6e74 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ValidationFunctions.java @@ -68,7 +68,7 @@ public static Geometry invalidGeoms(Geometry g) return g.getFactory().buildGeometry(invalidGeoms); } - public static boolean isValidAllowSelfTouchingRingFormingHole(Geometry g) { + public static boolean isValidAllowInvertedRing(Geometry g) { IsValidOp validOp = new IsValidOp(g); validOp.setSelfTouchingRingFormingHoleValid(true); return validOp.isValid(); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java new file mode 100644 index 0000000000..2c026def28 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Quadrant; + +/** + * Functions to compute topological information + * about nodes (ring intersections) in areal geometry. + * + * @author mdavis + * + */ +class AreaNode +{ + /** + * Check if the edges at a node between two rings (or one ring) cross. + * The node is topologically valid if the ring edges do not cross. + * This function assumes that the edges are not collinear. + * + * @param nodePt the node location + * @param a0 the previous edge endpoint in a ring + * @param a1 the next edge endpoint in a ring + * @param b0 the previous edge endpoint in the other ring + * @param b1 the next edge endpoint in the other ring + * @return true if the edges cross at the node + */ + public static boolean isCrossing(Coordinate nodePt, Coordinate a0, Coordinate a1, Coordinate b0, Coordinate b1) { + Coordinate aLo = a0; + Coordinate aHi = a1; + if (isAngleGreater(nodePt, aLo, aHi)) { + aLo = a1; + aHi = a0; + } + /** + * Find positions of b0 and b1. + * If they are the same they do not cross the other edge + */ + boolean isBetween0 = isBetween(nodePt, b0, aLo, aHi); + boolean isBetween1 = isBetween(nodePt, b1, aLo, aHi); + + return isBetween0 != isBetween1; + } + + /** + * Tests whether an edge node-b lies in the interior or exterior + * of a corner of a ring given by a0-node-a1. + * The ring interior is assumed to be on the right of the corner (a CW ring). + * The edge must not be collinear with the corner segments. + * + * @param nodePt the node location + * @param a0 the first vertex of the corner + * @param a1 the second vertex of the corner + * @param b the destination vertex of the edge + * @return true if the edge is interior to the ring corner + */ + public static boolean isInteriorSegment(Coordinate nodePt, Coordinate a0, Coordinate a1, Coordinate b) { + Coordinate aLo = a0; + Coordinate aHi = a1; + boolean isInteriorBetween = true; + if (isAngleGreater(nodePt, aLo, aHi)) { + aLo = a1; + aHi = a0; + isInteriorBetween = false; + } + boolean isBetween = isBetween(nodePt, b, aLo, aHi); + boolean isInterior = (isBetween && isInteriorBetween) + || (! isBetween && ! isInteriorBetween); + return isInterior; + } + + /** + * Tests if an edge p is between edges e0 and e1, + * where the edges all originate at a common origin. + * The "inside" of e0 and e1 is the arc which does not include the origin. + * The edges are assumed to be distinct (non-collinear). + * + * @param origin the origin + * @param p the destination point of edge p + * @param e0 the destination point of edge e0 + * @param e1 the destination point of edge e1 + * @return true if p is between e0 and e1 + */ + private static boolean isBetween(Coordinate origin, Coordinate p, Coordinate e0, Coordinate e1) { + boolean isGreater0 = isAngleGreater(origin, p, e0); + if (! isGreater0) return false; + boolean isGreater1 = isAngleGreater(origin, p, e1); + return ! isGreater1; + } + + /** + * Tests if the angle with the origin of a vector P is greater than that of the + * vector Q. + * + * @param origin the origin of the vectors + * @param p the endpoint of the vector P + * @param q the endpoint of the vector Q + * @return true if vector P has angle greater than Q + */ + private static boolean isAngleGreater(Coordinate origin, Coordinate p, Coordinate q) { + int quadrantP = quadrant(origin, p); + int quadrantQ = quadrant(origin, q); + + /** + * If the vectors are in different quadrants, + * that determines the ordering + */ + if (quadrantP > quadrantQ) return true; + if (quadrantP < quadrantQ) return false; + + //--- vectors are in the same quadrant + // Check relative orientation of vectors + // P > Q if it is CCW of Q + int orient = Orientation.index(origin, q, p); + return orient == Orientation.COUNTERCLOCKWISE; + } + + private static int quadrant(Coordinate origin, Coordinate p) { + double dx = p.getX() - origin.getX(); + double dy = p.getY() - origin.getY(); + return Quadrant.quadrant(dx, dy); + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java new file mode 100644 index 0000000000..5125586b32 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.algorithm.LineIntersector; +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.algorithm.PointLocation; +import org.locationtech.jts.algorithm.RobustLineIntersector; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateArrays; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Location; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.noding.BasicSegmentString; +import org.locationtech.jts.noding.MCIndexNoder; +import org.locationtech.jts.noding.SegmentString; + +/** + * Analyzes the topology of areal geometry + * to determine whether it is valid. + * + * @author mdavis + * + */ +class AreaTopologyAnalyzer { + + /** + * Finds a self-intersection (if any) in a {@link LinearRing}. + * + * @param ring the ring to analyze + * @return a self-intersection point if one exists, or null + */ + public static Coordinate findSelfIntersection(LinearRing ring) { + AreaTopologyAnalyzer ata = new AreaTopologyAnalyzer(ring, false); + if (ata.hasIntersection()) + return ata.getIntersectionLocation(); + return null; + } + + /** + * Tests whether a segment p0-p1 is inside or outside a ring. + *

    + * Preconditions: + *

      + *
    • The segment does not cross the ring + *
    • One or both of the segment endpoints may lie on the ring + *
    • The ring is valid + *
    + * + * @param p0 a segment vertex + * @param p1 a segment vertex + * @param ring the ring to test + * @return true if the segment lies inside the ring + */ + public static boolean isSegmentInRing(Coordinate p0, Coordinate p1, LinearRing ring) { + Coordinate[] ringPts = ring.getCoordinates(); + int loc = PointLocation.locateInRing(p0, ringPts); + if (loc == Location.EXTERIOR) return false; + if (loc == Location.INTERIOR) return true; + + /** + * The segment point is on the boundary of the ring. + * Use the topology at the node to check if the segment + * is inside or outside the ring. + */ + return isIncidentSegmentInRing(p0, p1, ringPts); + } + + /** + * Tests whether a touching segment is interior to a ring. + *

    + * Preconditions: + *

      + *
    • The segment does not cross the ring + *
    • The segment vertex p0 lies on the ring + *
    • The ring is valid + *
    + * This works for both shells and holes, but the caller must know + * the ring role. + * + * @param p0 the first vertex of the segment + * @param p1 the second vertex of the segment + * @param ringPts the points of the ring + * @return true if the segment is inside the ring. + */ + public static boolean isIncidentSegmentInRing(Coordinate p0, Coordinate p1, Coordinate[] ringPts) { + int index = intersectingSegIndex(ringPts, p0); + if (index < 0) { + throw new IllegalArgumentException("Segment vertex does not intersect ring"); + } + Coordinate rPrev = ringPts[index]; + Coordinate rNext = ringPts[index + 1]; + if (p0.equals2D(ringPts[index])) { + rPrev = ringPts[ringIndexPrev(ringPts, index)]; + } + /** + * If ring orientation is not normalized, flip the corner orientation + */ + boolean isInteriorOnRight = ! Orientation.isCCW(ringPts); + if (! isInteriorOnRight) { + Coordinate temp = rPrev; + rPrev = rNext; + rNext = temp; + } + return AreaNode.isInteriorSegment(p0, rPrev, rNext, p1); + } + + /** + * Computes the index of the segment which intersects a given point. + * @param ringPts the ring points + * @param pt the intersection point + * @return the intersection segment index, or -1 if no intersection is found + */ + private static int intersectingSegIndex(Coordinate[] ringPts, Coordinate pt) { + LineIntersector li = new RobustLineIntersector(); + for (int i = 0; i < ringPts.length - 1; i++) { + li.computeIntersection(pt, ringPts[i], ringPts[i + 1]); + if (li.hasIntersection()) { + //-- check if pt is the start point of the next segment + if (pt.equals2D(ringPts[i + 1])) { + return i + 1; + } + return i; + } + } + return -1; + } + + private static int ringIndexPrev(Coordinate[] ringPts, int index) { + int iPrev = index - 1; + if (index == 0) iPrev = ringPts.length - 2; + return iPrev; + } + + private Geometry inputGeom; + private boolean isInvertedRingValid; + + private InvalidIntersectionFinder intFinder; + private List polyRings = null; + private Coordinate disconnectionPt = null; + + public AreaTopologyAnalyzer(Geometry geom, boolean isInvertedRingValid) { + inputGeom = geom; + this.isInvertedRingValid = isInvertedRingValid; + analyze(); + } + + public boolean hasIntersection() { + return intFinder.hasIntersection(); + } + + public boolean hasDoubleTouch() { + return intFinder.hasDoubleTouch(); + } + + public Coordinate getIntersectionLocation() { + return intFinder.getIntersectionLocation(); + } + + /** + * Tests whether any polygon with holes has a disconnected interior + * by virtue of the holes (and possibly shell) forming a touch cycle. + *

    + * This is a global check, which relies on determining + * the touching graph of all holes in a polygon. + *

    + * If inverted rings disconnect the interior + * via a self-touch, this is checked by the {@link InvalidIntersectionFinder}. + * If inverted rings are part of a disconnected ring chain + * this is detected here. + * + * @return true if a polygon has a disconnected interior. + */ + public boolean isInteriorDisconnectedByRingCycle() { + /** + * PolyRings will be null for empty, no hole or LinearRing inputs + */ + if (polyRings != null) { + disconnectionPt = PolygonRing.findTouchCycleLocation(polyRings); + } + return disconnectionPt != null; + } + + public Coordinate getDisconnectionLocation() { + return disconnectionPt; + } + + /** + * Tests if an area interior is disconnected by a self-touching ring. + * This must be evaluated after other self-intersections have been analyzed + * and determined to not exist, since the logic relies on + * the rings not self-crossing (winding). + * + * @return true if an area interior is disconnected by a self-touch + */ + public boolean isInteriorDisconnectedBySelfTouch() { + if (polyRings != null) { + disconnectionPt = PolygonRing.findInteriorSelfNode(polyRings); + } + return disconnectionPt != null; + } + + private void analyze() { + if (inputGeom.isEmpty()) return; + intFinder = computeIntersections(inputGeom); + } + + private InvalidIntersectionFinder computeIntersections(Geometry geom) + { + List segStrings = extractSegmentStrings(geom); + InvalidIntersectionFinder segInt = new InvalidIntersectionFinder(isInvertedRingValid); + MCIndexNoder noder = new MCIndexNoder(); + noder.setSegmentIntersector(segInt); + noder.computeNodes(segStrings); + return segInt; + } + + private List extractSegmentStrings(Geometry geom) { + List segStrings = new ArrayList(); + if (geom instanceof LinearRing) { + LinearRing ring = (LinearRing) geom; + segStrings.add( createSegString(ring, null)); + return segStrings; + } + for (int i = 0; i < geom.getNumGeometries(); i++) { + Polygon poly = (Polygon) geom.getGeometryN(i); + if (poly.isEmpty()) continue; + boolean hasHoles = poly.getNumInteriorRing() > 0; + + //--- polygons with no holes do not need connected interior analysis + PolygonRing shellRing = null; + if (hasHoles || isInvertedRingValid) { + shellRing = new PolygonRing(poly.getExteriorRing()); + addPolygonRing(shellRing); + } + segStrings.add( createSegString(poly.getExteriorRing(), shellRing)); + + for (int j = 0 ; j < poly.getNumInteriorRing(); j++) { + LinearRing hole = poly.getInteriorRingN(j); + if (hole.isEmpty()) continue; + PolygonRing holeRing = new PolygonRing(hole, j, shellRing); + addPolygonRing(holeRing); + segStrings.add( createSegString(hole, holeRing)); + } + } + return segStrings; + } + + private void addPolygonRing(PolygonRing polyRing) { + if (polyRings == null) { + polyRings = new ArrayList(); + } + polyRings.add(polyRing); + } + + private static SegmentString createSegString(LinearRing ring, PolygonRing polyRing) { + Coordinate[] pts = ring.getCoordinates(); + + //--- repeated points must be removed for accurate intersection detection + if (CoordinateArrays.hasRepeatedPoints(pts)) { + pts = CoordinateArrays.removeRepeatedPoints(pts); + } + + SegmentString ss = new BasicSegmentString(pts, polyRing); + return ss; + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConnectedInteriorTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConnectedInteriorTester.java deleted file mode 100644 index 31e1401a93..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConnectedInteriorTester.java +++ /dev/null @@ -1,234 +0,0 @@ - - -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.operation.valid; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Location; -import org.locationtech.jts.geom.MultiPolygon; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.Position; -import org.locationtech.jts.geomgraph.DirectedEdge; -import org.locationtech.jts.geomgraph.Edge; -import org.locationtech.jts.geomgraph.EdgeRing; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.geomgraph.PlanarGraph; -import org.locationtech.jts.operation.overlay.MaximalEdgeRing; -import org.locationtech.jts.operation.overlay.OverlayNodeFactory; -import org.locationtech.jts.util.Assert; - -/** - * This class tests that the interior of an area {@link Geometry} - * ( {@link Polygon} or {@link MultiPolygon} ) - * is connected. - * This can happen if: - *

      - *
    • a shell self-intersects - *
    • one or more holes form a connected chain touching a shell at two different points - *
    • one or more holes form a ring around a subset of the interior - *
    - * If a disconnected situation is found the location of the problem is recorded. - * - * @version 1.7 - */ -public class ConnectedInteriorTester { - - public static Coordinate findDifferentPoint(Coordinate[] coord, Coordinate pt) - { - for (int i = 0; i < coord.length; i++) { - if (! coord[i].equals(pt)) - return coord[i]; - } - return null; - } - - private GeometryFactory geometryFactory = new GeometryFactory(); - - private GeometryGraph geomGraph; - // save a coordinate for any disconnected interior found - // the coordinate will be somewhere on the ring surrounding the disconnected interior - private Coordinate disconnectedRingcoord; - - public ConnectedInteriorTester(GeometryGraph geomGraph) - { - this.geomGraph = geomGraph; - } - - public Coordinate getCoordinate() { return disconnectedRingcoord; } - - public boolean isInteriorsConnected() - { - // node the edges, in case holes touch the shell - List splitEdges = new ArrayList(); - geomGraph.computeSplitEdges(splitEdges); - - // form the edges into rings - PlanarGraph graph = new PlanarGraph(new OverlayNodeFactory()); - graph.addEdges(splitEdges); - setInteriorEdgesInResult(graph); - graph.linkResultDirectedEdges(); - List edgeRings = buildEdgeRings(graph.getEdgeEnds()); - - /** - * Mark all the edges for the edgeRings corresponding to the shells - * of the input polygons. Note only ONE ring gets marked for each shell. - */ - visitShellInteriors(geomGraph.getGeometry(), graph); - - /** - * If there are any unvisited shell edges - * (i.e. a ring which is not a hole and which has the interior - * of the parent area on the RHS) - * this means that one or more holes must have split the interior of the - * polygon into at least two pieces. The polygon is thus invalid. - */ - return ! hasUnvisitedShellEdge(edgeRings); - } - - private void setInteriorEdgesInResult(PlanarGraph graph) - { - for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext(); ) { - DirectedEdge de = (DirectedEdge) it.next(); - if (de.getLabel().getLocation(0, Position.RIGHT) == Location.INTERIOR) { - de.setInResult(true); - } - } - } - - /** - * Form DirectedEdges in graph into Minimal EdgeRings. - * (Minimal Edgerings must be used, because only they are guaranteed to provide - * a correct isHole computation) - */ - private List buildEdgeRings(Collection dirEdges) - { - List edgeRings = new ArrayList(); - for (Iterator it = dirEdges.iterator(); it.hasNext(); ) { - DirectedEdge de = (DirectedEdge) it.next(); - // if this edge has not yet been processed - if (de.isInResult() - && de.getEdgeRing() == null) { - MaximalEdgeRing er = new MaximalEdgeRing(de, geometryFactory); - - er.linkDirectedEdgesForMinimalEdgeRings(); - List minEdgeRings = er.buildMinimalRings(); - edgeRings.addAll(minEdgeRings); - } - } - return edgeRings; - } - - /** - * Mark all the edges for the edgeRings corresponding to the shells - * of the input polygons. - * Only ONE ring gets marked for each shell - if there are others which remain unmarked - * this indicates a disconnected interior. - */ - private void visitShellInteriors(Geometry g, PlanarGraph graph) - { - if (g instanceof Polygon) { - Polygon p = (Polygon) g; - visitInteriorRing(p.getExteriorRing(), graph); - } - if (g instanceof MultiPolygon) { - MultiPolygon mp = (MultiPolygon) g; - for (int i = 0; i < mp.getNumGeometries(); i++) { - Polygon p = (Polygon) mp.getGeometryN(i); - visitInteriorRing(p.getExteriorRing(), graph); - } - } - } - - private void visitInteriorRing(LineString ring, PlanarGraph graph) - { - if (ring.isEmpty()) return; - Coordinate[] pts = ring.getCoordinates(); - Coordinate pt0 = pts[0]; - /** - * Find first point in coord list different to initial point. - * Need special check since the first point may be repeated. - */ - Coordinate pt1 = findDifferentPoint(pts, pt0); - Edge e = graph.findEdgeInSameDirection(pt0, pt1); - DirectedEdge de = (DirectedEdge) graph.findEdgeEnd(e); - DirectedEdge intDe = null; - if (de.getLabel().getLocation(0, Position.RIGHT) == Location.INTERIOR) { - intDe = de; - } - else if (de.getSym().getLabel().getLocation(0, Position.RIGHT) == Location.INTERIOR) { - intDe = de.getSym(); - } - Assert.isTrue(intDe != null, "unable to find dirEdge with Interior on RHS"); - - visitLinkedDirectedEdges(intDe); - } - - protected void visitLinkedDirectedEdges(DirectedEdge start) - { - DirectedEdge startDe = start; - DirectedEdge de = start; - do { - Assert.isTrue(de != null, "found null Directed Edge"); - de.setVisited(true); - de = de.getNext(); - } while (de != startDe); - } - - /** - * Check if any shell ring has an unvisited edge. - * A shell ring is a ring which is not a hole and which has the interior - * of the parent area on the RHS. - * (Note that there may be non-hole rings with the interior on the LHS, - * since the interior of holes will also be polygonized into CW rings - * by the linkAllDirectedEdges() step) - * - * @return true if there is an unvisited edge in a non-hole ring - */ - private boolean hasUnvisitedShellEdge(List edgeRings) - { - for (int i = 0; i < edgeRings.size(); i++) { - EdgeRing er = (EdgeRing) edgeRings.get(i); - // don't check hole rings - if (er.isHole()) - continue; - List edges = er.getEdges(); - DirectedEdge de = (DirectedEdge) edges.get(0); - // don't check CW rings which are holes - // (MD - this check may now be irrelevant) - if (de.getLabel().getLocation(0, Position.RIGHT) != Location.INTERIOR) continue; - - /** - * the edgeRing is CW ring which surrounds the INT of the area, so check all - * edges have been visited. If any are unvisited, this is a disconnected part of the interior - */ - for (int j = 0; j < edges.size(); j++) { - de = (DirectedEdge) edges.get(j); -//Debug.print("visted? "); Debug.println(de); - if (! de.isVisited()) { -//Debug.print("not visited "); Debug.println(de); - disconnectedRingcoord = de.getCoordinate(); - return true; - } - } - } - return false; - } -} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConsistentAreaTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConsistentAreaTester.java deleted file mode 100644 index 805402bcbb..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/ConsistentAreaTester.java +++ /dev/null @@ -1,149 +0,0 @@ - - -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.operation.valid; - -import java.util.Iterator; - -import org.locationtech.jts.algorithm.LineIntersector; -import org.locationtech.jts.algorithm.RobustLineIntersector; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.MultiPolygon; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.geomgraph.index.SegmentIntersector; -import org.locationtech.jts.operation.relate.EdgeEndBundle; -import org.locationtech.jts.operation.relate.RelateNode; -import org.locationtech.jts.operation.relate.RelateNodeGraph; - -/** - * Checks that a {@link GeometryGraph} representing an area - * (a {@link Polygon} or {@link MultiPolygon} ) - * has consistent semantics for area geometries. - * This check is required for any reasonable polygonal model - * (including the OGC-SFS model, as well as models which allow ring self-intersection at single points) - *

    - * Checks include: - *

      - *
    • test for rings which properly intersect - * (but not for ring self-intersection, or intersections at vertices) - *
    • test for consistent labelling at all node points - * (this detects vertex intersections with invalid topology, - * i.e. where the exterior side of an edge lies in the interior of the area) - *
    • test for duplicate rings - *
    - * If an inconsistency is found the location of the problem - * is recorded and is available to the caller. - * - * @version 1.7 - */ -public class ConsistentAreaTester { - - private final LineIntersector li = new RobustLineIntersector(); - private GeometryGraph geomGraph; - private RelateNodeGraph nodeGraph = new RelateNodeGraph(); - - // the intersection point found (if any) - private Coordinate invalidPoint; - - /** - * Creates a new tester for consistent areas. - * - * @param geomGraph the topology graph of the area geometry - */ - public ConsistentAreaTester(GeometryGraph geomGraph) - { - this.geomGraph = geomGraph; - } - - /** - * @return the intersection point, or null if none was found - */ - public Coordinate getInvalidPoint() { return invalidPoint; } - - /** - * Check all nodes to see if their labels are consistent with area topology. - * - * @return true if this area has a consistent node labelling - */ - public boolean isNodeConsistentArea() - { - /** - * To fully check validity, it is necessary to - * compute ALL intersections, including self-intersections within a single edge. - */ - SegmentIntersector intersector = geomGraph.computeSelfNodes(li, true, true); - /** - * A proper intersection means that the area is not consistent. - */ - if (intersector.hasProperIntersection()) { - invalidPoint = intersector.getProperIntersectionPoint(); - return false; - } - - nodeGraph.build(geomGraph); - - return isNodeEdgeAreaLabelsConsistent(); - } - - /** - * Check all nodes to see if their labels are consistent. - * If any are not, return false - * - * @return true if the edge area labels are consistent at this node - */ - private boolean isNodeEdgeAreaLabelsConsistent() - { - for (Iterator nodeIt = nodeGraph.getNodeIterator(); nodeIt.hasNext(); ) { - RelateNode node = (RelateNode) nodeIt.next(); - if (! node.getEdges().isAreaLabelsConsistent(geomGraph)) { - invalidPoint = node.getCoordinate().copy(); - return false; - } - } - return true; - } - - /** - * Checks for two duplicate rings in an area. - * Duplicate rings are rings that are topologically equal - * (that is, which have the same sequence of points up to point order). - * If the area is topologically consistent (determined by calling the - * isNodeConsistentArea, - * duplicate rings can be found by checking for EdgeBundles which contain - * more than one EdgeEnd. - * (This is because topologically consistent areas cannot have two rings sharing - * the same line segment, unless the rings are equal). - * The start point of one of the equal rings will be placed in - * invalidPoint. - * - * @return true if this area Geometry is topologically consistent but has two duplicate rings - */ - public boolean hasDuplicateRings() - { - for (Iterator nodeIt = nodeGraph.getNodeIterator(); nodeIt.hasNext(); ) { - RelateNode node = (RelateNode) nodeIt.next(); - for (Iterator i = node.getEdges().iterator(); i.hasNext(); ) { - EdgeEndBundle eeb = (EdgeEndBundle) i.next(); - if (eeb.getEdgeEnds().size() > 1) { - invalidPoint = eeb.getEdge().getCoordinate(0); - return true; - } - } - } - return false; - } - - - -} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java new file mode 100644 index 0000000000..194953f2d9 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import java.util.List; + +import org.locationtech.jts.algorithm.PointLocation; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Location; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.index.SpatialIndex; +import org.locationtech.jts.index.strtree.STRtree; + +/** + * Tests whether any holes of a Polygon are + * nested inside another hole, using a spatial + * index to speed up the comparisons. + *

    + * Assumes that the holes and polygon shell do not cross + * (are properly nested). + * Does not check the case where every vertex of a hole touches another + * hole; this is invalid, and must be checked elsewhere. + * + * @version 1.7 + */ +class IndexedNestedHoleTester +{ + private Polygon polygon; + private SpatialIndex index; + private Coordinate nestedPt; + + public IndexedNestedHoleTester(Polygon poly) + { + this.polygon = poly; + loadIndex(); + } + + private void loadIndex() + { + index = new STRtree(); + + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + LinearRing hole = (LinearRing) polygon.getInteriorRingN(i); + Envelope env = hole.getEnvelopeInternal(); + index.insert(env, hole); + } + } + + public Coordinate getNestedPoint() { return nestedPt; } + + public boolean isNested() + { + for (int i = 0; i < polygon.getNumInteriorRing(); i++) { + LinearRing hole = (LinearRing) polygon.getInteriorRingN(i); + + List results = index.query(hole.getEnvelopeInternal()); + for (int j = 0; j < results.size(); j++) { + LinearRing testHole = (LinearRing) results.get(j); + if (hole == testHole) + continue; + + /** + * Hole is not covered by in test hole, + * so cannot be inside + */ + if (! testHole.getEnvelopeInternal().covers( hole.getEnvelopeInternal()) ) + continue; + + if (isHoleInsideHole(hole, testHole)) + return true; + } + } + return false; + } + + private boolean isHoleInsideHole(LinearRing hole, LinearRing testHole) { + Coordinate[] testPts = testHole.getCoordinates(); + for (int i = 0; i < hole.getNumPoints(); i++) { + Coordinate holePt = hole.getCoordinateN(i); + int loc = PointLocation.locateInRing(holePt, testPts); + switch (loc) { + case Location.EXTERIOR: return false; + case Location.INTERIOR: + nestedPt = holePt; + return true; + } + // location is BOUNDARY, so keep trying points + } + return false; + } + + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedRingTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedRingTester.java deleted file mode 100644 index 739cd209bb..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedRingTester.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.operation.valid; - -import java.util.ArrayList; -import java.util.List; - -import org.locationtech.jts.algorithm.PointLocation; -import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geom.Location; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.index.SpatialIndex; -import org.locationtech.jts.index.strtree.STRtree; - -/** - * Tests whether any of a set of {@link LinearRing}s are - * nested inside another ring in the set, using a spatial - * index to speed up the comparisons. - * - * @version 1.7 - */ -public class IndexedNestedRingTester -{ - private GeometryGraph graph; // used to find non-node vertices - private List rings = new ArrayList(); - private Envelope totalEnv = new Envelope(); - private SpatialIndex index; - private Coordinate nestedPt; - - public IndexedNestedRingTester(GeometryGraph graph) - { - this.graph = graph; - } - - public Coordinate getNestedPoint() { return nestedPt; } - - public void add(LinearRing ring) - { - rings.add(ring); - totalEnv.expandToInclude(ring.getEnvelopeInternal()); - } - - public boolean isNonNested() - { - buildIndex(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing innerRing = (LinearRing) rings.get(i); - Coordinate[] innerRingPts = innerRing.getCoordinates(); - - List results = index.query(innerRing.getEnvelopeInternal()); -//System.out.println(results.size()); - for (int j = 0; j < results.size(); j++) { - LinearRing searchRing = (LinearRing) results.get(j); - Coordinate[] searchRingPts = searchRing.getCoordinates(); - - if (innerRing == searchRing) - continue; - - if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) - continue; - - Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph); - - /** - * If no non-node pts can be found, this means - * that the searchRing touches ALL of the innerRing vertices. - * This indicates an invalid polygon, since either - * the two holes create a disconnected interior, - * or they touch in an infinite number of points - * (i.e. along a line segment). - * Both of these cases are caught by other tests, - * so it is safe to simply skip this situation here. - */ - if (innerRingPt == null) - continue; - - boolean isInside = PointLocation.isInRing(innerRingPt, searchRingPts); - if (isInside) { - nestedPt = innerRingPt; - return false; - } - } - } - return true; - } - - /** - * An implementation of an optimization introduced in GEOS - * https://github.com/libgeos/geos/pull/255/commits/1bf16cdf5a4827b483a1f712e0597ccb243f58cb - * - * Not used for now, since improvement is small and very data-dependent. - * - * @return - */ - /* - private boolean isNonNestedWithIndex() - { - buildIndex(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing outerRing = (LinearRing) rings.get(i); - Coordinate[] outerRingPts = outerRing.getCoordinates(); - - IndexedPointInAreaLocator ptLocator = new IndexedPointInAreaLocator(outerRing); - List results = index.query(outerRing.getEnvelopeInternal()); -//System.out.println(results.size()); - for (int j = 0; j < results.size(); j++) { - LinearRing searchRing = (LinearRing) results.get(j); - if (outerRing == searchRing) - continue; - - if (! outerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) - continue; - - Coordinate[] searchRingPts = searchRing.getCoordinates(); - Coordinate innerRingPt = IsValidOp.findPtNotNode(searchRingPts, outerRing, graph); - - if (innerRingPt == null) - continue; - - boolean isInside = Location.EXTERIOR != ptLocator.locate(innerRingPt); - //boolean isInside = PointLocation.isInRing(innerRingPt, outerRingPts); - - if (isInside) { - nestedPt = innerRingPt; - return false; - } - } - } - return true; - } - */ - - private void buildIndex() - { - index = new STRtree(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing ring = (LinearRing) rings.get(i); - Envelope env = ring.getEnvelopeInternal(); - index.insert(env, ring); - } - } -} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java new file mode 100644 index 0000000000..f51abee31d --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java @@ -0,0 +1,210 @@ +package org.locationtech.jts.operation.valid; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.algorithm.LineIntersector; +import org.locationtech.jts.algorithm.RobustLineIntersector; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.noding.SegmentIntersector; +import org.locationtech.jts.noding.SegmentString; + +class InvalidIntersectionFinder +implements SegmentIntersector +{ + LineIntersector li = new RobustLineIntersector(); + private List intersectionPts = new ArrayList(); + private boolean hasProperInt = false; + private boolean hasIntersection = false; + private boolean hasCrossing= false; + private boolean hasDoubleTouch = false; + private boolean isInvertedRingValid; + + InvalidIntersectionFinder(boolean isInvertedRingValid) { + this.isInvertedRingValid = isInvertedRingValid; + } + + @Override + public boolean isDone() { + return hasIntersection || hasDoubleTouch; + } + + public Coordinate getIntersectionLocation() { + if (intersectionPts.size() == 0) return null; + return intersectionPts.get(0); + } + + public boolean hasDoubleTouch() { + return hasDoubleTouch; + } + + public boolean hasIntersection() { + return intersectionPts.size() > 0; + } + + @Override + public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { + // don't test a segment with itself + boolean isSameSegString = ss0 == ss1; + boolean isSameSegment = isSameSegString && segIndex0 == segIndex1; + if (isSameSegment) return; + + hasIntersection = findInvalidIntersection(ss0, segIndex0, ss1, segIndex1); + + if (hasIntersection) { + // found an intersection! + intersectionPts.add(li.getIntersection(0)); + } + } + + private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, + SegmentString ss1, int segIndex1) { + Coordinate p00 = ss0.getCoordinate(segIndex0); + Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); + Coordinate p10 = ss1.getCoordinate(segIndex1); + Coordinate p11 = ss1.getCoordinate(segIndex1 + 1); + + li.computeIntersection(p00, p01, p10, p11); + + if (! li.hasIntersection()) return false; + + /** + * Check for an intersection in the interior of both segments. + */ + hasProperInt = li.isProper(); + if (hasProperInt) + return true; + + /** + * Check for collinear segments (which produces two intersection points). + * This is invalid - either a zero-width spike or gore, + * or adjacent rings. + */ + hasProperInt = li.getIntersectionNum() >= 2; + if (hasProperInt) return true; + + /** + * Now know there is exactly one intersection, + * at a vertex of at least one segment. + */ + Coordinate intPt = li.getIntersection(0); + + /** + * If segments are adjacent the intersection must be their common endpoint. + * (since they are not collinear). + * This is valid. + */ + boolean isSameSegString = ss0 == ss1; + boolean isAdjacentSegments = isSameSegString && isAdjacentInRing(ss0, segIndex0, segIndex1); + // Assert: intersection is an endpoint of both segs + if (isAdjacentSegments) return false; + + // TODO: allow ring self-intersection - if NOT using OGC semantics + + /** + * Under OGC semantics, rings cannot self-intersect. + * So the intersection is invalid. + */ + if (isSameSegString && ! isInvertedRingValid) { + return true; + } + + /** + * Optimization: don't analyze intPts at the endpoint of a segment. + * This is because they are also start points, so don't need to be + * evaluated twice. + * This simplifies following logic, by removing the segment endpoint case. + */ + if (intPt.equals2D(p01) || intPt.equals2D(p11)) + return false; + + /** + * Check topology of a vertex intersection. + * The ring(s) must not cross. + */ + Coordinate e00 = p00; + Coordinate e01 = p01; + if (intPt.equals2D(p00)) { + e00 = prevCoordinateInRing(ss0, segIndex0); + e01 = p01; + } + Coordinate e10 = p10; + Coordinate e11 = p11; + if (intPt.equals2D(p10)) { + e10 = prevCoordinateInRing(ss1, segIndex1); + e11 = p11; + } + hasCrossing = AreaNode.isCrossing(intPt, e00, e01, e10, e11); + if (hasCrossing) + return true; + + /** + * If allowing inverted rings, record a self-touch to support later checking + * that it does not disconnect the interior. + */ + if (isSameSegString && isInvertedRingValid) { + addSelfTouch(ss0, intPt, e00, e01, e10, e11); + } + + /** + * If the rings are in the same polygon + * then record the touch to support connected interior checking. + * + * Also check for an invalid double-touch situation, + * if the rings are different. + */ + boolean isDoubleTouch = PolygonRing.addTouch((PolygonRing) ss0.getData(), (PolygonRing) ss1.getData(), intPt); + if (isDoubleTouch && ! isSameSegString) { + hasDoubleTouch = true; + return true; + } + + return false; + } + + private void addSelfTouch(SegmentString ss, Coordinate intPt, Coordinate e00, Coordinate e01, Coordinate e10, + Coordinate e11) { + PolygonRing polyRing = (PolygonRing) ss.getData(); + if (polyRing == null) { + throw new IllegalStateException("SegmentString missing PolygonRing data when checking valid self-touches"); + } + polyRing.addSelfTouch(intPt, e00, e01, e10, e11); + } + + /** + * For a segment string for a ring, gets the coordinate + * previous to the given index (wrapping if the index is 0) + * + * @param ringSS the ring segment string + * @param segIndex the segment index + * @return the coordinate previous to the given segment + */ + private static Coordinate prevCoordinateInRing(SegmentString ringSS, int segIndex) { + int prevIndex = segIndex - 1; + if (prevIndex < 0) { + prevIndex = ringSS.size() - 2; + } + return ringSS.getCoordinate( prevIndex ); + } + + /** + * Tests if two segments in a closed {@link SegmentString} are adjacent. + * This handles determining adjacency across the start/end of the ring. + * + * @param ringSS the segment string + * @param segIndex0 a segment index + * @param segIndex1 a segment index + * @return true if the segments are adjacent + */ + private static boolean isAdjacentInRing(SegmentString ringSS, int segIndex0, int segIndex1) { + int delta = Math.abs(segIndex1 - segIndex0); + if (delta <= 1) return true; + /** + * A string with N vertices has maximum segment index of N-2. + * If the delta is at least N-2, the segments must be + * at the start and end of the string and thus adjacent. + */ + if (delta >= ringSS.size() - 2) return true; + return false; + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 2d0f6e2c04..bd127739a6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -1,7 +1,5 @@ - - /* - * Copyright (c) 2016 Vivid Solutions. + * Copyright (c) 2021 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -13,13 +11,6 @@ */ package org.locationtech.jts.operation.valid; -import java.util.Iterator; -import java.util.Set; -import java.util.TreeSet; - -import org.locationtech.jts.algorithm.LineIntersector; -import org.locationtech.jts.algorithm.PointLocation; -import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; import org.locationtech.jts.algorithm.locate.PointOnGeometryLocator; import org.locationtech.jts.geom.Coordinate; @@ -32,11 +23,6 @@ import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geomgraph.Edge; -import org.locationtech.jts.geomgraph.EdgeIntersection; -import org.locationtech.jts.geomgraph.EdgeIntersectionList; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.util.Assert; /** * Implements the algorithms required to compute the isValid() method @@ -47,17 +33,20 @@ */ public class IsValidOp { - /** - * Tests whether a {@link Geometry} is valid. - * @param geom the Geometry to test - * @return true if the geometry is valid - */ - public static boolean isValid(Geometry geom) - { + private static final int MIN_SIZE_LINESTRING = 2; + private static final int MIN_SIZE_RING = 4; + + /** + * Tests whether a {@link Geometry} is valid. + * @param geom the Geometry to test + * @return true if the geometry is valid + */ + public static boolean isValid(Geometry geom) + { IsValidOp isValidOp = new IsValidOp(geom); return isValidOp.isValid(); - } - + } + /** * Checks whether a coordinate is valid for processing. * Coordinates are valid if their x and y ordinates are in the @@ -74,41 +63,27 @@ public static boolean isValid(Coordinate coord) if (Double.isInfinite(coord.y)) return false; return true; } + /** - * Find a point from the list of testCoords - * that is NOT a node in the edge for the list of searchCoords - * - * @return the point found, or null if none found + * The geometry being validated */ - public static Coordinate findPtNotNode( - Coordinate[] testCoords, - LinearRing searchRing, - GeometryGraph graph) - { - // find edge corresponding to searchRing. - Edge searchEdge = graph.findEdge(searchRing); - // find a point in the testCoords which is not a node of the searchRing - EdgeIntersectionList eiList = searchEdge.getEdgeIntersectionList(); - // somewhat inefficient - is there a better way? (Use a node map, for instance?) - for (int i = 0 ; i < testCoords.length; i++) { - Coordinate pt = testCoords[i]; - if (! eiList.isIntersection(pt)) - return pt; - } - return null; - } - - private Geometry parentGeometry; // the base Geometry to be validated + private Geometry inputGeometry; /** * If the following condition is TRUE JTS will validate inverted shells and exverted holes * (the ESRI SDE model) */ - private boolean isSelfTouchingRingFormingHoleValid = false; + private boolean isInvertedRingValid = false; + private TopologyValidationError validErr; - public IsValidOp(Geometry parentGeometry) + /** + * Creates a new validator for a geometry. + * + * @param inputGeometry the geometry to validate + */ + public IsValidOp(Geometry inputGeometry) { - this.parentGeometry = parentGeometry; + this.inputGeometry = inputGeometry; } /** @@ -124,32 +99,35 @@ public IsValidOp(Geometry parentGeometry) * The default (following the OGC SFS standard) * is that this condition is not valid (false). *

    - * This does not affect whether Self-Touching Rings - * disconnecting the polygon interior are considered valid - * (these are considered to be invalid under the SFS, and many other + * Self-Touching Rings which disconnect the + * the polygon interior are still considered to be invalid + * (these are invalid under the SFS, and many other * spatial models as well). - * This includes "bow-tie" shells, - * which self-touch at a single point causing the interior to - * be disconnected, - * and "C-shaped" holes which self-touch at a single point causing an island to be formed. + * This includes: + *

      + *
    • exverted ("bow-tie") shells which self-touch at a single point + *
    • inverted shells with the inversion touching the shell at another point + *
    • exverted holes with exversion touching the hole at another point + *
    • inverted ("C-shaped") holes which self-touch at a single point causing an island to be formed + *
    • inverted shells or exverted holes which form part of a chain of touching rings + * (which disconnect the interior) + *
    * * @param isValid states whether geometry with this condition is valid */ public void setSelfTouchingRingFormingHoleValid(boolean isValid) { - isSelfTouchingRingFormingHoleValid = isValid; + isInvertedRingValid = isValid; } /** - * Computes the validity of the geometry, - * and returns true if it is valid. + * Tests the validity of the input geometry. * * @return true if the geometry is valid */ public boolean isValid() { - checkValid(parentGeometry); - return validErr == null; + return isValidGeometry(inputGeometry); } /** @@ -162,359 +140,388 @@ public boolean isValid() */ public TopologyValidationError getValidationError() { - checkValid(parentGeometry); + isValidGeometry(inputGeometry); return validErr; } - - private void checkValid(Geometry g) + + private void logInvalid(int code, Coordinate pt) { + validErr = new TopologyValidationError(code, pt); + } + + private boolean hasInvalidError() { + return validErr != null; + + } + + private boolean isValidGeometry(Geometry g) { validErr = null; - // empty geometries are always valid! - if (g.isEmpty()) return; - - if (g instanceof Point) checkValid((Point) g); - else if (g instanceof MultiPoint) checkValid((MultiPoint) g); - // LineString also handles LinearRings - else if (g instanceof LinearRing) checkValid( (LinearRing) g); - else if (g instanceof LineString) checkValid( (LineString) g); - else if (g instanceof Polygon) checkValid( (Polygon) g); - else if (g instanceof MultiPolygon) checkValid( (MultiPolygon) g); - else if (g instanceof GeometryCollection) checkValid( (GeometryCollection) g); - else throw new UnsupportedOperationException(g.getClass().getName()); + // empty geometries are always valid + if (g.isEmpty()) return true; + + if (g instanceof Point) return isValid( (Point) g); + if (g instanceof MultiPoint) return isValid( (MultiPoint) g); + if (g instanceof LinearRing) return isValid( (LinearRing) g); + if (g instanceof LineString) return isValid( (LineString) g); + if (g instanceof Polygon) return isValid( (Polygon) g); + if (g instanceof MultiPolygon) return isValid( (MultiPolygon) g); + if (g instanceof GeometryCollection) return isValid( (GeometryCollection) g); + + // geometry type not known + throw new UnsupportedOperationException(g.getClass().getName()); } /** - * Checks validity of a Point. + * Tests validity of a Point. */ - private void checkValid(Point g) + private boolean isValid(Point g) { - checkInvalidCoordinates(g.getCoordinates()); + checkCoordinateInvalid(g.getCoordinates()); + if (hasInvalidError()) return false; + return true; } + /** - * Checks validity of a MultiPoint. + * Tests validity of a MultiPoint. */ - private void checkValid(MultiPoint g) + private boolean isValid(MultiPoint g) { - checkInvalidCoordinates(g.getCoordinates()); + checkCoordinateInvalid(g.getCoordinates()); + if (hasInvalidError()) return false; + return true; } /** - * Checks validity of a LineString. Almost anything goes for linestrings! + * Tests validity of a LineString. + * Almost anything goes for linestrings! */ - private void checkValid(LineString g) + private boolean isValid(LineString g) { - checkInvalidCoordinates(g.getCoordinates()); - if (validErr != null) return; - GeometryGraph graph = new GeometryGraph(0, g); - checkTooFewPoints(graph); + checkCoordinateInvalid(g.getCoordinates()); + if (hasInvalidError()) return false; + checkTooFewPoints(g, MIN_SIZE_LINESTRING); + if (hasInvalidError()) return false; + return true; } + /** - * Checks validity of a LinearRing. + * Tests validity of a LinearRing. */ - private void checkValid(LinearRing g) + private boolean isValid(LinearRing g) { - checkInvalidCoordinates(g.getCoordinates()); - if (validErr != null) return; - checkClosedRing(g); - if (validErr != null) return; - - GeometryGraph graph = new GeometryGraph(0, g); - checkTooFewPoints(graph); - if (validErr != null) return; + checkCoordinateInvalid(g.getCoordinates()); + if (hasInvalidError()) return false; - LineIntersector li = new RobustLineIntersector(); - graph.computeSelfNodes(li, true, true); - checkNoSelfIntersectingRings(graph); + checkRingNotClosed(g); + if (hasInvalidError()) return false; + + checkRingTooFewPoints(g); + if (hasInvalidError()) return false; + + checkSelfIntersectingRing(g); + return validErr == null; } /** - * Checks the validity of a polygon. + * Tests the validity of a polygon. * Sets the validErr flag. */ - private void checkValid(Polygon g) + private boolean isValid(Polygon g) { - checkInvalidCoordinates(g); - if (validErr != null) return; - checkClosedRings(g); - if (validErr != null) return; + checkCoordinateInvalid(g); + if (hasInvalidError()) return false; + + checkRingsNotClosed(g); + if (hasInvalidError()) return false; - GeometryGraph graph = new GeometryGraph(0, g); + checkRingsTooFewPoints(g); + if (hasInvalidError()) return false; - checkTooFewPoints(graph); - if (validErr != null) return; - checkConsistentArea(graph); - if (validErr != null) return; + AreaTopologyAnalyzer areaAnalyzer = new AreaTopologyAnalyzer(g, isInvertedRingValid); - if (! isSelfTouchingRingFormingHoleValid) { - checkNoSelfIntersectingRings(graph); - if (validErr != null) return; - } - checkHolesInShell(g, graph); - if (validErr != null) return; - //SLOWcheckHolesNotNested(g); - checkHolesNotNested(g, graph); - if (validErr != null) return; - checkConnectedInteriors(graph); + checkAreaIntersections(areaAnalyzer); + if (hasInvalidError()) return false; + + checkHolesOutsideShell(g); + if (hasInvalidError()) return false; + + checkHolesNotNested(g); + if (hasInvalidError()) return false; + + checkInteriorDisconnected(areaAnalyzer); + if (hasInvalidError()) return false; + + return true; } - private void checkValid(MultiPolygon g) + /** + * Tests validity of a MultiPolygon. + * + * @param g + * @return + */ + private boolean isValid(MultiPolygon g) { for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkInvalidCoordinates(p); - if (validErr != null) return; - checkClosedRings(p); - if (validErr != null) return; + checkCoordinateInvalid(p); + if (hasInvalidError()) return false; + + checkRingsNotClosed(p); + if (hasInvalidError()) return false; + checkRingsTooFewPoints(p); + if (hasInvalidError()) return false; } - GeometryGraph graph = new GeometryGraph(0, g); - - checkTooFewPoints(graph); - if (validErr != null) return; - checkConsistentArea(graph); - if (validErr != null) return; - if (! isSelfTouchingRingFormingHoleValid) { - checkNoSelfIntersectingRings(graph); - if (validErr != null) return; - } + AreaTopologyAnalyzer areaAnalyzer = new AreaTopologyAnalyzer(g, isInvertedRingValid); + + checkAreaIntersections(areaAnalyzer); + if (hasInvalidError()) return false; + for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkHolesInShell(p, graph); - if (validErr != null) return; + checkHolesOutsideShell(p); + if (hasInvalidError()) return false; } for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkHolesNotNested(p, graph); - if (validErr != null) return; + checkHolesNotNested(p); + if (hasInvalidError()) return false; } - checkShellsNotNested(g, graph); - if (validErr != null) return; - checkConnectedInteriors(graph); + checkShellsNotNested(g); + if (hasInvalidError()) return false; + + checkInteriorDisconnected(areaAnalyzer); + if (hasInvalidError()) return false; + + return true; } - private void checkValid(GeometryCollection gc) + /** + * Tests validity of a GeometryCollection. + * + * @param gc + * @return + */ + private boolean isValid(GeometryCollection gc) { for (int i = 0; i < gc.getNumGeometries(); i++) { - Geometry g = gc.getGeometryN(i); - checkValid(g); - if (validErr != null) return; + if (! isValidGeometry( gc.getGeometryN(i) )) + return false; } + return true; } - private void checkInvalidCoordinates(Coordinate[] coords) + private void checkCoordinateInvalid(Coordinate[] coords) { for (int i = 0; i < coords.length; i++) { if (! isValid(coords[i])) { - validErr = new TopologyValidationError( - TopologyValidationError.INVALID_COORDINATE, - coords[i]); + logInvalid(TopologyValidationError.INVALID_COORDINATE, coords[i]); return; } } } - private void checkInvalidCoordinates(Polygon poly) + private void checkCoordinateInvalid(Polygon poly) { - checkInvalidCoordinates(poly.getExteriorRing().getCoordinates()); - if (validErr != null) return; + checkCoordinateInvalid(poly.getExteriorRing().getCoordinates()); + if (hasInvalidError()) return; for (int i = 0; i < poly.getNumInteriorRing(); i++) { - checkInvalidCoordinates(poly.getInteriorRingN(i).getCoordinates()); - if (validErr != null) return; + checkCoordinateInvalid(poly.getInteriorRingN(i).getCoordinates()); + if (hasInvalidError()) return; } } - private void checkClosedRings(Polygon poly) + private void checkRingNotClosed(LinearRing ring) { - checkClosedRing(poly.getExteriorRing()); - if (validErr != null) return; - for (int i = 0; i < poly.getNumInteriorRing(); i++) { - checkClosedRing(poly.getInteriorRingN(i)); - if (validErr != null) return; + if (ring.isEmpty()) return; + if (! ring.isClosed() ) { + Coordinate pt = ring.getNumPoints() >= 1 ? ring.getCoordinateN(0) : null; + logInvalid( TopologyValidationError.RING_NOT_CLOSED, pt); + return; } } - - private void checkClosedRing(LinearRing ring) + + private void checkRingsNotClosed(Polygon poly) { - if (ring.isEmpty()) return; - if (! ring.isClosed() ) { - Coordinate pt = null; - if (ring.getNumPoints() >= 1) - pt = ring.getCoordinateN(0); - validErr = new TopologyValidationError( - TopologyValidationError.RING_NOT_CLOSED, - pt); + checkRingNotClosed(poly.getExteriorRing()); + if (hasInvalidError()) return; + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + checkRingNotClosed(poly.getInteriorRingN(i)); + if (hasInvalidError()) return; } } - private void checkTooFewPoints(GeometryGraph graph) + private void checkRingsTooFewPoints(Polygon poly) { - if (graph.hasTooFewPoints()) { - validErr = new TopologyValidationError( - TopologyValidationError.TOO_FEW_POINTS, - graph.getInvalidPoint()); - return; + checkRingTooFewPoints(poly.getExteriorRing()); + if (hasInvalidError()) return; + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + checkRingTooFewPoints(poly.getInteriorRingN(i)); + if (hasInvalidError()) return; } } + private void checkRingTooFewPoints(LinearRing ring) { + if (ring.isEmpty()) return; + checkTooFewPoints(ring, MIN_SIZE_RING); + } + /** - * Checks that the arrangement of edges in a polygonal geometry graph - * forms a consistent area. - * - * @param graph - * - * @see ConsistentAreaTester + * Check the number of non-repeated points is at least a given size. + * + * @param line + * @param minSize */ - private void checkConsistentArea(GeometryGraph graph) - { - ConsistentAreaTester cat = new ConsistentAreaTester(graph); - boolean isValidArea = cat.isNodeConsistentArea(); - if (! isValidArea) { - validErr = new TopologyValidationError( - TopologyValidationError.SELF_INTERSECTION, - cat.getInvalidPoint()); - return; - } - if (cat.hasDuplicateRings()) { - validErr = new TopologyValidationError( - TopologyValidationError.DUPLICATE_RINGS, - cat.getInvalidPoint()); + private void checkTooFewPoints(LineString line, int minSize) { + if (! isNonRepeatedSizeAtLeast(line, minSize) ) { + Coordinate pt = line.getNumPoints() >= 1 ? line.getCoordinateN(0) : null; + logInvalid(TopologyValidationError.TOO_FEW_POINTS, pt); } } /** - * Check that there is no ring which self-intersects (except of course at its endpoints). - * This is required by OGC topology rules (but not by other models - * such as ESRI SDE, which allow inverted shells and exverted holes). - * - * @param graph the topology graph of the geometry + * Test if the number of non-repeated points in a line + * is at least a given minimum size. + * + * @param line the line to test + * @param minSize the minimum line size + * @return true if the line has the required number of non-repeated points */ - private void checkNoSelfIntersectingRings(GeometryGraph graph) - { - for (Iterator i = graph.getEdgeIterator(); i.hasNext(); ) { - Edge e = (Edge) i.next(); - checkNoSelfIntersectingRing(e.getEdgeIntersectionList()); - if (validErr != null) - return; + private boolean isNonRepeatedSizeAtLeast(LineString line, int minSize) { + int numPts = 0; + Coordinate prevPt = null; + for (int i = 0; i < line.getNumPoints(); i++) { + if (numPts >= minSize) return true; + Coordinate pt = line.getCoordinateN(i); + if (prevPt == null || ! pt.equals2D(prevPt)) + numPts++; + prevPt = pt; } + return numPts >= minSize; + } + + private void checkAreaIntersections(AreaTopologyAnalyzer areaAnalyzer) { + if (areaAnalyzer.hasIntersection()) { + logInvalid(TopologyValidationError.SELF_INTERSECTION, + areaAnalyzer.getIntersectionLocation()); + return; + } + if (areaAnalyzer.hasDoubleTouch()) { + logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, + areaAnalyzer.getIntersectionLocation()); + return; + } + if (areaAnalyzer.isInteriorDisconnectedBySelfTouch()) { + logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, + areaAnalyzer.getDisconnectionLocation()); + return; + } + } /** - * Check that a ring does not self-intersect, except at its endpoints. - * Algorithm is to count the number of times each node along edge occurs. - * If any occur more than once, that must be a self-intersection. + * Check whether a ring self-intersects (except at its endpoints). + * + * @param ring the linear ring to check */ - private void checkNoSelfIntersectingRing(EdgeIntersectionList eiList) + private void checkSelfIntersectingRing(LinearRing ring) { - Set nodeSet = new TreeSet(); - for (Iterator i = eiList.iterator(); i.hasNext(); ) { - EdgeIntersection ei = (EdgeIntersection) i.next(); - /** - * Do not count start point, so start/end node is not counted as a self-intersection. - * Another segment with a node in same location will still trigger an invalid error. - * (Note that the edgeIntersectionList may not contain the start/end node, - * due to noding short-circuiting.) - */ - if (isStartNode(ei)) { - continue; - } - if (nodeSet.contains(ei.coord)) { - validErr = new TopologyValidationError( - TopologyValidationError.RING_SELF_INTERSECTION, - ei.coord); - return; - } - else { - nodeSet.add(ei.coord); - } + Coordinate intPt = AreaTopologyAnalyzer.findSelfIntersection(ring); + if (intPt != null) { + logInvalid(TopologyValidationError.RING_SELF_INTERSECTION, + intPt); } } - - private static boolean isStartNode(EdgeIntersection ei) { - return ei.getSegmentIndex() == 0 && ei.getDistance() == 0.0; - } - + /** * Tests that each hole is inside the polygon shell. * This routine assumes that the holes have previously been tested * to ensure that all vertices lie on the shell or on the same side of it * (i.e. that the hole rings do not cross the shell ring). - * In other words, this test is only correct if the ConsistentArea test is passed first. * Given this, a simple point-in-polygon test of a single point in the hole can be used, * provided the point is chosen such that it does not lie on the shell. * - * @param p the polygon to be tested for hole inclusion - * @param graph a GeometryGraph incorporating the polygon + * @param poly the polygon to be tested for hole inclusion */ - private void checkHolesInShell(Polygon p, GeometryGraph graph) + private void checkHolesOutsideShell(Polygon poly) { // skip test if no holes are present - if (p.getNumInteriorRing() <= 0) return; + if (poly.getNumInteriorRing() <= 0) return; - LinearRing shell = p.getExteriorRing(); + LinearRing shell = poly.getExteriorRing(); boolean isShellEmpty = shell.isEmpty(); - //PointInRing pir = new SimplePointInRing(shell); // testing only PointOnGeometryLocator pir = new IndexedPointInAreaLocator(shell); - for (int i = 0; i < p.getNumInteriorRing(); i++) { - - LinearRing hole = p.getInteriorRingN(i); - Coordinate holePt = null; + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + LinearRing hole = poly.getInteriorRingN(i); if (hole.isEmpty()) continue; - holePt = findPtNotNode(hole.getCoordinates(), shell, graph); - /** - * If no non-node hole vertex can be found, the hole must - * split the polygon into disconnected interiors. - * This will be caught by a subsequent check. - */ - if (holePt == null) return; - - boolean outside = isShellEmpty || (Location.EXTERIOR == pir.locate(holePt)); - if ( outside ) { - validErr = new TopologyValidationError( - TopologyValidationError.HOLE_OUTSIDE_SHELL, - holePt); + + Coordinate invalidPt = null; + if (isShellEmpty) { + invalidPt = hole.getCoordinate(); + } + else { + invalidPt = findHoleOutsideShellPoint(pir, hole); + } + if (invalidPt != null) { + logInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, + invalidPt); return; } } } /** - * Tests that no hole is nested inside another hole. - * This routine assumes that the holes are disjoint. - * To ensure this, holes have previously been tested - * to ensure that: - *
      - *
    • they do not partially overlap - * (checked by checkRelateConsistency) - *
    • they are not identical - * (checked by checkRelateConsistency) - *
    + * Checks if a polygon hole lies inside its shell + * and if not returns the point indicating this. + * The hole is known to be wholly inside or outside the shell, + * so it suffices to find a single point which is interior or exterior. + * A valid hole may only have a single point touching the shell + * (since otherwise it creates a disconnected interior). + * So there should be at least one point which is interior or exterior, + * and this should be the first or second point tested. + * + * @param shellLocator + * @param hole + * @return a hole point outside the shell, or null if valid + */ + private Coordinate findHoleOutsideShellPoint(PointOnGeometryLocator shellLocator, LinearRing hole) { + for (int i = 0; i < hole.getNumPoints() - 1; i++) { + Coordinate holePt = hole.getCoordinateN(i); + int loc = shellLocator.locate(holePt); + if (loc== Location.BOUNDARY) continue; + if (loc== Location.INTERIOR) return null; + /** + * Location is EXTERIOR, so hole is outside shell + */ + return holePt; + } + return null; + } + + /** + * Tests if any polygon hole is nested inside another. + * Assumes that holes do not cross (overlap), + * This is checked earlier. + * + * @param poly the polygon with holes to test */ - private void checkHolesNotNested(Polygon p, GeometryGraph graph) + private void checkHolesNotNested(Polygon poly) { // skip test if no holes are present - if (p.getNumInteriorRing() <= 0) return; + if (poly.getNumInteriorRing() <= 0) return; - IndexedNestedRingTester nestedTester = new IndexedNestedRingTester(graph); - //SimpleNestedRingTester nestedTester = new SimpleNestedRingTester(arg[0]); - //SweeplineNestedRingTester nestedTester = new SweeplineNestedRingTester(arg[0]); - - for (int i = 0; i < p.getNumInteriorRing(); i++) { - LinearRing innerHole = p.getInteriorRingN(i); - if (innerHole.isEmpty()) continue; - nestedTester.add(innerHole); - } - boolean isNonNested = nestedTester.isNonNested(); - if ( ! isNonNested ) { - validErr = new TopologyValidationError( - TopologyValidationError.NESTED_HOLES, + IndexedNestedHoleTester nestedTester = new IndexedNestedHoleTester(poly); + if ( nestedTester.isNested() ) { + logInvalid(TopologyValidationError.NESTED_HOLES, nestedTester.getNestedPoint()); } } /** - * Tests that no element polygon is wholly in the interior of another element polygon. + * Tests that no element polygon is in the interior of another element polygon. *

    * Preconditions: *

      @@ -522,114 +529,74 @@ private void checkHolesNotNested(Polygon p, GeometryGraph graph) *
    • shells do not touch along an edge *
    • no duplicate rings exist *
    - * This routine relies on the fact that while polygon shells may touch at one or - * more vertices, they cannot touch at ALL vertices. + * These have been confirmed by the {@link AreaTopologyAnalyzer}. */ - private void checkShellsNotNested(MultiPolygon mp, GeometryGraph graph) + private void checkShellsNotNested(MultiPolygon mp) { for (int i = 0; i < mp.getNumGeometries(); i++) { Polygon p = (Polygon) mp.getGeometryN(i); + if (p.isEmpty()) + continue; LinearRing shell = p.getExteriorRing(); for (int j = 0; j < mp.getNumGeometries(); j++) { if (i == j) continue; Polygon p2 = (Polygon) mp.getGeometryN(j); - checkShellNotNested(shell, p2, graph); - if (validErr != null) return; + Coordinate invalidPt = findShellSegmentInPolygon(shell, p2); + if (invalidPt != null) { + logInvalid(TopologyValidationError.NESTED_SHELLS, + invalidPt); + return; + } } } } /** - * Check if a shell is incorrectly nested within a polygon. This is the case - * if the shell is inside the polygon shell, but not inside a polygon hole. - * (If the shell is inside a polygon hole, the nesting is valid.) - *

    - * The algorithm used relies on the fact that the rings must be properly contained. - * E.g. they cannot partially overlap (this has been previously checked by - * checkRelateConsistency ) + * Finds a point of a shell segment which lies inside a polygon, if any. + * The shell is assume to touch the polyon only at shell vertices, + * and does not cross the polygon. + * + * @param the shell to test + * @param the polygon to test against + * @return an interior segment point, or null if the shell is nested correctly */ - private void checkShellNotNested(LinearRing shell, Polygon p, GeometryGraph graph) + private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) { - Coordinate[] shellPts = shell.getCoordinates(); - // test if shell is inside polygon shell - LinearRing polyShell = p.getExteriorRing(); - if (polyShell.isEmpty()) return; - Coordinate[] polyPts = polyShell.getCoordinates(); - Coordinate shellPt = findPtNotNode(shellPts, polyShell, graph); - // if no point could be found, we can assume that the shell is outside the polygon - if (shellPt == null) - return; - boolean insidePolyShell = PointLocation.isInRing(shellPt, polyPts); - if (! insidePolyShell) return; - - // if no holes, this is an error! - if (p.getNumInteriorRing() <= 0) { - validErr = new TopologyValidationError( - TopologyValidationError.NESTED_SHELLS, - shellPt); - return; - } + LinearRing polyShell = poly.getExteriorRing(); + if (polyShell.isEmpty()) return null; + + //--- if envelope is not covered --> not nested + if (! poly.getEnvelopeInternal().covers(shell.getEnvelopeInternal())) + return null; + + Coordinate shell0 = shell.getCoordinateN(0); + Coordinate shell1 = shell.getCoordinateN(1); + + if (! AreaTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) + return null; /** - * Check if the shell is inside one of the holes. - * This is the case if one of the calls to checkShellInsideHole - * returns a null coordinate. - * Otherwise, the shell is not properly contained in a hole, which is an error. + * Check if the shell is inside a hole (if there are any). + * If so this is valid. */ - Coordinate badNestedPt = null; - for (int i = 0; i < p.getNumInteriorRing(); i++) { - LinearRing hole = p.getInteriorRingN(i); - badNestedPt = checkShellInsideHole(shell, hole, graph); - if (badNestedPt == null) - return; - } - validErr = new TopologyValidationError( - TopologyValidationError.NESTED_SHELLS, - badNestedPt); - } - - /** - * This routine checks to see if a shell is properly contained in a hole. - * It assumes that the edges of the shell and hole do not - * properly intersect. - * - * @return null if the shell is properly contained, or - * a Coordinate which is not inside the hole if it is not - * - */ - private Coordinate checkShellInsideHole(LinearRing shell, LinearRing hole, GeometryGraph graph) - { - Coordinate[] shellPts = shell.getCoordinates(); - Coordinate[] holePts = hole.getCoordinates(); - // TODO: improve performance of this - by sorting pointlists for instance? - Coordinate shellPt = findPtNotNode(shellPts, hole, graph); - // if point is on shell but not hole, check that the shell is inside the hole - if (shellPt != null) { - boolean insideHole = PointLocation.isInRing(shellPt, holePts); - if (! insideHole) { - return shellPt; + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + LinearRing hole = poly.getInteriorRingN(i); + if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) + && AreaTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { + return null; } } - Coordinate holePt = findPtNotNode(holePts, shell, graph); - // if point is on hole but not shell, check that the hole is outside the shell - if (holePt != null) { - boolean insideShell = PointLocation.isInRing(holePt, shellPts); - if (insideShell) { - return holePt; - } - return null; - } - Assert.shouldNeverReachHere("points in shell and hole appear to be equal"); - return null; - } - - private void checkConnectedInteriors(GeometryGraph graph) - { - ConnectedInteriorTester cit = new ConnectedInteriorTester(graph); - if (! cit.isInteriorsConnected()) - validErr = new TopologyValidationError( - TopologyValidationError.DISCONNECTED_INTERIOR, - cit.getCoordinate()); + + /** + * The shell is contained in the polygon, but is not contained in a hole. + * This is invalid. + */ + return shell0; + } + + private void checkInteriorDisconnected(AreaTopologyAnalyzer areaAnalyzer) { + if (areaAnalyzer.isInteriorDisconnectedByRingCycle()) + logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, + areaAnalyzer.getDisconnectionLocation()); } - } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java new file mode 100644 index 0000000000..636951666b --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.LinearRing; + +/** + * A ring of a polygon being analyzed for topological validity. + * The shell and hole rings of valid polygons touch only at discrete points. + * The "touch" relationship induces a graph over the set of rings. + * The interior of a valid polygon must be connected. + * This is the case if there is no "chain" of touching rings + * (which would partition off part of the interior). + * This is equivalent to the touch graph having no cycles. + *

    + * Also, in a valid polygon two rings can touch only at a single location, + * since otherwise they disconnect a portion of the interior between them. + * This is checked as the touches relation is built + * (so the touch relation representation for a polygon ring does not need to support + * more than one touch location for each adjacent ring). + *

    + * Thus the touch graph of a valid polygon is a forest - a set of disjoint trees. + *

    + * The cycle detection algorithm works for polygon rings which also contain self-touches + * (inverted shells and exverted holes). + *

    + * Polygons with no holes do not need to be checked for + * a connected interior (unless self-touches are allowed). + *

    + * The class also records the topology at self-touch nodes, + * to support checking if an invalid self-touch disconnects the polygon. + * + * @author mdavis + * + */ +class PolygonRing { + + /** + * Tests if a polygon ring represents a shell. + * + * @param polyRing the ring to test (may be null) + * @return true if the ring represents a shell + */ + public static boolean isShell(PolygonRing polyRing) { + if (polyRing == null) return true; + return polyRing.isShell(); + } + + /** + * Records a touch location between two rings, + * and checks if the rings already touch in a different location. + * + * @param ring0 a polygon ring + * @param ring1 a polygon ring + * @param pt the location where they touch + * @return true if the polygons already touch + */ + public static boolean addTouch(PolygonRing ring0, PolygonRing ring1, Coordinate pt) { + //--- skip if either polygon does not have holes + if (ring0 == null || ring1 == null) + return false; + + //--- only record touches within a polygon + if (! ring0.isSamePolygon(ring1)) return false; + + if (! ring0.isOnlyTouch(ring1, pt)) return true; + if (! ring1.isOnlyTouch(ring0, pt)) return true; + + ring0.addTouch(ring1, pt); + ring1.addTouch(ring0, pt); + return false; + } + + /** + * Finds a location (if any) where a chain of rings forms a cycle + * in the ring touch graph. + * This indicates that a set of holes disconnects the interior of a polygon. + * + * @param polyRings the list of rings to check + * @return a vertex contained in a ring cycle, or null if none is found + */ + public static Coordinate findTouchCycleLocation(List polyRings) { + for (PolygonRing polyRing : polyRings) { + if (! polyRing.isInTouchSet()) { + Coordinate touchCycleLoc = polyRing.findTouchCycleLocation(); + if (touchCycleLoc != null) return touchCycleLoc; + } + } + return null; + } + + /** + * Finds a location of an interior self-touch in a list of rings, + * if one exists. + * This indicates that a self-touch disconnects the interior of a polygon, + * which is invalid. + * + * @param polyRings the list of rings to check + * @return the location of an interior self-touch node, or null if there are none + */ + public static Coordinate findInteriorSelfNode(List polyRings) { + for (PolygonRing polyRing : polyRings) { + Coordinate interiorSelfNode = polyRing.findInteriorSelfNode(); + if (interiorSelfNode != null) { + return interiorSelfNode; + } + } + return null; + } + + private int id; + private PolygonRing shell; + private LinearRing ring; + + /** + * The root of the touch graph tree containing this ring. + * Serves as the id for the graph partition induced by the touch relation. + */ + private PolygonRing touchSetRoot = null; + + /** + * The parent of this ring in the touch tree graph. + */ + private PolygonRing touchTreeParent = null; + + // lazily created + /** + * The set of PolygonRingTouch links + * for this ring. + * The set of all touches in the rings of a polygon + * forms the polygon touch graph. + * This supports detecting touch cycles, which + * reveal the condition of a disconnected interior. + *

    + * Only a single touch is recorded between any two rings, + * since more than one touch between two rings + * indicates interior disconnection as well. + */ + private Map touches = null; + + /** + * The set of self-nodes in this ring. + * This supports checking valid ring self-touch topology. + */ + private ArrayList selfNodes = null; + + /** + * Creates a ring for a polygon shell. + * @param ring + */ + public PolygonRing(LinearRing ring) { + this.ring = ring; + id = -1; + shell = this; + } + + /** + * Creates a ring for a polygon hole. + * @param ring the ring geometry + * @param index the index of the hole + * @param shell the parent polygon shell + */ + public PolygonRing(LinearRing ring, int index, PolygonRing shell) { + this.ring = ring; + this.id = index; + this.shell = shell; + } + + public boolean isSamePolygon(PolygonRing ring) { + return shell == ring.shell; + } + + public boolean isShell() { + return shell == this; + } + + private boolean isInTouchSet() { + return touchSetRoot != null; + } + + private void setTouchSetRoot(PolygonRing ring) { + touchSetRoot = ring; + } + + private PolygonRing getTouchSetRoot() { + return touchSetRoot; + } + + private void setParent(PolygonRing ring) { + touchTreeParent = ring; + } + + private boolean isChildOf(PolygonRing ring) { + return touchTreeParent == ring; + } + + private boolean hasTouches() { + return touches != null && ! touches.isEmpty(); + } + + private Collection getTouches() { + return touches.values(); + } + + private void addTouch(PolygonRing ring, Coordinate pt) { + if (touches == null) { + touches = new HashMap(); + } + PolygonRingTouch touch = touches.get(ring.id); + if (touch == null) { + touches.put(ring.id, new PolygonRingTouch(ring, pt)); + }; + } + + public void addSelfTouch(Coordinate origin, Coordinate e00, Coordinate e01, Coordinate e10, Coordinate e11) { + if (selfNodes == null) { + selfNodes = new ArrayList(); + } + selfNodes.add(new PolygonRingSelfNode(origin, e00, e01, e10, e11)); + } + + /** + * Tests if this ring touches a given ring at + * the single point specified. + * + * @param ring the other PolygonRing + * @param pt the touch point + * @return true if the rings touch only at the given point + */ + private boolean isOnlyTouch(PolygonRing ring, Coordinate pt) { + //--- no touches for this ring + if (touches == null) return true; + //--- no touches for other ring + PolygonRingTouch touch = touches.get(ring.id); + if (touch == null) return true; + //--- the rings touch - check if point is the same + return touch.isAtLocation(pt); + } + + /** + * Detects whether the subgraph of rings linked by touch to this ring + * contains a touch cycle. + * If no cycles are detected, the subgraph of touching rings is a tree. + * The subgraph is marked using this ring as the root. + * + * @return a vertex in a ring cycle, or null if no cycle found + */ + private Coordinate findTouchCycleLocation() { + //--- the touch set including this ring is already processed + if (isInTouchSet()) return null; + + //--- scan the touch set tree rooted at this ring + // Assert: this.touchSetRoot is null + PolygonRing root = this; + root.setParent(root); + root.setTouchSetRoot(root); + + Deque ringStack = new ArrayDeque(); + ringStack.add(root); + + while (! ringStack.isEmpty()) { + PolygonRing ring = ringStack.removeFirst(); + Coordinate touchCyclePt = scanForTouchCycle(root, ring, ringStack); + if (touchCyclePt != null) { + return touchCyclePt; + } + } + return null; + } + + /** + * Scans the rings touching a given ring, + * and checks if they are already part of its ring subgraph set. + * If so, a ring cycle has been detected. + * Otherwise, each touched ring is added to the current subgraph set, + * and queued to be scanned in turn. + * + * @param root the root of the touch subgraph + * @param ring the ring being processed + * @param ringStack the stack of rings to scan + * @return a vertex in a ring cycle if found, or null + */ + private Coordinate scanForTouchCycle(PolygonRing root, PolygonRing ring, Deque ringStack) { + if (! ring.hasTouches()) + return null; + + //-- check the touched rings + //--- either they form a touch cycle, or they are pushed on stack for processing + for (PolygonRingTouch touch : ring.getTouches()) { + PolygonRing touchRing = touch.getRing(); + /** + * There is always a link back to the touch-tree parent of the ring, + * so don't include it. + * (I.e. the ring touches the parent ring which originally + * added this ring to the stack) + */ + if (ring.isChildOf(touchRing)) + continue; + + /** + * Test if the touched ring has already been + * reached via a different path in the tree. + * This indicates a touching ring cycle has been found. + * This is invalid. + */ + if (touchRing.getTouchSetRoot() == root) + return touch.getCoordinate(); + + touchRing.setParent(ring); + touchRing.setTouchSetRoot(root); + ringStack.add(touchRing); + } + return null; + } + + /** + * Finds the location of an invalid interior self-touch in this ring, + * if one exists. + * + * @return the location of an interior self-touch node, or null if there are none + */ + public Coordinate findInteriorSelfNode() { + if (selfNodes == null) return null; + + /** + * Determine if the ring interior is on the Right. + * This is the case if the ring is a shell and is CW, + * or is a hole and is CCW. + */ + boolean isCCW = Orientation.isCCW(ring.getCoordinates()); + boolean isInteriorOnRight = isShell() ^ isCCW; + + for (PolygonRingSelfNode selfNode : selfNodes) { + if (! selfNode.isExterior( isInteriorOnRight) ) { + return selfNode.getCoordinate(); + } + } + return null; + } + + public String toString() { + return ring.toString(); + } + + } + +/** + * Records a point where a {@link PolygonRing} touches another one. + * This forms an edge in the induced ring touch graph. + * + * @author mdavis + * + */ +class PolygonRingTouch { + private PolygonRing ring; + private Coordinate touchPt; + + public PolygonRingTouch(PolygonRing ring, Coordinate pt) { + this.ring = ring; + this.touchPt = pt; + } + + public Coordinate getCoordinate() { + return touchPt; + } + + public PolygonRing getRing() { + return ring; + } + + public boolean isAtLocation(Coordinate pt) { + return touchPt.equals2D(pt); + } +} + +/** + * Represents a ring self-touch node, recording the node (intersection point) + * and the endpoints of the four adjacent segments. + *

    + * This is used to evaluate validity of self-touching nodes, + * when they are allowed. + * + * @author mdavis + * + */ +class PolygonRingSelfNode { + private Coordinate nodePt; + private Coordinate e00; + private Coordinate e01; + private Coordinate e10; + //private Coordinate e11; + + public PolygonRingSelfNode(Coordinate nodePt, + Coordinate e00, Coordinate e01, + Coordinate e10, Coordinate e11) { + this.nodePt = nodePt; + this.e00 = e00; + this.e01 = e01; + this.e10 = e10; + //this.e11 = e11; + } + + /** + * The node point. + * + * @return + */ + public Coordinate getCoordinate() { + return nodePt; + } + + /** + * Tests if a self-touch has the segments of each half of the touch + * lying in the exterior of a polygon. + * This is a valid self-touch. + * It applies to both shells and holes. + * Only one of the four possible cases needs to be tested, + * since the situation has full symmetry. + * + * @param isInteriorOnRight whether the interior is to the right of the parent ring + * @return true if the self-touch is in the exterior + */ + public boolean isExterior(boolean isInteriorOnRight) { + /** + * Note that either corner and either of the other edges could be used to test. + * The situation is fully symmetrical. + */ + boolean isInteriorSeg = AreaNode.isInteriorSegment(nodePt, e00, e01, e10); + boolean isExterior = isInteriorOnRight ? ! isInteriorSeg : isInteriorSeg; + return isExterior; + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html index 1db511694a..2395d66463 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html @@ -7,7 +7,8 @@ -Provides classes for testing the validity of geometries. +Classes for testing the validity and simplicity of geometries, +as defined in the OGC Simple Features specification. diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java new file mode 100644 index 0000000000..f2045db5fe --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java @@ -0,0 +1,48 @@ +package org.locationtech.jts.operation.valid; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.LineString; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class AreaNodeTest extends GeometryTestCase { + + public static void main(String args[]) { + TestRunner.run(AreaNodeTest.class); + } + + public AreaNodeTest(String name) { super(name); } + + public void testCrossing() { + checkValid("LINESTRING (500 1000, 1000 1000, 1000 1500)", + "LINESTRING (1000 500, 1000 1000, 500 1500)", false); + } + + public void testValidQuadrant2() { + checkValid("LINESTRING (500 1000, 1000 1000, 1000 1500)", + "LINESTRING (300 1200, 1000 1000, 500 1500)"); + } + + public void testValidQuadrant4() { + checkValid("LINESTRING (500 1000, 1000 1000, 1000 1500)", + "LINESTRING (1000 500, 1000 1000, 1500 1000)"); + } + + private void checkValid(String wktA, String wktB) { + checkValid(wktA, wktB, true); + } + + private void checkValid(String wktA, String wktB, boolean isExpectedValid) { + Coordinate[] a = readPts(wktA); + Coordinate[] b = readPts(wktB); + // assert: a[1] = b[1] + boolean isValid = ! AreaNode.isCrossing(a[1], a[0], a[2], b[0], b[2]); + assertTrue(isValid == isExpectedValid); + } + + private Coordinate[] readPts(String wkt) { + LineString line = (LineString) read(wkt); + return line.getCoordinates(); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index 4d39010a3b..6cc23acdfa 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -17,15 +17,13 @@ import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.io.WKTReader; -import junit.framework.TestCase; import junit.textui.TestRunner; - - +import test.jts.GeometryTestCase; /** * @version 1.7 */ -public class IsValidTest extends TestCase { +public class IsValidTest extends GeometryTestCase { private PrecisionModel precisionModel = new PrecisionModel(); private GeometryFactory geometryFactory = new GeometryFactory(precisionModel, 0); @@ -59,6 +57,56 @@ public void testZeroAreaPolygon() throws Exception { assertTrue(true); //No exception thrown [Jon Aquino] } + public void testValidSimplePolygon() throws Exception { + checkValid( + "POLYGON ((10 89, 90 89, 90 10, 10 10, 10 89))"); + } + + public void testInvalidSimplePolygonRingSelfIntersection() throws Exception { + checkInvalid( + "POLYGON ((10 90, 90 10, 90 90, 10 10, 10 90))"); + } + + public void testSimplePolygonHole() throws Exception { + checkValid( + "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (60 20, 20 70, 90 90, 60 20))"); + } + + public void testPolygonTouchingHoleAtVertex() throws Exception { + checkValid( + "POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 260, 140 240, 140 180))"); + } + + public void testInvalidPolygonHoleProperIntersection() throws Exception { + checkInvalid( + "POLYGON ((10 90, 50 50, 10 10, 10 90), (20 50, 60 70, 60 30, 20 50))"); + } + + public void testInvalidPolygonDisconnectedInterior() throws Exception { + checkInvalid( + "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 30 80, 20 20, 20 80), (80 30, 20 20, 80 20, 80 30), (80 80, 30 80, 80 30, 80 80))"); + } + + public void testValidMultiPolygonTouchAtVertices() throws Exception { + checkValid( + "MULTIPOLYGON (((10 10, 10 90, 90 90, 90 10, 80 80, 50 20, 20 80, 10 10)), ((90 10, 10 10, 50 20, 90 10)))"); + } + + public void testValidMultiPolygonTouchAtVerticesSegments() throws Exception { + checkValid( + "MULTIPOLYGON (((60 40, 90 10, 90 90, 10 90, 10 10, 40 40, 60 40)), ((50 40, 20 20, 80 20, 50 40)))"); + } + + public void testInvalidMultiPolygonTouchVertices() throws Exception { + checkInvalid( + "MULTIPOLYGON (((10 10, 20 30, 10 90, 90 90, 80 30, 90 10, 50 20, 10 10)), ((80 30, 20 30, 50 20, 80 30)))"); + } + + public void testValidMultiPolygonHoleTouchVertices() throws Exception { + checkValid( + "MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200)))"); + } + public void testLineString() throws Exception { Geometry g = reader.read( "LINESTRING(0 0, 0 0)"); @@ -84,4 +132,20 @@ public void testLinearRingSelfCrossing2() throws Exception { assertTrue(! g.isValid()); } + //============================================= + + private void checkValid(String wkt) { + checkValid(true, wkt); + } + + private void checkInvalid(String wkt) { + checkValid(false, wkt); + } + + private void checkValid(boolean isExpectedValid, String wkt) { + Geometry geom = read(wkt); + boolean isValid = geom.isValid(); + assertEquals( isExpectedValid, isValid ); + } + } diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/SimpleNestedRingTester.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/SimpleNestedRingTester.java deleted file mode 100644 index d24876f85b..0000000000 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/SimpleNestedRingTester.java +++ /dev/null @@ -1,80 +0,0 @@ - -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.operation.valid; - -import java.util.ArrayList; -import java.util.List; - -import org.locationtech.jts.algorithm.PointLocation; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.util.Assert; - -/** - * Tests whether any of a set of {@link LinearRing}s are - * nested inside another ring in the set, using a simple O(n^2) - * comparison. - * - * @version 1.7 - */ -public class SimpleNestedRingTester -{ - - private GeometryGraph graph; // used to find non-node vertices - private List rings = new ArrayList(); - private Coordinate nestedPt; - - public SimpleNestedRingTester(GeometryGraph graph) - { - this.graph = graph; - } - - public void add(LinearRing ring) - { - rings.add(ring); - } - - public Coordinate getNestedPoint() { return nestedPt; } - - public boolean isNonNested() - { - for (int i = 0; i < rings.size(); i++) { - LinearRing innerRing = (LinearRing) rings.get(i); - Coordinate[] innerRingPts = innerRing.getCoordinates(); - - for (int j = 0; j < rings.size(); j++) { - LinearRing searchRing = (LinearRing) rings.get(j); - Coordinate[] searchRingPts = searchRing.getCoordinates(); - - if (innerRing == searchRing) - continue; - - if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) - continue; - - Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph); - Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); - //Coordinate innerRingPt = innerRingPts[0]; - - boolean isInside = PointLocation.isInRing(innerRingPt, searchRingPts); - if (isInside) { - nestedPt = innerRingPt; - return false; - } - } - } - return true; - } - -} diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingFormingHoleTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java similarity index 66% rename from modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingFormingHoleTest.java rename to modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java index 1f774b941d..4e38c8eb47 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingFormingHoleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java @@ -13,14 +13,12 @@ package org.locationtech.jts.operation.valid; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.io.WKTReader; - -import junit.framework.TestCase; +import test.jts.GeometryTestCase; /** - * Tests allowing IsValidOp to validate polygons with - * Self-Touching Rings forming holes. + * Tests that IsValidOp validates polygons with + * Self-Touching Rings (inverted shells or exverted holes). * Mainly tests that configuring {@link IsValidOp} to allow validating * the STR validates polygons with this condition, and does not validate * polygons with other kinds of self-intersection (such as ones with Disconnected Interiors). @@ -30,17 +28,15 @@ * @author Martin Davis * @version 1.7 */ -public class ValidSelfTouchingRingFormingHoleTest - extends TestCase +public class ValidSelfTouchingRingTest + extends GeometryTestCase { - private static WKTReader rdr = new WKTReader(); - - public ValidSelfTouchingRingFormingHoleTest(String name) { + public ValidSelfTouchingRingTest(String name) { super(name); } public static void main(String[] args) { - junit.textui.TestRunner.run(ValidSelfTouchingRingFormingHoleTest.class); + junit.textui.TestRunner.run(ValidSelfTouchingRingTest.class); } /** @@ -51,9 +47,30 @@ public void testShellAndHoleSelfTouch() { String wkt = "POLYGON ((0 0, 0 340, 320 340, 320 0, 120 0, 180 100, 60 100, 120 0, 0 0), (80 300, 80 180, 200 180, 200 240, 280 200, 280 280, 200 240, 200 300, 80 300))"; checkIsValidSTR(wkt, true); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } + public void testShellTouchAtHole() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 50 50, 80 50, 80 80, 10 10, 10 90), (40 80, 20 60, 50 50, 40 80))"; + checkIsValidSTR(wkt, true); + checkIsValidOGC(wkt, false); + } + + public void testShellTouchInChain() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90, 20 70, 30 70, 30 50, 40 50, 40 70, 30 70, 30 80, 10 90))"; + checkIsValidSTR(wkt, true); + checkIsValidOGC(wkt, false); + } + + public void testHoleTouchInChain() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 20, 80 20, 80 50, 70 20, 70 50, 60 20, 60 50, 50 20, 50 50, 40 20, 40 50, 30 20, 30 50, 20 20))"; + checkIsValidSTR(wkt, true); + checkIsValidOGC(wkt, false); + } + /** * Tests a geometry representing the same area as in {@link #testShellAndHoleSelfTouch} * but using a shell-hole touch and a hole-hole touch. @@ -63,7 +80,7 @@ public void testShellHoleAndHoleHoleTouch() { String wkt = "POLYGON ((0 0, 0 340, 320 340, 320 0, 120 0, 0 0), (120 0, 180 100, 60 100, 120 0), (80 300, 80 180, 200 180, 200 240, 200 300, 80 300), (200 240, 280 200, 280 280, 200 240))"; checkIsValidSTR(wkt, true); - checkIsValidDefault(wkt, true); + checkIsValidOGC(wkt, true); } /** @@ -74,7 +91,7 @@ public void testShellSelfTouchHoleOverlappingHole() { String wkt = "POLYGON ((0 0, 220 0, 220 200, 120 200, 140 100, 80 100, 120 200, 0 200, 0 0), (200 80, 20 80, 120 200, 200 80))"; checkIsValidSTR(wkt, false); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } /** @@ -84,34 +101,45 @@ public void testDisconnectedInteriorShellSelfTouchAtNonVertex() { String wkt = "POLYGON ((40 180, 40 60, 240 60, 240 180, 140 60, 40 180))"; checkIsValidSTR(wkt, false); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } - /** - * Ensure that the Disconnected Interior condition is not validated - */ public void testDisconnectedInteriorShellSelfTouchAtVertex() { String wkt = "POLYGON ((20 20, 20 100, 140 100, 140 180, 260 180, 260 100, 140 100, 140 20, 20 20))"; checkIsValidSTR(wkt, false); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } + public void testDisconnectedInteriorShellTouchAtVertices() + { + String wkt = "POLYGON ((10 10, 90 10, 50 50, 80 70, 90 10, 90 90, 10 90, 10 10, 50 50, 20 70, 10 10))"; + checkIsValidSTR(wkt, false); + checkIsValidOGC(wkt, false); + } + + public void testDisconnectedInteriorHoleTouch() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 20, 20 80, 80 80, 80 30, 30 30, 70 40, 70 70, 20 20))"; + checkIsValidSTR(wkt, false); + checkIsValidOGC(wkt, false); + } + public void testShellCross() { String wkt = "POLYGON ((20 20, 120 20, 120 220, 240 220, 240 120, 20 120, 20 20))"; checkIsValidSTR(wkt, false); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } public void testShellCrossAndSTR() { String wkt = "POLYGON ((20 20, 120 20, 120 220, 180 220, 140 160, 200 160, 180 220, 240 220, 240 120, 20 120, 20 20))"; checkIsValidSTR(wkt, false); - checkIsValidDefault(wkt, false); + checkIsValidOGC(wkt, false); } - private void checkIsValidDefault(String wkt, boolean expected) + private void checkIsValidOGC(String wkt, boolean expected) { Geometry geom = fromWKT(wkt); IsValidOp validator = new IsValidOp(geom); @@ -132,7 +160,7 @@ Geometry fromWKT(String wkt) { Geometry geom = null; try { - geom = rdr.read(wkt); + geom = read(wkt); } catch (Exception ex) { ex.printStackTrace(); diff --git a/modules/core/src/test/java/test/jts/perf/operation/valid/QuadtreeNestedRingTester.java b/modules/core/src/test/java/test/jts/perf/operation/valid/QuadtreeNestedRingTester.java deleted file mode 100644 index 19df288ddd..0000000000 --- a/modules/core/src/test/java/test/jts/perf/operation/valid/QuadtreeNestedRingTester.java +++ /dev/null @@ -1,100 +0,0 @@ - -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package test.jts.perf.operation.valid; - -import java.util.ArrayList; -import java.util.List; - -import org.locationtech.jts.algorithm.PointLocation; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.index.quadtree.Quadtree; -import org.locationtech.jts.operation.valid.IsValidOp; -import org.locationtech.jts.util.Assert; - -/** - * Tests whether any of a set of {@link LinearRing}s are - * nested inside another ring in the set, using a {@link Quadtree} - * index to speed up the comparisons. - * - * @version 1.7 - */ -public class QuadtreeNestedRingTester -{ - - private GeometryGraph graph; // used to find non-node vertices - private List rings = new ArrayList(); - private Envelope totalEnv = new Envelope(); - private Quadtree quadtree; - private Coordinate nestedPt; - - public QuadtreeNestedRingTester(GeometryGraph graph) - { - this.graph = graph; - } - - public Coordinate getNestedPoint() { return nestedPt; } - - public void add(LinearRing ring) - { - rings.add(ring); - totalEnv.expandToInclude(ring.getEnvelopeInternal()); - } - - public boolean isNonNested() - { - buildQuadtree(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing innerRing = (LinearRing) rings.get(i); - Coordinate[] innerRingPts = innerRing.getCoordinates(); - - List results = quadtree.query(innerRing.getEnvelopeInternal()); -//System.out.println(results.size()); - for (int j = 0; j < results.size(); j++) { - LinearRing searchRing = (LinearRing) results.get(j); - Coordinate[] searchRingPts = searchRing.getCoordinates(); - - if (innerRing == searchRing) - continue; - - if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) - continue; - - Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph); - Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); - //Coordinate innerRingPt = innerRingPts[0]; - - boolean isInside = PointLocation.isInRing(innerRingPt, searchRingPts); - if (isInside) { - nestedPt = innerRingPt; - return false; - } - } - } - return true; - } - - private void buildQuadtree() - { - quadtree = new Quadtree(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing ring = (LinearRing) rings.get(i); - Envelope env = ring.getEnvelopeInternal(); - quadtree.insert(env, ring); - } - } -} diff --git a/modules/core/src/test/java/test/jts/perf/operation/valid/SweeplineNestedRingTester.java b/modules/core/src/test/java/test/jts/perf/operation/valid/SweeplineNestedRingTester.java deleted file mode 100644 index 9e5e69c3b6..0000000000 --- a/modules/core/src/test/java/test/jts/perf/operation/valid/SweeplineNestedRingTester.java +++ /dev/null @@ -1,115 +0,0 @@ - -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package test.jts.perf.operation.valid; - -import java.util.ArrayList; -import java.util.List; - -import org.locationtech.jts.algorithm.PointLocation; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geomgraph.GeometryGraph; -import org.locationtech.jts.index.sweepline.SweepLineIndex; -import org.locationtech.jts.index.sweepline.SweepLineInterval; -import org.locationtech.jts.index.sweepline.SweepLineOverlapAction; -import org.locationtech.jts.operation.valid.IsValidOp; -import org.locationtech.jts.util.Assert; - -/** - * Tests whether any of a set of {@link LinearRing}s are - * nested inside another ring in the set, using a {@link SweepLineIndex} - * index to speed up the comparisons. - * - * @version 1.7 - */ -public class SweeplineNestedRingTester -{ - - private GeometryGraph graph; // used to find non-node vertices - private List rings = new ArrayList(); - //private Envelope totalEnv = new Envelope(); - private SweepLineIndex sweepLine; - private Coordinate nestedPt = null; - - public SweeplineNestedRingTester(GeometryGraph graph) - { - this.graph = graph; - } - - public Coordinate getNestedPoint() { return nestedPt; } - - public void add(LinearRing ring) - { - rings.add(ring); - } - - public boolean isNonNested() - { - buildIndex(); - - OverlapAction action = new OverlapAction(); - - sweepLine.computeOverlaps(action); - return action.isNonNested; - } - - private void buildIndex() - { - sweepLine = new SweepLineIndex(); - - for (int i = 0; i < rings.size(); i++) { - LinearRing ring = (LinearRing) rings.get(i); - Envelope env = ring.getEnvelopeInternal(); - SweepLineInterval sweepInt = new SweepLineInterval(env.getMinX(), env.getMaxX(), ring); - sweepLine.add(sweepInt); - } - } - - private boolean isInside(LinearRing innerRing, LinearRing searchRing) - { - Coordinate[] innerRingPts = innerRing.getCoordinates(); - Coordinate[] searchRingPts = searchRing.getCoordinates(); - - if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal())) - return false; - - Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph); - Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring"); - - boolean isInside = PointLocation.isInRing(innerRingPt, searchRingPts); - if (isInside) { - nestedPt = innerRingPt; - return true; - } - return false; - } - - - class OverlapAction - implements SweepLineOverlapAction - { - boolean isNonNested = true; - - public void overlap(SweepLineInterval s0, SweepLineInterval s1) - { - LinearRing innerRing = (LinearRing) s0.getItem(); - LinearRing searchRing = (LinearRing) s1.getItem(); - if (innerRing == searchRing) return; - - if (isInside(innerRing, searchRing)) - isNonNested = false; - } - - } -} diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index 4ef18654aa..e4e4e0d561 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -2,210 +2,349 @@ - L - linear-ring bowtie - LINEARRING(0 0, 100 100, 100 0, 0 100, 0 0) - - false - + P - point + + POINT(10 10) + + true - L - linestring bowtie - LINESTRING(0 0, 100 100, 100 0, 0 100, 0 0) - - true - + P - empty point + + POINT EMPTY + + true - P - point + mP - no repeated points - POINT(10 10) + MULTIPOINT((10 10), (20 20), (30 30)) - true + true - P - empty point + mP - Valid - repeated points - POINT EMPTY + MULTIPOINT((10 10), (20 20), (30 30), (10 10)) - true + true - P - point with invalid X ordinate + L - Valid - empty - POINT(NaN 10) +LINESTRING EMPTY - false + true - P - point with invalid Y ordinate + L - Valid - no repeated points - POINT(10 NaN) +LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200) - false + true - mP - no repeated points + L - Valid - repeated points - MULTIPOINT((10 10), (20 20), (30 30)) +LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) - - true - + true + + + + L - Valid - linestring bowtie + LINESTRING(0 0, 100 100, 100 0, 0 100, 0 0) + true + + + + mL - MultiLinestring with empty component + MULTILINESTRING((1 1, 0 0), EMPTY) + true + + + + LR - Valid - linear-ring + LINEARRING (100 200, 200 200, 200 100, 100 100, 100 200) + true + + + + A - Valid - polygon with repeated point + POLYGON ((107 246, 107 246, 250 285, 294 137, 151 90, 15 125, 157 174, 107 246)) + true + + + + A - Valid - doughnut + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) + true + + + + A - shell has repeated points + POLYGON ((0 60, 0 0, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) + true + + + + A - shell touches hole without crossing it (valid) + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 60 20, 20 40)) + true + + + + A - non-empty shell and empty hole (valid) + POLYGON ((60 280, 260 180, 60 80, 60 280), EMPTY) + true - mP - repeated points + A - empty shell and holes (valid) + POLYGON (EMPTY, EMPTY, EMPTY) + true + + + + A - hole with repeated points - MULTIPOINT((10 10), (20 20), (30 30), (10 10)) +POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), + (70 230, 80 230, 80 220, 80 220, 70 230)) + true + + + + A - hole touches hole without crossing it (valid) + POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 20 100)) + true + + + + A - hole touches shell at one non-vertex point + +POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), + (140 180, 40 180, 140 240, 140 180)) - - true - + true - mP - invalid point + A - hole touches shell at one vertex point - MULTIPOINT((10 10), (20 20), (30 30), (10 NaN)) +POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), + (140 180, 40 260, 140 240, 140 180)) - - false - + true + + + + A - holes do not overlap, first point is identical + +POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), + (140 180, 60 120, 60 240, 140 180), + (140 180, 200 120, 200 240, 140 180)) + + true + + + + A - holes touch in one point + +POLYGON ((190 190, 360 20, 20 20, 190 190), + (90 50, 150 110, 190 50, 90 50), + (190 50, 230 110, 290 50, 190 50)) + + true + + + + A - touching holes do NOT disconnect (isCCW bug) + +POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), + (260 200, 340 60, 400 120, 260 200), + (260 200, 120 100, 200 60, 260 200)) + + true + + + + mA - nested non-overlapping shells + +MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), + (80 280, 80 100, 260 100, 80 280)), + ((120 160, 140 160, 140 140, 120 160))) + + true - L - empty + mA - nested non-overlapping shells, all vertices touch -LINESTRING EMPTY +MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 180 240, 60 200, 180 160, 340 60, 240 220, 220 340)), + ((180 240, 180 160, 240 220, 180 240))) - - true - + true + + + + mA - nested non-overlapping shells, all vertices touch + +MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), + ((60 200, 340 60, 220 340, 60 200))) + + true - L - no repeated points + mA - disconnected exterior -LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200) +MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), + ((20 100, 100 100, 100 180, 20 180, 20 100)), + ((100 180, 180 180, 180 260, 100 260, 100 180)), + ((180 100, 260 100, 260 180, 180 180, 180 100))) - - true - + true - L - invalid ordinate + mA - shells touch in single point -LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) +MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), + ((110 110, 150 20, 70 20, 110 110))) - - false - + true + + + + mA - shells are not nested but share all vertices + +MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), + ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80))) + + true - L - repeated points + mA - shell is nested inside first hole -LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) +MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), + (3 3, 7 3, 7 7, 3 7, 3 3), + (1 1, 2 1, 2 2, 1 2, 1 1)), + ((4 4, 4 6, 6 6, 6 4, 4 4))) + + true + + + + mA - non-empty and empty polygon + MULTIPOLYGON (((30 10, 40 40, 20 40, 10 20, 30 10)), EMPTY) + + true + + + + + + P - point with invalid X ordinate + + POINT(NaN 10) + + false + + + + P - point with invalid Y ordinate + + POINT(10 NaN) + + false + + + + mP - NaN ordinate + + MULTIPOINT((10 10), (20 20), (30 30), (10 NaN)) + + false + + + + L - NaN ordinate + +LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) - - true - + false + L - linestring with two identical points LINESTRING(0 0, 0 0) - - false - + false + mL - MultiLinestring with two identical points in first component MULTILINESTRING((1 1, 0 0), (0 0, 0 0)) - - false - + false mL - MultiLinestring with two identical points in second component MULTILINESTRING((1 1, 0 0), (0 0, 0 0)) - - false - - - - - mL - MultiLinestring with empty component - MULTILINESTRING((1 1, 0 0), EMPTY) - - true - + false mL - MultiLinestring with invalid point MULTILINESTRING((1 1, 2 NaN, 0 0)) - - false - + false + + LR - linear-ring bowtie + LINEARRING(0 0, 100 100, 100 0, 0 100, 0 0) + false + + A - zero-area polygon POLYGON ((0 0, 0 0, 0 0, 0 0, 0 0)) - - false - + false A - zero-area polygon with multiple points POLYGON ((0 0, 10 0, 20 0, 0 0, 0 0)) - - false - + false A - polygon with too few points POLYGON ((0 0, 10 10, 0 0)) - - false - + false A - polygon with invalid point POLYGON ((0 0, 10 NaN, 20 0, 0 10, 0 0)) - - false - - - - - A - polygon with repeated point - POLYGON ((107 246, 107 246, 250 285, 294 137, 151 90, 15 125, 157 174, 107 246)) - - true - + false A - polygon with degenerate hole ring (A-B-A) POLYGON ((0 0, 0 240, 260 240, 260 0, 0 0), (220 200, 40 200, 40 20, 40 200, 220 200, 220 200)) - - false - + false @@ -214,142 +353,72 @@ LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), ((180 100, 180 180, 180 180, 180 100))) - - false - + false A - polygon self-intersects at non-vertex POLYGON ((0 40, 0 0, 40 40, 40 0, 0 40)) - - false - + false A - polygon self-intersects at vertex MULTIPOLYGON ( ((0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) ) - - false - + false A - polygon self-intersects at vertex/non-vertex POLYGON ((0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) - - false - + false A - hole self-intersects at non-vertex POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 0 0, 40 40, 40 0, 0 40)) - - false - + false A - polygon self-intersects at vertex POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) - - false - + false A - polygon self-intersects at vertex/non-vertex POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) - - false - - - - A - Valid doughnut - POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) - - true - - - - A - shell has repeated points - POLYGON ((0 60, 0 0, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) - - true - - - - - A - shell touches hole without crossing it (valid) - POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 60 20, 20 40)) - - true - + false A - shell touches hole without crossing it, but does so twice (invalid) POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40)) - - false - - - - A - hole touches hole without crossing it (valid) - POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 20 100)) - - true - + false A - holel touches hole without crossing it, but does so twice (invalid) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 80 60, 100 80, 20 100)) - - false - + false A - hole touches hole without crossing it, but does so at an infinite number of points (invalid) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 100 80, 20 100)) - - false - + false A - spike (invalid) POLYGON ((0 60, 0 0, 60 0, 60 20, 100 20, 60 20, 60 60, 0 60)) - - false - + false A - puncture (invalid) POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60)) - - false - + false A - hole within a hole (invalid) POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100)) - - false - - - - A - non-empty shell and empty hole (valid) - POLYGON ((60 280, 260 180, 60 80, 60 280), EMPTY) - - true - - - - - A - empty shell and holes (valid) - POLYGON (EMPTY, EMPTY, EMPTY) - - true - + false @@ -358,11 +427,7 @@ LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) POLYGON ((60 280, 260 180, 60 80, 60 280), (140 80, 120 180, 200 180, 140 80)) - - - false - - + false @@ -370,32 +435,15 @@ POLYGON ((60 280, 260 180, 60 80, 60 280), POLYGON ((60 340, 60 100, 340 100, 340 280, 340 200, 340 340, 60 340)) - - false - + false - - A - hole with repeated points - -POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), - (70 230, 80 230, 80 220, 80 220, 70 230)) - - - true - - - A - hole outside but adjacent to shell POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), (180 160, 240 60, 120 60, 180 160)) - - - false - - + false @@ -404,33 +452,7 @@ POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 180, 140 260, 140 180)) - - - false - - - - - - A - hole touches shell at one non-vertex point - -POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), - (140 180, 40 180, 140 240, 140 180)) - - - true - - - - - A - hole touches shell at one vertex point - -POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), - (140 180, 40 260, 140 240, 140 180)) - - - true - + false @@ -439,11 +461,7 @@ POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (160 120, 180 100, 160 80, 160 120)) - - - false - - + false @@ -452,11 +470,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (20 180, 20 20, 140 20, 140 180, 20 180)) - - - false - - + false @@ -465,11 +479,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (20 180, 20 20, 140 20, 140 180, 20 180)) - - - false - - + false @@ -478,9 +488,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300)) - - false - + false @@ -490,21 +498,15 @@ POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), (140 280, 80 100, 200 100, 140 280), (140 280, 40 80, 240 80, 140 280)) - - false - + false - A - holes do not overlap, first point is identical + A - duplicate holes -POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), - (140 180, 60 120, 60 240, 140 180), - (140 180, 200 120, 200 240, 140 180)) - - - true - +POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200), (120 180, 180 180, 180 120, 120 120, 120 180), (120 180, 180 180, 180 120, 120 120, 120 180)) + + false @@ -512,18 +514,14 @@ POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), POLYGON ((340 320, 340 200, 200 280, 200 80, 340 200, 340 20, 60 20, 60 340, 340 320)) - - false - + false A - shell self-touches at non-vertex POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320, 300 320)) - - false - + false @@ -534,9 +532,7 @@ POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), (220 240, 160 220, 220 160, 220 240), (160 100, 80 180, 100 80, 160 100), (160 100, 220 160, 240 100, 160 100)) - - false - + false @@ -548,9 +544,7 @@ POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), (260 260, 240 160, 300 200, 260 260), (300 300, 300 200, 340 320, 300 300)) - - false - + false @@ -562,55 +556,25 @@ POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), (260 260, 240 160, 300 200, 260 260), (300 300, 300 200, 340 260, 300 300)) - - false - + false - A - holes touch in one point - -POLYGON ((190 190, 360 20, 20 20, 190 190), - (90 50, 150 110, 190 50, 90 50), - (190 50, 230 110, 290 50, 190 50)) - - - true - - - - - A - holes touch in one point - -POLYGON ((190 190, 360 20, 20 20, 190 190), - (90 50, 150 110, 190 50, 90 50), - (190 50, 230 110, 290 50, 190 50)) - - - true - - - - - A - one holes touches another at all vertices + A - one hole touches another at all vertices POLYGON( (0 0, 0 5, 6 5, 6 0, 0 0), (2 1, 4 1, 3 2, 2 1), (2 1, 1 4, 5 4, 4 1, 4 3, 3 2, 2 3, 2 1) ) - - false - + false - A - one holes touches another at several vertices + A - one hole touches another at several vertices POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), (2.5 1, 3.5 1, 3.5 2, 2.5 2, 2.5 1), (2.5 1.5, 1 4, 5 4, 3.5 1.5, 4 3, 3 2, 2 3, 2.5 1.5)) - - false - + false @@ -619,22 +583,9 @@ POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), POLYGON ((0 0, 10 10, 10 0, 0 0), (5 5, 5 0, 10 5, 5 5)) - - false - + false - - A - touching holes do NOT disconnect (isCCW bug) - -POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), - (260 200, 340 60, 400 120, 260 200), - (260 200, 120 100, 200 60, 260 200)) - - - true - - @@ -644,11 +595,7 @@ POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), ((140 120, 40 120, 40 200, 140 200, 140 120))) - - - false - - + false @@ -657,11 +604,7 @@ MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), ((160 120, 60 120, 40 200, 140 200, 160 120))) - - - false - - + false @@ -670,33 +613,7 @@ MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), ((120 240, 220 240, 220 140, 120 140, 120 240))) - - false - - - - - mA - nested non-overlapping shells - -MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), - (80 280, 80 100, 260 100, 80 280)), - ((120 160, 140 160, 140 140, 120 160))) - - - true - - - - - mA - nested non-overlapping shells, all vertices touch - -MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - (220 340, 180 240, 60 200, 180 160, 340 60, 240 220, 220 340)), - ((180 240, 180 160, 240 220, 180 240))) - - - true - + false @@ -706,21 +623,7 @@ MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200))) - - false - - - - - mA - nested non-overlapping shells, all vertices touch - -MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), - ((60 200, 340 60, 220 340, 60 200))) - - - true - + false @@ -730,33 +633,7 @@ MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), ((60 200, 340 60, 220 340, 60 200))) - - false - - - - - mA - disconnected exterior - -MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), - ((20 100, 100 100, 100 180, 20 180, 20 100)), - ((100 180, 180 180, 180 260, 100 260, 100 180)), - ((180 100, 260 100, 260 180, 180 180, 180 100))) - - - true - - - - - mA - shells touch in single point - -MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), - ((110 110, 150 20, 70 20, 110 110))) - - - true - + false @@ -765,41 +642,7 @@ MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), ((60 300, 320 220, 260 60, 60 100, 60 300))) - - false - - - - - mA - shells are not nested but share all vertices - -MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), - ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80))) - - - true - - - - - mA - shell is nested inside first hole - -MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), - (3 3, 7 3, 7 7, 3 7, 3 3), - (1 1, 2 1, 2 2, 1 2, 1 1)), - ((4 4, 4 6, 6 6, 6 4, 4 4))) - - - true - - - - - mA - non-empty and empty polygon - MULTIPOLYGON (((30 10, 40 40, 20 40, 10 20, 30 10)), EMPTY) - - true - + false From 9ac0c573ce383469649950b8cd74da94e4ec8bf8 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Jun 2021 11:18:42 -0700 Subject: [PATCH 031/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index db47ac5eb2..3e5b2ef798 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,6 +29,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717) +* Improve design and perforance of `IsValidOp` (#743) ### Bug Fixes From 9c10c99e8dc2036ad3cfee8da56154b214360f81 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Jun 2021 12:27:34 -0700 Subject: [PATCH 032/275] Use SegmentString.getCoordinate where appropriate (#744) Signed-off-by: Martin Davis --- .../jts/geomgraph/index/SegmentIntersector.java | 10 +++++----- .../jts/noding/InteriorIntersectionFinderAdder.java | 8 ++++---- .../org/locationtech/jts/noding/IntersectionAdder.java | 10 +++++----- .../jts/noding/IntersectionFinderAdder.java | 8 ++++---- .../org/locationtech/jts/noding/NodingValidator.java | 8 ++++---- .../jts/noding/SegmentIntersectionDetector.java | 8 ++++---- .../jts/noding/snap/SnappingIntersectionAdder.java | 8 ++++---- .../snapround/SnapRoundingIntersectionAdder.java | 8 ++++---- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java index b36a8911ad..0c4739f1f4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java @@ -141,11 +141,11 @@ public void addIntersections( ) { if (e0 == e1 && segIndex0 == segIndex1) return; -numTests++; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + numTests++; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); //if (li.hasIntersection() && li.isProper()) Debug.println(li); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/InteriorIntersectionFinderAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/InteriorIntersectionFinderAdder.java index c470a10357..0407ec5ea6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/InteriorIntersectionFinderAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/InteriorIntersectionFinderAdder.java @@ -64,10 +64,10 @@ public void processIntersections( // don't bother intersecting a segment with itself if (e0 == e1 && segIndex0 == segIndex1) return; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); //if (li.hasIntersection() && li.isProper()) Debug.println(li); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionAdder.java index a81ca085c1..a7f1340dc0 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionAdder.java @@ -121,11 +121,11 @@ public void processIntersections( ) { if (e0 == e1 && segIndex0 == segIndex1) return; -numTests++; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + numTests++; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); //if (li.hasIntersection() && li.isProper()) Debug.println(li); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionFinderAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionFinderAdder.java index 60c14fd1c0..2263855228 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionFinderAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/IntersectionFinderAdder.java @@ -65,10 +65,10 @@ public void processIntersections( // don't bother intersecting a segment with itself if (e0 == e1 && segIndex0 == segIndex1) return; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); //if (li.hasIntersection() && li.isProper()) Debug.println(li); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java index ee5bcb25ee..4a16a99ece 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java @@ -103,10 +103,10 @@ private void checkInteriorIntersections(SegmentString e0, int segIndex0, Segment { if (e0 == e1 && segIndex0 == segIndex1) return; //numTests++; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); if (li.hasIntersection()) { diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/SegmentIntersectionDetector.java b/modules/core/src/main/java/org/locationtech/jts/noding/SegmentIntersectionDetector.java index e6984877b5..dda4e9cb89 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/SegmentIntersectionDetector.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/SegmentIntersectionDetector.java @@ -145,10 +145,10 @@ public void processIntersections( // don't bother intersecting a segment with itself if (e0 == e1 && segIndex0 == segIndex1) return; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); // if (li.hasIntersection() && li.isProper()) Debug.println(li); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java index fb01cc4122..694fd091a3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingIntersectionAdder.java @@ -63,10 +63,10 @@ public void processIntersections( // don't bother intersecting a segment with itself if (seg0 == seg1 && segIndex0 == segIndex1) return; - Coordinate p00 = seg0.getCoordinates()[segIndex0]; - Coordinate p01 = seg0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = seg1.getCoordinates()[segIndex1]; - Coordinate p11 = seg1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = seg0.getCoordinate(segIndex0); + Coordinate p01 = seg0.getCoordinate(segIndex0 + 1); + Coordinate p10 = seg1.getCoordinate(segIndex1); + Coordinate p11 = seg1.getCoordinate(segIndex1 + 1); /** * Don't node intersections which are just diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java index f4c5bc3315..00a0b31bb3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java @@ -103,10 +103,10 @@ public void processIntersections( // don't bother intersecting a segment with itself if (e0 == e1 && segIndex0 == segIndex1) return; - Coordinate p00 = e0.getCoordinates()[segIndex0]; - Coordinate p01 = e0.getCoordinates()[segIndex0 + 1]; - Coordinate p10 = e1.getCoordinates()[segIndex1]; - Coordinate p11 = e1.getCoordinates()[segIndex1 + 1]; + Coordinate p00 = e0.getCoordinate(segIndex0); + Coordinate p01 = e0.getCoordinate(segIndex0 + 1); + Coordinate p10 = e1.getCoordinate(segIndex1); + Coordinate p11 = e1.getCoordinate(segIndex1 + 1); li.computeIntersection(p00, p01, p10, p11); //if (li.hasIntersection() && li.isProper()) Debug.println(li); From b520430f425a41961a2da0f09731dcdfbffeb937 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Jun 2021 13:42:21 -0700 Subject: [PATCH 033/275] Improve IsValidOp class naming, logic Signed-off-by: Martin Davis --- .../jts/operation/valid/IsValidOp.java | 16 +++--- ....java => PolygonIntersectionAnalyzer.java} | 42 ++++++++++++++-- .../valid/{AreaNode.java => PolygonNode.java} | 4 +- .../jts/operation/valid/PolygonRing.java | 2 +- ...yzer.java => PolygonTopologyAnalyzer.java} | 49 ++++++++++--------- ...AreaNodeTest.java => PolygonNodeTest.java} | 8 +-- 6 files changed, 78 insertions(+), 43 deletions(-) rename modules/core/src/main/java/org/locationtech/jts/operation/valid/{InvalidIntersectionFinder.java => PolygonIntersectionAnalyzer.java} (81%) rename modules/core/src/main/java/org/locationtech/jts/operation/valid/{AreaNode.java => PolygonNode.java} (98%) rename modules/core/src/main/java/org/locationtech/jts/operation/valid/{AreaTopologyAnalyzer.java => PolygonTopologyAnalyzer.java} (85%) rename modules/core/src/test/java/org/locationtech/jts/operation/valid/{AreaNodeTest.java => PolygonNodeTest.java} (83%) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index bd127739a6..6a92697026 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -238,7 +238,7 @@ private boolean isValid(Polygon g) checkRingsTooFewPoints(g); if (hasInvalidError()) return false; - AreaTopologyAnalyzer areaAnalyzer = new AreaTopologyAnalyzer(g, isInvertedRingValid); + PolygonTopologyAnalyzer areaAnalyzer = new PolygonTopologyAnalyzer(g, isInvertedRingValid); checkAreaIntersections(areaAnalyzer); if (hasInvalidError()) return false; @@ -274,7 +274,7 @@ private boolean isValid(MultiPolygon g) if (hasInvalidError()) return false; } - AreaTopologyAnalyzer areaAnalyzer = new AreaTopologyAnalyzer(g, isInvertedRingValid); + PolygonTopologyAnalyzer areaAnalyzer = new PolygonTopologyAnalyzer(g, isInvertedRingValid); checkAreaIntersections(areaAnalyzer); if (hasInvalidError()) return false; @@ -402,7 +402,7 @@ private boolean isNonRepeatedSizeAtLeast(LineString line, int minSize) { return numPts >= minSize; } - private void checkAreaIntersections(AreaTopologyAnalyzer areaAnalyzer) { + private void checkAreaIntersections(PolygonTopologyAnalyzer areaAnalyzer) { if (areaAnalyzer.hasIntersection()) { logInvalid(TopologyValidationError.SELF_INTERSECTION, areaAnalyzer.getIntersectionLocation()); @@ -428,7 +428,7 @@ private void checkAreaIntersections(AreaTopologyAnalyzer areaAnalyzer) { */ private void checkSelfIntersectingRing(LinearRing ring) { - Coordinate intPt = AreaTopologyAnalyzer.findSelfIntersection(ring); + Coordinate intPt = PolygonTopologyAnalyzer.findSelfIntersection(ring); if (intPt != null) { logInvalid(TopologyValidationError.RING_SELF_INTERSECTION, intPt); @@ -529,7 +529,7 @@ private void checkHolesNotNested(Polygon poly) *

  • shells do not touch along an edge *
  • no duplicate rings exist * - * These have been confirmed by the {@link AreaTopologyAnalyzer}. + * These have been confirmed by the {@link PolygonTopologyAnalyzer}. */ private void checkShellsNotNested(MultiPolygon mp) { @@ -572,7 +572,7 @@ private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) Coordinate shell0 = shell.getCoordinateN(0); Coordinate shell1 = shell.getCoordinateN(1); - if (! AreaTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) + if (! PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) return null; /** @@ -582,7 +582,7 @@ private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) for (int i = 0; i < poly.getNumInteriorRing(); i++) { LinearRing hole = poly.getInteriorRingN(i); if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) - && AreaTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { + && PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { return null; } } @@ -594,7 +594,7 @@ private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) return shell0; } - private void checkInteriorDisconnected(AreaTopologyAnalyzer areaAnalyzer) { + private void checkInteriorDisconnected(PolygonTopologyAnalyzer areaAnalyzer) { if (areaAnalyzer.isInteriorDisconnectedByRingCycle()) logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, areaAnalyzer.getDisconnectionLocation()); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java similarity index 81% rename from modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java rename to modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java index f51abee31d..329e047712 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/InvalidIntersectionFinder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.operation.valid; import java.util.ArrayList; @@ -9,7 +20,18 @@ import org.locationtech.jts.noding.SegmentIntersector; import org.locationtech.jts.noding.SegmentString; -class InvalidIntersectionFinder +/** + * Finds and analyzes intersections in and between polygons, + * to determine if they are valid. + *

    + * The {@link SegmentString}s which are analyzed can have {@link PolygonRing}s + * attached. If so they will be updated with intersection information + * to support further validity analysis which must be done after + * basic intersection validity has been confirmed. + * + * @author mdavis + */ +class PolygonIntersectionAnalyzer implements SegmentIntersector { LineIntersector li = new RobustLineIntersector(); @@ -20,7 +42,12 @@ class InvalidIntersectionFinder private boolean hasDoubleTouch = false; private boolean isInvertedRingValid; - InvalidIntersectionFinder(boolean isInvertedRingValid) { + /** + * Creates a new finder, allowing for the mode where inverted rings are valid. + * + * @param isInvertedRingValid true if inverted rings are valid. + */ + PolygonIntersectionAnalyzer(boolean isInvertedRingValid) { this.isInvertedRingValid = isInvertedRingValid; } @@ -42,6 +69,7 @@ public boolean hasIntersection() { return intersectionPts.size() > 0; } + @Override public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { // don't test a segment with itself @@ -134,7 +162,7 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, e10 = prevCoordinateInRing(ss1, segIndex1); e11 = p11; } - hasCrossing = AreaNode.isCrossing(intPt, e00, e01, e10, e11); + hasCrossing = PolygonNode.isCrossing(intPt, e00, e01, e10, e11); if (hasCrossing) return true; @@ -153,7 +181,7 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, * Also check for an invalid double-touch situation, * if the rings are different. */ - boolean isDoubleTouch = PolygonRing.addTouch((PolygonRing) ss0.getData(), (PolygonRing) ss1.getData(), intPt); + boolean isDoubleTouch = addDoubleTouch(ss0, ss1, intPt); if (isDoubleTouch && ! isSameSegString) { hasDoubleTouch = true; return true; @@ -162,11 +190,15 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, return false; } + private boolean addDoubleTouch(SegmentString ss0, SegmentString ss1, Coordinate intPt) { + return PolygonRing.addTouch((PolygonRing) ss0.getData(), (PolygonRing) ss1.getData(), intPt); + } + private void addSelfTouch(SegmentString ss, Coordinate intPt, Coordinate e00, Coordinate e01, Coordinate e10, Coordinate e11) { PolygonRing polyRing = (PolygonRing) ss.getData(); if (polyRing == null) { - throw new IllegalStateException("SegmentString missing PolygonRing data when checking valid self-touches"); + throw new IllegalStateException("SegmentString missing PolygonRing data when checking self-touches"); } polyRing.addSelfTouch(intPt, e00, e01, e10, e11); } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonNode.java similarity index 98% rename from modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java rename to modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonNode.java index 2c026def28..831b81c93d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaNode.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonNode.java @@ -17,12 +17,12 @@ /** * Functions to compute topological information - * about nodes (ring intersections) in areal geometry. + * about nodes (ring intersections) in polygonal geometry. * * @author mdavis * */ -class AreaNode +class PolygonNode { /** * Check if the edges at a node between two rings (or one ring) cross. diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java index 636951666b..5c4214d451 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java @@ -444,7 +444,7 @@ public boolean isExterior(boolean isInteriorOnRight) { * Note that either corner and either of the other edges could be used to test. * The situation is fully symmetrical. */ - boolean isInteriorSeg = AreaNode.isInteriorSegment(nodePt, e00, e01, e10); + boolean isInteriorSeg = PolygonNode.isInteriorSegment(nodePt, e00, e01, e10); boolean isExterior = isInteriorOnRight ? ! isInteriorSeg : isInteriorSeg; return isExterior; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java similarity index 85% rename from modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java rename to modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java index 5125586b32..8a95a56545 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/AreaTopologyAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java @@ -29,13 +29,13 @@ import org.locationtech.jts.noding.SegmentString; /** - * Analyzes the topology of areal geometry + * Analyzes the topology of polygonal geometry * to determine whether it is valid. * * @author mdavis * */ -class AreaTopologyAnalyzer { +class PolygonTopologyAnalyzer { /** * Finds a self-intersection (if any) in a {@link LinearRing}. @@ -44,7 +44,7 @@ class AreaTopologyAnalyzer { * @return a self-intersection point if one exists, or null */ public static Coordinate findSelfIntersection(LinearRing ring) { - AreaTopologyAnalyzer ata = new AreaTopologyAnalyzer(ring, false); + PolygonTopologyAnalyzer ata = new PolygonTopologyAnalyzer(ring, false); if (ata.hasIntersection()) return ata.getIntersectionLocation(); return null; @@ -115,7 +115,7 @@ public static boolean isIncidentSegmentInRing(Coordinate p0, Coordinate p1, Coor rPrev = rNext; rNext = temp; } - return AreaNode.isInteriorSegment(p0, rPrev, rNext, p1); + return PolygonNode.isInteriorSegment(p0, rPrev, rNext, p1); } /** @@ -148,14 +148,18 @@ private static int ringIndexPrev(Coordinate[] ringPts, int index) { private Geometry inputGeom; private boolean isInvertedRingValid; - private InvalidIntersectionFinder intFinder; + private PolygonIntersectionAnalyzer intFinder; private List polyRings = null; private Coordinate disconnectionPt = null; - public AreaTopologyAnalyzer(Geometry geom, boolean isInvertedRingValid) { + public PolygonTopologyAnalyzer(Geometry geom, boolean isInvertedRingValid) { inputGeom = geom; this.isInvertedRingValid = isInvertedRingValid; - analyze(); + if (! geom.isEmpty()) { + List segStrings = createSegmentStrings(geom, isInvertedRingValid); + polyRings = getPolygonRings(segStrings); + intFinder = analyzeIntersections(segStrings); + } } public boolean hasIntersection() { @@ -178,7 +182,7 @@ public Coordinate getIntersectionLocation() { * the touching graph of all holes in a polygon. *

    * If inverted rings disconnect the interior - * via a self-touch, this is checked by the {@link InvalidIntersectionFinder}. + * via a self-touch, this is checked by the {@link PolygonIntersectionAnalyzer}. * If inverted rings are part of a disconnected ring chain * this is detected here. * @@ -213,22 +217,16 @@ public boolean isInteriorDisconnectedBySelfTouch() { return disconnectionPt != null; } - private void analyze() { - if (inputGeom.isEmpty()) return; - intFinder = computeIntersections(inputGeom); - } - - private InvalidIntersectionFinder computeIntersections(Geometry geom) + private PolygonIntersectionAnalyzer analyzeIntersections(List segStrings) { - List segStrings = extractSegmentStrings(geom); - InvalidIntersectionFinder segInt = new InvalidIntersectionFinder(isInvertedRingValid); + PolygonIntersectionAnalyzer segInt = new PolygonIntersectionAnalyzer(isInvertedRingValid); MCIndexNoder noder = new MCIndexNoder(); noder.setSegmentIntersector(segInt); noder.computeNodes(segStrings); return segInt; } - private List extractSegmentStrings(Geometry geom) { + private static List createSegmentStrings(Geometry geom, boolean isInvertedRingValid) { List segStrings = new ArrayList(); if (geom instanceof LinearRing) { LinearRing ring = (LinearRing) geom; @@ -244,7 +242,6 @@ private List extractSegmentStrings(Geometry geom) { PolygonRing shellRing = null; if (hasHoles || isInvertedRingValid) { shellRing = new PolygonRing(poly.getExteriorRing()); - addPolygonRing(shellRing); } segStrings.add( createSegString(poly.getExteriorRing(), shellRing)); @@ -252,18 +249,24 @@ private List extractSegmentStrings(Geometry geom) { LinearRing hole = poly.getInteriorRingN(j); if (hole.isEmpty()) continue; PolygonRing holeRing = new PolygonRing(hole, j, shellRing); - addPolygonRing(holeRing); segStrings.add( createSegString(hole, holeRing)); } } return segStrings; } - private void addPolygonRing(PolygonRing polyRing) { - if (polyRings == null) { - polyRings = new ArrayList(); + private static List getPolygonRings(List segStrings) { + List polyRings = null; + for (SegmentString ss : segStrings) { + PolygonRing polyRing = (PolygonRing) ss.getData(); + if (polyRing != null) { + if (polyRings == null) { + polyRings = new ArrayList(); + } + polyRings.add(polyRing); + } } - polyRings.add(polyRing); + return polyRings; } private static SegmentString createSegString(LinearRing ring, PolygonRing polyRing) { diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/PolygonNodeTest.java similarity index 83% rename from modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java rename to modules/core/src/test/java/org/locationtech/jts/operation/valid/PolygonNodeTest.java index f2045db5fe..e010be1e0c 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/AreaNodeTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/PolygonNodeTest.java @@ -6,13 +6,13 @@ import junit.textui.TestRunner; import test.jts.GeometryTestCase; -public class AreaNodeTest extends GeometryTestCase { +public class PolygonNodeTest extends GeometryTestCase { public static void main(String args[]) { - TestRunner.run(AreaNodeTest.class); + TestRunner.run(PolygonNodeTest.class); } - public AreaNodeTest(String name) { super(name); } + public PolygonNodeTest(String name) { super(name); } public void testCrossing() { checkValid("LINESTRING (500 1000, 1000 1000, 1000 1500)", @@ -37,7 +37,7 @@ private void checkValid(String wktA, String wktB, boolean isExpectedValid) { Coordinate[] a = readPts(wktA); Coordinate[] b = readPts(wktB); // assert: a[1] = b[1] - boolean isValid = ! AreaNode.isCrossing(a[1], a[0], a[2], b[0], b[2]); + boolean isValid = ! PolygonNode.isCrossing(a[1], a[0], a[2], b[0], b[2]); assertTrue(isValid == isExpectedValid); } From bb7f1c5de7468533be213b9b70d7b7f837410c3c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Jun 2021 15:13:50 -0700 Subject: [PATCH 034/275] Add TestBuilder Valid panel Allow Inverted Rings checkbox Signed-off-by: Martin Davis --- .../jtstest/testbuilder/AppStrings.java | 2 + .../jtstest/testbuilder/ValidPanel.java | 168 +++++++++++++----- 2 files changed, 123 insertions(+), 47 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java index b8701c4dcf..2925e4d9a4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppStrings.java @@ -89,5 +89,7 @@ public class AppStrings { public static final String TIP_STYLE_SHIFT = "Shift layer display"; + public static final String TIP_ALLOW_INVERTED_RINGS = "Allow valid inverted shells and exverted holes"; + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java index d6354137b9..32a6009096 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java @@ -13,6 +13,7 @@ package org.locationtech.jtstest.testbuilder; import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -23,6 +24,7 @@ import javax.swing.BorderFactory; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; @@ -44,21 +46,20 @@ * @version 1.7 */ public class ValidPanel extends JPanel { + private static final int TEXT_BOX_WIDTH = 240; + TestCaseEdit testCase; private Coordinate markPoint = null; -//=========================================== - JButton btnValidate = new JButton(); - JButton btnSimple = new JButton(); + + //=========================================== JTextField txtIsValid = new JTextField(); + JTextField txtIsSimple = new JTextField(); JTextArea taInvalidMsg = new JTextArea(); JLabel lblValidSimple = new JLabel(); JPanel jPanel1 = new JPanel(); - GridBagLayout gridBagLayout2 = new GridBagLayout(); private transient Vector validPanelListeners; GridLayout gridLayout1 = new GridLayout(); JPanel markPanel = new JPanel(); - JPanel markSquishPanel = new JPanel(); - JPanel panelValidSimple = new JPanel(); JPanel markBtnPanel = new JPanel(); JTextField txtMarkLocation = new JTextField(); JTextField txtMarkLabel = new JTextField(); @@ -66,6 +67,7 @@ public class ValidPanel extends JPanel { JLabel lblMark = new JLabel(); JButton btnClearMark = new JButton(); JButton btnSetMark = new JButton(); + private JCheckBox cbInvertedRingAllowed; public ValidPanel() { try { @@ -76,44 +78,74 @@ public ValidPanel() { } } void jbInit() throws Exception { + JButton btnValidate = new JButton(); btnValidate.setText("Valid?"); btnValidate.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnValidate_actionPerformed(e); } }); + JButton btnSimple = new JButton(); btnSimple.setText("Simple?"); btnSimple.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnSimple_actionPerformed(e); } }); - this.setLayout(gridLayout1); - gridLayout1.setRows(2); + + JButton btnClear = new JButton(); + btnClear.setText("Clear"); + btnClear.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + clearAll(); + } + }); + + + cbInvertedRingAllowed = new JCheckBox(); + cbInvertedRingAllowed.setToolTipText(AppStrings.TIP_ALLOW_INVERTED_RINGS); + cbInvertedRingAllowed.setAlignmentX(Component.LEFT_ALIGNMENT); + cbInvertedRingAllowed.setText("Allow Inverted Rings"); + txtIsValid.setBackground(AppColors.BACKGROUND); txtIsValid.setEditable(false); - txtIsValid.setText("Y"); + //txtIsValid.setText("Y"); txtIsValid.setHorizontalAlignment(SwingConstants.CENTER); - taInvalidMsg.setPreferredSize(new Dimension(70, 80)); + Dimension flagSize = new Dimension(40, 24); + txtIsValid.setMinimumSize(flagSize); + txtIsValid.setPreferredSize(flagSize); + + txtIsSimple.setBackground(AppColors.BACKGROUND); + txtIsSimple.setEditable(false); + txtIsSimple.setHorizontalAlignment(SwingConstants.CENTER); + txtIsSimple.setMinimumSize(flagSize); + txtIsSimple.setPreferredSize(flagSize); + + taInvalidMsg.setPreferredSize(new Dimension(TEXT_BOX_WIDTH, 80)); + taInvalidMsg.setMaximumSize(new Dimension(TEXT_BOX_WIDTH, 80)); + taInvalidMsg.setMinimumSize(new Dimension(TEXT_BOX_WIDTH, 80)); + taInvalidMsg.setLineWrap(true); taInvalidMsg.setBorder(BorderFactory.createLoweredBevelBorder()); - taInvalidMsg.setMinimumSize(new Dimension(70, 70)); taInvalidMsg.setToolTipText(""); taInvalidMsg.setBackground(AppColors.BACKGROUND); taInvalidMsg.setEditable(true); taInvalidMsg.setFont(new java.awt.Font("SansSerif", 0, 12)); + lblValidSimple.setToolTipText(""); lblValidSimple.setText("Valid / Simple "); - jPanel1.setLayout(gridBagLayout2); + lblMark.setToolTipText(""); lblMark.setText("Mark Point ( X Y ) "); + btnClearMark.setToolTipText(""); btnClearMark.setText("Clear Mark"); btnClearMark.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { - btnClearMark_actionPerformed(e); + clearMark(); } }); + btnSetMark.setToolTipText(""); btnSetMark.setText("Set Mark"); btnSetMark.addActionListener(new java.awt.event.ActionListener() { @@ -121,29 +153,54 @@ public void actionPerformed(ActionEvent e) { btnSetMark_actionPerformed(e); } }); + + JPanel panelValidSimple = new JPanel(); panelValidSimple.add(btnValidate); - panelValidSimple.add(btnSimple); - jPanel1.add(panelValidSimple, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0 - ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 5, 10, 5), 0, 0)); - jPanel1.add(txtIsValid, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0 - ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 4, 0), 10, 0)); - jPanel1.add(taInvalidMsg, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0 - ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 4, 0, 4), 0, 0)); - jPanel1.add(lblValidSimple, new GridBagConstraints(0, 3, 1, 1, 1.0, 0.0 + panelValidSimple.add(txtIsValid); + + JPanel panelSimple = new JPanel(); + panelSimple.add(btnSimple); + panelSimple.add(txtIsSimple); + + JPanel panelMsg = new JPanel(); + panelMsg.add(taInvalidMsg); + + JPanel panelClear = new JPanel(); + panelClear.add(btnClear); + + jPanel1.setLayout(new GridBagLayout()); + jPanel1.add(panelSimple, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 5, 10, 5), 0, 0)); + jPanel1.add(panelValidSimple, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(10, 5, 10, 5), 0, 0)); + jPanel1.add(cbInvertedRingAllowed, new GridBagConstraints(0, 3, 2, 1, 1.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(4, 0, 4, 0), 10, 0)); + + /*jPanel1.add(lblValidSimple, new GridBagConstraints(0, 4, 1, 1, 1.0, 0.0 ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 4, 0, 4), 0, 0)); - this.add(jPanel1, null); - markPanel.setLayout(new BorderLayout()); +*/ /* - markPanel.setLayout(gridBagLayout1); - markPanel.add(jLabel2, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0 - ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); -// jPanel2.add(jLabel3, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0 -// ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - markPanel.add(txtMark, new GridBagConstraints(1, 1, 2, 1, 0.5, 0.0 - ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 20), 0, 0)); -// jPanel2.add(txtX, new GridBagConstraints(1, 0, 2, 1, 0.5, 0.0 -// ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 20), 0, 0)); + jPanel1.add(txtIsValid, new GridBagConstraints(1, 4, 1, 1, 1.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 4, 0), 10, 0)); */ + jPanel1.add(panelMsg, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 4, 0, 4), 0, 0)); + jPanel1.add(panelClear, new GridBagConstraints(0, 5, 2, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 4, 0, 4), 0, 0)); + + //---------------------------------------------- + txtMarkLocation.setBorder(BorderFactory.createLoweredBevelBorder()); + txtMarkLocation.setToolTipText(""); + txtMarkLocation.setEditable(true); + txtMarkLocation.setFont(new java.awt.Font("SansSerif", 0, 12)); + txtMarkLocation.setHorizontalAlignment(SwingConstants.LEFT); + Dimension markDim = new Dimension(TEXT_BOX_WIDTH, 20); + txtMarkLocation.setPreferredSize(markDim); + txtMarkLocation.setMaximumSize(markDim); + txtMarkLocation.setMinimumSize(markDim); + + + markPanel.setLayout(new BorderLayout()); markBtnPanel.add(btnSetMark); markBtnPanel.add(btnClearMark); @@ -152,15 +209,10 @@ public void actionPerformed(ActionEvent e) { //markPanel.add(txtMarkLabel, BorderLayout.CENTER); markPanel.add(markBtnPanel, BorderLayout.SOUTH); - markSquishPanel.setLayout(new BorderLayout()); - markSquishPanel.add(markPanel, BorderLayout.NORTH); - /* - markPanel.add(btnSetMark, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0 - ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 4, 10), 0, 0)); - markPanel.add(btnClearMark, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0 - ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 10, 4, 10), 0, 0)); - */ - this.add(markSquishPanel, null); + //---------------------------------------------- + this.setLayout(new BorderLayout()); + this.add(jPanel1, BorderLayout.CENTER); + this.add(markPanel, BorderLayout.SOUTH); } public void setTestCase(TestCaseEdit testCase) { @@ -169,11 +221,21 @@ public void setTestCase(TestCaseEdit testCase) { public Coordinate getMarkPoint() { return markPoint; } + void clearAll() { + clearFlag(txtIsValid); + clearFlag(txtIsSimple); + taInvalidMsg.setText(""); + clearMark(); + } + void btnValidate_actionPerformed(ActionEvent e) { TopologyValidationError err = null; if (testCase.getGeometry(0) != null) { IsValidOp validOp = new IsValidOp(testCase.getGeometry(0)); + if (cbInvertedRingAllowed.isSelected()) { + validOp.setSelfTouchingRingFormingHoleValid(true); + } err = validOp.getValidationError(); } String msg = ""; @@ -185,7 +247,7 @@ void btnValidate_actionPerformed(ActionEvent e) invalidPoint = err.getCoordinate(); } taInvalidMsg.setText(msg); - txtIsValid.setText(isValid ? "Y" : "N"); + setFlagText(txtIsValid, isValid); setMarkPoint(invalidPoint); } void btnSimple_actionPerformed(ActionEvent e) @@ -201,9 +263,20 @@ void btnSimple_actionPerformed(ActionEvent e) "" : "Non-simple intersection at " + WKTWriter.toPoint(nonSimpleLoc); taInvalidMsg.setText(msg); - txtIsValid.setText(isSimple ? "Y" : "N"); + setFlagText(txtIsSimple, isSimple); setMarkPoint(nonSimpleLoc); } + + private void setFlagText(JTextField txt, boolean val) { + txt.setText(val ? "Y" : "N"); + txt.setBackground(val ? AppColors.BACKGROUND : AppColors.BACKGROUND_ERROR); + } + + private void clearFlag(JTextField txt) { + txt.setText(""); + txt.setBackground(AppColors.BACKGROUND); + } + private void setMarkPoint(Coordinate coord) { markPoint = coord; @@ -214,6 +287,11 @@ private void setMarkPoint(Coordinate coord) txtMarkLocation.setText(markText); fireSetHighlightPerformed(new ValidPanelEvent(this)); } + + private void clearMark() { + setMarkPoint(null); + } + public synchronized void removeValidPanelListener(ValidPanelListener l) { if (validPanelListeners != null && validPanelListeners.contains(l)) { Vector v = (Vector) validPanelListeners.clone(); @@ -243,10 +321,6 @@ void btnSetMark_actionPerformed(ActionEvent e) { setMarkPoint(parseXY(xyStr)); } - void btnClearMark_actionPerformed(ActionEvent e) { - setMarkPoint(null); - } - Coordinate parseXY(String xyStr) { // remove commas and underscores in case they are present From c37bcf8bb307edb44b22ec15537c61d1432e03cf Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 18 Jun 2021 17:00:48 -0700 Subject: [PATCH 035/275] Fix PolygonOverlayFunctions to support MultiPolygons Signed-off-by: Martin Davis --- .../jtstest/function/PolygonOverlayFunctions.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java index d94f2892b5..2cbdcfe070 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java @@ -21,6 +21,7 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Polygonal; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.geom.util.LinearComponentExtracter; import org.locationtech.jts.index.strtree.STRtree; @@ -175,7 +176,7 @@ public void addSourcePolygons(Geometry source) { if (source == null || source.getDimension() < 2) return; for (int i = 0; i < source.getNumGeometries(); i++) { Geometry geom = source.getGeometryN(i); - if (geom instanceof Polygon) { + if (geom instanceof Polygonal) { sourceIndex.insert(geom.getEnvelopeInternal(), geom); } } @@ -187,8 +188,8 @@ public List findParents(List resultants) { Point intPt = res.getInteriorPoint(); Coordinate intCoord = intPt.getCoordinate(); - List candidates = sourceIndex.query(intPt.getEnvelopeInternal()); - for (Polygon cand : candidates) { + List candidates = sourceIndex.query(intPt.getEnvelopeInternal()); + for (Geometry cand : candidates) { boolean isParent = SimplePointInAreaLocator.isContained(intCoord, cand); if (isParent) { From d46da5a0517330439abc4a0eee13385f7847c400 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 18 Jun 2021 17:28:56 -0700 Subject: [PATCH 036/275] Javadoc Signed-off-by: Martin Davis --- .../jts/operation/valid/PolygonTopologyAnalyzer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java index 8a95a56545..ea2d867345 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java @@ -31,6 +31,11 @@ /** * Analyzes the topology of polygonal geometry * to determine whether it is valid. + *

    + * Analyzing polygons with inverted rings (shells or exverted holes) + * is performed if specified. + * Inverted rings may cause a disconnected interior due to a self-touch; + * this is reported by {@link #isInteriorDisconnectedBySelfTouch()}. * * @author mdavis * @@ -152,6 +157,12 @@ private static int ringIndexPrev(Coordinate[] ringPts, int index) { private List polyRings = null; private Coordinate disconnectionPt = null; + /** + * Creates a new analyzer for a {@link Polygon} or {@link MultiPolygon}. + * + * @param geom a Polygon or MultiPolygon + * @param isInvertedRingValid a flag indicating whether inverted rings are allowed + */ public PolygonTopologyAnalyzer(Geometry geom, boolean isInvertedRingValid) { inputGeom = geom; this.isInvertedRingValid = isInvertedRingValid; From ab2a5b47589ce53a690d2835749475bbb50ce967 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 21 Jun 2021 17:53:11 -0700 Subject: [PATCH 037/275] All TestBuilder PolygonOverlayFunctions to support linear inputs Signed-off-by: Martin Davis --- .../jtstest/function/PolygonOverlayFunctions.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java index 2cbdcfe070..c62a3ca23e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PolygonOverlayFunctions.java @@ -93,8 +93,17 @@ private static Geometry computeOverlay(Geometry g1, Geometry g2, Noder noder) polygonizer.add(nodedDedupedLinework); List resultants = (List) polygonizer.getPolygons(); - // use PIP to find polygons which have a parent - List polys = ParentFinder.findParents(g1, g2, resultants); + /** + * If the input contained polygons, + * use PIP to find polygons which have a parent. + * Otherwise just return all resultants + * (to support providing just lines as input) + */ + boolean hasPolys = g1.getDimension() >= 2; + List polys = resultants; + if (hasPolys) { + polys = ParentFinder.findParents(g1, g2, resultants); + } // convert to collection for return Polygon[] polyArray = GeometryFactory.toPolygonArray(polys); From 1bcdd78fc983de255ca957c71a4266e87ed0107a Mon Sep 17 00:00:00 2001 From: Ivan Veselovsky <78496819+ivanveselovsky-tomtom@users.noreply.github.com> Date: Wed, 23 Jun 2021 04:28:15 +0200 Subject: [PATCH 038/275] Fix SortedPackedIntervalRtree to be thread-safe (#746) * Fix SortedPackedIntervalRtree to be thread-safe Signed-off-by: veselovs --- .../locate/IndexedPointInAreaLocator.java | 12 ++-- .../SortedPackedIntervalRTree.java | 32 ++++----- .../PreparedGeometryConcurrencyBugTest.java | 69 +++++++++++++++++++ 3 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java index d18b586309..9d0a3cea20 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java @@ -55,7 +55,7 @@ public class IndexedPointInAreaLocator { private Geometry geom; - private IntervalIndexedGeometry index = null; + private volatile IntervalIndexedGeometry index = null; /** * Creates a new locator for a given {@link Geometry}. @@ -110,7 +110,7 @@ private synchronized void createIndex() { private static class SegmentVisitor implements ItemVisitor { - private RayCrossingCounter counter; + private final RayCrossingCounter counter; public SegmentVisitor(RayCrossingCounter counter) { @@ -126,15 +126,17 @@ public void visitItem(Object item) private static class IntervalIndexedGeometry { - private boolean isEmpty = false; - private SortedPackedIntervalRTree index= new SortedPackedIntervalRTree(); + private final boolean isEmpty; + private final SortedPackedIntervalRTree index= new SortedPackedIntervalRTree(); public IntervalIndexedGeometry(Geometry geom) { if (geom.isEmpty()) isEmpty = true; - else + else { + isEmpty = false; init(geom); + } } private void init(Geometry geom) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/SortedPackedIntervalRTree.java b/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/SortedPackedIntervalRTree.java index 7af3625abd..81fdbeb0e9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/SortedPackedIntervalRTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/SortedPackedIntervalRTree.java @@ -15,9 +15,7 @@ import java.util.Collections; import java.util.List; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.index.ItemVisitor; -import org.locationtech.jts.io.WKTWriter; /** @@ -37,7 +35,7 @@ */ public class SortedPackedIntervalRTree { - private List leaves = new ArrayList(); + private final List leaves = new ArrayList(); /** * If root is null that indicates @@ -45,8 +43,8 @@ public class SortedPackedIntervalRTree * OR nothing has been added to the tree. * In both cases, the tree is still open for insertions. */ - private IntervalRTreeNode root = null; - + private volatile IntervalRTreeNode root = null; + public SortedPackedIntervalRTree() { @@ -68,7 +66,7 @@ public void insert(double min, double max, Object item) leaves.add(new IntervalRTreeLeafNode(min, max, item)); } - private void init() + private synchronized void init() { // already built if (root != null) return; @@ -82,7 +80,7 @@ private void init() buildRoot(); } - private synchronized void buildRoot() + private void buildRoot() { if (root != null) return; root = buildTree(); @@ -109,12 +107,12 @@ private IntervalRTreeNode buildTree() dest = temp; } } - - private int level = 0; - + + //private int level = 0; + private void buildLevel(List src, List dest) { - level++; + //level++; dest.clear(); for (int i = 0; i < src.size(); i += 2) { IntervalRTreeNode n1 = (IntervalRTreeNode) src.get(i); @@ -132,12 +130,12 @@ private void buildLevel(List src, List dest) } } } - - private void printNode(IntervalRTreeNode node) - { - System.out.println(WKTWriter.toLineString(new Coordinate(node.min, level), new Coordinate(node.max, level))); - } - + + // private void printNode(IntervalRTreeNode node) + // { + // System.out.println(WKTWriter.toLineString(new Coordinate(node.min, level), new Coordinate(node.max, level))); + // } + /** * Search for intervals in the index which intersect the given closed interval * and apply the visitor to them. diff --git a/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java new file mode 100644 index 0000000000..619221d99a --- /dev/null +++ b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java @@ -0,0 +1,69 @@ +package test.jts.perf.geom.prep; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.junit.Assert; +import org.junit.Test; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.prep.PreparedGeometry; +import org.locationtech.jts.geom.prep.PreparedGeometryFactory; +import org.locationtech.jts.io.WKTReader; + +/** + * To achieve maximum reproducibility + * please run this test JVM option " -Djava.compiler=NONE ". + */ +public class PreparedGeometryConcurrencyBugTest { + private final PreparedGeometry preparedGeometry; + + public PreparedGeometryConcurrencyBugTest() throws Exception { + Geometry geometry = new WKTReader().read("MULTIPOLYGON (((-68.3233207378872 45.95414744388297, -68.3233207378872 46.36350003948202, -67.78323905592147 46.36350003948202, -67.7831824582579 46.360613558640125, -67.78313216656608 46.35737980285657, -67.78303661235164 46.35108931804467, -67.78300844900423 46.34914504123918, -67.78284449808892 46.33717561858782, -67.78266847716758 46.325590424461865, -67.78267250050291 46.32359786763226, -67.78259505129752 46.31960370146851, -67.78254576543955 46.3175417421042, -67.78251860792597 46.31565680949507, -67.78243110038221 46.309206397103196, -67.78231542949105 46.29783343391671, -67.78229631864816 46.29618990142828, -67.78218668276001 46.28645443572722, -67.78211526855763 46.2793814121907, -67.78211325688996 46.27917320458661, -67.78208710521022 46.275919332126335, -67.78205491852746 46.27178233755782, -67.78201367934017 46.266482599074635, -67.78199859183263 46.264539328102984, -67.78198853349427 46.25005029169182, -67.78186381009857 46.23839468919735, -67.7818255884128 46.235723194528276, -67.7817923958962 46.23238684369343, -67.78174311003822 46.22733152283243, -67.78173707503521 46.22676624421646, -67.78164654998994 46.21753570710118, -67.78163850331926 46.21706397103199, -67.7815147857574 46.20460671897001, -67.7814041440354 46.19073627036815, -67.7813679340173 46.18616877891773, -67.78136692818346 46.18602494467913, -67.78130255481794 46.17465499899416, -67.78129450814725 46.173130154898416, -67.78127539730437 46.169318044659036, -67.78123616978475 46.15945986722994, -67.78119895393282 46.146595252464294, -67.7811959364313 46.14540736270368, -67.7811848722591 46.13702776101388, -67.78116173808087 46.13562663447999, -67.78116173808087 46.134921544960775, -67.78128545564273 46.13293804063568, -67.78117984308992 46.12505330919333, -67.78099577549789 46.12165359082678, -67.78096660631664 46.11688795011065, -67.78094749547375 46.11094749547374, -67.78098269965801 46.105255481794416, -67.78098873466104 46.10430597465299, -67.78091128545564 46.097327499497084, -67.78094548380608 46.09648662240997, -67.78093844296922 46.09618990142829, -67.78092737879702 46.095644739489046, -67.78084691209013 46.091684771675716, -67.78087205793602 46.088170388251854, -67.7808851337759 46.086454435727205, -67.78085596459465 46.07591329712332, -67.78081271373969 46.07353651176825, -67.78073627036814 46.06928082880708, -67.78069503118085 46.06146650573325, -67.78064574532287 46.05410883122107, -67.7806437336552 46.05378193522429, -67.78060350030175 46.04886944276805, -67.78058036612352 46.046000804667074, -67.7804385435526 46.038453027559854, -67.7805602494468 46.03127036813518, -67.78063971031986 46.026529873264934, -67.78052806276403 46.021696841681745, -67.78044659022329 46.01135184067593, -67.78046167773084 46.00444679139005, -67.78057634278817 46.00098672299336, -67.7806075236371 46.00005632669482, -67.7807020720177 45.99244518205592, -67.7807966203983 45.984834037417016, -67.78074331120499 45.984817944075644, -67.78107825387247 45.973686381009855, -67.78134479983906 45.96554817944076, -67.78128243814122 45.96297425065379, -67.78130356065178 45.95796218064777, -67.78124132526604 45.95414744388297, -68.3233207378872 45.95414744388297)))"); + preparedGeometry = new PreparedGeometryFactory().create(geometry); + } + + void test0() throws Exception { + List> tasks = IntStream.range(0, 50).mapToObj(i -> (Callable) this::getForPoint).collect(Collectors.toList()); + ExecutorService service = Executors.newFixedThreadPool(20); + List> list = service.invokeAll(tasks); + for (Future f : list) { + Boolean su = f.get(); + Assert.assertTrue(su); + } + } + + @Test + public void test() throws Exception { + for (int i=0; i<100; i++) { + new PreparedGeometryConcurrencyBugTest().test0(); + } + } + + private boolean getForPoint() { + return getFor(-67.78258, 46.13388705); + } + + public boolean getFor(final double x, final double y) { + List speedUnits = IntStream.range(0, 5) + .mapToObj(i -> getForEx(x, y)) + .collect(Collectors.toList()); + Boolean firstSpeedUnit = speedUnits.get(0); + boolean allMatch = speedUnits.stream().allMatch(unit -> unit.equals(firstSpeedUnit)); + + Assert.assertTrue("Inconsistent result: " + speedUnits, allMatch); + return firstSpeedUnit; + } + + private boolean getForEx(double x, double y) { + Point point = new GeometryFactory().createPoint(new Coordinate(x, y)); + return preparedGeometry.contains(point); + } +} From 1b07a087f98033e6b4f560869c883e6bbc04e27a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 22 Jun 2021 19:29:08 -0700 Subject: [PATCH 039/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 3e5b2ef798..2eea9343d8 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -30,6 +30,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717) * Improve design and perforance of `IsValidOp` (#743) +* Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) ### Bug Fixes From 2d12640c2147418bcff5db3eeed082e384d10bbe Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 23 Jun 2021 08:22:23 -0700 Subject: [PATCH 040/275] Javadoc Signed-off-by: Martin Davis --- .../perf/geom/prep/PreparedGeometryConcurrencyBugTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java index 619221d99a..1bfce5dd42 100644 --- a/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java +++ b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java @@ -19,8 +19,12 @@ import org.locationtech.jts.io.WKTReader; /** + * Tests race condition in {@link SortedPackedIntervalRTree}. + * See https://github.com/locationtech/jts/pull/746 + * * To achieve maximum reproducibility * please run this test JVM option " -Djava.compiler=NONE ". + * */ public class PreparedGeometryConcurrencyBugTest { private final PreparedGeometry preparedGeometry; From fe1fe57937041bf4f0ea6ba169b57ff1a759fd4c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 25 Jun 2021 15:14:12 -0700 Subject: [PATCH 041/275] Fix & Improve IsValidOp (#748) * Improve invalidity categorization * Refactoring, logic improvement * Fix hole cycle detection algorithm * Improve unit tests Signed-off-by: Martin Davis --- .../valid/IndexedNestedHoleTester.java | 63 ++++----- .../jts/operation/valid/IsValidOp.java | 83 +++++------- .../valid/PolygonIntersectionAnalyzer.java | 95 +++++++------- .../jts/operation/valid/PolygonRing.java | 120 +++++++++--------- .../valid/PolygonTopologyAnalyzer.java | 112 ++++++++++------ .../jts/operation/valid/IsValidTest.java | 56 ++++++-- .../valid/ValidSelfTouchingRingTest.java | 37 ++++-- .../resources/testxml/general/TestValid.xml | 12 +- 8 files changed, 332 insertions(+), 246 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java index 194953f2d9..c8d4773385 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java @@ -13,11 +13,9 @@ import java.util.List; -import org.locationtech.jts.algorithm.PointLocation; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geom.Location; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.index.SpatialIndex; import org.locationtech.jts.index.strtree.STRtree; @@ -27,10 +25,14 @@ * nested inside another hole, using a spatial * index to speed up the comparisons. *

    - * Assumes that the holes and polygon shell do not cross - * (are properly nested). - * Does not check the case where every vertex of a hole touches another - * hole; this is invalid, and must be checked elsewhere. + * The logic assumes that the holes do not overlap and have no collinear segments + * (so they are properly nested, and there are no duplicate holes). + *

    + * The situation where every vertex of a hole touches another hole + * is invalid because either the hole is nested, + * or else it disconnects the polygon interior. + * This class detects the nested situation. + * The disconnected interior situation must be checked elsewhere. * * @version 1.7 */ @@ -57,48 +59,49 @@ private void loadIndex() } } + /** + * Gets a point on a nested hole, if one exists. + * + * @return a point on a nested hole, or null if none are nested + */ public Coordinate getNestedPoint() { return nestedPt; } + /** + * Tests if any hole is nested (contained) within another hole. + * This is invalid. + * The nested point will be set to reflect this. + * @return true if some hole is nested + */ public boolean isNested() { for (int i = 0; i < polygon.getNumInteriorRing(); i++) { LinearRing hole = (LinearRing) polygon.getInteriorRingN(i); - List results = index.query(hole.getEnvelopeInternal()); - for (int j = 0; j < results.size(); j++) { - LinearRing testHole = (LinearRing) results.get(j); + List results = index.query(hole.getEnvelopeInternal()); + for (LinearRing testHole : results) { if (hole == testHole) continue; /** - * Hole is not covered by in test hole, - * so cannot be inside + * Hole is not fully covered by test hole, so cannot be nested */ if (! testHole.getEnvelopeInternal().covers( hole.getEnvelopeInternal()) ) continue; - if (isHoleInsideHole(hole, testHole)) - return true; - } - } - return false; - } - - private boolean isHoleInsideHole(LinearRing hole, LinearRing testHole) { - Coordinate[] testPts = testHole.getCoordinates(); - for (int i = 0; i < hole.getNumPoints(); i++) { - Coordinate holePt = hole.getCoordinateN(i); - int loc = PointLocation.locateInRing(holePt, testPts); - switch (loc) { - case Location.EXTERIOR: return false; - case Location.INTERIOR: - nestedPt = holePt; - return true; + /** + * Checks nesting via a point-in-polygon test, + * or if the point lies on the boundary via + * the topology of the incident edges. + */ + Coordinate holePt0 = hole.getCoordinateN(0); + Coordinate holePt1 = hole.getCoordinateN(1); + if (PolygonTopologyAnalyzer.isSegmentInRing(holePt0, holePt1, testHole)) { + nestedPt = holePt0; + return true; + } } - // location is BOUNDARY, so keep trying points } return false; } - } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 6a92697026..12035574e9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -246,7 +246,7 @@ private boolean isValid(Polygon g) checkHolesOutsideShell(g); if (hasInvalidError()) return false; - checkHolesNotNested(g); + checkHolesNested(g); if (hasInvalidError()) return false; checkInteriorDisconnected(areaAnalyzer); @@ -286,10 +286,10 @@ private boolean isValid(MultiPolygon g) } for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkHolesNotNested(p); + checkHolesNested(p); if (hasInvalidError()) return false; } - checkShellsNotNested(g); + checkShellsNested(g); if (hasInvalidError()) return false; checkInteriorDisconnected(areaAnalyzer); @@ -403,22 +403,11 @@ private boolean isNonRepeatedSizeAtLeast(LineString line, int minSize) { } private void checkAreaIntersections(PolygonTopologyAnalyzer areaAnalyzer) { - if (areaAnalyzer.hasIntersection()) { - logInvalid(TopologyValidationError.SELF_INTERSECTION, - areaAnalyzer.getIntersectionLocation()); + if (areaAnalyzer.hasInvalidIntersection()) { + logInvalid(areaAnalyzer.getInvalidCode(), + areaAnalyzer.getInvalidLocation()); return; } - if (areaAnalyzer.hasDoubleTouch()) { - logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, - areaAnalyzer.getIntersectionLocation()); - return; - } - if (areaAnalyzer.isInteriorDisconnectedBySelfTouch()) { - logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, - areaAnalyzer.getDisconnectionLocation()); - return; - } - } /** @@ -452,7 +441,6 @@ private void checkHolesOutsideShell(Polygon poly) LinearRing shell = poly.getExteriorRing(); boolean isShellEmpty = shell.isEmpty(); - PointOnGeometryLocator pir = new IndexedPointInAreaLocator(shell); for (int i = 0; i < poly.getNumInteriorRing(); i++) { LinearRing hole = poly.getInteriorRingN(i); @@ -463,7 +451,7 @@ private void checkHolesOutsideShell(Polygon poly) invalidPt = hole.getCoordinate(); } else { - invalidPt = findHoleOutsideShellPoint(pir, hole); + invalidPt = findHoleOutsideShellPoint(hole, shell); } if (invalidPt != null) { logInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, @@ -475,40 +463,37 @@ private void checkHolesOutsideShell(Polygon poly) /** * Checks if a polygon hole lies inside its shell - * and if not returns the point indicating this. + * and if not returns a point indicating this. * The hole is known to be wholly inside or outside the shell, - * so it suffices to find a single point which is interior or exterior. - * A valid hole may only have a single point touching the shell - * (since otherwise it creates a disconnected interior). - * So there should be at least one point which is interior or exterior, - * and this should be the first or second point tested. + * so it suffices to find a single point which is interior or exterior, + * or check the edge topology at a point on the boundary of the shell. * - * @param shellLocator - * @param hole - * @return a hole point outside the shell, or null if valid + * @param hole the hole to test + * @param shell the polygon shell to test against + * @return a hole point outside the shell, or null if it is inside */ - private Coordinate findHoleOutsideShellPoint(PointOnGeometryLocator shellLocator, LinearRing hole) { - for (int i = 0; i < hole.getNumPoints() - 1; i++) { - Coordinate holePt = hole.getCoordinateN(i); - int loc = shellLocator.locate(holePt); - if (loc== Location.BOUNDARY) continue; - if (loc== Location.INTERIOR) return null; - /** - * Location is EXTERIOR, so hole is outside shell - */ - return holePt; - } - return null; + private Coordinate findHoleOutsideShellPoint(LinearRing hole, LinearRing shell) { + Coordinate holePt0 = hole.getCoordinateN(0); + Coordinate holePt1 = hole.getCoordinateN(1); + /** + * If hole envelope is not covered by shell, it must be outside + */ + if (! shell.getEnvelopeInternal().covers( hole.getEnvelopeInternal() )) + return holePt0; + + if (PolygonTopologyAnalyzer.isSegmentInRing(holePt0, holePt1, shell)) + return null; + return holePt0; } - + /** - * Tests if any polygon hole is nested inside another. + * Checks if any polygon hole is nested inside another. * Assumes that holes do not cross (overlap), * This is checked earlier. * * @param poly the polygon with holes to test */ - private void checkHolesNotNested(Polygon poly) + private void checkHolesNested(Polygon poly) { // skip test if no holes are present if (poly.getNumInteriorRing() <= 0) return; @@ -521,7 +506,7 @@ private void checkHolesNotNested(Polygon poly) } /** - * Tests that no element polygon is in the interior of another element polygon. + * Checks that no element polygon is in the interior of another element polygon. *

    * Preconditions: *

      @@ -531,7 +516,7 @@ private void checkHolesNotNested(Polygon poly) *
    * These have been confirmed by the {@link PolygonTopologyAnalyzer}. */ - private void checkShellsNotNested(MultiPolygon mp) + private void checkShellsNested(MultiPolygon mp) { for (int i = 0; i < mp.getNumGeometries(); i++) { Polygon p = (Polygon) mp.getGeometryN(i); @@ -594,9 +579,11 @@ private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) return shell0; } - private void checkInteriorDisconnected(PolygonTopologyAnalyzer areaAnalyzer) { - if (areaAnalyzer.isInteriorDisconnectedByRingCycle()) + private void checkInteriorDisconnected(PolygonTopologyAnalyzer analyzer) { + if (analyzer.isInteriorDisconnected()) { logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, - areaAnalyzer.getDisconnectionLocation()); + analyzer.getDisconnectionLocation()); + } } + } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java index 329e047712..249e0c104b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java @@ -11,9 +11,6 @@ */ package org.locationtech.jts.operation.valid; -import java.util.ArrayList; -import java.util.List; - import org.locationtech.jts.algorithm.LineIntersector; import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.geom.Coordinate; @@ -34,13 +31,16 @@ class PolygonIntersectionAnalyzer implements SegmentIntersector { - LineIntersector li = new RobustLineIntersector(); - private List intersectionPts = new ArrayList(); - private boolean hasProperInt = false; - private boolean hasIntersection = false; - private boolean hasCrossing= false; - private boolean hasDoubleTouch = false; + private static final int NO_INVALID_INTERSECTION = -1; + private boolean isInvertedRingValid; + + private LineIntersector li = new RobustLineIntersector(); + private int invalidCode = NO_INVALID_INTERSECTION; + private Coordinate invalidLocation = null; + + private boolean hasDoubleTouch = false; + private Coordinate doubleTouchLocation; /** * Creates a new finder, allowing for the mode where inverted rings are valid. @@ -53,22 +53,28 @@ class PolygonIntersectionAnalyzer @Override public boolean isDone() { - return hasIntersection || hasDoubleTouch; + return isInvalid() || hasDoubleTouch; } - public Coordinate getIntersectionLocation() { - if (intersectionPts.size() == 0) return null; - return intersectionPts.get(0); + public boolean isInvalid() { + return invalidCode >= 0; + } + + public int getInvalidCode() { + return invalidCode; + } + + public Coordinate getInvalidLocation() { + return invalidLocation; } public boolean hasDoubleTouch() { return hasDoubleTouch; } - public boolean hasIntersection() { - return intersectionPts.size() > 0; + public Coordinate getDoubleTouchLocation() { + return doubleTouchLocation; } - @Override public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { @@ -77,15 +83,19 @@ public void processIntersections(SegmentString ss0, int segIndex0, SegmentString boolean isSameSegment = isSameSegString && segIndex0 == segIndex1; if (isSameSegment) return; - hasIntersection = findInvalidIntersection(ss0, segIndex0, ss1, segIndex1); - - if (hasIntersection) { - // found an intersection! - intersectionPts.add(li.getIntersection(0)); - } + int code = findInvalidIntersection(ss0, segIndex0, ss1, segIndex1); + /** + * Ensure that invalidCode is only set once, + * since the short-circuiting in {@link SegmentIntersector} is not guaranteed + * to happen immediately. + */ + if (code != NO_INVALID_INTERSECTION) { + invalidCode = code; + invalidLocation = li.getIntersection(0); + } } - private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, + private int findInvalidIntersection(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { Coordinate p00 = ss0.getCoordinate(segIndex0); Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); @@ -94,22 +104,19 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, li.computeIntersection(p00, p01, p10, p11); - if (! li.hasIntersection()) return false; + if (! li.hasIntersection()) { + return NO_INVALID_INTERSECTION; + } /** * Check for an intersection in the interior of both segments. - */ - hasProperInt = li.isProper(); - if (hasProperInt) - return true; - - /** - * Check for collinear segments (which produces two intersection points). - * This is invalid - either a zero-width spike or gore, + * Collinear intersections by definition contain an interior intersection. + * They occur in either a zero-width spike or gore, * or adjacent rings. */ - hasProperInt = li.getIntersectionNum() >= 2; - if (hasProperInt) return true; + if (li.isProper() || li.getIntersectionNum() >= 2) { + return TopologyValidationError.SELF_INTERSECTION; + } /** * Now know there is exactly one intersection, @@ -125,16 +132,14 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, boolean isSameSegString = ss0 == ss1; boolean isAdjacentSegments = isSameSegString && isAdjacentInRing(ss0, segIndex0, segIndex1); // Assert: intersection is an endpoint of both segs - if (isAdjacentSegments) return false; + if (isAdjacentSegments) return NO_INVALID_INTERSECTION; - // TODO: allow ring self-intersection - if NOT using OGC semantics - /** * Under OGC semantics, rings cannot self-intersect. * So the intersection is invalid. */ if (isSameSegString && ! isInvertedRingValid) { - return true; + return TopologyValidationError.SELF_INTERSECTION; } /** @@ -144,7 +149,7 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, * This simplifies following logic, by removing the segment endpoint case. */ if (intPt.equals2D(p01) || intPt.equals2D(p11)) - return false; + return NO_INVALID_INTERSECTION; /** * Check topology of a vertex intersection. @@ -162,9 +167,10 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, e10 = prevCoordinateInRing(ss1, segIndex1); e11 = p11; } - hasCrossing = PolygonNode.isCrossing(intPt, e00, e01, e10, e11); - if (hasCrossing) - return true; + boolean hasCrossing = PolygonNode.isCrossing(intPt, e00, e01, e10, e11); + if (hasCrossing) { + return TopologyValidationError.SELF_INTERSECTION; + } /** * If allowing inverted rings, record a self-touch to support later checking @@ -184,10 +190,11 @@ private boolean findInvalidIntersection(SegmentString ss0, int segIndex0, boolean isDoubleTouch = addDoubleTouch(ss0, ss1, intPt); if (isDoubleTouch && ! isSameSegString) { hasDoubleTouch = true; - return true; + doubleTouchLocation = intPt; + // TODO: for poly-hole or hole-hole touch, check if it has bad topology. If so return invalid code } - return false; + return NO_INVALID_INTERSECTION; } private boolean addDoubleTouch(SegmentString ss0, SegmentString ss1, Coordinate intPt) { diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java index 5c4214d451..70b655f01a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonRing.java @@ -31,6 +31,7 @@ * This is the case if there is no "chain" of touching rings * (which would partition off part of the interior). * This is equivalent to the touch graph having no cycles. + * Thus the touch graph of a valid polygon is a forest - a set of disjoint trees. *

    * Also, in a valid polygon two rings can touch only at a single location, * since otherwise they disconnect a portion of the interior between them. @@ -38,14 +39,11 @@ * (so the touch relation representation for a polygon ring does not need to support * more than one touch location for each adjacent ring). *

    - * Thus the touch graph of a valid polygon is a forest - a set of disjoint trees. - *

    * The cycle detection algorithm works for polygon rings which also contain self-touches * (inverted shells and exverted holes). *

    * Polygons with no holes do not need to be checked for - * a connected interior (unless self-touches are allowed). - *

    + * a connected interior, unless self-touches are allowed. * The class also records the topology at self-touch nodes, * to support checking if an invalid self-touch disconnects the polygon. * @@ -91,18 +89,19 @@ public static boolean addTouch(PolygonRing ring0, PolygonRing ring1, Coordinate } /** - * Finds a location (if any) where a chain of rings forms a cycle + * Finds a location (if any) where a chain of holes forms a cycle * in the ring touch graph. + * The shell may form part of the chain as well. * This indicates that a set of holes disconnects the interior of a polygon. * * @param polyRings the list of rings to check * @return a vertex contained in a ring cycle, or null if none is found */ - public static Coordinate findTouchCycleLocation(List polyRings) { + public static Coordinate findHoleCycleLocation(List polyRings) { for (PolygonRing polyRing : polyRings) { if (! polyRing.isInTouchSet()) { - Coordinate touchCycleLoc = polyRing.findTouchCycleLocation(); - if (touchCycleLoc != null) return touchCycleLoc; + Coordinate holeCycleLoc = polyRing.findHoleCycleLocation(); + if (holeCycleLoc != null) return holeCycleLoc; } } return null; @@ -137,11 +136,6 @@ public static Coordinate findInteriorSelfNode(List polyRings) { */ private PolygonRing touchSetRoot = null; - /** - * The parent of this ring in the touch tree graph. - */ - private PolygonRing touchTreeParent = null; - // lazily created /** * The set of PolygonRingTouch links @@ -204,14 +198,6 @@ private void setTouchSetRoot(PolygonRing ring) { private PolygonRing getTouchSetRoot() { return touchSetRoot; } - - private void setParent(PolygonRing ring) { - touchTreeParent = ring; - } - - private boolean isChildOf(PolygonRing ring) { - return touchTreeParent == ring; - } private boolean hasTouches() { return touches != null && ! touches.isEmpty(); @@ -257,77 +243,91 @@ private boolean isOnlyTouch(PolygonRing ring, Coordinate pt) { } /** - * Detects whether the subgraph of rings linked by touch to this ring - * contains a touch cycle. - * If no cycles are detected, the subgraph of touching rings is a tree. - * The subgraph is marked using this ring as the root. + * Detects whether the subgraph of holes linked by touch to this ring + * contains a hole cycle. + * If no cycles are detected, the set of touching rings is a tree. + * The set is marked using this ring as the root. * - * @return a vertex in a ring cycle, or null if no cycle found + * @return a vertex in a hole cycle, or null if no cycle found */ - private Coordinate findTouchCycleLocation() { + private Coordinate findHoleCycleLocation() { //--- the touch set including this ring is already processed if (isInTouchSet()) return null; //--- scan the touch set tree rooted at this ring // Assert: this.touchSetRoot is null PolygonRing root = this; - root.setParent(root); root.setTouchSetRoot(root); - Deque ringStack = new ArrayDeque(); - ringStack.add(root); + if (! hasTouches()) + return null; + + Deque touchStack = new ArrayDeque(); + init(root, touchStack); - while (! ringStack.isEmpty()) { - PolygonRing ring = ringStack.removeFirst(); - Coordinate touchCyclePt = scanForTouchCycle(root, ring, ringStack); - if (touchCyclePt != null) { - return touchCyclePt; + while (! touchStack.isEmpty()) { + PolygonRingTouch touch = touchStack.pop(); + Coordinate holeCyclePt = scanForHoleCycle(touch, root, touchStack); + if (holeCyclePt != null) { + return holeCyclePt; } } return null; } + private static void init(PolygonRing root, + Deque touchStack) + { + for (PolygonRingTouch touch : root.getTouches()) { + touch.getRing().setTouchSetRoot(root); + touchStack.push(touch); + } + } + /** - * Scans the rings touching a given ring, - * and checks if they are already part of its ring subgraph set. - * If so, a ring cycle has been detected. - * Otherwise, each touched ring is added to the current subgraph set, - * and queued to be scanned in turn. + * Scans for a hole cycle starting at a given touch. * + * @param currentTouch the touch to investigate * @param root the root of the touch subgraph - * @param ring the ring being processed - * @param ringStack the stack of rings to scan - * @return a vertex in a ring cycle if found, or null + * @param touchStack the stack of touches to scan + * @return a vertex in a hole cycle if found, or null */ - private Coordinate scanForTouchCycle(PolygonRing root, PolygonRing ring, Deque ringStack) { - if (! ring.hasTouches()) - return null; + private Coordinate scanForHoleCycle(PolygonRingTouch currentTouch, + PolygonRing root, + Deque touchStack) { + PolygonRing ring = currentTouch.getRing(); + Coordinate currentPt = currentTouch.getCoordinate(); - //-- check the touched rings - //--- either they form a touch cycle, or they are pushed on stack for processing + /** + * Scan the touched rings + * Either they form a hole cycle, or they are added to the touch set + * and pushed on the stack for scanning + */ for (PolygonRingTouch touch : ring.getTouches()) { - PolygonRing touchRing = touch.getRing(); /** - * There is always a link back to the touch-tree parent of the ring, - * so don't include it. - * (I.e. the ring touches the parent ring which originally - * added this ring to the stack) + * Don't check touches at the entry point + * to avoid trivial cycles. + * They will already be processed or on the stack + * from the previous ring (which touched + * all the rings at that point as well) */ - if (ring.isChildOf(touchRing)) + if (currentPt.equals2D( touch.getCoordinate())) continue; /** * Test if the touched ring has already been - * reached via a different path in the tree. - * This indicates a touching ring cycle has been found. - * This is invalid. + * reached via a different touch path. + * This is indicated by it already being marked as + * part of the touch set. + * This indicates a hole cycle has been found. */ + PolygonRing touchRing = touch.getRing(); if (touchRing.getTouchSetRoot() == root) return touch.getCoordinate(); - - touchRing.setParent(ring); + touchRing.setTouchSetRoot(root); - ringStack.add(touchRing); + + touchStack.push(touch); } return null; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java index ea2d867345..32149af73c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java @@ -50,8 +50,8 @@ class PolygonTopologyAnalyzer { */ public static Coordinate findSelfIntersection(LinearRing ring) { PolygonTopologyAnalyzer ata = new PolygonTopologyAnalyzer(ring, false); - if (ata.hasIntersection()) - return ata.getIntersectionLocation(); + if (ata.hasInvalidIntersection()) + return ata.getInvalidLocation(); return null; } @@ -60,9 +60,9 @@ public static Coordinate findSelfIntersection(LinearRing ring) { *

    * Preconditions: *

      - *
    • The segment does not cross the ring - *
    • One or both of the segment endpoints may lie on the ring - *
    • The ring is valid + *
    • The segment intersects the ring only at the endpoints + *
    • One, none or both of the segment endpoints may lie on the ring + *
    • The ring does not self-cross, but it may self-touch *
    * * @param p0 a segment vertex @@ -89,9 +89,9 @@ public static boolean isSegmentInRing(Coordinate p0, Coordinate p1, LinearRing r *

    * Preconditions: *

      - *
    • The segment does not cross the ring + *
    • The segment does not intersect the ring other than at the endpoints *
    • The segment vertex p0 lies on the ring - *
    • The ring is valid + *
    • The ring does not self-cross, but it may self-touch *
    * This works for both shells and holes, but the caller must know * the ring role. @@ -150,7 +150,6 @@ private static int ringIndexPrev(Coordinate[] ringPts, int index) { return iPrev; } - private Geometry inputGeom; private boolean isInvertedRingValid; private PolygonIntersectionAnalyzer intFinder; @@ -164,53 +163,79 @@ private static int ringIndexPrev(Coordinate[] ringPts, int index) { * @param isInvertedRingValid a flag indicating whether inverted rings are allowed */ public PolygonTopologyAnalyzer(Geometry geom, boolean isInvertedRingValid) { - inputGeom = geom; this.isInvertedRingValid = isInvertedRingValid; - if (! geom.isEmpty()) { - List segStrings = createSegmentStrings(geom, isInvertedRingValid); - polyRings = getPolygonRings(segStrings); - intFinder = analyzeIntersections(segStrings); - } + analyze(geom); } - - public boolean hasIntersection() { - return intFinder.hasIntersection(); + + public boolean hasInvalidIntersection() { + return intFinder.isInvalid(); } - public boolean hasDoubleTouch() { - return intFinder.hasDoubleTouch(); + public int getInvalidCode() { + return intFinder.getInvalidCode(); } - public Coordinate getIntersectionLocation() { - return intFinder.getIntersectionLocation(); + public Coordinate getInvalidLocation() { + return intFinder.getInvalidLocation(); } + /** + * Tests whether the interior of the polygonal geometry is + * disconnected. + * If true, the disconnection location is available from + * {@link #getDisconnectionLocation()}. + * + * @return true if the interior is disconnected + */ + public boolean isInteriorDisconnected() { + /** + * May already be set by a double-touching hole + */ + if (disconnectionPt != null) { + return true; + } + if (isInvertedRingValid) { + checkInteriorDisconnectedBySelfTouch(); + if (disconnectionPt != null) { + return true; + } + } + checkInteriorDisconnectedByHoleCycle(); + if (disconnectionPt != null) { + return true; + } + return false; + } + + /** + * Gets a location where the polyonal interior is disconnected. + * {@link #isInteriorDisconnected()} must be called first. + * + * @return the location of an interior disconnection, or null + */ + public Coordinate getDisconnectionLocation() { + return disconnectionPt; + } + /** * Tests whether any polygon with holes has a disconnected interior - * by virtue of the holes (and possibly shell) forming a touch cycle. + * by virtue of the holes (and possibly shell) forming a hole cycle. *

    * This is a global check, which relies on determining * the touching graph of all holes in a polygon. *

    * If inverted rings disconnect the interior * via a self-touch, this is checked by the {@link PolygonIntersectionAnalyzer}. - * If inverted rings are part of a disconnected ring chain - * this is detected here. - * - * @return true if a polygon has a disconnected interior. + * If inverted rings are part of a hole cycle + * this is detected here as well. */ - public boolean isInteriorDisconnectedByRingCycle() { + public void checkInteriorDisconnectedByHoleCycle() { /** * PolyRings will be null for empty, no hole or LinearRing inputs */ if (polyRings != null) { - disconnectionPt = PolygonRing.findTouchCycleLocation(polyRings); + disconnectionPt = PolygonRing.findHoleCycleLocation(polyRings); } - return disconnectionPt != null; - } - - public Coordinate getDisconnectionLocation() { - return disconnectionPt; } /** @@ -218,14 +243,27 @@ public Coordinate getDisconnectionLocation() { * This must be evaluated after other self-intersections have been analyzed * and determined to not exist, since the logic relies on * the rings not self-crossing (winding). - * - * @return true if an area interior is disconnected by a self-touch + *

    + * If self-touching rings are not allowed, + * then the self-touch will previously trigger a self-intersection error. */ - public boolean isInteriorDisconnectedBySelfTouch() { + public void checkInteriorDisconnectedBySelfTouch() { if (polyRings != null) { disconnectionPt = PolygonRing.findInteriorSelfNode(polyRings); } - return disconnectionPt != null; + } + + private void analyze(Geometry geom) { + if (geom.isEmpty()) + return; + List segStrings = createSegmentStrings(geom, isInvertedRingValid); + polyRings = getPolygonRings(segStrings); + intFinder = analyzeIntersections(segStrings); + + if (intFinder.hasDoubleTouch()) { + disconnectionPt = intFinder.getDoubleTouchLocation(); + return; + } } private PolygonIntersectionAnalyzer analyzeIntersections(List segStrings) diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index 6cc23acdfa..7f086999bc 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -63,7 +63,7 @@ public void testValidSimplePolygon() throws Exception { } public void testInvalidSimplePolygonRingSelfIntersection() throws Exception { - checkInvalid( + checkInvalid( TopologyValidationError.SELF_INTERSECTION, "POLYGON ((10 90, 90 10, 90 90, 10 10, 10 90))"); } @@ -77,13 +77,33 @@ public void testPolygonTouchingHoleAtVertex() throws Exception { "POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 260, 140 240, 140 180))"); } + public void testPolygonMultipleHolesTouchAtSamePoint() throws Exception { + checkValid( + "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 60, 20 40, 50 50, 20 60), (40 20, 60 20, 50 50, 40 20))"); + } + + public void testPolygonHoleOutsideShellAllTouch() throws Exception { + checkInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, + "POLYGON ((10 10, 30 10, 30 50, 70 50, 70 10, 90 10, 90 90, 10 90, 10 10), (50 50, 30 10, 70 10, 50 50))"); + } + + public void testPolygonHoleOutsideShellDoubleTouch() throws Exception { + checkInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, + "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 80 80, 80 20, 20 20, 20 80), (90 70, 150 50, 90 20, 110 40, 90 70))"); + } + + public void testPolygonNestedHolesAllTouch() throws Exception { + checkInvalid(TopologyValidationError.NESTED_HOLES, + "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 80 80, 80 20, 20 20, 20 80), (50 80, 80 50, 50 20, 20 50, 50 80))"); + } + public void testInvalidPolygonHoleProperIntersection() throws Exception { - checkInvalid( + checkInvalid( TopologyValidationError.SELF_INTERSECTION, "POLYGON ((10 90, 50 50, 10 10, 10 90), (20 50, 60 70, 60 30, 20 50))"); } public void testInvalidPolygonDisconnectedInterior() throws Exception { - checkInvalid( + checkInvalid( TopologyValidationError.DISCONNECTED_INTERIOR, "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 30 80, 20 20, 20 80), (80 30, 20 20, 80 20, 80 30), (80 80, 30 80, 80 30, 80 80))"); } @@ -92,13 +112,18 @@ public void testValidMultiPolygonTouchAtVertices() throws Exception { "MULTIPOLYGON (((10 10, 10 90, 90 90, 90 10, 80 80, 50 20, 20 80, 10 10)), ((90 10, 10 10, 50 20, 90 10)))"); } + public void testInvalidMultiPolygonHoleOverlapCrossing() throws Exception { + checkInvalid( TopologyValidationError.SELF_INTERSECTION, + "MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200)))"); + } + public void testValidMultiPolygonTouchAtVerticesSegments() throws Exception { checkValid( "MULTIPOLYGON (((60 40, 90 10, 90 90, 10 90, 10 10, 40 40, 60 40)), ((50 40, 20 20, 80 20, 50 40)))"); } - public void testInvalidMultiPolygonTouchVertices() throws Exception { - checkInvalid( + public void testInvalidMultiPolygonNestedAllTouchAtVertices() throws Exception { + checkInvalid( TopologyValidationError.NESTED_SHELLS, "MULTIPOLYGON (((10 10, 20 30, 10 90, 90 90, 80 30, 90 10, 50 20, 10 10)), ((80 30, 20 30, 50 20, 80 30)))"); } @@ -106,9 +131,9 @@ public void testValidMultiPolygonHoleTouchVertices() throws Exception { checkValid( "MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200)))"); } - - public void testLineString() throws Exception { - Geometry g = reader.read( + + public void testLineString() { + Geometry g = read( "LINESTRING(0 0, 0 0)"); g.isValid(); assertTrue(true); //No exception thrown [Jon Aquino] @@ -137,10 +162,7 @@ public void testLinearRingSelfCrossing2() throws Exception { private void checkValid(String wkt) { checkValid(true, wkt); } - - private void checkInvalid(String wkt) { - checkValid(false, wkt); - } + private void checkValid(boolean isExpectedValid, String wkt) { Geometry geom = read(wkt); @@ -148,4 +170,14 @@ private void checkValid(boolean isExpectedValid, String wkt) { assertEquals( isExpectedValid, isValid ); } + private void checkInvalid(String wkt) { + checkValid(false, wkt); + } + + private void checkInvalid(int exepctedErrType, String wkt) { + Geometry geom = read(wkt); + IsValidOp validOp = new IsValidOp(geom); + TopologyValidationError err = validOp.getValidationError(); + assertEquals( exepctedErrType, err.getErrorType() ); + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java index 4e38c8eb47..b4985ac6ee 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/ValidSelfTouchingRingTest.java @@ -139,9 +139,31 @@ public void testShellCrossAndSTR() checkIsValidOGC(wkt, false); } + public void testExvertedHoleStarTouchHoleCycle() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 50 30, 80 80, 80 30, 20 30, 20 80), (40 70, 50 70, 50 30, 40 70), (40 20, 60 20, 50 30, 40 20), (40 80, 20 80, 40 70, 40 80))"; + checkInvalidSTR(wkt, TopologyValidationError.DISCONNECTED_INTERIOR); + //checkIsValidOGC(wkt, false); + } + + public void testExvertedHoleStarTouch() + { + String wkt = "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 50 30, 80 80, 80 30, 20 30, 20 80), (40 70, 50 70, 50 30, 40 70), (40 20, 60 20, 50 30, 40 20))"; + checkIsValidSTR(wkt, true); + checkIsValidOGC(wkt, false); + } + + private void checkInvalidSTR(String wkt, int exepctedErrType) { + Geometry geom = read(wkt); + IsValidOp validOp = new IsValidOp(geom); + validOp.setSelfTouchingRingFormingHoleValid(true); + TopologyValidationError err = validOp.getValidationError(); + assertEquals( exepctedErrType, err.getErrorType() ); + } + private void checkIsValidOGC(String wkt, boolean expected) { - Geometry geom = fromWKT(wkt); + Geometry geom = read(wkt); IsValidOp validator = new IsValidOp(geom); boolean isValid = validator.isValid(); assertTrue(isValid == expected); @@ -149,23 +171,12 @@ private void checkIsValidOGC(String wkt, boolean expected) private void checkIsValidSTR(String wkt, boolean expected) { - Geometry geom = fromWKT(wkt); + Geometry geom = read(wkt); IsValidOp validator = new IsValidOp(geom); validator.setSelfTouchingRingFormingHoleValid(true); boolean isValid = validator.isValid(); assertTrue(isValid == expected); } - Geometry fromWKT(String wkt) - { - Geometry geom = null; - try { - geom = read(wkt); - } - catch (Exception ex) { - ex.printStackTrace(); - } - return geom; - } } diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index e4e4e0d561..974970d584 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -173,6 +173,14 @@ POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), true + + A - multiple holes touch at same point + +POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 60, 20 40, 50 50, 20 60), (40 20, 60 20, 50 50, 40 20)) + + true + + mA - nested non-overlapping shells @@ -617,7 +625,7 @@ MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), - mA - nested overlapping shells, all vertices touch + mA - nested shells, all vertices touch hole, partial overlap MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), @@ -627,7 +635,7 @@ MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - mA - nested overlapping shells, all vertices touch + mA - nested shells, all vertices touch hole, full overlap MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), From 11a7d66b8c97283fda4b686661f02058ac76436a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 25 Jun 2021 15:17:11 -0700 Subject: [PATCH 042/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 2eea9343d8..6f1540d1f5 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,7 +29,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717) -* Improve design and perforance of `IsValidOp` (#743) +* Improve design and perforance of `IsValidOp` (#743 & #748) * Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) ### Bug Fixes From 563f0417eb326850f459c469c07f1d97b7231695 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 28 Jun 2021 10:42:01 -0700 Subject: [PATCH 043/275] Improve TestValid XML descriptions Signed-off-by: Martin Davis --- .../resources/testxml/general/TestValid.xml | 234 ++++++++---------- 1 file changed, 99 insertions(+), 135 deletions(-) diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index 974970d584..b10cbbc5d4 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -2,7 +2,7 @@ - P - point + P - point (valid) POINT(10 10) @@ -10,7 +10,7 @@ - P - empty point + P - empty point (valid) POINT EMPTY @@ -18,7 +18,7 @@ - mP - no repeated points + mP - no repeated points (valid) MULTIPOINT((10 10), (20 20), (30 30)) @@ -26,7 +26,7 @@ - mP - Valid - repeated points + mP - repeated points (valid) MULTIPOINT((10 10), (20 20), (30 30), (10 10)) @@ -34,7 +34,7 @@ - L - Valid - empty + L - empty (valid) LINESTRING EMPTY @@ -42,7 +42,7 @@ LINESTRING EMPTY - L - Valid - no repeated points + L - no repeated points (valid) LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200) @@ -50,7 +50,7 @@ LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200) - L - Valid - repeated points + L - repeated points (valid) LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) @@ -58,43 +58,43 @@ LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) - L - Valid - linestring bowtie + L - linestring bowtie (valid) LINESTRING(0 0, 100 100, 100 0, 0 100, 0 0) true - mL - MultiLinestring with empty component + mL - MultiLinestring with empty component (valid) MULTILINESTRING((1 1, 0 0), EMPTY) true - LR - Valid - linear-ring + LR - linear-ring (valid) LINEARRING (100 200, 200 200, 200 100, 100 100, 100 200) true - A - Valid - polygon with repeated point + A - polygon with repeated point (valid) POLYGON ((107 246, 107 246, 250 285, 294 137, 151 90, 15 125, 157 174, 107 246)) true - A - Valid - doughnut + A - with hole (valid) POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) true - A - shell has repeated points + A - shell has repeated points (valid) POLYGON ((0 60, 0 0, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40)) true - A - shell touches hole without crossing it (valid) + A - shell touches hole (valid) POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 60 20, 20 40)) true @@ -112,7 +112,7 @@ LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200) - A - hole with repeated points + A - hole with repeated points (valid) POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), (70 230, 80 230, 80 220, 80 220, 70 230)) @@ -120,13 +120,13 @@ POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), - A - hole touches hole without crossing it (valid) + A - hole touches hole (valid) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 20 100)) true - A - hole touches shell at one non-vertex point + A - hole touches shell at non-vertex (valid) POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 180, 140 240, 140 180)) @@ -135,7 +135,7 @@ POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), - A - hole touches shell at one vertex point + A - hole touches shell at vertex (valid) POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 260, 140 240, 140 180)) @@ -144,7 +144,7 @@ POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), - A - holes do not overlap, first point is identical + A - holes do not overlap, first point is identical (valid) POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), (140 180, 60 120, 60 240, 140 180), @@ -154,7 +154,7 @@ POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), - A - holes touch in one point + A - holes touch at one point (valid) POLYGON ((190 190, 360 20, 20 20, 190 190), (90 50, 150 110, 190 50, 90 50), @@ -164,25 +164,15 @@ POLYGON ((190 190, 360 20, 20 20, 190 190), - A - touching holes do NOT disconnect (isCCW bug) + A - multiple holes touch at one point (valid) -POLYGON ((60 40, 60 240, 460 240, 460 40, 60 40), - (260 200, 340 60, 400 120, 260 200), - (260 200, 120 100, 200 60, 260 200)) +POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 60, 20 40, 50 50, 20 60), (40 20, 60 20, 50 50, 40 20), (80 60, 80 40, 50 50, 80 60)) true - A - multiple holes touch at same point - -POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 60, 20 40, 50 50, 20 60), (40 20, 60 20, 50 50, 40 20)) - - true - - - - mA - nested non-overlapping shells + mA - shell inside hole, no touch (valid) MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), (80 280, 80 100, 260 100, 80 280)), @@ -192,7 +182,7 @@ MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), - mA - nested non-overlapping shells, all vertices touch + mA - shell inside hole, all shell vertices touch (valid) MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 180 160, 340 60, 240 220, 220 340)), @@ -202,17 +192,7 @@ MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - mA - nested non-overlapping shells, all vertices touch - -MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), - ((60 200, 340 60, 220 340, 60 200))) - - true - - - - mA - disconnected exterior + mA - shells touch, disconnected exterior (valid) MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), @@ -223,7 +203,7 @@ MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), - mA - shells touch in single point + mA - shells touch at one point (valid) MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), ((110 110, 150 20, 70 20, 110 110))) @@ -232,7 +212,7 @@ MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), - mA - shells are not nested but share all vertices + mA - shell touches other shell at all vertices (valid) MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80))) @@ -241,7 +221,7 @@ MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), - mA - shell is nested inside first hole + mA - shell is inside hole (valid) MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), (3 3, 7 3, 7 7, 3 7, 3 3), @@ -252,7 +232,7 @@ MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), - mA - non-empty and empty polygon + mA - non-empty and empty polygon (valid) MULTIPOLYGON (((30 10, 40 40, 20 40, 10 20, 30 10)), EMPTY) true @@ -261,7 +241,7 @@ MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), - P - point with invalid X ordinate + P - invalid NaN X ordinate POINT(NaN 10) @@ -269,7 +249,7 @@ MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), - P - point with invalid Y ordinate + P - invalid NaN Y ordinate POINT(10 NaN) @@ -294,20 +274,20 @@ LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) - L - linestring with two identical points + L - linestring with two identical points (too few distinct points) LINESTRING(0 0, 0 0) false - mL - MultiLinestring with two identical points in first component - MULTILINESTRING((1 1, 0 0), (0 0, 0 0)) + mL - MultiLinestring with two identical points in first component (too few distinct points) + MULTILINESTRING((0 0, 0 0), (1 1, 0 0)) false - mL - MultiLinestring with two identical points in second component + mL - MultiLinestring with two identical points in second component (too few distinct points) MULTILINESTRING((1 1, 0 0), (0 0, 0 0)) false @@ -319,25 +299,25 @@ LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) - LR - linear-ring bowtie + LR - linear-ring bowtie (self-intersection) LINEARRING(0 0, 100 100, 100 0, 0 100, 0 0) false - A - zero-area polygon + A - zero-area polygon (too few distinct points) POLYGON ((0 0, 0 0, 0 0, 0 0, 0 0)) false - A - zero-area polygon with multiple points + A - zero-area polygon with multiple points (self-intersection) POLYGON ((0 0, 10 0, 20 0, 0 0, 0 0)) false - A - polygon with too few points + A - polygon with too few points (too few distinct points) POLYGON ((0 0, 10 10, 0 0)) false @@ -349,14 +329,14 @@ LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) - A - polygon with degenerate hole ring (A-B-A) + A - polygon with degenerate hole ring A-B-C-B--A (self-intersection) POLYGON ((0 0, 0 240, 260 240, 260 0, 0 0), (220 200, 40 200, 40 20, 40 200, 220 200, 220 200)) false - mA - multipolygon with component with too few points + mA - multipolygon with component with too few points (too few distinct points) MULTIPOLYGON ( ((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), @@ -365,76 +345,88 @@ LINESTRING (40 180, 120 120, 140 200, 200 140, NaN 200) - A - polygon self-intersects at non-vertex + A - shell self-touches at vertex (self-intersection) + +POLYGON ((340 320, 340 200, 200 280, 200 80, 340 200, 340 20, 60 20, 60 340, 340 320)) + + false + + + + A - shell self-touches at non-vertex (self-intersection) + +POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320, 300 320)) + false + + + + A - shell self-crosses at non-vertex (self-intersection) POLYGON ((0 40, 0 0, 40 40, 40 0, 0 40)) false - A - polygon self-intersects at vertex + A - shell self-crosses at vertex (self-intersection) MULTIPOLYGON ( ((0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) ) false - A - polygon self-intersects at vertex/non-vertex + A - shell self-crosses at vertex/non-vertex (self-intersection) POLYGON ((0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) false - A - hole self-intersects at non-vertex + A - hole self-crosses at non-vertex (self-intersection) POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 0 0, 40 40, 40 0, 0 40)) false - A - polygon self-intersects at vertex + A - hole self-crosses at vertex (self-intersection) POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) false - A - polygon self-intersects at vertex/non-vertex + A - hole self-crosses at vertex/non-vertex (self-intersection) POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) false - A - shell touches hole without crossing it, but does so twice (invalid) + A - hole touches shell twice (interior disconnected) POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40)) false - A - holel touches hole without crossing it, but does so twice (invalid) + A - hole touches hole twice (interior disconnected) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 80 60, 100 80, 20 100)) false - A - hole touches hole without crossing it, but does so at an infinite number of points (invalid) + A - hole adjacent to hole (self-intersection) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 100 80, 20 100)) false - A - spike (invalid) + A - spike (self-intersection)) POLYGON ((0 60, 0 0, 60 0, 60 20, 100 20, 60 20, 60 60, 0 60)) false - A - puncture (invalid) + A - puncture (self-intersection) POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60)) false - A - hole within a hole (invalid) + A - hole within a hole (nested holes) POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100)) false A - hole overlapping shell at non-vertex - -POLYGON ((60 280, 260 180, 60 80, 60 280), - (140 80, 120 180, 200 180, 140 80)) - + POLYGON ((60 280, 260 180, 60 80, 60 280), (140 80, 120 180, 200 180, 140 80)) false @@ -454,15 +446,6 @@ POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), false - - A - hole touches shell at two points - -POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), - (140 180, 40 180, 140 260, 140 180)) - - false - - A - hole outside shell @@ -482,16 +465,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), - A - hole identical to shell - -POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), - (20 180, 20 20, 140 20, 140 180, 20 180)) - - false - - - - A - hole self-intersects + A - hole self-touches at vertex-segment POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300)) @@ -517,23 +491,8 @@ POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200), (120 180, 180 180, 180 1 false - - A - shell self-touches at vertex - -POLYGON ((340 320, 340 200, 200 280, 200 80, 340 200, 340 20, 60 20, 60 340, 340 320)) - - false - - - - A - shell self-touches at non-vertex - -POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320, 300 320)) - false - - - A - chain of holes surrounds an island inside the polygon + A - chain of holes surrounds an area (interior disconnected) POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), (120 240, 80 180, 160 220, 120 240), @@ -544,7 +503,7 @@ POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), - A - chain of holes splits polygon in two (touching at vertices) + A - chain of holes splits polygon, touches at vertices (interior disconnected) POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), (100 120, 40 20, 180 100, 100 120), @@ -556,7 +515,7 @@ POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), - A - chain of holes splits polygon in two (touching at non-vertex) + A - chain of holes splits polygon, touches at non-vertex (interior disconnected) POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), (100 120, 40 20, 180 100, 100 120), @@ -568,7 +527,7 @@ POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), - A - one hole touches another at all vertices + A - hole touches hole at all vertices (interior disconnected) POLYGON( (0 0, 0 5, 6 5, 6 0, 0 0), (2 1, 4 1, 3 2, 2 1), (2 1, 1 4, 5 4, 4 1, 4 3, 3 2, 2 3, 2 1) ) @@ -576,7 +535,7 @@ POLYGON( (0 0, 0 5, 6 5, 6 0, 0 0), (2 1, 4 1, 3 2, 2 1), (2 1, 1 4, 5 4, 4 1, 4 - A - one hole touches another at several vertices + A - hole touches hole at several vertices (interior disconnected) POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), (2.5 1, 3.5 1, 3.5 2, 2.5 2, 2.5 1), @@ -586,19 +545,16 @@ POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), - A - hole disconnects interiors + A - hole touches shell at all points (interior disconnected) POLYGON ((0 0, 10 10, 10 0, 0 0), (5 5, 5 0, 10 5, 5 5)) false - - - - mA - adjacent shells (shared vertices) + mA - shells adjacent, same vertices (self-intersection) MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), ((140 120, 40 120, 40 200, 140 200, 140 120))) @@ -607,16 +563,25 @@ MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), - mA - adjacent shells (different vertices) + mA - shells adjacent, different vertices (self-intersection) MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), ((160 120, 60 120, 40 200, 140 200, 160 120))) false + + + mA - duplicate shells (self-intersection) + +MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), + ((60 300, 320 220, 260 60, 60 100, 60 300))) + + false + - mA - nested overlapping shells + mA - shell inside shell, disjoint (nested shells) MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), ((120 240, 220 240, 220 140, 120 140, 120 240))) @@ -625,31 +590,30 @@ MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), - mA - nested shells, all vertices touch hole, partial overlap + mA - shell inside shell, all vertices touch (nested shells) -MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), - ((60 200, 340 60, 220 340, 60 200))) - +MULTIPOLYGON (((10 10, 20 30, 10 90, 90 90, 80 30, 90 10, 50 20, 10 10)), ((80 30, 20 30, 50 20, 80 30))) + false - + - mA - nested shells, all vertices touch hole, full overlap + mA - shell inside shell, hole overlapped, all vertices touch hole (self-intersection) MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), - (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), + (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200))) false - mA - duplicate shells + mA - shell inside shell, all vertices touch hole (nested shells) -MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), - ((60 300, 320 220, 260 60, 60 100, 60 300))) - +MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), + (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), + ((60 200, 340 60, 220 340, 60 200))) + false From 86cfeb8a10ffd15a1f293a0e554324d9e432b623 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 28 Jun 2021 15:25:19 -0700 Subject: [PATCH 044/275] Add TestValid.xml test, improve descriptions Signed-off-by: Martin Davis --- .../resources/testxml/general/TestValid.xml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index b10cbbc5d4..e65aa8ce66 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -414,7 +414,7 @@ POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320 false - A - puncture (self-intersection) + A - gore (self-intersection) POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60)) false @@ -425,13 +425,13 @@ POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320 - A - hole overlapping shell at non-vertex + A - hole crossing shell at non-vertex (self-intersection) POLYGON ((60 280, 260 180, 60 80, 60 280), (140 80, 120 180, 200 180, 140 80)) false - A - shell self-overlaps + A - shell self-overlaps (self-intersection) POLYGON ((60 340, 60 100, 340 100, 340 280, 340 200, 340 340, 60 340)) @@ -439,7 +439,7 @@ POLYGON ((60 340, 60 100, 340 100, 340 280, 340 200, 340 340, 60 340)) - A - hole outside but adjacent to shell + A - hole outside, adjacent (self-intersection) POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), (180 160, 240 60, 120 60, 180 160)) @@ -447,16 +447,23 @@ POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), - A - hole outside shell + A - hole outside, disjoint (hole outside shell) -POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), - (160 120, 180 100, 160 80, 160 120)) +POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (160 120, 180 100, 160 80, 160 120)) + + false + + + + A - hole outside, touches at all vertices (hole outside shell) + +POLYGON ((10 10, 30 10, 30 50, 70 50, 70 10, 90 10, 90 90, 10 90, 10 10), (50 50, 30 10, 70 10, 50 50)) false - A - hole identical to shell + A - hole identical to shell (self-intersection) POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (20 180, 20 20, 140 20, 140 180, 20 180)) @@ -465,7 +472,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), - A - hole self-touches at vertex-segment + A - hole self-touches at vertex-segment (self-intersection) POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300)) @@ -474,7 +481,7 @@ POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), - A - holes overlap, first point is identical + A - hole within hole, first point is identical (nested holes) POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), (140 280, 80 100, 200 100, 140 280), @@ -484,7 +491,7 @@ POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), - A - duplicate holes + A - duplicate holes (self-intersection) POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200), (120 180, 180 180, 180 120, 120 120, 120 180), (120 180, 180 180, 180 120, 120 120, 120 180)) From c2702c0ec469ab53ab893536b5a2bfee0347c9f4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 28 Jun 2021 16:50:16 -0700 Subject: [PATCH 045/275] Clean up TestValid.xml Signed-off-by: Martin Davis --- .../resources/testxml/general/TestValid.xml | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index e65aa8ce66..15924a222f 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -389,18 +389,6 @@ POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320 POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 0 0, 0 40)) false - - A - hole touches shell twice (interior disconnected) - POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40)) - false - - - A - hole touches hole twice (interior disconnected) - POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), - (100 100, 100 20, 120 20, 120 100, 100 100), - (20 100, 20 40, 100 40, 80 60, 100 80, 20 100)) - false - A - hole adjacent to hole (self-intersection) POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), @@ -418,11 +406,6 @@ POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320 POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60)) false - - A - hole within a hole (nested holes) - POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100)) - false - A - hole crossing shell at non-vertex (self-intersection) @@ -455,7 +438,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), (160 120, 180 100, 160 80, 16 - A - hole outside, touches at all vertices (hole outside shell) + A - hole outside, all vertices touch (hole outside shell) POLYGON ((10 10, 30 10, 30 50, 70 50, 70 10, 90 10, 90 90, 10 90, 10 10), (50 50, 30 10, 70 10, 50 50)) @@ -472,7 +455,7 @@ POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), - A - hole self-touches at vertex-segment (self-intersection) + A - hole self-touch at vertex-segment (self-intersection) POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300)) @@ -481,12 +464,26 @@ POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), - A - hole within hole, first point is identical (nested holes) + A - hole inside hole, disjoint (nested holes) + POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100)) + false + + + + A - hole inside hole, first point is identical (nested holes) POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), (140 280, 80 100, 200 100, 140 280), (140 280, 40 80, 240 80, 140 280)) - + + false + + + + A - hole inside hole, all vertices touch (nested holes) + +POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 80 80, 80 20, 20 20, 20 80), (50 80, 20 50, 50 20, 80 50, 50 80)) + false @@ -498,8 +495,14 @@ POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200), (120 180, 180 180, 180 1 false + + A - hole touches shell twice (interior disconnected) + POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40)) + false + + - A - chain of holes surrounds an area (interior disconnected) + A - chain of holes surrounds area (interior disconnected) POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), (120 240, 80 180, 160 220, 120 240), @@ -542,7 +545,7 @@ POLYGON( (0 0, 0 5, 6 5, 6 0, 0 0), (2 1, 4 1, 3 2, 2 1), (2 1, 1 4, 5 4, 4 1, 4 - A - hole touches hole at several vertices (interior disconnected) + A - hole touches hole at two vertices (interior disconnected) POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), (2.5 1, 3.5 1, 3.5 2, 2.5 2, 2.5 1), @@ -552,7 +555,7 @@ POLYGON ((0 0, 0 5, 6 5, 6 0, 0 0), - A - hole touches shell at all points (interior disconnected) + A - hole touches shell at all vertices (interior disconnected) POLYGON ((0 0, 10 10, 10 0, 0 0), (5 5, 5 0, 10 5, 5 5)) From 948f5ef7afa76390619d19bfcf3e852ba41908f0 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 30 Jun 2021 21:09:50 -0700 Subject: [PATCH 046/275] Fix imports Signed-off-by: Martin Davis --- .../java/org/locationtech/jts/operation/valid/IsValidOp.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 12035574e9..8f8269a3ce 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -11,14 +11,11 @@ */ package org.locationtech.jts.operation.valid; -import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; -import org.locationtech.jts.algorithm.locate.PointOnGeometryLocator; import org.locationtech.jts.geom.Coordinate; 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.Location; import org.locationtech.jts.geom.MultiPoint; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Point; From 0310498dae8fc57b9f553e75752dd160bc0fc7cb Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 5 Jul 2021 13:02:34 -0700 Subject: [PATCH 047/275] Add failure case for JTS-750 Signed-off-by: Martin Davis --- .../jts/algorithm/OrientationIndexFailureTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexFailureTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexFailureTest.java index e1e1d22cb2..12535d184e 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexFailureTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/OrientationIndexFailureTest.java @@ -53,6 +53,19 @@ public void testBadCCW() throws Exception checkOrientation(pts); } + // disabled beause it is a failure in the DD algorithm + /* + public void testJTS750() throws Exception + { + Coordinate[] pts = { + new Coordinate(0, 100), + new Coordinate(1, 102.1082), + new Coordinate(3, 106.3246), + }; + checkOrientation(pts); + } +*/ + public void testBadCCW2() throws Exception { // this case fails because subtraction of small from large loses precision From 05b807fb2ae6a2876ca68be7b74e184e768cc10d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 5 Jul 2021 13:32:21 -0700 Subject: [PATCH 048/275] Add IndexedNestedPolygonTester (#755) Signed-off-by: Martin Davis --- .../valid/IndexedNestedPolygonTester.java | 175 ++++++++++++++++++ .../jts/operation/valid/IsValidOp.java | 65 +------ 2 files changed, 182 insertions(+), 58 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java new file mode 100644 index 0000000000..5501a57ebb --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.operation.valid; + +import java.util.List; + +import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Location; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.index.SpatialIndex; +import org.locationtech.jts.index.strtree.STRtree; + +/** + * Tests whether a MultiPolygon has any element polygon + * nested inside another polygon, using a spatial + * index to speed up the comparisons. + *

    + * The logic assumes that the polygons do not overlap and have no collinear segments + * (so they are properly nested, and there are no duplicate rings). + */ +class IndexedNestedPolygonTester +{ + private MultiPolygon multiPoly; + private SpatialIndex index; + private IndexedPointInAreaLocator[] locators; + private Coordinate nestedPt; + + public IndexedNestedPolygonTester(MultiPolygon multiPoly) + { + this.multiPoly = multiPoly; + loadIndex(); + } + + private void loadIndex() + { + index = new STRtree(); + + for (int i = 0; i < multiPoly.getNumGeometries(); i++) { + Polygon poly = (Polygon) multiPoly.getGeometryN(i); + Envelope env = poly.getEnvelopeInternal(); + index.insert(env, i); + } + } + + private IndexedPointInAreaLocator getLocator(int polyIndex) { + if (locators == null) { + locators = new IndexedPointInAreaLocator[multiPoly.getNumGeometries()]; + } + IndexedPointInAreaLocator locator = locators[polyIndex]; + if (locator == null) { + locator = new IndexedPointInAreaLocator(multiPoly.getGeometryN(polyIndex)); + locators[polyIndex] = locator; + } + return locator; + } + + /** + * Gets a point on a nested polygon, if one exists. + * + * @return a point on a nested polygon, or null if none are nested + */ + public Coordinate getNestedPoint() { return nestedPt; } + + /** + * Tests if any polygon is nested (contained) within another polygon. + * This is invalid. + * The nested point will be set to reflect this. + * @return true if some polygon is nested + */ + public boolean isNested() + { + for (int i = 0; i < multiPoly.getNumGeometries(); i++) { + Polygon poly = (Polygon) multiPoly.getGeometryN(i); + LinearRing shell = poly.getExteriorRing(); + + List results = index.query(poly.getEnvelopeInternal()); + for (Integer polyIndex : results) { + Polygon possibleOuterPoly = (Polygon) multiPoly.getGeometryN(polyIndex); + + if (poly == possibleOuterPoly) + continue; + /** + * If polygon is not fully covered by candidate polygon it cannot be nested + */ + if (! possibleOuterPoly.getEnvelopeInternal().covers( poly.getEnvelopeInternal()) ) + continue; + + nestedPt = findNestedPoint(shell, possibleOuterPoly, getLocator(polyIndex)); + if (nestedPt != null) + return true; + } + } + return false; + } + + private Coordinate findNestedPoint(LinearRing shell, + Polygon possibleOuterPoly, IndexedPointInAreaLocator locator) + { + /** + * Try checking two points, since checking point location is fast. + */ + Coordinate shellPt0 = shell.getCoordinateN(0); + int loc0 = locator.locate(shellPt0); + if (loc0 == Location.EXTERIOR) return null; + if (loc0 == Location.INTERIOR) { + return shellPt0; + } + + Coordinate shellPt1 = shell.getCoordinateN(0); + int loc1 = locator.locate(shellPt1); + if (loc1 == Location.EXTERIOR) return null; + if (loc1 == Location.INTERIOR) { + return shellPt1; + } + + /** + * The shell points both lie on the boundary of + * the polygon. + * Nesting can be checked via the topology of the incident edges. + */ + return findSegmentInPolygon(shell, possibleOuterPoly); + } + + /** + * Finds a point of a shell segment which lies inside a polygon, if any. + * The shell is assume to touch the polyon only at shell vertices, + * and does not cross the polygon. + * + * @param the shell to test + * @param the polygon to test against + * @return an interior segment point, or null if the shell is nested correctly + */ + private static Coordinate findSegmentInPolygon(LinearRing shell, Polygon poly) + { + LinearRing polyShell = poly.getExteriorRing(); + if (polyShell.isEmpty()) return null; + + Coordinate shell0 = shell.getCoordinateN(0); + Coordinate shell1 = shell.getCoordinateN(1); + + if (! PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) + return null; + + /** + * Check if the shell is inside a hole (if there are any). + * If so this is valid. + */ + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + LinearRing hole = poly.getInteriorRingN(i); + if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) + && PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { + return null; + } + } + + /** + * The shell is contained in the polygon, but is not contained in a hole. + * This is invalid. + */ + return shell0; + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 8f8269a3ce..3dcf2dcb52 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -515,66 +515,15 @@ private void checkHolesNested(Polygon poly) */ private void checkShellsNested(MultiPolygon mp) { - for (int i = 0; i < mp.getNumGeometries(); i++) { - Polygon p = (Polygon) mp.getGeometryN(i); - if (p.isEmpty()) - continue; - LinearRing shell = p.getExteriorRing(); - for (int j = 0; j < mp.getNumGeometries(); j++) { - if (i == j) continue; - Polygon p2 = (Polygon) mp.getGeometryN(j); - Coordinate invalidPt = findShellSegmentInPolygon(shell, p2); - if (invalidPt != null) { - logInvalid(TopologyValidationError.NESTED_SHELLS, - invalidPt); - return; - } - } - } - } - - /** - * Finds a point of a shell segment which lies inside a polygon, if any. - * The shell is assume to touch the polyon only at shell vertices, - * and does not cross the polygon. - * - * @param the shell to test - * @param the polygon to test against - * @return an interior segment point, or null if the shell is nested correctly - */ - private Coordinate findShellSegmentInPolygon(LinearRing shell, Polygon poly) - { - LinearRing polyShell = poly.getExteriorRing(); - if (polyShell.isEmpty()) return null; - - //--- if envelope is not covered --> not nested - if (! poly.getEnvelopeInternal().covers(shell.getEnvelopeInternal())) - return null; + // skip test if only one shell present + if (mp.getNumGeometries() <= 1) return; - Coordinate shell0 = shell.getCoordinateN(0); - Coordinate shell1 = shell.getCoordinateN(1); - - if (! PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) - return null; - - /** - * Check if the shell is inside a hole (if there are any). - * If so this is valid. - */ - for (int i = 0; i < poly.getNumInteriorRing(); i++) { - LinearRing hole = poly.getInteriorRingN(i); - if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) - && PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { - return null; - } + IndexedNestedPolygonTester nestedTester = new IndexedNestedPolygonTester(mp); + if ( nestedTester.isNested() ) { + logInvalid(TopologyValidationError.NESTED_SHELLS, + nestedTester.getNestedPoint()); } - - /** - * The shell is contained in the polygon, but is not contained in a hole. - * This is invalid. - */ - return shell0; - } + } private void checkInteriorDisconnected(PolygonTopologyAnalyzer analyzer) { if (analyzer.isInteriorDisconnected()) { From 068e8ce845ed24a42be7073000ccaf7732b7e88e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 5 Jul 2021 13:32:54 -0700 Subject: [PATCH 049/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 6f1540d1f5..85e20404d3 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,7 +29,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717) -* Improve design and perforance of `IsValidOp` (#743 & #748) +* Improve design and perforance of `IsValidOp` (#743, #748, #755) * Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) ### Bug Fixes From 6782b2eaa3461f98dda819e804f41907cec17fc2 Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Tue, 6 Jul 2021 21:04:27 +0200 Subject: [PATCH 050/275] Fix IsSimpleOp to support FindAllLocations for all geometry types (#754) Fix IsSimpleOp to support FindAllLocations for all geometry types. Signed-off-by: Felix Obermaier --- .../jts/operation/valid/IsSimpleOp.java | 165 +++++++++--------- .../jts/operation/valid/IsSimpleTest.java | 20 ++- 2 files changed, 106 insertions(+), 79 deletions(-) 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 0640ae470a..26929ddc3a 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 @@ -45,21 +45,21 @@ *

  • MultiPoint geometries are simple if every point is unique *
  • LineString geometries are simple if they do not self-intersect at interior points * (i.e. points other than the endpoints). - *
  • MultiLineString geometries are simple if - * their elements are simple and they intersect only at points - * which are boundary points of both elements. + *
  • MultiLineString geometries are simple if + * their elements are simple and they intersect only at points + * which are boundary points of both elements. * (The notion of boundary points can be user-specified - see below). *
  • Polygonal geometries have no definition of simplicity. * The isSimple code checks if all polygon rings are simple. - * (Note: this means that isSimple cannot be used to test - * for all self-intersections in Polygons. + * (Note: this means that isSimple cannot be used to test + * for all self-intersections in Polygons. * In order to check if a Polygonal geometry has self-intersections, * use {@link Geometry#isValid()}). *
  • GeometryCollection geometries are simple if all their elements are simple. *
  • Empty geometries are simple * - * For {@link Lineal} geometries the evaluation of simplicity - * can be customized by supplying a {@link BoundaryNodeRule} + * For {@link Lineal} geometries the evaluation of simplicity + * can be customized by supplying a {@link BoundaryNodeRule} * to define how boundary points are determined. * The default is the SFS-standard {@link BoundaryNodeRule#MOD2_BOUNDARY_RULE}. *

    @@ -68,7 +68,7 @@ * This means that an intersection at their endpoints makes the geometry non-simple. * If it is required to test whether a set of LineStrings touch * only at their endpoints, use {@link BoundaryNodeRule#ENDPOINT_BOUNDARY_RULE}. - * For example, this can be used to validate that a collection of lines + * For example, this can be used to validate that a collection of lines * form a topologically valid linear network. *

    * By default this class finds a single non-simple location. @@ -76,7 +76,7 @@ * before calling {@link #isSimple(), and retrieve the locations * via {@link #getNonSimpleLocations(). * This can be used to find all intersection points in a linear network. - * + * * @see BoundaryNodeRule * @see Geometry#isValid() * @@ -86,7 +86,7 @@ public class IsSimpleOp { /** * Tests whether a geometry is simple. - * + * * @param geom the geometry to test * @return true if the geometry is simple */ @@ -94,22 +94,22 @@ public static boolean isSimple(Geometry geom) { IsSimpleOp op = new IsSimpleOp(geom); return op.isSimple(); } - + /** * Gets a non-simple location in a geometry, if any. - * + * * @param geom the input geometry * @return a non-simple location, or null if the geometry is simple */ - public static Coordinate getNonSimpleLcation(Geometry geom) { + public static Coordinate getNonSimpleLocation(Geometry geom) { IsSimpleOp op = new IsSimpleOp(geom); return op.getNonSimpleLocation(); } - - private Geometry inputGeom; - private boolean isClosedEndpointsInInterior = true; + + private final Geometry inputGeom; + private final boolean isClosedEndpointsInInterior; private boolean isFindAllLocations; - + private boolean isSimple = false; private List nonSimplePts; @@ -135,15 +135,15 @@ public IsSimpleOp(Geometry geom, BoundaryNodeRule boundaryNodeRule) } /** - * Sets whether all non-simple intersection points + * Sets whether all non-simple intersection points * will be found. - * + * * @param isFindAll whether to find all non-simple points */ public void setFindAllLocations(boolean isFindAll) { this.isFindAllLocations = isFindAll; } - + /** * Tests whether the geometry is simple. * @@ -154,10 +154,10 @@ public boolean isSimple() compute(); return isSimple; } - + /** * Gets the coordinate for an location where the geometry - * fails to be simple. + * fails to be simple. * (i.e. where it has a non-boundary self-intersection). * * @return a coordinate for the location of the non-boundary self-intersection @@ -169,10 +169,10 @@ public Coordinate getNonSimpleLocation() if (nonSimplePts.size() == 0) return null; return nonSimplePts.get(0); } - + /** * Gets all non-simple intersection locations. - * + * * @return a list of the coordinates of non-simple locations */ public List getNonSimpleLocations() @@ -180,13 +180,13 @@ public List getNonSimpleLocations() compute(); return nonSimplePts; } - + private void compute() { if (nonSimplePts != null) return; nonSimplePts = new ArrayList(); isSimple = computeSimple(inputGeom); } - + private boolean computeSimple(Geometry geom) { if (geom.isEmpty()) return true; @@ -203,54 +203,68 @@ private boolean computeSimple(Geometry geom) private boolean isSimpleMultiPoint(MultiPoint mp) { if (mp.isEmpty()) return true; + boolean isSimple = true; Set points = new HashSet(); for (int i = 0; i < mp.getNumGeometries(); i++) { Point pt = (Point) mp.getGeometryN(i); Coordinate p = pt.getCoordinate(); if (points.contains(p)) { nonSimplePts.add(p); - return false; + isSimple = false; + if (!isFindAllLocations) + break; } - points.add(p); + else + points.add(p); } - return true; + return isSimple; } /** * Computes simplicity for polygonal geometries. * Polygonal geometries are simple if and only if * all of their component rings are simple. - * + * * @param geom a Polygonal geometry * @return true if the geometry is simple */ private boolean isSimplePolygonal(Geometry geom) { + boolean isSimple = true; List rings = LinearComponentExtracter.getLines(geom); for (Geometry ring : rings) { if (! isSimpleLinearGeometry(ring)) - return false; + { + isSimple = false; + if (!isFindAllLocations) + break; + } } - return true; + return isSimple; } - + /** - * Semantics for GeometryCollection is + * Semantics for GeometryCollection is * simple iff all components are simple. - * - * @param geom + * + * @param geom a geometry collection * @return true if the geometry is simple */ private boolean isSimpleGeometryCollection(Geometry geom) { + boolean isSimple = true; for (int i = 0; i < geom.getNumGeometries(); i++ ) { Geometry comp = geom.getGeometryN(i); if (! computeSimple(comp)) - return false; + { + isSimple = false; + if (!isFindAllLocations) + break; + } } - return true; + return isSimple; } - + private boolean isSimpleLinearGeometry(Geometry geom) { if (geom.isEmpty()) return true; @@ -275,19 +289,14 @@ private static List extractSegmentStrings(Geometry geom) { return segStrings; } - private static class NonSimpleIntersectionFinder + private static class NonSimpleIntersectionFinder implements SegmentIntersector { - private boolean isClosedEndpointsInInterior; - private boolean isFindAll = false; - + private final boolean isClosedEndpointsInInterior; + private final boolean isFindAll; + LineIntersector li = new RobustLineIntersector(); - private List intersectionPts; - - private boolean hasInteriorInt; - private boolean hasInteriorVertexInt; - private boolean hasEqualSegments; - private boolean hasInteriorEndpointInt; + private final List intersectionPts; public NonSimpleIntersectionFinder(boolean isClosedEndpointsInInterior, boolean isFindAll, List intersectionPts) { this.isClosedEndpointsInInterior =isClosedEndpointsInInterior; @@ -297,75 +306,75 @@ public NonSimpleIntersectionFinder(boolean isClosedEndpointsInInterior, boolean /** * Tests whether an intersection was found. - * + * * @return true if an intersection was found */ - public boolean hasIntersection() - { - return intersectionPts.size() > 0; + public boolean hasIntersection() + { + return intersectionPts.size() > 0; } - + @Override public void processIntersections(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { - + // don't test a segment with itself boolean isSameSegString = ss0 == ss1; boolean isSameSegment = isSameSegString && segIndex0 == segIndex1; if (isSameSegment) return; - + boolean hasInt = findIntersection(ss0, segIndex0, ss1, segIndex1); - + if (hasInt) { // found an intersection! intersectionPts.add(li.getIntersection(0)); - } + } } - private boolean findIntersection(SegmentString ss0, int segIndex0, + private boolean findIntersection(SegmentString ss0, int segIndex0, SegmentString ss1, int segIndex1) { - + Coordinate p00 = ss0.getCoordinate(segIndex0); Coordinate p01 = ss0.getCoordinate(segIndex0 + 1); Coordinate p10 = ss1.getCoordinate(segIndex1); Coordinate p11 = ss1.getCoordinate(segIndex1 + 1); - + li.computeIntersection(p00, p01, p10, p11); if (! li.hasIntersection()) return false; - + /** * Check for an intersection in the interior of a segment. */ - hasInteriorInt = li.isInteriorIntersection(); + boolean hasInteriorInt = li.isInteriorIntersection(); if (hasInteriorInt) return true; - + /** * Check for equal segments (which will produce two intersection points). * These also intersect in interior points, so are non-simple. * (This is not triggered by zero-length segments, since they * are filtered out by the MC index). */ - hasEqualSegments = li.getIntersectionNum() >= 2; + boolean hasEqualSegments = li.getIntersectionNum() >= 2; if (hasEqualSegments) return true; - + /** * Following tests assume non-adjacent segments. */ boolean isSameSegString = ss0 == ss1; boolean isAdjacentSegment = isSameSegString && Math.abs(segIndex1 - segIndex0) <= 1; if (isAdjacentSegment) return false; - + /** - * At this point there is a single intersection point + * At this point there is a single intersection point * which is a vertex in each segString. * Classify them as endpoints or interior */ boolean isIntersectionEndpt0 = isIntersectionEndpoint(ss0, segIndex0, li, 0); boolean isIntersectionEndpt1 = isIntersectionEndpoint(ss1, segIndex1, li, 1); - - hasInteriorVertexInt = ! (isIntersectionEndpt0 && isIntersectionEndpt1); + + boolean hasInteriorVertexInt = ! (isIntersectionEndpt0 && isIntersectionEndpt1); if (hasInteriorVertexInt) return true; - - /** + + /** * Both intersection vertices must be endpoints. * Final check is if one or both of them is interior due * to being endpoint of a closed ring. @@ -373,7 +382,7 @@ private boolean findIntersection(SegmentString ss0, int segIndex0, * (which avoids reporting ring endpoints). */ if (isClosedEndpointsInInterior && !isSameSegString) { - hasInteriorEndpointInt = ss0.isClosed() || ss1.isClosed(); + boolean hasInteriorEndpointInt = ss0.isClosed() || ss1.isClosed(); if (hasInteriorEndpointInt) return true; } return false; @@ -381,14 +390,14 @@ private boolean findIntersection(SegmentString ss0, int segIndex0, /** * Tests whether an intersection vertex is an endpoint of a segment string. - * + * * @param ss the segmentString * @param ssIndex index of segment in segmentString * @param li the line intersector * @param liSegmentIndex index of segment in intersector * @return true if the intersection vertex is an endpoint */ - private static boolean isIntersectionEndpoint(SegmentString ss, int ssIndex, + private static boolean isIntersectionEndpoint(SegmentString ss, int ssIndex, LineIntersector li, int liSegmentIndex) { int vertexIndex = intersectionVertexIndex(li, liSegmentIndex); /** @@ -404,9 +413,9 @@ private static boolean isIntersectionEndpoint(SegmentString ss, int ssIndex, } /** - * Finds the vertex index in a segment of an intersection + * Finds the vertex index in a segment of an intersection * which is known to be a vertex. - * + * * @param li the line intersector * @param segmentIndex the intersection segment index * @return the vertex index (0 or 1) in the segment vertex of the intersection point @@ -416,7 +425,7 @@ private static int intersectionVertexIndex(LineIntersector li, int segmentIndex) Coordinate endPt0 = li.getEndpoint(segmentIndex, 0); return intPt.equals2D(endPt0) ? 0 : 1; } - + @Override public boolean isDone() { if (isFindAll) return false; diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java index a07806f6aa..0d4c402809 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsSimpleTest.java @@ -55,7 +55,7 @@ public void test2TouchAtEndpoint() throws Exception { /** * 3 LineStrings touching at an endpoint. - * + * * @throws Exception */ public void test3TouchAtEndpoint() throws Exception { @@ -104,6 +104,24 @@ public void testLinesAll() { "MULTIPOINT((50 20), (50 30))"); } + public void testPolygonAll() { + checkIsSimpleAll("POLYGON ((0 0, 7 0, 6 -1, 6 -0.1, 6 0.1, 3 5.9, 3 6.1, 3.1 6, 2.9 6, 0 0))", + BoundaryNodeRule.MOD2_BOUNDARY_RULE, + "MULTIPOINT((6 0), (3 6))"); + } + + public void testMultiPointAll() { + checkIsSimpleAll("MULTIPOINT((1 1), (1 2), (1 2), (1 3), (1 4), (1 4), (1 5), (1 5))", + BoundaryNodeRule.MOD2_BOUNDARY_RULE, + "MULTIPOINT((1 2), (1 4), (1 5))"); + } + public void testGeometryCollectionAll() { + checkIsSimpleAll("GEOMETRYCOLLECTION(MULTILINESTRING ((10 20, 90 20), (10 30, 90 30), (50 40, 50 10)), " + + "MULTIPOINT((1 1), (1 2), (1 2), (1 3), (1 4), (1 4), (1 5), (1 5)))", + BoundaryNodeRule.MOD2_BOUNDARY_RULE, + "MULTIPOINT((50 20), (50 30), (1 2), (1 4), (1 5))"); + } + private void checkIsSimple(String wkt, BoundaryNodeRule bnRule, boolean expectedResult) { checkIsSimple(wkt, bnRule, expectedResult, null); From 11aec88ca98ce5e827d1af2ba390a5a9acb23be6 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 6 Jul 2021 12:18:06 -0700 Subject: [PATCH 051/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 85e20404d3..752ce513f5 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -28,7 +28,7 @@ Distributions for older JTS versions can be obtained at the ### Functionality Improvements * Add `GeometryFixer` class (#704) -* Improve design and performance of `IsSimpleOp` (#717) +* Improve design and performance of `IsSimpleOp` (#717, #754) * Improve design and perforance of `IsValidOp` (#743, #748, #755) * Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) From 54559f013045e98cba3f0b54cd99f6a02f60d0c1 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 7 Jul 2021 16:50:45 -0700 Subject: [PATCH 052/275] Fix TestBuilder SVG-HtML output Signed-off-by: Martin Davis --- .../locationtech/jtstest/testbuilder/io/HtmlSvgTestWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlSvgTestWriter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlSvgTestWriter.java index 15694df1ef..7446df7ad3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlSvgTestWriter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlSvgTestWriter.java @@ -36,7 +36,7 @@ public HtmlSvgTestWriter() {} public String write(TestCaseList tcList) { StringBuilder sb = new StringBuilder(); - appendln(sb, ""); + appendln(sb, ""); writeStyles(sb); appendln(sb, ""); sb.append(defsMarkers()); From a394ab0a30becccc16eff262f3ffdefb9a792b99 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 13 Jul 2021 14:43:12 -0700 Subject: [PATCH 053/275] Fix IsValidOp to return RING_SELF_INTERSECTION (#756) Signed-off-by: Martin Davis --- .../valid/PolygonIntersectionAnalyzer.java | 14 ++-- .../jts/operation/valid/IsValidTest.java | 68 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java index 249e0c104b..3787aba98a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java @@ -108,6 +108,8 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, return NO_INVALID_INTERSECTION; } + boolean isSameSegString = ss0 == ss1; + /** * Check for an intersection in the interior of both segments. * Collinear intersections by definition contain an interior intersection. @@ -115,7 +117,7 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, * or adjacent rings. */ if (li.isProper() || li.getIntersectionNum() >= 2) { - return TopologyValidationError.SELF_INTERSECTION; + return selfIntersectionCode(isSameSegString); } /** @@ -129,7 +131,6 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, * (since they are not collinear). * This is valid. */ - boolean isSameSegString = ss0 == ss1; boolean isAdjacentSegments = isSameSegString && isAdjacentInRing(ss0, segIndex0, segIndex1); // Assert: intersection is an endpoint of both segs if (isAdjacentSegments) return NO_INVALID_INTERSECTION; @@ -139,7 +140,7 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, * So the intersection is invalid. */ if (isSameSegString && ! isInvertedRingValid) { - return TopologyValidationError.SELF_INTERSECTION; + return TopologyValidationError.RING_SELF_INTERSECTION; } /** @@ -169,7 +170,7 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, } boolean hasCrossing = PolygonNode.isCrossing(intPt, e00, e01, e10, e11); if (hasCrossing) { - return TopologyValidationError.SELF_INTERSECTION; + return selfIntersectionCode(isSameSegString); } /** @@ -197,6 +198,11 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, return NO_INVALID_INTERSECTION; } + private static int selfIntersectionCode(boolean isSameRing) { + return isSameRing ? TopologyValidationError.RING_SELF_INTERSECTION + : TopologyValidationError.SELF_INTERSECTION; + } + private boolean addDoubleTouch(SegmentString ss0, SegmentString ss1, Coordinate intPt) { return PolygonRing.addTouch((PolygonRing) ss0.getData(), (PolygonRing) ss1.getData(), intPt); } diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index 7f086999bc..fbeb29a0cc 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -50,111 +50,105 @@ public void testInvalidCoordinate() throws Exception assertEquals(false, valid); } - public void testZeroAreaPolygon() throws Exception { - Geometry g = reader.read( - "POLYGON((0 0, 0 0, 0 0, 0 0, 0 0))"); - g.isValid(); - assertTrue(true); //No exception thrown [Jon Aquino] + public void testZeroAreaPolygon() { + checkInvalid( "POLYGON((0 0, 0 0, 0 0, 0 0, 0 0))"); } - public void testValidSimplePolygon() throws Exception { - checkValid( - "POLYGON ((10 89, 90 89, 90 10, 10 10, 10 89))"); + public void testValidSimplePolygon() { + checkValid( "POLYGON ((10 89, 90 89, 90 10, 10 10, 10 89))"); } - public void testInvalidSimplePolygonRingSelfIntersection() throws Exception { - checkInvalid( TopologyValidationError.SELF_INTERSECTION, + public void testInvalidSimplePolygonRingSelfIntersection() { + checkInvalid( TopologyValidationError.RING_SELF_INTERSECTION, "POLYGON ((10 90, 90 10, 90 90, 10 10, 10 90))"); } - public void testSimplePolygonHole() throws Exception { + public void testInvalidPolygonInverted() { + checkInvalid( TopologyValidationError.RING_SELF_INTERSECTION, + "POLYGON ((70 250, 40 500, 100 400, 70 250, 80 350, 60 350, 70 250))"); + } + + public void testSimplePolygonHole() { checkValid( "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (60 20, 20 70, 90 90, 60 20))"); } - public void testPolygonTouchingHoleAtVertex() throws Exception { + public void testPolygonTouchingHoleAtVertex() { checkValid( "POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), (140 180, 40 260, 140 240, 140 180))"); } - public void testPolygonMultipleHolesTouchAtSamePoint() throws Exception { + public void testPolygonMultipleHolesTouchAtSamePoint() { checkValid( "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 60, 20 40, 50 50, 20 60), (40 20, 60 20, 50 50, 40 20))"); } - public void testPolygonHoleOutsideShellAllTouch() throws Exception { + public void testPolygonHoleOutsideShellAllTouch() { checkInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, "POLYGON ((10 10, 30 10, 30 50, 70 50, 70 10, 90 10, 90 90, 10 90, 10 10), (50 50, 30 10, 70 10, 50 50))"); } - public void testPolygonHoleOutsideShellDoubleTouch() throws Exception { + public void testPolygonHoleOutsideShellDoubleTouch() { checkInvalid(TopologyValidationError.HOLE_OUTSIDE_SHELL, "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 80 80, 80 20, 20 20, 20 80), (90 70, 150 50, 90 20, 110 40, 90 70))"); } - public void testPolygonNestedHolesAllTouch() throws Exception { + public void testPolygonNestedHolesAllTouch() { checkInvalid(TopologyValidationError.NESTED_HOLES, "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 80 80, 80 20, 20 20, 20 80), (50 80, 80 50, 50 20, 20 50, 50 80))"); } - public void testInvalidPolygonHoleProperIntersection() throws Exception { + public void testInvalidPolygonHoleProperIntersection() { checkInvalid( TopologyValidationError.SELF_INTERSECTION, "POLYGON ((10 90, 50 50, 10 10, 10 90), (20 50, 60 70, 60 30, 20 50))"); } - public void testInvalidPolygonDisconnectedInterior() throws Exception { + public void testInvalidPolygonDisconnectedInterior() { checkInvalid( TopologyValidationError.DISCONNECTED_INTERIOR, "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 30 80, 20 20, 20 80), (80 30, 20 20, 80 20, 80 30), (80 80, 30 80, 80 30, 80 80))"); } - public void testValidMultiPolygonTouchAtVertices() throws Exception { + public void testValidMultiPolygonTouchAtVertices() { checkValid( "MULTIPOLYGON (((10 10, 10 90, 90 90, 90 10, 80 80, 50 20, 20 80, 10 10)), ((90 10, 10 10, 50 20, 90 10)))"); } - public void testInvalidMultiPolygonHoleOverlapCrossing() throws Exception { + public void testInvalidMultiPolygonHoleOverlapCrossing() { checkInvalid( TopologyValidationError.SELF_INTERSECTION, "MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200)))"); } - public void testValidMultiPolygonTouchAtVerticesSegments() throws Exception { + public void testValidMultiPolygonTouchAtVerticesSegments() { checkValid( "MULTIPOLYGON (((60 40, 90 10, 90 90, 10 90, 10 10, 40 40, 60 40)), ((50 40, 20 20, 80 20, 50 40)))"); } - public void testInvalidMultiPolygonNestedAllTouchAtVertices() throws Exception { + public void testInvalidMultiPolygonNestedAllTouchAtVertices() { checkInvalid( TopologyValidationError.NESTED_SHELLS, "MULTIPOLYGON (((10 10, 20 30, 10 90, 90 90, 80 30, 90 10, 50 20, 10 10)), ((80 30, 20 30, 50 20, 80 30)))"); } - public void testValidMultiPolygonHoleTouchVertices() throws Exception { + public void testValidMultiPolygonHoleTouchVertices() { checkValid( "MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), ((60 200, 340 60, 220 340, 60 200)))"); } public void testLineString() { - Geometry g = read( - "LINESTRING(0 0, 0 0)"); - g.isValid(); - assertTrue(true); //No exception thrown [Jon Aquino] + checkInvalid( "LINESTRING(0 0, 0 0)"); } - public void testLinearRingTriangle() throws Exception { - Geometry g = reader.read( - "LINEARRING (100 100, 150 200, 200 100, 100 100)"); - assertTrue(g.isValid()); + public void testLinearRingTriangle() { + checkValid( "LINEARRING (100 100, 150 200, 200 100, 100 100)"); } - public void testLinearRingSelfCrossing() throws Exception { - Geometry g = reader.read( + public void testLinearRingSelfCrossing() { + checkInvalid( TopologyValidationError.RING_SELF_INTERSECTION, "LINEARRING (150 100, 300 300, 100 300, 350 100, 150 100)"); - assertTrue(! g.isValid()); } - public void testLinearRingSelfCrossing2() throws Exception { - Geometry g = reader.read( + public void testLinearRingSelfCrossing2() { + checkInvalid( TopologyValidationError.RING_SELF_INTERSECTION, "LINEARRING (0 0, 100 100, 100 0, 0 100, 0 0)"); - assertTrue(! g.isValid()); } //============================================= From c9fc4ca22f6ac0cc62b5ec889e5adb6cd280b434 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 13 Jul 2021 14:44:55 -0700 Subject: [PATCH 054/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 752ce513f5..967dff646b 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,7 +29,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717, #754) -* Improve design and perforance of `IsValidOp` (#743, #748, #755) +* Improve design and perforance of `IsValidOp` (#743, #748, #755, #756) * Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) ### Bug Fixes From 150ceade16f8893a89b05a67015fe2259d6cdbd7 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 14 Jul 2021 08:34:09 -0700 Subject: [PATCH 055/275] Fine-tune IsValidOp Ring Self-Intersection reporting (#757) * Fine-tune IsValidOp Ring Self-Intersection reporting Signed-off-by: Martin Davis --- .../valid/PolygonIntersectionAnalyzer.java | 13 ++++--------- .../jts/operation/valid/IsValidTest.java | 7 ++++++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java index 3787aba98a..3db2100219 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonIntersectionAnalyzer.java @@ -113,11 +113,9 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, /** * Check for an intersection in the interior of both segments. * Collinear intersections by definition contain an interior intersection. - * They occur in either a zero-width spike or gore, - * or adjacent rings. */ if (li.isProper() || li.getIntersectionNum() >= 2) { - return selfIntersectionCode(isSameSegString); + return TopologyValidationError.SELF_INTERSECTION; } /** @@ -138,6 +136,8 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, /** * Under OGC semantics, rings cannot self-intersect. * So the intersection is invalid. + * + * The return of RING_SELF_INTERSECTION is to match the previous IsValid semantics. */ if (isSameSegString && ! isInvertedRingValid) { return TopologyValidationError.RING_SELF_INTERSECTION; @@ -170,7 +170,7 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, } boolean hasCrossing = PolygonNode.isCrossing(intPt, e00, e01, e10, e11); if (hasCrossing) { - return selfIntersectionCode(isSameSegString); + return TopologyValidationError.SELF_INTERSECTION; } /** @@ -198,11 +198,6 @@ private int findInvalidIntersection(SegmentString ss0, int segIndex0, return NO_INVALID_INTERSECTION; } - private static int selfIntersectionCode(boolean isSameRing) { - return isSameRing ? TopologyValidationError.RING_SELF_INTERSECTION - : TopologyValidationError.SELF_INTERSECTION; - } - private boolean addDoubleTouch(SegmentString ss0, SegmentString ss1, Coordinate intPt) { return PolygonRing.addTouch((PolygonRing) ss0.getData(), (PolygonRing) ss1.getData(), intPt); } diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index fbeb29a0cc..5dc904da4e 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -59,7 +59,7 @@ public void testValidSimplePolygon() { } public void testInvalidSimplePolygonRingSelfIntersection() { - checkInvalid( TopologyValidationError.RING_SELF_INTERSECTION, + checkInvalid( TopologyValidationError.SELF_INTERSECTION, "POLYGON ((10 90, 90 10, 90 90, 10 10, 10 90))"); } @@ -68,6 +68,11 @@ public void testInvalidPolygonInverted() { "POLYGON ((70 250, 40 500, 100 400, 70 250, 80 350, 60 350, 70 250))"); } + public void testInvalidPolygonSelfCrossing() { + checkInvalid( TopologyValidationError.SELF_INTERSECTION, + "POLYGON ((70 250, 70 500, 80 400, 40 400, 70 250))"); + } + public void testSimplePolygonHole() { checkValid( "POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (60 20, 20 70, 90 90, 60 20))"); From a860aff52721d864717a31d95f11a6fe944f8fc9 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 14 Jul 2021 08:35:04 -0700 Subject: [PATCH 056/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 967dff646b..b97a78d183 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,7 +29,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` class (#704) * Improve design and performance of `IsSimpleOp` (#717, #754) -* Improve design and perforance of `IsValidOp` (#743, #748, #755, #756) +* Improve design and perforance of `IsValidOp` (#743, #748, #755, #756, #757) * Fix `SortedPackedIntervalRtree` to be thread-safe (fixes `PreparedPolygon` too) (#746) ### Bug Fixes From 6cf20ff39259fd52cfb4eb41e682769a66c46f3c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 14 Jul 2021 08:40:55 -0700 Subject: [PATCH 057/275] Javadoc Signed-off-by: Martin Davis --- .../java/org/locationtech/jts/operation/valid/IsValidOp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 3dcf2dcb52..5dee0eefff 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -89,8 +89,8 @@ public IsValidOp(Geometry inputGeometry) * If this flag is set, the following Self-Touching conditions * are treated as being valid: *

      - *
    • the shell ring self-touches to create a hole touching the shell - *
    • a hole ring self-touches to create two holes touching at a point + *
    • inverted shell - the shell ring self-touches to create a hole touching the shell + *
    • exverted hole - a hole ring self-touches to create two holes touching at a point *
    *

    * The default (following the OGC SFS standard) From 080cb200733d7d73b384829a4df38db66b280fba Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Thu, 12 Aug 2021 11:41:45 -0700 Subject: [PATCH 058/275] Update oracle to public jdbc8 19.10.0.0 driver and fix XY and XYZM test failures (#766) * Fix oracle build * Fix up oracle tests, can now be run even without local oracle database Any tests that require an oracle connection are skipped, this was good enough to notice functionality that has improved now that XYZM coordiante sequences are available. Signed-off-by: Jody Garnett --- modules/io/ora/pom.xml | 17 +++++++--- .../jts/io/oracle/ConnectedTestCase.java | 23 +++++++++----- .../jts/io/oracle/OraWriterCreateTest.java | 4 +-- .../jts/io/oracle/OraWriterSQLTest.java | 2 +- .../jts/io/oracle/StaticLineStringTest.java | 12 +++++++ .../io/oracle/StaticMultiLineStringTest.java | 13 +++++++- .../jts/io/oracle/StaticMultiPointTest.java | 9 ++++++ .../jts/io/oracle/StaticMultiPolygonTest.java | 31 ++++++++++++++++--- .../jts/io/oracle/StaticPointTest.java | 9 ++++++ .../jts/io/oracle/StaticPolygonTest.java | 25 +++++++++++++++ .../jts/io/oracle/connection.properties | 1 + modules/io/sde/pom.xml | 9 +++++- pom.xml | 6 ++-- 13 files changed, 136 insertions(+), 25 deletions(-) rename modules/io/ora/src/test/{java => resources}/org/locationtech/jts/io/oracle/connection.properties (84%) diff --git a/modules/io/ora/pom.xml b/modules/io/ora/pom.xml index 1b19c18181..b0dc49bd6e 100644 --- a/modules/io/ora/pom.xml +++ b/modules/io/ora/pom.xml @@ -4,7 +4,7 @@ org.locationtech.jts jts-io - 1.18.0-SNAPSHOT + 1.18.2-SNAPSHOT org.locationtech.jts.io jts-io-ora @@ -35,10 +35,17 @@ - - com.oracle - ojdbc7 - + + org.locationtech.jts + jts-core + ${project.version} + tests + test + + + com.oracle.database.jdbc + ojdbc8 + provided diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/ConnectedTestCase.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/ConnectedTestCase.java index de819ef7a5..f572108363 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/ConnectedTestCase.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/ConnectedTestCase.java @@ -53,22 +53,29 @@ protected OracleConnection getConnection() return connection; } + + /** * @see junit.framework.TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); - + Properties props = new Properties(); - URL path = ClassLoader.getSystemResource("com/vividsolutions/jts/io/oracle/connection.properties"); + URL path = ClassLoader.getSystemResource("org/locationtech/jts/io/oracle/connection.properties"); + if( path == null ) { + return; + } props.load(path.openStream()); - + if( "TRUE".equalsIgnoreCase(props.getProperty("test.skip"))) { + return; + } connection = getOracleConnection( - props.getProperty("test.server"), - props.getProperty("test.port"), - props.getProperty("test.sid"), - props.getProperty("test.user"), - props.getProperty("test.pwd")); + props.getProperty("test.server"), + props.getProperty("test.port"), + props.getProperty("test.sid"), + props.getProperty("test.user"), + props.getProperty("test.pwd")); } private static OracleConnection getOracleConnection(String server, String port, String sid, String userid, String pwd) throws SQLException { diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterCreateTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterCreateTest.java index 454bc3560a..be45653109 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterCreateTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterCreateTest.java @@ -63,12 +63,12 @@ public void TODO_testXYZM_Point() throws Exception { public void testXYZM_Point() throws Exception { OraGeom oraGeom = MDSYS.SDO_GEOMETRY(4001,0,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,1),MDSYS.SDO_ORDINATE_ARRAY(50,50,DNULL,DNULL)); - checkValue(oraGeom, 4, "POINT (50 50)"); + checkValue(oraGeom, 4, "POINT ZM(50 50 NaN NaN)"); } public void testXYZM_Point_SetDim() throws Exception { OraGeom oraGeom = MDSYS.SDO_GEOMETRY(4001,0,NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1,1),MDSYS.SDO_ORDINATE_ARRAY(50,50,DNULL,DNULL)); - checkValue(oraGeom, false, false, 4, "POINT (50 50)"); + checkValue(oraGeom, false, false, 4, "POINT ZM(50 50 NaN NaN)"); } public void testXYZ_PointType() throws Exception { diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterSQLTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterSQLTest.java index 69b6bea7be..20f55fd886 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterSQLTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/OraWriterSQLTest.java @@ -44,7 +44,7 @@ public void testPointType() throws Exception { public void testXYZ_LineString() throws Exception { checkValue("LINESTRING (0 0 0, 50 50 100)", - "SDO_GEOMETRY(2002,0,NULL,SDO_ELEM_INFO_ARRAY(1,2,1),SDO_ORDINATE_ARRAY(0,0, 50,50))"); + "SDO_GEOMETRY(3002,0,NULL,SDO_ELEM_INFO_ARRAY(1,2,1),SDO_ORDINATE_ARRAY(0,0,0, 50,50,100))"); } //============================================================ diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticLineStringTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticLineStringTest.java index 2cf8d54165..ddede1b386 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticLineStringTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticLineStringTest.java @@ -47,6 +47,9 @@ public StaticLineStringTest(String arg) { * @throws SQLException */ public void testSingleLineStringRoundTrip() throws SQLException{ + if(getConnection()==null){ + return; // skip + } LineStringGenerator pg = new LineStringGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -70,6 +73,9 @@ public void testSingleLineStringRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridLineStringsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -108,6 +114,9 @@ public void testGridLineStringsRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingLineStringsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -150,6 +159,9 @@ public void testOverlappingLineStringsRoundTrip() throws SQLException{ * @throws SQLException */ public void testSingleLineStringManyPointRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } LineStringGenerator pg = new LineStringGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiLineStringTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiLineStringTest.java index f2091ede5a..fa6dedd31d 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiLineStringTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiLineStringTest.java @@ -47,6 +47,9 @@ public StaticMultiLineStringTest(String arg) { * @throws SQLException */ public void testSingleMultiLineStringRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } LineStringGenerator pgc = new LineStringGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setNumberPoints(10); @@ -73,6 +76,9 @@ public void testSingleMultiLineStringRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridMultiLineStringsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -116,6 +122,9 @@ public void testGridMultiLineStringsRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingMultiLineStringsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -163,7 +172,9 @@ public void testOverlappingMultiLineStringsRoundTrip() throws SQLException{ * @throws SQLException */ public void testSingleMultiLineStringManyPointRoundTrip() throws SQLException{ - + if(getConnection()==null){ + return; // skip + } LineStringGenerator pgc = new LineStringGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setNumberPoints(1000); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPointTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPointTest.java index 1f6d0bed1c..4a6c02b15e 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPointTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPointTest.java @@ -43,6 +43,9 @@ public StaticMultiPointTest(String arg) { * @throws SQLException */ public void testSingleMultiPointRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } PointGenerator pgc = new PointGenerator(); pgc.setGeometryFactory(geometryFactory); MultiGenerator pg = new MultiGenerator(pgc); @@ -68,6 +71,9 @@ public void testSingleMultiPointRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridMultiPointsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -110,6 +116,9 @@ public void testGridMultiPointsRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingMultiPointsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPolygonTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPolygonTest.java index 378ec37457..fd0d78d891 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPolygonTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticMultiPolygonTest.java @@ -49,6 +49,9 @@ public StaticMultiPolygonTest(String arg) { * @throws SQLException */ public void testSingleMultiPolygonNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } PolygonGenerator pgc = new PolygonGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setNumberPoints(10); @@ -75,6 +78,9 @@ public void testSingleMultiPolygonNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridMultiPolygonsNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -118,6 +124,9 @@ public void testGridMultiPolygonsNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingMultiPolygonsNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -165,7 +174,9 @@ public void testOverlappingMultiPolygonsNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testSingleMultiPolygonManyPointsNoHoleRoundTrip() throws SQLException{ - + if( getConnection() == null) { + return; // skip + } PolygonGenerator pgc = new PolygonGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setGenerationAlgorithm(PolygonGenerator.BOX); @@ -194,7 +205,9 @@ public void testSingleMultiPolygonManyPointsNoHoleRoundTrip() throws SQLExceptio * @throws SQLException */ public void testSingleMultiPolygonHolesRoundTrip() throws SQLException{ - + if( getConnection() == null) { + return; // skip + } PolygonGenerator pgc = new PolygonGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setGenerationAlgorithm(PolygonGenerator.BOX); @@ -223,6 +236,9 @@ public void testSingleMultiPolygonHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridMultiPolygonsHolesRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -269,6 +285,9 @@ public void testGridMultiPolygonsHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingMultiPolygonsHolesRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -319,7 +338,9 @@ public void testOverlappingMultiPolygonsHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testSingleMultiPolygonManyPointsHolesRoundTrip() throws SQLException{ - + if( getConnection() == null) { + return; // skip + } PolygonGenerator pgc = new PolygonGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setGenerationAlgorithm(PolygonGenerator.BOX); @@ -349,7 +370,9 @@ public void testSingleMultiPolygonManyPointsHolesRoundTrip() throws SQLException * @throws SQLException */ public void testSingleMultiPolygonManyPointsManyHolesRoundTrip() throws SQLException{ - + if( getConnection() == null) { + return; // skip + } PolygonGenerator pgc = new PolygonGenerator(); pgc.setGeometryFactory(geometryFactory); pgc.setGenerationAlgorithm(PolygonGenerator.BOX); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPointTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPointTest.java index 019f5689cf..ce10161040 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPointTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPointTest.java @@ -44,6 +44,9 @@ public StaticPointTest(String arg) { * @throws SQLException */ public void testSinglePointRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } PointGenerator pg = new PointGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -66,6 +69,9 @@ public void testSinglePointRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridPointsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -103,6 +109,9 @@ public void testGridPointsRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingPointsRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPolygonTest.java b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPolygonTest.java index ac3296bd1a..e048b3348e 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPolygonTest.java +++ b/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/StaticPolygonTest.java @@ -49,6 +49,9 @@ public StaticPolygonTest(String arg) { * @throws SQLException */ public void testSinglePolygonNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } PolygonGenerator pg = new PolygonGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -72,6 +75,9 @@ public void testSinglePolygonNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridPolygonsNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -110,6 +116,9 @@ public void testGridPolygonsNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingPolygonsNoHoleRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -152,6 +161,9 @@ public void testOverlappingPolygonsNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testSinglePolygonManyPointsNoHoleRoundTrip() throws SQLException{ + if(getConnection()==null){ + return; // skip + } PolygonGenerator pg = new PolygonGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -177,6 +189,9 @@ public void testSinglePolygonManyPointsNoHoleRoundTrip() throws SQLException{ * @throws SQLException */ public void testSinglePolygonHolesRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } PolygonGenerator pg = new PolygonGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -201,6 +216,9 @@ public void testSinglePolygonHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testGridPolygonsHolesRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -240,6 +258,9 @@ public void testGridPolygonsHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testOverlappingPolygonsHolesRoundTrip() throws SQLException{ + if( getConnection() == null) { + return; // skip + } GridGenerator grid = new GridGenerator(); grid.setGeometryFactory(geometryFactory); grid.setBoundingBox(new Envelope(0,10,0,10)); @@ -283,6 +304,7 @@ public void testOverlappingPolygonsHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testSinglePolygonManyPointsHolesRoundTrip() throws SQLException{ + if( this.getConnection() == null ) return; PolygonGenerator pg = new PolygonGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); @@ -309,6 +331,9 @@ public void testSinglePolygonManyPointsHolesRoundTrip() throws SQLException{ * @throws SQLException */ public void testSinglePolygonManyPointsManyHolesRoundTrip() throws SQLException{ + if(getConnection()==null){ + return; // skip + } PolygonGenerator pg = new PolygonGenerator(); pg.setGeometryFactory(geometryFactory); pg.setBoundingBox(new Envelope(0,10,0,10)); diff --git a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/connection.properties b/modules/io/ora/src/test/resources/org/locationtech/jts/io/oracle/connection.properties similarity index 84% rename from modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/connection.properties rename to modules/io/ora/src/test/resources/org/locationtech/jts/io/oracle/connection.properties index febdd24d84..2aa32ca6fd 100644 --- a/modules/io/ora/src/test/java/org/locationtech/jts/io/oracle/connection.properties +++ b/modules/io/ora/src/test/resources/org/locationtech/jts/io/oracle/connection.properties @@ -3,3 +3,4 @@ test.port=1521 test.sid=DEV test.user=cisd test.pwd=password +test.skip=true \ No newline at end of file diff --git a/modules/io/sde/pom.xml b/modules/io/sde/pom.xml index fae9646669..cca6db82b0 100644 --- a/modules/io/sde/pom.xml +++ b/modules/io/sde/pom.xml @@ -4,7 +4,7 @@ org.locationtech.jts jts-io - 1.18.0-SNAPSHOT + 1.18.2-SNAPSHOT org.locationtech.jts.io jts-io-sde @@ -45,6 +45,13 @@ + + org.locationtech.jts + jts-core + ${project.version} + tests + test + com.esri jsde_sdk diff --git a/pom.xml b/pom.xml index 314d8bc2aa..4e0ad10e11 100644 --- a/pom.xml +++ b/pom.xml @@ -112,9 +112,9 @@ 3.7 - com.oracle - ojdbc7 - 11.1.0.7.0 + com.oracle.database.jdbc + ojdbc8 + 19.10.0.0 From 539ef272f20507cfd666f5c219f9af150b593604 Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Thu, 12 Aug 2021 16:33:03 -0700 Subject: [PATCH 059/275] introduce pmd and checkstyle build-tools and tone down javadoc failures (#762) * introduce pmd and checkstyle build-tools and tone down javadoc failures * Update developing instructions * Start off using checkstyle to ensure the presense of JTS header * Fix pmd error parsing TestBufferFailure.xml * Address PMD failure for useless override * address pmd failure unused imports * Address PMD failure for loop that should be whilte * GeometryInspectorDialog was ignoring frame parameter * refactor package.html to package-info.java * Use properties to control lint and doclint Signed-off-by: Jody Garnett --- .gitignore | 8 +- DEVELOPING.md | 144 +- build-tools/pom.xml | 47 + .../src/main/resources/jts/checkstyle.xml | 26 + build-tools/src/main/resources/jts/header.txt | 11 + .../src/main/resources/jts/pmd-ruleset.xml | 111 + .../src/main/resources/jts/suppressions.xml | 9 + modules/app/pom.xml | 3 +- .../jtstest/clean/CleanDuplicatePoints.java | 1 - .../jtstest/cmd/CommandError.java | 11 + .../jtstest/cmd/CommandOutput.java | 11 + .../jtstest/cmd/GeometryOutput.java | 11 + .../org/locationtech/jtstest/cmd/WKBDump.java | 11 + .../jtstest/function/DiffFunctions.java | 11 + .../jtstest/function/LabellingFunctions.java | 11 + .../function/LineHandlingFunctions.java | 3 - .../function/OrientationFunctions.java | 11 + .../function/PointLocationFunctions.java | 11 + .../SpreaderGeometryFunction.java | 11 + .../locationtech/jtstest/test/TestCase.java | 2 - .../jtstest/test/TestCaseList.java | 2 - .../locationtech/jtstest/test/Testable.java | 2 - .../jtstest/testbuilder/AppColors.java | 11 + .../jtstest/testbuilder/AppIcons.java | 11 + .../jtstest/testbuilder/FunctionPanel.java | 11 + .../GeometryFunctionListPanel.java | 1 - .../testbuilder/GeometryInspectorDialog.java | 2 +- .../testbuilder/GeometryTreePanel.java | 2 - .../testbuilder/GeometryViewStylePanel.java | 11 + .../testbuilder/LabelComponentsPanel.java | 11 + .../testbuilder/PrecisionModelDialog.java | 2 - .../testbuilder/PrecisionModelPanel.java | 2 - .../jtstest/testbuilder/RelatePanel.java | 2 - .../testbuilder/TestBuilderDialogs.java | 11 + .../jtstest/testbuilder/ValidPanel.java | 1 - .../testbuilder/event/ValidPanelEvent.java | 1 - .../testbuilder/event/ValidPanelListener.java | 1 - .../testbuilder/geom/EnvelopeClipper.java | 11 + .../testbuilder/geom/SegmentClipper.java | 11 + .../testbuilder/geom/SegmentExtracter.java | 11 + .../jtstest/testbuilder/io/HtmlUtil.java | 11 + .../jtstest/testbuilder/io/HtmlWriter.java | 2 - .../jtstest/testbuilder/io/IOUtil.java | 11 + .../jtstest/testbuilder/io/PNGWriter.java | 2 - .../shapefile/InvalidShapefileException.java | 17 +- .../io/shapefile/MultiLineHandler.java | 25 +- .../io/shapefile/MultiPointHandler.java | 18 +- .../io/shapefile/PointHandler.java | 17 +- .../io/shapefile/PolygonHandler.java | 17 +- .../io/shapefile/ShapeHandler.java | 17 +- .../testbuilder/io/shapefile/Shapefile.java | 17 +- .../io/shapefile/ShapefileException.java | 17 +- .../io/shapefile/ShapefileHeader.java | 17 +- .../testbuilder/model/TestCaseEdit.java | 2 - .../model/TestRunnerTestCaseAdapter.java | 2 - .../jtstest/testbuilder/model/UndoBuffer.java | 11 + .../jtstest/testbuilder/ui/dnd/FileDrop.java | 2 +- .../testbuilder/ui/render/DrawingGrid.java | 1 - .../testbuilder/ui/render/RenderManager.java | 2 +- .../ui/style/LineLabelBaseline.java | 11 + .../jtstest/testbuilder/ui/style/Palette.java | 11 + .../testbuilder/ui/style/StyleGroup.java | 11 + .../testbuilder/ui/tools/BoxBandTool.java | 2 - .../testbuilder/ui/tools/ZoomTool.java | 2 - .../locationtech/jtstest/util/HSBPalette.java | 11 + .../jtstest/TestFileGeometryExtractor.java | 1 - modules/core/pom.xml | 9 +- .../org/locationtech/jts/algorithm/Angle.java | 4 +- .../org/locationtech/jts/algorithm/Area.java | 4 +- .../jts/algorithm/BoundaryNodeRule.java | 2 +- .../jts/algorithm/CGAlgorithms.java | 93 +- .../jts/algorithm/CGAlgorithms3D.java | 24 +- .../jts/algorithm/CGAlgorithmsDD.java | 35 +- .../locationtech/jts/algorithm/Centroid.java | 20 +- .../jts/algorithm/ConvexHull.java | 2 - .../jts/algorithm/InteriorPointArea.java | 1 - .../jts/algorithm/InteriorPointLine.java | 1 - .../jts/algorithm/InteriorPointPoint.java | 1 - .../jts/algorithm/MinimumBoundingCircle.java | 18 +- .../jts/algorithm/MinimumDiameter.java | 1 - .../algorithm/NotRepresentableException.java | 3 - .../jts/algorithm/PointLocation.java | 2 +- .../jts/algorithm/RobustDeterminant.java | 2 - .../jts/algorithm/RobustLineIntersector.java | 3 +- .../construct/MaximumInscribedCircle.java | 1 + .../jts/algorithm/construct/package-info.java | 16 + .../jts/algorithm/construct/package.html | 13 - .../jts/algorithm/distance/package-info.java | 16 + .../jts/algorithm/distance/package.html | 13 - .../locate/IndexedPointInAreaLocator.java | 2 +- .../locate/SimplePointInAreaLocator.java | 20 +- .../jts/algorithm/locate/package-info.java | 16 + .../jts/algorithm/locate/package.html | 13 - .../jts/algorithm/match/package-info.java | 16 + .../jts/algorithm/match/package.html | 13 - .../jts/algorithm/package-info.java | 46 + .../locationtech/jts/algorithm/package.html | 50 - .../org/locationtech/jts/awt/ShapeWriter.java | 2 +- .../locationtech/jts/awt/package-info.java | 16 + .../org/locationtech/jts/awt/package.html | 13 - .../jts/densify/package-info.java | 16 + .../org/locationtech/jts/densify/package.html | 13 - .../jts/dissolve/LineDissolver.java | 8 +- .../locationtech/jts/edgegraph/HalfEdge.java | 4 +- .../org/locationtech/jts/geom/Coordinate.java | 14 +- .../jts/geom/CoordinateArrays.java | 4 +- .../jts/geom/CoordinateFilter.java | 2 - .../locationtech/jts/geom/CoordinateList.java | 8 +- .../jts/geom/CoordinateSequence.java | 17 +- .../jts/geom/CoordinateSequenceFilter.java | 2 - .../jts/geom/CoordinateSequences.java | 12 +- .../locationtech/jts/geom/CoordinateXYM.java | 2 +- .../org/locationtech/jts/geom/Dimension.java | 2 - .../jts/geom/GeometryCollection.java | 2 - .../jts/geom/GeometryCollectionIterator.java | 2 - .../jts/geom/GeometryComponentFilter.java | 2 - .../jts/geom/GeometryFactory.java | 2 - .../locationtech/jts/geom/GeometryFilter.java | 2 - .../jts/geom/IntersectionMatrix.java | 2 - .../locationtech/jts/geom/LineSegment.java | 2 - .../org/locationtech/jts/geom/LinearRing.java | 2 - .../org/locationtech/jts/geom/Location.java | 2 - .../org/locationtech/jts/geom/MultiPoint.java | 2 - .../locationtech/jts/geom/MultiPolygon.java | 2 - .../java/org/locationtech/jts/geom/Point.java | 2 - .../org/locationtech/jts/geom/Polygon.java | 2 - .../locationtech/jts/geom/PrecisionModel.java | 2 - .../jts/geom/TopologyException.java | 3 - .../geom/impl/CoordinateArraySequence.java | 1 - .../geom/impl/PackedCoordinateSequence.java | 8 +- .../jts/geom/impl/package-info.java | 16 + .../locationtech/jts/geom/impl/package.html | 13 - .../locationtech/jts/geom/package-info.java | 26 + .../org/locationtech/jts/geom/package.html | 25 - .../jts/geom/prep/package-info.java | 16 + .../locationtech/jts/geom/prep/package.html | 13 - .../jts/geom/util/AffineTransformation.java | 26 +- .../util/ComponentCoordinateExtracter.java | 1 - .../jts/geom/util/GeometryEditor.java | 1 - .../jts/geom/util/GeometryExtracter.java | 1 - .../jts/geom/util/LineStringExtracter.java | 1 - .../geom/util/LinearComponentExtracter.java | 1 - .../jts/geom/util/PointExtracter.java | 1 - .../jts/geom/util/PolygonExtracter.java | 1 - .../jts/geom/util/package-info.java | 16 + .../locationtech/jts/geom/util/package.html | 13 - .../org/locationtech/jts/geomgraph/Depth.java | 4 +- .../jts/geomgraph/DirectedEdge.java | 29 +- .../jts/geomgraph/DirectedEdgeStar.java | 7 +- .../org/locationtech/jts/geomgraph/Edge.java | 18 +- .../locationtech/jts/geomgraph/EdgeEnd.java | 6 +- .../jts/geomgraph/EdgeEndStar.java | 8 +- .../jts/geomgraph/EdgeIntersection.java | 31 +- .../jts/geomgraph/EdgeIntersectionList.java | 5 + .../locationtech/jts/geomgraph/EdgeList.java | 7 +- .../jts/geomgraph/EdgeNodingValidator.java | 1 - .../locationtech/jts/geomgraph/EdgeRing.java | 8 +- .../jts/geomgraph/GeometryGraph.java | 19 +- .../jts/geomgraph/GraphComponent.java | 8 +- .../org/locationtech/jts/geomgraph/Label.java | 26 +- .../org/locationtech/jts/geomgraph/Node.java | 11 +- .../jts/geomgraph/NodeFactory.java | 3 +- .../locationtech/jts/geomgraph/NodeMap.java | 10 +- .../jts/geomgraph/PlanarGraph.java | 14 +- .../jts/geomgraph/TopologyLocation.java | 8 +- .../geomgraph/index/EdgeSetIntersector.java | 6 +- .../jts/geomgraph/index/MonotoneChain.java | 3 - .../geomgraph/index/MonotoneChainEdge.java | 3 - .../geomgraph/index/MonotoneChainIndexer.java | 3 - .../geomgraph/index/SegmentIntersector.java | 13 +- .../index/SimpleEdgeSetIntersector.java | 3 - .../index/SimpleMCSweepLineIntersector.java | 3 - .../index/SimpleSweepLineIntersector.java | 3 - .../jts/geomgraph/index/SweepLineEvent.java | 3 - .../jts/geomgraph/index/SweepLineSegment.java | 3 - .../jts/geomgraph/index/package-info.java | 16 + .../jts/geomgraph/index/package.html | 13 - .../jts/geomgraph/package-info.java | 26 + .../locationtech/jts/geomgraph/package.html | 25 - .../locationtech/jts/index/SpatialIndex.java | 1 - .../jts/index/bintree/Bintree.java | 5 +- .../jts/index/bintree/Interval.java | 1 - .../locationtech/jts/index/bintree/Key.java | 1 - .../locationtech/jts/index/bintree/Node.java | 1 - .../jts/index/bintree/NodeBase.java | 1 - .../locationtech/jts/index/bintree/Root.java | 1 - .../jts/index/bintree/package-info.java | 16 + .../jts/index/bintree/package.html | 13 - .../jts/index/chain/MonotoneChain.java | 5 +- .../chain/MonotoneChainOverlapAction.java | 2 - .../chain/MonotoneChainSelectAction.java | 3 - .../jts/index/chain/package-info.java | 16 + .../locationtech/jts/index/chain/package.html | 13 - .../jts/index/intervalrtree/package-info.java | 16 + .../jts/index/intervalrtree/package.html | 13 - .../locationtech/jts/index/kdtree/KdNode.java | 2 +- .../locationtech/jts/index/kdtree/KdTree.java | 2 +- .../jts/index/kdtree/package-info.java | 16 + .../jts/index/kdtree/package.html | 13 - .../locationtech/jts/index/package-info.java | 16 + .../org/locationtech/jts/index/package.html | 13 - .../jts/index/quadtree/DoubleBits.java | 1 - .../jts/index/quadtree/IntervalSize.java | 1 - .../locationtech/jts/index/quadtree/Key.java | 1 - .../locationtech/jts/index/quadtree/Node.java | 1 - .../jts/index/quadtree/NodeBase.java | 1 - .../jts/index/quadtree/Quadtree.java | 1 - .../locationtech/jts/index/quadtree/Root.java | 1 - .../jts/index/quadtree/package-info.java | 16 + .../jts/index/quadtree/package.html | 13 - .../jts/index/strtree/AbstractNode.java | 1 - .../jts/index/strtree/AbstractSTRtree.java | 1 - .../jts/index/strtree/Boundable.java | 1 - .../BoundablePairDistanceComparator.java | 1 - .../index/strtree/GeometryItemDistance.java | 2 +- .../jts/index/strtree/Interval.java | 1 - .../jts/index/strtree/ItemBoundable.java | 1 - .../jts/index/strtree/SIRtree.java | 1 - .../jts/index/strtree/STRtree.java | 3 +- .../jts/index/strtree/package-info.java | 16 + .../jts/index/strtree/package.html | 8 - .../jts/index/sweepline/SweepLineEvent.java | 3 - .../jts/index/sweepline/SweepLineIndex.java | 2 - .../index/sweepline/SweepLineInterval.java | 2 - .../sweepline/SweepLineOverlapAction.java | 2 - .../jts/index/sweepline/package-info.java | 16 + .../jts/index/sweepline/package.html | 13 - .../locationtech/jts/io/ParseException.java | 2 - .../locationtech/jts/io/WKTFileReader.java | 2 +- .../jts/io/gml2/GeometryStrategies.java | 6 +- .../jts/io/gml2/package-info.java | 17 + .../org/locationtech/jts/io/gml2/package.html | 6 - .../org/locationtech/jts/io/package-info.java | 26 + .../java/org/locationtech/jts/io/package.html | 25 - .../jts/linearref/package-info.java | 38 + .../locationtech/jts/linearref/package.html | 37 - .../org/locationtech/jts/math/Vector3D.java | 6 +- .../jts/noding/BasicSegmentString.java | 1 - .../jts/noding/FastNodingValidator.java | 1 - .../jts/noding/IteratedNoder.java | 1 - .../locationtech/jts/noding/MCIndexNoder.java | 4 +- .../MCIndexSegmentSetMutualIntersector.java | 4 +- .../jts/noding/NodedSegmentString.java | 14 +- .../org/locationtech/jts/noding/Noder.java | 1 - .../jts/noding/NodingIntersectionFinder.java | 4 +- .../jts/noding/NodingValidator.java | 1 - .../jts/noding/SegmentString.java | 1 - .../locationtech/jts/noding/SimpleNoder.java | 1 - .../SimpleSegmentSetMutualIntersector.java | 4 +- .../jts/noding/SinglePassNoder.java | 1 - .../jts/noding/ValidatingNoder.java | 17 +- .../locationtech/jts/noding/package-info.java | 16 + .../org/locationtech/jts/noding/package.html | 13 - .../jts/noding/snap/SnappingNoder.java | 2 +- .../jts/noding/snapround/GeometryNoder.java | 4 +- .../jts/noding/snapround/HotPixel.java | 2 - .../noding/snapround/MCIndexSnapRounder.java | 1 - .../SnapRoundingIntersectionAdder.java | 6 +- .../noding/snapround/SnapRoundingNoder.java | 4 +- .../jts/noding/snapround/package-info.java | 16 + .../jts/noding/snapround/package.html | 13 - .../jts/operation/GeometryGraphOperation.java | 2 - .../jts/operation/IsSimpleOp.java | 2 - .../jts/operation/buffer/BufferBuilder.java | 2 - .../jts/operation/buffer/BufferSubgraph.java | 2 - .../buffer/OffsetCurveSetBuilder.java | 3 - .../operation/buffer/RightmostEdgeFinder.java | 2 - .../buffer/SubgraphDepthLocater.java | 1 - .../jts/operation/buffer/VariableBuffer.java | 1 + .../jts/operation/buffer/package-info.java | 16 + .../jts/operation/buffer/package.html | 13 - .../buffer/validate/package-info.java | 16 + .../operation/buffer/validate/package.html | 13 - .../ConnectedElementLocationFilter.java | 1 - .../distance/ConnectedElementPointFilter.java | 1 - .../operation/distance/GeometryLocation.java | 1 - .../jts/operation/distance/package-info.java | 16 + .../jts/operation/distance/package.html | 13 - .../jts/operation/linemerge/EdgeString.java | 1 - .../linemerge/LineMergeDirectedEdge.java | 1 - .../operation/linemerge/LineMergeEdge.java | 1 - .../operation/linemerge/LineMergeGraph.java | 1 - .../jts/operation/linemerge/LineMerger.java | 1 - .../jts/operation/linemerge/package-info.java | 16 + .../jts/operation/linemerge/package.html | 13 - .../jts/operation/overlay/EdgeSetNoder.java | 2 - .../operation/overlay/MaximalEdgeRing.java | 2 - .../operation/overlay/MinimalEdgeRing.java | 2 - .../operation/overlay/OverlayNodeFactory.java | 2 - .../jts/operation/overlay/OverlayOp.java | 2 - .../jts/operation/overlay/package-info.java | 42 + .../jts/operation/overlay/package.html | 41 - .../operation/overlay/snap/package-info.java | 16 + .../jts/operation/overlay/snap/package.html | 13 - .../overlay/validate/package-info.java | 16 + .../operation/overlay/validate/package.html | 13 - .../operation/overlayng/CoverageUnion.java | 6 +- .../overlayng/FastOverlayFilter.java | 13 +- .../overlayng/IndexedPointOnLineLocator.java | 11 + .../operation/overlayng/MaximalEdgeRing.java | 11 + .../operation/overlayng/OverlayLabeller.java | 1 - .../jts/operation/overlayng/OverlayNG.java | 35 +- .../operation/overlayng/OverlayNGRobust.java | 2 +- .../operation/overlayng/PrecisionReducer.java | 6 +- .../operation/overlayng/PrecisionUtil.java | 10 +- .../jts/operation/overlayng/RingClipper.java | 5 +- .../jts/operation/overlayng/package-info.java | 209 + .../jts/operation/overlayng/package.html | 221 - .../jts/operation/package-info.java | 16 + .../locationtech/jts/operation/package.html | 13 - .../jts/operation/polygonize/EdgeRing.java | 5 +- .../polygonize/PolygonizeDirectedEdge.java | 1 - .../operation/polygonize/PolygonizeEdge.java | 3 - .../operation/polygonize/PolygonizeGraph.java | 17 +- .../jts/operation/polygonize/Polygonizer.java | 3 +- .../operation/polygonize/package-info.java | 16 + .../jts/operation/polygonize/package.html | 13 - .../jts/operation/predicate/package-info.java | 16 + .../jts/operation/predicate/package.html | 13 - .../jts/operation/relate/EdgeEndBuilder.java | 3 - .../jts/operation/relate/EdgeEndBundle.java | 3 - .../operation/relate/EdgeEndBundleStar.java | 3 - .../jts/operation/relate/RelateComputer.java | 3 - .../jts/operation/relate/RelateNode.java | 3 - .../operation/relate/RelateNodeFactory.java | 2 - .../jts/operation/relate/RelateNodeGraph.java | 1 - .../jts/operation/relate/RelateOp.java | 3 - .../jts/operation/relate/package-info.java | 51 + .../jts/operation/relate/package.html | 52 - .../jts/operation/union/UnaryUnionOp.java | 2 +- .../jts/operation/union/UnionStrategy.java | 2 +- .../jts/operation/union/package-info.java | 16 + .../jts/operation/union/package.html | 13 - .../jts/operation/valid/IsSimpleOp.java | 6 +- .../operation/valid/RepeatedPointTester.java | 2 - .../valid/TopologyValidationError.java | 2 - .../jts/operation/valid/package-info.java | 17 + .../jts/operation/valid/package.html | 14 - .../jts/planargraph/DirectedEdge.java | 1 - .../jts/planargraph/DirectedEdgeStar.java | 11 +- .../locationtech/jts/planargraph/Edge.java | 1 - .../jts/planargraph/GraphComponent.java | 1 - .../locationtech/jts/planargraph/Node.java | 3 - .../jts/planargraph/PlanarGraph.java | 1 - .../planargraph/algorithm/package-info.java | 16 + .../jts/planargraph/algorithm/package.html | 13 - .../jts/planargraph/package-info.java | 16 + .../locationtech/jts/planargraph/package.html | 13 - .../jts/precision/CommonBits.java | 1 - .../jts/precision/CommonBitsOp.java | 1 - .../jts/precision/CommonBitsRemover.java | 1 - .../jts/precision/EnhancedPrecisionOp.java | 1 - .../precision/GeometryPrecisionReducer.java | 1 - .../jts/precision/MinimumClearance.java | 2 +- .../PointwisePrecisionReducerTransformer.java | 2 - .../SimpleGeometryPrecisionReducer.java | 1 - .../jts/precision/package-info.java | 17 + .../locationtech/jts/precision/package.html | 14 - .../jts/simplify/package-info.java | 16 + .../locationtech/jts/simplify/package.html | 13 - .../jts/triangulate/package-info.java | 16 + .../locationtech/jts/triangulate/package.html | 13 - .../triangulate/quadedge/package-info.java | 17 + .../jts/triangulate/quadedge/package.html | 14 - .../org/locationtech/jts/util/Assert.java | 2 - .../jts/util/AssertionFailedException.java | 2 - .../jts/util/CoordinateArrayFilter.java | 2 - .../jts/util/CoordinateCountFilter.java | 2 - .../java/org/locationtech/jts/util/Debug.java | 4 +- .../org/locationtech/jts/util/Stopwatch.java | 1 - .../jts/util/UniqueCoordinateArrayFilter.java | 2 - .../locationtech/jts/util/package-info.java | 16 + .../org/locationtech/jts/util/package.html | 8 - .../locationtech/jts/algorithm/AngleTest.java | 1 - .../algorithm/MinimumBoundingCircleTest.java | 1 - .../jts/algorithm/MinimumDiameterTest.java | 1 - .../algorithm/NonRobustLineIntersector.java | 2 - .../locationtech/jts/geom/AreaLengthTest.java | 1 - .../jts/geom/CoordinateSequencesTest.java | 7 +- .../jts/geom/GeometryImplTest.java | 1 - .../jts/geom/MiscellaneousTest.java | 12 +- .../jts/geom/MultiPointImplTest.java | 4 +- .../impl/BasicCoordinateSequenceTest.java | 1 - .../jts/index/SpatialIndexTester.java | 1 - .../jts/index/bintree/BinTreeCorrectTest.java | 1 - .../jts/index/bintree/IntervalList.java | 1 - .../jts/index/quadtree/EnvelopeList.java | 7 +- .../index/quadtree/QuadtreeCorrectTest.java | 1 - .../strtree/GeometryDistanceComparator.java | 1 - .../jts/index/strtree/IntervalTest.java | 1 - .../jts/index/strtree/SIRtreeTest.java | 1 - .../strtree/STRtreeNearestNeighbourTest.java | 1 - .../jts/index/strtree/STRtreeTest.java | 1 - .../jts/io/SerializabilityTest.java | 1 - .../linearref/AbstractIndexedLineTest.java | 4 +- .../buffer/BufferResultValidatorTest.java | 1 - .../jts/operation/buffer/BufferTest.java | 1 - .../jts/operation/buffer/BufferValidator.java | 1 - .../operation/buffer/DepthSegmentTest.java | 1 - .../operation/distance/BaseDistanceTest.java | 1 - .../jts/operation/distance/DistanceTest.java | 1 - .../operation/linemerge/LineMergerTest.java | 1 - .../operation/polygonize/PolygonizerTest.java | 1 - .../GeometryPrecisionReducerTest.java | 1 - .../SimpleGeometryPrecisionReducerTest.java | 1 - .../src/test/java/test/jts/IsValidTester.java | 1 - .../geom/CoordinateSequenceExperiment2.java | 1 - .../test/jts/geom/GeometryTestFactory.java | 1 - .../test/java/test/jts/index/STRtreeDemo.java | 1 - .../java/test/jts/perf/ExamplePerfTest.java | 2 +- .../algorithm/InteriorPointAreaPerfTest.java | 2 +- .../perf/algorithm/IntersectionPerfTest.java | 9 +- .../algorithm/IntersectionStressTest.java | 4 +- .../algorithm/PointInRingRobustnessTest.java | 4 +- .../PreparedGeometryConcurrencyBugTest.java | 2 +- .../test/jts/perf/index/EnvelopeList.java | 1 - .../java/test/jts/perf/index/IndexTester.java | 1 - .../test/jts/perf/index/TreeTimeTest.java | 1 - .../buffer/BufferCorrectnessTest.java | 1 - .../buffer/DepthSegmentStressTest.java | 2 +- .../buffer/IteratedBufferStressTest.java | 1 - .../buffer/OffsetCurveCorrectnessTest.java | 1 - .../buffer/RandomLineBufferStressTest.java | 1 - .../GeometryPrecisionReducerPerfTest.java | 4 +- .../jtsexample/geom/BasicExample.java | 1 - .../jtsexample/geom/ExtendedCoordinate.java | 1 - .../geom/ExtendedCoordinateExample.java | 1 - .../geom/ExtendedCoordinateSequence.java | 1 - .../ExtendedCoordinateSequenceFactory.java | 1 - .../geom/PrecisionModelExample.java | 1 - .../geom/prep/PreparedGeometryExample.java | 1 - .../distance/ClosestPointExample.java | 1 - .../operation/linemerge/LineMergeExample.java | 1 - .../polygonize/PolygonizeExample.java | 1 - .../precision/EnhancedPrecisionOpExample.java | 1 - .../SearchUsingPreparedGeometryIndex.java | 1 - .../io/geojson/OrientationTransformer.java | 11 + .../locationtech/jts/io/oracle/OraReader.java | 17 +- .../locationtech/jts/io/oracle/OraWriter.java | 17 +- modules/io/pom.xml | 4 +- modules/lab/pom.xml | 6 +- .../locationtech/jtslab/EdgeRayFunctions.java | 11 + .../jtslab/IteratedOverlayFunctions.java | 11 + .../jtslab/OverlayOptFunctions.java | 2 +- .../locationtech/jtslab/edgeray/EdgeRay.java | 11 + .../jtslab/edgeray/EdgeRayArea.java | 11 + .../edgeray/EdgeRayIntersectionArea.java | 11 + .../jtslab/geom/util/GeometryEditorEx.java | 26 +- modules/tests/pom.xml | 5 +- .../jtstest/command/CommandLine.java | 2 - .../locationtech/jtstest/command/Option.java | 2 - .../jtstest/command/OptionSpec.java | 4 +- .../jtstest/command/ParseException.java | 2 - .../jtstest/geomop/ArgumentConverter.java | 4 +- .../geomop/GeometryMethodOperation.java | 4 +- .../jtstest/testrunner/DoubleResult.java | 1 - .../jtstest/testrunner/GeometryResult.java | 2 - .../jtstest/testrunner/IntegerResult.java | 1 - .../jtstest/testrunner/JTSTestRunnerCmd.java | 1 - .../jtstest/testrunner/Result.java | 2 - .../jtstest/testrunner/TestEngine.java | 2 - .../jtstest/testrunner/TestReader.java | 2 - .../jtstest/testrunner/TestRun.java | 2 - .../locationtech/jtstest/util/FilesUtil.java | 11 + .../testxml/failure/TestBufferFailure.xml | 1 + pom.xml | 246 +- review.txt | 6763 +++++++++++++++++ 467 files changed, 9385 insertions(+), 1870 deletions(-) create mode 100644 build-tools/pom.xml create mode 100644 build-tools/src/main/resources/jts/checkstyle.xml create mode 100644 build-tools/src/main/resources/jts/header.txt create mode 100644 build-tools/src/main/resources/jts/pmd-ruleset.xml create mode 100644 build-tools/src/main/resources/jts/suppressions.xml create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/match/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/match/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/awt/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/awt/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/densify/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/densify/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/impl/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/impl/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/prep/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/prep/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/util/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geom/util/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/geomgraph/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/geomgraph/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/bintree/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/bintree/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/chain/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/chain/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/kdtree/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/kdtree/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/quadtree/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/quadtree/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/strtree/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/strtree/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/index/sweepline/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/index/sweepline/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/io/gml2/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/io/gml2/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/io/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/io/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/linearref/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/linearref/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/noding/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/noding/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/noding/snapround/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/noding/snapround/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/buffer/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/buffer/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/distance/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/distance/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/predicate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/predicate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/relate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/relate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/union/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/union/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/planargraph/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/planargraph/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/precision/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/precision/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/simplify/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/simplify/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package.html create mode 100644 modules/core/src/main/java/org/locationtech/jts/util/package-info.java delete mode 100644 modules/core/src/main/java/org/locationtech/jts/util/package.html create mode 100644 review.txt diff --git a/.gitignore b/.gitignore index 931f1f1dae..7f2d07b41f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ .idea *.iml target +build + +# eclipse ignores .classpath .project .settings .externalToolBuilders -build +.checkstyle +.eclipse-pmd +.pmd +.ruleset # standard ignores *.class diff --git a/DEVELOPING.md b/DEVELOPING.md index bde79fd6e9..194c158b19 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -22,60 +22,35 @@ The JTS build chain uses Maven. Build commands are executed at the project root * Build everything: - mvn install -Pall + mvn install -Dall=true -## Javadoc - -* Build Javadoc for core modules - - mvn javadoc:aggregate - -## Eclipse Configuration +* Limit build to release artifacts: -* Generate Eclipse configuration using `mvn eclipse:eclipse` -* Import the generated projects into an Eclipse workspace + mvn install -Drelease=true -### Run Configurations +## Quality Assurance -It is convenient to define the following Run Configurations: +JTS build verify stage includes pmd, checkstyle and more: + mvn verify -* **JTS TestRunner** - for executing XML tests: - -Field | Value -------|------ -Type | Java Application -Project | `jts-tests` -Main class | `org.locationtech.jtstest.testrunner.JTSTestRunnerCmd` -Program arguments | `validate general` -Working directory | `${workspace_loc:jts-tests/src/test/resources/testxml}` +To skip QA checks: -* **JTS TestBuilder** - for viewing and processing geometry with JTS + mvn verify -Dpmd.skip=true -Dcheckstyle.skip=true -Field | Value -------|------ -Type | Java Application -Project | `jts-app` -Main class | `org.locationtech.jtstest.testbuilder.JTSTestBuilder` -Program arguments (optional) | `-geomfunc ...` -VM args | `-Xmx1000M` -VM args (optional, for Mac) | `-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel` -Working directory | Default +Errors are logged, to browse errors use: + + mvn site:site + open modules/core/target/site/index.html -## Testing +### JUnit tests JTS aims for 100% code coverage for unit tests. -There are two kinds of unit tests: +Unit tests are written in Java and are used for verifying API code, internal data structures, and ancillary algorithms. -### JUnit tests - -Used for verifying API code, internal data structures, and ancillary algorithms. -These tests are written in Java. -This allows testing all parts of the codebase, -and can provide richer error detection and reporting. -However, the tests are not as readable or portable -as the XML tests. +This allows testing all parts of the codebase, and can provide richer error detection and reporting. +However, the tests are not as readable or portable as the XML tests. * To run the unit tests in a module (`jts-core`): @@ -84,6 +59,7 @@ as the XML tests. ### XML Tests JTS provides a code-independent, declarative XML-based format for expressing geometric functional tests. + This format has the following advantages: * allows encoding large geometries @@ -96,4 +72,90 @@ express fundamental geometric semantics of the JTS library. The XML test format can be executed using the JTS TestRunner, or imported into the JTS TestBuilder. +## Javadoc + +* Build Javadoc for core modules + + mvn javadoc:aggregate + +## Eclipse Configuration + +Project: + +1. Startup eclipse, creating a new `jts-workspace` location. This folder is used by eclipse to keep track of settings alongside your jts source code. + +2. Use *File > Import*, and the wizard *Maven > Existing Maven Project*. + + Select top-level `jts` folder as the Root directory. + +3. Once imported eclipse will build the project using built-in maven support. + +4. During initial build warning is shown for maven lifecycle mapping for `checkstyle:check`. Use the quickfix to **ignore** this lifecycle mapping. + + Do not try the *maven-checkstyle-plugin* connector as it fails to install. + +Plugins: + +* Install *Eclipse-CS* from the market place. + + 1. Select *jts-core* project properties and navigate to *Checkstyle* preference page. + + 2. From the *Local check configuration* tab use *new* to create a check configuration. + + 3. Setup a *Project Relative Configuration* named `jts`, selecting ``build-tools/src/main/resources/jts/checkstyle.xml`` + + 4. Press *Additional properties* to open the *Additional Checkstyle configuration file properties* dialog. + + 5. Press *Find unresolved properties* to define: + + * property `checkstyle.header.file` value: Absolute path to `src/main/resources/jts/header.txt` + * property `checkstyle.header.file`value: Absolute path to `src/main/resources/jts/suppressions.xml` + + 6. From the *Main* tab: + + * Enable *Checkstyle active for this project*. + * Select ``jts`` configuration + + 7. You can *jts-core* as a blueprint to copy the Checkstyle configuration to other modules. + + Checkstyle is integrated into the build cycle updating warnings each time you save. + +* Install *eclipse-pmd* following directions for [offline install](https://acanda.github.io/eclipse-pmd/getting-started.html) to download a [recent release](https://github.com/eclipse-pmd/eclipse-pmd/releases/). + + 1. Select *jts-core* project properties and navigate to *PMD* preference page. + 2. Use *add* button to add a workspace ruleset using `build-tools/src/main/resources/jts/pmd-ruleset.xml` + 3. Name the ruleset `jts` + + PMD is integrated into the build cycle updating warnings each time you save, and providing some quickfixes. + +* Alternative: Install *pmd-eclipse-plugin* from the market place. + + 1. Select *jts-core* project properties and navigate to *PMD* preference page. + 2. Enable PMD + 3. Use a the ruleset configured in a project file, selecting `build-tools/src/main/resources/jts/pmd-ruleset.xml` + + You can use *PMD > Check code* to list errors and warnings. The results are shown in their own view, and quickfixes are not available. + +Run Configurations: + +* **JTS TestRunner** - for executing XML tests: + + Field | Value + ------|------ + Type | Java Application + Project | `jts-tests` + Main class | `org.locationtech.jtstest.testrunner.JTSTestRunnerCmd` + Program arguments | `validate general` + Working directory | `${workspace_loc:jts-tests/src/test/resources/testxml}` + +* **JTS TestBuilder** - for viewing and processing geometry with JTS + Field | Value + ------|------ + Type | Java Application + Project | `jts-app` + Main class | `org.locationtech.jtstest.testbuilder.JTSTestBuilder` + Program arguments (optional) | `-geomfunc ...` + VM args | `-Xmx1000M` + VM args (optional, for Mac) | `-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel` + Working directory | Default \ No newline at end of file diff --git a/build-tools/pom.xml b/build-tools/pom.xml new file mode 100644 index 0000000000..c9e283017d --- /dev/null +++ b/build-tools/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + JTS Topology Suite Build Configuration + + + + + + maven-assembly-plugin + 2.6 + + + maven-jar-plugin + 3.0.2 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + + + + org.apache.maven.plugins + maven-site-plugin + + true + + + + + \ No newline at end of file diff --git a/build-tools/src/main/resources/jts/checkstyle.xml b/build-tools/src/main/resources/jts/checkstyle.xml new file mode 100644 index 0000000000..dbe735d0d6 --- /dev/null +++ b/build-tools/src/main/resources/jts/checkstyle.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-tools/src/main/resources/jts/header.txt b/build-tools/src/main/resources/jts/header.txt new file mode 100644 index 0000000000..f628c953fe --- /dev/null +++ b/build-tools/src/main/resources/jts/header.txt @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ \ No newline at end of file diff --git a/build-tools/src/main/resources/jts/pmd-ruleset.xml b/build-tools/src/main/resources/jts/pmd-ruleset.xml new file mode 100644 index 0000000000..51492b5515 --- /dev/null +++ b/build-tools/src/main/resources/jts/pmd-ruleset.xml @@ -0,0 +1,111 @@ + + + + + + JTS Topology Suite rulset used in conjunction with maven build to improve library quality. + + Maven build configured handle priority High (1) as failure. + Individual rulsets may be configured to reflect JTS library conventions. + + For more information https://pmd.github.io/latest/pmd_userdocs_making_rulesets.html + + + + + + + + + + + + + + + + + diff --git a/build-tools/src/main/resources/jts/suppressions.xml b/build-tools/src/main/resources/jts/suppressions.xml new file mode 100644 index 0000000000..89a9e3ed09 --- /dev/null +++ b/build-tools/src/main/resources/jts/suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/modules/app/pom.xml b/modules/app/pom.xml index 3f2224279f..5bcc7b5e34 100644 --- a/modules/app/pom.xml +++ b/modules/app/pom.xml @@ -38,7 +38,6 @@ org.apache.maven.plugins maven-assembly-plugin - 2.6 jar-with-dependencies @@ -49,7 +48,7 @@ JTSTestBuilder - false + false diff --git a/modules/app/src/main/java/org/locationtech/jtstest/clean/CleanDuplicatePoints.java b/modules/app/src/main/java/org/locationtech/jtstest/clean/CleanDuplicatePoints.java index 85d8e494bd..d7a2745691 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/clean/CleanDuplicatePoints.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/clean/CleanDuplicatePoints.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandError.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandError.java index f5342d9dea..894cddc10c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandError.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandError.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.cmd; public class CommandError extends RuntimeException { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOutput.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOutput.java index d149f7edbe..1ce333d083 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOutput.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/CommandOutput.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.cmd; public class CommandOutput { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java index 4ad264bc3d..9c237ed826 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/GeometryOutput.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.cmd; import java.util.List; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/WKBDump.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/WKBDump.java index 6b95f78f99..f283baef68 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/WKBDump.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/WKBDump.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.cmd; import java.io.PrintWriter; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java index 95ec07abf0..343f316f86 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/DiffFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis, and others + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.function; import java.util.ArrayList; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/LabellingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/LabellingFunctions.java index f3e28578ee..19c8b068ad 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/LabellingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/LabellingFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.function; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/LineHandlingFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/LineHandlingFunctions.java index b78a0a43d7..d182b9bb24 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/LineHandlingFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/LineHandlingFunctions.java @@ -13,13 +13,10 @@ import java.util.*; -import org.locationtech.jts.algorithm.*; -import org.locationtech.jts.densify.*; import org.locationtech.jts.dissolve.LineDissolver; import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.util.*; import org.locationtech.jts.operation.linemerge.*; -import org.locationtech.jts.operation.polygonize.*; public class LineHandlingFunctions { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/OrientationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/OrientationFunctions.java index 0068204091..ba1fca472b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/OrientationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/OrientationFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2017 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.function; import org.locationtech.jts.algorithm.Orientation; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PointLocationFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PointLocationFunctions.java index f50df352a6..22ec188518 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PointLocationFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PointLocationFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2017 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.function; import org.locationtech.jts.algorithm.PointLocation; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java index c907800d13..6f3ce50187 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SpreaderGeometryFunction.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.geomfunction; import java.util.ArrayList; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/test/TestCase.java b/modules/app/src/main/java/org/locationtech/jtstest/test/TestCase.java index 15c1766554..96c4f75154 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/test/TestCase.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/test/TestCase.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/test/TestCaseList.java b/modules/app/src/main/java/org/locationtech/jtstest/test/TestCaseList.java index 53df311bb5..6e1027d35e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/test/TestCaseList.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/test/TestCaseList.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/test/Testable.java b/modules/app/src/main/java/org/locationtech/jtstest/test/Testable.java index 7f41425d95..b9e8c5ddc9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/test/Testable.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/test/Testable.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppColors.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppColors.java index fd2bed1022..18942f3556 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppColors.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppColors.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import java.awt.Color; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java index 36d71b5660..cceac4883c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import javax.swing.ImageIcon; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/FunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/FunctionPanel.java index 3590339cb0..ad0d09852e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/FunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/FunctionPanel.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import org.locationtech.jtstest.geomfunction.GeometryFunction; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionListPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionListPanel.java index c5ae341e02..d4630f3ec2 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionListPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryFunctionListPanel.java @@ -20,7 +20,6 @@ import javax.swing.event.*; import javax.swing.border.*; -import org.locationtech.jtstest.function.*; import org.locationtech.jtstest.geomfunction.GeometryFunction; import org.locationtech.jtstest.util.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java index 6fe1786095..8eaae8908a 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java @@ -46,7 +46,7 @@ public GeometryInspectorDialog() public GeometryInspectorDialog(Frame frame) { - this(null, "Geometry Inspector", false); + this(frame, "Geometry Inspector", false); } void initUI() throws Exception diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java index f6ccc98c17..50f95e23f0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryTreePanel.java @@ -22,8 +22,6 @@ import javax.swing.tree.*; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jtstest.function.*; -import org.locationtech.jtstest.util.*; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java index 5a4cba41d8..03fe341ed9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import java.awt.Color; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LabelComponentsPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LabelComponentsPanel.java index 93295b1124..7ef3add85f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LabelComponentsPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LabelComponentsPanel.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import java.awt.Component; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelDialog.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelDialog.java index 8767f8b983..66c201d891 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelDialog.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelDialog.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelPanel.java index 767890eeaf..5279e441dc 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/PrecisionModelPanel.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java index 2a479a2ba3..d4923b0b9a 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/RelatePanel.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java index 44db16e0d8..2b73e000f0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestBuilderDialogs.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder; import java.io.File; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java index 32a6009096..a7edbd1715 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ValidPanel.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelEvent.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelEvent.java index 5343bfacde..a8e33cdf76 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelEvent.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelEvent.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelListener.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelListener.java index f508e70294..8cda823b52 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelListener.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/event/ValidPanelListener.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/EnvelopeClipper.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/EnvelopeClipper.java index d3f1465886..1bc9dd5b93 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/EnvelopeClipper.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/EnvelopeClipper.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.geom; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentClipper.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentClipper.java index 6afd404769..169bad2156 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentClipper.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentClipper.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.geom; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentExtracter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentExtracter.java index 4bd79c22b1..59c31eb78f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentExtracter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/SegmentExtracter.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.geom; import java.util.ArrayList; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlUtil.java index dad903c394..333b4c0973 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlUtil.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.io; public class HtmlUtil { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlWriter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlWriter.java index 2c25a4dabb..e81a9c5480 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlWriter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/HtmlWriter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/IOUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/IOUtil.java index c993fdf383..409de65dc7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/IOUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/IOUtil.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.io; import org.locationtech.jts.geom.Geometry; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java index 025c9e90bb..91a6cfa9b0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/PNGWriter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/InvalidShapefileException.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/InvalidShapefileException.java index 543ef9bfd2..19f4ab05e0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/InvalidShapefileException.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/InvalidShapefileException.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiLineHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiLineHandler.java index 5eca01fb3f..cc3bee67fb 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiLineHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiLineHandler.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,18 +9,27 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; import java.io.IOException; import org.locationtech.jts.geom.*; - +import org.locationtech.jtstest.testbuilder.io.shapefile.EndianDataInputStream; +import org.locationtech.jtstest.testbuilder.io.shapefile.InvalidShapefileException; +import org.locationtech.jtstest.testbuilder.io.shapefile.ShapeHandler; /** * Wrapper for a Shapefile arc. */ -public class MultiLineHandler implements ShapeHandler{ +public class MultiLineHandler implements ShapeHandler { int myShapeType = -1; private PrecisionModel precisionModel = new PrecisionModel(); @@ -49,7 +50,7 @@ public MultiLineHandler(int type) throws InvalidShapefileException } - public Geometry read( EndianDataInputStream file , GeometryFactory geometryFactory, int contentLength) throws IOException,InvalidShapefileException + public Geometry read( EndianDataInputStream file , GeometryFactory geometryFactory, int contentLength) throws IOException,InvalidShapefileException { double junk; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiPointHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiPointHandler.java index a46dd07fa4..227dd49eff 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiPointHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/MultiPointHandler.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,13 +9,19 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ /* * MultiPointHandler.java * * Created on July 17, 2002, 4:13 PM */ - package org.locationtech.jtstest.testbuilder.io.shapefile; import java.io.IOException; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PointHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PointHandler.java index 0658171d15..dc00ece73f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PointHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PointHandler.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; import java.io.IOException; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java index 574f102c5b..7138304817 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/PolygonHandler.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; import java.io.IOException; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapeHandler.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapeHandler.java index f4075a7cc1..1b48c58ce9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapeHandler.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapeHandler.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; import org.locationtech.jts.geom.Geometry; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/Shapefile.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/Shapefile.java index ebb9a58b3f..828e4805b3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/Shapefile.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/Shapefile.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; import java.io.*; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileException.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileException.java index 892380a2a7..e803369c65 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileException.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileException.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jtstest.testbuilder.io.shapefile; /** diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileHeader.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileHeader.java index cf9523765a..c2c32ea2a3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileHeader.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/io/shapefile/ShapefileHeader.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ /* * Header.java * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java index 64e6cbdcce..ebd917bbb9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestCaseEdit.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestRunnerTestCaseAdapter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestRunnerTestCaseAdapter.java index ba4be877a2..317f84199c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestRunnerTestCaseAdapter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/TestRunnerTestCaseAdapter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/UndoBuffer.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/UndoBuffer.java index 7ab386dbb9..f178fd98b9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/UndoBuffer.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/UndoBuffer.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.model; import java.util.Stack; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/dnd/FileDrop.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/dnd/FileDrop.java index 7d56073a78..3c4fb9e08f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/dnd/FileDrop.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/dnd/FileDrop.java @@ -403,7 +403,7 @@ private static boolean supportsDnD() catch( Exception e ) { support = false; } // end catch - supportsDnD = new Boolean( support ); + supportsDnD = Boolean.valueOf( support ); } // end if: first time through return supportsDnD.booleanValue(); } // end supportsDnD diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/DrawingGrid.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/DrawingGrid.java index 4613b03818..7c315762d4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/DrawingGrid.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/DrawingGrid.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/RenderManager.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/RenderManager.java index 5a88adb8a6..d2cc9c1034 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/RenderManager.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/RenderManager.java @@ -141,7 +141,7 @@ public Object construct() Graphics2D gr = (Graphics2D) image.getGraphics(); renderer.render(gr); isRendering = false; - return new Boolean(true); + return Boolean.TRUE; } public boolean isRendering() diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java index 5437750efa..e98cad7ac0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/LineLabelBaseline.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.ui.style; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java index de9faa7a1c..6fbe876314 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.ui.style; import java.awt.Color; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/StyleGroup.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/StyleGroup.java index 2a53d69bc2..ee30c7d19d 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/StyleGroup.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/StyleGroup.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2019 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.testbuilder.ui.style; import java.awt.Graphics2D; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java index a5af4c75d7..8a9db28abe 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/BoxBandTool.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/ZoomTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/ZoomTool.java index 120af5afab..901810524c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/ZoomTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/ZoomTool.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java b/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java index dbc62cbce1..62331ed03e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2017 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.util; import java.awt.Color; diff --git a/modules/app/src/test/java/org/locationtech/jtstest/TestFileGeometryExtractor.java b/modules/app/src/test/java/org/locationtech/jtstest/TestFileGeometryExtractor.java index 9838da9fea..7d1cbd0c54 100644 --- a/modules/app/src/test/java/org/locationtech/jtstest/TestFileGeometryExtractor.java +++ b/modules/app/src/test/java/org/locationtech/jtstest/TestFileGeometryExtractor.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/pom.xml b/modules/core/pom.xml index ad48854197..ceb3e69dbb 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -8,12 +8,16 @@ jts-core ${project.groupId}:${project.artifactId} bundle - + + + unchecked + none + + maven-jar-plugin - 3.0.2 @@ -41,7 +45,6 @@ org.apache.felix maven-bundle-plugin - 3.3.0 true true diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java index 311d0ccac4..20b88be439 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java @@ -17,7 +17,7 @@ * Utility functions for working with angles. * Unless otherwise noted, methods in this class express angles in radians. */ -public class Angle +@SuppressWarnings("DuplicatedCode") public class Angle { /** * The value of 2*Pi @@ -192,7 +192,7 @@ public static double angleBetweenOriented(Coordinate tip1, Coordinate tail, * the next point of the ring * @param p2 * the next point of the ring - * @return the interior angle based at p1 + * @return the interior angle based at {@code p1} */ public static double interiorAngle(Coordinate p0, Coordinate p1, Coordinate p2) { diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java index f01261285c..8849fd4a88 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java @@ -58,7 +58,7 @@ public static double ofRingSigned(Coordinate[] ring) if (ring.length < 3) return 0.0; double sum = 0.0; - /** + /* * Based on the Shoelace formula. * http://en.wikipedia.org/wiki/Shoelace_formula */ @@ -89,7 +89,7 @@ public static double ofRingSigned(CoordinateSequence ring) int n = ring.size(); if (n < 3) return 0.0; - /** + /* * Based on the Shoelace formula. * http://en.wikipedia.org/wiki/Shoelace_formula */ diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/BoundaryNodeRule.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/BoundaryNodeRule.java index 338860b018..7770f6273a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/BoundaryNodeRule.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/BoundaryNodeRule.java @@ -16,7 +16,7 @@ import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.operation.BoundaryOp; -import org.locationtech.jts.operation.IsSimpleOp; +import org.locationtech.jts.operation.valid.IsSimpleOp; import org.locationtech.jts.operation.relate.RelateOp; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms.java index c6ea766781..274be09516 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms.java @@ -72,42 +72,43 @@ public class CGAlgorithms public static final int STRAIGHT = COLLINEAR; /** - * Returns the index of the direction of the point q relative to - * a vector specified by p1-p2. + * Returns the index of the direction of the point {@code q} relative to + * a vector specified by {@code p1-p2}. * * @param p1 the origin point of the vector * @param p2 the final point of the vector * @param q the point to compute the direction to * - * @return 1 if q is counter-clockwise (left) from p1-p2 - * @return -1 if q is clockwise (right) from p1-p2 - * @return 0 if q is collinear with p1-p2 + * @return {@code 1} if q is counter-clockwise (left) from p1-p2 + * {@code -1} if q is clockwise (right) from p1-p2 + * {@code 0} if q is collinear with p1-p2 + * * @deprecated Use {@link Orientation#index(Coordinate, Coordinate, Coordinate)} * instead. */ public static int orientationIndex(Coordinate p1, Coordinate p2, Coordinate q) { - /** - * MD - 9 Aug 2010 It seems that the basic algorithm is slightly orientation - * dependent, when computing the orientation of a point very close to a - * line. This is possibly due to the arithmetic in the translation to the - * origin. - * - * For instance, the following situation produces identical results in spite - * of the inverse orientation of the line segment: - * - * Coordinate p0 = new Coordinate(219.3649559090992, 140.84159161824724); - * Coordinate p1 = new Coordinate(168.9018919682399, -5.713787599646864); - * - * Coordinate p = new Coordinate(186.80814046338352, 46.28973405831556); int - * orient = orientationIndex(p0, p1, p); int orientInv = - * orientationIndex(p1, p0, p); - * - * A way to force consistent results is to normalize the orientation of the - * vector using the following code. However, this may make the results of - * orientationIndex inconsistent through the triangle of points, so it's not - * clear this is an appropriate patch. - * + /* + MD - 9 Aug 2010 It seems that the basic algorithm is slightly orientation + dependent, when computing the orientation of a point very close to a + line. This is possibly due to the arithmetic in the translation to the + origin. + + For instance, the following situation produces identical results in spite + of the inverse orientation of the line segment: + + Coordinate p0 = new Coordinate(219.3649559090992, 140.84159161824724); + Coordinate p1 = new Coordinate(168.9018919682399, -5.713787599646864); + + Coordinate p = new Coordinate(186.80814046338352, 46.28973405831556); int + orient = orientationIndex(p0, p1, p); int orientInv = + orientationIndex(p1, p0, p); + + A way to force consistent results is to normalize the orientation of the + vector using the following code. However, this may make the results of + orientationIndex inconsistent through the triangle of points, so it's not + clear this is an appropriate patch. + */ return CGAlgorithmsDD.orientationIndex(p1, p2, q); // testing only @@ -136,7 +137,7 @@ public CGAlgorithms() * first point identical to last point) * @return true if p is inside ring * - * @see locatePointInRing + * @see #locatePointInRing(Coordinate, Coordinate[]) * @deprecated Use {@link PointLocation#isInRing(Coordinate, Coordinate[])} * instead. */ @@ -245,25 +246,25 @@ public static boolean isCCW(Coordinate[] ring) Coordinate prev = ring[iPrev]; Coordinate next = ring[iNext]; - /** - * This check catches cases where the ring contains an A-B-A configuration - * of points. This can happen if the ring does not contain 3 distinct points - * (including the case where the input array has fewer than 4 elements), or - * it contains coincident line segments. + /* + This check catches cases where the ring contains an A-B-A configuration + of points. This can happen if the ring does not contain 3 distinct points + (including the case where the input array has fewer than 4 elements), or + it contains coincident line segments. */ if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) return false; int disc = computeOrientation(prev, hiPt, next); - /** - * If disc is exactly 0, lines are collinear. There are two possible cases: - * (1) the lines lie along the x axis in opposite directions (2) the lines - * lie on top of one another - * - * (1) is handled by checking if next is left of prev ==> CCW (2) will never - * happen if the ring is valid, so don't check for it (Might want to assert - * this) + /* + If disc is exactly 0, lines are collinear. There are two possible cases: + (1) the lines lie along the x axis in opposite directions (2) the lines + lie on top of one another + + (1) is handled by checking if next is left of prev ==> CCW (2) will never + happen if the ring is valid, so don't check for it (Might want to assert + this) */ boolean isCCW; if (disc == 0) { @@ -521,9 +522,9 @@ public static double signedArea(Coordinate[] ring) if (ring.length < 3) return 0.0; double sum = 0.0; - /** - * Based on the Shoelace formula. - * http://en.wikipedia.org/wiki/Shoelace_formula + /* + Based on the Shoelace formula. + http://en.wikipedia.org/wiki/Shoelace_formula */ double x0 = ring[0].x; for (int i = 1; i < ring.length - 1; i++) { @@ -554,9 +555,9 @@ public static double signedArea(CoordinateSequence ring) int n = ring.size(); if (n < 3) return 0.0; - /** - * Based on the Shoelace formula. - * http://en.wikipedia.org/wiki/Shoelace_formula + /* + Based on the Shoelace formula. + http://en.wikipedia.org/wiki/Shoelace_formula */ Coordinate p0 = new Coordinate(); Coordinate p1 = new Coordinate(); diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms3D.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms3D.java index 721fc95355..8cfe3ca5a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms3D.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithms3D.java @@ -93,18 +93,18 @@ public static double distancePointSegment(Coordinate p, public static double distanceSegmentSegment( Coordinate A, Coordinate B, Coordinate C, Coordinate D) { - /** - * This calculation is susceptible to roundoff errors when - * passed large ordinate values. - * It may be possible to improve this by using {@link DD} arithmetic. + /* + This calculation is susceptible to round off errors when + passed large ordinate values. + It may be possible to improve this by using {@link DD} arithmetic. */ if (A.equals3D(B)) return distancePointSegment(A, C, D); if (C.equals3D(B)) return distancePointSegment(C, A, B); - /** - * Algorithm derived from http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm + /* + Algorithm derived from http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm */ double a = Vector3D.dot(A, B, A, B); double b = Vector3D.dot(A, B, C, D); @@ -119,9 +119,9 @@ public static double distanceSegmentSegment( double s; double t; if (denom <= 0.0) { - /** - * The lines are parallel. - * In this case solve for the parameters s and t by assuming s is 0. + /* + The lines are parallel. + In this case solve for the parameters s and t by assuming s is 0. */ s = 0; // choose largest denominator for optimal numeric conditioning @@ -143,9 +143,9 @@ else if (t < 0) else if(t > 1) { return distancePointSegment(D, A, B); } - /** - * The closest points are in interiors of segments, - * so compute them directly + /* + The closest points are in interiors of segments, + so compute them directly */ double x1 = A.x + s * (B.x - A.x); double y1 = A.y + s * (B.y - A.y); diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithmsDD.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithmsDD.java index 88b199502b..44c92999c3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithmsDD.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/CGAlgorithmsDD.java @@ -25,16 +25,16 @@ public class CGAlgorithmsDD private CGAlgorithmsDD() {} /** - * Returns the index of the direction of the point q relative to - * a vector specified by p1-p2. + * Returns the index of the direction of the point {@code q} relative to + * a vector specified by {@code p1-p2}. * * @param p1 the origin point of the vector * @param p2 the final point of the vector * @param q the point to compute the direction to * - * @return 1 if q is counter-clockwise (left) from p1-p2 - * @return -1 if q is clockwise (right) from p1-p2 - * @return 0 if q is collinear with p1-p2 + * @return {@code 1} if q is counter-clockwise (left) from p1-p2 + * {@code -1} if q is clockwise (right) from p1-p2 + * {@code 0} if q is collinear with p1-p2 */ public static int orientationIndex(Coordinate p1, Coordinate p2, Coordinate q) { @@ -42,8 +42,8 @@ public static int orientationIndex(Coordinate p1, Coordinate p2, Coordinate q) } /** - * Returns the index of the direction of the point q relative to - * a vector specified by p1-p2. + * Returns the index of the direction of the point {@code q} relative to + * a vector specified by {@code p1-p2}. * * @param p1x the x ordinate of the vector origin point * @param p1y the y ordinate of the vector origin point @@ -53,8 +53,8 @@ public static int orientationIndex(Coordinate p1, Coordinate p2, Coordinate q) * @param qy the y ordinate of the query point * * @return 1 if q is counter-clockwise (left) from p1-p2 - * @return -1 if q is clockwise (right) from p1-p2 - * @return 0 if q is collinear with p1-p2 + * -1 if q is clockwise (right) from p1-p2 + * 0 if q is collinear with p1-p2 */ public static int orientationIndex(double p1x, double p1y, double p2x, double p2y, @@ -80,8 +80,8 @@ public static int orientationIndex(double p1x, double p1y, * with the given entries. * * @return -1 if the determinant is negative, - * @return 1 if the determinant is positive, - * @return 0 if the determinant is 0. + * 1 if the determinant is positive, + * 0 if the determinant is 0. */ public static int signOfDet2x2(DD x1, DD y1, DD x2, DD y2) { @@ -94,8 +94,8 @@ public static int signOfDet2x2(DD x1, DD y1, DD x2, DD y2) * with the given entries. * * @return -1 if the determinant is negative, - * @return 1 if the determinant is positive, - * @return 0 if the determinant is 0. + * 1 if the determinant is positive, + * 0 if the determinant is 0. */ public static int signOfDet2x2(double dx1, double dy1, double dx2, double dy2) { @@ -128,9 +128,12 @@ public static int signOfDet2x2(double dx1, double dy1, double dx2, double dy2) *

    * Uses an approach due to Jonathan Shewchuk, which is in the public domain. * - * @param pa a coordinate - * @param pb a coordinate - * @param pc a coordinate + * @param pax A coordinate + * @param pay A coordinate + * @param pbx B coordinate + * @param pby B coordinate + * @param pcx C coordinate + * @param pcy C coordinate * @return the orientation index if it can be computed safely * @return i > 1 if the orientation index cannot be computed safely */ diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java index 253b3cc3e3..868a4715d1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Centroid.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -23,15 +22,16 @@ * Computes the centroid of a {@link Geometry} of any dimension. * For collections the centroid is computed for the collection of * non-empty elements of highest dimension. - * The centroid of an empty geometry is null. + * The centroid of an empty geometry is {@code null}. * - *

    Algorithm

    + *

    Algorithm

    + * *
      *
    • Dimension 2 - the centroid is computed * as the weighted sum of the centroids * of a decomposition of the area into (possibly overlapping) triangles. * Holes and multipolygons are handled correctly. - * See http://www.faqs.org/faqs/graphics/algorithms-faq/ + * See http://www.faqs.org/faqs/graphics/algorithms-faq/ * for further details of the basic approach. * *
    • Dimension 1 - Computes the average of the midpoints @@ -43,8 +43,8 @@ *
    * * @see InteriorPoint - * @see MaximumInscribedCircle - * @see LargestEmptyCircle + * @see org.locationtech.jts.algorithm.construct.MaximumInscribedCircle + * @see org.locationtech.jts.algorithm.construct.LargestEmptyCircle * * @version 1.7 */ @@ -117,7 +117,7 @@ else if (geom instanceof GeometryCollection) { */ public Coordinate getCentroid() { - /** + /* * The centroid is computed from the highest dimension components present in the input. * I.e. areas dominate lineal geometry, which dominates points. * Degenerate geometry are computed using their effective dimension @@ -125,21 +125,21 @@ public Coordinate getCentroid() */ Coordinate cent = new Coordinate(); if (Math.abs(areasum2) > 0.0) { - /** + /* * Input contains areal geometry */ cent.x = cg3.x / 3 / areasum2; cent.y = cg3.y / 3 / areasum2; } else if (totalLength > 0.0) { - /** + /* * Input contains lineal geometry */ cent.x = lineCentSum.x / totalLength; cent.y = lineCentSum.y / totalLength; } else if (ptCount > 0){ - /** + /* * Input contains puntal geometry only */ cent.x = ptCentSum.x / ptCount; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/ConvexHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/ConvexHull.java index 379ed19fbe..62686c4f19 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/ConvexHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/ConvexHull.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointArea.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointArea.java index 0d76f07020..b35cf13846 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointArea.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointArea.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java index f5217b0152..ca5a9832d4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointLine.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java index 5c28814c4f..23a0243062 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/InteriorPointPoint.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java index 6226c379b2..06e5ab7589 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumBoundingCircle.java @@ -318,24 +318,26 @@ private void computeCirclePoints() for (int i = 0; i < pts.length; i++) { Coordinate R = pointWithMinAngleWithSegment(pts, P, Q); - // if PRQ is obtuse, then MBC is determined by P and Q if (Angle.isObtuse(P, R, Q)) { + // if PRQ is obtuse, then MBC is determined by P and Q extremalPts = new Coordinate[] { new Coordinate(P), new Coordinate(Q) }; return; } - // if RPQ is obtuse, update baseline and iterate - if (Angle.isObtuse(R, P, Q)) { + else if (Angle.isObtuse(R, P, Q)) { + // if RPQ is obtuse, update baseline and iterate P = R; continue; } - // if RQP is obtuse, update baseline and iterate - if (Angle.isObtuse(R, Q, P)) { + else if (Angle.isObtuse(R, Q, P)) { + // if RQP is obtuse, update baseline and iterate Q = R; continue; } - // otherwise all angles are acute, and the MBC is determined by the triangle PQR - extremalPts = new Coordinate[] { new Coordinate(P), new Coordinate(Q), new Coordinate(R) }; - return; + else { + // otherwise all angles are acute, and the MBC is determined by the triangle PQR + extremalPts = new Coordinate[] { new Coordinate(P), new Coordinate(Q), new Coordinate(R) }; + return; + } } Assert.shouldNeverReachHere("Logic failure in Minimum Bounding Circle algorithm!"); } diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumDiameter.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumDiameter.java index 0721693565..907fb667bd 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumDiameter.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/MinimumDiameter.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/NotRepresentableException.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/NotRepresentableException.java index 55abb4c044..cea6ac47fc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/NotRepresentableException.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/NotRepresentableException.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/PointLocation.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/PointLocation.java index 48cfe9d587..54fa6c4351 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/PointLocation.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/PointLocation.java @@ -88,7 +88,7 @@ public static boolean isOnLine(Coordinate p, CoordinateSequence line) * first point identical to last point) * @return true if p is inside ring * - * @see locatePointInRing + * @see PointLocation#locateInRing(Coordinate, Coordinate[]) */ public static boolean isInRing(Coordinate p, Coordinate[] ring) { diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustDeterminant.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustDeterminant.java index 96cbe0fbe1..dd772e6e03 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustDeterminant.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustDeterminant.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustLineIntersector.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustLineIntersector.java index 2e3e7fda86..fdd1dc5f20 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustLineIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/RobustLineIntersector.java @@ -1,6 +1,5 @@ /* - * Copyright (c) 2016 Vivid Solutions. - * Copyright (c) 2020 Martin Davis. + * Copyright (c) 2016 Vivid Solutions, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java index 515f67c761..980d3bf94d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/MaximumInscribedCircle.java @@ -14,6 +14,7 @@ import java.util.PriorityQueue; import org.locationtech.jts.algorithm.Centroid; +import org.locationtech.jts.algorithm.InteriorPoint; import org.locationtech.jts.algorithm.locate.IndexedPointInAreaLocator; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package-info.java new file mode 100644 index 0000000000..ec318d920b --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes that implement various kinds of geometric constructions. + */ +package org.locationtech.jts.algorithm.construct; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package.html b/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package.html deleted file mode 100644 index d7992ff884..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/construct/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes that implement various kinds of geometric constructions. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package-info.java new file mode 100644 index 0000000000..f22db92f78 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to compute distance metrics between geometries. + */ +package org.locationtech.jts.algorithm.distance; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package.html b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package.html deleted file mode 100644 index b1caa99b11..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to compute distance metrics between geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java index 9d0a3cea20..d6c62511c2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/IndexedPointInAreaLocator.java @@ -37,7 +37,7 @@ *

    * The Location is computed precisely, in that points * located on the geometry boundary or segments will - * return {@link Location.BOUNDARY}. + * return {@link Location#BOUNDARY}. *

    * {@link Polygonal} and {@link LinearRing} geometries * are supported. diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/SimplePointInAreaLocator.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/SimplePointInAreaLocator.java index fa367b0752..07e07f66c0 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/SimplePointInAreaLocator.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/SimplePointInAreaLocator.java @@ -34,7 +34,7 @@ * or exactly on the boundary of the Geometry. *

    * Instance methods are provided to implement - * the interface {@link PointInAreaLocator}. + * the interface {@link SimplePointInAreaLocator}. * However, they provide no performance * advantage over the class methods. *

    @@ -53,9 +53,9 @@ public class SimplePointInAreaLocator * Determines the {@link Location} of a point in an areal {@link Geometry}. * The return value is one of: *

      - *
    • {@link Location.INTERIOR} if the point is in the geometry interior - *
    • {@link Location.BOUNDARY} if the point lies exactly on the boundary - *
    • {@link Location.EXTERIOR} if the point is outside the geometry + *
    • {@link Location#INTERIOR} if the point is in the geometry interior + *
    • {@link Location#BOUNDARY} if the point lies exactly on the boundary + *
    • {@link Location#EXTERIOR} if the point is outside the geometry *
    * * @param p the point to test @@ -113,9 +113,9 @@ private static int locateInGeometry(Coordinate p, Geometry geom) * Determines the {@link Location} of a point in a {@link Polygon}. * The return value is one of: *
      - *
    • {@link Location.INTERIOR} if the point is in the geometry interior - *
    • {@link Location.BOUNDARY} if the point lies exactly on the boundary - *
    • {@link Location.EXTERIOR} if the point is outside the geometry + *
    • {@link Location#INTERIOR} if the point is in the geometry interior + *
    • {@link Location#BOUNDARY} if the point lies exactly on the boundary + *
    • {@link Location#EXTERIOR} if the point is outside the geometry *
    * * This method is provided for backwards compatibility only. @@ -190,9 +190,9 @@ public SimplePointInAreaLocator(Geometry geom) { * Determines the {@link Location} of a point in an areal {@link Geometry}. * The return value is one of: *
      - *
    • {@link Location.INTERIOR} if the point is in the geometry interior - *
    • {@link Location.BOUNDARY} if the point lies exactly on the boundary - *
    • {@link Location.EXTERIOR} if the point is outside the geometry + *
    • {@link Location#INTERIOR} if the point is in the geometry interior + *
    • {@link Location#BOUNDARY} if the point lies exactly on the boundary + *
    • {@link Location#EXTERIOR} if the point is outside the geometry *
    * * @param p the point to test diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package-info.java new file mode 100644 index 0000000000..47b8b9efd4 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to determine the topological location of points in geometries. + */ +package org.locationtech.jts.algorithm.locate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package.html b/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package.html deleted file mode 100644 index 9826ba6816..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/locate/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to determine the topological location of points in geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package-info.java new file mode 100644 index 0000000000..6e08df918d --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to compute matching metrics between geometries. + */ +package org.locationtech.jts.algorithm.match; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package.html b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package.html deleted file mode 100644 index f4f0fb0016..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to compute matching metrics between geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/package-info.java new file mode 100644 index 0000000000..66820b76d9 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/package-info.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes and interfaces implementing fundamental computational geometry algorithms. + *

    Robustness

    + * Geometrical algorithms involve a combination of combinatorial and numerical computation. As with + * all numerical computation using finite-precision numbers, the algorithms chosen are susceptible to + * problems of robustness. A robustness problem occurs when a numerical calculation produces an + * incorrect answer for some inputs due to round-off errors. Robustness problems are especially + * serious in geometric computation, since they can result in errors during topology building. + *

    + * There are many approaches to dealing with the problem of robustness in geometrical computation. + * Not surprisingly, most robust algorithms are substantially more complex and less performant than + * the non-robust versions. Fortunately, JTS is sensitive to robustness problems in only a few key + * functions (such as line intersection and the point-in-polygon test). There are efficient robust + * algorithms available for these functions, and these algorithms are implemented in JTS. + *

    Computational Performance

    + * Runtime performance is an important consideration for a production-quality implementation of + * geometric algorithms. The most computationally intensive algorithm used in JTS is intersection + * detection. JTS methods need to determine both all intersection between the line segments in a + * single Geometry (self-intersection) and all intersections between the line segments of two different + * Geometries. + *

    + * The obvious naive algorithm for intersection detection (comparing every segment with every other) + * has unacceptably slow performance. There is a large literature of faster algorithms for intersection + * detection. Unfortunately, many of them involve substantial code complexity. JTS tries to balance code + * simplicity with performance gains. It uses some simple techniques to produce substantial performance + * gains for common types of input data. + *

    Package Specification

    + *
    + */ +package org.locationtech.jts.algorithm; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/package.html b/modules/core/src/main/java/org/locationtech/jts/algorithm/package.html deleted file mode 100644 index db0faacdb7..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/package.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - -Contains classes and interfaces implementing fundamental computational geometry algorithms. - -

    Robustness

    - -Geometrical algorithms involve a combination of combinatorial and numerical computation. As with -all numerical computation using finite-precision numbers, the algorithms chosen are susceptible to -problems of robustness. A robustness problem occurs when a numerical calculation produces an -incorrect answer for some inputs due to round-off errors. Robustness problems are especially -serious in geometric computation, since they can result in errors during topology building. -

    -There are many approaches to dealing with the problem of robustness in geometrical computation. -Not surprisingly, most robust algorithms are substantially more complex and less performant than -the non-robust versions. Fortunately, JTS is sensitive to robustness problems in only a few key -functions (such as line intersection and the point-in-polygon test). There are efficient robust -algorithms available for these functions, and these algorithms are implemented in JTS. - -

    Computational Performance

    - -Runtime performance is an important consideration for a production-quality implementation of -geometric algorithms. The most computationally intensive algorithm used in JTS is intersection -detection. JTS methods need to determine both all intersection between the line segments in a -single Geometry (self-intersection) and all intersections between the line segments of two different -Geometries. -

    -The obvious naive algorithm for intersection detection (comparing every segment with every other) -has unacceptably slow performance. There is a large literature of faster algorithms for intersection -detection. Unfortunately, many of them involve substantial code complexity. JTS tries to balance code -simplicity with performance gains. It uses some simple techniques to produce substantial performance -gains for common types of input data. - - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/awt/ShapeWriter.java b/modules/core/src/main/java/org/locationtech/jts/awt/ShapeWriter.java index a63d763838..6b18784b3a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/awt/ShapeWriter.java +++ b/modules/core/src/main/java/org/locationtech/jts/awt/ShapeWriter.java @@ -120,7 +120,7 @@ public ShapeWriter() { *

    * The default is false. * - * @param doDecimation whether decimation is to be used + * @param doRemoveDuplicatePoints whether decimation is to be used to remove duplicate points */ public void setRemoveDuplicatePoints(boolean doRemoveDuplicatePoints) { diff --git a/modules/core/src/main/java/org/locationtech/jts/awt/package-info.java b/modules/core/src/main/java/org/locationtech/jts/awt/package-info.java new file mode 100644 index 0000000000..05356ba077 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/awt/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform conversions from Java2D shape objects. + */ +package org.locationtech.jts.awt; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/awt/package.html b/modules/core/src/main/java/org/locationtech/jts/awt/package.html deleted file mode 100644 index d3b657aed2..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/awt/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform conversions from Java2D shape objects. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/densify/package-info.java b/modules/core/src/main/java/org/locationtech/jts/densify/package-info.java new file mode 100644 index 0000000000..14ee7e7532 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/densify/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform densification on geometries. + */ +package org.locationtech.jts.densify; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/densify/package.html b/modules/core/src/main/java/org/locationtech/jts/densify/package.html deleted file mode 100644 index 348dbc0d47..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/densify/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform densification on geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/dissolve/LineDissolver.java b/modules/core/src/main/java/org/locationtech/jts/dissolve/LineDissolver.java index 6baa3a94ec..d2ea4a2361 100644 --- a/modules/core/src/main/java/org/locationtech/jts/dissolve/LineDissolver.java +++ b/modules/core/src/main/java/org/locationtech/jts/dissolve/LineDissolver.java @@ -33,7 +33,7 @@ /** * Dissolves the linear components * from a collection of {@link Geometry}s - * into a set of maximal-length {@link Linestring}s + * into a set of maximal-length {@link LineString}s * in which every unique segment appears once only. * The output linestrings run between node vertices * of the input, which are vertices which have @@ -70,8 +70,8 @@ public static Geometry dissolve(Geometry g) private Geometry result; private GeometryFactory factory; - private DissolveEdgeGraph graph; - private List lines = new ArrayList(); + private final DissolveEdgeGraph graph; + private final List lines = new ArrayList(); public LineDissolver() { @@ -153,7 +153,7 @@ private void computeResult() { result = factory.buildGeometry(lines); } - private Stack nodeEdgeStack = new Stack(); + private final Stack nodeEdgeStack = new Stack(); private void process(HalfEdge e) { HalfEdge eNode = e.prevNode(); diff --git a/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java b/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java index 8be390d96d..690baa370e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java @@ -82,7 +82,7 @@ public HalfEdge(Coordinate orig) { * Links this edge with its sym (opposite) edge. * This must be done for each pair of edges created. * - * @param e the sym edge to link. + * @param sym the sym edge to link. */ public void link(HalfEdge sym) { @@ -480,7 +480,7 @@ public int degree() { /** * Finds the first node previous to this edge, if any. - * A node has degree <> 2. + * A node has degree {@code <> 2}. * If no such node exists (i.e. the edge is part of a ring) * then null is returned. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Coordinate.java b/modules/core/src/main/java/org/locationtech/jts/geom/Coordinate.java index 355d9dd35a..a2f381a65b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Coordinate.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Coordinate.java @@ -62,7 +62,8 @@ public class Coordinate implements Comparable, Cloneable, Serializab * Standard ordinate index value for, where Z is 2. * *

    This constant assumes XYZM coordinate sequence definition, please check this assumption - * using {@link #getDimension()} and {@link #getMeasures()} before use. + * using {@link CoordinateSequence#getDimension()} and {@link CoordinateSequence#getMeasures()} + * before use. */ public static final int Z = 2; @@ -70,7 +71,8 @@ public class Coordinate implements Comparable, Cloneable, Serializab * Standard ordinate index value for, where M is 3. * *

    This constant assumes XYZM coordinate sequence definition, please check this assumption - * using {@link #getDimension()} and {@link #getMeasures()} before use. + * using {@link CoordinateSequence#getDimension()} and {@link CoordinateSequence#getMeasures()} + * before use. */ public static final int M = 3; @@ -220,7 +222,7 @@ public void setM(double m) { * Gets the ordinate value for the given index. * * The base implementation supports values for the index are - * {@link X}, {@link Y}, and {@link Z}. + * {@link #X}, {@link #Y}, and {@link #Z}. * * @param ordinateIndex the ordinate index * @return the value of the ordinate @@ -241,7 +243,7 @@ public double getOrdinate(int ordinateIndex) * to a given value. * * The base implementation supported values for the index are - * {@link X}, {@link Y}, and {@link Z}. + * {@link #X}, {@link #Y}, and {@link #Z}. * * @param ordinateIndex the ordinate index * @param value the value to set @@ -531,8 +533,8 @@ public DimensionalComparator(int dimensionsToTest) * Compares two {@link Coordinate}s along to the number of * dimensions specified. * - * @param o1 a {@link Coordinate} - * @param o2 a {link Coordinate} + * @param c1 a {@link Coordinate} + * @param c2 a {link Coordinate} * @return -1, 0, or 1 depending on whether o1 is less than, * equal to, or greater than 02 * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java index a8387064f5..a9ff8a0939 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -371,7 +369,7 @@ public static Coordinate[] toCoordinateArray(Collection coordList) { } /** - * Tests whether {@link Coordinate#equals(Object) returns true for any two consecutive Coordinates + * Tests whether {@link Coordinate#equals(Object)} returns true for any two consecutive Coordinates * in the given array. * * @param coord an array of coordinates diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateFilter.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateFilter.java index 1370335576..a6b8c05562 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateList.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateList.java index d0fe5a9387..4c7e5997a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateList.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateList.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -65,10 +63,6 @@ public CoordinateList(Coordinate[] coord, boolean allowRepeated) add(coord, allowRepeated); } - public boolean add(Coordinate coord) { - return super.add(coord); - } - public Coordinate getCoordinate(int i) { return (Coordinate) get(i); } @@ -221,7 +215,7 @@ public Coordinate[] toCoordinateArray() * Creates an array containing the coordinates in this list, * oriented in the given direction (forward or reverse). * - * @param direction the direction value: true for forward, false for reverse + * @param isForward true if the direction is forward, false for reverse * @return an oriented array of coordinates */ public Coordinate[] toCoordinateArray(boolean isForward) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequence.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequence.java index e066847ff5..071d235520 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequence.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequence.java @@ -33,12 +33,10 @@ * The new Geometries * will use the custom CoordinateSequence implementation. *

    - * For an example, see the code for - * {@link ExtendedCoordinateExample}. + * For an example, see the code for ExtendedCoordinateExample. * * @see CoordinateArraySequenceFactory * @see PackedCoordinateSequenceFactory - * @see ExtendedCoordinateExample * * @version 1.7 */ @@ -108,7 +106,7 @@ default boolean hasZ() { /** * Tests whether the coordinates in the sequence have measures associated with them. Returns true - * if {@link #getMeasures()} > 0. See {@link #getMeasures()} to determine the number of measures + * if {@link #getMeasures()} {@code > 0}. See {@link #getMeasures()} to determine the number of measures * present. * * @return true if {@link #getM(int)} is supported. @@ -170,7 +168,7 @@ default Coordinate createCoordinate() { /** * Returns ordinate X (0) of the specified coordinate. * - * @param index + * @param index the coordinate index in the sequence * @return the value of the X ordinate in the index'th coordinate */ double getX(int index); @@ -178,15 +176,15 @@ default Coordinate createCoordinate() { /** * Returns ordinate Y (1) of the specified coordinate. * - * @param index + * @param index the coordinate index in the sequence * @return the value of the Y ordinate in the index'th coordinate */ double getY(int index); /** * Returns ordinate Z of the specified coordinate if available. - * - * @param index + * + @param index the coordinate index in the sequence * @return the value of the Z ordinate in the index'th coordinate, or Double.NaN if not defined. */ default double getZ(int index) @@ -201,7 +199,7 @@ default double getZ(int index) /** * Returns ordinate M of the specified coordinate if available. * - * @param index + * @param index the coordinate index in the sequence * @return the value of the M ordinate in the index'th coordinate, or Double.NaN if not defined. */ default double getM(int index) @@ -225,6 +223,7 @@ default double getM(int index) * * @param index the coordinate index in the sequence * @param ordinateIndex the ordinate index in the coordinate (in range [0, dimension-1]) + * @return ordinate value */ double getOrdinate(int index, int ordinateIndex); diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequenceFilter.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequenceFilter.java index 7a054597b5..7f121cc6a7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequenceFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequenceFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java index 9ede9eabd3..d87b113b49 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateSequences.java @@ -185,12 +185,16 @@ public static boolean isEqual(CoordinateSequence cs1, CoordinateSequence cs2) { for (int d = 0; d < dim; d++) { double v1 = cs1.getOrdinate(i, d); double v2 = cs2.getOrdinate(i, d); - if (cs1.getOrdinate(i, d) == cs2.getOrdinate(i, d)) + if (cs1.getOrdinate(i, d) == cs2.getOrdinate(i, d)) { continue; - // special check for NaNs - if (Double.isNaN(v1) && Double.isNaN(v2)) + } + else if (Double.isNaN(v1) && Double.isNaN(v2)) { + // special check for NaNs continue; - return false; + } + else { + return false; + } } } return true; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateXYM.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateXYM.java index 5bb4215060..eda6c4bc47 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateXYM.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateXYM.java @@ -36,7 +36,7 @@ public class CoordinateXYM extends Coordinate { * Standard ordinate index value for M in XYM sequences. * *

    This constant assumes XYM coordinate sequence definition. Check this assumption using - * {@link #getDimension()} and {@link #getMeasures()} before use. + * {@link CoordinateSequence#getDimension()} and {@link CoordinateSequence#getMeasures()} before use. */ public static final int M = 2; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Dimension.java b/modules/core/src/main/java/org/locationtech/jts/geom/Dimension.java index 17500b9613..9fff8d003a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Dimension.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Dimension.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java index 62ca39105c..4a72f0e416 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollection.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollectionIterator.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollectionIterator.java index f702ddeeff..dfeecf50c9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollectionIterator.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryCollectionIterator.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryComponentFilter.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryComponentFilter.java index 35a5deb19a..6573dea239 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryComponentFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryComponentFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFactory.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFactory.java index 28bec7c791..de8da951db 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFactory.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFactory.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFilter.java b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFilter.java index 823a8a72fd..6a87bacfe1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/IntersectionMatrix.java b/modules/core/src/main/java/org/locationtech/jts/geom/IntersectionMatrix.java index 4bb94d929b..cf771f781e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/IntersectionMatrix.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/IntersectionMatrix.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java index ad63994695..a94aeff7f8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LineSegment.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java b/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java index da44edebed..c903bd4246 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LinearRing.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Location.java b/modules/core/src/main/java/org/locationtech/jts/geom/Location.java index 740dcb9934..dd0499422e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Location.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Location.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java index 0080a44761..ec14068170 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPoint.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java index 9ec9f94a93..d795b4db7e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/MultiPolygon.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Point.java b/modules/core/src/main/java/org/locationtech/jts/geom/Point.java index 6114c9b136..cbe145416f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Point.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Point.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java b/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java index 494fd0d8fb..94a8bc17fa 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java index b14fb13a6f..03ef2bbe13 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/TopologyException.java b/modules/core/src/main/java/org/locationtech/jts/geom/TopologyException.java index 0f0f7f13af..19aba63303 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/TopologyException.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/TopologyException.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/impl/CoordinateArraySequence.java b/modules/core/src/main/java/org/locationtech/jts/geom/impl/CoordinateArraySequence.java index c47499aa07..da703d3e54 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/impl/CoordinateArraySequence.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/impl/CoordinateArraySequence.java @@ -12,7 +12,6 @@ package org.locationtech.jts.geom.impl; import java.io.Serializable; -import java.lang.reflect.Array; import org.locationtech.jts.geom.*; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java b/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java index 4486734d63..3c705a7899 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java @@ -250,7 +250,7 @@ public static class Double extends PackedCoordinateSequence { /** * Builds a new packed coordinate sequence * - * @param coords an array of double values that contains the ordinate values of the sequence + * @param coords an array of double values that contains the ordinate values of the sequence * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence. * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has. */ @@ -266,7 +266,7 @@ public Double(double[] coords, int dimension, int measures) { /** * Builds a new packed coordinate sequence out of a float coordinate array * - * @param coords an array of float values that contains the ordinate values of the sequence + * @param coords an array of float values that contains the ordinate values of the sequence * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence. * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has. */ @@ -433,7 +433,7 @@ public static class Float extends PackedCoordinateSequence { /** * Constructs a packed coordinate sequence from an array of floats * - * @param coords an array of float values that contains the ordinate values of the sequence + * @param coords an array of float values that contains the ordinate values of the sequence * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence. * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has. */ @@ -449,7 +449,7 @@ public Float(float[] coords, int dimension, int measures) { /** * Constructs a packed coordinate sequence from an array of doubles * - * @param coords an array of double values that contains the ordinate values of the sequence + * @param coords an array of double values that contains the ordinate values of the sequence * @param dimension the total number of ordinates that make up a {@link Coordinate} in this sequence. * @param measures the number of measure-ordinates each {@link Coordinate} in this sequence has. */ diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/impl/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geom/impl/package-info.java new file mode 100644 index 0000000000..e4935d02ff --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geom/impl/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Implementations of interfaces for geometric structures. + */ +package org.locationtech.jts.geom.impl; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/impl/package.html b/modules/core/src/main/java/org/locationtech/jts/geom/impl/package.html deleted file mode 100644 index 3c5e4dbe0b..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geom/impl/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Implementations of interfaces for geometric structures. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geom/package-info.java new file mode 100644 index 0000000000..babcfd6648 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geom/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains the Geometry interface hierarchy and supporting classes. + *

    + * The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. + *

    + * JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. + *

    Package Specification

    + * + */ +package org.locationtech.jts.geom; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/package.html b/modules/core/src/main/java/org/locationtech/jts/geom/package.html deleted file mode 100644 index 54500b07d8..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geom/package.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - -Contains the Geometry interface hierarchy and supporting classes. -

    -The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. -

    -JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/prep/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geom/prep/package-info.java new file mode 100644 index 0000000000..de35754fdf --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geom/prep/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform optimized geometric operations on suitably prepared geometries. + */ +package org.locationtech.jts.geom.prep; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/prep/package.html b/modules/core/src/main/java/org/locationtech/jts/geom/prep/package.html deleted file mode 100644 index ba5ef77a6e..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geom/prep/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform optimized geometric operations on suitably prepared geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java index 0f67241e1e..ee9b079f00 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java @@ -50,9 +50,9 @@ * Affine transformations can be composed using the {@link #compose} method. * Composition is computed via multiplication of the * transformation matrices, and is defined as: - *
    + * 
    * A.compose(B) = TB x TA - *
    + * * This produces a transformation whose effect is that of A followed by B. * The methods {@link #reflect}, {@link #rotate}, * {@link #scale}, {@link #shear}, and {@link #translate} @@ -541,13 +541,13 @@ public AffineTransformation setToReflection(double x0, double y0, double x1, dou * about the line defined by vector (x,y). * The transformation for a reflection * is computed by: - *
    -   * d = sqrt(x2 + y2)  
    -   * sin = y / d;
    -   * cos = x / d;
    -   * 
    -   * Tref = Trot(sin, cos) x Tscale(1, -1) x Trot(-sin, cos)
    -   * 
    + *
    + * d = sqrt(x2 + y2)
    + * sin = y / d;
    + * cos = x / d;
    + *
    + * Tref = Trot(sin, cos) x Tscale(1, -1) x Trot(-sin, cos)
    + *
    * * @param x the x-component of the reflection line vector * @param y the y-component of the reflection line vector @@ -899,9 +899,9 @@ public AffineTransformation translate(double x, double y) * is equal to applying this transformation * followed by the argument transformation. * Mathematically, - *
    +   * 
    * A.compose(B) = TB x TA - *
    + * * * @param trans an affine transformation * @return this transformation, with an updated matrix @@ -930,9 +930,9 @@ public AffineTransformation compose(AffineTransformation trans) * is equal to applying the argument transformation * followed by this transformation. * Mathematically, - *
    +   * 
    * A.composeBefore(B) = TA x TB - *
    + * * * @param trans an affine transformation * @return this transformation, with an updated matrix diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/ComponentCoordinateExtracter.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/ComponentCoordinateExtracter.java index 908e248ac1..abb6c03216 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/ComponentCoordinateExtracter.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/ComponentCoordinateExtracter.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryEditor.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryEditor.java index adc87801b1..abbec9a5df 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryEditor.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryEditor.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 c28721859c..cde0cf1f6a 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 df60ca1271..5782a0f900 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 8a04efab8d..2975952d30 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 0958e67228..ca0432cccb 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 859200188c..686038ae1e 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 @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/package-info.java new file mode 100644 index 0000000000..2d9fa460e8 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes that parse and modify Geometry objects. + */ +package org.locationtech.jts.geom.util; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/package.html b/modules/core/src/main/java/org/locationtech/jts/geom/util/package.html deleted file mode 100644 index ce18f258b5..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes that parse and modify Geometry objects. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Depth.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Depth.java index cfe2ba4610..cca8d5dadf 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Depth.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Depth.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -63,6 +61,8 @@ public void add(int geomIndex, int posIndex, int location) } /** * A Depth object is null (has never been initialized) if all depths are null. + * + * @return True if depth is null (has never been initialized) */ public boolean isNull() { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdge.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdge.java index 8e85ccaab9..91f8307e86 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdge.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -30,7 +27,12 @@ public class DirectedEdge /** * Computes the factor for the change in depth when moving from one location to another. - * E.g. if crossing from the INTERIOR to the EXTERIOR the depth decreases, so the factor is -1 + * E.g. if crossing from the {@link Location#INTERIOR} to the{@link Location#EXTERIOR} + * the depth decreases, so the factor is -1. + * + * @param currLocation Current location + * @param nextLocation Next location + * @return change of depth moving from currLocation to nextLocation */ public static int depthFactor(int currLocation, int nextLocation) { @@ -80,6 +82,15 @@ public DirectedEdge(Edge edge, boolean isForward) public EdgeRing getMinEdgeRing() { return minEdgeRing; } public int getDepth(int position) { return depth[position]; } + /** + * Set depth for a position. + * + * You may also use {@link #setEdgeDepths(int, int)} to + * update depth and opposite depth together. + * + * @param position Position to update + * @param depthVal Depth at the provided position + */ public void setDepth(int position, int depthVal) { if (depth[position] != -999) { @@ -101,9 +112,12 @@ public int getDepthDelta() } /** - * setVisitedEdge marks both DirectedEdges attached to a given Edge. + * Marks both DirectedEdges attached to a given Edge. + * * This is used for edges corresponding to lines, which will only * appear oriented in a single direction in the result. + * + * @param isVisited True to mark edge as visited */ public void setVisitedEdge(boolean isVisited) { @@ -132,6 +146,8 @@ public void setSym(DirectedEdge de) *
  • at least one of the labels is a line label *
  • any labels which are not line labels have all Locations = EXTERIOR * + * + * @return If edge is a line edge */ public boolean isLineEdge() { @@ -178,6 +194,9 @@ private void computeDirectedLabel() /** * Set both edge depths. One depth for a given side is provided. The other is * computed depending on the Location transition and the depthDelta of the edge. + * + * @param position Position to update + * @param depth Depth at the provided position */ public void setEdgeDepths(int position, int depth) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java index 3f1ef6e32c..cf5f818125 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/DirectedEdgeStar.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -141,7 +138,9 @@ public void mergeSymLabels() } /** - * Update incomplete dirEdge labels from the labelling for the node + * Update incomplete dirEdge labels from the labelling for the node. + * + * @param nodeLabel Label to apply */ public void updateLabelling(Label nodeLabel) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java index 71b97fe67f..1ad1c1a409 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -34,6 +31,8 @@ public class Edge /** * Updates an IM from the label for an edge. * Handles edges from both L and A geometries. + * @param label Label defining position + * @param im intersection matrix */ public static void updateIM(Label label, IntersectionMatrix im) { @@ -115,6 +114,8 @@ public boolean isClosed() /** * An Edge is collapsed if it is an Area edge and it consists of * two segments which are equal and opposite (eg a zero-width V). + * + * @return zero-width V area edge, consisting of two segments which are equal and of oppose orientation */ public boolean isCollapsed() { @@ -144,6 +145,9 @@ public boolean isIsolated() /** * Adds EdgeIntersections for one or both * intersections found for a segment of an edge to the edge intersection list. + * @param li Determining number of intersections to add + * @param segmentIndex Segment index to add + * @param geomIndex Geometry index to add */ public void addIntersections(LineIntersector li, int segmentIndex, int geomIndex) { @@ -155,6 +159,11 @@ public void addIntersections(LineIntersector li, int segmentIndex, int geomIndex * Add an EdgeIntersection for intersection intIndex. * An intersection that falls exactly on a vertex of the edge is normalized * to use the higher of the two possible segmentIndexes + * + * @param li Determining number of intersections to add + * @param segmentIndex Segment index to add + * @param geomIndex Geometry index to add + * @param intIndex intIndex is 0 or 1 */ public void addIntersection(LineIntersector li, int segmentIndex, int geomIndex, int intIndex) { @@ -223,6 +232,9 @@ public boolean equals(Object o) } /** + * Check if coordinate sequences of the Edges are identical. + * + * @param e Edge * @return true if the coordinate sequences of the Edges are identical */ public boolean isPointwiseEqual(Edge e) diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEnd.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEnd.java index bedb7c5974..136849bcfd 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEnd.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEnd.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -94,6 +91,9 @@ public int compareTo(Object obj) * are different, it it trivial to determine which vector is "greater". * - if the vectors lie in the same quadrant, the computeOrientation function * can be used to decide the relative orientation of the vectors. + * + * @param e EdgeEnd + * @return direction comparison */ public int compareDirection(EdgeEnd e) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEndStar.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEndStar.java index 0e77dc47e5..5477cc12f9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEndStar.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeEndStar.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -59,12 +56,15 @@ public EdgeEndStar() /** * Insert a EdgeEnd into this EdgeEndStar + * @param e EdgeEnd */ abstract public void insert(EdgeEnd e); /** * Insert an EdgeEnd into the map, and clear the edgeList cache, * since the list of edges has now changed + * @param e EdgeEnd + * @param obj Object */ protected void insertEdgeEnd(EdgeEnd e, Object obj) { @@ -92,6 +92,8 @@ public int getDegree() * copying the map collection to a list. (This assumes that * once an iterator is requested, it is likely that insertion into * the map is complete). + * + * @return access to ordered list of edges */ public Iterator iterator() { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersection.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersection.java index cd2613820e..d118398aae 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersection.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersection.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -32,10 +29,22 @@ public class EdgeIntersection implements Comparable { - public Coordinate coord; // the point of intersection - public int segmentIndex; // the index of the containing line segment in the parent edge - public double dist; // the edge distance of this point along the containing line segment + /** Point of intersection */ + public Coordinate coord; + + /** Index of the containing line segment in the parent edge */ + public int segmentIndex; + /** Edge distance of this point along the containing line segment */ + public double dist; + + /** + * EdgeIntersection. + * + * @param coord Point of intersection + * @param segmentIndex Index of the containing line segment in the parent edge + * @param dist Edge distance of this point along the containing line segment + */ public EdgeIntersection(Coordinate coord, int segmentIndex, double dist) { this.coord = new Coordinate(coord); this.segmentIndex = segmentIndex; @@ -54,9 +63,13 @@ public int compareTo(Object obj) return compare(other.segmentIndex, other.dist); } /** - * @return -1 this EdgeIntersection is located before the argument location - * @return 0 this EdgeIntersection is at the argument location - * @return 1 this EdgeIntersection is located after the argument location + * Comparison with segment and distance. + * + * @param segmentIndex index of the containing line segment + * @param dist dge distance of this point along the containing line segment + * @return {@code 1} this EdgeIntersection is located before the argument location, + * {@code 0} this EdgeIntersection is at the argument location, + * {@code 1} this EdgeIntersection is located after the argument location */ public int compare(int segmentIndex, double dist) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersectionList.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersectionList.java index ad1b103e44..e3d6b75973 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersectionList.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeIntersectionList.java @@ -40,6 +40,11 @@ public EdgeIntersectionList(Edge edge) /** * Adds an intersection into the list, if it isn't already there. * The input segmentIndex and dist are expected to be normalized. + * + * @param intPt Point of intersection + * @param segmentIndex Index of the containing line segment in the parent edge + * @param dist Edge distance of this point along the containing line segment + * * @return the EdgeIntersection found or added */ public EdgeIntersection add(Coordinate intPt, int segmentIndex, double dist) diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeList.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeList.java index c71dd405f6..3582a99dd0 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeList.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeList.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -45,6 +42,8 @@ public EdgeList() { /** * Insert an edge unless it is already in the list + * + * @param e Edge */ public void add(Edge e) { @@ -65,6 +64,7 @@ public void addAll(Collection edgeColl) /** * If there is an edge equal to e already in the list, return it. * Otherwise return null. + * @param e Edge * @return equal edge, if there is one already in the list * null otherwise */ @@ -82,6 +82,7 @@ public Edge findEqualEdge(Edge e) /** * If the edge e is already in the list, return its index. + * @param e Edge * @return index, if e is already in the list * -1 otherwise */ diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeNodingValidator.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeNodingValidator.java index 39fe6d5e9d..396462c2c6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeNodingValidator.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeNodingValidator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeRing.java index 0495c8ae61..7d82f09d50 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/EdgeRing.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -107,6 +104,8 @@ public void computeRing() /** * Returns the list of DirectedEdges that make up this EdgeRing + * + * @return List of DirectedEdges */ public List getEdges() { return edges; } @@ -213,6 +212,9 @@ protected void addPoints(Edge edge, boolean isForward, boolean isFirstEdge) /** * This method will cause the ring to be computed. * It will also check any holes, if they have been assigned. + * + * @param p point + * @return true of ring contains point */ public boolean containsPoint(Coordinate p) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java index 0cdf34bc10..6fc618162b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -76,6 +73,13 @@ public static int determineBoundary(int boundaryCount) } */ + /** + * Determine boundary + * + * @param boundaryNodeRule Boundary node rule + * @param boundaryCount the number of component boundaries that this point occurs in + * @return boundary or interior + */ public static int determineBoundary(BoundaryNodeRule boundaryNodeRule, int boundaryCount) { return boundaryNodeRule.isInBoundary(boundaryCount) @@ -139,7 +143,7 @@ public GeometryGraph(int argIndex, Geometry parentGeom, BoundaryNodeRule boundar } } - /** + /* * This constructor is used by clients that wish to add Edges explicitly, * rather than adding a Geometry. (An example is BufferOp). */ @@ -301,7 +305,7 @@ private void addLineString(LineString line) Edge e = new Edge(coord, new Label(argIndex, Location.INTERIOR)); lineEdgeMap.put(line, e); insertEdge(e); - /** + /* * Add the boundary points of the LineString, if any. * Even if the LineString is closed, add both points as if they were endpoints. * This allows for the case that the node already exists and is a boundary point. @@ -309,12 +313,13 @@ private void addLineString(LineString line) Assert.isTrue(coord.length >= 2, "found LineString with single point"); insertBoundaryPoint(argIndex, coord[0]); insertBoundaryPoint(argIndex, coord[coord.length - 1]); - } /** * Add an Edge computed externally. The label on the Edge is assumed * to be correct. + * + * @param e Edge */ public void addEdge(Edge e) { @@ -328,6 +333,8 @@ public void addEdge(Edge e) /** * Add a point computed externally. The point is assumed to be a * Point Geometry part, which has a location of INTERIOR. + * + * @param pt Coordinate */ public void addPoint(Coordinate pt) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GraphComponent.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GraphComponent.java index b87773884a..ba05a2f98e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GraphComponent.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GraphComponent.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -60,7 +57,9 @@ public void setCovered(boolean isCovered) */ abstract public Coordinate getCoordinate(); /** - * compute the contribution to an IM for this component + * Compute the contribution to an IM for this component. + * + * @param im Intersection matrix */ abstract protected void computeIM(IntersectionMatrix im); /** @@ -74,6 +73,7 @@ public void setCovered(boolean isCovered) /** * Update the IM with the contribution for this component. * A component only contributes if it has a labelling for both parent geometries + * @param im Intersection matrix */ public void updateIM(IntersectionMatrix im) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Label.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Label.java index dfd3671df0..94d019032e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Label.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Label.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -58,6 +55,8 @@ public static Label toLineLabel(Label label) /** * Construct a Label with a single location for both Geometries. * Initialize the locations to Null + * + * @param onLoc On location */ public Label(int onLoc) { @@ -67,6 +66,9 @@ public Label(int onLoc) /** * Construct a Label with a single location for both Geometries. * Initialize the location for the Geometry index. + * + * @param geomIndex Geometry index + * @param onLoc On location */ public Label(int geomIndex, int onLoc) { @@ -77,6 +79,10 @@ public Label(int geomIndex, int onLoc) /** * Construct a Label with On, Left and Right locations for both Geometries. * Initialize the locations for both Geometries to the given values. + * + * @param onLoc On location + * @param rightLoc Right location + * @param leftLoc Left location */ public Label(int onLoc, int leftLoc, int rightLoc) { @@ -86,6 +92,11 @@ public Label(int onLoc, int leftLoc, int rightLoc) /** * Construct a Label with On, Left and Right locations for both Geometries. * Initialize the locations for the given Geometry index. + * + * @param geomIndex Geometry index + * @param onLoc On location + * @param rightLoc Right location + * @param leftLoc Left location */ public Label(int geomIndex, int onLoc, int leftLoc, int rightLoc) { @@ -95,6 +106,8 @@ public Label(int geomIndex, int onLoc, int leftLoc, int rightLoc) } /** * Construct a Label with the same values as the argument Label. + * + * @param lbl Label */ public Label(Label lbl) { @@ -133,8 +146,10 @@ public void setAllLocationsIfNull(int location) } /** * Merge this label with another one. - * Merging updates any null attributes of this label with the attributes from lbl - */ + * Merging updates any null attributes of this label with the attributes from lbl. + * + * @param lbl Label to merge +s */ public void merge(Label lbl) { for (int i = 0; i < 2; i++) { @@ -180,6 +195,7 @@ public boolean allPositionsEqual(int geomIndex, int loc) } /** * Converts one GeometryLocation to a Line location + * @param geomIndex geometry location */ public void toLine(int geomIndex) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Node.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Node.java index a9266f3c8b..a44f267c53 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Node.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Node.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -68,7 +65,9 @@ public boolean isIsolated() */ protected void computeIM(IntersectionMatrix im) {} /** - * Add the edge to the list of edges at this node + * Add the edge to the list of edges at this node. + * + * @param e EdgeEnd */ public void add(EdgeEnd e) { @@ -87,8 +86,9 @@ public void mergeLabel(Node n) * the merged location for each LabelElement is computed. * The location for the corresponding node LabelElement is set to the result, * as long as the location is non-null. + * + * @param label2 Label to merge */ - public void mergeLabel(Label label2) { for (int i = 0; i < 2; i++) { @@ -110,6 +110,7 @@ public void setLabel(int argIndex, int onLocation) /** * Updates the label of a node to BOUNDARY, * obeying the mod-2 boundaryDetermination rule. + * @param argIndex location index */ public void setLabelBoundary(int argIndex) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeFactory.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeFactory.java index 95dbdff1e5..75a896eeb1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeFactory.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeFactory.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -21,6 +20,8 @@ public class NodeFactory { /** * The basic node constructor does not allow for incident edges + * @param coord Coordinate + * @return created node */ public Node createNode(Coordinate coord) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeMap.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeMap.java index 37051d529d..66ffa2c06f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeMap.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/NodeMap.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -51,6 +48,8 @@ protected Node createNode(Coordinate coord) */ /** * This method expects that a node has a coordinate value. + * @param coord Coordinate + * @return node for the provided coord */ public Node addNode(Coordinate coord) { @@ -77,6 +76,8 @@ public Node addNode(Node n) * Adds a node for the start point of this EdgeEnd * (if one does not already exist in this map). * Adds the EdgeEnd to the (possibly new) node. + * + * @param e EdgeEnd */ public void add(EdgeEnd e) { @@ -85,6 +86,9 @@ public void add(EdgeEnd e) n.add(e); } /** + * Find coordinate. + * + * @param coord Coordinate to find * @return the node if found; null otherwise */ public Node find(Coordinate coord) { return (Node) nodeMap.get(coord); } diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/PlanarGraph.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/PlanarGraph.java index 5607af97f7..1c737e237a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/PlanarGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/PlanarGraph.java @@ -50,6 +50,8 @@ public class PlanarGraph * For nodes in the Collection, link the DirectedEdges at the node that are in the result. * This allows clients to link only a subset of nodes in the graph, for * efficiency (because they know that only a subset is of interest). + * + * @param nodes Collection of nodes */ public static void linkResultDirectedEdges(Collection nodes) { @@ -97,6 +99,9 @@ public void add(EdgeEnd e) public Node addNode(Node node) { return nodes.addNode(node); } public Node addNode(Coordinate coord) { return nodes.addNode(coord); } /** + * Find coordinate. + * + * @param coord Coordinate to find * @return the node if found; null otherwise */ public Node find(Coordinate coord) { return nodes.find(coord); } @@ -104,6 +109,8 @@ public void add(EdgeEnd e) /** * Add a set of edges to the graph. For each edge two DirectedEdges * will be created. DirectedEdges are NOT linked by this method. + * + * @param edgesToAdd Set of edges to add to the graph */ public void addEdges(List edgesToAdd) { @@ -150,6 +157,7 @@ public void linkAllDirectedEdges() * Returns the EdgeEnd which has edge e as its base edge * (MD 18 Feb 2002 - this should return a pair of edges) * + * @param e Edge * @return the edge, if found * null if the edge was not found */ @@ -166,6 +174,8 @@ public EdgeEnd findEdgeEnd(Edge e) /** * Returns the edge whose first two coordinates are p0 and p1 * + * @param p0 first coordinate to match + * @param p1 second coordinate to match * @return the edge, if found * null if the edge was not found */ @@ -183,7 +193,9 @@ public Edge findEdge(Coordinate p0, Coordinate p1) * Returns the edge which starts at p0 and whose first segment is * parallel to p1 * - * @return the edge, if found + * @param p0 Starting coordinate + * @param p1 Coordinate used to establish direction + * @return matching edge, if found * null if the edge was not found */ public Edge findEdgeInSameDirection(Coordinate p0, Coordinate p1) diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/TopologyLocation.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/TopologyLocation.java index 818e2848c3..388b41152f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/TopologyLocation.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/TopologyLocation.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -54,6 +51,9 @@ public TopologyLocation(int[] location) * parameters are Location.NULL, Location.EXTERIOR, Location.BOUNDARY, * and Location.INTERIOR. * @see Location + * @param on on position + * @param left left position + * @param right right position */ public TopologyLocation(int on, int left, int right) { init(3); @@ -158,6 +158,8 @@ public boolean allPositionsEqual(int loc) /** * merge updates only the NULL attributes of this object * with the attributes of another. + * + * @param gl Topology location */ public void merge(TopologyLocation gl) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/EdgeSetIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/EdgeSetIntersector.java index 148aced980..1cac1104c4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/EdgeSetIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/EdgeSetIntersector.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -49,6 +46,9 @@ public EdgeSetIntersector() { /** * Computes all mutual intersections between two sets of edges. + * @param edges0 set of edges + * @param edges1 set of edges + * @param si segment intersector */ abstract public void computeIntersections(List edges0, List edges1, SegmentIntersector si); diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChain.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChain.java index 0b933e2bf5..fb32135c4b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChain.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChain.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainEdge.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainEdge.java index 56f03d477e..20d0b51443 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainEdge.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java index d6e4fa0a30..f0f785d2f5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/MonotoneChainIndexer.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java index 0c4739f1f4..3794a0c50c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -96,11 +93,15 @@ public boolean isDone() { * in the interior of the entire Geometry, since another edge may have * an endpoint equal to the intersection, which according to SFS semantics * can result in the point being on the Boundary of the Geometry. + * + * @return indicates a proper intersection with an interior to at least two line segments */ public boolean hasProperIntersection() { return hasProper; } /** * A proper interior intersection is a proper intersection which is not * contained in the set of boundary nodes set for this SegmentIntersector. + * + * @return indicates a proper interior intersection */ public boolean hasProperInteriorIntersection() { return hasProperInterior; } @@ -110,6 +111,12 @@ public boolean isDone() { * is simply the point shared by adjacent line segments. * Note that closed edges require a special check for the point shared by the beginning * and end segments. + * + * @oaram e0 edge 0 + * @param segIndex0 segment index 0 + * @param e1 edge 1 + * @param segIndex1 segment index 1 + * @return indicates a trivial intersection, a point shared by adjacent line segments */ private boolean isTrivialIntersection(Edge e0, int segIndex0, Edge e1, int segIndex1) { diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleEdgeSetIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleEdgeSetIntersector.java index e368a199b2..6bbc751ff6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleEdgeSetIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleEdgeSetIntersector.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleMCSweepLineIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleMCSweepLineIntersector.java index d21c5d6634..0607e2ed15 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleMCSweepLineIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleMCSweepLineIntersector.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleSweepLineIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleSweepLineIntersector.java index 9c00aec4e3..eb3b4d7405 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleSweepLineIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SimpleSweepLineIntersector.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineEvent.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineEvent.java index 347dba433b..5c7ece9eec 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineEvent.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineEvent.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineSegment.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineSegment.java index cd4066955e..7433d8ab4f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineSegment.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SweepLineSegment.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package-info.java new file mode 100644 index 0000000000..c56420ecd7 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that implement indexes for performing noding on geometry graph edges. + */ +package org.locationtech.jts.geomgraph.index; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package.html b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package.html deleted file mode 100644 index d6c2cbe103..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes that implement indexes for performing noding on geometry graph edges. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/package-info.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/package-info.java new file mode 100644 index 0000000000..f7035a2a74 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that implement topology graphs. + *

    + * The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. + *

    + * JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. + *

    Package Specification

    + * + */ +package org.locationtech.jts.geomgraph; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/package.html b/modules/core/src/main/java/org/locationtech/jts/geomgraph/package.html deleted file mode 100644 index 1be97f6e80..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/package.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - -Contains classes that implement topology graphs. -

    -The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. -

    -JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/SpatialIndex.java b/modules/core/src/main/java/org/locationtech/jts/index/SpatialIndex.java index 7f14ef8813..c44fcf9fca 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/SpatialIndex.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/SpatialIndex.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Bintree.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Bintree.java index da6e61df1a..b5927778ad 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Bintree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Bintree.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -127,7 +126,7 @@ public void insert(Interval itemInterval, Object item) /** * Removes a single item from the tree. * - * @param itemEnv the Envelope of the item to be removed + * @param itemInterval the interval of the item to be removed * @param item the item to remove * @return true if the item was found (and thus removed) */ @@ -173,7 +172,7 @@ public List query(Interval interval) * If the query interval is null, add all items in the tree. * * @param interval a query interval, or null - * @param resultItems the candidate items found + * @param foundItems the candidate items found */ public void query(Interval interval, Collection foundItems) { diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Interval.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Interval.java index 63f2a685ad..e6a3f29015 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Interval.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Interval.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Key.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Key.java index 2cd7e4e2f5..044f65525e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Key.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Key.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Node.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Node.java index 4dc32ff0d0..ed41be0c5e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Node.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Node.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/NodeBase.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/NodeBase.java index 9221b08292..9ee784488d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/NodeBase.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/NodeBase.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Root.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Root.java index cce5927508..087edaf93c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/Root.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/Root.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/bintree/package-info.java new file mode 100644 index 0000000000..b1fb71953d --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/bintree/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that implement a Binary Interval Tree index + */ +package org.locationtech.jts.index.bintree; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/bintree/package.html b/modules/core/src/main/java/org/locationtech/jts/index/bintree/package.html deleted file mode 100644 index 9d4dc5d85f..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/bintree/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes that implement a Binary Interval Tree index - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChain.java b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChain.java index 3371e5899d..c4a5bcc897 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChain.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChain.java @@ -1,8 +1,5 @@ - - /* - * Copyright (c) 2016 Vivid Solutions. - * Copyright (c) 2020 Martin Davis. + * Copyright (c) 2016 Vivid Solutions, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainOverlapAction.java b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainOverlapAction.java index 0e585f7304..a3e756c6a9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainOverlapAction.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainOverlapAction.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainSelectAction.java b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainSelectAction.java index 1094078f8d..b53c03e70e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainSelectAction.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/chain/MonotoneChainSelectAction.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -13,7 +11,6 @@ */ package org.locationtech.jts.index.chain; -import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.LineSegment; /** * The action for the internal iterator for performing diff --git a/modules/core/src/main/java/org/locationtech/jts/index/chain/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/chain/package-info.java new file mode 100644 index 0000000000..0ecb9e4642 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/chain/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that implement Monotone Chains + */ +package org.locationtech.jts.index.chain; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/chain/package.html b/modules/core/src/main/java/org/locationtech/jts/index/chain/package.html deleted file mode 100644 index 7becd69ed1..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/chain/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes that implement Monotone Chains - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package-info.java new file mode 100644 index 0000000000..5219d56591 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes to implement an R-tree index for one-dimensional intervals. + */ +package org.locationtech.jts.index.intervalrtree; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package.html b/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package.html deleted file mode 100644 index 07e85cdef3..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/intervalrtree/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes to implement an R-tree index for one-dimensional intervals. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java index d06ff5075c..92a0790d29 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java @@ -85,7 +85,7 @@ public Coordinate getCoordinate() { /** * Gets the user data object associated with this node. - * @return + * @return user data */ public Object getData() { return data; diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index e80e0224ab..f268e6bfca 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -44,7 +44,7 @@ * but an imbalanced tree may be much deeper. * This has a serious impact on query efficiency. * Even worse, since recursion is used for querying the tree - * an extremely deep tree may cause a {@link StackOverflowException}. + * an extremely deep tree may cause a {@link StackOverflowError}. * One solution to this is to randomize the order of points before insertion * (e.g. by using Fisher-Yates shuffling). * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package-info.java new file mode 100644 index 0000000000..60170adf28 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes which implement a k-D tree index over 2-D point data. + */ +package org.locationtech.jts.index.kdtree; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package.html b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package.html deleted file mode 100644 index 66462bf6ba..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes which implement a k-D tree index over 2-D point data. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/package-info.java new file mode 100644 index 0000000000..5211efceb0 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes for various kinds of spatial indexes. + */ +package org.locationtech.jts.index; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/package.html b/modules/core/src/main/java/org/locationtech/jts/index/package.html deleted file mode 100644 index 6fa83a8374..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes for various kinds of spatial indexes. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/DoubleBits.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/DoubleBits.java index 5cf1129584..9f7b030cc7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/DoubleBits.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/DoubleBits.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/IntervalSize.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/IntervalSize.java index dd40cc9bd1..8fe2a9f21b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/IntervalSize.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/IntervalSize.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Key.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Key.java index 07b888b026..ea63eebfe7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Key.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Key.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Node.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Node.java index c8ce8f8df1..9fd2f0c1d9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Node.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Node.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java index f6ac031d6f..d545e22557 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Quadtree.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Quadtree.java index 5dcb78ea35..8ac5563868 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Quadtree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Quadtree.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Root.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Root.java index c70bae3237..be50868932 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Root.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/Root.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package-info.java new file mode 100644 index 0000000000..00848636f2 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that implement a Quadtree spatial index + */ +package org.locationtech.jts.index.quadtree; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package.html b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package.html deleted file mode 100644 index 2e84919618..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes that implement a Quadtree spatial index - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractNode.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractNode.java index de1a2e829d..71d5801962 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractNode.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractNode.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractSTRtree.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractSTRtree.java index cdfbe6bde0..19dee621ad 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractSTRtree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/AbstractSTRtree.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Boundable.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Boundable.java index 4319b26267..9e241cfc18 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Boundable.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Boundable.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/BoundablePairDistanceComparator.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/BoundablePairDistanceComparator.java index b337eda1f0..ae9c52d2a0 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/BoundablePairDistanceComparator.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/BoundablePairDistanceComparator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2017 Jia Yu. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/GeometryItemDistance.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/GeometryItemDistance.java index 89b985609d..22e9e18c8f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/GeometryItemDistance.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/GeometryItemDistance.java @@ -23,7 +23,7 @@ * using to query a single index tree, * the distance metric is anti-reflexive. * That is, if the two arguments are the same Geometry object, - * the distance returned is {@link Double.MAX_VALUE}. + * the distance returned is {@link Double#MAX_VALUE}. * * @author Martin Davis * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java index 28f5573312..0d1d683f5b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/ItemBoundable.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/ItemBoundable.java index 38e308daf4..d11dbe5963 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/ItemBoundable.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/ItemBoundable.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/SIRtree.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/SIRtree.java index 6c78f26fe4..b843e530aa 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/SIRtree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/SIRtree.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java index 3594107d24..a0bfc91e04 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/STRtree.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -230,7 +229,7 @@ public void insert(Envelope itemEnv, Object item) { public List query(Envelope searchEnv) { //Yes this method does something. It specifies that the bounds is an //Envelope. super.query takes an Object, not an Envelope. [Jon Aquino 10/24/2003] - return super.query(searchEnv); + return super.query((Object)searchEnv); } /** diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/package-info.java new file mode 100644 index 0000000000..2d7eaa3a96 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains 2-D and 1-D versions of the Sort-Tile-Recursive (STR) tree, a query-only R-tree. + */ +package org.locationtech.jts.index.strtree; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/package.html b/modules/core/src/main/java/org/locationtech/jts/index/strtree/package.html deleted file mode 100644 index 16a836e3d7..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - -Contains 2-D and 1-D versions of the Sort-Tile-Recursive (STR) tree, a query-only R-tree. - - diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineEvent.java b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineEvent.java index 8a07ea02c7..4b26d51273 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineEvent.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineEvent.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineIndex.java b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineIndex.java index 04e22f2060..1138f42edc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineIndex.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineIndex.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineInterval.java b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineInterval.java index 62a7737740..9f23cf0614 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineInterval.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineInterval.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineOverlapAction.java b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineOverlapAction.java index c47fb8c048..0527898401 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineOverlapAction.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/SweepLineOverlapAction.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package-info.java b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package-info.java new file mode 100644 index 0000000000..2beecf861b --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes which implement a sweepline algorithm for scanning geometric data structures. + */ +package org.locationtech.jts.index.sweepline; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package.html b/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package.html deleted file mode 100644 index 381c0f5f1d..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/index/sweepline/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes which implement a sweepline algorithm for scanning geometric data structures. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/io/ParseException.java b/modules/core/src/main/java/org/locationtech/jts/io/ParseException.java index 29479c0199..7441ca1433 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/ParseException.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/ParseException.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java index c3764f7bea..fe6d7e3e4c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTFileReader.java @@ -93,7 +93,7 @@ public void setLimit(int limit) * after at least one geometry has been read, * to return a partial result. * - * @param isLenient whether to ignore parse errors + * @param isStrict whether to ignore parse errors */ public void setStrictParsing(boolean isStrict) { diff --git a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GeometryStrategies.java b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GeometryStrategies.java index ab0ae92c0b..55b5f56306 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GeometryStrategies.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GeometryStrategies.java @@ -412,7 +412,7 @@ else if(arg.attrs.getIndex(GMLConstants.GML_NAMESPACE,"ts")>=0) } } // fill remaining dim - for(;dimIndex - JTS IO: Java Topology Suite IO Library - - Classes to read and write the GML2 geometry format. - - diff --git a/modules/core/src/main/java/org/locationtech/jts/io/package-info.java b/modules/core/src/main/java/org/locationtech/jts/io/package-info.java new file mode 100644 index 0000000000..11290e9c49 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/io/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains the interfaces for converting JTS objects to and from other formats. + *

    + * The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. + *

    + * JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. + *

    Package Specification

    + * + */ +package org.locationtech.jts.io; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/io/package.html b/modules/core/src/main/java/org/locationtech/jts/io/package.html deleted file mode 100644 index 4df9404f79..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/io/package.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - -Contains the interfaces for converting JTS objects to and from other formats. -

    -The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets. -

    -JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible. In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative. Differences from and elaborations of the SFS are documented in this specification. - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/linearref/package-info.java b/modules/core/src/main/java/org/locationtech/jts/linearref/package-info.java new file mode 100644 index 0000000000..6348da0021 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/linearref/package-info.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes and interfaces implementing linear referencing on linear geometries + *

    Linear Referencing

    + * Linear Referencing is a way of defining positions along linear geometries + * (LineStrings and MultiLineStrings). + * It is used extensively in linear network systems. + * There are numerous possible Linear Referencing Methods which + * can be used to define positions along linear geometry. + * This package supports two: + *
      + *
    • Linear Location - a linear location is a triple + * (component index, segment index, segment fraction) + * which precisely specifies a point on a linear geometry. + * It allows for efficient mapping of the index value to actual coordinate values.
    • + *
    • Length - the natural concept of using the length along + * the geometry to specify a position.
    • + *
    + * + *

    Package Specification

    + * + */ +package org.locationtech.jts.linearref; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/linearref/package.html b/modules/core/src/main/java/org/locationtech/jts/linearref/package.html deleted file mode 100644 index 303986d5fe..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/linearref/package.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - -Contains classes and interfaces implementing linear referencing on linear geometries - -

    Linear Referencing

    - -Linear Referencing is a way of defining positions along linear geometries -(LineStrings and MultiLineStrings). -It is used extensively in linear network systems. -There are numerous possible Linear Referencing Methods which -can be used to define positions along linear geometry. -This package supports two: -
      -
    • Linear Location - a linear location is a triple -(component index, segment index, segment fraction) -which precisely specifies a point on a linear geometry. -It allows for efficient mapping of the index value to actual coordinate values. -
    • Length - the natural concept of using the length along -the geometry to specify a position. - -

      Package Specification

      - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/math/Vector3D.java b/modules/core/src/main/java/org/locationtech/jts/math/Vector3D.java index 2f6f560568..e7d789b90f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/math/Vector3D.java +++ b/modules/core/src/main/java/org/locationtech/jts/math/Vector3D.java @@ -85,8 +85,7 @@ public static double dot(Coordinate v1, Coordinate v2) { * Creates a new 3D vector from a {@link Coordinate}. The coordinate should have * the X,Y and Z ordinates specified. * - * @param coord the Coordinate to copy - * @return a new vector + * @param v the Coordinate to copy */ public Vector3D(Coordinate v) { x = v.x; @@ -95,13 +94,12 @@ public Vector3D(Coordinate v) { } /** - * Creates a vector with the direction and magnitude + * Creates a new vector with the direction and magnitude * of the difference between the * to and from {@link Coordinate}s. * * @param from the origin Coordinate * @param to the destination Coordinate - * @return a new vector */ public Vector3D(Coordinate from, Coordinate to) { x = to.x - from.x; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/BasicSegmentString.java b/modules/core/src/main/java/org/locationtech/jts/noding/BasicSegmentString.java index c026090a62..d8986d4671 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/BasicSegmentString.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/BasicSegmentString.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/FastNodingValidator.java b/modules/core/src/main/java/org/locationtech/jts/noding/FastNodingValidator.java index 5dd06f4f10..f50f268529 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/FastNodingValidator.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/FastNodingValidator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/IteratedNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/IteratedNoder.java index d93b9ad7b5..1a4f9c2322 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/IteratedNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/IteratedNoder.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexNoder.java index a343e19367..9536abc3d5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexNoder.java @@ -1,7 +1,5 @@ - /* - * Copyright (c) 2016 Vivid Solutions. - * Copyright (c) 2020 Martin Davis. + * Copyright (c) 2016 Vivid Solutions, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexSegmentSetMutualIntersector.java b/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexSegmentSetMutualIntersector.java index 56cb538f21..8c338ac63c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexSegmentSetMutualIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/MCIndexSegmentSetMutualIntersector.java @@ -83,8 +83,8 @@ private void addToIndex(SegmentString segStr) * for all candidate intersections between * the given collection of SegmentStrings and the set of indexed segments. * - * @param a set of segments to intersect - * @param the segment intersector to use + * @param segStrings set of segments to intersect + * @param segInt segment intersector to use */ public void process(Collection segStrings, SegmentIntersector segInt) { diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/NodedSegmentString.java b/modules/core/src/main/java/org/locationtech/jts/noding/NodedSegmentString.java index eb708f2ce1..09207fb7b7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/NodedSegmentString.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/NodedSegmentString.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -14,7 +13,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; import org.locationtech.jts.algorithm.LineIntersector; @@ -58,8 +56,8 @@ public static List getNodedSubstrings(Collection segStrings) */ public static void getNodedSubstrings(Collection segStrings, Collection resultEdgelist) { - for (Iterator i = segStrings.iterator(); i.hasNext(); ) { - NodedSegmentString ss = (NodedSegmentString) i.next(); + for (Object segString : segStrings) { + NodedSegmentString ss = (NodedSegmentString) segString; ss.getNodeList().addSplitEdges(resultEdgelist); } } @@ -83,7 +81,7 @@ public NodedSegmentString(Coordinate[] pts, Object data) /** * Creates a new instance from a {@link SegmentString}. * - * @param segString the segment string to use + * @param ss the segment string to use */ public NodedSegmentString(SegmentString ss) { @@ -125,7 +123,7 @@ public boolean isClosed() } /** - * Gets the octant of the segment starting at vertex index. + * Gets the octant of the segment starting at vertex {@code index}. * * @param index the index of the vertex starting the segment. Must not be * the last index in the vertex list @@ -201,8 +199,8 @@ public SegmentNode addIntersectionNode(Coordinate intPt, int segmentIndex) { normalizedSegmentIndex = nextSegIndex; } } - /** - * Add the intersection point to edge intersection list. + /* + Add the intersection point to edge intersection list. */ SegmentNode ei = nodeList.add(intPt, normalizedSegmentIndex); return ei; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/Noder.java b/modules/core/src/main/java/org/locationtech/jts/noding/Noder.java index cd8ab36318..76f54e6113 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/Noder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/Noder.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/NodingIntersectionFinder.java b/modules/core/src/main/java/org/locationtech/jts/noding/NodingIntersectionFinder.java index c70468800e..35221a0277 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/NodingIntersectionFinder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/NodingIntersectionFinder.java @@ -30,10 +30,10 @@ * (with a segment string endpoint or with another interior vertex) *
    * The finder can be limited to finding only interior intersections - * by setting {@link #setInteriorIntersectionsOnly(boolean). + * by setting {@link #setInteriorIntersectionsOnly(boolean)}. *

    * By default only the first intersection is found, - * but all can be found by setting {@link #setFindAllIntersections(boolean) + * but all can be found by setting {@link #setFindAllIntersections(boolean)} * * @version 1.7 */ diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java index 4a16a99ece..ef7e72679a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/NodingValidator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/SegmentString.java b/modules/core/src/main/java/org/locationtech/jts/noding/SegmentString.java index 0ecdf35df9..8860ab59b7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/SegmentString.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/SegmentString.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/SimpleNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/SimpleNoder.java index fed6fb703d..859c88adeb 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/SimpleNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/SimpleNoder.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/SimpleSegmentSetMutualIntersector.java b/modules/core/src/main/java/org/locationtech/jts/noding/SimpleSegmentSetMutualIntersector.java index 74e45c6441..c5c37cdad7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/SimpleSegmentSetMutualIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/SimpleSegmentSetMutualIntersector.java @@ -43,8 +43,8 @@ public SimpleSegmentSetMutualIntersector(Collection segStrings) * for all candidate intersections between * the given collection of SegmentStrings and the set of base segments. * - * @param a set of segments to intersect - * @param the segment intersector to use + * @param segStrings set of segments to intersect + * @param segInt segment intersector to use */ public void process(Collection segStrings, SegmentIntersector segInt) { for (Iterator i = baseSegStrings.iterator(); i.hasNext(); ) { diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/SinglePassNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/SinglePassNoder.java index ebbc552d1b..8d89f0277b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/SinglePassNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/SinglePassNoder.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java index 921b7cdc29..a6c16916cb 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/ValidatingNoder.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis, and others + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.noding; import java.util.Collection; @@ -9,7 +20,7 @@ * An arrangement of line segments is fully noded if * there is no line segment * which has another segment intersecting its interior. - * If the noding is not correct, a {@link TopologyException} is thrown + * If the noding is not correct, a {@link org.locationtech.jts.geom.TopologyException} is thrown * with details of the first invalid location found. * * @author mdavis @@ -19,7 +30,7 @@ */ public class ValidatingNoder implements Noder { - private Noder noder; + private final Noder noder; private Collection nodedSS; /** @@ -35,7 +46,7 @@ public ValidatingNoder(Noder noder) { * Checks whether the output of the wrapped noder is fully noded. * Throws an exception if it is not. * - * @throws TopologyException + * @throws org.locationtech.jts.geom.TopologyException */ @SuppressWarnings("unchecked") @Override diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/package-info.java b/modules/core/src/main/java/org/locationtech/jts/noding/package-info.java new file mode 100644 index 0000000000..64a739b4bf --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/noding/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to compute nodings for arrangements of line segments and line segment sequences. + */ +package org.locationtech.jts.noding; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/package.html b/modules/core/src/main/java/org/locationtech/jts/noding/package.html deleted file mode 100644 index be69c89aaa..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/noding/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to compute nodings for arrangements of line segments and line segment sequences. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java index 8c74ba4744..c14e7ca3ed 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java @@ -42,7 +42,7 @@ * given a small enough snap tolerance. *

    * The correctness of the output is not verified by this noder. - * If required this can be done by {@link ValidatingNoder}. + * If required this can be done by {@link org.locationtech.jts.noding.ValidatingNoder}. * * @version 1.17 */ diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java index 819dc14e8d..3ae335ca81 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/GeometryNoder.java @@ -1,6 +1,5 @@ /* - * Copyright (c) 2016 Vivid Solutions. - * Copyright (c) 2020 Martin Davis. + * Copyright (c) 2016 Vivid Solutions, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,7 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - package org.locationtech.jts.noding.snapround; import java.util.ArrayList; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java index 8b71e32a9c..c7087af608 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/HotPixel.java @@ -66,8 +66,6 @@ public class HotPixel * * @param pt the coordinate at the centre of the pixel (already rounded) * @param scaleFactor the scaleFactor determining the pixel size. Must be > 0 - * @param li the intersector to use for testing intersection with line segments - * */ public HotPixel(Coordinate pt, double scaleFactor) { originalPt = pt; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/MCIndexSnapRounder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/MCIndexSnapRounder.java index 9922079910..2a0d5c4097 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/MCIndexSnapRounder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/MCIndexSnapRounder.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java index 00a0b31bb3..94e6a5cd14 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java @@ -50,10 +50,10 @@ public class SnapRoundingIntersectionAdder */ private static final int NEARNESS_FACTOR = 100; - private LineIntersector li; + private final LineIntersector li; private final List intersections; - private PrecisionModel precModel; - private double nearnessTol; + private final PrecisionModel precModel; + private final double nearnessTol; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java index eeee8516d4..87ec99fb7b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java @@ -50,7 +50,7 @@ * each of the segment vertices will be noded. * This still provides fully-noded output. * This is the same behaviour provided by other noders, - * such as {@link MCIndexNoder} and {@link SnappingNoder}. + * such as {@link MCIndexNoder} and {@link org.locationtech.jts.noding.snap.SnappingNoder}. * * @version 1.7 */ @@ -58,7 +58,7 @@ public class SnapRoundingNoder implements Noder { private final PrecisionModel pm; - private HotPixelIndex pixelIndex; + private final HotPixelIndex pixelIndex; private List snappedResult; diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package-info.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package-info.java new file mode 100644 index 0000000000..e08c450625 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes to implement the Snap Rounding algorithm for noding linestrings. + */ +package org.locationtech.jts.noding.snapround; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package.html b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package.html deleted file mode 100644 index a25101d4fa..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes to implement the Snap Rounding algorithm for noding linestrings. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/GeometryGraphOperation.java b/modules/core/src/main/java/org/locationtech/jts/operation/GeometryGraphOperation.java index 4dca586258..5bcbb232c2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/GeometryGraphOperation.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/GeometryGraphOperation.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java index 0015467355..f31f169d6f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/IsSimpleOp.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferBuilder.java index 6ea4631e47..c415e61251 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferBuilder.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferSubgraph.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferSubgraph.java index 15e7b37c9f..5e26a61834 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferSubgraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferSubgraph.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetCurveSetBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetCurveSetBuilder.java index fdb7cdcc5e..e3e6494de5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetCurveSetBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetCurveSetBuilder.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/RightmostEdgeFinder.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/RightmostEdgeFinder.java index 423f19279e..8f2c710203 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/RightmostEdgeFinder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/RightmostEdgeFinder.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/SubgraphDepthLocater.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/SubgraphDepthLocater.java index de0ce9c107..2b76199947 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/SubgraphDepthLocater.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/SubgraphDepthLocater.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/VariableBuffer.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/VariableBuffer.java index 7064f650cc..c15c3394ed 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/VariableBuffer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/VariableBuffer.java @@ -22,6 +22,7 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineSegment; import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; /** diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package-info.java new file mode 100644 index 0000000000..a36bae8455 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes for computing buffers of geometries + */ +package org.locationtech.jts.operation.buffer; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package.html deleted file mode 100644 index 1dec4cc46d..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes for computing buffers of geometries - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package-info.java new file mode 100644 index 0000000000..4d6541edb3 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform validation of the results of buffer operations. + */ +package org.locationtech.jts.operation.buffer.validate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package.html deleted file mode 100644 index ffca7a052f..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/validate/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform validation of the results of buffer operations. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementLocationFilter.java b/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementLocationFilter.java index cddaff2063..a67311faff 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementLocationFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementLocationFilter.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementPointFilter.java b/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementPointFilter.java index c69d7b1ef9..edcc2d2b40 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementPointFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/distance/ConnectedElementPointFilter.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/GeometryLocation.java b/modules/core/src/main/java/org/locationtech/jts/operation/distance/GeometryLocation.java index 7b287e634b..3c978b4bff 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/distance/GeometryLocation.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/distance/GeometryLocation.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/distance/package-info.java new file mode 100644 index 0000000000..ad4fcee8f0 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/distance/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes for computing the distance between geometries + */ +package org.locationtech.jts.operation.distance; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/distance/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/distance/package.html deleted file mode 100644 index 891a13b276..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/distance/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes for computing the distance between geometries - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/EdgeString.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/EdgeString.java index e02b22de70..04d78a49c6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/EdgeString.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/EdgeString.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeDirectedEdge.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeDirectedEdge.java index afb0c8455d..827ac7f78e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeDirectedEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeDirectedEdge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeEdge.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeEdge.java index 53f0f079d9..537736c31b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeEdge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeGraph.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeGraph.java index 8cf97640e4..08a3286d99 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMergeGraph.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMerger.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMerger.java index 1ee7de5604..ea0cfb1fc4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMerger.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/LineMerger.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package-info.java new file mode 100644 index 0000000000..e3ea3033b2 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform line merging. + */ +package org.locationtech.jts.operation.linemerge; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package.html deleted file mode 100644 index 72fcd2fc8f..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/linemerge/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform line merging. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/EdgeSetNoder.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/EdgeSetNoder.java index 0e3fa538d8..5dd48f3125 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/EdgeSetNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/EdgeSetNoder.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MaximalEdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MaximalEdgeRing.java index fa978d7045..763dc3f40b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MaximalEdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MaximalEdgeRing.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MinimalEdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MinimalEdgeRing.java index d840cf55ea..4c6810dd03 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MinimalEdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/MinimalEdgeRing.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayNodeFactory.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayNodeFactory.java index 106589a1db..f71ecc5753 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayNodeFactory.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayNodeFactory.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayOp.java index 4cb261901b..10b74917b4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/OverlayOp.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package-info.java new file mode 100644 index 0000000000..ebb951c4f8 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package-info.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes that perform a topological overlay to compute boolean spatial functions. + *

    + * The Overlay Algorithm is used in spatial analysis methods for computing set-theoretic + * operations (boolean combinations) of input {@link org.locationtech.jts.geom.Geometry}s. The algorithm for + * computing the overlay uses the intersection operations supported by topology graphs. + * To compute an overlay it is necessary to explicitly compute the resultant graph formed + * by the computed intersections. + *

    + * The algorithm to compute a set-theoretic spatial analysis method has the following steps: + *

      + *
    • Build topology graphs of the two input geometries. For each geometry all + * self-intersection nodes are computed and added to the graph. + *
    • Compute nodes for all intersections between edges and nodes of the graphs. + *
    • Compute the labeling for the computed nodes by merging the labels from the input graphs. + *
    • Compute new edges between the compute intersection nodes. Label the edges appropriately. + *
    • Build the resultant graph from the new nodes and edges. + *
    • Compute the labeling for isolated components of the graph. Add the + * isolated components to the resultant graph. + *
    • Compute the result of the boolean combination by selecting the node and edges + * with the appropriate labels. Polygonize areas and sew linear geometries together. + *
    + *

    Package Specification

    + * + */ +package org.locationtech.jts.operation.overlay; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package.html deleted file mode 100644 index ca5685db45..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/package.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - -Contains classes that perform a topological overlay to compute boolean spatial functions. -

    -The Overlay Algorithm is used in spatial analysis methods for computing set-theoretic -operations (boolean combinations) of input {@link Geometry}s. The algorithm for -computing the overlay uses the intersection operations supported by topology graphs. -To compute an overlay it is necessary to explicitly compute the resultant graph formed -by the computed intersections. -

    -The algorithm to compute a set-theoretic spatial analysis method has the following steps: -

      -
    • Build topology graphs of the two input geometries. For each geometry all - self-intersection nodes are computed and added to the graph. -
    • Compute nodes for all intersections between edges and nodes of the graphs. -
    • Compute the labeling for the computed nodes by merging the labels from the input graphs. -
    • Compute new edges between the compute intersection nodes. Label the edges appropriately. -
    • Build the resultant graph from the new nodes and edges. -
    • Compute the labeling for isolated components of the graph. Add the - isolated components to the resultant graph. -
    • Compute the result of the boolean combination by selecting the node and edges - with the appropriate labels. Polygonize areas and sew linear geometries together. -
    - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package-info.java new file mode 100644 index 0000000000..85b98295a8 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform snapping on geometries to prepare them for overlay operations. + */ +package org.locationtech.jts.operation.overlay.snap; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package.html deleted file mode 100644 index f7b83d7061..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/snap/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform snapping on geometries to prepare them for overlay operations. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package-info.java new file mode 100644 index 0000000000..48e29926d9 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to validate the results of overlay operations. + */ +package org.locationtech.jts.operation.overlay.validate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package.html deleted file mode 100644 index 8093abc475..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlay/validate/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to validate the results of overlay operations. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/CoverageUnion.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/CoverageUnion.java index eaf91a6bda..1d2e9c52f9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/CoverageUnion.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/CoverageUnion.java @@ -19,7 +19,7 @@ * Unions a valid coverage of polygons or lines * in an efficient way. *

    - * A valid polygonal coverage is a collection of {@link Polygon}s + * A valid polygonal coverage is a collection of {@link org.locationtech.jts.geom.Polygon}s * which satisfy the following conditions: *

      *
    1. Vector-clean - Line segments within the collection @@ -28,7 +28,7 @@ * may overlap. Equivalently, polygons must be interior-disjoint. *
    *

    - * A valid linear coverage is a collection of {@link LineString}s + * A valid linear coverage is a collection of {@link org.locationtech.jts.geom.LineString}s * which satisfies the Vector-clean condition. * Note that this does not require the LineStrings to be fully noded * - i.e. they may contain coincident linework. @@ -40,7 +40,7 @@ * which is much more expensive than the union phase. * If the input is not a valid coverage * then in some cases this will be detected during processing - * and a {@link TopologyException} is thrown. + * and a {@link org.locationtech.jts.geom.TopologyException} is thrown. * Otherwise, the computation will produce output, but it will be invalid. *

    * Unioning a valid coverage implies that no new vertices are created. diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/FastOverlayFilter.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/FastOverlayFilter.java index 75ea01b28a..afb33dbb18 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/FastOverlayFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/FastOverlayFilter.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.operation.overlayng; import org.locationtech.jts.geom.Geometry; @@ -23,7 +34,7 @@ public FastOverlayFilter(Geometry geom) { * * @param geom * @param overlayOpCode - * @return + * @return overlay of the input geometries */ public Geometry overlay(Geometry geom, int overlayOpCode) { // for now only INTERSECTION is handled diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.java index 82ffe8e979..d8fe278a79 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/IndexedPointOnLineLocator.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.operation.overlayng; import org.locationtech.jts.algorithm.PointLocator; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java index fe23121fea..71b7c08b11 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/MaximalEdgeRing.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.operation.overlayng; import java.util.ArrayList; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayLabeller.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayLabeller.java index cade407341..a72573bce6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayLabeller.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayLabeller.java @@ -23,7 +23,6 @@ import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.util.Assert; -import org.locationtech.jts.util.Debug; /** * Implements the logic to compute the full labeling diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java index 1210a4bc00..c22b61bf12 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNG.java @@ -32,14 +32,15 @@ /** * Computes the geometric overlay of two {@link Geometry}s, - * using an explicit precision model to allow robust computation. + * using an explicit precision model to allow robust computation. + *

    * The overlay can be used to determine any of the - * following set-theoretic operations (boolean combinations) of the geometries: + * following set-theoretic operations (boolean combinations) of the geometries:

    *
      - *
    • {@link INTERSECTION} - all points which lie in both geometries - *
    • {@link UNION} - all points which lie in at least one geometry - *
    • {@link DIFFERENCE} - all points which lie in the first geometry but not the second - *
    • {@link SYMDIFFERENCE} - all points which lie in one geometry but not both + *
    • {@link #INTERSECTION} - all points which lie in both geometries
    • + *
    • {@link #UNION} - all points which lie in at least one geometry
    • + *
    • {@link #DIFFERENCE} - all points which lie in the first geometry but not the second
    • + *
    • {@link #SYMDIFFERENCE} - all points which lie in one geometry but not both
    • *
    * Input geometries may have different dimension. * Input collections must be homogeneous (all elements must have the same dimension). @@ -49,49 +50,51 @@ * The main use for this is to allow using a fixed precision * for geometry with a floating precision model. * This does two things: ensures robust computation; - * and forces the output to be validly rounded to the precision model. + * and forces the output to be validly rounded to the precision model.

    *

    * For fixed precision models noding is performed using a {@link SnapRoundingNoder}. * This provides robust computation (as long as precision is limited to - * around 13 decimal digits). + * around 13 decimal digits).

    *

    * For floating precision an {@link MCIndexNoder} is used. * This is not fully robust, so can sometimes result in * {@link TopologyException}s being thrown. - * For robust full-precision overlay see {@link OverlayNGRobust}. + * For robust full-precision overlay see {@link OverlayNGRobust}.

    *

    * A custom {@link Noder} can be supplied. * This allows using a more performant noding strategy in specific cases, - * for instance in {@link CoverageUnion}. + * for instance in {@link CoverageUnion}.

    *

    * Note: *

    * Optionally the overlay computation can process using strict mode * (via {@link #setStrictMode(boolean)}. - * In strict mode result semantics are: + * In strict mode result semantics are:

    *
      *
    • Lines and Points resulting from topology collapses are not included in the result
    • *
    • Result geometry is homogeneous - * for the {@link #INTERSECTION} and {@link #DIFFERENCE} operations. + * for the {@link #INTERSECTION} and {@link #DIFFERENCE} operations.
    • *
    • Result geometry is homogeneous * for the {@link #UNION} and {@link #SYMDIFFERENCE} operations * if the inputs have the same dimension
    • *
    - * Strict mode has the following benefits: + *

    + * Strict mode has the following benefits:

    *
      *
    • Results are simpler
    • *
    • Overlay operations are chainable * without needing to remove lower-dimension elements
    • *
    - * The original JTS overlay semantics corresponds to non-strict mode. + *

    + * The original JTS overlay semantics corresponds to non-strict mode.

    *

    * If a robustness error occurs, a {@link TopologyException} is thrown. * These are usually caused by numerical rounding causing the noding output * to not be fully noded. - * For robust computation with full-precision {@link OverlayNGRobust} can be used. + * For robust computation with full-precision {@link OverlayNGRobust} can be used.

    * * @author mdavis * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java index c4dacf3517..c57807007a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/OverlayNGRobust.java @@ -35,7 +35,7 @@ *
  • First snapping each geometry to itself, * and then overlaying them using a SnappingNoder. *
  • The above two strategies are repeated with increasing snap tolerance, up to a limit. - *
  • Finally a {@link SnapRoundngNoder} is used with a automatically-determined scale factor + *
  • Finally a {@link org.locationtech.jts.noding.snapround.SnapRoundingNoder} is used with a automatically-determined scale factor * intended to preserve input precision while still preventing robustness problems. * * If all of the above attempts fail to compute a valid overlay, diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java index d4a9a737c0..10c656e806 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionReducer.java @@ -15,17 +15,17 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.precision.GeometryPrecisionReducer; /** * Functions to reduce the precision of a geometry * by rounding it to a given precision model. *

    * This class handles only polygonal and linear inputs. - * For full functionality see {@link GeometryPrecisionReducer}. + * For full functionality see {@link org.locationtech.jts.precision.GeometryPrecisionReducer}. * - * @see GeometryPrecisionReducer + * @see org.locationtech.jts.precision.GeometryPrecisionReducer * @author Martin Davis - * */ public class PrecisionReducer { diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionUtil.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionUtil.java index 3d7ef844a8..676128df3d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionUtil.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/PrecisionUtil.java @@ -68,7 +68,7 @@ public static PrecisionModel robustPM(Geometry a, Geometry b) { /** * Computes a safe scale factor for a numeric value. * A safe scale factor ensures that rounded - * number has no more than {@link MAX_PRECISION_DIGITS} + * number has no more than {@link #MAX_ROBUST_DP_DIGITS} * digits of precision. * * @param value a numeric value @@ -82,7 +82,7 @@ public static double safeScale(double value) /** * Computes a safe scale factor for a geometry. * A safe scale factor ensures that the rounded - * ordinates have no more than {@link MAX_PRECISION_DIGITS} + * ordinates have no more than {@link #MAX_ROBUST_DP_DIGITS} * digits of precision. * * @param geom a geometry @@ -96,7 +96,7 @@ public static double safeScale(Geometry geom) /** * Computes a safe scale factor for two geometries. * A safe scale factor ensures that the rounded - * ordinates have no more than {@link MAX_PRECISION_DIGITS} + * ordinates have no more than {@link #MAX_ROBUST_DP_DIGITS} * digits of precision. * * @param a a geometry @@ -194,8 +194,8 @@ public static double inherentScale(double value) { *

    * WARNING: this is very slow. * - * @param value a number - * @return the inherent scale factor of the number + * @param geom geometry + * @return inherent scale of a geometry */ public static double inherentScale(Geometry geom) { InherentScaleFilter scaleFilter = new InherentScaleFilter(); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/RingClipper.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/RingClipper.java index 0add8a52f5..78d8bb392c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/RingClipper.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/RingClipper.java @@ -72,9 +72,8 @@ public RingClipper(Envelope clipEnv) { /** * Clips a list of points to the clipping rectangle box. * - * @param ring - * @param env - * @return + * @param pts + * @return clipped pts array */ public Coordinate[] clip(Coordinate[] pts) { for (int edgeIndex = 0; edgeIndex < 4; edgeIndex++) { diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package-info.java new file mode 100644 index 0000000000..addbe29a2d --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package-info.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + *

    + * Contains classes that perform vector overlay + * to compute boolean set-theoretic spatial functions. + * Overlay operations are used in spatial analysis for computing set-theoretic + * operations (boolean combinations) of input {@link org.locationtech.jts.geom.Geometry}s. + *

    + *

    + * The {@link org.locationtech.jts.operation.overlayng.OverlayNG} class provides the standard Simple Features + * boolean set-theoretic overlay operations. + * These are: + *

      + *
    • Intersection - all points which lie in both geometries
    • + *
    • Union - all points which lie in at least one geometry
    • + *
    • Difference - all points which lie in the first geometry but not the second
    • + *
    • Symmetric Difference - all points which lie in one geometry but not both
    • + *
    + * These operations are supported for all combinations of the basic geometry types and their homogeneous collections. + *

    + * Additional operations include: + *

      + *
    • {@link org.locationtech.jts.operation.overlayng.UnaryUnionNG} unions collections of geometries in an efficient way
    • + *
    • {@link org.locationtech.jts.operation.overlayng.CoverageUnion} provides enhanced performance for unioning + * valid polygonal and lineal coverages
    • + *
    • {@link org.locationtech.jts.operation.overlayng.PrecisionReducer} allows reducing the precision of a geometry + * in a topologically-valid way
    • + *
    + *

    + *

    Semantics

    + * The requirements for overlay input are: + *
      + *
    • Input geometries may have different dimension.
    • + *
    • Collections must be homogeneous + * (all elements must have the same dimension).
    • + *
    • In general, inputs must be valid geometries.
    • + *
    • However, polygonal inputs may contain the following two kinds of "mild" invalid topology: + *
        + *
      • rings which self-touch at discrete points (sometimes called inverted shells and exverted holes).
      • + *
      • rings which touch along line segments (i.e. topology collapse).
      • + *
      + *
    • + *
    + *

    + * The semantics of overlay output are: + *

      + *
    • Results are always valid geometries. + * In particular, result MultiPolygons are valid.
    • + *
    • Repeated vertices are removed.
    • + *
    • Linear results include all nodes (endpoints) present in the input. + * In some cases more nodes will be present. + * (If merged lines are required see {@link org.locationtech.jts.operation.linemerge.LineMerger}.)
    • + *
    • Polygon edges which undergo topology collapse to lines + * (due to rounding or snapping) are included in the result. + * This means that all operations may produce a heterogeneous result. + * Usually this only occurs when using a fixed-precision model, + * but it can happen due to snapping performed to improve robustness. + *
    • + *
    • The intersection operation result includes + * all components of the intersection + * for geometries which intersect in components of the same and/or lower dimension.
    • + *
    • The difference operation produces a homogeneous result + * if no topology collapses are present. + * In this case the result dimension is equal to that of the left-hand operand.
    • + *
    • The union and symmetric difference operations + * may produce a heterogeneous result if the inputs are of mixed dimension.
    • + *
    • Homogeneous results are output as Multi geometries.
    • + *
    • Heterogeneous results are output as a GeometryCollection + * containing a set of atomic geometries. + * (This provides backwards compatibility with the original overlay implementation. + * However, it loses the information that the polygonal results + * have valid MultiPolygon topology.)
    • + *
    • Empty results are atomic EMPTY geometries + * of dimension appropriate to the operation.
    • + *
    • As far as possible, results preserve the order and direction of the inputs. + * For instance, a MultiLineString intersection with a Polygon + * will have resultants which are in the same order and have the same direction + * as the input lines (assuming the input lines are disjoint). + * If an input line is split into two or more parts, + * they are ordered in the direction of occurence along their parent line.
    • + *
    + *

    Features

    + *

    Functionality

    + *
      + *
    • Precision Model - operations are performed using a defined precision model + * (finite or floating) + *
    • Robust Computation - provides fully robust computation when an appropriate noder is used + *
    • Performance optimizations - including: + *
        + *
      • Short-circuiting for disjoint input envelopes + *
      • Reduction of input segment count via clipping / limiting to overlap envelope + *
      • Optimizations can be disabled if required (e.g. for testing or performance evaluation) + *
      + *
    • Pluggable Noding - allows using different noders to change characteristics of performance and accuracy + *
    • Precision Reduction - in a topologically correct way. + * Implemented by unioning a single input with an empty geometry + *
    • Topology Correction / Conversion - handles certain kinds + * of polygonal inputs which are invalid + *
    • Fast Coverage Union - of valid polygonal and linear coverages + *
    + *

    Pluggable Noding

    + * The noding phase of overlay uses a {@link org.locationtech.jts.noding.Noder} subclass. + * This is determine automatically based on the precision model of the input. + * Or it can be provided explicity, which allows changing characteristics + * of performance and robustness. + * Examples of relevant noders include: + *
      + *
    • {@link org.locationtech.jts.noding.MCIndexNoder} - a fast full-precision noder, which however may not produce + * a valid noding in some situations. + * Should be combined with a {@link org.locationtech.jts.noding.ValidatingNoder} wrapper to detect + * noding failures.
    • + *
    • {@link org.locationtech.jts.noding.snap.SnappingNoder} - a robust full-precision noder
    • + *
    • {@link org.locationtech.jts.noding.snapround.SnapRoundingNoder} - a noder which enforces a supplied fixed precision model + * by snapping vertices and intersections to a grid
    • + *
    • {@link org.locationtech.jts.noding.SegmentExtractingNoder} - a special-purpose noder that provides very fast noding + * for valid polygonal coverages. Requires node-clean input to operate correctly.
    • + *
    + *

    Topology Correction / Conversion

    + * As noted above, the overlay process + * can handle polygonal inputs which are invalid according to the OGC topology model + * in certain limited ways. + * These invalid conditions are: + *
      + *
    • rings which self-touch at discrete points (sometimes called inverted shells and exverted holes).
    • + *
    • rings which touch along line segments (i.e. topology collapse).
    • + *
    + * These invalidities are corrected during the overlay process. + *

    + * Some of these invalidities are considered as valid in other geometry models. + * By peforming a self-overlay these inputs can be converted + * into the JTS OGC topological model. + *

    Codebase

    + *
      + *
    • Defines a simple, full-featured topology model, with clear semantics. + * The topology model incorporates handling topology collapse, which is + * essential for snapping and fixed-precision noding.
    • + *
    • Uses a simple topology graph data structure (based on the winged edge pattern).
    • + *
    • Decouples noding and topology-build phases. + * This makes the code clearer, and makes it possible + * to allow supplying alternate implementations and semantics for each phase.
    • + *
    • All optimizations are implemented internally, + * so that clients do not have to add checks such as envelope overlap.
    • + *
    + *

    Algorithm

    + * For non-point inputs the overlay algorithm is: + *

    + *

      + *
    1. Check for empty input geometries, and return a result appropriate for the specified operation + *
    2. Extract linework and points from input geometries, with topology location information + *
    3. (If optimization enabled) Apply overlap envelope optimizations: + *
        + *
      1. For Intersection, check if the input envelopes are disjoint + * (using an envelope expansion adjustment to account for the precision grid). + *
      2. For Intersection and Difference, clip or limit the linework of the input geometries to the overlap envelope. + *
      3. If the optimized linework is empty, return an empty result of appropriate type. + *
      + *
    4. Node the linework. For full robustness snap-rounding noding is used. + * Other kinds of noder can be used as well (for instance, the full-precision noding algorithm as the original overlay code). + *
    5. Merge noded edges. + * Coincident edges from the two input geometries are merged, along with their topological labelling. + * Topology collapses are detected in this step, and are flagged in the labelling so they can be handled appropriately duing result polygon extraction + *
    6. Build a fully-labelled topology graph. This includes: + *
        + *
      1. Create a graph structure on the noded, merged edges + *
      2. Propagate topology locations around nodes in the graph + *
      3. Label edges that have incomplete topology locations. + * These occur when edges from an input geometry are isolated (disjoint from the edges of the other geometry in the graph). + *
      + *
    7. If result is empty return an empty geometry of appropriate type + *
    8. Generate the result geometry from the labelled graph: + *
        + *
      1. Build result polygons + *
          + *
        1. Mark edges which should be included in the result areas + *
        2. Link maximal rings together + *
        3. Convert maximal rings to minimal (valid) rings + *
        4. Determine nesting of holes + *
        5. Construct result polygons + *
        + *
      2. Build result linework + *
          + *
        1. Mark edges to be included in the result lines + *
        2. Extract edges as lines + *
        + *
      3. Build result points (certain intersection situations only) + *
          + *
        1. Output points occur where the inputs touch at single points + *
        + *
      4. Collect result elements into the result geometry + *
      + *
    + *

    Package Specification

    + * + */ +package org.locationtech.jts.operation.overlayng; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html deleted file mode 100644 index 40feada647..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/overlayng/package.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - -

    -Contains classes that perform vector overlay -to compute boolean set-theoretic spatial functions. -Overlay operations are used in spatial analysis for computing set-theoretic -operations (boolean combinations) of input {@link Geometry}s. -

    -

    -The {@link OverlayNG} class provides the standard Simple Features -boolean set-theoretic overlay operations. -These are: -

      -
    • Intersection - all points which lie in both geometries
    • -
    • Union - all points which lie in at least one geometry
    • -
    • Difference - all points which lie in the first geometry but not the second
    • -
    • Symmetric Difference - all points which lie in one geometry but not both
    • -
    -These operations are supported for all combinations of the basic geometry types and their homogeneous collections. -

    -Additional operations include: -

      -
    • {@link UnaryUnion} unions collections of geometries in an efficient way
    • -
    • {@link CoverageUnion} provides enhanced performance for unioning -valid polygonal and lineal coverages
    • -
    • {@link PrecisionReducer} allows reducing the precision of a geometry -in a topologically-valid way
    • -
    -

    -

    -

    Semantics

    -The requirements for overlay input are: - -
      -
    • Input geometries may have different dimension.
    • -
    • Collections must be homogeneous - (all elements must have the same dimension).
    • -
    • In general, inputs must be valid geometries.
    • -
    • However, polygonal inputs may contain the following two kinds of "mild" invalid topology: -
        -
      • rings which self-touch at discrete points (sometimes called inverted shells and exverted holes).
      • -
      • rings which touch along line segments (i.e. topology collapse).
      • -
      -
    • -
    - -

    -The semantics of overlay output are: - -

      -
    • Results are always valid geometries. - In particular, result MultiPolygons are valid.
    • -
    • Repeated vertices are removed.
    • -
    • Linear results include all nodes (endpoints) present in the input. - In some cases more nodes will be present. - (If merged lines are required see {@link LineMerger}.)
    • -
    • Polygon edges which undergo topology collapse to lines - (due to rounding or snapping) are included in the result. - This means that all operations may produce a heterogeneous result. - Usually this only occurs when using a fixed-precision model, - but it can happen due to snapping performed to improve robustness. -
    • -
    • The intersection operation result includes - all components of the intersection - for geometries which intersect in components of the same and/or lower dimension.
    • -
    • The difference operation produces a homogeneous result - if no topology collapses are present. - In this case the result dimension is equal to that of the left-hand operand.
    • -
    • The union and symmetric difference operations - may produce a heterogeneous result if the inputs are of mixed dimension.
    • -
    • Homogeneous results are output as Multi geometries.
    • -
    • Heterogeneous results are output as a GeometryCollection - containing a set of atomic geometries. - (This provides backwards compatibility with the original overlay implementation. - However, it loses the information that the polygonal results - have valid MultiPolygon topology.)
    • -
    • Empty results are atomic EMPTY geometries - of dimension appropriate to the operation.
    • -
    • As far as possible, results preserve the order and direction of the inputs. - For instance, a MultiLineString intersection with a Polygon - will have resultants which are in the same order and have the same direction - as the input lines (assuming the input lines are disjoint). - If an input line is split into two or more parts, - they are ordered in the direction of occurence along their parent line.
    • -
    - -

    -

    Features

    -

    Functionality

    -
      -
    • Precision Model - operations are performed using a defined precision model -(finite or floating) -
    • Robust Computation - provides fully robust computation when an appropriate noder is used -
    • Performance optimizations - including: -
        -
      • Short-circuiting for disjoint input envelopes -
      • Reduction of input segment count via clipping / limiting to overlap envelope -
      • Optimizations can be disabled if required (e.g. for testing or performance evaluation) -
      -
    • Pluggable Noding - allows using different noders to change characteristics of performance and accuracy -
    • Precision Reduction - in a topologically correct way. -Implemented by unioning a single input with an empty geometry -
    • Topology Correction / Conversion - handles certain kinds -of polygonal inputs which are invalid -
    • Fast Coverage Union - of valid polygonal and linear coverages -
    - -

    Pluggable Noding

    - -The noding phase of overlay uses a {@link Noder} subclass. -This is determine automatically based on the precision model of the input. -Or it can be provided explicity, which allows changing characteristics -of performance and robustness. -Examples of relevant noders include: -
      -
    • {@link MCIndexNoder} - a fast full-precision noder, which however may not produce -a valid noding in some situations. -Should be combined with a {@link ValidatingNoder} wrapper to detect -noding failures.
    • -
    • {@link SnappingNoder} - a robust full-precision noder
    • -
    • {@link SnapRoundingNoder} - a noder which enforces a supplied fixed precision model -by snapping vertices and intersections to a grid
    • -
    • {@link SegmentExtractingNoder} - a special-purpose noder that provides very fast noding -for valid polygonal coverages. Requires node-clean input to operate correctly.
    • -
    - -

    Topology Correction / Conversion

    - -As noted above, the overlay process -can handle polygonal inputs which are invalid according to the OGC topology model -in certain limited ways. -These invalid conditions are: -
      -
    • rings which self-touch at discrete points (sometimes called inverted shells and exverted holes).
    • -
    • rings which touch along line segments (i.e. topology collapse).
    • -
    -These invalidities are corrected during the overlay process.
  • -

    -Some of these invalidities are considered as valid in other geometry models. -By peforming a self-overlay these inputs can be converted -into the JTS OGC topological model. - -

    Codebase

    -
      -
    • Defines a simple, full-featured topology model, with clear semantics. -The topology model incorporates handling topology collapse, which is -essential for snapping and fixed-precision noding.
    • -
    • Uses a simple topology graph data structure (based on the winged edge pattern).
    • -
    • Decouples noding and topology-build phases. -This makes the code clearer, and makes it possible -to allow supplying alternate implementations and semantics for each phase.
    • -
    • All optimizations are implemented internally, -so that clients do not have to add checks such as envelope overlap<./li> -
    -

    -

    Algorithm

    -For non-point inputs the overlay algorithm is: -

    -

      -
    1. Check for empty input geometries, and return a result appropriate for the specified operation -
    2. Extract linework and points from input geometries, with topology location information -
    3. (If optimization enabled) Apply overlap envelope optimizations: -
        -
      1. For Intersection, check if the input envelopes are disjoint - (using an envelope expansion adjustment to account for the precision grid). -
      2. For Intersection and Difference, clip or limit the linework of the input geometries to the overlap envelope. -
      3. If the optimized linework is empty, return an empty result of appropriate type. -
      -
    4. Node the linework. For full robustness snap-rounding noding is used. -Other kinds of noder can be used as well (for instance, the full-precision noding algorithm as the original overlay code). -
    5. Merge noded edges. - Coincident edges from the two input geometries are merged, along with their topological labelling. - Topology collapses are detected in this step, and are flagged in the labelling so they can be handled appropriately duing result polygon extraction -
    6. Build a fully-labelled topology graph. This includes: -
        -
      1. Create a graph structure on the noded, merged edges -
      2. Propagate topology locations around nodes in the graph -
      3. Label edges that have incomplete topology locations. - These occur when edges from an input geometry are isolated (disjoint from the edges of the other geometry in the graph). -
      -
    7. If result is empty return an empty geometry of appropriate type -
    8. Generate the result geometry from the labelled graph: -
        -
      1. Build result polygons -
          -
        1. Mark edges which should be included in the result areas -
        2. Link maximal rings together -
        3. Convert maximal rings to minimal (valid) rings -
        4. Determine nesting of holes -
        5. Construct result polygons -
        -
      2. Build result linework -
          -
        1. Mark edges to be included in the result lines -
        2. Extract edges as lines -
        -
      3. Build result points (certain intersection situations only) -
          -
        1. Output points occur where the inputs touch at single points -
        -
      4. Collect result elements into the result geometry -
      -
    - -

    - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/package-info.java new file mode 100644 index 0000000000..a53b362786 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes for implementing operations on geometries + */ +package org.locationtech.jts.operation; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/package.html deleted file mode 100644 index 2ad4c14605..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Provides classes for implementing operations on geometries - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java index 84778c5eb7..c22d386d5c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/EdgeRing.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -10,8 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - - package org.locationtech.jts.operation.polygonize; import java.util.ArrayList; @@ -202,7 +199,7 @@ public void addHole(LinearRing hole) { /** * Adds a hole to the polygon formed by this ring. - * @param hole the {@link LinearRing} forming the hole. + * @param holeER the {@link LinearRing} forming the hole. */ public void addHole(EdgeRing holeER) { holeER.setShell(this); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeDirectedEdge.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeDirectedEdge.java index 545cb229a3..5884ae39b5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeDirectedEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeDirectedEdge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeEdge.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeEdge.java index 7dd29cb28d..ea6df88cd5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeEdge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -10,8 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - - package org.locationtech.jts.operation.polygonize; import org.locationtech.jts.geom.LineString; diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeGraph.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeGraph.java index e574d11469..dd4cfc7d3c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/PolygonizeGraph.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -10,8 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - - package org.locationtech.jts.operation.polygonize; import java.util.ArrayList; @@ -36,7 +33,7 @@ /** * Represents a planar graph of edges that can be used to compute a * polygonization, and implements the algorithms to compute the - * {@link EdgeRings} formed by the graph. + * {@link EdgeRing}s formed by the graph. *

    * The marked flag on {@link DirectedEdge}s is used to indicate that a directed edge * has be logically deleted from the graph. @@ -49,9 +46,9 @@ class PolygonizeGraph private static int getDegreeNonDeleted(Node node) { - List edges = node.getOutEdges().getEdges(); + List edges = node.getOutEdges().getEdges(); int degree = 0; - for (Iterator i = edges.iterator(); i.hasNext(); ) { + for (Iterator i = edges.iterator(); i.hasNext(); ) { PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next(); if (! de.isMarked()) degree++; } @@ -60,9 +57,9 @@ private static int getDegreeNonDeleted(Node node) private static int getDegree(Node node, long label) { - List edges = node.getOutEdges().getEdges(); + List edges = node.getOutEdges().getEdges(); int degree = 0; - for (Iterator i = edges.iterator(); i.hasNext(); ) { + for (Iterator i = edges.iterator(); i.hasNext(); ) { PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next(); if (de.getLabel() == label) degree++; } @@ -74,8 +71,8 @@ private static int getDegree(Node node, long label) */ public static void deleteAllEdges(Node node) { - List edges = node.getOutEdges().getEdges(); - for (Iterator i = edges.iterator(); i.hasNext(); ) { + List edges = node.getOutEdges().getEdges(); + for (Iterator i = edges.iterator(); i.hasNext(); ) { PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next(); de.setMarked(true); PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) de.getSym(); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/Polygonizer.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/Polygonizer.java index 02ac85b3e5..9f4c5ccff2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/Polygonizer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/Polygonizer.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -178,7 +177,7 @@ public Collection getPolygons() /** * Gets a geometry representing the polygons formed by the polygonization. - * If a valid polygonal geometry was extracted the result is a {@link Polygonal} geometry. + * If a valid polygonal geometry was extracted the result is a {@link org.locationtech.jts.geom.Polygonal} geometry. * * @return a geometry containing the polygons */ diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package-info.java new file mode 100644 index 0000000000..21e045f61f --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * An API for polygonizing sets of lines. + */ +package org.locationtech.jts.operation.polygonize; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package.html deleted file mode 100644 index 5e4679134e..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/polygonize/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -An API for polygonizing sets of lines. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package-info.java new file mode 100644 index 0000000000..7c5795e199 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes which implement topological predicates optimized for particular kinds of geometries. + */ +package org.locationtech.jts.operation.predicate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package.html deleted file mode 100644 index 7e58ae93f4..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes which implement topological predicates optimized for particular kinds of geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBuilder.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBuilder.java index 6522fd7bb1..4e8c6b457d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBuilder.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundle.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundle.java index 07ceda5fb2..3580f4d2f4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundle.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundle.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundleStar.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundleStar.java index 66c3156394..e67f624c48 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundleStar.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/EdgeEndBundleStar.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateComputer.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateComputer.java index f559a1b0ef..d93100b377 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateComputer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateComputer.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNode.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNode.java index a1e28806fc..90086c9618 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNode.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNode.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeFactory.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeFactory.java index c96bceb3af..671a7f6fdc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeFactory.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeFactory.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeGraph.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeGraph.java index 69232dd1ce..04be137682 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateNodeGraph.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateOp.java index 6cb6eb0275..fb1513c025 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/RelateOp.java @@ -1,6 +1,3 @@ - - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/relate/package-info.java new file mode 100644 index 0000000000..7b8b63913e --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/relate/package-info.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes to implement the computation of the spatial relationships of Geometrys. + *

    + * The relate algorithm computes the IntersectionMatrix describing the + * relationship of two Geometrys. The algorithm for computing relate + * uses the intersection operations supported by topology graphs. Although the relate + * result depends on the resultant graph formed by the computed intersections, there is + * no need to explicitly compute the entire graph. + * It is sufficient to compute the local structure of the graph + * at each intersection node. + *

    + * The algorithm to compute relate has the following steps: + *

      + *
    • Build topology graphs of the two input geometries. For each geometry + * all self-intersection nodes are computed and added to the graph. + *
    • Compute nodes for all intersections between edges and nodes of the graphs. + *
    • Compute the labeling for the computed nodes by merging the labels from the input graphs. + *
    • Compute the labeling for isolated components of the graph (see below) + *
    • Compute the IntersectionMatrix from the labels on the nodes and edges. + *
    + *

    Labeling isolated components

    + * Isolated components are components (edges or nodes) of an input Geometry which + * do not contain any intersections with the other input Geometry. The + * topological relationship of these components to the other input Geometry + * must be computed in order to determine the complete labeling of the component. This can + * be done by testing whether the component lies in the interior or exterior of the other + * Geometry. If the other Geometry is 1-dimensional, the isolated + * component must lie in the exterior (since otherwise it would have an intersection with an + * edge of the Geometry). If the other Geometry is 2-dimensional, + * a Point-In-Polygon test can be used to determine whether the isolated component is in the + * interior or exterior. + *

    Package Specification

    + * + */ +package org.locationtech.jts.operation.relate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/relate/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/relate/package.html deleted file mode 100644 index e6d4c6379c..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/relate/package.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - -Contains classes to implement the computation of the spatial relationships of Geometrys. -

    -The relate algorithm computes the IntersectionMatrix describing the -relationship of two Geometrys. The algorithm for computing relate -uses the intersection operations supported by topology graphs. Although the relate -result depends on the resultant graph formed by the computed intersections, there is -no need to explicitly compute the entire graph. -It is sufficient to compute the local structure of the graph -at each intersection node. -

    -The algorithm to compute relate has the following steps: -

      -
    • Build topology graphs of the two input geometries. For each geometry - all self-intersection nodes are computed and added to the graph. -
    • Compute nodes for all intersections between edges and nodes of the graphs. -
    • Compute the labeling for the computed nodes by merging the labels from the input graphs. -
    • Compute the labeling for isolated components of the graph (see below) -
    • Compute the IntersectionMatrix from the labels on the nodes and edges. -
    - -

    Labeling isolated components

    - -Isolated components are components (edges or nodes) of an input Geometry which -do not contain any intersections with the other input Geometry. The -topological relationship of these components to the other input Geometry -must be computed in order to determine the complete labeling of the component. This can -be done by testing whether the component lies in the interior or exterior of the other -Geometry. If the other Geometry is 1-dimensional, the isolated -component must lie in the exterior (since otherwise it would have an intersection with an -edge of the Geometry). If the other Geometry is 2-dimensional, -a Point-In-Polygon test can be used to determine whether the isolated component is in the -interior or exterior. - -

    Package Specification

    - - - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/union/UnaryUnionOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/union/UnaryUnionOp.java index 0433e78a72..92f119764b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/union/UnaryUnionOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/union/UnaryUnionOp.java @@ -26,7 +26,7 @@ /** * Unions a Collection of {@link Geometry}s or a single Geometry - * (which may be a {@link GeoometryCollection}) together. + * (which may be a {@link GeometryCollection}) together. * By using this special-purpose operation over a collection of geometries * it is possible to take advantage of various optimizations to improve performance. * Heterogeneous {@link GeometryCollection}s are fully supported. diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/union/UnionStrategy.java b/modules/core/src/main/java/org/locationtech/jts/operation/union/UnionStrategy.java index 15ebf321be..49a490c135 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/union/UnionStrategy.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/union/UnionStrategy.java @@ -24,7 +24,7 @@ public interface UnionStrategy { /** * Computes the union of two geometries. - * This method may throw a {@link Toppology Exception} + * This method may throw a {@link org.locationtech.jts.geom.TopologyException} * if one is encountered. * * @param g0 a geometry diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/union/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/union/package-info.java new file mode 100644 index 0000000000..71c8ec4553 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/union/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to perform efficient unioning of collections of geometries. + */ +package org.locationtech.jts.operation.union; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/union/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/union/package.html deleted file mode 100644 index 1c503d089a..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/union/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to perform efficient unioning of collections of geometries. - - - 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 26929ddc3a..cefdb32746 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 @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2021 Martin Davis. * @@ -73,8 +71,8 @@ *

    * By default this class finds a single non-simple location. * To find all non-simple locations, set {@link #setFindAllLocations(boolean)} - * before calling {@link #isSimple(), and retrieve the locations - * via {@link #getNonSimpleLocations(). + * before calling {@link #isSimple()}, and retrieve the locations + * via {@link #getNonSimpleLocations()}. * This can be used to find all intersection points in a linear network. * * @see BoundaryNodeRule diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/RepeatedPointTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/RepeatedPointTester.java index 9b04851d04..919c9bf16c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/RepeatedPointTester.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/RepeatedPointTester.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/TopologyValidationError.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/TopologyValidationError.java index 6818b14c0b..9eb14b8826 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/TopologyValidationError.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/TopologyValidationError.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/package-info.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/package-info.java new file mode 100644 index 0000000000..c0c1b5e46f --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes for testing the validity and simplicity of geometries, + * as defined in the OGC Simple Features specification. + */ +package org.locationtech.jts.operation.valid; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html b/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html deleted file mode 100644 index 2395d66463..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -Classes for testing the validity and simplicity of geometries, -as defined in the OGC Simple Features specification. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java index 682c03bc0e..7db4807ede 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdgeStar.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdgeStar.java index f457d61064..56e69b2ddc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdgeStar.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/DirectedEdgeStar.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -10,8 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - - package org.locationtech.jts.planargraph; import java.util.ArrayList; @@ -33,7 +30,7 @@ public class DirectedEdgeStar /** * The underlying list of outgoing DirectedEdges */ - protected List outEdges = new ArrayList(); + protected List outEdges = new ArrayList<>(); private boolean sorted = false; /** @@ -59,7 +56,7 @@ public void remove(DirectedEdge de) /** * Returns an Iterator over the DirectedEdges, in ascending order by angle with the positive x-axis. */ - public Iterator iterator() + public Iterator iterator() { sortEdges(); return outEdges.iterator(); @@ -75,7 +72,7 @@ public Iterator iterator() */ public Coordinate getCoordinate() { - Iterator it = iterator(); + Iterator it = iterator(); if (! it.hasNext()) return null; DirectedEdge e = (DirectedEdge) it.next(); return e.getCoordinate(); @@ -84,7 +81,7 @@ public Coordinate getCoordinate() /** * Returns the DirectedEdges, in ascending order by angle with the positive x-axis. */ - public List getEdges() + public List getEdges() { sortEdges(); return outEdges; diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/Edge.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/Edge.java index 5b5966cb96..968f2834c8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/Edge.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/Edge.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/GraphComponent.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/GraphComponent.java index 31fb712406..2e8f38e5d5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/GraphComponent.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/GraphComponent.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/Node.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/Node.java index be8fb19660..ae111b778f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/Node.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/Node.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -10,8 +9,6 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - - package org.locationtech.jts.planargraph; import java.util.Collection; diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/PlanarGraph.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/PlanarGraph.java index 8bad9758ab..83c5bd3119 100644 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/PlanarGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/PlanarGraph.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package-info.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package-info.java new file mode 100644 index 0000000000..2e938f5330 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes which implement graph algorithms on planar graphs. + */ +package org.locationtech.jts.planargraph.algorithm; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package.html b/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package.html deleted file mode 100644 index 7cdfd8a7b0..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/algorithm/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes which implement graph algorithms on planar graphs. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/package-info.java b/modules/core/src/main/java/org/locationtech/jts/planargraph/package-info.java new file mode 100644 index 0000000000..790e6d8221 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/planargraph/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes to implement a planar graph data structure. + */ +package org.locationtech.jts.planargraph; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/planargraph/package.html b/modules/core/src/main/java/org/locationtech/jts/planargraph/package.html deleted file mode 100644 index e59938beb1..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/planargraph/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Contains classes to implement a planar graph data structure. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBits.java b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBits.java index 7e6ccc228a..fd27b44caf 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBits.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBits.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsOp.java b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsOp.java index 2571e048b4..8c3c94eecd 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsOp.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsRemover.java b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsRemover.java index b5e5a92971..b01a9eb163 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsRemover.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/CommonBitsRemover.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/EnhancedPrecisionOp.java b/modules/core/src/main/java/org/locationtech/jts/precision/EnhancedPrecisionOp.java index 75036b808c..99447bdd7b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/EnhancedPrecisionOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/EnhancedPrecisionOp.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index f635ecdcae..d0547cc6ab 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Martin Davis. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/MinimumClearance.java b/modules/core/src/main/java/org/locationtech/jts/precision/MinimumClearance.java index 3006e29c94..82e8276f95 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/MinimumClearance.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/MinimumClearance.java @@ -55,7 +55,7 @@ * The following image shows an example of the Minimum Clearance * of a simple polygon. *

    - *

    + *
    minimum clearance
    *

    * If G has only a single vertex (i.e. is a * {@link Point}), the value of the minimum clearance diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java b/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java index b0a08968fb..19cdb8333c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/PointwisePrecisionReducerTransformer.java @@ -14,8 +14,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.PrecisionModel; import org.locationtech.jts.geom.util.GeometryTransformer; diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducer.java index 58abaf9247..35faf37da1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducer.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/package-info.java b/modules/core/src/main/java/org/locationtech/jts/precision/package-info.java new file mode 100644 index 0000000000..c25075a94f --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/precision/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Provides classes for analyzing and + * manipulating the precision of Geometries. + */ +package org.locationtech.jts.precision; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/package.html b/modules/core/src/main/java/org/locationtech/jts/precision/package.html deleted file mode 100644 index b09149832a..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/precision/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -Provides classes for analyzing and -manipulating the precision of Geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/simplify/package-info.java b/modules/core/src/main/java/org/locationtech/jts/simplify/package-info.java new file mode 100644 index 0000000000..a4f03dc27b --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/simplify/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes which implement algorithms for simplifying or generalizing geometries. + */ +package org.locationtech.jts.simplify; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/simplify/package.html b/modules/core/src/main/java/org/locationtech/jts/simplify/package.html deleted file mode 100644 index 127e21bce9..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/simplify/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes which implement algorithms for simplifying or generalizing geometries. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/package-info.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/package-info.java new file mode 100644 index 0000000000..b81f9e2b7e --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to compute Delaunay triangulations. + */ +package org.locationtech.jts.triangulate; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/package.html b/modules/core/src/main/java/org/locationtech/jts/triangulate/package.html deleted file mode 100644 index d49fa04b4a..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - -Classes to compute Delaunay triangulations. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package-info.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package-info.java new file mode 100644 index 0000000000..2477be9805 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package-info.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes to implement a topological subdivision of quadeges, to support creating triangulations + * and Voronoi diagrams. + */ +package org.locationtech.jts.triangulate.quadedge; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package.html b/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package.html deleted file mode 100644 index c69a9396c5..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/quadedge/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -Classes to implement a topological subdivision of quadeges, to support creating triangulations -and Voronoi diagrams. - - - diff --git a/modules/core/src/main/java/org/locationtech/jts/util/Assert.java b/modules/core/src/main/java/org/locationtech/jts/util/Assert.java index 3484f63f01..f87e38989e 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/Assert.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/Assert.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/AssertionFailedException.java b/modules/core/src/main/java/org/locationtech/jts/util/AssertionFailedException.java index 3b264358fe..1ec789a6dc 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/AssertionFailedException.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/AssertionFailedException.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/CoordinateArrayFilter.java b/modules/core/src/main/java/org/locationtech/jts/util/CoordinateArrayFilter.java index 5279226f42..07d60a1335 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/CoordinateArrayFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/CoordinateArrayFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/CoordinateCountFilter.java b/modules/core/src/main/java/org/locationtech/jts/util/CoordinateCountFilter.java index 640248eaea..d09e8c8709 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/CoordinateCountFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/CoordinateCountFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/Debug.java b/modules/core/src/main/java/org/locationtech/jts/util/Debug.java index 10764382e7..44483ac4ed 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/Debug.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/Debug.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -287,7 +285,7 @@ else if (obj instanceof Iterator) { public void instancePrint(Iterator it) { - for (; it.hasNext(); ) { + while (it.hasNext()) { Object obj = it.next(); instancePrintObject(obj); } diff --git a/modules/core/src/main/java/org/locationtech/jts/util/Stopwatch.java b/modules/core/src/main/java/org/locationtech/jts/util/Stopwatch.java index 4351ff9003..e6e3cbee79 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/Stopwatch.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/Stopwatch.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/UniqueCoordinateArrayFilter.java b/modules/core/src/main/java/org/locationtech/jts/util/UniqueCoordinateArrayFilter.java index 27579730b1..1dc1d5b276 100644 --- a/modules/core/src/main/java/org/locationtech/jts/util/UniqueCoordinateArrayFilter.java +++ b/modules/core/src/main/java/org/locationtech/jts/util/UniqueCoordinateArrayFilter.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/main/java/org/locationtech/jts/util/package-info.java b/modules/core/src/main/java/org/locationtech/jts/util/package-info.java new file mode 100644 index 0000000000..d993191512 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/util/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains support classes for the Java Topology Suite. + */ +package org.locationtech.jts.util; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/util/package.html b/modules/core/src/main/java/org/locationtech/jts/util/package.html deleted file mode 100644 index e44abbc281..0000000000 --- a/modules/core/src/main/java/org/locationtech/jts/util/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - -Contains support classes for the Java Topology Suite. - - diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java index 7fe8cbba12..80b38bf7f1 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumBoundingCircleTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumBoundingCircleTest.java index bdeda19c42..d59bb9cc5f 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumBoundingCircleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumBoundingCircleTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumDiameterTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumDiameterTest.java index 9c4beaddcb..cac85799a4 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumDiameterTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/MinimumDiameterTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/NonRobustLineIntersector.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/NonRobustLineIntersector.java index bfdc1a642a..e8e4eab667 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/NonRobustLineIntersector.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/NonRobustLineIntersector.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/AreaLengthTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/AreaLengthTest.java index 2f42e41c4c..6655ac4654 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/AreaLengthTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/AreaLengthTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateSequencesTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateSequencesTest.java index f704d6b004..e8f974c662 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateSequencesTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateSequencesTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -118,16 +117,16 @@ public void testReverse() { } /** - * Method used to create a {@link this.ordinateValues}. + * Method used to create a {@link #ordinateValues}. * Usage: remove first 't' and run as unit test. * Note: When parameters are changed, some unit tests may need to be * changed, too.

    - * This is especially true for the (@link testMinCoordinateIndex) test, + * This is especially true for the {@link #testMinCoordinateIndex()} test, * which assumes that the coordinates in the sequence are all within an * envelope of [Env(10, 100, 10, 100)]. *

    . * - * @deprecated only use to update {@link this.ordinateValues} + * @deprecated only use to update {@link #ordinateValues} */ public void ttestCreateRandomOrdinates() { CoordinateSequence sequence = createRandomTestSequence(CoordinateArraySequenceFactory.instance(), 20, diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryImplTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryImplTest.java index bd31718e5a..27ccdbbfdd 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryImplTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryImplTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/MiscellaneousTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/MiscellaneousTest.java index f537e9712d..94f61f22db 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/MiscellaneousTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/MiscellaneousTest.java @@ -267,7 +267,7 @@ public void testPointGetBoundary() throws Exception { assertTrue(g.getBoundary().isEmpty()); } -/** +/* * @todo Enable when #isSimple implemented */ // public void testMultiPointIsSimple1() throws Exception { @@ -280,7 +280,7 @@ public void testMultiPointGetBoundary() throws Exception { assertTrue(g.getBoundary().isEmpty()); } -/** +/* * @todo Enable when #isSimple implemented */ // public void testMultiPointIsSimple2() throws Exception { @@ -288,7 +288,7 @@ public void testMultiPointGetBoundary() throws Exception { // assertTrue(! g.isSimple()); // } -/** +/* * @todo Enable when #isSimple implemented */ // public void testLineStringIsSimple1() throws Exception { @@ -309,7 +309,7 @@ public void testLineStringGetBoundary2() throws Exception { assertTrue(g.getBoundary().isEmpty()); } -/** +/* * @todo Enable when #isSimple implemented */ // public void testLineStringIsSimple2() throws Exception { @@ -375,7 +375,7 @@ public void testMultiPolygonIsSimple2() throws Exception { // assertTrue(! g.isSimple()); // } -/** +/* * @todo Enable when #isSimple implemented */ // public void testMultiLineStringIsSimple1() throws Exception { @@ -385,7 +385,7 @@ public void testMultiPolygonIsSimple2() throws Exception { // assertTrue(g.isSimple()); // } -/** +/* * @todo Enable when #isSimple implemented */ // public void testMultiLineStringIsSimple2() throws Exception { diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/MultiPointImplTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/MultiPointImplTest.java index 103898dc6b..3715f83780 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/MultiPointImplTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/MultiPointImplTest.java @@ -40,7 +40,7 @@ public static void main(String args[]) { public static Test suite() { return new TestSuite(MultiPointImplTest.class); } -/** +/* * @todo Enable when #isSimple implemented */ // public void testIsSimple1() throws Exception { @@ -48,7 +48,7 @@ public static void main(String args[]) { // assertTrue(m.isSimple()); // } -/** +/* * @todo Enable when #isSimple implemented */ // public void testIsSimple2() throws Exception { diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/impl/BasicCoordinateSequenceTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/impl/BasicCoordinateSequenceTest.java index e51548b924..645e216770 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/impl/BasicCoordinateSequenceTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/impl/BasicCoordinateSequenceTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/SpatialIndexTester.java b/modules/core/src/test/java/org/locationtech/jts/index/SpatialIndexTester.java index 2424325634..52dcad640f 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/SpatialIndexTester.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/SpatialIndexTester.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/bintree/BinTreeCorrectTest.java b/modules/core/src/test/java/org/locationtech/jts/index/bintree/BinTreeCorrectTest.java index 52ccb6498c..83771d2555 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/bintree/BinTreeCorrectTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/bintree/BinTreeCorrectTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/bintree/IntervalList.java b/modules/core/src/test/java/org/locationtech/jts/index/bintree/IntervalList.java index 2746e8fe2a..3d10b6271b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/bintree/IntervalList.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/bintree/IntervalList.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/quadtree/EnvelopeList.java b/modules/core/src/test/java/org/locationtech/jts/index/quadtree/EnvelopeList.java index 40ae7fdce1..b53eb49078 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/quadtree/EnvelopeList.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/quadtree/EnvelopeList.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * @@ -25,7 +24,7 @@ */ public class EnvelopeList { - List envList = new ArrayList(); + List envList = new ArrayList<>(); public EnvelopeList() { } @@ -35,9 +34,9 @@ public void add(Envelope env) envList.add(env); } - public List query(Envelope searchEnv) + public List query(Envelope searchEnv) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (Iterator i = envList.iterator(); i.hasNext(); ) { Envelope env = (Envelope) i.next(); if (env.intersects(searchEnv)) diff --git a/modules/core/src/test/java/org/locationtech/jts/index/quadtree/QuadtreeCorrectTest.java b/modules/core/src/test/java/org/locationtech/jts/index/quadtree/QuadtreeCorrectTest.java index 30dfd366b1..fc545470ca 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/quadtree/QuadtreeCorrectTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/quadtree/QuadtreeCorrectTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/strtree/GeometryDistanceComparator.java b/modules/core/src/test/java/org/locationtech/jts/index/strtree/GeometryDistanceComparator.java index 731cb98ff4..6b52167348 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/strtree/GeometryDistanceComparator.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/strtree/GeometryDistanceComparator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2017 Jia Yu. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/strtree/IntervalTest.java b/modules/core/src/test/java/org/locationtech/jts/index/strtree/IntervalTest.java index ba4306cb3b..76b7002fb1 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/strtree/IntervalTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/strtree/IntervalTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/strtree/SIRtreeTest.java b/modules/core/src/test/java/org/locationtech/jts/index/strtree/SIRtreeTest.java index 7a39be7e39..27160dd438 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/strtree/SIRtreeTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/strtree/SIRtreeTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeNearestNeighbourTest.java b/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeNearestNeighbourTest.java index bb98bec9e2..1e9740569d 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeNearestNeighbourTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeNearestNeighbourTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeTest.java b/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeTest.java index 524555b76d..ff7744ff8f 100644 --- a/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/index/strtree/STRtreeTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/io/SerializabilityTest.java b/modules/core/src/test/java/org/locationtech/jts/io/SerializabilityTest.java index 584937874c..c7e84b5fb3 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/SerializabilityTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/SerializabilityTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/linearref/AbstractIndexedLineTest.java b/modules/core/src/test/java/org/locationtech/jts/linearref/AbstractIndexedLineTest.java index 92430b6e27..9cd11d3d8b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/linearref/AbstractIndexedLineTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/linearref/AbstractIndexedLineTest.java @@ -221,8 +221,8 @@ protected void runIndexOfAfterTest(String inputStr, * (This should be the case for all except pathological cases, * such as the input test point being beyond the end of the line). * - * @param input - * @param testPt + * @param input geometry + * @param testPt test point * @return true if the result of indexOfAfter is the same as the input point */ protected abstract boolean indexOfAfterCheck(Geometry input, Coordinate testPt); diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferResultValidatorTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferResultValidatorTest.java index c039e1978c..5d798ef8af 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferResultValidatorTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferResultValidatorTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java index ddea72c26e..78b4c0d019 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java index 67069083e6..3cf73ae47e 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferValidator.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/DepthSegmentTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/DepthSegmentTest.java index b1ed9e7a8b..489d46285d 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/DepthSegmentTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/DepthSegmentTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/distance/BaseDistanceTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/distance/BaseDistanceTest.java index d3399ce3e7..f9b6a6804c 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/distance/BaseDistanceTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/distance/BaseDistanceTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/distance/DistanceTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/distance/DistanceTest.java index 7410c74ca4..cc5f1adbd8 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/distance/DistanceTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/distance/DistanceTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/linemerge/LineMergerTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/linemerge/LineMergerTest.java index b0f74dee65..fb86adc3c9 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/linemerge/LineMergerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/linemerge/LineMergerTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/polygonize/PolygonizerTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/polygonize/PolygonizerTest.java index 5305e0d113..b7d16f92e4 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/polygonize/PolygonizerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/polygonize/PolygonizerTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java index 34b313862f..d0a8cf1c17 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducerTest.java index 10be13aba6..8d59e170f6 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/SimpleGeometryPrecisionReducerTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/IsValidTester.java b/modules/core/src/test/java/test/jts/IsValidTester.java index 46f6446b7d..eae53800fa 100644 --- a/modules/core/src/test/java/test/jts/IsValidTester.java +++ b/modules/core/src/test/java/test/jts/IsValidTester.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/geom/CoordinateSequenceExperiment2.java b/modules/core/src/test/java/test/jts/geom/CoordinateSequenceExperiment2.java index ea10caa861..1a4c7cb668 100644 --- a/modules/core/src/test/java/test/jts/geom/CoordinateSequenceExperiment2.java +++ b/modules/core/src/test/java/test/jts/geom/CoordinateSequenceExperiment2.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/geom/GeometryTestFactory.java b/modules/core/src/test/java/test/jts/geom/GeometryTestFactory.java index 320608f224..3058a92e23 100644 --- a/modules/core/src/test/java/test/jts/geom/GeometryTestFactory.java +++ b/modules/core/src/test/java/test/jts/geom/GeometryTestFactory.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/index/STRtreeDemo.java b/modules/core/src/test/java/test/jts/index/STRtreeDemo.java index bbde643bb4..7dbf1cb760 100644 --- a/modules/core/src/test/java/test/jts/index/STRtreeDemo.java +++ b/modules/core/src/test/java/test/jts/index/STRtreeDemo.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/ExamplePerfTest.java b/modules/core/src/test/java/test/jts/perf/ExamplePerfTest.java index 786d8e517c..94d3a91f18 100644 --- a/modules/core/src/test/java/test/jts/perf/ExamplePerfTest.java +++ b/modules/core/src/test/java/test/jts/perf/ExamplePerfTest.java @@ -13,7 +13,7 @@ package test.jts.perf; /** - * An example of the usage of the {@PerformanceTestRunner}. + * An example of the usage of the {@link PerformanceTestRunner}. * * @author Martin Davis * diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/InteriorPointAreaPerfTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/InteriorPointAreaPerfTest.java index d8995b574a..a3d5531131 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/InteriorPointAreaPerfTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/InteriorPointAreaPerfTest.java @@ -23,7 +23,7 @@ import test.jts.perf.PerformanceTestRunner; /** - * An example of the usage of the {@PerformanceTestRunner}. + * An example of the usage of the {@link PerformanceTestRunner}. * * @author Martin Davis * diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionPerfTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionPerfTest.java index 0e455658c2..de10230835 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionPerfTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionPerfTest.java @@ -24,9 +24,9 @@ *
      *
    • DP - a basic double-precision (DP) implementation, with no attempt at reducing the effects of numerical round-off *
    • DP-Cond - a DP implementation in which the inputs are conditioned by translating them to around the origin - *
    • DP-CB - a DP implementation using the {@link CommonBitsRemover} functionality - *
    • DD - an implementation using extended-precision {@link DoubleDouble} arithmetic - *
    • DDFilter - an experimental implementation using extended-precision {@link DoubleDouble} arithmetic + *
    • DP-CB - a DP implementation using the {@link org.locationtech.jts.precision.CommonBitsRemover} functionality + *
    • DD - an implementation using extended-precision {@link org.locationtech.jts.math.DD} arithmetic + *
    • DDFilter - an experimental implementation using extended-precision {@link org.locationtech.jts.math.DD} arithmetic * along with a filter that uses DP if the accuracy is sufficient *
    *

    Results

    @@ -43,9 +43,6 @@ * is provided by DP-Cond. * * @author Martin Davis - * - * @see IntersectionStressTest - * */ public class IntersectionPerfTest extends PerformanceTestCase { private static final int N_ITER = 1000000; diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionStressTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionStressTest.java index 1379b2d8b6..a63453cab0 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/IntersectionStressTest.java @@ -32,8 +32,8 @@ *
      *
    • DP - a basic double-precision (DP) implementation, with no attempt at reducing the effects of numerical round-off *
    • DP-Cond - a DP implementation in which the inputs are conditioned by translating them to around the origin - *
    • DP-CB - a DP implementation using the {@link CommonBitsRemover} functionality - *
    • DD - an implementation using extended-precision {@link DoubleDouble} arithmetic + *
    • DP-CB - a DP implementation using the {@link org.locationtech.jts.precision.CommonBitsRemover} functionality + *
    • DD - an implementation using extended-precision {@link org.locationtech.jts.math.DD} arithmetic *
    *

    Results

    *
      diff --git a/modules/core/src/test/java/test/jts/perf/algorithm/PointInRingRobustnessTest.java b/modules/core/src/test/java/test/jts/perf/algorithm/PointInRingRobustnessTest.java index 3230a279a6..b31a6da00e 100644 --- a/modules/core/src/test/java/test/jts/perf/algorithm/PointInRingRobustnessTest.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/PointInRingRobustnessTest.java @@ -23,10 +23,10 @@ *

      * The stress test reveals that the original {@link RayCrossingCounter} * has inconsistencies with the - * {@link CGAlgorithmsDD#index(Coordinate, Coordinate, Coordinate)} + * {@link org.locationtech.jts.algorithm.CGAlgorithmsDD#orientationIndex(Coordinate, Coordinate, Coordinate)} * orientation index computation * (which is now the standard in JTS, due to its improved robustness). - * The {@link RayCrossingCounterDD} implementation is consistent, + * The {@link RayCrossingCounter} implementation is consistent, * as expected. *

      * Note that the inconsistency does not indicate which algorithm is diff --git a/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java index 1bfce5dd42..f2649361cc 100644 --- a/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java +++ b/modules/core/src/test/java/test/jts/perf/geom/prep/PreparedGeometryConcurrencyBugTest.java @@ -19,7 +19,7 @@ import org.locationtech.jts.io.WKTReader; /** - * Tests race condition in {@link SortedPackedIntervalRTree}. + * Tests race condition in {@link org.locationtech.jts.index.intervalrtree.SortedPackedIntervalRTree}. * See https://github.com/locationtech/jts/pull/746 * * To achieve maximum reproducibility diff --git a/modules/core/src/test/java/test/jts/perf/index/EnvelopeList.java b/modules/core/src/test/java/test/jts/perf/index/EnvelopeList.java index 9d7d662cae..641dd834f6 100644 --- a/modules/core/src/test/java/test/jts/perf/index/EnvelopeList.java +++ b/modules/core/src/test/java/test/jts/perf/index/EnvelopeList.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/index/IndexTester.java b/modules/core/src/test/java/test/jts/perf/index/IndexTester.java index cf3d7ce0d2..c508845e09 100644 --- a/modules/core/src/test/java/test/jts/perf/index/IndexTester.java +++ b/modules/core/src/test/java/test/jts/perf/index/IndexTester.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/index/TreeTimeTest.java b/modules/core/src/test/java/test/jts/perf/index/TreeTimeTest.java index c0e6ed3123..fd3126a1b9 100644 --- a/modules/core/src/test/java/test/jts/perf/index/TreeTimeTest.java +++ b/modules/core/src/test/java/test/jts/perf/index/TreeTimeTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/operation/buffer/BufferCorrectnessTest.java b/modules/core/src/test/java/test/jts/perf/operation/buffer/BufferCorrectnessTest.java index f2e3411ae7..c75eda4217 100644 --- a/modules/core/src/test/java/test/jts/perf/operation/buffer/BufferCorrectnessTest.java +++ b/modules/core/src/test/java/test/jts/perf/operation/buffer/BufferCorrectnessTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/operation/buffer/DepthSegmentStressTest.java b/modules/core/src/test/java/test/jts/perf/operation/buffer/DepthSegmentStressTest.java index 5ee82ed34c..07fa9e6fba 100644 --- a/modules/core/src/test/java/test/jts/perf/operation/buffer/DepthSegmentStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/operation/buffer/DepthSegmentStressTest.java @@ -22,7 +22,7 @@ import test.jts.perf.PerformanceTestRunner; /** - * Stress tests {@link DepthSegment} to determine if the compare contract is maintained. + * Stress tests DepthSegment to determine if the compare contract is maintained. * * @author Martin Davis * diff --git a/modules/core/src/test/java/test/jts/perf/operation/buffer/IteratedBufferStressTest.java b/modules/core/src/test/java/test/jts/perf/operation/buffer/IteratedBufferStressTest.java index 7cd352eae0..05b738acc0 100644 --- a/modules/core/src/test/java/test/jts/perf/operation/buffer/IteratedBufferStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/operation/buffer/IteratedBufferStressTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/operation/buffer/OffsetCurveCorrectnessTest.java b/modules/core/src/test/java/test/jts/perf/operation/buffer/OffsetCurveCorrectnessTest.java index ea26160e23..cd20324f28 100644 --- a/modules/core/src/test/java/test/jts/perf/operation/buffer/OffsetCurveCorrectnessTest.java +++ b/modules/core/src/test/java/test/jts/perf/operation/buffer/OffsetCurveCorrectnessTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/operation/buffer/RandomLineBufferStressTest.java b/modules/core/src/test/java/test/jts/perf/operation/buffer/RandomLineBufferStressTest.java index 476b0caee7..cd57d3f214 100644 --- a/modules/core/src/test/java/test/jts/perf/operation/buffer/RandomLineBufferStressTest.java +++ b/modules/core/src/test/java/test/jts/perf/operation/buffer/RandomLineBufferStressTest.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java b/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java index 2ecc05aeb0..e01a1239cd 100644 --- a/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java +++ b/modules/core/src/test/java/test/jts/perf/precision/GeometryPrecisionReducerPerfTest.java @@ -22,8 +22,8 @@ import test.jts.perf.PerformanceTestRunner; /** - * This test revealed a scaling issue with the {@link SnapRoundingNoder}: - * the {@link HotPixelIndex} could not handle very large numbers + * This test revealed a scaling issue with the {@link org.locationtech.jts.noding.snapround.SnapRoundingNoder}: + * the {@link org.locationtech.jts.noding.snapround.HotPixelIndex} could not handle very large numbers * of points due to kdTree becoming unbalanced. * * @author Martin Davis diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java index 7e24e790e5..2cd68e6773 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/BasicExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinate.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinate.java index 5a85089284..d0d0939c54 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinate.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinate.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateExample.java index c20df47312..1807263edc 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequence.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequence.java index 8817b574a5..573430bd5b 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequence.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequence.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequenceFactory.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequenceFactory.java index 62de7101e3..410d30de53 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequenceFactory.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/ExtendedCoordinateSequenceFactory.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/PrecisionModelExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/PrecisionModelExample.java index 9a4cecef5c..77edeae37c 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/PrecisionModelExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/PrecisionModelExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/geom/prep/PreparedGeometryExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/geom/prep/PreparedGeometryExample.java index 9f45f4df69..1bec1c7c45 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/geom/prep/PreparedGeometryExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/geom/prep/PreparedGeometryExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/operation/distance/ClosestPointExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/operation/distance/ClosestPointExample.java index 81e4c7e04c..a2015f92e7 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/operation/distance/ClosestPointExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/operation/distance/ClosestPointExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/operation/linemerge/LineMergeExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/operation/linemerge/LineMergeExample.java index a5b46eb319..2da85d6abb 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/operation/linemerge/LineMergeExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/operation/linemerge/LineMergeExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/operation/polygonize/PolygonizeExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/operation/polygonize/PolygonizeExample.java index 019fcbd3b1..9dabfe0348 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/operation/polygonize/PolygonizeExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/operation/polygonize/PolygonizeExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/precision/EnhancedPrecisionOpExample.java b/modules/example/src/main/java/org/locationtech/jtsexample/precision/EnhancedPrecisionOpExample.java index f09417418a..d60fdeaae7 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/precision/EnhancedPrecisionOpExample.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/precision/EnhancedPrecisionOpExample.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/example/src/main/java/org/locationtech/jtsexample/technique/SearchUsingPreparedGeometryIndex.java b/modules/example/src/main/java/org/locationtech/jtsexample/technique/SearchUsingPreparedGeometryIndex.java index 94f3e125e9..9a5397fda4 100644 --- a/modules/example/src/main/java/org/locationtech/jtsexample/technique/SearchUsingPreparedGeometryIndex.java +++ b/modules/example/src/main/java/org/locationtech/jtsexample/technique/SearchUsingPreparedGeometryIndex.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * 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 89fa053cf8..883b31ace9 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 @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jts.io.geojson; import org.locationtech.jts.algorithm.Orientation; diff --git a/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraReader.java b/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraReader.java index 0796f871d9..c1ba15c022 100644 --- a/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraReader.java +++ b/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraReader.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jts.io.oracle; import java.sql.SQLException; diff --git a/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraWriter.java b/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraWriter.java index 7d6f32b9cc..0876a2d5a1 100644 --- a/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraWriter.java +++ b/modules/io/ora/src/main/java/org/locationtech/jts/io/oracle/OraWriter.java @@ -1,11 +1,3 @@ -/* - * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. - * - * This program and the accompanying materials are made available under the terms - * of the OSGeo BSD License v1.0 available at: - * - * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt - */ /* * Copyright (c) 2016 Vivid Solutions. * @@ -17,7 +9,14 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ - +/* + * Copyright (c) 2003 Open Source Geospatial Foundation, All rights reserved. + * + * This program and the accompanying materials are made available under the terms + * of the OSGeo BSD License v1.0 available at: + * + * https://www.osgeo.org/sites/osgeo.org/files/Page/osgeo-bsd-license.txt + */ package org.locationtech.jts.io.oracle; import java.sql.SQLException; diff --git a/modules/io/pom.xml b/modules/io/pom.xml index bac38ac76a..937875671a 100644 --- a/modules/io/pom.xml +++ b/modules/io/pom.xml @@ -19,7 +19,7 @@ ora - + diff --git a/modules/lab/pom.xml b/modules/lab/pom.xml index 4061647e0c..0e696de94b 100644 --- a/modules/lab/pom.xml +++ b/modules/lab/pom.xml @@ -20,8 +20,8 @@ org.locationtech.jts jts-core ${project.version} - test-jar - test + test-jar + test @@ -32,6 +32,6 @@ - + diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/EdgeRayFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/EdgeRayFunctions.java index 4f7d529665..6565d3d175 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/EdgeRayFunctions.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/EdgeRayFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtslab; import org.locationtech.jts.geom.Geometry; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java index 168c543783..029a71ee88 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/IteratedOverlayFunctions.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2020 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtslab; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/OverlayOptFunctions.java b/modules/lab/src/main/java/org/locationtech/jtslab/OverlayOptFunctions.java index 96496ded1d..f0df922bb7 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/OverlayOptFunctions.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/OverlayOptFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Martin Davis. + * Copyright (c) 2019 Martin Davis, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRay.java b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRay.java index 2e6c8e4918..cf1790f358 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRay.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRay.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtslab.edgeray; import org.locationtech.jts.geom.Coordinate; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayArea.java b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayArea.java index 857e9ca6f3..378c34c96f 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayArea.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayArea.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtslab.edgeray; import org.locationtech.jts.algorithm.Orientation; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java index df9035602a..07fb6a9d8e 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/edgeray/EdgeRayIntersectionArea.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2018 Martin Davis, and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtslab.edgeray; import org.locationtech.jts.algorithm.LineIntersector; diff --git a/modules/lab/src/main/java/org/locationtech/jtslab/geom/util/GeometryEditorEx.java b/modules/lab/src/main/java/org/locationtech/jtslab/geom/util/GeometryEditorEx.java index 42ea6b3230..e5f6c55bcd 100644 --- a/modules/lab/src/main/java/org/locationtech/jtslab/geom/util/GeometryEditorEx.java +++ b/modules/lab/src/main/java/org/locationtech/jtslab/geom/util/GeometryEditorEx.java @@ -34,10 +34,10 @@ * which are modifications of existing ones, * maintaining the same type structure. * Geometry objects are intended to be treated as immutable. - * This class "modifies" Geometrys + * This class "modifies" Geometry instances * by traversing them, applying a user-defined * {@link GeometryEditorOperation}, {@link CoordinateSequenceOperation} or {@link CoordinateOperation} - * and creating new Geometrys with the same structure but + * and creating new Geometry instances with the same structure but * (possibly) modified components. *

      * Examples of the kinds of modifications which can be made are: @@ -58,7 +58,7 @@ * If changing the structure is required, use a {@link GeometryTransformer}. *

      * This class supports creating an edited Geometry - * using a different GeometryFactory via the {@link #GeometryEditor(GeometryFactory)} + * using a different GeometryFactory via the {@link org.locationtech.jts.geom.util.GeometryEditor#GeometryEditor(GeometryFactory)} * constructor. * Examples of situations where this is required is if the geometry is * transformed to a new SRID and/or a new PrecisionModel. @@ -99,7 +99,7 @@ public GeometryEditorEx() * Creates a new GeometryEditor object which will create * edited {@link Geometry}s with the given {@link GeometryFactory}. * - * @param targetFactory the GeometryFactory to create edited Geometrys with + * @param targetFactory the GeometryFactory to create edited Geometries with */ public GeometryEditorEx(GeometryFactory targetFactory) { @@ -108,7 +108,7 @@ public GeometryEditorEx(GeometryFactory targetFactory) /** * Creates a GeometryEditor which edits geometries using - * a given {@link GeometryOperation} + * a given {@link GeometryOperation} * and the same {@link GeometryFactory} as the input Geometry. * * @param operation the edit operation to use @@ -249,7 +249,7 @@ private Polygon editPolygon(Polygon polygon) { return targetFactory.createPolygon(null, null); } - ArrayList holes = new ArrayList(); + ArrayList holes = new ArrayList<>(); for (int i = 0; i < newPolygon.getNumInteriorRing(); i++) { LinearRing hole = (LinearRing) edit(newPolygon.getInteriorRingN(i)); if (hole == null || hole.isEmpty()) { @@ -274,7 +274,7 @@ private GeometryCollection editGeometryCollection( } // edit the component geometries - ArrayList geometries = new ArrayList(); + ArrayList geometries = new ArrayList<>(); for (int i = 0; i < collectionForType.getNumGeometries(); i++) { Geometry geometry = edit(collectionForType.getGeometryN(i)); if (geometry == null || geometry.isEmpty()) { @@ -288,14 +288,14 @@ private GeometryCollection editGeometryCollection( new Point[] { })); } if (collectionForType.getClass() == MultiLineString.class) { - return targetFactory.createMultiLineString((LineString[]) geometries.toArray( + return targetFactory.createMultiLineString(geometries.toArray( new LineString[] { })); } if (collectionForType.getClass() == MultiPolygon.class) { - return targetFactory.createMultiPolygon((Polygon[]) geometries.toArray( + return targetFactory.createMultiPolygon(geometries.toArray( new Polygon[] { })); } - return targetFactory.createGeometryCollection((Geometry[]) geometries.toArray( + return targetFactory.createGeometryCollection(geometries.toArray( new Geometry[] { })); } @@ -315,7 +315,7 @@ public interface GeometryEditorOperation * It may be null if the geometry is to be deleted. * * @param geometry the Geometry to modify - * @param factory the factory with which to construct the modified Geometry + * @param targetFactory the factory with which to construct the modified Geometry * (may be different to the factory of the input geometry) * @return a new Geometry which is a modification of the input Geometry * @return null if the Geometry is to be deleted completely @@ -373,7 +373,7 @@ public final Geometry edit(Geometry geometry, GeometryFactory targetFactory) { /** * Edits the array of {@link Coordinate}s from a {@link Geometry}. *

      - * If it is desired to preserve the immutability of Geometrys, + * If it is desired to preserve the immutability of Geometry instances, * if the coordinates are changed a new array should be created * and returned. * @@ -418,7 +418,7 @@ public final Geometry edit(Geometry geometry, GeometryFactory targetFactory) { /** * Edits a {@link CoordinateSequence} from a {@link Geometry}. * - * @param coordseq the coordinate array to operate on + * @param coordSeq the coordinate array to operate on * @param geometry the geometry containing the coordinate list * @return an edited coordinate sequence (which may be the same as the input) */ diff --git a/modules/tests/pom.xml b/modules/tests/pom.xml index d31f552dfd..e5a309ed09 100644 --- a/modules/tests/pom.xml +++ b/modules/tests/pom.xml @@ -30,7 +30,6 @@ org.apache.maven.plugins maven-assembly-plugin - 2.6 jar-with-dependencies @@ -41,8 +40,8 @@ JTSTestRunner - false - + false + make-assembly diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java b/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java index e00333be18..fd624ed368 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/command/Option.java b/modules/tests/src/main/java/org/locationtech/jtstest/command/Option.java index b7c10f14e3..9e372811dd 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/command/Option.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/command/Option.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/command/OptionSpec.java b/modules/tests/src/main/java/org/locationtech/jtstest/command/OptionSpec.java index c75017e584..91e12efba1 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/command/OptionSpec.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/command/OptionSpec.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * @@ -111,7 +109,7 @@ void checkNumArgs(String[] args) throws ParseException { if (nAllowedArgs == NARGS_ZERO_OR_MORE) { - // args must be ok + return; // args must be ok } else if (nAllowedArgs == NARGS_ONE_OR_MORE) { if (args.length <= 0) diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/command/ParseException.java b/modules/tests/src/main/java/org/locationtech/jtstest/command/ParseException.java index 0c4e7de284..535ea9545b 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/command/ParseException.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/command/ParseException.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/ArgumentConverter.java b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/ArgumentConverter.java index 5bf8382a16..8d2bb975a0 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/ArgumentConverter.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/ArgumentConverter.java @@ -44,10 +44,10 @@ private Object convertFromString(Class destClass, String src) { if (destClass == Boolean.class || destClass == boolean.class) { if (src.equals("true")) { - return new Boolean(true); + return Boolean.TRUE; } else if (src.equals("false")) { - return new Boolean(false); + return Boolean.FALSE; } throwInvalidConversion(destClass, src); } diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/GeometryMethodOperation.java b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/GeometryMethodOperation.java index 754ae8ee0a..8ca6c91284 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/geomop/GeometryMethodOperation.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/geomop/GeometryMethodOperation.java @@ -150,11 +150,11 @@ private boolean convertArgFromString(Class destClass, String srcStr, Object[] co convArg[0] = null; if (destClass == Boolean.class || destClass == boolean.class) { if (srcStr.equals("true")) { - convArg[0] = new Boolean(true); + convArg[0] = Boolean.TRUE; return true; } else if (srcStr.equals("false")) { - convArg[0] = new Boolean(false); + convArg[0] = Boolean.FALSE; return true; } return false; diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/DoubleResult.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/DoubleResult.java index 2b46852013..cb5fa96323 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/DoubleResult.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/DoubleResult.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/GeometryResult.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/GeometryResult.java index cb7c031b3d..bb47114b07 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/GeometryResult.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/GeometryResult.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/IntegerResult.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/IntegerResult.java index 36daf0b69f..03825aaf4a 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/IntegerResult.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/IntegerResult.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/JTSTestRunnerCmd.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/JTSTestRunnerCmd.java index c4d0eff4dd..ba1ef96914 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/JTSTestRunnerCmd.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/JTSTestRunnerCmd.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Result.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Result.java index 21b099e92a..4a038d6b95 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Result.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/Result.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestEngine.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestEngine.java index a791acbcb9..e80e731343 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestEngine.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestEngine.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java index cb97371f94..dffd87e205 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestReader.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestRun.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestRun.java index a4ef73e706..1e3775f3cb 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestRun.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/TestRun.java @@ -1,5 +1,3 @@ - - /* * Copyright (c) 2016 Vivid Solutions. * diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/util/FilesUtil.java b/modules/tests/src/main/java/org/locationtech/jtstest/util/FilesUtil.java index 4a3bcc3250..5aa0dd636f 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/util/FilesUtil.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/util/FilesUtil.java @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2017 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ package org.locationtech.jtstest.util; import java.io.File; diff --git a/modules/tests/src/test/resources/testxml/failure/TestBufferFailure.xml b/modules/tests/src/test/resources/testxml/failure/TestBufferFailure.xml index 71f344e070..0172730256 100644 --- a/modules/tests/src/test/resources/testxml/failure/TestBufferFailure.xml +++ b/modules/tests/src/test/resources/testxml/failure/TestBufferFailure.xml @@ -84,6 +84,7 @@ POLYGON ((640638.9602330392 216830.41627953897, 640637.6789730891 216832.6245762 640653.2982606251 216801.2408982852, 640652.4493669361 216802.96480338392, 640651.3917088212 216805.1657685859, 640650.6229513759 216806.80331532354, 640649.9775878104 216808.2109784786, 640649.1804828342 216809.53050169148, 640647.295870119 216812.78718420916, 640646.353731435 216814.48827641216, 640643.8462805866 216819.32161146, 640642.8276369496 216821.423211662, 640639.9686218789 216827.89569423962, 640638.9602330392 216830.41627953897)) + diff --git a/pom.xml b/pom.xml index 4e0ad10e11..b54ed1814c 100644 --- a/pom.xml +++ b/pom.xml @@ -39,9 +39,15 @@ 1.1.1 9.1 - + 1.8 1.8 + 6.36.0 + 8.44 + + unchecked + + none @@ -60,13 +66,13 @@ Martin Davis mbdavis@VividSolutions.com Vivid Solutions Inc. - http://www.vividsolutions.com/ + https://www.vividsolutions.com/ Martin Davis mtnclimb@gmail.com Individual - http://tsusiatsoftware.net + https://tsusiatsoftware.net @@ -87,6 +93,10 @@ ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + jts + https://locationtech.github.io/jts + @@ -152,7 +162,6 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 true ossrh @@ -162,9 +171,7 @@ - org.apache.maven.plugins maven-gpg-plugin - 1.5 sign-artifacts @@ -179,18 +186,95 @@ + + + + + maven-assembly-plugin + 2.6 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-compiler-plugin + 3.8.1 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-jxr-plugin + 3.1.1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + + + + + maven-compiler-plugin + + true + + -Xlint:${lint} + + + + - org.apache.maven.plugins maven-surefire-plugin - 2.15 **/*Test.java @@ -203,17 +287,15 @@ - org.apache.maven.plugins maven-javadoc-plugin - 3.0.0-M1 + true public

      ${project.name} ${project.version}
      ${project.name} ${project.version}
      ${basedir}/modules/core/src/main/javadoc/overview.html org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false Core - Geometry @@ -236,6 +318,9 @@ org.locationtech.jts.io.* + ${doclint} + false + false @@ -247,9 +332,7 @@ - org.apache.maven.plugins maven-source-plugin - 2.2.1 attach-sources @@ -260,18 +343,151 @@ - org.apache.maven.plugins maven-release-plugin - 2.5.3 true + + maven-site-plugin + 3.9.1 + + + maven-pmd-plugin + + + + check + + + + + + + org.locationtech.jts + build-tools + ${project.version} + + + + net.sourceforge.pmd + pmd-core + ${pmd.version} + + + net.sourceforge.pmd + pmd-java + ${pmd.version} + + + net.sourceforge.pmd + pmd-javascript + ${pmd.version} + + + net.sourceforge.pmd + pmd-jsp + ${pmd.version} + + + + + jts/pmd-ruleset.xml + + 2 + false + true + + + + maven-checkstyle-plugin + + + + org.locationtech.jts + build-tools + ${project.version} + + + + com.puppycrawl.tools + checkstyle + ${checkstyle.version} + + + + true + jts/checkstyle.xml + jts/header.txt + jts/suppressions.xml + checkstyle.suppressions.file + + + + validate + validate + + check + + + + + + + + org.apache.maven.plugins + maven-site-plugin + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-jxr-plugin + + + + jxr + + + + + + org.apache.maven.plugins + maven-pmd-plugin + + + jts/pmd-ruleset.xml + + + + + + + build-tools modules diff --git a/review.txt b/review.txt new file mode 100644 index 0000000000..81a9ac3589 --- /dev/null +++ b/review.txt @@ -0,0 +1,6763 @@ +[INFO] Scanning for projects... +[INFO] ------------------------------------------------------------------------ +[INFO] Reactor Build Order: +[INFO] +[INFO] JTS Topology Suite Build Configuration [jar] +[INFO] JTS Topology Suite [pom] +[INFO] org.locationtech.jts:jts-modules [pom] +[INFO] org.locationtech.jts:jts-core [bundle] +[INFO] org.locationtech.jts:jts-io [pom] +[INFO] org.locationtech.jts.io:jts-io-common [jar] +[INFO] org.locationtech.jts:jts-example [jar] +[INFO] org.locationtech.jts:jts-tests [jar] +[INFO] org.locationtech.jts:jts-app [jar] +[INFO] org.locationtech.jts:jts-lab [jar] +[INFO] +[INFO] ----------------------< org.locationtech.jts:jts >---------------------- +[INFO] Building JTS Topology Suite 1.18.2-SNAPSHOT [1/10] +[INFO] --------------------------------[ pom ]--------------------------------- +[INFO] +[INFO] --- maven-help-plugin:3.2.0:effective-pom (default-cli) @ jts --- +[INFO] +Effective POMs, after inheritance, interpolation, and profiles are applied: + + + + + + + + + + + + + + + + + 4.0.0 + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + JTS Topology Suite Build Configuration + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/build-tools/src/main/java + /Users/jgarnett/java/jts/build-tools/src/main/scripts + /Users/jgarnett/java/jts/build-tools/src/test/java + /Users/jgarnett/java/jts/build-tools/target/classes + /Users/jgarnett/java/jts/build-tools/target/test-classes + + + /Users/jgarnett/java/jts/build-tools/src/main/resources + + + + + /Users/jgarnett/java/jts/build-tools/src/test/resources + + + /Users/jgarnett/java/jts/build-tools/target + build-tools-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.2-beta-5 + + + maven-dependency-plugin + 2.8 + + + maven-release-plugin + 2.5.3 + + + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 2.4 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-surefire-plugin + 2.12.4 + + + default-test + test + + test + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + + + maven-site-plugin + 3.3 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/build-tools/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/build-tools/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + /Users/jgarnett/java/jts/build-tools/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + + /Users/jgarnett/java/jts/build-tools/target/site + + + + + + + + + + 4.0.0 + org.locationtech.jts + jts + 1.18.2-SNAPSHOT + pom + JTS Topology Suite + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + build-tools + modules + + + scm:git::git@github.com:locationtech/jts.git + scm:git:git@github.com:locationtech/jts.git + https://github.com/locationtech/jts + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/src/main/java + /Users/jgarnett/java/jts/src/main/scripts + /Users/jgarnett/java/jts/src/test/java + /Users/jgarnett/java/jts/target/classes + /Users/jgarnett/java/jts/target/test-classes + + + /Users/jgarnett/java/jts/src/main/resources + + + + + /Users/jgarnett/java/jts/src/test/resources + + + /Users/jgarnett/java/jts/target + jts-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      JTS Topology Suite 1.18.2-SNAPSHOT
      +
      JTS Topology Suite 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      JTS Topology Suite 1.18.2-SNAPSHOT
      +
      JTS Topology Suite 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + release + + + release + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + pom + org.locationtech.jts:jts-modules + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + core + io + example + tests + app + lab + + + scm:git::git@github.com:locationtech/jts.git/jts-modules + scm:git:git@github.com:locationtech/jts.git/jts-modules + https://github.com/locationtech/jts/jts-modules + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/src/main/java + /Users/jgarnett/java/jts/modules/src/main/scripts + /Users/jgarnett/java/jts/modules/src/test/java + /Users/jgarnett/java/jts/modules/target/classes + /Users/jgarnett/java/jts/modules/target/test-classes + + + /Users/jgarnett/java/jts/modules/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/src/test/resources + + + /Users/jgarnett/java/jts/modules/target + jts-modules-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + all + + + !release + + + + example + tests + app + lab + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + bundle + org.locationtech.jts:jts-core + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-core + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-core + https://github.com/locationtech/jts/jts-modules/jts-core + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/core/src/main/java + /Users/jgarnett/java/jts/modules/core/src/main/scripts + /Users/jgarnett/java/jts/modules/core/src/test/java + /Users/jgarnett/java/jts/modules/core/target/classes + /Users/jgarnett/java/jts/modules/core/target/test-classes + + + /Users/jgarnett/java/jts/modules/core/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/core/src/test/resources + + + /Users/jgarnett/java/jts/modules/core/target + jts-core-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/core/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/core/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/core/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/core/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/core/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-jar-plugin + 3.0.2 + + + package + + test-jar + + + + + org.locationtech.jts + + + + org.locationtech.jts + + true + + + + + + + + + + + org.locationtech.jts + + + + org.locationtech.jts + + true + + + + + + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + true + + + default-bundle + package + + bundle + + + true + + org.locationtech.jts.jts-core + jts-core + 1.18.2-SNAPSHOT + org.locationtech.jts.* + + + + + default-install + install + + install + + + true + + org.locationtech.jts.jts-core + jts-core + 1.18.2-SNAPSHOT + org.locationtech.jts.* + + + + + default-deploy + deploy + + deploy + + + true + + org.locationtech.jts.jts-core + jts-core + 1.18.2-SNAPSHOT + org.locationtech.jts.* + + + + + + true + + org.locationtech.jts.jts-core + jts-core + 1.18.2-SNAPSHOT + org.locationtech.jts.* + + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-compiler-plugin + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + + + default-install + install + + install + + + + + + maven-deploy-plugin + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/core/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-io + 1.18.2-SNAPSHOT + pom + org.locationtech.jts:jts-io + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + common + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-io + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-io + https://github.com/locationtech/jts/jts-modules/jts-io + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/io/src/main/java + /Users/jgarnett/java/jts/modules/io/src/main/scripts + /Users/jgarnett/java/jts/modules/io/src/test/java + /Users/jgarnett/java/jts/modules/io/target/classes + /Users/jgarnett/java/jts/modules/io/target/test-classes + + + /Users/jgarnett/java/jts/modules/io/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/io/src/test/resources + + + /Users/jgarnett/java/jts/modules/io/target + jts-io-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/io/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/io/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/io/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/io/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/io/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/io/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + oracle + + + all + + + + ora + + + + arcsde + + + all + + + + sde + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-io + 1.18.2-SNAPSHOT + + org.locationtech.jts.io + jts-io-common + 1.18.2-SNAPSHOT + org.locationtech.jts.io:jts-io-common + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-io/jts-io-common + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-io/jts-io-common + https://github.com/locationtech/jts/jts-modules/jts-io/jts-io-common + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + com.googlecode.json-simple + json-simple + 1.1.1 + compile + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + test-jar + test + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/io/common/src/main/java + /Users/jgarnett/java/jts/modules/io/common/src/main/scripts + /Users/jgarnett/java/jts/modules/io/common/src/test/java + /Users/jgarnett/java/jts/modules/io/common/target/classes + /Users/jgarnett/java/jts/modules/io/common/target/test-classes + + + /Users/jgarnett/java/jts/modules/io/common/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/io/common/src/test/resources + + + /Users/jgarnett/java/jts/modules/io/common/target + jts-io-common-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      +
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/io/common/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      +
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/io/common/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/io/common/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/io/common/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/io/common/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-jar-plugin + 3.0.2 + + + default-jar + package + + jar + + + + + org.locationtech.jts.io + + + + org.locationtech.jts.io + + true + + + + + + + + + + + org.locationtech.jts.io + + + + org.locationtech.jts.io + + true + + + + + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/io/common/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-example + 1.18.2-SNAPSHOT + org.locationtech.jts:jts-example + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-example + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-example + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-example + https://github.com/locationtech/jts/jts-modules/jts-example + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/example/src/main/java + /Users/jgarnett/java/jts/modules/example/src/main/scripts + /Users/jgarnett/java/jts/modules/example/src/test/java + /Users/jgarnett/java/jts/modules/example/target/classes + /Users/jgarnett/java/jts/modules/example/target/test-classes + + + /Users/jgarnett/java/jts/modules/example/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/example/src/test/resources + + + /Users/jgarnett/java/jts/modules/example/target + jts-example-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/example/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/example/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/example/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/example/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/example/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.0.2 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/example/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-tests + 1.18.2-SNAPSHOT + org.locationtech.jts:jts-tests + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-tests + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-tests + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-tests + https://github.com/locationtech/jts/jts-modules/jts-tests + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + org.jdom + jdom2 + 2.0.6 + compile + + + org.apache.commons + commons-lang3 + 3.7 + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/tests/src/main/java + /Users/jgarnett/java/jts/modules/tests/src/main/scripts + /Users/jgarnett/java/jts/modules/tests/src/test/java + /Users/jgarnett/java/jts/modules/tests/target/classes + /Users/jgarnett/java/jts/modules/tests/target/test-classes + + + /Users/jgarnett/java/jts/modules/tests/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/tests/src/test/resources + + + /Users/jgarnett/java/jts/modules/tests/target + jts-tests-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/tests/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/tests/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/tests/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/tests/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/tests/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-assembly-plugin + 2.6 + + + make-assembly + package + + single + + + + jar-with-dependencies + + + + org.locationtech.jtstest.testrunner.JTSTestRunnerCmd + + + JTSTestRunner + false + + + + + + jar-with-dependencies + + + + org.locationtech.jtstest.testrunner.JTSTestRunnerCmd + + + JTSTestRunner + false + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.0.2 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/tests/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-app + 1.18.2-SNAPSHOT + org.locationtech.jts:jts-app + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-app + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-app + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-app + https://github.com/locationtech/jts/jts-modules/jts-app + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + org.locationtech.jts + jts-tests + 1.18.2-SNAPSHOT + compile + + + org.locationtech.jts.io + jts-io-common + 1.18.2-SNAPSHOT + compile + + + org.jdom + jdom2 + 2.0.6 + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/app/src/main/java + /Users/jgarnett/java/jts/modules/app/src/main/scripts + /Users/jgarnett/java/jts/modules/app/src/test/java + /Users/jgarnett/java/jts/modules/app/target/classes + /Users/jgarnett/java/jts/modules/app/target/test-classes + + + /Users/jgarnett/java/jts/modules/app/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/app/src/test/resources + + + /Users/jgarnett/java/jts/modules/app/target + jts-app-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/app/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/app/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/app/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/app/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/app/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-assembly-plugin + 2.6 + + + make-assembly + package + + single + + + + jar-with-dependencies + + + + org.locationtech.jtstest.testbuilder.JTSTestBuilder + + + JTSTestBuilder + false + + + + + + jar-with-dependencies + + + + org.locationtech.jtstest.testbuilder.JTSTestBuilder + + + JTSTestBuilder + false + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.0.2 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/app/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      + + + + + + + + 4.0.0 + + org.locationtech.jts + jts-modules + 1.18.2-SNAPSHOT + + org.locationtech.jts + jts-lab + 1.18.2-SNAPSHOT + org.locationtech.jts:jts-lab + The JTS Topology Suite is an API for 2D linear geometry predicates and functions. + https://www.locationtech.org/projects/technology.jts/jts-modules/jts-lab + + + Eclipse Public License, Version 2.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt + + + Eclipse Distribution License - v 1.0 + https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + + + + + Martin Davis + mbdavis@VividSolutions.com + Vivid Solutions Inc. + http://www.vividsolutions.com/ + + + Martin Davis + mtnclimb@gmail.com + Individual + http://tsusiatsoftware.net + + + + scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-lab + scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-lab + https://github.com/locationtech/jts/jts-modules/jts-lab + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + repo.eclipse.org + JTS Repository - Snapshots + https://repo.eclipse.org/content/repositories/jts-snapshots/ + + + + 2.0.6 + 1.1.1 + 1.2 + 4.13.1 + 1.8 + 1.8 + 6.36.0 + UTF-8 + 9.1 + + + + + junit + junit + 4.13.1 + + + org.jdom + jdom2 + 2.0.6 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.apache.commons + commons-lang3 + 3.7 + + + com.oracle + ojdbc7 + 11.1.0.7.0 + + + + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + compile + + + org.locationtech.jts + jts-core + 1.18.2-SNAPSHOT + test-jar + test + + + org.jdom + jdom2 + 2.0.6 + compile + + + junit + junit + 4.13.1 + test + + + + + + false + + central.maven.org + Central Maven repository + https://central.maven.org/maven2 + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + never + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /Users/jgarnett/java/jts/modules/lab/src/main/java + /Users/jgarnett/java/jts/modules/lab/src/main/scripts + /Users/jgarnett/java/jts/modules/lab/src/test/java + /Users/jgarnett/java/jts/modules/lab/target/classes + /Users/jgarnett/java/jts/modules/lab/target/test-classes + + + /Users/jgarnett/java/jts/modules/lab/src/main/resources + + + + + /Users/jgarnett/java/jts/modules/lab/src/test/resources + + + /Users/jgarnett/java/jts/modules/lab/target + jts-lab-1.18.2-SNAPSHOT + + + + maven-antrun-plugin + 1.3 + + + maven-assembly-plugin + 2.6 + + + maven-dependency-plugin + 2.8 + + + maven-checkstyle-plugin + 3.1.2 + + + maven-jar-plugin + 3.0.2 + + + maven-javadoc-plugin + 3.0.0-M1 + + + maven-pmd-plugin + 3.14.0 + + + maven-project-info-reports-plugin + 3.1.2 + + + maven-release-plugin + 2.5.3 + + + maven-site-plugin + 3.9.1 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.15 + + + org.apache.felix + maven-bundle-plugin + 3.3.0 + + + + + + maven-surefire-plugin + 2.15 + + + default-test + test + + test + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + + + **/*Test.java + + + **/*PerfTest.java + **/*StressTest.java + **/jts/perf/**/*.java + + + + + maven-javadoc-plugin + 3.0.0-M1 + + + attach-javadocs + + jar + + + public +
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/lab/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      +
      + + public +
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      +
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      + /Users/jgarnett/java/jts/modules/lab/modules/core/src/main/javadoc/overview.html + org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + false + + + Core - Geometry + org.locationtech.jts.geom:org.locationtech.jts.geom.* + + + Core - I/O + org.locationtech.jts.io + + + Core - Algorithms + org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate + + + Core - Other + org.locationtech.jts:org.locationtech.jts.* + + + I/O - Common + org.locationtech.jts.io.* + + + none +
      +
      + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + maven-release-plugin + 2.5.3 + + true + + + + maven-site-plugin + 3.9.1 + + + default-site + site + + site + + + /Users/jgarnett/java/jts/modules/lab/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + default-deploy + site-deploy + + deploy + + + /Users/jgarnett/java/jts/modules/lab/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + + /Users/jgarnett/java/jts/modules/lab/target/site + + + org.apache.maven.plugins + maven-site-plugin + 3.9.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + default + + index + licenses + dependency-info + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + + + + maven-pmd-plugin + 3.14.0 + + + + check + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + net.sourceforge.pmd + pmd-core + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-java + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-javascript + 6.36.0 + compile + + + net.sourceforge.pmd + pmd-jsp + 6.36.0 + compile + + + + + jts/pmd-ruleset.xml + + 1 + false + false + + + + maven-checkstyle-plugin + 3.1.2 + + + org.locationtech.jts + build-tools + 1.18.2-SNAPSHOT + compile + + + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-clean-plugin + 2.5 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 2.6 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.0.2 + + + default-jar + package + + jar + + + + + + maven-compiler-plugin + 3.1 + + + default-compile + compile + + compile + + + + default-testCompile + test-compile + + testCompile + + + + + + maven-install-plugin + 2.4 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 2.7 + + + default-deploy + deploy + + deploy + + + + +
      +
      + + /Users/jgarnett/java/jts/modules/lab/target/site + + + maven-site-plugin + 3.9.1 + + + maven-project-info-reports-plugin + 3.1.2 + + + + index + licenses + dependency-info + + + + + + maven-checkstyle-plugin + + true + jts/checkstyle.xml + jts/header.txt + + + + maven-pmd-plugin + + true + + jts/pmd-ruleset.xml + + + + + +
      +
      + + +[INFO] ------------------------------------------------------------------------ +[INFO] Reactor Summary for JTS Topology Suite 1.18.2-SNAPSHOT: +[INFO] +[INFO] JTS Topology Suite Build Configuration ............. SKIPPED +[INFO] JTS Topology Suite ................................. SUCCESS [ 1.600 s] +[INFO] org.locationtech.jts:jts-modules ................... SKIPPED +[INFO] org.locationtech.jts:jts-core ...................... SKIPPED +[INFO] org.locationtech.jts:jts-io ........................ SKIPPED +[INFO] org.locationtech.jts.io:jts-io-common .............. SKIPPED +[INFO] org.locationtech.jts:jts-example ................... SKIPPED +[INFO] org.locationtech.jts:jts-tests ..................... SKIPPED +[INFO] org.locationtech.jts:jts-app ....................... SKIPPED +[INFO] org.locationtech.jts:jts-lab ....................... SKIPPED +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 2.451 s +[INFO] Finished at: 2021-07-29T17:09:31-07:00 +[INFO] ------------------------------------------------------------------------ From 8adff26af9f1c6224e0c6f9fcc1505f153d3abbf Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 20 Aug 2021 13:34:35 -0700 Subject: [PATCH 060/275] Update DEVELOPING.md --- DEVELOPING.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index 194c158b19..49b6be425f 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -72,6 +72,13 @@ express fundamental geometric semantics of the JTS library. The XML test format can be executed using the JTS TestRunner, or imported into the JTS TestBuilder. +### External QA tools + +* LGTM CodeQL analysis: + * [Alerts report](https://lgtm.com/projects/g/locationtech/jts/alerts/?mode=tree) + * [![Total alerts](https://img.shields.io/lgtm/alerts/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/alerts/) + * [![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/context:java) + ## Javadoc * Build Javadoc for core modules @@ -158,4 +165,4 @@ Run Configurations: Program arguments (optional) | `-geomfunc ...` VM args | `-Xmx1000M` VM args (optional, for Mac) | `-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel` - Working directory | Default \ No newline at end of file + Working directory | Default From 97e39e417e70d92bf5b539c7450ca0ce8e9c8b96 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 20 Aug 2021 16:34:23 -0700 Subject: [PATCH 061/275] Fix Javadoc Signed-off-by: Martin Davis --- .../jts/operation/valid/IndexedNestedPolygonTester.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java index 5501a57ebb..cce97b212a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java @@ -139,8 +139,8 @@ private Coordinate findNestedPoint(LinearRing shell, * The shell is assume to touch the polyon only at shell vertices, * and does not cross the polygon. * - * @param the shell to test - * @param the polygon to test against + * @param shell the shell to test + * @param poly the polygon to test against * @return an interior segment point, or null if the shell is nested correctly */ private static Coordinate findSegmentInPolygon(LinearRing shell, Polygon poly) From 380f8ffa13a9fa660df22d5126dbad2143d8949b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 20 Aug 2021 16:38:39 -0700 Subject: [PATCH 062/275] Remove unused declaration Signed-off-by: Martin Davis --- .../jts/shape/fractal/SierpinskiCarpetBuilder.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/fractal/SierpinskiCarpetBuilder.java b/modules/core/src/main/java/org/locationtech/jts/shape/fractal/SierpinskiCarpetBuilder.java index aae9e99454..b6d807221d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/fractal/SierpinskiCarpetBuilder.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/fractal/SierpinskiCarpetBuilder.java @@ -16,7 +16,6 @@ import java.util.List; import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.CoordinateList; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineSegment; @@ -27,9 +26,7 @@ public class SierpinskiCarpetBuilder extends GeometricShapeBuilder -{ - private CoordinateList coordList = new CoordinateList(); - +{ public SierpinskiCarpetBuilder(GeometryFactory geomFactory) { super(geomFactory); From f3015061371e1543bb1b7f72e1bc99a8926864cc Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 20 Aug 2021 16:44:45 -0700 Subject: [PATCH 063/275] Add if stmt braces Signed-off-by: Martin Davis --- .../core/src/main/java/org/locationtech/jts/io/WKBWriter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java b/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java index 58dfa30add..5fe9e840f6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKBWriter.java @@ -443,8 +443,9 @@ private void writeCoordinate(CoordinateSequence seq, int index, OutStream os) if (outputDimension >= 3) { // if 3rd dim is requested, only write it if the CoordinateSequence provides it double ordVal = Coordinate.NULL_ORDINATE; - if (seq.getDimension() >= 3) + if (seq.getDimension() >= 3) { ordVal = seq.getOrdinate(index, 2); + } ByteOrderValues.putDouble(ordVal, buf, byteOrder); os.write(buf, 8); } From 8d0ac16f412d9a6d4324ef21280646d0aeaa8f7b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 24 Aug 2021 12:44:11 -0700 Subject: [PATCH 064/275] Add missing hashcode functions (#767) Signed-off-by: Martin Davis --- .../geomfunction/BaseGeometryFunction.java | 16 +++++++++++++ .../locationtech/jts/geom/PrecisionModel.java | 15 ++++++++++++ .../jts/geom/util/AffineTransformation.java | 23 +++++++++++++++++++ .../org/locationtech/jts/geomgraph/Edge.java | 21 +++++++++++++++++ .../jts/index/strtree/Interval.java | 16 +++++++++++++ 5 files changed, 91 insertions(+) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java index d4c08076b7..f9055816ce 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/BaseGeometryFunction.java @@ -12,6 +12,8 @@ package org.locationtech.jtstest.geomfunction; +import java.util.Arrays; + import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.util.*; @@ -177,6 +179,20 @@ public boolean equals(Object obj) return true; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + Arrays.hashCode(parameterNames); + result = prime * result + Arrays.hashCode(parameterTypes); + result = prime * result + ((returnType == null) ? 0 : returnType.hashCode()); + return result; + } + public int compareTo(Object o) { GeometryFunction func = (GeometryFunction) o; diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java index 03ef2bbe13..534aac4be3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java @@ -426,6 +426,21 @@ public boolean equals(Object other) { return modelType == otherPrecisionModel.modelType && scale == otherPrecisionModel.scale; } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((modelType == null) ? 0 : modelType.hashCode()); + long temp; + temp = Double.doubleToLongBits(scale); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + /** * Compares this {@link PrecisionModel} object with the specified object for order. * A PrecisionModel is greater than another if it provides greater precision. diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java index ee9b079f00..2f7b0c699a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/AffineTransformation.java @@ -1063,6 +1063,29 @@ public boolean equals(Object obj) && m12 == trans.m12; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(m00); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m01); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m02); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m10); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m11); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(m12); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + /** * Gets a text representation of this transformation. * The string is of the form: diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java index 1ad1c1a409..8d5fd3a1a8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/Edge.java @@ -231,6 +231,27 @@ public boolean equals(Object o) return true; } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + pts.length; + if (pts.length > 0) { + Coordinate p0 = pts[0]; + Coordinate p1 = pts[pts.length - 1]; + if (1 == p0.compareTo(p1)) { + p0 = pts[pts.length - 1]; + p1 = pts[0]; + } + result = prime * result + p0.hashCode(); + result = prime * result + p1.hashCode(); + } + return result; + } + /** * Check if coordinate sequences of the Edges are identical. * diff --git a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java index 0d1d683f5b..01dc2d45b2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/strtree/Interval.java @@ -48,9 +48,25 @@ public Interval expandToInclude(Interval other) { public boolean intersects(Interval other) { return !(other.min > max || other.max < min); } + public boolean equals(Object o) { if (! (o instanceof Interval)) { return false; } Interval other = (Interval) o; return min == other.min && max == other.max; } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(max); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(min); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } } From 34a3c6747cf74e1a95398ab4f1c2c46685d4fda4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 24 Aug 2021 17:59:50 -0700 Subject: [PATCH 065/275] Add index check to PackedCoordinateSequence expandEnvelope function (#769) Signed-off-by: Martin Davis --- .../jts/geom/impl/PackedCoordinateSequence.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java b/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java index 3c705a7899..ed23960e3b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/impl/PackedCoordinateSequence.java @@ -414,7 +414,10 @@ public void setOrdinate(int index, int ordinate, double value) { public Envelope expandEnvelope(Envelope env) { for (int i = 0; i < coords.length; i += dimension ) { - env.expandToInclude(coords[i], coords[i + 1]); + // added to make static code analysis happy + if (i + 1 < coords.length) { + env.expandToInclude(coords[i], coords[i + 1]); + } } return env; } @@ -591,7 +594,10 @@ public void setOrdinate(int index, int ordinate, double value) { public Envelope expandEnvelope(Envelope env) { for (int i = 0; i < coords.length; i += dimension ) { - env.expandToInclude(coords[i], coords[i + 1]); + // added to make static code analysis happy + if (i + 1 < coords.length) { + env.expandToInclude(coords[i], coords[i + 1]); + } } return env; } From b6a2bbd2d4dba63ab08ab23794c1c578737a59b7 Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Thu, 26 Aug 2021 17:56:57 -0700 Subject: [PATCH 066/275] Skip deploy of build-tools as it is not needed (#771) Signed-off-by: Jody Garnett --- build-tools/pom.xml | 7 +++++++ pom.xml | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build-tools/pom.xml b/build-tools/pom.xml index c9e283017d..8ae6103402 100644 --- a/build-tools/pom.xml +++ b/build-tools/pom.xml @@ -42,6 +42,13 @@ true + + maven-deploy-plugin + 3.0.0-M1 + + true + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b54ed1814c..79617dbbb2 100644 --- a/pom.xml +++ b/pom.xml @@ -208,7 +208,10 @@ 3.8.1 - org.apache.maven.plugins + maven-deploy-plugin + 3.0.0-M1 + + maven-gpg-plugin 1.5 @@ -221,7 +224,6 @@ 3.3.0 - org.apache.maven.plugins maven-jxr-plugin 3.1.1 From e5dccdb5962929d2b25cbc16db9cea55214afc92 Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Thu, 26 Aug 2021 17:20:37 -0700 Subject: [PATCH 067/275] Remove effective pom summary used for troubleshooting Signed-off-by: Jody Garnett --- review.txt | 6763 ---------------------------------------------------- 1 file changed, 6763 deletions(-) delete mode 100644 review.txt diff --git a/review.txt b/review.txt deleted file mode 100644 index 81a9ac3589..0000000000 --- a/review.txt +++ /dev/null @@ -1,6763 +0,0 @@ -[INFO] Scanning for projects... -[INFO] ------------------------------------------------------------------------ -[INFO] Reactor Build Order: -[INFO] -[INFO] JTS Topology Suite Build Configuration [jar] -[INFO] JTS Topology Suite [pom] -[INFO] org.locationtech.jts:jts-modules [pom] -[INFO] org.locationtech.jts:jts-core [bundle] -[INFO] org.locationtech.jts:jts-io [pom] -[INFO] org.locationtech.jts.io:jts-io-common [jar] -[INFO] org.locationtech.jts:jts-example [jar] -[INFO] org.locationtech.jts:jts-tests [jar] -[INFO] org.locationtech.jts:jts-app [jar] -[INFO] org.locationtech.jts:jts-lab [jar] -[INFO] -[INFO] ----------------------< org.locationtech.jts:jts >---------------------- -[INFO] Building JTS Topology Suite 1.18.2-SNAPSHOT [1/10] -[INFO] --------------------------------[ pom ]--------------------------------- -[INFO] -[INFO] --- maven-help-plugin:3.2.0:effective-pom (default-cli) @ jts --- -[INFO] -Effective POMs, after inheritance, interpolation, and profiles are applied: - - - - - - - - - - - - - - - - - 4.0.0 - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - JTS Topology Suite Build Configuration - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/build-tools/src/main/java - /Users/jgarnett/java/jts/build-tools/src/main/scripts - /Users/jgarnett/java/jts/build-tools/src/test/java - /Users/jgarnett/java/jts/build-tools/target/classes - /Users/jgarnett/java/jts/build-tools/target/test-classes - - - /Users/jgarnett/java/jts/build-tools/src/main/resources - - - - - /Users/jgarnett/java/jts/build-tools/src/test/resources - - - /Users/jgarnett/java/jts/build-tools/target - build-tools-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.2-beta-5 - - - maven-dependency-plugin - 2.8 - - - maven-release-plugin - 2.5.3 - - - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-jar-plugin - 2.4 - - - default-jar - package - - jar - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-surefire-plugin - 2.12.4 - - - default-test - test - - test - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - - - maven-site-plugin - 3.3 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/build-tools/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/build-tools/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - - /Users/jgarnett/java/jts/build-tools/target/site - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - - - - - /Users/jgarnett/java/jts/build-tools/target/site - - - - - - - - - - 4.0.0 - org.locationtech.jts - jts - 1.18.2-SNAPSHOT - pom - JTS Topology Suite - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - build-tools - modules - - - scm:git::git@github.com:locationtech/jts.git - scm:git:git@github.com:locationtech/jts.git - https://github.com/locationtech/jts - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/src/main/java - /Users/jgarnett/java/jts/src/main/scripts - /Users/jgarnett/java/jts/src/test/java - /Users/jgarnett/java/jts/target/classes - /Users/jgarnett/java/jts/target/test-classes - - - /Users/jgarnett/java/jts/src/main/resources - - - - - /Users/jgarnett/java/jts/src/test/resources - - - /Users/jgarnett/java/jts/target - jts-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      JTS Topology Suite 1.18.2-SNAPSHOT
      -
      JTS Topology Suite 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      JTS Topology Suite 1.18.2-SNAPSHOT
      -
      JTS Topology Suite 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - release - - - release - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - pom - org.locationtech.jts:jts-modules - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - core - io - example - tests - app - lab - - - scm:git::git@github.com:locationtech/jts.git/jts-modules - scm:git:git@github.com:locationtech/jts.git/jts-modules - https://github.com/locationtech/jts/jts-modules - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/src/main/java - /Users/jgarnett/java/jts/modules/src/main/scripts - /Users/jgarnett/java/jts/modules/src/test/java - /Users/jgarnett/java/jts/modules/target/classes - /Users/jgarnett/java/jts/modules/target/test-classes - - - /Users/jgarnett/java/jts/modules/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/src/test/resources - - - /Users/jgarnett/java/jts/modules/target - jts-modules-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-modules 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - all - - - !release - - - - example - tests - app - lab - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - bundle - org.locationtech.jts:jts-core - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-core - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-core - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-core - https://github.com/locationtech/jts/jts-modules/jts-core - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/core/src/main/java - /Users/jgarnett/java/jts/modules/core/src/main/scripts - /Users/jgarnett/java/jts/modules/core/src/test/java - /Users/jgarnett/java/jts/modules/core/target/classes - /Users/jgarnett/java/jts/modules/core/target/test-classes - - - /Users/jgarnett/java/jts/modules/core/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/core/src/test/resources - - - /Users/jgarnett/java/jts/modules/core/target - jts-core-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/core/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-core 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/core/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/core/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/core/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/core/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-jar-plugin - 3.0.2 - - - package - - test-jar - - - - - org.locationtech.jts - - - - org.locationtech.jts - - true - - - - - - - - - - - org.locationtech.jts - - - - org.locationtech.jts - - true - - - - - - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - true - - - default-bundle - package - - bundle - - - true - - org.locationtech.jts.jts-core - jts-core - 1.18.2-SNAPSHOT - org.locationtech.jts.* - - - - - default-install - install - - install - - - true - - org.locationtech.jts.jts-core - jts-core - 1.18.2-SNAPSHOT - org.locationtech.jts.* - - - - - default-deploy - deploy - - deploy - - - true - - org.locationtech.jts.jts-core - jts-core - 1.18.2-SNAPSHOT - org.locationtech.jts.* - - - - - - true - - org.locationtech.jts.jts-core - jts-core - 1.18.2-SNAPSHOT - org.locationtech.jts.* - - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-compiler-plugin - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - - - default-install - install - - install - - - - - - maven-deploy-plugin - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/core/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-io - 1.18.2-SNAPSHOT - pom - org.locationtech.jts:jts-io - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - common - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-io - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-io - https://github.com/locationtech/jts/jts-modules/jts-io - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/io/src/main/java - /Users/jgarnett/java/jts/modules/io/src/main/scripts - /Users/jgarnett/java/jts/modules/io/src/test/java - /Users/jgarnett/java/jts/modules/io/target/classes - /Users/jgarnett/java/jts/modules/io/target/test-classes - - - /Users/jgarnett/java/jts/modules/io/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/io/src/test/resources - - - /Users/jgarnett/java/jts/modules/io/target - jts-io-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/io/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-io 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/io/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/io/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/io/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/io/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/io/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - oracle - - - all - - - - ora - - - - arcsde - - - all - - - - sde - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-io - 1.18.2-SNAPSHOT - - org.locationtech.jts.io - jts-io-common - 1.18.2-SNAPSHOT - org.locationtech.jts.io:jts-io-common - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-io/jts-io-common - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-io/jts-io-common - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-io/jts-io-common - https://github.com/locationtech/jts/jts-modules/jts-io/jts-io-common - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - com.googlecode.json-simple - json-simple - 1.1.1 - compile - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - test-jar - test - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/io/common/src/main/java - /Users/jgarnett/java/jts/modules/io/common/src/main/scripts - /Users/jgarnett/java/jts/modules/io/common/src/test/java - /Users/jgarnett/java/jts/modules/io/common/target/classes - /Users/jgarnett/java/jts/modules/io/common/target/test-classes - - - /Users/jgarnett/java/jts/modules/io/common/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/io/common/src/test/resources - - - /Users/jgarnett/java/jts/modules/io/common/target - jts-io-common-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      -
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/io/common/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      -
      org.locationtech.jts.io:jts-io-common 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/io/common/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/io/common/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/io/common/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/io/common/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-jar-plugin - 3.0.2 - - - default-jar - package - - jar - - - - - org.locationtech.jts.io - - - - org.locationtech.jts.io - - true - - - - - - - - - - - org.locationtech.jts.io - - - - org.locationtech.jts.io - - true - - - - - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/io/common/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-example - 1.18.2-SNAPSHOT - org.locationtech.jts:jts-example - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-example - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-example - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-example - https://github.com/locationtech/jts/jts-modules/jts-example - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/example/src/main/java - /Users/jgarnett/java/jts/modules/example/src/main/scripts - /Users/jgarnett/java/jts/modules/example/src/test/java - /Users/jgarnett/java/jts/modules/example/target/classes - /Users/jgarnett/java/jts/modules/example/target/test-classes - - - /Users/jgarnett/java/jts/modules/example/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/example/src/test/resources - - - /Users/jgarnett/java/jts/modules/example/target - jts-example-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/example/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-example 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/example/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/example/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/example/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/example/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-jar-plugin - 3.0.2 - - - default-jar - package - - jar - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/example/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-tests - 1.18.2-SNAPSHOT - org.locationtech.jts:jts-tests - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-tests - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-tests - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-tests - https://github.com/locationtech/jts/jts-modules/jts-tests - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - org.jdom - jdom2 - 2.0.6 - compile - - - org.apache.commons - commons-lang3 - 3.7 - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/tests/src/main/java - /Users/jgarnett/java/jts/modules/tests/src/main/scripts - /Users/jgarnett/java/jts/modules/tests/src/test/java - /Users/jgarnett/java/jts/modules/tests/target/classes - /Users/jgarnett/java/jts/modules/tests/target/test-classes - - - /Users/jgarnett/java/jts/modules/tests/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/tests/src/test/resources - - - /Users/jgarnett/java/jts/modules/tests/target - jts-tests-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/tests/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-tests 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/tests/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/tests/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/tests/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/tests/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-assembly-plugin - 2.6 - - - make-assembly - package - - single - - - - jar-with-dependencies - - - - org.locationtech.jtstest.testrunner.JTSTestRunnerCmd - - - JTSTestRunner - false - - - - - - jar-with-dependencies - - - - org.locationtech.jtstest.testrunner.JTSTestRunnerCmd - - - JTSTestRunner - false - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-jar-plugin - 3.0.2 - - - default-jar - package - - jar - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/tests/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-app - 1.18.2-SNAPSHOT - org.locationtech.jts:jts-app - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-app - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-app - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-app - https://github.com/locationtech/jts/jts-modules/jts-app - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - org.locationtech.jts - jts-tests - 1.18.2-SNAPSHOT - compile - - - org.locationtech.jts.io - jts-io-common - 1.18.2-SNAPSHOT - compile - - - org.jdom - jdom2 - 2.0.6 - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/app/src/main/java - /Users/jgarnett/java/jts/modules/app/src/main/scripts - /Users/jgarnett/java/jts/modules/app/src/test/java - /Users/jgarnett/java/jts/modules/app/target/classes - /Users/jgarnett/java/jts/modules/app/target/test-classes - - - /Users/jgarnett/java/jts/modules/app/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/app/src/test/resources - - - /Users/jgarnett/java/jts/modules/app/target - jts-app-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/app/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-app 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/app/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/app/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/app/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/app/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-assembly-plugin - 2.6 - - - make-assembly - package - - single - - - - jar-with-dependencies - - - - org.locationtech.jtstest.testbuilder.JTSTestBuilder - - - JTSTestBuilder - false - - - - - - jar-with-dependencies - - - - org.locationtech.jtstest.testbuilder.JTSTestBuilder - - - JTSTestBuilder - false - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-jar-plugin - 3.0.2 - - - default-jar - package - - jar - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/app/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      - - - - - - - - 4.0.0 - - org.locationtech.jts - jts-modules - 1.18.2-SNAPSHOT - - org.locationtech.jts - jts-lab - 1.18.2-SNAPSHOT - org.locationtech.jts:jts-lab - The JTS Topology Suite is an API for 2D linear geometry predicates and functions. - https://www.locationtech.org/projects/technology.jts/jts-modules/jts-lab - - - Eclipse Public License, Version 2.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt - - - Eclipse Distribution License - v 1.0 - https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt - - - - - Martin Davis - mbdavis@VividSolutions.com - Vivid Solutions Inc. - http://www.vividsolutions.com/ - - - Martin Davis - mtnclimb@gmail.com - Individual - http://tsusiatsoftware.net - - - - scm:git::git@github.com:locationtech/jts.git/jts-modules/jts-lab - scm:git:git@github.com:locationtech/jts.git/jts-modules/jts-lab - https://github.com/locationtech/jts/jts-modules/jts-lab - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - repo.eclipse.org - JTS Repository - Snapshots - https://repo.eclipse.org/content/repositories/jts-snapshots/ - - - - 2.0.6 - 1.1.1 - 1.2 - 4.13.1 - 1.8 - 1.8 - 6.36.0 - UTF-8 - 9.1 - - - - - junit - junit - 4.13.1 - - - org.jdom - jdom2 - 2.0.6 - - - com.googlecode.json-simple - json-simple - 1.1.1 - - - org.apache.commons - commons-lang3 - 3.7 - - - com.oracle - ojdbc7 - 11.1.0.7.0 - - - - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - compile - - - org.locationtech.jts - jts-core - 1.18.2-SNAPSHOT - test-jar - test - - - org.jdom - jdom2 - 2.0.6 - compile - - - junit - junit - 4.13.1 - test - - - - - - false - - central.maven.org - Central Maven repository - https://central.maven.org/maven2 - - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - - - never - - - false - - central - Central Repository - https://repo.maven.apache.org/maven2 - - - - /Users/jgarnett/java/jts/modules/lab/src/main/java - /Users/jgarnett/java/jts/modules/lab/src/main/scripts - /Users/jgarnett/java/jts/modules/lab/src/test/java - /Users/jgarnett/java/jts/modules/lab/target/classes - /Users/jgarnett/java/jts/modules/lab/target/test-classes - - - /Users/jgarnett/java/jts/modules/lab/src/main/resources - - - - - /Users/jgarnett/java/jts/modules/lab/src/test/resources - - - /Users/jgarnett/java/jts/modules/lab/target - jts-lab-1.18.2-SNAPSHOT - - - - maven-antrun-plugin - 1.3 - - - maven-assembly-plugin - 2.6 - - - maven-dependency-plugin - 2.8 - - - maven-checkstyle-plugin - 3.1.2 - - - maven-jar-plugin - 3.0.2 - - - maven-javadoc-plugin - 3.0.0-M1 - - - maven-pmd-plugin - 3.14.0 - - - maven-project-info-reports-plugin - 3.1.2 - - - maven-release-plugin - 2.5.3 - - - maven-site-plugin - 3.9.1 - - - maven-source-plugin - 2.2.1 - - - maven-surefire-plugin - 2.15 - - - org.apache.felix - maven-bundle-plugin - 3.3.0 - - - - - - maven-surefire-plugin - 2.15 - - - default-test - test - - test - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - - - **/*Test.java - - - **/*PerfTest.java - **/*StressTest.java - **/jts/perf/**/*.java - - - - - maven-javadoc-plugin - 3.0.0-M1 - - - attach-javadocs - - jar - - - public -
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/lab/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      -
      - - public -
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      -
      org.locationtech.jts:jts-lab 1.18.2-SNAPSHOT
      - /Users/jgarnett/java/jts/modules/lab/modules/core/src/main/javadoc/overview.html - org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* - false - - - Core - Geometry - org.locationtech.jts.geom:org.locationtech.jts.geom.* - - - Core - I/O - org.locationtech.jts.io - - - Core - Algorithms - org.locationtech.jts.algorithm:org.locationtech.jts.algorithm.*:org.locationtech.jts.densify:org.locationtech.jts.dissolve:org.locationtech.jts.linearref:org.locationtech.jts.operation.*:org.locationtech.jts.simplify:org.locationtech.jts.triangulate - - - Core - Other - org.locationtech.jts:org.locationtech.jts.* - - - I/O - Common - org.locationtech.jts.io.* - - - none -
      -
      - - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - maven-release-plugin - 2.5.3 - - true - - - - maven-site-plugin - 3.9.1 - - - default-site - site - - site - - - /Users/jgarnett/java/jts/modules/lab/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - default-deploy - site-deploy - - deploy - - - /Users/jgarnett/java/jts/modules/lab/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - - /Users/jgarnett/java/jts/modules/lab/target/site - - - org.apache.maven.plugins - maven-site-plugin - 3.9.1 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 3.1.2 - - - default - - index - licenses - dependency-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - org.apache.maven.plugins - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - - - - maven-pmd-plugin - 3.14.0 - - - - check - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - net.sourceforge.pmd - pmd-core - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-java - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-javascript - 6.36.0 - compile - - - net.sourceforge.pmd - pmd-jsp - 6.36.0 - compile - - - - - jts/pmd-ruleset.xml - - 1 - false - false - - - - maven-checkstyle-plugin - 3.1.2 - - - org.locationtech.jts - build-tools - 1.18.2-SNAPSHOT - compile - - - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-clean-plugin - 2.5 - - - default-clean - clean - - clean - - - - - - maven-resources-plugin - 2.6 - - - default-testResources - process-test-resources - - testResources - - - - default-resources - process-resources - - resources - - - - - - maven-jar-plugin - 3.0.2 - - - default-jar - package - - jar - - - - - - maven-compiler-plugin - 3.1 - - - default-compile - compile - - compile - - - - default-testCompile - test-compile - - testCompile - - - - - - maven-install-plugin - 2.4 - - - default-install - install - - install - - - - - - maven-deploy-plugin - 2.7 - - - default-deploy - deploy - - deploy - - - - -
      -
      - - /Users/jgarnett/java/jts/modules/lab/target/site - - - maven-site-plugin - 3.9.1 - - - maven-project-info-reports-plugin - 3.1.2 - - - - index - licenses - dependency-info - - - - - - maven-checkstyle-plugin - - true - jts/checkstyle.xml - jts/header.txt - - - - maven-pmd-plugin - - true - - jts/pmd-ruleset.xml - - - - - -
      -
      - - -[INFO] ------------------------------------------------------------------------ -[INFO] Reactor Summary for JTS Topology Suite 1.18.2-SNAPSHOT: -[INFO] -[INFO] JTS Topology Suite Build Configuration ............. SKIPPED -[INFO] JTS Topology Suite ................................. SUCCESS [ 1.600 s] -[INFO] org.locationtech.jts:jts-modules ................... SKIPPED -[INFO] org.locationtech.jts:jts-core ...................... SKIPPED -[INFO] org.locationtech.jts:jts-io ........................ SKIPPED -[INFO] org.locationtech.jts.io:jts-io-common .............. SKIPPED -[INFO] org.locationtech.jts:jts-example ................... SKIPPED -[INFO] org.locationtech.jts:jts-tests ..................... SKIPPED -[INFO] org.locationtech.jts:jts-app ....................... SKIPPED -[INFO] org.locationtech.jts:jts-lab ....................... SKIPPED -[INFO] ------------------------------------------------------------------------ -[INFO] BUILD SUCCESS -[INFO] ------------------------------------------------------------------------ -[INFO] Total time: 2.451 s -[INFO] Finished at: 2021-07-29T17:09:31-07:00 -[INFO] ------------------------------------------------------------------------ From 510bcd7ec9a8a78b22f8cc0ab98500711bf97f0b Mon Sep 17 00:00:00 2001 From: Jody Garnett Date: Thu, 26 Aug 2021 17:20:59 -0700 Subject: [PATCH 068/275] Upgrade maven assembly plugin to 3.3.0 for Java 17 compatibility Signed-off-by: Jody Garnett --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79617dbbb2..4ddbcf4ae1 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ maven-assembly-plugin - 2.6 + 3.3.0 maven-checkstyle-plugin From fa234e14eacfcfd14c2daca218e97901477d0d7d Mon Sep 17 00:00:00 2001 From: Jim Hughes Date: Fri, 27 Aug 2021 14:42:34 -0400 Subject: [PATCH 069/275] Setting the 1.18.2 release version. Signed-off-by: Jim Hughes --- doc/JTS_Version_History.md | 2 +- modules/core/src/main/java/org/locationtech/jts/JTSVersion.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index b97a78d183..bdd058eecf 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -19,7 +19,7 @@ Distributions for older JTS versions can be obtained at the # Version 1.18.2 -*Release Date: TBD* +*Release Date: 08/27/2021* ### API Changes diff --git a/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java b/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java index 661a641674..c5bd666806 100644 --- a/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java +++ b/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java @@ -46,7 +46,7 @@ public class JTSVersion { /** * An optional string providing further release info (such as "alpha 1"); */ - private static final String RELEASE_INFO = "SNAPSHOT"; + private static final String RELEASE_INFO = ""; /** * Prints the current JTS version to stdout. From f36d492e62a941224c41278aa5b8cee9c5124e10 Mon Sep 17 00:00:00 2001 From: Jim Hughes Date: Fri, 27 Aug 2021 14:43:42 -0400 Subject: [PATCH 070/275] [maven-release-plugin] prepare release jts-1.18.2 --- build-tools/pom.xml | 2 +- modules/app/pom.xml | 2 +- modules/core/pom.xml | 2 +- modules/example/pom.xml | 2 +- modules/io/common/pom.xml | 2 +- modules/io/pom.xml | 2 +- modules/lab/pom.xml | 2 +- modules/pom.xml | 2 +- modules/tests/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-tools/pom.xml b/build-tools/pom.xml index 8ae6103402..7a187dcab1 100644 --- a/build-tools/pom.xml +++ b/build-tools/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.locationtech.jts build-tools - 1.18.2-SNAPSHOT + 1.18.2 JTS Topology Suite Build Configuration diff --git a/modules/app/pom.xml b/modules/app/pom.xml index 5bcc7b5e34..09fba7d9f0 100644 --- a/modules/app/pom.xml +++ b/modules/app/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-app ${project.groupId}:${project.artifactId} diff --git a/modules/core/pom.xml b/modules/core/pom.xml index ceb3e69dbb..2b9e330208 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-core ${project.groupId}:${project.artifactId} diff --git a/modules/example/pom.xml b/modules/example/pom.xml index 598dfa3861..76adea9efe 100644 --- a/modules/example/pom.xml +++ b/modules/example/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-example ${project.groupId}:${project.artifactId} diff --git a/modules/io/common/pom.xml b/modules/io/common/pom.xml index a6e981320b..c06d58c08c 100644 --- a/modules/io/common/pom.xml +++ b/modules/io/common/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-io - 1.18.2-SNAPSHOT + 1.18.2 org.locationtech.jts.io jts-io-common diff --git a/modules/io/pom.xml b/modules/io/pom.xml index 937875671a..d35eeefaee 100644 --- a/modules/io/pom.xml +++ b/modules/io/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-io ${project.groupId}:${project.artifactId} diff --git a/modules/lab/pom.xml b/modules/lab/pom.xml index 0e696de94b..dabf05d64f 100644 --- a/modules/lab/pom.xml +++ b/modules/lab/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-lab ${project.groupId}:${project.artifactId} diff --git a/modules/pom.xml b/modules/pom.xml index 20a7edb392..dc36e117e7 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts - 1.18.2-SNAPSHOT + 1.18.2 jts-modules ${project.groupId}:${project.artifactId} diff --git a/modules/tests/pom.xml b/modules/tests/pom.xml index e5a309ed09..93b4477b6f 100644 --- a/modules/tests/pom.xml +++ b/modules/tests/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2-SNAPSHOT + 1.18.2 jts-tests ${project.groupId}:${project.artifactId} diff --git a/pom.xml b/pom.xml index 4ddbcf4ae1..593efff554 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.locationtech.jts jts - 1.18.2-SNAPSHOT + 1.18.2 pom JTS Topology Suite @@ -80,7 +80,7 @@ scm:git::git@github.com:locationtech/jts.git scm:git:git@github.com:locationtech/jts.git https://github.com/locationtech/jts - HEAD + jts-1.18.2 From 6947198e36e2a2c71c2b6e5d48fdc421fc8b6308 Mon Sep 17 00:00:00 2001 From: Jim Hughes Date: Fri, 27 Aug 2021 14:43:44 -0400 Subject: [PATCH 071/275] [maven-release-plugin] prepare for next development iteration --- build-tools/pom.xml | 2 +- modules/app/pom.xml | 2 +- modules/core/pom.xml | 2 +- modules/example/pom.xml | 2 +- modules/io/common/pom.xml | 2 +- modules/io/pom.xml | 2 +- modules/lab/pom.xml | 2 +- modules/pom.xml | 2 +- modules/tests/pom.xml | 2 +- pom.xml | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-tools/pom.xml b/build-tools/pom.xml index 7a187dcab1..9e7bf673ac 100644 --- a/build-tools/pom.xml +++ b/build-tools/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.locationtech.jts build-tools - 1.18.2 + 1.18.3-SNAPSHOT JTS Topology Suite Build Configuration diff --git a/modules/app/pom.xml b/modules/app/pom.xml index 09fba7d9f0..b851d08da9 100644 --- a/modules/app/pom.xml +++ b/modules/app/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-app ${project.groupId}:${project.artifactId} diff --git a/modules/core/pom.xml b/modules/core/pom.xml index 2b9e330208..528856778f 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-core ${project.groupId}:${project.artifactId} diff --git a/modules/example/pom.xml b/modules/example/pom.xml index 76adea9efe..5cf7852c7b 100644 --- a/modules/example/pom.xml +++ b/modules/example/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-example ${project.groupId}:${project.artifactId} diff --git a/modules/io/common/pom.xml b/modules/io/common/pom.xml index c06d58c08c..e91b8c2744 100644 --- a/modules/io/common/pom.xml +++ b/modules/io/common/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-io - 1.18.2 + 1.18.3-SNAPSHOT org.locationtech.jts.io jts-io-common diff --git a/modules/io/pom.xml b/modules/io/pom.xml index d35eeefaee..c3cf342b3a 100644 --- a/modules/io/pom.xml +++ b/modules/io/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-io ${project.groupId}:${project.artifactId} diff --git a/modules/lab/pom.xml b/modules/lab/pom.xml index dabf05d64f..0bdf29a09f 100644 --- a/modules/lab/pom.xml +++ b/modules/lab/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-lab ${project.groupId}:${project.artifactId} diff --git a/modules/pom.xml b/modules/pom.xml index dc36e117e7..c010958143 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts - 1.18.2 + 1.18.3-SNAPSHOT jts-modules ${project.groupId}:${project.artifactId} diff --git a/modules/tests/pom.xml b/modules/tests/pom.xml index 93b4477b6f..7efb1df386 100644 --- a/modules/tests/pom.xml +++ b/modules/tests/pom.xml @@ -3,7 +3,7 @@ org.locationtech.jts jts-modules - 1.18.2 + 1.18.3-SNAPSHOT jts-tests ${project.groupId}:${project.artifactId} diff --git a/pom.xml b/pom.xml index 593efff554..c2b954de0e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.locationtech.jts jts - 1.18.2 + 1.18.3-SNAPSHOT pom JTS Topology Suite @@ -80,7 +80,7 @@ scm:git::git@github.com:locationtech/jts.git scm:git:git@github.com:locationtech/jts.git https://github.com/locationtech/jts - jts-1.18.2 + HEAD From 1ef4dd9350d6640a7763920ef2ce14ec6daea5a5 Mon Sep 17 00:00:00 2001 From: Jim Hughes Date: Fri, 27 Aug 2021 15:38:56 -0400 Subject: [PATCH 072/275] Preparing JTSVersion.java and JTS_Version_History.md for 1.18.3-SNAPSHOT development. Signed-off-by: Jim Hughes --- doc/JTS_Version_History.md | 5 +++++ .../core/src/main/java/org/locationtech/jts/JTSVersion.java | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index bdd058eecf..3796fae622 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -17,6 +17,11 @@ Distributions for older JTS versions can be obtained at the +# Version 1.18.3 + +* Release Date: TBD * + + # Version 1.18.2 *Release Date: 08/27/2021* diff --git a/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java b/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java index c5bd666806..3d83169721 100644 --- a/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java +++ b/modules/core/src/main/java/org/locationtech/jts/JTSVersion.java @@ -41,12 +41,12 @@ public class JTSVersion { /** * The patch version number. */ - public static final int PATCH = 2; + public static final int PATCH = 3; /** * An optional string providing further release info (such as "alpha 1"); */ - private static final String RELEASE_INFO = ""; + private static final String RELEASE_INFO = "SNAPSHOT"; /** * Prints the current JTS version to stdout. From b8c3656c8fcb9999e7b391a68cffe5f104b961a7 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 31 Aug 2021 15:14:53 -0700 Subject: [PATCH 073/275] Update DEVELOPING.md --- DEVELOPING.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index 49b6be425f..9b36399ba3 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -74,10 +74,15 @@ The XML test format can be executed using the JTS TestRunner, or imported into t ### External QA tools -* LGTM CodeQL analysis: - * [Alerts report](https://lgtm.com/projects/g/locationtech/jts/alerts/?mode=tree) - * [![Total alerts](https://img.shields.io/lgtm/alerts/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/alerts/) - * [![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/context:java) +#### LGTM CodeQL analysis +[![Total alerts](https://img.shields.io/lgtm/alerts/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/alerts/) +[![Language grade: Java](https://img.shields.io/lgtm/grade/java/g/locationtech/jts.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/locationtech/jts/context:java) +* [Alerts report](https://lgtm.com/projects/g/locationtech/jts/alerts/?mode=tree) + + +#### ABI Laboratory Tracker +* [Binary compatibility report for JTS core](https://abi-laboratory.pro/?view=timeline&lang=java&l=jts-core) +* [Binary compatibility for JTS 1.15 and before](https://abi-laboratory.pro/index.php?view=timeline&lang=java&l=jts) ## Javadoc From 66bb70e8da57b137055cbec5ff33c98adec36601 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 31 Aug 2021 15:25:42 -0700 Subject: [PATCH 074/275] Update DEVELOPING.md --- DEVELOPING.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index 9b36399ba3..15cd946ae7 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -38,7 +38,7 @@ To skip QA checks: mvn verify -Dpmd.skip=true -Dcheckstyle.skip=true -Errors are logged, to browse errors use: +To browse QA errors: mvn site:site open modules/core/target/site/index.html @@ -46,31 +46,32 @@ Errors are logged, to browse errors use: ### JUnit tests JTS aims for 100% code coverage for unit tests. - -Unit tests are written in Java and are used for verifying API code, internal data structures, and ancillary algorithms. +Unit tests are written in Java using JUnit. +They are used for verifying API code, internal data structures, and ancillary algorithms. This allows testing all parts of the codebase, and can provide richer error detection and reporting. However, the tests are not as readable or portable as the XML tests. -* To run the unit tests in a module (`jts-core`): +* Run the unit tests in a module (e.g. `jts-core`): mvn test -pl modules/core ### XML Tests -JTS provides a code-independent, declarative XML-based format for expressing geometric functional tests. +JTS provides a simple language-independent, declarative XML-based format for expressing geometric functional tests. This format has the following advantages: -* allows encoding large geometries -* provides geometric test cases in a reusable way -* easily consumed by tools such as the JTS TestBuilder or by other geometry libraries (e.g. GEOS) +* allows encoding large geometries more easily +* provides geometric test cases in a reusable, language-independent way +* easily consumed by tools such as the **JTS TestBuilder** +* easily used by JTS ports (e.g. [GEOS](https://trac.osgeo.org/geos)) or other geometry libraries * allows geometric tests to be used with other operation implementations, for testing or comparison purposes -This format should be used for tests which involve large geometries, or which +This format should be used for tests which express fundamental geometric semantics of the JTS library. -The XML test format can be executed using the JTS TestRunner, or imported into the JTS TestBuilder. +The XML test format can be executed using the **JTS TestRunner**, or imported into the **JTS TestBuilder**. ### External QA tools From c883b042aecb9f9413520da1f1a21c23a856f684 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 2 Sep 2021 16:04:36 -0700 Subject: [PATCH 075/275] Improve behaviour of GeometryFixer for holes outside shell (#772) Change behaviour of GeometryFixer for holes outside polygons Signed-off-by: Martin Davis --- .../jts/geom/util/GeometryFixer.java | 105 ++++++++++++++---- .../jts/geom/util/GeometryFixerTest.java | 5 + .../perf/geom/util/GeometryFixerFuzzer.java | 58 +++++++--- 3 files changed, 135 insertions(+), 33 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java index 1f592f779b..94a9ba5181 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java @@ -26,6 +26,8 @@ import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.prep.PreparedGeometry; +import org.locationtech.jts.geom.prep.PreparedGeometryFactory; import org.locationtech.jts.operation.buffer.BufferOp; import org.locationtech.jts.operation.overlayng.OverlayNG; import org.locationtech.jts.operation.overlayng.OverlayNGRobust; @@ -45,18 +47,23 @@ *
    • Empty atomic geometries are valid and are returned unchanged
    • *
    • Empty elements are removed from collections
    • *
    • Point: keep valid coordinate, or EMPTY
    • - *
    • LineString: fix coordinate list
    • - *
    • LinearRing: fix coordinate list, return as valid ring or else LineString
    • + *
    • LineString: coordinates are fixed
    • + *
    • LinearRing: coordinates are fixed. Keep valid ring, or else convert into LineString
    • *
    • Polygon: transform into a valid polygon, - * preserving as much of the extent and vertices as possible
    • - *
    • MultiPolygon: fix each polygon, - * then ensure result is non-overlapping (via union)
    • - *
    • GeometryCollection: fix each element
    • + * preserving as much of the extent and vertices as possible. + *
        + *
      • Rings are fixed to ensure they are valid
      • + *
      • Holes intersecting the shell are subtracted from the shell
      • + *
      • Holes outside the shell are converted into polygons
      • + *
      + *
    • MultiPolygon: each polygon is fixed, + * then result made non-overlapping (via union)
    • + *
    • GeometryCollection: each element is fixed
    • *
    • Collapsed lines and polygons are handled as follows, * depending on the keepCollapsed setting: *
        - *
      • false: (default) collapses are converted to empty geometries
      • - *
      • true: collapses are converted to a valid geometry of lower dimension
      • + *
      • false: (default) collapses are converted to empty geometries
      • + *
      • true: collapses are converted to a valid geometry of lower dimension
      • *
      *
    • * @@ -263,25 +270,85 @@ private Geometry fixPolygonElement(Polygon geom) { if (isKeepCollapsed) { return fixLineString(shell); } - //-- if not allowing collapses then return empty polygon + //--- if not allowing collapses then return empty polygon return null; } - // if no holes then done + //--- if no holes then done if (geom.getNumInteriorRing() == 0) { return fixShell; } - Geometry fixHoles = fixHoles(geom); - Geometry result = removeHoles(fixShell, fixHoles); + + //--- fix holes, classify, and construct shell-true holes + List holesFixed = fixHoles(geom); + List holes = new ArrayList(); + List shells = new ArrayList(); + classifyHoles(fixShell, holesFixed, holes, shells); + Geometry polyWithHoles = difference(fixShell, holes); + if (shells.size() == 0) { + return polyWithHoles; + } + + //--- if some holes converted to shells, union all shells + shells.add(polyWithHoles); + Geometry result = union(shells); return result; } - private Geometry removeHoles(Geometry shell, Geometry holes) { - if (holes == null) + private List fixHoles(Polygon geom) { + List holes = new ArrayList(); + for (int i = 0; i < geom.getNumInteriorRing(); i++) { + Geometry holeRep = fixRing(geom.getInteriorRingN(i)); + if (holeRep != null) { + holes.add(holeRep); + } + } + return holes; + } + + private void classifyHoles(Geometry shell, List holesFixed, List holes, List shells) { + PreparedGeometry shellPrep = PreparedGeometryFactory.prepare(shell); + for (Geometry hole : holesFixed) { + if (shellPrep.intersects(hole)) { + holes.add(hole); + } + else { + shells.add(hole); + } + } + } + + /** + * Subtracts a list of polygonal geometries from a polygonal geometry. + * + * @param shell polygonal geometry for shell + * @param holes polygonal geometries to subtract + * @return the result geometry + */ + private Geometry difference(Geometry shell, List holes) { + if (holes == null || holes.size() == 0) return shell; - return OverlayNGRobust.overlay(shell, holes, OverlayNG.DIFFERENCE); + Geometry holesUnion = union(holes); + return OverlayNGRobust.overlay(shell, holesUnion, OverlayNG.DIFFERENCE); + } + + /** + * Unions a list of polygonal geometries. + * Optimizes case of zero or one input geometries. + * Requires that the inputs are net new objects. + * + * @param polys the polygonal geometries to union + * @return the union of the inputs + */ + private Geometry union(List polys) { + if (polys.size() == 0) return factory.createPolygon(); + if (polys.size() == 1) { + return polys.get(0); + } + // TODO: replace with holes.union() once OverlayNG is the default + return OverlayNGRobust.union(polys); } - private Geometry fixHoles(Polygon geom) { + private Geometry OLDfixHoles(Polygon geom) { List holes = new ArrayList(); for (int i = 0; i < geom.getNumInteriorRing(); i++) { Geometry holeRep = fixRing(geom.getInteriorRingN(i)); @@ -299,9 +366,9 @@ private Geometry fixHoles(Polygon geom) { } private Geometry fixRing(LinearRing ring) { - //-- always execute fix, since it may remove repeated coords etc + //-- always execute fix, since it may remove repeated/invalid coords etc + // TODO: would it be faster to check ring validity first? Geometry poly = factory.createPolygon(ring); - // TOD: check if buffer removes invalid coordinates return BufferOp.bufferByZero(poly, true); } @@ -318,7 +385,7 @@ private Geometry fixMultiPolygon(MultiPolygon geom) { return factory.createMultiPolygon(); } // TODO: replace with polys.union() once OverlayNG is the default - Geometry result = OverlayNGRobust.union(polys); + Geometry result = union(polys); return result; } diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java index 4b049b5832..4f82b08baf 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java @@ -253,6 +253,11 @@ public void testPolygonHoleKeepCollapse() { "POLYGON ((10 10, 10 90, 90 90, 90 10, 10 10))"); } + public void testPolygonHoleOverlapAndOutsideOverlap() { + checkFix("POLYGON ((50 90, 80 90, 80 10, 50 10, 50 90), (70 80, 90 80, 90 20, 70 20, 70 80), (40 80, 40 50, 0 50, 0 80, 40 80), (30 40, 10 40, 10 60, 30 60, 30 40), (60 70, 80 70, 80 30, 60 30, 60 70))", + "MULTIPOLYGON (((10 40, 10 50, 0 50, 0 80, 40 80, 40 50, 30 50, 30 40, 10 40)), ((70 80, 70 70, 60 70, 60 30, 70 30, 70 20, 80 20, 80 10, 50 10, 50 90, 80 90, 80 80, 70 80)))"); + } + //---------------------------------------- public void testMultiPolygonEmpty() { diff --git a/modules/core/src/test/java/test/jts/perf/geom/util/GeometryFixerFuzzer.java b/modules/core/src/test/java/test/jts/perf/geom/util/GeometryFixerFuzzer.java index 1bd5d20c4e..e93de13d27 100644 --- a/modules/core/src/test/java/test/jts/perf/geom/util/GeometryFixerFuzzer.java +++ b/modules/core/src/test/java/test/jts/perf/geom/util/GeometryFixerFuzzer.java @@ -15,11 +15,15 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.util.GeometryFixer; public class GeometryFixerFuzzer { + private static final int GEOM_EXTENT_SIZE = 100; private static final int NUM_ITER = 10000; + private static final boolean IS_VERBOSE = false; public static void main(String[] args) { GeometryFixerFuzzer.run(); @@ -37,33 +41,38 @@ public GeometryFixerFuzzer() { } private void run(int numIter) { + System.out.println("GeometryFixer fuzzer: iterations = " + numIter); for (int i = 0; i < numIter; i++) { int numHoles = (int) (10 * Math.random()); - Geometry invalidPoly = createRandomPoly(100, numHoles); + //Geometry invalidPoly = createRandomLinePoly(100, numHoles); + Geometry invalidPoly = createRandomCirclePoly(100, numHoles); Geometry result = GeometryFixer.fix(invalidPoly); boolean isValid = result.isValid(); - String status = isValid ? "valid" : "INVALID"; - String msg = String.format("%d: Pts - input %d, output %d - %s", - i, invalidPoly.getNumPoints(), result.getNumPoints(), status); - //System.out.println(invalidPoly); - if (! isValid) { - System.out.println(msg); - System.out.println(invalidPoly); - } + report(i, invalidPoly, result, isValid); } } - private Geometry createRandomPoly(int numPoints, int numHoles) { + private void report(int i, Geometry invalidPoly, Geometry result, boolean isValid) { + String status = isValid ? "valid" : "INVALID"; + String msg = String.format("%d: Pts - input %d, output %d - %s", + i, invalidPoly.getNumPoints(), result.getNumPoints(), status); + if (IS_VERBOSE || ! isValid) { + System.out.println(msg); + System.out.println(invalidPoly); + } + } + + private Geometry createRandomLinePoly(int numPoints, int numHoles) { int numRingPoints = numPoints / (numHoles + 1); - LinearRing shell = createRandomRing(numRingPoints); + LinearRing shell = createRandomLineRing(numRingPoints); LinearRing[] holes = new LinearRing[numHoles]; for (int i = 0; i < numHoles; i++) { - holes[i] = createRandomRing(numRingPoints); + holes[i] = createRandomLineRing(numRingPoints); } return factory.createPolygon(shell, holes); } - private LinearRing createRandomRing(int numPoints) { + private LinearRing createRandomLineRing(int numPoints) { return factory.createLinearRing(createRandomPoints(numPoints)); } @@ -78,7 +87,28 @@ private Coordinate[] createRandomPoints(int numPoints) { } private double randOrd() { - double ord = 100 * Math.random(); + double ord = GEOM_EXTENT_SIZE * Math.random(); return ord; } + + private Geometry createRandomCirclePoly(int numPoints, int numHoles) { + int numRingPoints = numPoints / (numHoles + 1); + LinearRing shell = ceateRandomCircleRing(numRingPoints); + LinearRing[] holes = new LinearRing[numHoles]; + for (int i = 0; i < numHoles; i++) { + holes[i] = ceateRandomCircleRing(numRingPoints); + } + return factory.createPolygon(shell, holes); + } + + private LinearRing ceateRandomCircleRing(int numPoints) { + int numQuadSegs = (numPoints / 4) + 1; + if (numQuadSegs < 3) numQuadSegs = 3; + + Coordinate p = new Coordinate(randOrd(), randOrd()); + Point pt = factory.createPoint( p ); + double radius = GEOM_EXTENT_SIZE * Math.random() / 2; + Polygon buffer = (Polygon) pt.buffer(radius, numQuadSegs); + return buffer.getExteriorRing(); + } } From 176eeba6ccef4cc09ab366973cd17be4e8879e9d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 2 Sep 2021 16:07:52 -0700 Subject: [PATCH 076/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 3796fae622..697926a4ed 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -21,6 +21,9 @@ Distributions for older JTS versions can be obtained at the * Release Date: TBD * +### Functionality Improvements + +* Improve `GeometryFixer` behaviour for holes outside polygons (#772) # Version 1.18.2 From 102f680644eafefe9074f67a24a4b7dffdfbb4b3 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Sep 2021 12:40:34 -0700 Subject: [PATCH 077/275] Add polygon triangulation algorithms and Tri data structure (#775) * Add polygon triangulation algorithms and Tri data structure Signed-off-by: Martin Davis --- .../function/SpatialIndexFunctions.java | 17 + .../function/TriangulatePolyFunctions.java | 32 + .../GeometryFunctionRegistry.java | 2 + .../org/locationtech/jts/geom/Triangle.java | 68 ++- .../org/locationtech/jts/math/MathUtil.java | 25 + .../ConstrainedDelaunayTriangulator.java | 95 +++ .../polygon/PolygonEarClipper.java | 400 +++++++++++++ .../polygon/PolygonHoleJoiner.java | 334 +++++++++++ .../polygon/PolygonTriangulator.java | 99 ++++ .../polygon/TriDelaunayImprover.java | 186 ++++++ .../polygon/VertexSequencePackedRtree.java | 256 ++++++++ .../jts/triangulate/polygon/package-info.java | 19 + .../locationtech/jts/triangulate/tri/Tri.java | 549 ++++++++++++++++++ .../jts/triangulate/tri/TriEdge.java | 68 +++ .../triangulate/tri/TriangulationBuilder.java | 82 +++ .../jts/triangulate/tri/package-info.java | 18 + .../locationtech/jts/geom/TriangleTest.java | 40 +- .../ConstrainedDelaunayTriangulatorTest.java | 73 +++ .../polygon/PolygonTriangulatorTest.java | 83 +++ .../VertexSequencePackedRtreeTest.java | 88 +++ .../jts/triangulate/tri/TriTest.java | 73 +++ 21 files changed, 2590 insertions(+), 17 deletions(-) create mode 100644 modules/app/src/main/java/org/locationtech/jtstest/function/TriangulatePolyFunctions.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonHoleJoiner.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/TriDelaunayImprover.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtree.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/package-info.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriEdge.java create mode 100755 modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriangulationBuilder.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/triangulate/tri/package-info.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulatorTest.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtreeTest.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/triangulate/tri/TriTest.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java index 05ce51e864..9124b1eb1f 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java @@ -35,6 +35,7 @@ import org.locationtech.jts.index.strtree.Boundable; import org.locationtech.jts.index.strtree.GeometryItemDistance; import org.locationtech.jts.index.strtree.STRtree; +//import org.locationtech.jts.triangulatepoly.VertexSequencePackedRtree; public class SpatialIndexFunctions @@ -249,4 +250,20 @@ public static Geometry monotoneChains(Geometry geom) { } return geom.getFactory().buildGeometry(lines); } + + /* + public static Geometry sprTreeBounds(Geometry geom) + { + Coordinate[] pts = geom.getCoordinates(); + VertexSequencePackedRtree index = new VertexSequencePackedRtree(pts); + Envelope[] bounds = index.getBounds(); + Geometry[] polys = new Geometry[bounds.length]; + int i = 0; + for (Envelope env : bounds) { + polys[i++] = geom.getFactory().toGeometry(env); + } + return geom.getFactory().createGeometryCollection(polys); + } + */ + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulatePolyFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulatePolyFunctions.java new file mode 100644 index 0000000000..566cf5a13d --- /dev/null +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/TriangulatePolyFunctions.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jtstest.function; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator; +import org.locationtech.jts.triangulate.polygon.PolygonTriangulator; + + +public class TriangulatePolyFunctions +{ + public static Geometry triangulate(Geometry geom) + { + return PolygonTriangulator.triangulate(geom); + } + + public static Geometry constrainedDelaunay(Geometry geom) + { + return ConstrainedDelaunayTriangulator.triangulate(geom); + } + + +} diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java index 4183997a57..098cca12d4 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/GeometryFunctionRegistry.java @@ -65,6 +65,7 @@ import org.locationtech.jtstest.function.SpatialIndexFunctions; import org.locationtech.jtstest.function.SpatialPredicateFunctions; import org.locationtech.jtstest.function.TriangleFunctions; +import org.locationtech.jtstest.function.TriangulatePolyFunctions; import org.locationtech.jtstest.function.TriangulationFunctions; import org.locationtech.jtstest.function.ValidationFunctions; import org.locationtech.jtstest.function.WriterFunctions; @@ -131,6 +132,7 @@ public static GeometryFunctionRegistry createTestBuilderRegistry() funcRegistry.add(SnappingFunctions.class); funcRegistry.add(SortingFunctions.class); funcRegistry.add(TriangulationFunctions.class); + funcRegistry.add(TriangulatePolyFunctions.class); funcRegistry.add(TriangleFunctions.class); funcRegistry.add(ValidationFunctions.class); funcRegistry.add(WriterFunctions.class); diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java index baa4fae5fe..8cdaa1d16f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java @@ -33,12 +33,9 @@ public class Triangle * Note: this implementation is not robust for angles very close to 90 * degrees. * - * @param a - * a vertex of the triangle - * @param b - * a vertex of the triangle - * @param c - * a vertex of the triangle + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle * @return true if the triangle is acute */ public static boolean isAcute(Coordinate a, Coordinate b, Coordinate c) @@ -51,6 +48,38 @@ public static boolean isAcute(Coordinate a, Coordinate b, Coordinate c) return false; return true; } + + /** + * Tests whether a triangle is oriented counter-clockwise. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return true if the triangle orientation is counter-clockwise + */ + public static boolean isCCW(Coordinate a, Coordinate b, Coordinate c) + { + return Orientation.COUNTERCLOCKWISE == Orientation.index(a, b, c); + } + + /** + * Tests whether a triangle intersects a point. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point to test + * @return true if the triangle intersects the point + */ + public static boolean intersects(Coordinate a, Coordinate b, Coordinate c, Coordinate p) + { + int exteriorIndex = isCCW(a, b, c) ? + Orientation.CLOCKWISE : Orientation.COUNTERCLOCKWISE; + if (exteriorIndex == Orientation.index(a, b, p)) return false; + if (exteriorIndex == Orientation.index(b, c, p)) return false; + if (exteriorIndex == Orientation.index(c, a, p)) return false; + return true; + } /** * Computes the line which is the perpendicular bisector of the line segment @@ -437,8 +466,8 @@ public static double interpolateZ(Coordinate p, Coordinate v0, Coordinate v1, double u = (-c * dx + a * dy) / det; double z = v0.getZ() + t * (v1.getZ() - v0.getZ()) + u * (v2.getZ() - v0.getZ()); return z; - } - + } + /** * The coordinates of the vertices of the triangle */ @@ -487,9 +516,18 @@ public Coordinate inCentre() */ public boolean isAcute() { - return isAcute(this.p0, this.p1, this.p2); + return isAcute(p0, p1, p2); } + /** + * Tests whether this triangle is oriented counter-clockwise. + * + * @return true if the triangle orientation is counter-clockwise + */ + public boolean isCCW() { + return isCCW(p0, p1, p2); + } + /** * Computes the circumcentre of this triangle. The circumcentre is the centre * of the circumcircle, the smallest circle which encloses the triangle. It is @@ -507,7 +545,7 @@ public boolean isAcute() */ public Coordinate circumcentre() { - return circumcentre(this.p0, this.p1, this.p2); + return circumcentre(p0, p1, p2); } /** @@ -522,7 +560,7 @@ public Coordinate circumcentre() */ public Coordinate centroid() { - return centroid(this.p0, this.p1, this.p2); + return centroid(p0, p1, p2); } /** @@ -532,7 +570,7 @@ public Coordinate centroid() */ public double longestSideLength() { - return longestSideLength(this.p0, this.p1, this.p2); + return longestSideLength(p0, p1, p2); } /** @@ -545,7 +583,7 @@ public double longestSideLength() */ public double area() { - return area(this.p0, this.p1, this.p2); + return area(p0, p1, p2); } /** @@ -563,7 +601,7 @@ public double area() */ public double signedArea() { - return signedArea(this.p0, this.p1, this.p2); + return signedArea(p0, p1, p2); } /** @@ -574,7 +612,7 @@ public double signedArea() */ public double area3D() { - return area3D(this.p0, this.p1, this.p2); + return area3D(p0, p1, p2); } /** diff --git a/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java b/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java index 2a7f84275a..63bca1b672 100644 --- a/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java +++ b/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java @@ -47,6 +47,31 @@ public static int clamp(int x, int min, int max) return x; } + /** + * Clamps an integer to a given maximum limit. + * + * @param x the value to clamp + * @param max the maximum value + * @return the clamped value + */ + public static int clampMax(int x, int max) + { + if (x > max) return max; + return x; + } + + /** + * Computes the ceiling function of the dividend of two integers. + * + * @param num the numerator + * @param denom the denominator + * @return the ceiling of num / denom + */ + public static int ceil(int num, int denom) { + int div = num / denom; + return div * denom >= num ? div : div + 1; + } + private static final double LOG_10 = Math.log(10); /** diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java new file mode 100644 index 0000000000..8872a2a2c1 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.util.PolygonExtracter; +import org.locationtech.jts.triangulate.tri.Tri; +import org.locationtech.jts.triangulate.tri.TriangulationBuilder; + +/** + * Computes the Constrained Delaunay Triangulation of polygons. + * The Constrained Delaunay Triangulation of a polygon is a set of triangles + * covering the polygon, with the maximum total interior angle over all + * possible triangulations. It provides the "best quality" triangulation + * of the polygon. + *

      + * Holes are supported. + */ +public class ConstrainedDelaunayTriangulator { + + /** + * Computes the Constrained Delaunay Triangulation of each polygon element in a geometry. + * + * @param geom the input geometry + * @return a GeometryCollection of the computed triangle polygons + */ + public static Geometry triangulate(Geometry geom) { + ConstrainedDelaunayTriangulator cdt = new ConstrainedDelaunayTriangulator(geom); + return cdt.compute(); + } + + private final GeometryFactory geomFact; + private final Geometry inputGeom; + + /** + * Constructs a new Constrained Delaunay triangulator. + * + * @param inputGeom the input geometry + */ + public ConstrainedDelaunayTriangulator(Geometry inputGeom) { + geomFact = new GeometryFactory(); + this.inputGeom = inputGeom; + } + + private Geometry compute() { + List polys = PolygonExtracter.getPolygons(inputGeom); + List triList = new ArrayList(); + for (Polygon poly : polys) { + List polyTriList = triangulatePolygon(poly); + triList.addAll(polyTriList); + } + return Tri.toGeometry(triList, geomFact); + } + + /** + * Computes the triangulation of a single polygon + * and returns it as a list of {@link Tri}s. + * + * @param poly the input polygon + * @return list of Tris forming the triangulation + */ + List triangulatePolygon(Polygon poly) { + /** + * Normalize to ensure that shell and holes have canonical orientation. + * + * TODO: perhaps better to just correct orientation of rings? + */ + Polygon polyNorm = (Polygon) poly.norm(); + Coordinate[] polyShell = PolygonHoleJoiner.join(polyNorm); + List triList = PolygonEarClipper.triangulate(polyShell); + + //long start = System.currentTimeMillis(); + TriangulationBuilder.build(triList); + TriDelaunayImprover.improve(triList); + //System.out.println("swap used: " + (System.currentTimeMillis() - start) + " milliseconds"); + + return triList; + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java new file mode 100644 index 0000000000..df6dff7f79 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.algorithm.Angle; +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateList; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Triangle; +import org.locationtech.jts.triangulate.tri.Tri; + +/** + * Triangulates a polygon using the Ear-Clipping technique. + * The polygon is provided as a closed list of contiguous vertices + * defining its boundary. + * The vertices must have clockwise orientation. + *

      + * The polygon boundary must not self-cross, + * but may self-touch at points or along an edge. + * It may contain repeated points, which are treated as a single vertex. + * By default every vertex is triangulated, + * including ones which are "flat" (the adjacent segments are collinear). + * These can be removed by setting {@link #setSkipFlatCorners(boolean)} + *

      + * The polygon representation does not allow holes. + * Polygons with holes can be triangulated by preparing them + * with {@link PolygonHoleJoiner}. + * + * @author Martin Davis + * + */ +class PolygonEarClipper { + + private static final int NO_VERTEX_INDEX = -1; + + /** + * Triangulates a polygon via ear-clipping. + * + * @param polyShell the vertices of the polygon + * @return a list of the Tris + */ + public static List triangulate(Coordinate[] polyShell) { + PolygonEarClipper clipper = new PolygonEarClipper(polyShell); + return clipper.compute(); + } + + private boolean isFlatCornersSkipped = false; + + /** + * The polygon vertices are provided in CW orientation. + * Thus for convex interior angles + * the vertices forming the angle are in CW orientation. + */ + private final Coordinate[] vertex; + + private final int[] vertexNext; + private int vertexSize; + // first available vertex index + private int vertexFirst; + + // indices for current corner + private int[] cornerIndex; + + /** + * Indexing vertices improves ear intersection testing performance a lot. + * The polyShell vertices are contiguous, so are suitable for an SPRtree. + */ + private VertexSequencePackedRtree vertexCoordIndex; + + /** + * Creates a new ear-clipper instance. + * + * @param polyShell the polygon vertices to process + */ + public PolygonEarClipper(Coordinate[] polyShell) { + this.vertex = polyShell; + + // init working storage + vertexSize = vertex.length - 1; + vertexNext = createNextLinks(vertexSize); + vertexFirst = 0; + + vertexCoordIndex = new VertexSequencePackedRtree(vertex); + } + + private static int[] createNextLinks(int size) { + int[] next = new int[size]; + for (int i = 0; i < size; i++) { + next[i] = i + 1; + } + next[size - 1] = 0; + return next; + } + + /** + * Sets whether flat corners formed by collinear adjacent line segments + * are included in the triangulation. + * Skipping flat corners reduces the number of triangles in the output. + * However, it produces a triangulation which does not include + * all input vertices. This may be undesirable for downstream processes + * (such as computing a Constrained Delaunay Triangulation for + * purposes of computing the medial axis). + *

      + * The default is to include all vertices in the result triangulation. + * This still produces a valid triangulation, with no zero-area triangles. + *

      + * Note that repeated vertices are always skipped. + * + * @param isFlatCornersSkipped whether to skip collinear vertices + */ + public void setSkipFlatCorners(boolean isFlatCornersSkipped) { + this.isFlatCornersSkipped = isFlatCornersSkipped; + } + + public List compute() { + List triList = new ArrayList(); + + /** + * Count scanned corners, to catch infinite loops + * (which indicate an algorithm bug) + */ + int cornerScanCount = 0; + + initCornerIndex(); + Coordinate[] corner = new Coordinate[3]; + fetchCorner(corner); + + /** + * Scan continuously around vertex ring, + * until all ears have been found. + */ + while (true) { + /** + * Non-convex corner- remove if flat, or skip + * (a concave corner will turn into a convex corner + * after enough ears are removed) + */ + if (! isConvex(corner)) { + // remove the corner if it is flat or a repeated point + boolean isCornerRemoved = hasRepeatedPoint(corner) + || (isFlatCornersSkipped && isFlat(corner)); + if (isCornerRemoved) { + removeCorner(); + } + cornerScanCount++; + if ( cornerScanCount > 2 * vertexSize ) { + throw new IllegalStateException("Unable to find a convex corner"); + } + } + /** + * Convex corner - check if it is a valid ear + */ + else if ( isValidEar(cornerIndex[1], corner) ) { + triList.add(Tri.create(corner)); + removeCorner(); + cornerScanCount = 0; + } + if ( cornerScanCount > 2 * vertexSize ) { + //System.out.println(toGeometry()); + throw new IllegalStateException("Unable to find a valid ear"); + } + + //--- done when all corners are processed and removed + if ( vertexSize < 3 ) { + return triList; + } + + /** + * Skip to next corner. + * This is done even after an ear is removed, + * since that creates fewer skinny triangles. + */ + nextCorner(corner); + } + } + + private boolean isValidEar(int cornerIndex, Coordinate[] corner) { + int intApexIndex = findIntersectingVertex(cornerIndex, corner); + //--- no intersections found + if (intApexIndex == NO_VERTEX_INDEX) + return true; + //--- check for duplicate corner apex vertex + if ( vertex[intApexIndex].equals2D(corner[1]) ) { + //--- a duplicate corner vertex requires a full scan + return isValidEarScan(cornerIndex, corner); + } + return false; + } + + /** + * Finds another vertex intersecting the corner triangle, if any. + * Uses the vertex spatial index for efficiency. + *

      + * Also finds any vertex which is a duplicate of the corner apex vertex, + * which then requires a full scan of the vertices to confirm ear is valid. + * This is usually a rare situation, so has little impact on performance. + * + * @param cornerIndex the index of the corner apex vertex + * @param corner the corner vertices + * @return the index of an intersecting or duplicate vertex, or {@link #NO_VERTEX_INDEX} if none + */ + private int findIntersectingVertex(int cornerIndex, Coordinate[] corner) { + Envelope cornerEnv = envelope(corner); + int[] result = vertexCoordIndex.query(cornerEnv); + + int dupApexIndex = NO_VERTEX_INDEX; + //--- check for duplicate vertices + for (int i = 0; i < result.length; i++) { + int vertIndex = result[i]; + + if (vertIndex == cornerIndex + || vertIndex == vertex.length - 1 + || isRemoved(vertIndex)) + continue; + + Coordinate v = vertex[vertIndex]; + /** + * If another vertex at the corner is found, + * need to do a full scan to check the incident segments. + * This happens when the polygon ring self-intersects, + * usually due to hold joining. + * But only report this if no properly intersecting vertex is found, + * for efficiency. + */ + if ( v.equals2D(corner[1]) ) { + dupApexIndex = vertIndex; + } + //--- don't need to check other corner vertices + else if ( v.equals2D(corner[0]) || v.equals2D(corner[2]) ) { + continue; + } + //--- this is a properly intersecting vertex + else if (Triangle.intersects(corner[0], corner[1], corner[2], v) ) + return vertIndex; + } + if (dupApexIndex != NO_VERTEX_INDEX) { + return dupApexIndex; + } + return NO_VERTEX_INDEX; + } + + /** + * Scan all vertices in current ring to check if any are duplicates + * of the corner apex vertex, and if so whether the corner ear + * intersects the adjacent segments and thus is invalid. + * + * @param cornerIndex the index of the corner apex + * @param corner the corner vertices + * @return true if the corner ia a valid ear + */ + private boolean isValidEarScan(int cornerIndex, Coordinate[] corner) { + double cornerAngle = Angle.angleBetweenOriented(corner[0], corner[1], corner[2]); + + int currIndex = nextIndex(vertexFirst); + int prevIndex = vertexFirst; + Coordinate vPrev = vertex[prevIndex]; + for (int i = 0; i < vertexSize; i++) { + Coordinate v = vertex[currIndex]; + /** + * Because of hole-joining vertices can occur more than once. + * If vertex is same as corner[1], + * check whether either adjacent edge lies inside the ear corner. + * If so the ear is invalid. + */ + if ( currIndex != cornerIndex + && v.equals2D(corner[1]) ) { + Coordinate vNext = vertex[nextIndex(currIndex)]; + + //TODO: for robustness use segment orientation instead + double aOut = Angle.angleBetweenOriented(corner[0], corner[1], vNext); + double aIn = Angle.angleBetweenOriented(corner[0], corner[1], vPrev); + if ( aOut > 0 && aOut < cornerAngle ) { + return false; + } + if ( aIn > 0 && aIn < cornerAngle ) { + return false; + } + if ( aOut == 0 && aIn == cornerAngle ) { + return false; + } + } + + //--- move to next vertex + vPrev = v; + prevIndex = currIndex; + currIndex = nextIndex(currIndex); + } + return true; + } + + private static Envelope envelope(Coordinate[] corner) { + Envelope cornerEnv = new Envelope(corner[0], corner[1]); + cornerEnv.expandToInclude(corner[2]); + return cornerEnv; + } + + /** + * Remove the corner apex vertex and update the candidate corner location. + */ + private void removeCorner() { + int cornerApexIndex = cornerIndex[1]; + if ( vertexFirst == cornerApexIndex) { + vertexFirst = vertexNext[cornerApexIndex]; + } + vertexNext[cornerIndex[0]] = vertexNext[cornerApexIndex]; + vertexCoordIndex.remove(cornerApexIndex); + vertexNext[cornerApexIndex] = NO_VERTEX_INDEX; + vertexSize--; + //-- adjust following corner indexes + cornerIndex[1] = nextIndex(cornerIndex[0]); + cornerIndex[2] = nextIndex(cornerIndex[1]); + } + + private boolean isRemoved(int vertexIndex) { + return NO_VERTEX_INDEX == vertexNext[vertexIndex]; + } + + private void initCornerIndex() { + cornerIndex = new int[3]; + cornerIndex[0] = 0; + cornerIndex[1] = 1; + cornerIndex[2] = 2; + } + + /** + * Fetch the corner vertices from the indices. + * + * @param corner an array for the corner vertices + */ + private void fetchCorner(Coordinate[] cornerVertex) { + cornerVertex[0] = vertex[cornerIndex[0]]; + cornerVertex[1] = vertex[cornerIndex[1]]; + cornerVertex[2] = vertex[cornerIndex[2]]; + } + + /** + * Move to next corner. + */ + private void nextCorner(Coordinate[] cornerVertex) { + if ( vertexSize < 3 ) { + return; + } + cornerIndex[0] = nextIndex(cornerIndex[0]); + cornerIndex[1] = nextIndex(cornerIndex[0]); + cornerIndex[2] = nextIndex(cornerIndex[1]); + fetchCorner(cornerVertex); + } + + /** + * Get the index of the next available shell coordinate starting from the given + * index. + * + * @param index candidate position + * @return index of the next available shell coordinate + */ + private int nextIndex(int index) { + return vertexNext[index]; + } + + private static boolean isConvex(Coordinate[] pts) { + return Orientation.CLOCKWISE == Orientation.index(pts[0], pts[1], pts[2]); + } + + private static boolean isFlat(Coordinate[] pts) { + return Orientation.COLLINEAR == Orientation.index(pts[0], pts[1], pts[2]); + } + + private static boolean hasRepeatedPoint(Coordinate[] pts) { + return pts[1].equals2D(pts[0]) || pts[1].equals2D(pts[2]); + } + + public Polygon toGeometry() { + GeometryFactory fact = new GeometryFactory(); + CoordinateList coordList = new CoordinateList(); + int index = vertexFirst; + for (int i = 0; i < vertexSize; i++) { + Coordinate v = vertex[index]; + index = nextIndex(index); + // if (i < shellCoordAvailable.length && shellCoordAvailable.get(i)) + coordList.add(v, true); + } + coordList.closeRing(); + return fact.createPolygon(fact.createLinearRing(coordList.toCoordinateArray())); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonHoleJoiner.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonHoleJoiner.java new file mode 100755 index 0000000000..5aebe3f437 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonHoleJoiner.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.TreeSet; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.noding.BasicSegmentString; +import org.locationtech.jts.noding.MCIndexSegmentSetMutualIntersector; +import org.locationtech.jts.noding.SegmentIntersectionDetector; +import org.locationtech.jts.noding.SegmentSetMutualIntersector; +import org.locationtech.jts.noding.SegmentString; +import org.locationtech.jts.noding.SegmentStringUtil; + +/** + * Transforms a polygon with holes into a single self-touching ring + * by connecting holes to the exterior shell or to another hole. + * The holes are added from the lowest upwards. + * As the resulting shell develops, a hole might be added to what was + * originally another hole. + */ +class PolygonHoleJoiner { + + public static Polygon joinAsPolygon(Polygon inputPolygon) { + return inputPolygon.getFactory().createPolygon(join(inputPolygon)); + } + + public static Coordinate[] join(Polygon inputPolygon) { + PolygonHoleJoiner joiner = new PolygonHoleJoiner(inputPolygon); + return joiner.compute(); + } + + private static final double EPS = 1.0E-4; + + private List shellCoords; + // orderedCoords is a copy of shellCoords for sort purposes + private TreeSet orderedCoords; + // Key: starting end of the cut; Value: list of the other end of the cut + private HashMap> cutMap; + private SegmentSetMutualIntersector polygonIntersector; + + private Polygon inputPolygon; + + public PolygonHoleJoiner(Polygon inputPolygon) { + this.inputPolygon = inputPolygon; + polygonIntersector = createPolygonIntersector(inputPolygon); + } + + /** + * Computes the joined ring. + * + * @return the points in the joined ring + */ + public Coordinate[] compute() { + //--- copy the input polygon shell coords + shellCoords = ringCoordinates(inputPolygon.getExteriorRing()); + if ( inputPolygon.getNumInteriorRing() != 0 ) { + joinHoles(); + } + return shellCoords.toArray(new Coordinate[0]); + } + + private static List ringCoordinates(LinearRing ring) { + Coordinate[] coords = ring.getCoordinates(); + List coordList = new ArrayList(); + for (Coordinate p : coords) { + coordList.add(p); + } + return coordList; + } + + private void joinHoles() { + orderedCoords = new TreeSet(); + orderedCoords.addAll(shellCoords); + cutMap = new HashMap>(); + List orderedHoles = sortHoles(inputPolygon); + for (int i = 0; i < orderedHoles.size(); i++) { + joinHole(orderedHoles.get(i)); + } + } + + /** + * Joins a single hole to the current shellRing. + * + * @param hole the hole to join + */ + private void joinHole(LinearRing hole) { + /** + * 1) Get a list of HoleVertex Index. + * 2) Get a list of ShellVertex. + * 3) Get the pair that has the shortest distance between them. + * This pair is the endpoints of the cut + * 4) The selected ShellVertex may occurs multiple times in + * shellCoords[], so find the proper one and add the hole after it. + */ + final Coordinate[] holeCoords = hole.getCoordinates(); + List holeLeftVerticesIndex = getLeftMostVertex(hole); + Coordinate holeCoord = holeCoords[holeLeftVerticesIndex.get(0)]; + List shellCoordsList = getLeftShellVertex(holeCoord); + Coordinate shellCoord = shellCoordsList.get(0); + int shortestHoleVertexIndex = 0; + //--- pick the shell-hole vertex pair that gives the shortest distance + if ( Math.abs(shellCoord.x - holeCoord.x) < EPS ) { + double shortest = Double.MAX_VALUE; + for (int i = 0; i < holeLeftVerticesIndex.size(); i++) { + for (int j = 0; j < shellCoordsList.size(); j++) { + double currLength = Math.abs(shellCoordsList.get(j).y - holeCoords[holeLeftVerticesIndex.get(i)].y); + if ( currLength < shortest ) { + shortest = currLength; + shortestHoleVertexIndex = i; + shellCoord = shellCoordsList.get(j); + } + } + } + } + int shellVertexIndex = getShellCoordIndex(shellCoord, + holeCoords[holeLeftVerticesIndex.get(shortestHoleVertexIndex)]); + addHoleToShell(shellVertexIndex, holeCoords, holeLeftVerticesIndex.get(shortestHoleVertexIndex)); + } + + /** + * Get the ith shellvertex in shellCoords[] that the current should add after + * + * @param shellVertex Coordinate of the shell vertex + * @param holeVertex Coordinate of the hole vertex + * @return the ith shellvertex + */ + private int getShellCoordIndex(Coordinate shellVertex, Coordinate holeVertex) { + int numSkip = 0; + ArrayList newValueList = new ArrayList(); + newValueList.add(holeVertex); + if ( cutMap.containsKey(shellVertex) ) { + for (Coordinate coord : cutMap.get(shellVertex)) { + if ( coord.y < holeVertex.y ) { + numSkip++; + } + } + cutMap.get(shellVertex).add(holeVertex); + } else { + cutMap.put(shellVertex, newValueList); + } + if ( !cutMap.containsKey(holeVertex) ) { + cutMap.put(holeVertex, new ArrayList(newValueList)); + } + return getShellCoordIndexSkip(shellVertex, numSkip); + } + + /** + * Find the index of the coordinate in ShellCoords ArrayList, + * skipping over some number of matches + * + * @param coord + * @return + */ + private int getShellCoordIndexSkip(Coordinate coord, int numSkip) { + for (int i = 0; i < shellCoords.size(); i++) { + if ( shellCoords.get(i).equals2D(coord, EPS) ) { + if ( numSkip == 0 ) + return i; + numSkip--; + } + } + throw new IllegalStateException("Vertex is not in shellcoords"); + } + + /** + * Gets a list of shell vertices that could be used to join with the hole. + * This list contains only one item if the chosen vertex does not share the same + * x value with holeCoord + * + * @param holeCoord the hole coordinates + * @return a list of candidate join vertices + */ + private List getLeftShellVertex(Coordinate holeCoord) { + ArrayList list = new ArrayList(); + Coordinate closest = orderedCoords.higher(holeCoord); + while (closest.x == holeCoord.x) { + closest = orderedCoords.higher(closest); + } + do { + closest = orderedCoords.lower(closest); + } while (!isJoinable(holeCoord, closest) && !closest.equals(orderedCoords.first())); + list.add(closest); + if ( closest.x != holeCoord.x ) + return list; + double chosenX = closest.x; + list.clear(); + while (chosenX == closest.x) { + list.add(closest); + closest = orderedCoords.lower(closest); + if ( closest == null ) + return list; + } + return list; + } + + /** + * Determine if a line segment between a hole vertex + * and a shell vertex lies inside the input polygon. + * + * @param holeCoord a hole coordinate + * @param shellCoord a shell coordinate + * @return true if the line lies inside the polygon + */ + private boolean isJoinable(Coordinate holeCoord, Coordinate shellCoord) { + /** + * Since the line runs between a hole and the shell, + * it is inside the polygon if it does not cross the polygon boundary. + */ + boolean isJoinable = ! crossesPolygon(holeCoord, shellCoord); + /* + //--- slow code for testing only + LineString join = geomFact.createLineString(new Coordinate[] { holeCoord, shellCoord }); + boolean isJoinableSlow = inputPolygon.covers(join) + if (isJoinableSlow != isJoinable) { + System.out.println(WKTWriter.toLineString(holeCoord, shellCoord)); + } + //Assert.isTrue(isJoinableSlow == isJoinable); + */ + return isJoinable; + } + + /** + * Tests whether a line segment crosses the polygon boundary. + * + * @param p0 a vertex + * @param p1 a vertex + * @return true if the line segment crosses the polygon boundary + */ + private boolean crossesPolygon(Coordinate p0, Coordinate p1) { + SegmentString segString = new BasicSegmentString( + new Coordinate[] { p0, p1 }, null); + List segStrings = new ArrayList(); + segStrings.add(segString); + + SegmentIntersectionDetector segInt = new SegmentIntersectionDetector(); + segInt.setFindProper(true); + polygonIntersector.process(segStrings, segInt); + + return segInt.hasProperIntersection(); + } + + /** + * Add hole at proper position in shell coordinate list. + * Also adds hole points to ordered coordinates. + * + * @param shellVertexIndex + * @param holeCoords + * @param holeVertexIndex + */ + private void addHoleToShell(int shellVertexIndex, Coordinate[] holeCoords, int holeVertexIndex) { + List newCoords = new ArrayList(); + newCoords.add(new Coordinate(shellCoords.get(shellVertexIndex))); + final int nPts = holeCoords.length - 1; + int i = holeVertexIndex; + do { + newCoords.add(new Coordinate(holeCoords[i])); + i = (i + 1) % nPts; + } while (i != holeVertexIndex); + newCoords.add(new Coordinate(holeCoords[holeVertexIndex])); + shellCoords.addAll(shellVertexIndex, newCoords); + orderedCoords.addAll(newCoords); + } + + /** + * Sort the holes by minimum X, minimum Y. + * + * @param poly polygon that contains the holes + * @return a list of ordered hole geometry + */ + private List sortHoles(final Polygon poly) { + List holes = new ArrayList(); + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + holes.add(poly.getInteriorRingN(i)); + } + Collections.sort(holes, new EnvelopeComparator()); + return holes; + } + + /** + * Gets a list of indices of the leftmost vertices in a ring. + * + * @param geom the hole ring + * @return index of the left most vertex + */ + private List getLeftMostVertex(LinearRing ring) { + Coordinate[] coords = ring.getCoordinates(); + ArrayList list = new ArrayList(); + double minX = ring.getEnvelopeInternal().getMinX(); + for (int i = 0; i < coords.length; i++) { + if ( Math.abs(coords[i].x - minX) < EPS ) { + list.add(i); + } + } + return list; + } + + private SegmentSetMutualIntersector createPolygonIntersector(Polygon polygon) { + List polySegStrings = SegmentStringUtil.extractSegmentStrings(polygon); + return new MCIndexSegmentSetMutualIntersector(polySegStrings); + } + + /** + * + * @author mdavis + * + */ + private static class EnvelopeComparator implements Comparator { + public int compare(Geometry o1, Geometry o2) { + Envelope e1 = o1.getEnvelopeInternal(); + Envelope e2 = o2.getEnvelopeInternal(); + return e1.compareTo(e2); + } + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java new file mode 100755 index 0000000000..2da07e8a8e --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.util.PolygonExtracter; +import org.locationtech.jts.triangulate.tri.Tri; + +/** + * Computes a triangulation of each polygon in a {@link Geometry}. + * A polygon triangulation is a non-overlapping set of triangles which + * cover the polygon and have the same vertices as the polygon. + * The priority is on performance rather than triangulation quality, + * so that the output may contain many narrow triangles. + *

      + * Holes are handled by joining them to the shell to form a + * (self-touching) polygon shell with no holes. + * Although invalid, this can be triangulated effectively. + *

      + * For better-quality triangulation use {@link ConstrainedDelaunayTriangulator}. + * + * @see ConstrainedDelaunayTriangulator + * + * @author Martin Davis + * + */ +public class PolygonTriangulator { + + /** + * Computes a triangulation of each polygon in a geometry. + * + * @param geom a geometry containing polygons + * @return a GeometryCollection containing the triangle polygons + */ + public static Geometry triangulate(Geometry geom) { + PolygonTriangulator clipper = new PolygonTriangulator(geom); + return clipper.compute(); + } + + private final GeometryFactory geomFact; + private final Geometry inputGeom; + + /** + * Constructs a new triangulator. + * + * @param inputGeom the input geometry + */ + public PolygonTriangulator(Geometry inputGeom) { + geomFact = new GeometryFactory(); + this.inputGeom = inputGeom; + } + + private Geometry compute() { + @SuppressWarnings("unchecked") + List polys = PolygonExtracter.getPolygons(inputGeom); + List triList = new ArrayList(); + for (Polygon poly : polys) { + List polyTriList = triangulatePolygon(poly); + triList.addAll(polyTriList); + } + return Tri.toGeometry(triList, geomFact); + } + + /** + * Computes the triangulation of a single polygon + * + * @return GeometryCollection of triangular polygons + */ + private List triangulatePolygon(Polygon poly) { + /** + * Normalize to ensure that shell and holes have canonical orientation. + * + * TODO: perhaps better to just correct orientation of rings? + */ + Polygon polyNorm = (Polygon) poly.norm(); + Coordinate[] polyShell = PolygonHoleJoiner.join(polyNorm); + + List triList = PolygonEarClipper.triangulate(polyShell); + //Tri.validate(triList); + + return triList; + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/TriDelaunayImprover.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/TriDelaunayImprover.java new file mode 100755 index 0000000000..c57d03e278 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/TriDelaunayImprover.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import java.util.List; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.triangulate.quadedge.TrianglePredicate; +import org.locationtech.jts.triangulate.tri.Tri; +import org.locationtech.jts.triangulate.tri.TriangulationBuilder; + +/** + * Improves the quality of a triangulation of {@link Tri}s via + * iterated Delaunay flipping. + * This produces a Constrained Delaunay Triangulation + * with the constraints being the boundary of the input triangulation. + * + * @author mdavis + */ +class TriDelaunayImprover { + + /** + * Improves the quality of a triangulation of {@link Tri}s via + * iterated Delaunay flipping. + * The Tris are assumed to be linked into a Triangulation + * (e.g. via {@link TriangulationBuilder}). + * + * @param triList the list of Tris to flip. + */ + public static void improve(List triList) { + TriDelaunayImprover improver = new TriDelaunayImprover(triList); + improver.improve(); + } + + private static int MAX_ITERATION = 200; + private List triList; + + private TriDelaunayImprover(List triList) { + this.triList = triList; + } + + private void improve() { + for (int i = 0; i < MAX_ITERATION; i++) { + int improveCount = improveScan(triList); + //System.out.println("improve #" + i + " - count = " + improveCount); + if ( improveCount == 0 ) { + return; + } + } + } + + /** + * Improves a triangulation by examining pairs of adjacent triangles + * (forming a quadrilateral) and testing if flipping the diagonal of + * the quadrilateral would produce two new triangles with larger minimum + * interior angles. + * + * @return the number of flips that were made + */ + private int improveScan(List triList) { + int improveCount = 0; + for (int i = 0; i < triList.size() - 1; i++) { + Tri tri = triList.get(i); + for (int j = 0; j < 3; j++) { + //Tri neighb = tri.getAdjacent(j); + //tri.validateAdjacent(j); + if ( improveNonDelaunay(tri, j) ) { + // TODO: improve performance by only rescanning tris adjacent to flips? + improveCount++; + } + } + } + return improveCount; + } + + /** + * Does a flip of the common edge of two Tris if the Delaunay condition is not met. + * + * @param tri0 a Tri + * @param tri1 a Tri + * @return true if the triangles were flipped + */ + private boolean improveNonDelaunay(Tri tri, int index) { + if ( tri == null ) { + return false; + } + Tri tri1 = tri.getAdjacent(index); + if ( tri1 == null ) { + return false; + } + //tri0.validate(); + //tri1.validate(); + + + int index1 = tri1.getIndex(tri); + + Coordinate adj0 = tri.getCoordinate(index); + Coordinate adj1 = tri.getCoordinate(Tri.next(index)); + Coordinate opp0 = tri.getCoordinate(Tri.oppVertex(index)); + Coordinate opp1 = tri1.getCoordinate(Tri.oppVertex(index1)); + + /** + * The candidate new edge is opp0 - opp1. + * Check if it is inside the quadrilateral formed by the two triangles. + * This is the case if the quadrilateral is convex. + */ + if ( ! isConvex(adj0, adj1, opp0, opp1) ) { + return false; + } + + /** + * The candidate edge is inside the quadrilateral. Check to see if the flipping + * criteria is met. The flipping criteria is to flip if the two triangles are + * not Delaunay (i.e. one of the opposite vertices is in the circumcircle of the + * other triangle). + */ + if ( ! isDelaunay(adj0, adj1, opp0, opp1) ) { + tri.flip(index); + return true; + } + return false; + } + + /** + * Tests if the quadrilateral formed by two adjacent triangles is convex. + * opp0-adj0-adj1 and opp1-adj1-adj0 are the triangle corners + * and hence are known to be convex. + * The quadrilateral is convex if the other corners opp0-adj0-opp1 + * and opp1-adj1-opp0 have the same orientation (since at least one must be convex). + * + * @param adj0 adjacent edge vertex 0 + * @param adj1 adjacent edge vertex 1 + * @param opp0 corner vertex of triangle 0 + * @param opp1 corner vertex of triangle 1 + * @return true if the quadrilateral is convex + */ + private static boolean isConvex(Coordinate adj0, Coordinate adj1, Coordinate opp0, Coordinate opp1) { + int dir0 = Orientation.index(opp0, adj0, opp1); + int dir1 = Orientation.index(opp1, adj1, opp0); + boolean isConvex = dir0 == dir1; + return isConvex; + } + + /** + * Tests if either of a pair of adjacent triangles satisfy the Delaunay condition. + * The triangles are opp0-adj0-adj1 and opp1-adj1-adj0. + * The Delaunay condition is not met if one opposite vertex + * lies is in the circumcircle of the other triangle. + * + * @param adj0 adjacent edge vertex 0 + * @param adj1 adjacent edge vertex 1 + * @param opp0 corner vertex of triangle 0 + * @param opp1 corner vertex of triangle 1 + * @return true if the triangles are Delaunay + */ + private static boolean isDelaunay(Coordinate adj0, Coordinate adj1, Coordinate opp0, Coordinate opp1) { + if (isInCircle(adj0, adj1, opp0, opp1)) return false; + if (isInCircle(adj1, adj0, opp1, opp0)) return false; + return true; + } + + /** + * Tests whether a point p is in the circumcircle of a triangle abc + * (oriented clockwise). + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point + * + * @return true if the point is in the circumcircle + */ + private static boolean isInCircle(Coordinate a, Coordinate b, Coordinate c, Coordinate p) { + return TrianglePredicate.isInCircleRobust(a, c, b, p); + } + +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtree.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtree.java new file mode 100644 index 0000000000..f1fd182925 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtree.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.math.MathUtil; +import org.locationtech.jts.util.IntArrayList; + +/** + * A semi-static spatial index for points which occur + * in a spatially-coherent sequence. + * In particular, this is suitable for indexing the vertices + * of a {@link LineString} or {@link Polygon} ring. + *

      + * The index is constructed in a batch fashion on a given sequence of coordinates. + * Coordinates can be removed via the {@link #remove(int)} method. + *

      + * Note that this index queries only the individual points + * of the input coordinate sequence, + * not any line segments which might be lie between them. + * + * @author Martin Davis + * + */ +class VertexSequencePackedRtree { + + /** + * Number of items/nodes in a parent node. + * Determined empirically. Performance is not too sensitive to this. + */ + private static final int NODE_CAPACITY = 16; + + private Coordinate[] items; + private int[] levelOffset; + private int nodeCapacity = NODE_CAPACITY; + private Envelope[] bounds; + + /** + * Creates a new tree over the given sequence of coordinates. + * The sequence should be spatially coherent to provide query performance. + * + * @param pts a sequence of points + */ + public VertexSequencePackedRtree(Coordinate[] pts) { + this.items = pts; + build(); + } + + public Envelope[] getBounds() { + return bounds.clone(); + } + + private void build() { + levelOffset = computeLevelOffsets(); + bounds = createBounds(); + } + + /** + * Computes the level offsets. + * This is the position in the bounds array of each level. + * + * The levelOffsets array includes a sentinel value of offset[0] = 0. + * The top level is always of size 1, + * and so also indicates the total number of bounds. + * + * @return the level offsets + */ + private int[] computeLevelOffsets() { + IntArrayList offsets = new IntArrayList(); + offsets.add(0); + int levelSize = items.length; + int currOffset = 0; + do { + levelSize = levelNodeCount(levelSize); + currOffset += levelSize; + offsets.add(currOffset); + } while (levelSize > 1); + return offsets.toArray(); + } + + private int levelNodeCount(int numNodes) { + return MathUtil.ceil(numNodes, nodeCapacity); + } + + private Envelope[] createBounds() { + int boundsSize = levelOffset[levelOffset.length - 1] + 1; + Envelope[] bounds = new Envelope[boundsSize]; + fillItemBounds(bounds); + + for (int lvl = 1; lvl < levelOffset.length; lvl++) { + fillLevelBounds(lvl, bounds); + } + return bounds; + } + + private void fillLevelBounds(int lvl, Envelope[] bounds) { + int levelStart = levelOffset[lvl - 1]; + int levelEnd = levelOffset[lvl]; + int nodeStart = levelStart; + int levelBoundIndex = levelOffset[lvl]; + do { + int nodeEnd = MathUtil.clampMax(nodeStart + nodeCapacity, levelEnd); + bounds[levelBoundIndex++] = computeNodeEnvelope(bounds, nodeStart, nodeEnd); + nodeStart = nodeEnd; + } + while (nodeStart < levelEnd); + } + + private void fillItemBounds(Envelope[] bounds) { + int nodeStart = 0; + int boundIndex = 0; + do { + int nodeEnd = MathUtil.clampMax(nodeStart + nodeCapacity, items.length); + bounds[boundIndex++] = computeItemEnvelope(items, nodeStart, nodeEnd); + nodeStart = nodeEnd; + } + while (nodeStart < items.length); + } + + private static Envelope computeNodeEnvelope(Envelope[] bounds, int start, int end) { + Envelope env = new Envelope(); + for (int i = start; i < end; i++) { + env.expandToInclude(bounds[i]); + } + return env; + } + + private static Envelope computeItemEnvelope(Coordinate[] items, int start, int end) { + Envelope env = new Envelope(); + for (int i = start; i < end; i++) { + env.expandToInclude(items[i]); + } + return env; + } + + //------------------------ + + /** + * Queries the index to find all items which intersect an extent. + * The query result is a list of the indices of input coordinates + * which intersect the extent. + * + * @param queryEnv the query extent + * @return an array of the indices of the input coordinates + */ + public int[] query(Envelope queryEnv) { + IntArrayList resultList = new IntArrayList(); + int level = levelOffset.length - 1; + queryNode(queryEnv, level, 0, resultList); + int[] result = resultList.toArray(); + return result; + } + + private void queryNode(Envelope queryEnv, int level, int nodeIndex, IntArrayList resultList) { + int boundsIndex = levelOffset[level] + nodeIndex; + Envelope nodeEnv = bounds[boundsIndex]; + //--- node is empty + if (nodeEnv == null) + return; + if (! queryEnv.intersects(nodeEnv)) + return; + + int childNodeIndex = nodeIndex * nodeCapacity; + if (level == 0) { + queryItemRange(queryEnv, childNodeIndex, resultList); + } + else { + queryNodeRange(queryEnv, level - 1, childNodeIndex, resultList); + } + } + + private void queryNodeRange(Envelope queryEnv, int level, int nodeStartIndex, IntArrayList resultList) { + int levelMax = levelSize(level); + for (int i = 0; i < nodeCapacity; i++) { + int index = nodeStartIndex + i; + if (index >= levelMax) + return; + queryNode(queryEnv, level, index, resultList); + } + } + + private int levelSize(int level) { + return levelOffset[level + 1] - levelOffset[level]; + } + + private void queryItemRange(Envelope queryEnv, int itemIndex, IntArrayList resultList) { + for (int i = 0; i < nodeCapacity; i++) { + int index = itemIndex + i; + if (index >= items.length) + return; + Coordinate p = items[index]; + if (p != null + && queryEnv.contains(p)) + resultList.add(index); + } + } + + //------------------------ + + /** + * Removes the input item at the given index from the spatial index. + * + * @param index the index of the item in the input + */ + public void remove(int index) { + items[index] = null; + + //--- prune the item parent node if all its items are removed + int nodeIndex = index / nodeCapacity; + if (! isItemsNodeEmpty(nodeIndex)) + return; + + bounds[nodeIndex] = null; + + if (levelOffset.length <= 2) + return; + + //-- prune the node parent if all children removed + int nodeLevelIndex = nodeIndex / nodeCapacity; + if (! isNodeEmpty(1, nodeLevelIndex)) + return; + int nodeIndex1 = levelOffset[1] + nodeLevelIndex; + bounds[nodeIndex1] = null; + + //TODO: propagate removal up the tree nodes? + } + + private boolean isNodeEmpty(int level, int index) { + int start = index * nodeCapacity; + int end = MathUtil.clampMax(start + nodeCapacity, levelOffset[level]); + for (int i = start; i < end; i++) { + if (bounds[i] != null) return false; + } + return true; + } + + private boolean isItemsNodeEmpty(int nodeIndex) { + int start = nodeIndex * nodeCapacity; + int end = MathUtil.clampMax(start + nodeCapacity, items.length); + for (int i = start; i < end; i++) { + if (items[i] != null) return false; + } + return true; + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/package-info.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/package-info.java new file mode 100644 index 0000000000..43d687cc57 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes for triangulating polygons. + * {@link ConstrainedDelaunayTriangulator} can be used to provide high-quality + * near-Delaunay triangulations of polygonal geometry. + * The {@link PolygonTriangulator} produces lower-quality but faster triangulations. + */ +package org.locationtech.jts.triangulate.polygon; \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java new file mode 100755 index 0000000000..337905e815 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.tri; + +import java.util.List; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.algorithm.RobustLineIntersector; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.WKTWriter; +import org.locationtech.jts.util.Assert; + +/** + * A memory-efficient representation of a triangle in a triangulation. + * Contains three vertices, and links to adjacent Tris for each edge. + * Tris are constructed independently, and if needed linked + * into a triangulation using {@link TriangulationBuilder}. + * + * @author mdavis + * + */ +public class Tri { + + /** + * Creates a {@link GeometryCollection} of {@link Polygon}s + * representing the triangles in a list. + * + * @param triList a list of Tris + * @param geomFact the GeometryFactory to use + * @return the polygons for the triangles + */ + public static Geometry toGeometry(List triList, GeometryFactory geomFact) { + Geometry[] geoms = new Geometry[triList.size()]; + for (int i = 0; i < triList.size(); i++) { + geoms[i] = triList.get(i).toPolygon(geomFact); + } + return geomFact.createGeometryCollection(geoms); + } + + /** + * Validates a list of Tris. + * + * @param triList the tris to validate + */ + public static void validate(List triList) { + for (Tri tri : triList) { + tri.validate(); + } + } + + /** + * Creates a triangle with the given vertices. + * The vertices should be oriented clockwise. + * + * @param p0 the first triangle vertex + * @param p1 the second triangle vertex + * @param p2 the third triangle vertex + * @return the created triangle + */ + public static Tri create(Coordinate p0, Coordinate p1, Coordinate p2) { + return new Tri(p0, p1, p2); + } + + /** + * Creates a triangle from an array with three vertex coordinates. + * The vertices should be oriented clockwise. + * + * @param pts the array of vertex coordinates + * @return the created triangle + */ + public static Tri create(Coordinate[] pts) { + return new Tri(pts[0], pts[1], pts[2]); + } + + private Coordinate p0; + private Coordinate p1; + private Coordinate p2; + + /** + * triN is the adjacent triangle across the edge pN - pNN. + * pNN is the next vertex CW from pN. + */ + private Tri tri0; + private Tri tri1; + private Tri tri2; + + /** + * Creates a triangle with the given vertices. + * The vertices should be oriented clockwise. + * + * @param p0 the first triangle vertex + * @param p1 the second triangle vertex + * @param p2 the third triangle vertex + */ + public Tri(Coordinate p0, Coordinate p1, Coordinate p2) { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + //Assert.isTrue( Orientation.CLOCKWISE != Orientation.index(p0, p1, p2), "Tri is not oriented correctly"); + } + + /** + * Sets the adjacent triangles. + * The vertices of the adjacent triangles are + * assumed to match the appropriate vertices in this triangle. + * + * @param tri0 the triangle adjacent to edge 0 + * @param tri1 the triangle adjacent to edge 1 + * @param tri2 the triangle adjacent to edge 2 + */ + public void setAdjacent(Tri tri0, Tri tri1, Tri tri2) { + this.tri0 = tri0; + this.tri1 = tri1; + this.tri2 = tri2; + } + + /** + * Sets the triangle adjacent to the edge originating + * at a given vertex. + * The vertices of the adjacent triangles are + * assumed to match the appropriate vertices in this triangle. + * + * @param pt the edge start point + * @param tri the adjacent triangle + */ + public void setAdjacent(Coordinate pt, Tri tri) { + int index = getIndex(pt); + setTri(index, tri); + // TODO: validate that tri is adjacent at the edge specified + } + + /** + * Sets the triangle adjacent to an edge. + * The vertices of the adjacent triangle are + * assumed to match the appropriate vertices in this triangle. + * + * @param edgeIndex the edge triangle is adjacent to + * @param tri the adjacent triangle + */ + public void setTri(int edgeIndex, Tri tri) { + switch (edgeIndex) { + case 0: tri0 = tri; return; + case 1: tri1 = tri; return; + case 2: tri2 = tri; return; + } + Assert.shouldNeverReachHere(); + } + + private void setCoordinates(Coordinate p0, Coordinate p1, Coordinate p2) { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + //Assert.isTrue( Orientation.CLOCKWISE != Orientation.index(p0, p1, p2), "Tri is not oriented correctly"); + } + + /** + * Spits a triangle by a point located inside the triangle. + * Creates the three new resulting triangles with adjacent links + * set correctly. + * Returns the new triangle whose 0'th vertex is the splitting point. + * + * @param p the point to insert + * @return the new triangle whose 0'th vertex is p + */ + public Tri split(Coordinate p) { + Tri tt0 = new Tri(p, p0, p1); + Tri tt1 = new Tri(p, p1, p2); + Tri tt2 = new Tri(p, p2, p0); + tt0.setAdjacent(tt2, tri0, tt1); + tt1.setAdjacent(tt0, tri1, tt2); + tt2.setAdjacent(tt1, tri2, tt0); + return tt0; + } + + /** + * Interchanges the vertices of this triangle and a neighbor + * so that their common edge + * becomes the the other diagonal of the quadrilateral they form. + * Neighbour triangle links are modified accordingly. + * + * @param index the index of the adjacent tri to flip with + */ + public void flip(int index) { + Tri tri = getAdjacent(index); + int index1 = tri.getIndex(this); + + Coordinate adj0 = getCoordinate(index); + Coordinate adj1 = getCoordinate(next(index)); + Coordinate opp0 = getCoordinate(oppVertex(index)); + Coordinate opp1 = tri.getCoordinate(oppVertex(index1)); + + flip(tri, index, index1, adj0, adj1, opp0, opp1); + } + + private void flip(Tri tri, int index0, int index1, Coordinate adj0, Coordinate adj1, Coordinate opp0, Coordinate opp1) { + //System.out.println("Flipping: " + this + " -> " + tri); + + //validate(); + //tri.validate(); + + this.setCoordinates(opp1, opp0, adj0); + tri.setCoordinates(opp0, opp1, adj1); + /** + * Order: 0: opp0-adj0 edge, 1: opp0-adj1 edge, + * 2: opp1-adj0 edge, 3: opp1-adj1 edge + */ + Tri[] adjacent = getAdjacentTris(tri, index0, index1); + this.setAdjacent(tri, adjacent[0], adjacent[2]); + //--- update the adjacent triangles with new adjacency + if ( adjacent[2] != null ) { + adjacent[2].replace(tri, this); + } + tri.setAdjacent(this, adjacent[3], adjacent[1]); + if ( adjacent[1] != null ) { + adjacent[1].replace(this, tri); + } + //validate(); + //tri.validate(); + } + + /** + * Replaces an adjacent triangle with a different one. + * + * @param triOld an adjacent triangle + * @param triNew the triangle to replace it with + */ + private void replace(Tri triOld, Tri triNew) { + if ( tri0 != null && tri0 == triOld ) { + tri0 = triNew; + } else if ( tri1 != null && tri1 == triOld ) { + tri1 = triNew; + } else if ( tri2 != null && tri2 == triOld ) { + tri2 = triNew; + } + } + + /** + * Gets the triangles adjacent to the quadrilateral + * formed by this triangle and an adjacent one. + * The triangles are returned in the following order: + *

      + * Order: 0: opp0-adj0 edge, 1: opp0-adj1 edge, + * 2: opp1-adj0 edge, 3: opp1-adj1 edge + * + * @param tri1 an adjacent triangle + * @param index the index of the common edge in this triangle + * @param index1 the index of the common edge in the adjacent triangle + * @return + */ + private Tri[] getAdjacentTris(Tri triAdj, int index, int indexAdj) { + Tri[] adj = new Tri[4]; + adj[0] = getAdjacent(prev(index)); + adj[1] = getAdjacent(next(index)); + adj[2] = triAdj.getAdjacent(next(indexAdj)); + adj[3] = triAdj.getAdjacent(prev(indexAdj)); + return adj; + } + + /** + * Validates that a tri is correct. + * Currently just checks that orientation is CW. + * + * @throw IllegalArgumentException if tri is not valid + */ + public void validate() { + if ( Orientation.CLOCKWISE != Orientation.index(p0, p1, p2) ) { + throw new IllegalArgumentException("Tri is not oriented correctly"); + } + + validateAdjacent(0); + validateAdjacent(1); + validateAdjacent(2); + } + + /** + * Validates that the vertices of an adjacent linked triangle are correct. + * + * @param index the index of the adjacent triangle + */ + public void validateAdjacent(int index) { + Tri tri = getAdjacent(index); + if (tri == null) return; + + assert(this.isAdjacent(tri)); + assert(tri.isAdjacent(this)); + + Coordinate e0 = getCoordinate(index); + Coordinate e1 = getCoordinate(next(index)); + int indexNeighbor = tri.getIndex(this); + Coordinate n0 = tri.getCoordinate(indexNeighbor); + Coordinate n1 = tri.getCoordinate(next(indexNeighbor)); + Assert.isTrue(e0.equals2D(n1), "Edge coord not equal"); + Assert.isTrue(e1.equals2D(n0), "Edge coord not equal"); + + //--- check that no edges cross + RobustLineIntersector li = new RobustLineIntersector(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + Coordinate p00 = getCoordinate(i); + Coordinate p01 = getCoordinate(next(i)); + Coordinate p10 = tri.getCoordinate(j); + Coordinate p11 = tri.getCoordinate(next(j)); + li.computeIntersection(p00, p01, p10, p11); + assert(! li.isProper()); + } + } + } + + /** + * Gets the start and end vertex of the edge adjacent to another triangle. + * + * @param neighbor + * @return + */ + /* + //TODO: define when needed + public Coordinate[] getEdge(Tri neighbor) { + int index = getIndex(neighbor); + int next = next(index); + + Coordinate e0 = getCoordinate(index); + Coordinate e1 = getCoordinate(next); + assert (neighbor.hasCoordinate(e0)); + assert (neighbor.hasCoordinate(e1)); + int iN = neighbor.getIndex(e0); + int iNPrev = prev(iN); + assert (neighbor.getIndex(e1) == iNPrev); + + return new Coordinate[] { getCoordinate(index), getCoordinate(next) }; + } + + public Coordinate getEdgeStart(int i) { + return getCoordinate(i); + } + + public Coordinate getEdgeEnd(int i) { + return getCoordinate(next(i)); + } + + public boolean hasCoordinate(Coordinate v) { + if ( p0.equals(v) || p1.equals(v) || p2.equals(v) ) { + return true; + } + return false; + } + */ + + /** + * Gets the coordinate for a vertex. + * This is the start vertex of the edge. + * + * @param index the vertex (edge) index + * @return the vertex coordinate + */ + public Coordinate getCoordinate(int index) { + if ( index == 0 ) { + return p0; + } + if ( index == 1 ) { + return p1; + } + return p2; + } + + /** + * Gets the index of the triangle vertex which has a given coordinate (if any). + * This is also the index of the edge which originates at the vertex. + * + * @param p the coordinate to find + * @return the vertex index, or -1 if it is not in the triangle + */ + public int getIndex(Coordinate p) { + if ( p0.equals2D(p) ) + return 0; + if ( p1.equals2D(p) ) + return 1; + if ( p2.equals2D(p) ) + return 2; + return -1; + } + + /** + * Gets the edge index which a triangle is adjacent to (if any), + * based on the adjacent triangle link. + * + * @param tri the tri to find + * @return the index of the edge adjacent to the triangle, or -1 if not found + */ + public int getIndex(Tri tri) { + if ( tri0 == tri ) + return 0; + if ( tri1 == tri ) + return 1; + if ( tri2 == tri ) + return 2; + return -1; + } + + /** + * Gets the triangle adjacent to an edge. + * + * @param index the edge index + * @return the adjacent triangle (may be null) + */ + public Tri getAdjacent(int index) { + switch(index) { + case 0: return tri0; + case 1: return tri1; + case 2: return tri2; + } + Assert.shouldNeverReachHere(); + return null; + } + + /** + * Tests if there is an adjacent triangle to an edge. + * + * @param index the edge index + * @return true if there is a triangle adjacent to edge + */ + public boolean hasAdjacent(int index) { + return null != getAdjacent(index); + } + + /** + * Tests if a triangle is adjacent to some edge of this triangle. + * + * @param tri the triangle to test + * @return true if the triangle is adjacent + * @see getIndex(Tri) + */ + public boolean isAdjacent(Tri tri) { + return getIndex(tri) >= 0; + } + + /** + * Computes the number of triangle adjacent to this triangle. + * This is a number in the range [0,2]. + * + * @return the number of adjacent triangles + */ + public int numAdjacent() { + int num = 0; + if ( tri0 != null ) + num++; + if ( tri1 != null ) + num++; + if ( tri2 != null ) + num++; + return num; + } + + /** + * Computes the vertex or edge index which is the next one + * (clockwise) around the triangle. + * + * @param index the index + * @return the next index value + */ + public static int next(int index) { + switch (index) { + case 0: return 1; + case 1: return 2; + case 2: return 0; + } + return -1; + } + + /** + * Computes the vertex or edge index which is the previous one + * (counter-clockwise) around the triangle. + * + * @param index the index + * @return the previous index value + */ + public static int prev(int index) { + switch (index) { + case 0: return 2; + case 1: return 0; + case 2: return 1; + } + return -1; + } + + /** + * Gets the index of the vertex opposite an edge. + * + * @param edgeIndex the edge index + * @return the index of the opposite vertex + */ + public static int oppVertex(int edgeIndex) { + return prev(edgeIndex); + } + + /** + * Gets the index of the edge opposite a vertex. + * + * @param vertexIndex the index of the vertex + * @return the index of the opposite edge + */ + public static int oppEdge(int vertexIndex) { + return next(vertexIndex); + } + + /** + * Computes a coordinate for the midpoint of a triangle edge. + * + * @param edgeIndex the edge index + * @return the midpoint of the triangle edge + */ + public Coordinate midpoint(int edgeIndex) { + Coordinate p0 = getCoordinate(edgeIndex); + Coordinate p1 = getCoordinate(next(edgeIndex)); + double midX = (p0.getX() + p1.getX()) / 2; + double midY = (p0.getY() + p1.getY()) / 2; + return new Coordinate(midX, midY); + } + + /** + * Creates a {@link Polygon} representing this triangle. + * + * @param geomFact the geometry factory + * @return a polygon + */ + public Polygon toPolygon(GeometryFactory geomFact) { + return geomFact.createPolygon( + geomFact.createLinearRing(new Coordinate[] { p0.copy(), p1.copy(), p2.copy(), p0.copy() }), null); + } + + @Override + public String toString() { + return String.format("POLYGON ((%s, %s, %s, %s))", + WKTWriter.format(p0), WKTWriter.format(p1), WKTWriter.format(p2), + WKTWriter.format(p0)); + } + +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriEdge.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriEdge.java new file mode 100755 index 0000000000..e7fe3a42b0 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriEdge.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.tri; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.io.WKTWriter; + +/** + * Represents an edge in a {@link Tri}, + * to be used as a key for looking up Tris + * while building a triangulation. + * The edge value is normalized to allow lookup + * of adjacent triangles. + * + * @author mdavis + * + */ +class TriEdge { + public Coordinate p0; + public Coordinate p1; + + public TriEdge(Coordinate a, Coordinate b) { + p0 = a; + p1 = b; + normalize(); + } + + private void normalize() { + if ( p0.compareTo(p1) < 0 ) { + Coordinate tmp = p0; + p0 = p1; + p1 = tmp; + } + } + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + Coordinate.hashCode(p0.x); + result = 37 * result + Coordinate.hashCode(p1.x); + result = 37 * result + Coordinate.hashCode(p0.y); + result = 37 * result + Coordinate.hashCode(p1.y); + return result; + } + + @Override + public boolean equals(Object arg) { + if ( !(arg instanceof TriEdge) ) + return false; + TriEdge other = (TriEdge) arg; + if ( p0.equals(other.p0) && p1.equals(other.p1) ) + return true; + return false; + } + + public String toString() { + return WKTWriter.toLineString(new Coordinate[] { p0, p1}); + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriangulationBuilder.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriangulationBuilder.java new file mode 100755 index 0000000000..a4828fc9d4 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/TriangulationBuilder.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.tri; + +import java.util.HashMap; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; + +/** + * Builds a triangulation from a set of {@link Tri}s + * by populating the links to adjacent triangles. + * + * @author mdavis + * + */ +public class TriangulationBuilder { + + /** + * Computes the triangulation of a set of {@link Tri}s. + * + * @param triList the list of Tris + */ + public static void build(List triList) { + new TriangulationBuilder(triList); + } + + private HashMap triMap; + + /** + * Computes the triangulation of a set of {@link Tri}s. + * + * @param triList the list of Tris + */ + private TriangulationBuilder(List triList) { + triMap = new HashMap(); + for (Tri tri : triList) { + add(tri); + } + } + + private Tri find(Coordinate p0, Coordinate p1) { + TriEdge e = new TriEdge(p0, p1); + return triMap.get(e); + } + + private void add(Tri tri) { + Coordinate p0 = tri.getCoordinate(0); + Coordinate p1 = tri.getCoordinate(1); + Coordinate p2 = tri.getCoordinate(2); + + // get adjacent triangles, if any + Tri n0 = find(p0, p1); + Tri n1 = find(p1, p2); + Tri n2 = find(p2, p0); + + tri.setAdjacent(n0, n1, n2); + addAdjacent(tri, n0, p0, p1); + addAdjacent(tri, n1, p1, p2); + addAdjacent(tri, n2, p2, p0); + } + + private void addAdjacent(Tri tri, Tri adj, Coordinate p0, Coordinate p1) { + /** + * If adjacent is null, this tri is first one to be recorded for edge + */ + if (adj == null) { + triMap.put(new TriEdge(p0, p1), tri); + return; + } + adj.setAdjacent(p1, tri); + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/package-info.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/package-info.java new file mode 100644 index 0000000000..e6b47f6730 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 Martin Davis + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Classes for representing a planar triangulation as a set of linked triangles. + * Triangles are represented by memory-efficient {@link Tri} objects. + * A set of triangles can be linked into a triangulation using {@link TriangulationBuilder}. + */ +package org.locationtech.jts.triangulate.tri; \ No newline at end of file diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/TriangleTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/TriangleTest.java index aa10d55340..938f46d516 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/TriangleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/TriangleTest.java @@ -15,12 +15,13 @@ import junit.framework.TestCase; import junit.textui.TestRunner; +import test.jts.GeometryTestCase; /** * @version 1.7 */ -public class TriangleTest extends TestCase +public class TriangleTest extends GeometryTestCase { private PrecisionModel precisionModel = new PrecisionModel(); @@ -155,7 +156,7 @@ public void testCentroid() throws Exception checkCentroid("POLYGON((10 10, 20 10, 15 20, 10 10))", new Coordinate( (10.0 + 20.0 + 15.0) / 3.0, (10.0 + 10.0 + 20.0) / 3.0)); } - + public void checkCentroid(String wkt, Coordinate expectedValue) throws Exception { @@ -222,5 +223,40 @@ public void checkLongestSideLength(String wkt, double expectedValue) //System.out.println("(Instance) longestSideLength = " + length); assertEquals(expectedValue, length, 0.00000001); } + + //=============================================================== + + public void testIsCCW() { + checkIsCCW("POLYGON ((30 90, 80 50, 20 20, 30 90))", false); + checkIsCCW("POLYGON ((90 90, 20 40, 10 10, 90 90))", true); + } + + public void checkIsCCW(String wkt, boolean expectedValue) + { + Coordinate[] pt = read(wkt).getCoordinates(); + boolean actual = Triangle.isCCW(pt[0], pt[1], pt[2]); + assertEquals(expectedValue, actual); + } + + //=============================================================== + + public void testIntersects() { + checkIntersects("POLYGON ((30 90, 80 50, 20 20, 30 90))", "POINT (70 20)", false); + // triangle vertex + checkIntersects("POLYGON ((30 90, 80 50, 20 20, 30 90))", "POINT (30 90)", true); + checkIntersects("POLYGON ((30 90, 80 50, 20 20, 30 90))", "POINT (40 40)", true); + + // on an edge + checkIntersects("POLYGON ((30 90, 70 50, 71.5 16.5, 30 90))", "POINT (50 70)", true); + } + + public void checkIntersects(String wktTri, String wktPt, boolean expectedValue) + { + Coordinate[] tri = read(wktTri).getCoordinates(); + Coordinate pt = read(wktPt).getCoordinate(); + + boolean actual = Triangle.intersects(tri[0], tri[1], tri[2], pt); + assertEquals(expectedValue, actual); + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulatorTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulatorTest.java new file mode 100644 index 0000000000..fe3e220f67 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulatorTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class ConstrainedDelaunayTriangulatorTest extends GeometryTestCase { + + public static void main(String args[]) { + TestRunner.run(ConstrainedDelaunayTriangulatorTest.class); + } + + public ConstrainedDelaunayTriangulatorTest(String name) { + super(name); + } + + public void testQuad() { + checkTri("POLYGON ((10 10, 20 40, 90 90, 90 10, 10 10))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 10, 10 10)), POLYGON ((90 90, 20 40, 90 10, 90 90)))"); + } + + public void testPent() { + checkTri("POLYGON ((10 10, 20 40, 90 90, 100 50, 90 10, 10 10))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 10, 10 10)), POLYGON ((90 90, 20 40, 100 50, 90 90)), POLYGON ((100 50, 20 40, 90 10, 100 50)))"); + } + + public void testHoleCW() { + checkTri("POLYGON ((10 90, 90 90, 90 20, 10 10, 10 90), (30 70, 80 70, 50 30, 30 70))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 10 90, 30 70, 10 10)), POLYGON ((10 10, 30 70, 50 30, 10 10)), POLYGON ((80 70, 30 70, 90 90, 80 70)), POLYGON ((10 90, 30 70, 90 90, 10 90)), POLYGON ((80 70, 90 90, 90 20, 80 70)), POLYGON ((90 20, 10 10, 50 30, 90 20)), POLYGON ((90 20, 50 30, 80 70, 90 20)))"); + } + + public void testMultiPolygon() { + checkTri("MULTIPOLYGON (((10 10, 20 50, 50 50, 40 20, 10 10)), ((20 60, 60 60, 90 20, 90 90, 20 60)), ((10 90, 10 70, 40 70, 50 90, 10 90)))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 50, 40 20, 10 10)), POLYGON ((50 50, 20 50, 40 20, 50 50)), POLYGON ((90 90, 90 20, 60 60, 90 90)), POLYGON ((90 90, 60 60, 20 60, 90 90)), POLYGON ((10 70, 10 90, 40 70, 10 70)), POLYGON ((50 90, 10 90, 40 70, 50 90)))"); + } + + public void testFail() { + checkTri( + "POLYGON ((110 170, 138 272, 145 286, 152 296, 160 307, 303 307, 314 301, 332 287, 343 278, 352 270, 385 99, 374 89, 359 79, 178 89, 167 91, 153 99, 146 107, 173 157, 182 163, 191 170, 199 176, 208 184, 218 194, 226 203, 198 252, 188 247, 182 239, 175 231, 167 223, 161 213, 156 203, 155 198, 110 170))" + ); + } + + private void checkTri(String wkt, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConstrainedDelaunayTriangulator.triangulate(geom); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } + + /** + * Check union of result equals original geom + * @param wkt + */ + private void checkTri(String wkt) { + Geometry geom = read(wkt); + Geometry actual = ConstrainedDelaunayTriangulator.triangulate(geom); + Geometry actualUnion = actual.union(); + checkEqual(geom, actualUnion); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java new file mode 100644 index 0000000000..c77c663603 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.triangulate.polygon.PolygonTriangulator; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class PolygonTriangulatorTest extends GeometryTestCase { + + public static void main(String args[]) { + TestRunner.run(PolygonTriangulatorTest.class); + } + + public PolygonTriangulatorTest(String name) { + super(name); + } + + public void testQuad() { + checkTri("POLYGON ((10 10, 20 40, 90 90, 90 10, 10 10))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 90, 10 10)), POLYGON ((90 90, 90 10, 10 10, 90 90)))"); + } + + public void testPent() { + checkTri("POLYGON ((10 10, 20 40, 90 90, 100 50, 90 10, 10 10))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 90, 10 10)), POLYGON ((90 90, 100 50, 90 10, 90 90)), POLYGON ((90 10, 10 10, 90 90, 90 10)))"); + } + + public void testHoleCW() { + checkTri("POLYGON ((10 90, 90 90, 90 20, 10 10, 10 90), (30 70, 80 70, 50 30, 30 70))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 10 90, 30 70, 10 10)), POLYGON ((80 70, 30 70, 10 90, 80 70)), POLYGON ((10 10, 30 70, 50 30, 10 10)), POLYGON ((80 70, 10 90, 90 90, 80 70)), POLYGON ((90 20, 10 10, 50 30, 90 20)), POLYGON ((80 70, 90 90, 90 20, 80 70)), POLYGON ((90 20, 50 30, 80 70, 90 20)))"); + } + + public void testTouchingHoles() { + checkTri("POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (20 80, 50 70, 30 30, 20 80), (70 20, 50 70, 80 80, 70 20))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 10 90, 20 80, 10 10)), POLYGON ((50 70, 20 80, 10 90, 50 70)), POLYGON ((10 10, 20 80, 30 30, 10 10)), POLYGON ((30 30, 50 70, 70 20, 30 30)), POLYGON ((80 80, 50 70, 10 90, 80 80)), POLYGON ((90 10, 10 10, 30 30, 90 10)), POLYGON ((80 80, 10 90, 90 90, 80 80)), POLYGON ((90 10, 30 30, 70 20, 90 10)), POLYGON ((70 20, 80 80, 90 90, 70 20)), POLYGON ((90 90, 90 10, 70 20, 90 90)))"); + } + + public void testRepeatedPoints() { + checkTri("POLYGON ((71 195, 178 335, 178 335, 239 185, 380 210, 290 60, 110 70, 71 195))" + ,"GEOMETRYCOLLECTION (POLYGON ((71 195, 178 335, 239 185, 71 195)), POLYGON ((71 195, 239 185, 290 60, 71 195)), POLYGON ((71 195, 290 60, 110 70, 71 195)), POLYGON ((239 185, 380 210, 290 60, 239 185)))"); + } + + public void testMultiPolygon() { + checkTri("MULTIPOLYGON (((10 10, 20 50, 50 50, 40 20, 10 10)), ((20 60, 60 60, 90 20, 90 90, 20 60)), ((10 90, 10 70, 40 70, 50 90, 10 90)))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 50, 50 50, 10 10)), POLYGON ((50 50, 40 20, 10 10, 50 50)), POLYGON ((90 90, 90 20, 60 60, 90 90)), POLYGON ((60 60, 20 60, 90 90, 60 60)), POLYGON ((10 70, 10 90, 50 90, 10 70)), POLYGON ((50 90, 40 70, 10 70, 50 90)))"); + } + + public void testCeeShape() { + checkTri( + "POLYGON ((110 170, 138 272, 145 286, 152 296, 160 307, 303 307, 314 301, 332 287, 343 278, 352 270, 385 99, 374 89, 359 79, 178 89, 167 91, 153 99, 146 107, 173 157, 182 163, 191 170, 199 176, 208 184, 218 194, 226 203, 198 252, 188 247, 182 239, 175 231, 167 223, 161 213, 156 203, 155 198, 110 170))" + ); + } + + private void checkTri(String wkt, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = PolygonTriangulator.triangulate(geom); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } + + /** + * Check union of result equals original geom + * @param wkt + */ + private void checkTri(String wkt) { + Geometry geom = read(wkt); + Geometry actual = PolygonTriangulator.triangulate(geom); + Geometry actualUnion = actual.union(); + checkEqual(geom, actualUnion); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtreeTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtreeTest.java new file mode 100644 index 0000000000..d6e1ed237e --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/VertexSequencePackedRtreeTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.triangulate.polygon; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.triangulate.polygon.VertexSequencePackedRtree; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +public class VertexSequencePackedRtreeTest extends TestCase { + public static void main(String args[]) { + TestRunner.run(VertexSequencePackedRtreeTest.class); + } + + public VertexSequencePackedRtreeTest(String name) { + super(name); + } + + public void test1() { + VertexSequencePackedRtree tree = createSPRtree(1,1); + checkQuery(tree, 1,1,4,4, result( 0 )); + } + + public void test2() { + VertexSequencePackedRtree tree = createSPRtree(0,0, 1,1); + checkQuery(tree, 1,1,4,4, result( 1 )); + } + + public void test6() { + VertexSequencePackedRtree tree = createSPRtree(0,0, 1,1, 2,2, 3,3, 4,4, 5,5); + checkQuery(tree, 2,2,4,4, result( 2,3,4 )); + checkQuery(tree, 0,0,0,0, result( 0 )); + } + + public void test10() { + VertexSequencePackedRtree tree = createSPRtree(0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7, 8,8, 9,9, 10,10); + checkQuery(tree, 2,2,4,4, result( 2,3,4 )); + checkQuery(tree, 7,7,8,8, result( 7,8 )); + checkQuery(tree, 0,0,0,0, result( 0 )); + } + + public void test6WithDups() { + VertexSequencePackedRtree tree = createSPRtree(0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 4,4, 3,3, 2,2, 1,1, 0,0); + checkQuery(tree, 2,2,4,4, result( 2,3,4, 6, 7, 8 )); + checkQuery(tree, 0,0,0,0, result( 0, 10 )); + } + + private void checkQuery(VertexSequencePackedRtree tree, + double xmin, double ymin, double xmax, double ymax, int[] expected) { + Envelope env = new Envelope(xmin, xmax, ymin, ymax); + int[] result = tree.query(env); + assertEquals(expected.length, result.length); + assertTrue( isEqualResult(expected, result) ); + } + + private boolean isEqualResult(int[] expected, int[] result) { + for (int i = 0; i < result.length; i++) { + if (expected[i] != result[i]) + return false; + + } + return true; + } + + private int[] result(int... i) { + return i; + } + + private VertexSequencePackedRtree createSPRtree(int... ords) { + int numCoord = ords.length / 2; + Coordinate[] pt = new Coordinate[numCoord]; + for (int i = 0 ; i < numCoord; i++) { + pt[i] = new Coordinate(ords[2*i], ords[2*i+1]); + } + return new VertexSequencePackedRtree(pt); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/tri/TriTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/tri/TriTest.java new file mode 100644 index 0000000000..466f7efbd4 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/tri/TriTest.java @@ -0,0 +1,73 @@ +package org.locationtech.jts.triangulate.tri; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.Coordinate; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class TriTest extends GeometryTestCase { + + public static void main(String args[]) { + TestRunner.run(TriTest.class); + } + + private static Tri triCentre = createSimpleTriangulation(); + private static Tri tri0; + private static Tri tri1; + private static Tri tri2; + + public TriTest(String name) { + super(name); + } + + public void testAdjacent() { + assertTrue(tri0 == triCentre.getAdjacent(0)); + assertTrue(tri1 == triCentre.getAdjacent(1)); + assertTrue(tri2 == triCentre.getAdjacent(2)); + } + + public void testMidpoint() { + Tri tri = tri(0,0, 0,10, 10,0 ); + checkEqualXY(new Coordinate(0, 5), tri.midpoint(0)); + checkEqualXY(new Coordinate(5, 5), tri.midpoint(1)); + checkEqualXY(new Coordinate(5, 0), tri.midpoint(2)); + } + + public void testCoordinateIndex() { + Tri tri = tri(0,0, 0,10, 10,0 ); + assertEquals(0, tri.getIndex(new Coordinate(0,0))); + assertEquals(1, tri.getIndex(new Coordinate(0,10))); + assertEquals(2, tri.getIndex(new Coordinate(10,0))); + } + + private static Tri tri(double x0, double y0, double x1, double y1, double x2, double y2) { + Tri tri = Tri.create( + new Coordinate(x0, y0), + new Coordinate(x1, y1), + new Coordinate(x2, y2)); + assertTrue( Orientation.CLOCKWISE == Orientation.index( + tri.getCoordinate(0), tri.getCoordinate(1), tri.getCoordinate(2) )); + return tri; + } + + private static Tri createSimpleTriangulation() { + Tri tri = tri(10,10, 10,20, 20,10 ); + tri0 = tri(10,20, 10,10, 0,10 ); + tri1 = tri(20,10, 10,20, 20,20 ); + tri2 = tri(10,10, 20,10, 10,0 ); + build(tri, tri0, tri1, tri2); + return tri; + } + + private static void build(Tri... tri) { + List triList = new ArrayList(); + for (int i = 0; i < tri.length; i++) { + triList.add(tri[i]); + } + TriangulationBuilder.build(triList); + } +} From 540f3ce98e1acc102e96adce872cd708d5bccbcb Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Sep 2021 12:43:21 -0700 Subject: [PATCH 078/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 697926a4ed..cdf5fe4a9d 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -24,6 +24,8 @@ Distributions for older JTS versions can be obtained at the ### Functionality Improvements * Improve `GeometryFixer` behaviour for holes outside polygons (#772) +* Add `ConstrainedDelaunayTriangulator` and `PolygonTriangulator` (#775) +* Add `Tri` data structure for representing triangulations (#775) # Version 1.18.2 From c8b6a8d465dba9f4a92bfb333edf05c740158840 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 16 Sep 2021 15:35:27 -0700 Subject: [PATCH 079/275] Refactor triangulation classes Add ApproximateMedialAxis class to LAB Signed-off-by: Martin Davis --- .../ConstrainedDelaunayTriangulator.java | 30 +- .../polygon/PolygonTriangulator.java | 30 +- .../algorithm/axis/ApproximateMedialAxis.java | 523 ++++++++++++++++++ .../axis/ApproximateMedialAxisTest.java | 44 ++ 4 files changed, 618 insertions(+), 9 deletions(-) create mode 100644 modules/lab/src/main/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxis.java create mode 100644 modules/lab/src/test/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxisTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java index 8872a2a2c1..5448a66159 100644 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java @@ -41,11 +41,12 @@ public class ConstrainedDelaunayTriangulator { */ public static Geometry triangulate(Geometry geom) { ConstrainedDelaunayTriangulator cdt = new ConstrainedDelaunayTriangulator(geom); - return cdt.compute(); + return cdt.getResult(); } private final GeometryFactory geomFact; private final Geometry inputGeom; + private List triList; /** * Constructs a new Constrained Delaunay triangulator. @@ -57,14 +58,35 @@ public ConstrainedDelaunayTriangulator(Geometry inputGeom) { this.inputGeom = inputGeom; } - private Geometry compute() { + /** + * Gets the triangulation as a {@link GeometryCollection} of triangular {@link Polygon}s. + * + * @return a collection of the result triangle polygons + */ + public Geometry getResult() { + compute(); + return Tri.toGeometry(triList, geomFact); + } + + /** + * Gets the triangulation as a list of {@link Tri}s. + * + * @return the list of Tris in the triangulation + */ + public List getTriangles() { + compute(); + return triList; + } + + private void compute() { + if (triList != null) return; + List polys = PolygonExtracter.getPolygons(inputGeom); - List triList = new ArrayList(); + triList = new ArrayList(); for (Polygon poly : polys) { List polyTriList = triangulatePolygon(poly); triList.addAll(polyTriList); } - return Tri.toGeometry(triList, geomFact); } /** diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java index 2da07e8a8e..c93683e8a2 100755 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java @@ -48,12 +48,13 @@ public class PolygonTriangulator { * @return a GeometryCollection containing the triangle polygons */ public static Geometry triangulate(Geometry geom) { - PolygonTriangulator clipper = new PolygonTriangulator(geom); - return clipper.compute(); + PolygonTriangulator triangulator = new PolygonTriangulator(geom); + return triangulator.getResult(); } private final GeometryFactory geomFact; private final Geometry inputGeom; + private List triList; /** * Constructs a new triangulator. @@ -65,15 +66,34 @@ public PolygonTriangulator(Geometry inputGeom) { this.inputGeom = inputGeom; } - private Geometry compute() { + /** + * Gets the triangulation as a {@link GeometryCollection} of triangular {@link Polygon}s. + * + * @return a collection of the result triangle polygons + */ + public Geometry getResult() { + compute(); + return Tri.toGeometry(triList, geomFact); + } + + /** + * Gets the triangulation as a list of {@link Tri}s. + * + * @return the list of Tris in the triangulation + */ + public List getTriangles() { + compute(); + return triList; + } + + private void compute() { @SuppressWarnings("unchecked") List polys = PolygonExtracter.getPolygons(inputGeom); - List triList = new ArrayList(); + triList = new ArrayList(); for (Polygon poly : polys) { List polyTriList = triangulatePolygon(poly); triList.addAll(polyTriList); } - return Tri.toGeometry(triList, geomFact); } /** diff --git a/modules/lab/src/main/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxis.java b/modules/lab/src/main/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxis.java new file mode 100644 index 0000000000..ec9aebec18 --- /dev/null +++ b/modules/lab/src/main/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxis.java @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.axis; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.locationtech.jts.algorithm.Distance; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateArrays; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineSegment; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Triangle; +import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator; +import org.locationtech.jts.triangulate.tri.Tri; + + +/** + * Constructs an approximation to the medial axis of a Polygon, + * as a set of linestrings representing the medial axis graph. + * + * @author mdavis + * + */ +public class ApproximateMedialAxis { + + public static Geometry medialAxis(Geometry geom) { + ApproximateMedialAxis tt = new ApproximateMedialAxis((Polygon) geom); + return tt.compute(); + } + + /* + //-- Testing only + public static Geometry axisPointSegment(Geometry pt, Geometry seg) { + Coordinate p = pt.getCoordinate(); + Coordinate[] pts = seg.getCoordinates(); + Coordinate axisPt = medialAxisPoint(p, pts[0], pts[1]); + return pt.getFactory().createPoint(axisPt); + } + */ + + private Polygon inputPolygon; + private GeometryFactory geomFact; + + private Map nodeMap = new HashMap(); + private Deque nodeQue = new ArrayDeque(); + + public ApproximateMedialAxis(Polygon polygon) { + this.inputPolygon = polygon; + geomFact = inputPolygon.getFactory(); + } + + private Geometry compute() { + ConstrainedDelaunayTriangulator cdt = new ConstrainedDelaunayTriangulator(inputPolygon); + List tris = cdt.getTriangles(); + + List lines = constructLines(tris); + return geomFact.createMultiLineString(GeometryFactory.toLineStringArray(lines)); + } + + private List constructLines(List tris) + { + List lines = new ArrayList(); + for (Tri tri : tris) { + if (tri.numAdjacent() == 1) { + lines.add( constructLeafLine(tri) ); + } + } + while (! nodeQue.isEmpty()) { + AxisNode node = nodeQue.peek(); + if (node.isPathComplete()) { + nodeQue.pop(); + node.addInternalLines(lines, geomFact); + //--- done with this node + } + else { + LineString path = constructPath(node); + if (path != null) + lines.add( path ); + //-- node is left in queue for further processing + } + } + return lines; + } + + private LineString constructLeafLine(Tri triStart) { + int eAdj = indexOfAdjacent(triStart); + + int vOpp = Tri.oppVertex(eAdj); + Coordinate startPt = triStart.getCoordinate(vOpp); + Coordinate edgePt = angleBisector(triStart, vOpp); + + return constructPath(triStart, eAdj, startPt, edgePt); + } + + private LineString constructPath(AxisNode node) { + Tri tri = node.getTri(); + int freeEdge = node.getNonPathEdge(); + //Coordinate exitPt = node.getPathPoint(freeEdge); + Coordinate startPt = node.createPathPoint(freeEdge); + + Tri triNext = tri.getAdjacent(freeEdge); + /** + * If next tri is a node as well, queue it. + * No path is constructed, since node internal lines connect + */ + if (triNext.numAdjacent() == 3) { + int adjNext = triNext.getIndex(tri); + addNodePathPoint(triNext, adjNext, startPt); + return null; + } + return constructPath(tri, freeEdge, startPt, null); + } + + private LineString constructPath(Tri triStart, int eStart, + Coordinate p0, Coordinate p1) + { + ArrayList pts = new ArrayList(); + if (p0 != null) pts.add(p0); + if (p1 != null) pts.add(p1); + + Tri triNext = triStart.getAdjacent(eStart); + int eAdjNext = triNext.getIndex(triStart); + extendPath(triNext, eAdjNext, pts); + + return geomFact.createLineString(CoordinateArrays.toCoordinateArray(pts)); + } + + private void extendPath(Tri tri, int edgeEntry, List pts) { + //if (pts.size() > 100) return; + + //TODO: make this iterative instead of recursive + + int numAdj = tri.numAdjacent(); + if (numAdj == 3) { + addNodePathPoint(tri, edgeEntry, pts.get(pts.size() - 1)); + //--- path terminates at a node (3-adj tri) + return; + } + if (numAdj < 2) { + //-- leaf node - should never happen, has already been processed + return; + } + + //--- now are only dealing with 2-Adj triangles + int eAdj = indexOfAdjacentOther(tri, edgeEntry); + if (false && isTube(tri, eAdj)) { + /** + * This triangle and the next one form a "tube" + * so use both to construct the medial line. + */ + Tri tri2 = tri.getAdjacent(eAdj); + Coordinate p = exitPointTube(tri, tri2); + pts.add(p); + + int eAdj2 = tri2.getIndex(tri); + int eOpp2 = indexOfAdjacentOther(tri2, eAdj2); + Tri triN = tri2.getAdjacent(eOpp2); + int eOppN = triN.getIndex(tri2); + extendPath(triN, eOppN, pts); + } + else { + /** + * A "wedge" triangle (with one boundary edge). + */ + Coordinate p = exitPointWedge(tri, eAdj); + pts.add(p); + Tri triN = tri.getAdjacent(eAdj); + int eAdjN = triN.getIndex(tri); + extendPath(triN, eAdjN, pts); + } + } + + private void addNodePathPoint(Tri tri, int edgeEntry, Coordinate pt) { + AxisNode node = nodeMap.get(tri); + if (node == null) { + node = new AxisNode(tri); + nodeMap.put(tri, node); + } + node.addPathPoint(edgeEntry, pt); + nodeQue.add(node); + } + + private Coordinate exitPointWedge(Tri tri, int eExit) { + int eBdy = indexOfNonAdjacent(tri); + Coordinate pt = tri.getCoordinate(Tri.oppVertex(eBdy)); + Coordinate p0 = tri.getCoordinate(eBdy); + Coordinate p1 = tri.getCoordinate(Tri.next(eBdy)); + if (Tri.next(eBdy) != eExit) { + p0 = tri.getCoordinate(Tri.next(eBdy)); + p1 = tri.getCoordinate(eBdy); + } + /** + * Midpoint produces a straighter line in nearly-parallel corridors, + * but is more see-sawed elsewhere. + */ + + return tri.midpoint(eExit); + //return medialAxisPoint(pt, p0, p1); + } + + /** + * Computes medial axis point on exit edge of a "tube". + * + * @param tri1 the first triangle in the tube + * @param tri2 the second triangle in the tube + * @return medial axis exit point of tube + */ + private Coordinate exitPointTube(Tri tri1, Tri tri2) { + + int eBdy1 = indexOfNonAdjacent(tri1); + int eBdy2 = indexOfNonAdjacent(tri2); + //--- Case eBdy1 is eEntry.next + Coordinate p00 = tri1.getCoordinate(eBdy1); + Coordinate p01 = tri1.getCoordinate(Tri.next(eBdy1)); + Coordinate p10 = tri2.getCoordinate(Tri.next(eBdy2)); + Coordinate p11 = tri2.getCoordinate(eBdy2); + + int eAdj1 = tri1.getIndex(tri2); + if (Tri.next(eBdy1) != eAdj1) { + p00 = tri1.getCoordinate(Tri.next(eBdy1)); + p01 = tri1.getCoordinate(eBdy1); + p10 = tri2.getCoordinate(eBdy2); + p11 = tri2.getCoordinate(Tri.next(eBdy2)); + } + Coordinate axisPoint = medialAxisPoint(p00, p01, p10, p11); + return axisPoint; + } + + private static final double MEDIAL_AXIS_EPS = .01; + + /** + * Computes the approximate point where the medial axis + * between two line segments + * intersects the line between the ends of the segments. + * + * @param p00 the start vertex of segment 0 + * @param p01 the end vertex of segment 0 + * @param p10 the start vertex of segment 1 + * @param p11 the end vertex of segment 1 + * @return the approximate medial axis point + */ + private static Coordinate medialAxisPoint( + Coordinate p00, Coordinate p01, + Coordinate p10, Coordinate p11) { + double endFrac0 = 0; + double endFrac1 = 1; + double eps = 0.0; + LineSegment edgeExit = new LineSegment(p01, p11); + double edgeLen = edgeExit.getLength(); + Coordinate axisPt = null; + do { + double midFrac = (endFrac0 + endFrac1) / 2; + axisPt = edgeExit.pointAlong(midFrac); + double dist0 = Distance.pointToSegment(axisPt, p00, p01); + double dist1 = Distance.pointToSegment(axisPt, p10, p11); + if (dist0 > dist1) { + endFrac1 = midFrac; + } + else { + endFrac0 = midFrac; + } + eps = Math.abs(dist0 - dist1) / edgeLen; + } + while (eps > MEDIAL_AXIS_EPS); + return axisPt; + } + + /** + * Computes the approximate point where the medial axis + * between a point and a line segment + * intersects the line between the point and the segment endpoint + * + * @param p the point + * @param p0 the first vertex of the segment + * @param p1 the second vertex of the segment + * @return + */ + private static Coordinate medialAxisPoint(Coordinate p, Coordinate p0, Coordinate p1) { + double endFrac0 = 0; + double endFrac1 = 1; + double eps = 0.0; + LineSegment edgeExit = new LineSegment(p, p1); + double edgeLen = edgeExit.getLength(); + Coordinate axisPt = null; + do { + double midFrac = (endFrac0 + endFrac1) / 2; + axisPt = edgeExit.pointAlong(midFrac); + double distPt = p.distance(axisPt); + double distSeg = Distance.pointToSegment(axisPt, p0, p1); + if (distPt > distSeg) { + endFrac1 = midFrac; + } + else { + endFrac0 = midFrac; + } + eps = Math.abs(distSeg - distPt) / edgeLen; + } + while (eps > MEDIAL_AXIS_EPS); + return axisPt; + } + + /** + * Tests if a triangle and its adjacent tri form a "tube", + * where the opposite edges of the triangles are on the boundary. + * + * @param tri the triangle to test + * @param eAdj the edge adjacent to the next triangle + * @return true if the two triangles form a tube + */ + private static boolean isTube(Tri tri, int eAdj) { + Tri triNext = tri.getAdjacent(eAdj); + if (triNext.numAdjacent() != 2) + return false; + + int eBdy = indexOfNonAdjacent(tri); + int vOppBdy = Tri.oppVertex(eBdy); + Coordinate pOppBdy = tri.getCoordinate(vOppBdy); + + int eBdyN = indexOfNonAdjacent(triNext); + int vOppBdyN = Tri.oppVertex(eBdyN); + Coordinate pOppBdyN = triNext.getCoordinate(vOppBdyN); + + return ! pOppBdy.equals2D(pOppBdyN); + } + + private static int indexOfAdjacent(Tri tri) { + for (int i = 0; i < 3; i++) { + if (tri.hasAdjacent(i)) + return i; + } + return -1; + } + + private static int indexOfAdjacentOther(Tri tri, int e) { + for (int i = 0; i < 3; i++) { + if (i != e && tri.hasAdjacent(i)) + return i; + } + return -1; + } + + private static int indexOfNonAdjacent(Tri tri) { + for (int i = 0; i < 3; i++) { + if (! tri.hasAdjacent(i)) + return i; + } + return -1; + } + + private static Coordinate angleBisector(Tri tri, int v) { + return Triangle.angleBisector( + tri.getCoordinate(Tri.prev(v)), + tri.getCoordinate(v), + tri.getCoordinate(Tri.next(v)) + ); + } +} + +class AxisNode { + + private Tri tri; + /** + * Axis path points along tri edges + */ + private Coordinate p0; + private Coordinate p1; + private Coordinate p2; + private boolean isLinesAdded = false; + + public AxisNode(Tri tri) { + this.tri = tri; + } + + public Tri getTri() { + return tri; + } + + public void addPathPoint(int edgeIndex, Coordinate p) { + switch (edgeIndex) { + case 0: p0 = p; return; + case 1: p1 = p; return; + case 2: p2 = p; return; + } + } + + public Coordinate createPathPoint(int edgeIndex) { + Coordinate pt = tri.midpoint(edgeIndex); + addPathPoint(edgeIndex, pt); + return pt; + } + + public Coordinate getPathPoint(int edgeIndex) { + switch (edgeIndex) { + case 0: return p0; + case 1: return p1; + case 2: return p2; + } + return null; + } + + public boolean isPathComplete() { + return numPaths() == 3; + } + + public int numPaths() { + int num = 0; + if (p0 != null) num++; + if (p1 != null) num++; + if (p2 != null) num++; + return num; + } + + public int getNonPathEdge() { + if (p0 == null) return 0; + if (p1 == null) return 1; + if (p2 == null) return 2; + return -1; + } + + public void addInternalLines(List lines, GeometryFactory geomFact) { + //Assert.assertTrue( isPathComplete() ); + if (isLinesAdded) return; + Coordinate cc = circumcentre(); + if (intersects(cc)) { + addInternalLines(cc, -1, lines, geomFact); + } + else { + addInternalLinesToEdge(lines, geomFact); + } + isLinesAdded = true; + } + + /* + //--- Using cc int point isn't as good as midpoint + private void fillEdgePoints(int longEdge, Coordinate cc) { + if (p0 == null) p0 = medialPoint(0, longEdge, cc); + if (p1 == null) p1 = medialPoint(1, longEdge, cc); + if (p2 == null) p2 = medialPoint(2, longEdge, cc); + } + + private Coordinate medialPoint(int edge, int longEdge, Coordinate cc) { + if (edge != longEdge) { + return tri.midpoint(edge); + } + return intersection( + tri.getEdgeStart(edge), tri.getEdgeEnd(edge), + tri.getCoordinate( Tri.oppVertex(edge) ), cc); + } + + private Coordinate intersection(Coordinate p00, Coordinate p01, Coordinate p10, Coordinate p11) { + LineIntersector li = new RobustLineIntersector(); + li.computeIntersection(p00, p01, p10, p11); + return li.getIntersection(0); + } +*/ + + private void addInternalLinesToEdge(List lines, GeometryFactory geomFact) { + int nodeEdge = longEdge(); + Coordinate nodePt = getPathPoint(nodeEdge); + addInternalLines(nodePt, nodeEdge, lines, geomFact); + } + + private void addInternalLines(Coordinate p, int skipEdge, List lines, GeometryFactory geomFact) { + if (skipEdge != 0) addLine(p0, p, geomFact, lines); + if (skipEdge != 1) addLine(p1, p, geomFact, lines); + if (skipEdge != 2) addLine(p2, p, geomFact, lines); + } + + private boolean intersects(Coordinate p) { + return Triangle.intersects(tri.getCoordinate(0), + tri.getCoordinate(1), tri.getCoordinate(2), p); + } + + private Coordinate circumcentre() { + return Triangle.circumcentre(tri.getCoordinate(0), + tri.getCoordinate(1), tri.getCoordinate(2)); + } + + /** + * Edge opposite obtuse angle, if any + * @return edge index of longest edge + */ + private int longEdge() { + int e = 0; + if (edgeLen(1) > edgeLen(e)) { + e = 1; + } + if (edgeLen(2) > edgeLen(e)) { + e = 2; + } + return e; + } + + private double edgeLen(int i) { + return tri.getCoordinate(i).distance(tri.getCoordinate(Tri.next(i))); + } + + private static void addLine(Coordinate p0, Coordinate p1, + GeometryFactory geomFact, List lines) { + LineString line = geomFact.createLineString(new Coordinate[] { + p0.copy(), p1.copy() + }); + lines.add(line); + } + +} diff --git a/modules/lab/src/test/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxisTest.java b/modules/lab/src/test/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxisTest.java new file mode 100644 index 0000000000..f0aa82b65d --- /dev/null +++ b/modules/lab/src/test/java/org/locationtech/jts/algorithm/axis/ApproximateMedialAxisTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.axis; + +import org.locationtech.jts.geom.Geometry; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class ApproximateMedialAxisTest extends GeometryTestCase { + public static void main(String args[]) { + TestRunner.run(ApproximateMedialAxisTest.class); + } + + public ApproximateMedialAxisTest(String name) { + super(name); + } + + public void testQuad() { + checkTree("POLYGON ((10 10, 30 30, 60 40, 90 70, 90 10, 10 10))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 10, 10 10)), POLYGON ((90 90, 20 40, 90 10, 90 90)))"); + } + + public void testRandom() { + checkTree("POLYGON ((200 100, 100 100, 150 200, 250 250, 300 300, 360 400, 500 300, 400 250, 300 200, 300 150, 200 100))" + ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 40, 90 10, 10 10)), POLYGON ((90 90, 20 40, 90 10, 90 90)))"); + } + + private void checkTree(String wkt, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ApproximateMedialAxis.medialAxis(geom); + Geometry expected = read(wktExpected); + //checkEqual(expected, actual); + } +} From fb042c51b19f2624a39b59a1a59cf2c4d49791b2 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 20 Sep 2021 13:13:10 -0700 Subject: [PATCH 080/275] Add JtsOp -where op val functionality Signed-off-by: Martin Davis --- .../locationtech/jtstest/cmd/JTSOpCmd.java | 35 ++++++++++---- .../locationtech/jtstest/cmd/JTSOpRunner.java | 13 +++-- ...ction.java => FilterGeometryFunction.java} | 48 +++++++++++++++---- .../jtstest/command/CommandLine.java | 2 +- 4 files changed, 71 insertions(+), 27 deletions(-) rename modules/app/src/main/java/org/locationtech/jtstest/geomfunction/{SelecterGeometryFunction.java => FilterGeometryFunction.java} (52%) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java index 20823c3fea..29df546c81 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java @@ -25,6 +25,7 @@ import org.locationtech.jtstest.command.ParseException; import org.locationtech.jtstest.function.DoubleKeyMap; import org.locationtech.jtstest.geomfunction.BaseGeometryFunction; +import org.locationtech.jtstest.geomfunction.FilterGeometryFunction; import org.locationtech.jtstest.geomfunction.GeometryFunction; import org.locationtech.jtstest.geomfunction.GeometryFunctionRegistry; import org.locationtech.jtstest.util.io.MultiFormatReader; @@ -120,7 +121,7 @@ private static CommandLine createCmdLine() { .addOptionSpec(new OptionSpec(CommandOptions.OFFSET, 1)) .addOptionSpec(new OptionSpec(CommandOptions.REPEAT, 1)) .addOptionSpec(new OptionSpec(CommandOptions.SRID, 1)) - .addOptionSpec(new OptionSpec(CommandOptions.WHERE, 1)) + .addOptionSpec(new OptionSpec(CommandOptions.WHERE, 2)) .addOptionSpec(new OptionSpec(CommandOptions.VALIDATE, 0)) .addOptionSpec(new OptionSpec(OptionSpec.OPTION_FREE_ARGS, OptionSpec.NARGS_ONE_OR_MORE)); return commandLine; @@ -139,7 +140,7 @@ private static CommandLine createCmdLine() { " [ -eachb ]", " [ -index ]", " [ -repeat N ]", - " [ -where D ]", + " [ -where (eq | ne | ge | gt | le | lt) N ]", " [ -validate ]", " [ -explode", " [ -srid SRID ]", @@ -165,7 +166,8 @@ private static CommandLine createCmdLine() { " -eachb execute op on each element of B", " -index index the B geometries", " -repeat repeat the operation N times", - " -where output geometry where operation result equals the value D (1=true, 0=false)", + " -where op v output geometry where operation result matches predicate op and value.", + " Predicates ops are: eq,ne,ge,gt,le,lt", " -validate validate the result of each operation", " -geomfunc specifies class providing geometry operations", " -op separator to delineate operation arguments", @@ -340,13 +342,16 @@ JTSOpRunner.OpParams parseArgs(String[] args) throws ParseException, ClassNotFou cmdArgs.repeat = commandLine.hasOption(CommandOptions.REPEAT) ? commandLine.getOptionArgAsInt(CommandOptions.REPEAT, 0) : 1; - cmdArgs.validate = commandLine.hasOption(CommandOptions.VALIDATE); - cmdArgs.isSelect = commandLine.hasOption(CommandOptions.WHERE); - cmdArgs.selectVal = cmdArgs.isSelect ? - commandLine.getOptionArgAsNum(CommandOptions.WHERE, 0) - : 1; + cmdArgs.validate = commandLine.hasOption(CommandOptions.VALIDATE); + + cmdArgs.isFilter = commandLine.hasOption(CommandOptions.WHERE); + cmdArgs.filterOp = cmdArgs.isFilter ? + parseFilterOp(commandLine.getOptionArg(CommandOptions.WHERE, 0)) + : 0; + cmdArgs.filterVal = cmdArgs.isFilter ? + commandLine.getOptionArgAsNum(CommandOptions.WHERE, 1) + : 0; - cmdArgs.eachA = commandLine.hasOption(CommandOptions.EACHA); cmdArgs.eachB = commandLine.hasOption(CommandOptions.EACHB); @@ -396,7 +401,7 @@ JTSOpRunner.OpParams parseArgs(String[] args) throws ParseException, ClassNotFou return cmdArgs; } - + private String[] parseOpArg(String arg) { if (isArgMultiValues(arg)) { return parseValues(arg); @@ -456,4 +461,14 @@ private String[] parseMacroArgs(String macroTerm) { String args = macroTerm.substring(indexLeft + 1, indexRight); return args.split(","); } + + private int parseFilterOp(String opStr) { + if ("eq".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_EQ; + if ("ne".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_NE; + if ("ge".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_GE; + if ("gt".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_GT; + if ("le".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_LE; + if ("lt".equalsIgnoreCase(opStr)) return FilterGeometryFunction.OP_LT; + throw new CommandError(ERR_INVALID_ARG_PARAM, opStr); + } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java index f62e8ac180..a6503b7405 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java @@ -29,7 +29,7 @@ import org.locationtech.jts.util.Stopwatch; import org.locationtech.jtstest.geomfunction.GeometryFunction; import org.locationtech.jtstest.geomfunction.GeometryFunctionRegistry; -import org.locationtech.jtstest.geomfunction.SelecterGeometryFunction; +import org.locationtech.jtstest.geomfunction.FilterGeometryFunction; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; import org.locationtech.jtstest.util.io.MultiFormatBufferedReader; import org.locationtech.jtstest.util.io.MultiFormatFileReader; @@ -105,8 +105,9 @@ static class OpParams { public boolean isExplode = false; public int srid; - public boolean isSelect = false; - public double selectVal = 0; + public boolean isFilter = false; + public int filterOp; + public double filterVal = 0; String operation; public String[] argList; @@ -250,13 +251,11 @@ private void loadGeometryAB() { geomB = toList(geomAB.get(1)); } - - private void executeFunction() { GeometryFunction baseFun = getFunction(param.operation); GeometryFunction func = baseFun; - if (param.isSelect) { - func = new SelecterGeometryFunction(func, param.selectVal); + if (param.isFilter) { + func = new FilterGeometryFunction(func, param.filterOp, param.filterVal); } if (func == null) { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SelecterGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/FilterGeometryFunction.java similarity index 52% rename from modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SelecterGeometryFunction.java rename to modules/app/src/main/java/org/locationtech/jtstest/geomfunction/FilterGeometryFunction.java index 96dbc213ce..1b96181a76 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/SelecterGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/FilterGeometryFunction.java @@ -13,13 +13,29 @@ import org.locationtech.jts.geom.Geometry; -public class SelecterGeometryFunction implements GeometryFunction { +public class FilterGeometryFunction implements GeometryFunction { + + public static int OP_EQ = 1; + public static int OP_NE = 2; + public static int OP_GE = 3; + public static int OP_GT = 4; + public static int OP_LE = 5; + public static int OP_LT = 6; + private GeometryFunction fun; - private double selectVal; + private double filterVal; + private int filterOp; + + public FilterGeometryFunction(GeometryFunction fun, double filterVal) { + this.fun = fun; + this.filterVal = filterVal; + filterOp = OP_EQ; + } - public SelecterGeometryFunction(GeometryFunction fun, double selectVal) { + public FilterGeometryFunction(GeometryFunction fun, int op, double val) { this.fun = fun; - this.selectVal = selectVal; + this.filterOp = op; + this.filterVal = val; } public String getCategory() { @@ -61,13 +77,27 @@ public boolean isRequiredB() { @Override public Object invoke(Geometry geom, Object[] args) { Object result = fun.invoke(geom, args); - double val = 0; - if (result instanceof Boolean) { - val = ((Boolean) result) ? 1 : 0; - } - if (selectVal == val) return geom; + double val = toDouble(result); + if (isMatch(val)) return geom; return null; } + private double toDouble(Object result) { + if (result instanceof Boolean) return ((Boolean) result) ? 1 : 0; + if (result instanceof Number) + return ((Number) result).doubleValue(); + return 0; + } + + private boolean isMatch(double val) { + if (OP_EQ == filterOp) return val == filterVal; + if (OP_NE == filterOp) return val != filterVal; + if (OP_GE == filterOp) return val >= filterVal; + if (OP_GT == filterOp) return val > filterVal; + if (OP_LE == filterOp) return val <= filterVal; + if (OP_LT == filterOp) return val < filterVal; + return false; + } + } diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java b/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java index fd624ed368..9bbfc40003 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/command/CommandLine.java @@ -83,7 +83,7 @@ public double getOptionArgAsNum(String name, int argIndex) if (spec == null) return 0; Option opt = spec.getOption(0); if (opt == null) return 0; - return opt.getArgAsNum(0); + return opt.getArgAsNum(argIndex); } public String[] getOptionArgs(String name) From d352d4bb62620fb991fb977d1f3e1e0f499be58a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 20 Sep 2021 13:14:00 -0700 Subject: [PATCH 081/275] Fix triangulator geometry factory initialization Signed-off-by: Martin Davis --- .../triangulate/polygon/ConstrainedDelaunayTriangulator.java | 2 +- .../jts/triangulate/polygon/PolygonTriangulator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java index 5448a66159..e636ef4e73 100644 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/ConstrainedDelaunayTriangulator.java @@ -54,7 +54,7 @@ public static Geometry triangulate(Geometry geom) { * @param inputGeom the input geometry */ public ConstrainedDelaunayTriangulator(Geometry inputGeom) { - geomFact = new GeometryFactory(); + geomFact = inputGeom.getFactory(); this.inputGeom = inputGeom; } diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java index c93683e8a2..d4021ac575 100755 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java @@ -62,7 +62,7 @@ public static Geometry triangulate(Geometry geom) { * @param inputGeom the input geometry */ public PolygonTriangulator(Geometry inputGeom) { - geomFact = new GeometryFactory(); + geomFact = inputGeom.getFactory(); this.inputGeom = inputGeom; } From 92f80b1f54dd854f5cb2dd1acf45964e2193c801 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 20 Sep 2021 13:17:10 -0700 Subject: [PATCH 082/275] Fix JtsOp unit test Signed-off-by: Martin Davis --- .../test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java index eee570708f..764cef4500 100644 --- a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java +++ b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java @@ -203,7 +203,7 @@ public void testWhereValid() { JTSOpCmd cmd = runCmd( args( "-a", "POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9))", "-f", "wkt", - "-where", "1", + "-where", "eq", "1", "isValid" ), null, null ); List results = cmd.getResultGeometry(); @@ -214,7 +214,7 @@ public void testWhereInvalid() { JTSOpCmd cmd = runCmd( args( "-a", "POLYGON ((1 9, 9 1, 9 9, 1 1, 1 9))", "-f", "wkt", - "-where","0", + "-where","eq", "0", "isValid" ), null, null ); List results = cmd.getResultGeometry(); From 6fe4fade67777d7ceb72aaa399a3c09be65ef595 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 20 Sep 2021 13:27:01 -0700 Subject: [PATCH 083/275] Add JtsOp unit tests for -where Signed-off-by: Martin Davis --- .../jtstest/cmd/JTSOpCmdTest.java | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java index 764cef4500..202677a72c 100644 --- a/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java +++ b/modules/app/src/test/java/org/locationtech/jtstest/cmd/JTSOpCmdTest.java @@ -99,10 +99,9 @@ public void testCollectUnion() { runCmd( args("-a", "stdin", "-collect", "-f", "wkt", "Overlay.unaryUnion"), - stdin(new String[] { - "POLYGON ((1 3, 3 3, 3 1, 1 1, 1 3))", - "POLYGON ((5 3, 5 1, 3 1, 3 3, 5 3))" - }), + stdin( "POLYGON ((1 3, 3 3, 3 1, 1 1, 1 3))", + "POLYGON ((5 3, 5 1, 3 1, 3 3, 5 3))" + ), "POLYGON ((1 3, 3 3, 5 3, 5 1, 3 1, 1 1, 1 3))" ); } @@ -112,11 +111,10 @@ public void testCollectLimitUnion() { "-collect", "-limit", "2", "-f", "wkt", "Overlay.unaryUnion"), - stdin(new String[] { - "POLYGON ((1 3, 3 3, 3 1, 1 1, 1 3))", - "POLYGON ((5 3, 5 1, 3 1, 3 3, 5 3))", - "POLYGON ((1 5, 5 5, 5 3, 1 3, 1 5))" - }), + stdin( "POLYGON ((1 3, 3 3, 3 1, 1 1, 1 3))", + "POLYGON ((5 3, 5 1, 3 1, 3 3, 5 3))", + "POLYGON ((1 5, 5 5, 5 3, 1 3, 1 5))" + ), "POLYGON ((1 3, 3 3, 5 3, 5 1, 3 1, 1 1, 1 3))" ); } @@ -199,13 +197,14 @@ public void testOpBufferMultiArgNoParen() { assertEquals("Incorrect summary value for arg values", computeArea(results), 93.6, 1); } + //---------------------------------------------------------------- + public void testWhereValid() { JTSOpCmd cmd = runCmd( args( "-a", "POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9))", "-f", "wkt", "-where", "eq", "1", - "isValid" ), - null, null ); + "isValid" ) ); List results = cmd.getResultGeometry(); assertTrue("Not enough results for arg values", results.size() == 1 ); } @@ -215,12 +214,27 @@ public void testWhereInvalid() { "-a", "POLYGON ((1 9, 9 1, 9 9, 1 1, 1 9))", "-f", "wkt", "-where","eq", "0", - "isValid" ), - null, null ); + "isValid" ) ); List results = cmd.getResultGeometry(); assertTrue("Not enough results for arg values", results.size() == 1 ); } + public void testWhereLength() { + JTSOpCmd cmd = runCmd( args( + "-a", "stdin", + "-f", "wkt", + "-where","gt", "1", + "Geometry.length" ), + stdin( + "LINESTRING ( 0 1, 0 1)", + "LINESTRING ( 0 1, 0 2)", + "LINESTRING ( 0 1, 0 3)", + "LINESTRING ( 0 1, 0 4)" + ) ); + List results = cmd.getResultGeometry(); + assertTrue("Not enough results for arg values", results.size() == 2 ); + } + //---------------------------------------------------------------- public void testSRIDBuffer() throws ParseException { @@ -373,7 +387,7 @@ private static InputStream stdin(String data) { return instr; } - private static InputStream stdin(String[] dataArr) { + private static InputStream stdin(String... dataArr) { String data = String.join("\n", dataArr); return stdin(data); } @@ -392,6 +406,14 @@ public void runCmd(String[] args, String expected) runCmd(args, null, expected); } + private JTSOpCmd runCmd(String[] args, InputStream stdin) { + return runCmd(args, stdin, null); + } + + private JTSOpCmd runCmd(String[] args) { + return runCmd(args, null, null); + } + private JTSOpCmd runCmd(String[] args, InputStream stdin, String expected) { JTSOpCmd cmd = new JTSOpCmd(); cmd.captureOutput(); From 3a532d27ab7b23071d7d406c75d14ce7b1a8002e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 21 Sep 2021 22:34:14 -0700 Subject: [PATCH 084/275] Javadoc Signed-off-by: Martin Davis --- .../polygon/PolygonEarClipper.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java index df6dff7f79..b16c2f9523 100644 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonEarClipper.java @@ -77,8 +77,10 @@ public static List triangulate(Coordinate[] polyShell) { private int[] cornerIndex; /** - * Indexing vertices improves ear intersection testing performance a lot. + * Indexing vertices improves ear intersection testing performance. * The polyShell vertices are contiguous, so are suitable for an SPRtree. + * Note that a KDtree cannot be used because the vertex indices must be stored + * and duplicates must be stored. */ private VertexSequencePackedRtree vertexCoordIndex; @@ -199,15 +201,16 @@ private boolean isValidEar(int cornerIndex, Coordinate[] corner) { //--- a duplicate corner vertex requires a full scan return isValidEarScan(cornerIndex, corner); } + //-- vertex is contained in corner, so it is not a valid ear return false; } /** - * Finds another vertex intersecting the corner triangle, if any. + * Finds a vertex contained in the corner triangle, if any. * Uses the vertex spatial index for efficiency. *

      - * Also finds any vertex which is a duplicate of the corner apex vertex, - * which then requires a full scan of the vertices to confirm ear is valid. + * Also finds any vertex which is a duplicate of the corner apex vertex. + * This requires a full scan of the vertices to confirm ear is valid. * This is usually a rare situation, so has little impact on performance. * * @param cornerIndex the index of the corner apex vertex @@ -230,11 +233,11 @@ private int findIntersectingVertex(int cornerIndex, Coordinate[] corner) { Coordinate v = vertex[vertIndex]; /** - * If another vertex at the corner is found, - * need to do a full scan to check the incident segments. - * This happens when the polygon ring self-intersects, - * usually due to hold joining. - * But only report this if no properly intersecting vertex is found, + * If the vertex is equal to the corner apex, record it. + * This can happen where the polygon ring self-touches, + * usually due to hole joining. + * This will require a full scan to check the incident segments. + * So only report this if no properly intersecting vertex is found, * for efficiency. */ if ( v.equals2D(corner[1]) ) { From 06a8871f635237ff6e6e0fe14b889c54a1f87c2f Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 24 Sep 2021 08:58:28 -0700 Subject: [PATCH 085/275] Improve JTSOp error reporting Signed-off-by: Martin Davis --- .../main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java | 4 ++++ .../jtstest/geomfunction/StaticMethodGeometryFunction.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java index a6503b7405..22da731eb0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java @@ -368,6 +368,10 @@ private Object executeFunctionOnce(Geometry geomA, GeometryFunction func, Object private String errorMsg(Throwable ex) { String msg = "ERROR excuting function: " + ex.getMessage() + "\n"; msg += toStackString(ex); + if (ex.getCause() != null) { + msg += "Caused by:\n"; + msg += toStackString(ex.getCause()); + } return msg; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java index 319bc7ea7c..24e24d4223 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/geomfunction/StaticMethodGeometryFunction.java @@ -191,7 +191,7 @@ public static Object invoke(Method method, Object target, Object[] args) Throwable t = ex.getCause(); if (t instanceof RuntimeException) throw (RuntimeException) t; - throw new RuntimeException(invocationErrMsg(ex)); + throw new RuntimeException(invocationErrMsg(ex), ex); } catch (Exception ex) { System.out.println(ex.getMessage()); From 38b17fc3e5a9147bd895a16dac6d4553c9dfbf85 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 24 Sep 2021 09:07:06 -0700 Subject: [PATCH 086/275] Add PolygonTriangulator empty short-circuit Signed-off-by: Martin Davis --- .../jts/triangulate/polygon/PolygonTriangulator.java | 1 + .../jts/triangulate/polygon/PolygonTriangulatorTest.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java index d4021ac575..40aef91baa 100755 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulator.java @@ -91,6 +91,7 @@ private void compute() { List polys = PolygonExtracter.getPolygons(inputGeom); triList = new ArrayList(); for (Polygon poly : polys) { + if (poly.isEmpty()) continue; List polyTriList = triangulatePolygon(poly); triList.addAll(polyTriList); } diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java index c77c663603..32a4467273 100644 --- a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java @@ -52,6 +52,11 @@ public void testRepeatedPoints() { ,"GEOMETRYCOLLECTION (POLYGON ((71 195, 178 335, 239 185, 71 195)), POLYGON ((71 195, 239 185, 290 60, 71 195)), POLYGON ((71 195, 290 60, 110 70, 71 195)), POLYGON ((239 185, 380 210, 290 60, 239 185)))"); } + public void testEmpty() { + checkTri("POLYGON EMPTY" + ,"GEOMETRYCOLLECTION EMPTY"); + } + public void testMultiPolygon() { checkTri("MULTIPOLYGON (((10 10, 20 50, 50 50, 40 20, 10 10)), ((20 60, 60 60, 90 20, 90 90, 20 60)), ((10 90, 10 70, 40 70, 50 90, 10 90)))" ,"GEOMETRYCOLLECTION (POLYGON ((10 10, 20 50, 50 50, 10 10)), POLYGON ((50 50, 40 20, 10 10, 50 50)), POLYGON ((90 90, 90 20, 60 60, 90 90)), POLYGON ((60 60, 20 60, 90 90, 60 60)), POLYGON ((10 70, 10 90, 50 90, 10 70)), POLYGON ((50 90, 40 70, 10 70, 50 90)))"); From 28b5bd45097bbf84c9130602450d8bfed5655381 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 28 Sep 2021 10:06:32 -0700 Subject: [PATCH 087/275] Fix JtsOp to explode copied inputs Signed-off-by: Martin Davis --- .../src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java index 22da731eb0..1fde27e58c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpRunner.java @@ -493,7 +493,7 @@ private void outputList(List geoms, String outputFormat) { if (outputFormat == null) return; for (Geometry geom : geoms) { - printGeometry(geom, param.srid, outputFormat); + outputResult(geom, param.isExplode, outputFormat); } } From 3b8c3376b3102e0900aa57015e1f962fae7a874b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 29 Sep 2021 09:38:36 -0700 Subject: [PATCH 088/275] Remove unneeded import Signed-off-by: Martin Davis --- .../jts/triangulate/polygon/PolygonTriangulatorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java index 32a4467273..897a5aa41b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/triangulate/polygon/PolygonTriangulatorTest.java @@ -12,7 +12,6 @@ package org.locationtech.jts.triangulate.polygon; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.triangulate.polygon.PolygonTriangulator; import junit.textui.TestRunner; import test.jts.GeometryTestCase; From 861402ae03f0e957dec0eb703fb06b963e2f69e6 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 29 Sep 2021 09:47:23 -0700 Subject: [PATCH 089/275] Fix BufferParameters logic for quadsegs (#778) Signed-off-by: Martin Davis --- .../jts/operation/buffer/BufferOp.java | 3 +- .../operation/buffer/BufferParameters.java | 66 +++++------------- .../buffer/OffsetSegmentGenerator.java | 5 +- .../operation/buffer/BufferParameterTest.java | 67 +++++++++++++++++++ 4 files changed, 89 insertions(+), 52 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferOp.java index 6737e3c82f..05b459540b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferOp.java @@ -344,7 +344,8 @@ public void setEndCapStyle(int endCapStyle) } /** - * Sets the number of segments used to approximate a angle fillet + * Sets the number of line segments in a quarter-circle + * used to approximate angle fillets for round end caps and joins. * * @param quadrantSegments the number of segments in a fillet for a quadrant */ diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferParameters.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferParameters.java index 1d535d0024..14ac0b4c57 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferParameters.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferParameters.java @@ -137,6 +137,7 @@ public BufferParameters(int quadrantSegments, /** * Gets the number of quadrant segments which will be used + * to approximate angle fillets in round endcaps and joins. * * @return the number of quadrant segments */ @@ -146,60 +147,24 @@ public int getQuadrantSegments() } /** - * Sets the number of line segments used to approximate an angle fillet. - *

        - *
      • If quadSegs >= 1, joins are round, and quadSegs indicates the number of - * segments to use to approximate a quarter-circle. - *
      • If quadSegs = 0, joins are bevelled (flat) - *
      • If quadSegs < 0, joins are mitred, and the value of qs - * indicates the mitre ration limit as - *
        -   * mitreLimit = |quadSegs|
        -   * 
        - *
      - * For round joins, quadSegs determines the maximum + * Sets the number of line segments in a quarter-circle + * used to approximate angle fillets in round endcaps and joins. + * The value should be at least 1. + *

      + * This determines the * error in the approximation to the true buffer curve. - * The default value of 8 gives less than 2% max error in the buffer distance. - * For a max error of < 1%, use QS = 12. - * For a max error of < 0.1%, use QS = 18. + * The default value of 8 gives less than 2% error in the buffer distance. + * For a error of < 1%, use QS = 12. + * For a error of < 0.1%, use QS = 18. * The error is always less than the buffer distance * (in other words, the computed buffer curve is always inside the true * curve). * - * @param quadSegs the number of segments in a fillet for a quadrant + * @param quadSegs the number of segments in a fillet for a circle quadrant */ public void setQuadrantSegments(int quadSegs) { quadrantSegments = quadSegs; - - /** - * Indicates how to construct fillets. - * If qs >= 1, fillet is round, and qs indicates number of - * segments to use to approximate a quarter-circle. - * If qs = 0, fillet is bevelled flat (i.e. no filleting is performed) - * If qs < 0, fillet is mitred, and absolute value of qs - * indicates maximum length of mitre according to - * - * mitreLimit = |qs| - */ - if (quadrantSegments == 0) - joinStyle = JOIN_BEVEL; - if (quadrantSegments < 0) { - joinStyle = JOIN_MITRE; - mitreLimit = Math.abs(quadrantSegments); - } - - if (quadSegs <= 0) { - quadrantSegments = 1; - } - - /** - * If join style was set by the quadSegs value, - * use the default for the actual quadrantSegments value. - */ - if (joinStyle != JOIN_ROUND) { - quadrantSegments = DEFAULT_QUADRANT_SEGMENTS; - } } /** @@ -218,7 +183,7 @@ public static double bufferDistanceError(int quadSegs) /** * Gets the end cap style. * - * @return the end cap style + * @return the end cap style code */ public int getEndCapStyle() { @@ -228,9 +193,9 @@ public int getEndCapStyle() /** * Specifies the end cap style of the generated buffer. * The styles supported are {@link #CAP_ROUND}, {@link #CAP_FLAT}, and {@link #CAP_SQUARE}. - * The default is CAP_ROUND. + * The default is {@link #CAP_ROUND}. * - * @param endCapStyle the end cap style to specify + * @param endCapStyle the code for the end cap style */ public void setEndCapStyle(int endCapStyle) { @@ -238,7 +203,7 @@ public void setEndCapStyle(int endCapStyle) } /** - * Gets the join style + * Gets the join style. * * @return the join style code */ @@ -249,8 +214,9 @@ public int getJoinStyle() /** * Sets the join style for outside (reflex) corners between line segments. - * Allowable values are {@link #JOIN_ROUND} (which is the default), + * The styles supported are {@link #JOIN_ROUND}, * {@link #JOIN_MITRE} and {link JOIN_BEVEL}. + * The default is {@link #JOIN_ROUND}. * * @param joinStyle the code for the join style */ diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index c6d607fe57..616ed01845 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -107,7 +107,10 @@ public OffsetSegmentGenerator(PrecisionModel precisionModel, // compute intersections in full precision, to provide accuracy // the points are rounded as they are inserted into the curve line li = new RobustLineIntersector(); - filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments(); + + int quadSegs = bufParams.getQuadrantSegments(); + if (quadSegs < 1) quadSegs = 1; + filletAngleQuantum = Math.PI / 2.0 / quadSegs; /** * Non-round joins cause issues with short closing segments, so don't use diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java new file mode 100644 index 0000000000..6b94aadc2c --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java @@ -0,0 +1,67 @@ +package org.locationtech.jts.operation.buffer; + +import org.locationtech.jts.geom.Geometry; + +import test.jts.GeometryTestCase; + +/** + * Tests for the effect of buffer parameter values. + * + * @author Martin Davis + * + */ +public class BufferParameterTest extends GeometryTestCase { + + public static void main(String[] args) { + junit.textui.TestRunner.run(BufferParameterTest.class); + } + + public BufferParameterTest(String name) { + super(name); + } + + public void testQuadSegsNeg() { + checkBuffer("LINESTRING (20 20, 80 20, 80 80)", + 10.0, -99, + "POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))"); + } + + public void testQuadSegs0() { + checkBuffer("LINESTRING (20 20, 80 20, 80 80)", + 10.0, 0, + "POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))"); + } + + public void testQuadSegs1() { + checkBuffer("LINESTRING (20 20, 80 20, 80 80)", + 10.0, 1, + "POLYGON ((70 30, 70 80, 80 90, 90 80, 90 20, 80 10, 20 10, 10 20, 20 30, 70 30))"); + } + + public void testQuadSegs2() { + checkBuffer("LINESTRING (20 20, 80 20, 80 80)", + 10.0, 2, + "POLYGON ((70 30, 70 80, 72.92893218813452 87.07106781186548, 80 90, 87.07106781186548 87.07106781186548, 90 80, 90 20, 87.07106781186548 12.928932188134524, 80 10, 20 10, 12.928932188134523 12.928932188134524, 10 20, 12.928932188134524 27.071067811865476, 20 30, 70 30))"); + } + + public void testQuadSegs2Bevel() { + checkBuffer("LINESTRING (20 20, 80 20, 80 80)", + 10.0, 2, BufferParameters.JOIN_BEVEL, + "POLYGON ((70 30, 70 80, 72.92893218813452 87.07106781186548, 80 90, 87.07106781186548 87.07106781186548, 90 80, 90 20, 80 10, 20 10, 12.928932188134523 12.928932188134524, 10 20, 12.928932188134524 27.071067811865476, 20 30, 70 30))"); + } + + + private void checkBuffer(String wkt, double dist, int quadSegs, String wktExpected) { + checkBuffer( wkt, dist, quadSegs, BufferParameters.JOIN_ROUND, wktExpected); + } + + private void checkBuffer(String wkt, double dist, int quadSegs, int joinStyle, String wktExpected) { + BufferParameters param = new BufferParameters(); + param.setQuadrantSegments(quadSegs); + param.setJoinStyle(joinStyle); + Geometry geom = read(wkt); + Geometry result = BufferOp.bufferOp(geom, dist, param); + Geometry expected = read(wktExpected); + checkEqual(expected, result); + } +} From 2d5a84f866fdd78f228c894c1aa0c61f6a6b3eae Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 29 Sep 2021 09:48:36 -0700 Subject: [PATCH 090/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index cdf5fe4a9d..18f4be7dd5 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -26,6 +26,7 @@ Distributions for older JTS versions can be obtained at the * Improve `GeometryFixer` behaviour for holes outside polygons (#772) * Add `ConstrainedDelaunayTriangulator` and `PolygonTriangulator` (#775) * Add `Tri` data structure for representing triangulations (#775) +* Simplify and fix logic of `BufferParameters.setQuadSegs` (#778) # Version 1.18.2 From 886eeb4fc99080243f86a6d85af110172d219845 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 4 Oct 2021 16:54:51 -0700 Subject: [PATCH 091/275] Improve KdTree query to avoid recursion (#779) Signed-off-by: Martin Davis --- .../locationtech/jts/index/kdtree/KdNode.java | 86 ++++++++ .../locationtech/jts/index/kdtree/KdTree.java | 192 ++++++++++-------- .../test/jts/perf/index/KdtreeStressTest.java | 55 +++++ 3 files changed, 250 insertions(+), 83 deletions(-) create mode 100644 modules/core/src/test/java/test/jts/perf/index/KdtreeStressTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java index 92a0790d29..d260c74adf 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdNode.java @@ -13,6 +13,7 @@ package org.locationtech.jts.index.kdtree; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; /** * A node of a {@link KdTree}, which represents one or more points in the same location. @@ -74,6 +75,23 @@ public double getY() { return p.y; } + /** + * Gets the split value at a node, depending on + * whether the node splits on X or Y. + * The X (or Y) ordinates of all points in the left subtree + * are less than the split value, and those + * in the right subtree are greater than or equal to the split value. + * + * @param isSplitOnX whether the node splits on X or Y + * @return the splitting value + */ + public double splitValue(boolean isSplitOnX) { + if (isSplitOnX) { + return p.getX(); + } + return p.getY(); + } + /** * Returns the location of this node * @@ -141,4 +159,72 @@ void setLeft(KdNode _left) { void setRight(KdNode _right) { right = _right; } + + /** + * Tests whether the node's left subtree may contain values + * in a given range envelope. + * + * @param isSplitOnX whether the node splits on X or Y + * @param env the range envelope + * @return true if the left subtree is in range + */ + boolean isRangeOverLeft(boolean isSplitOnX, Envelope env) { + double envMin; + if ( isSplitOnX ) { + envMin = env.getMinX(); + } else { + envMin = env.getMinY(); + } + double splitValue = splitValue(isSplitOnX); + boolean isInRange = envMin < splitValue; + return isInRange; + } + + /** + * Tests whether the node's right subtree may contain values + * in a given range envelope. + * + * @param isSplitOnX whether the node splits on X or Y + * @param env the range envelope + * @return true if the right subtree is in range + */ + boolean isRangeOverRight(boolean isSplitOnX, Envelope env) { + double envMax; + if ( isSplitOnX ) { + envMax = env.getMaxX(); + } else { + envMax = env.getMaxY(); + } + double splitValue = splitValue(isSplitOnX); + boolean isInRange = splitValue <= envMax; + return isInRange; + } + + /** + * Tests whether a point is strictly to the left + * of the splitting plane for this node. + * If so it may be in the left subtree of this node, + * Otherwise, the point may be in the right subtree. + * The point is to the left if its X (or Y) ordinate + * is less than the split value. + * + * @param isSplitOnX whether the node splits on X or Y + * @param pt the query point + * @return true if the point is strictly to the left. + * + * @see #splitValue(boolean) + */ + boolean isPointOnLeft(boolean isSplitOnX, Coordinate pt) { + double ptOrdinate; + if (isSplitOnX) { + ptOrdinate = pt.x; + } + else { + ptOrdinate = pt.y; + } + double splitValue = splitValue(isSplitOnX); + boolean isInRange = (ptOrdinate < splitValue); + return isInRange; + } + } diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index f268e6bfca..1c9e54ffe9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -16,35 +16,39 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Stack; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateList; import org.locationtech.jts.geom.Envelope; - /** - * An implementation of a 2-D KD-Tree. KD-trees provide fast range searching - * and fast lookup for point data. + * An implementation of a + * KD-Tree + * over two dimensions (X and Y). + * KD-trees provide fast range searching and fast lookup for point data. + * The tree is built dynamically by inserting points. + * The tree supports queries by range and for point equality. + * For querying an internal stack is used instead of recursion to avoid overflow. *

      * This implementation supports detecting and snapping points which are closer * than a given distance tolerance. * If the same point (up to tolerance) is inserted * more than once, it is snapped to the existing node. - * In other words, if a point is inserted which lies within the tolerance of a node already in the index, + * In other words, if a point is inserted which lies + * within the tolerance of a node already in the index, * it is snapped to that node. - * When a point is snapped to a node then a new node is not created but the count of the existing node - * is incremented. + * When an inserted point is snapped to a node then a new node is not created + * but the count of the existing node is incremented. * If more than one node in the tree is within tolerance of an inserted point, * the closest and then lowest node is snapped to. *

      - * Note that the structure of a KD-Tree depends on the order of insertion of the points. - * A tree may become imbalanced if the inserted points are coherent + * The structure of a KD-Tree depends on the order of insertion of the points. + * A tree may become unbalanced if the inserted points are coherent * (e.g. monotonic in one or both dimensions). * A perfectly balanced tree has depth of only log2(N), - * but an imbalanced tree may be much deeper. + * but an unbalanced tree may be much deeper. * This has a serious impact on query efficiency. - * Even worse, since recursion is used for querying the tree - * an extremely deep tree may cause a {@link StackOverflowError}. * One solution to this is to randomize the order of points before insertion * (e.g. by using Fisher-Yates shuffling). * @@ -237,7 +241,7 @@ public void visit(KdNode node) { private KdNode insertExact(Coordinate p, Object data) { KdNode currentNode = root; KdNode leafNode = root; - boolean isOddLevel = true; + boolean isXLevel = true; boolean isLessThan = true; /** @@ -254,10 +258,11 @@ private KdNode insertExact(Coordinate p, Object data) { return currentNode; } - if (isOddLevel) { - isLessThan = p.x < currentNode.getX(); + double splitValue = currentNode.splitValue(isXLevel); + if (isXLevel) { + isLessThan = p.x < splitValue; } else { - isLessThan = p.y < currentNode.getY(); + isLessThan = p.y < splitValue; } leafNode = currentNode; if (isLessThan) { @@ -268,7 +273,7 @@ private KdNode insertExact(Coordinate p, Object data) { currentNode = currentNode.getRight(); } - isOddLevel = ! isOddLevel; + isXLevel = ! isXLevel; } //System.out.println("<<"); // no node found, add new leaf node to tree @@ -282,81 +287,67 @@ private KdNode insertExact(Coordinate p, Object data) { return node; } - private void queryNode(KdNode currentNode, - Envelope queryEnv, boolean odd, KdNodeVisitor visitor) { - if (currentNode == null) - return; - - double min; - double max; - double discriminant; - if (odd) { - min = queryEnv.getMinX(); - max = queryEnv.getMaxX(); - discriminant = currentNode.getX(); - } else { - min = queryEnv.getMinY(); - max = queryEnv.getMaxY(); - discriminant = currentNode.getY(); - } - boolean searchLeft = min < discriminant; - boolean searchRight = discriminant <= max; - - // search is computed via in-order traversal - if (searchLeft) { - queryNode(currentNode.getLeft(), queryEnv, !odd, visitor); - } - if (queryEnv.contains(currentNode.getCoordinate())) { - visitor.visit(currentNode); - } - if (searchRight) { - queryNode(currentNode.getRight(), queryEnv, !odd, visitor); - } - - } - - private KdNode queryNodePoint(KdNode currentNode, - Coordinate queryPt, boolean odd) { - if (currentNode == null) - return null; - if (currentNode.getCoordinate().equals2D(queryPt)) - return currentNode; - - double ord; - double discriminant; - if (odd) { - ord = queryPt.getX(); - discriminant = currentNode.getX(); - } else { - ord = queryPt.getY(); - discriminant = currentNode.getY(); - } - boolean searchLeft = ord < discriminant; - - if (searchLeft) { - return queryNodePoint(currentNode.getLeft(), queryPt, !odd); - } - else { - return queryNodePoint(currentNode.getRight(), queryPt, !odd); - } - } - /** * Performs a range search of the points in the index and visits all nodes found. * - * @param queryEnv - * the range rectangle to query + * @param queryEnv the range rectangle to query * @param visitor a visitor to visit all nodes found by the search */ public void query(Envelope queryEnv, KdNodeVisitor visitor) { - queryNode(root, queryEnv, true, visitor); + + Stack queryStack = new Stack(); + KdNode currentNode = root; + boolean isXLevel = true; + + // search is computed via in-order traversal + while (true) { + if ( currentNode != null ) { + queryStack.push(new QueryStackFrame(currentNode, isXLevel)); + + boolean searchLeft = currentNode.isRangeOverLeft(isXLevel, queryEnv); + if ( searchLeft ) { + currentNode = currentNode.getLeft(); + if ( currentNode != null ) { + isXLevel = ! isXLevel; + } + } + else { + currentNode = null; + } + } + else if ( ! queryStack.isEmpty() ) { + // currentNode is empty, so pop stack + QueryStackFrame frame = queryStack.pop(); + currentNode = frame.getNode(); + isXLevel = frame.isXLevel(); + + //-- check if search matches current node + if ( queryEnv.contains(currentNode.getCoordinate()) ) { + visitor.visit(currentNode); + } + + boolean searchRight = currentNode.isRangeOverRight(isXLevel, queryEnv); + if ( searchRight ) { + currentNode = currentNode.getRight(); + if ( currentNode != null ) { + isXLevel = ! isXLevel; + } + } + else { + currentNode = null; + } + } else { + //-- stack is empty and no current node + return; + } + } + } - + /** * Performs a range search of the points in the index. * - * @param queryEnv - * the range rectangle to query + * @param queryEnv the range rectangle to query * @return a list of the KdNodes found */ public List query(Envelope queryEnv) { @@ -374,7 +365,7 @@ public List query(Envelope queryEnv) { * a list to accumulate the result nodes into */ public void query(Envelope queryEnv, final List result) { - queryNode(root, queryEnv, true, new KdNodeVisitor() { + query(queryEnv, new KdNodeVisitor() { public void visit(KdNode node) { result.add(node); @@ -390,7 +381,23 @@ public void visit(KdNode node) { * @return the point node, if it is found in the index, or null if not */ public KdNode query(Coordinate queryPt) { - return queryNodePoint(root, queryPt, true); + KdNode currentNode = root; + boolean isXLevel = true; + + while (currentNode != null) { + if ( currentNode.getCoordinate().equals2D(queryPt) ) + return currentNode; + + boolean searchLeft = currentNode.isPointOnLeft(isXLevel, queryPt); + if ( searchLeft ) { + currentNode = currentNode.getLeft(); + } else { + currentNode = currentNode.getRight(); + } + isXLevel = ! isXLevel; + } + //-- point not found + return null; } /** @@ -428,4 +435,23 @@ private int sizeNode(KdNode currentNode) { int sizeR = sizeNode(currentNode.getRight()); return 1 + sizeL + sizeR; } + + static class QueryStackFrame { + private KdNode node; + private boolean isXLevel = false; + + public QueryStackFrame(KdNode node, boolean isXLevel) { + this.node = node; + this.isXLevel = isXLevel; + } + + public KdNode getNode() { + return node; + } + + public boolean isXLevel() { + return isXLevel; + } + + } } diff --git a/modules/core/src/test/java/test/jts/perf/index/KdtreeStressTest.java b/modules/core/src/test/java/test/jts/perf/index/KdtreeStressTest.java new file mode 100644 index 0000000000..ed1dbf940b --- /dev/null +++ b/modules/core/src/test/java/test/jts/perf/index/KdtreeStressTest.java @@ -0,0 +1,55 @@ +package test.jts.perf.index; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.index.kdtree.KdTree; + +/** + * Tests an issue where deep KdTrees caused a {@link StackOverflowError} + * when using a recursive query implementation. + * + * See a fix for this issue in GEOS + * at https://github.com/libgeos/geos/pull/481. + * + * @author mdavis + * + */ +public class KdtreeStressTest { + + // In code with recursive query 50,000 points causes StackOverflowError + int NUM_PTS = 50000; + + public static void main(String[] args) throws Exception + { + KdtreeStressTest test = new KdtreeStressTest(); + test.run(); + } + + private void run() { + System.out.format("Loading iIndex with %d points\n", NUM_PTS); + KdTree index = createUnbalancedTree(NUM_PTS); + + System.out.format("Querying Index loaded with %d points\n", NUM_PTS); + for (int i = 0; i < NUM_PTS; i++) { + Envelope env = new Envelope(i, i + 10, 0, 1); + index.query(env); + } + System.out.format("Queries complete\n"); + } + + /** + * Create an unbalanced tree by loading a + * series of monotonically increasing points + * + * @param numPts number of points to load + * @return a new index + */ + private KdTree createUnbalancedTree(int numPts) { + KdTree index = new KdTree(); + for (int i = 0; i < numPts; i++) { + Coordinate pt = new Coordinate(i, 0); + index.insert(pt); + } + return index; + } +} From 49077b2a33dab6b4c201940242a63efc29bc4083 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 5 Oct 2021 11:11:04 -0700 Subject: [PATCH 092/275] Change KdTree to use Deque Signed-off-by: Martin Davis --- .../java/org/locationtech/jts/index/kdtree/KdTree.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index 1c9e54ffe9..268775f8d8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -12,11 +12,12 @@ package org.locationtech.jts.index.kdtree; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.Deque; import java.util.Iterator; import java.util.List; -import java.util.Stack; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateList; @@ -294,8 +295,8 @@ private KdNode insertExact(Coordinate p, Object data) { * @param visitor a visitor to visit all nodes found by the search */ public void query(Envelope queryEnv, KdNodeVisitor visitor) { - - Stack queryStack = new Stack(); + //-- Deque is faster than Stack + Deque queryStack = new ArrayDeque(); KdNode currentNode = root; boolean isXLevel = true; From 1fbe4daa4be27183564ca4e3f73b380a2b35defb Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 5 Oct 2021 11:35:46 -0700 Subject: [PATCH 093/275] Make KdTree inner class private Signed-off-by: Martin Davis --- .../src/main/java/org/locationtech/jts/index/kdtree/KdTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index 268775f8d8..d1d917fdf6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -437,7 +437,7 @@ private int sizeNode(KdNode currentNode) { return 1 + sizeL + sizeR; } - static class QueryStackFrame { + private static class QueryStackFrame { private KdNode node; private boolean isXLevel = false; From 20a9e4f4d33ed82a1109947257a52d5426fe3a2b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 5 Oct 2021 11:37:34 -0700 Subject: [PATCH 094/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 18f4be7dd5..19c127ae12 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -27,6 +27,7 @@ Distributions for older JTS versions can be obtained at the * Add `ConstrainedDelaunayTriangulator` and `PolygonTriangulator` (#775) * Add `Tri` data structure for representing triangulations (#775) * Simplify and fix logic of `BufferParameters.setQuadSegs` (#778) +* Improve `KdTree` query code to avoid recursion (#779) # Version 1.18.2 From fa7c5b61b192fe5b6159ec590e47691abf098471 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 5 Oct 2021 11:41:30 -0700 Subject: [PATCH 095/275] Minor KdTree code reorg Signed-off-by: Martin Davis --- .../locationtech/jts/index/kdtree/KdTree.java | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index d1d917fdf6..cc1a99a1a2 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -342,9 +342,26 @@ else if ( ! queryStack.isEmpty() ) { return; } } - } + private static class QueryStackFrame { + private KdNode node; + private boolean isXLevel = false; + + public QueryStackFrame(KdNode node, boolean isXLevel) { + this.node = node; + this.isXLevel = isXLevel; + } + + public KdNode getNode() { + return node; + } + + public boolean isXLevel() { + return isXLevel; + } + } + /** * Performs a range search of the points in the index. * @@ -436,23 +453,4 @@ private int sizeNode(KdNode currentNode) { int sizeR = sizeNode(currentNode.getRight()); return 1 + sizeL + sizeR; } - - private static class QueryStackFrame { - private KdNode node; - private boolean isXLevel = false; - - public QueryStackFrame(KdNode node, boolean isXLevel) { - this.node = node; - this.isXLevel = isXLevel; - } - - public KdNode getNode() { - return node; - } - - public boolean isXLevel() { - return isXLevel; - } - - } } From 4da89c8520d366d9ce678617a43a18f70111da52 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 6 Oct 2021 15:08:39 -0700 Subject: [PATCH 096/275] Add CreateRandomShape function robertsPoints Signed-off-by: Martin Davis --- .../function/CreateRandomShapeFunctions.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java index 708e313afa..e828773f86 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateRandomShapeFunctions.java @@ -173,6 +173,52 @@ private static double haltonOrdinate(int index, int base) return result; } + static final double PHI2 = 1.32471795724474602596; + + /** + * Creates a set of quasi-random 2D points using the Roberts recurrences. + * Roberts recurrences + * are based on the generalized Golden Ratio (for the 2D case, Phi2). + * They have excellent low-discrepancy characteristics. + * This mean they are non-periodic and have less clustering + * than random points or Halton points. + * + * @param geom + * @param nPts + * @return + */ + @Metadata(description="Create Roberts quasi-random points") + public static Geometry robertsPoints(Geometry geom, int nPts) + { + Envelope env = FunctionsUtil.getEnvelopeOrDefault(geom); + Coordinate[] pts = new Coordinate[nPts]; + double baseX = env.getMinX(); + double baseY = env.getMinY(); + + final double A1 = 1.0 / PHI2; + final double A2 = 1.0/(PHI2 * PHI2); + double r1 = 0.5; + double r2 = 0.5; + int i = 0; + while (i < nPts) { + r1 = quasirandom(r1, A1); + r2 = quasirandom(r2, A2); + double x = baseX + env.getWidth() * r1; + double y = baseY + env.getHeight() * r2; + Coordinate p = new Coordinate(x, y); + if (! env.contains(p)) + continue; + pts[i++] = p; + } + return FunctionsUtil.getFactoryOrDefault(geom).createMultiPoint(pts); + } + + private static double quasirandom(double curr, double alpha) { + double next = curr + alpha; + if (next < 1) return next; + return next - Math.floor(next); + } + public static Geometry randomSegments(Geometry g, int nPts) { Envelope env = FunctionsUtil.getEnvelopeOrDefault(g); GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g); From 94a62219d19621bb6092969e562e9aaa945fe5fa Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 9 Oct 2021 22:46:20 -0700 Subject: [PATCH 097/275] Add SnappingNoder seeding (#780) * Add SnappingNoder seeding * Add MathUtil.quasirand Signed-off-by: Martin Davis --- .../function/SpatialIndexFunctions.java | 132 ++++++++++++++++++ .../locationtech/jts/index/kdtree/KdTree.java | 10 ++ .../org/locationtech/jts/math/MathUtil.java | 47 +++++++ .../jts/noding/snap/SnappingNoder.java | 34 ++++- .../jts/noding/snap/SnappingPointIndex.java | 14 ++ 5 files changed, 236 insertions(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java index 9124b1eb1f..2659400830 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/SpatialIndexFunctions.java @@ -29,6 +29,7 @@ import org.locationtech.jts.index.chain.MonotoneChain; import org.locationtech.jts.index.chain.MonotoneChainBuilder; import org.locationtech.jts.index.hprtree.HPRtree; +import org.locationtech.jts.index.kdtree.KdNode; import org.locationtech.jts.index.kdtree.KdTree; import org.locationtech.jts.index.quadtree.Quadtree; import org.locationtech.jts.index.strtree.AbstractNode; @@ -36,6 +37,7 @@ import org.locationtech.jts.index.strtree.GeometryItemDistance; import org.locationtech.jts.index.strtree.STRtree; //import org.locationtech.jts.triangulatepoly.VertexSequencePackedRtree; +import org.locationtech.jts.math.MathUtil; public class SpatialIndexFunctions @@ -76,6 +78,117 @@ public static Geometry kdTreeQueryRepeated(Geometry pts, Geometry queryEnv, doub return pts.getFactory().createMultiPoint(resultCoords); } + public static Geometry kdTreeGraphSeed(Geometry geom) { + return kdTreeGraph(geom, buildKdTreeSeed(geom, 0)); + } + + public static Geometry kdTreeGraph(Geometry geom) { + return kdTreeGraph(geom, buildKdTree(geom, 0)); + } + + private static Geometry kdTreeGraph(Geometry geom, KdTree index) { + KdNode root = index.getRoot(); + List edges = new ArrayList(); + + double x = geom.getEnvelopeInternal().centre().getX(); + double xInc = geom.getEnvelopeInternal().getWidth() / 2; + addGraphEdges(root, true, 0, x, xInc, edges, geom.getFactory()); + return geom.getFactory().buildGeometry(edges); + } + + private static void addGraphEdges(KdNode node, + boolean isXLevel, int depth, double x, double xInc, + List edges, GeometryFactory factory) { + double xInc2 = xInc / 2; + KdNode left = node.getLeft(); + if (left != null) { + double xLeft = x - xInc2; + Geometry edgeLeft = factory.createLineString(new Coordinate[] { + new Coordinate(x, -depth), new Coordinate(xLeft, -depth-1) + }); + edges.add(edgeLeft); + addGraphEdges(left, ! isXLevel, depth+1, xLeft, xInc2, edges, factory); + } + KdNode right = node.getRight(); + if (right != null) { + double xRight = x + xInc2; + Geometry edgeRight = factory.createLineString(new Coordinate[] { + new Coordinate(x, -depth), new Coordinate(xRight, -depth-1) + }); + edges.add(edgeRight); + addGraphEdges(right, ! isXLevel, depth+1, xRight, xInc2, edges, factory); + } + } + + public static Geometry kdTreeSplits(Geometry geom) { + return kdTreeSplits(geom, buildKdTree(geom, 0)); + } + + public static Geometry kdTreeSplitsSeed(Geometry geom) { + return kdTreeSplits(geom, buildKdTreeSeed(geom, 0)); + } + + private static Geometry kdTreeSplits(Geometry geom, KdTree index) { + Envelope extent = geom.getEnvelopeInternal(); + KdNode root = index.getRoot(); + List splits = new ArrayList(); + + addSplits(root, true, extent, splits, geom.getFactory()); + return geom.getFactory().buildGeometry(splits); + } + + private static void addSplits(KdNode node, boolean isXLevel, Envelope extent, List splits, + GeometryFactory factory) { + double splitVal = node.splitValue(isXLevel); + Geometry splitLine = createSplitLine(extent, splitVal, isXLevel, factory); + splits.add(splitLine); + + KdNode left = node.getLeft(); + if (left != null) { + addSplits(left, ! isXLevel, splitExtent(extent, splitVal, isXLevel, true), splits, factory); + } + KdNode right = node.getRight(); + if (right != null) { + addSplits(right, ! isXLevel, splitExtent(extent, splitVal, isXLevel, false), splits, factory); + } + } + + private static Envelope splitExtent(Envelope extent, double splitVal, boolean isXLevel, boolean isLeft) { + double xMin = extent.getMinX(); + double yMin = extent.getMinY(); + double xMax = extent.getMaxX(); + double yMax = extent.getMaxY(); + if (isXLevel) { + if (isLeft) { + xMax = splitVal; + } + else { + xMin = splitVal; + } + } + else { + if (isLeft) { + yMax = splitVal; + } + else { + yMin = splitVal; + } + } + return new Envelope(xMin, xMax, yMin, yMax); + } + + private static Geometry createSplitLine(Envelope extent, double splitVal, + boolean isXLevel, GeometryFactory factory) { + + double x1 = isXLevel ? splitVal : extent.getMinX(); + double y1 = isXLevel ? extent.getMinY() : splitVal; + double x2 = isXLevel ? splitVal : extent.getMaxX(); + double y2 = isXLevel ? extent.getMaxY() : splitVal; + + Coordinate[] pts = { new Coordinate(x1, y1), new Coordinate(x2, y2) }; + return factory.createLineString(pts); + } + private static KdTree buildKdTree(Geometry geom, double tolerance) { final KdTree index = new KdTree(tolerance); Coordinate[] pt = geom.getCoordinates(); @@ -85,6 +198,25 @@ private static KdTree buildKdTree(Geometry geom, double tolerance) { return index; } + private static KdTree buildKdTreeSeed(Geometry geom, double tolerance) { + final KdTree tree = new KdTree(tolerance); + Coordinate[] pt = geom.getCoordinates(); + + //-- seed the tree with some randomly selected points + int numSeed = pt.length / 100; + double rand = 0; + for (int i = 0; i < numSeed; i++) { + rand = MathUtil.quasirandom(rand); + int index = (int) (pt.length * rand); + tree.insert(pt[index]); + } + //-- insert all the points + for (int i = 0; i < pt.length; i++) { + tree.insert(pt[i]); + } + return tree; + } + public static Geometry strTreeBounds(Geometry geoms) { STRtree index = new STRtree(); diff --git a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java index cc1a99a1a2..ad40b75f25 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/kdtree/KdTree.java @@ -116,6 +116,15 @@ public KdTree(double tolerance) { this.tolerance = tolerance; } + /** + * Gets the root node of this tree. + * + * @return the root node of the tree + */ + public KdNode getRoot() { + return root; + } + /** * Tests whether the index contains any items. * @@ -453,4 +462,5 @@ private int sizeNode(KdNode currentNode) { int sizeR = sizeNode(currentNode.getRight()); return 1 + sizeL + sizeR; } + } diff --git a/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java b/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java index 63bca1b672..02b307be16 100644 --- a/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java +++ b/modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java @@ -147,4 +147,51 @@ public static double min(double v1, double v2, double v3, double v4) if (v4 < min) min = v4; return min; } + + /** + * The inverse of the Golden Ratio phi. + */ + public static final double PHI_INV = (Math.sqrt(5) - 1.0) / 2.0; + + /** + * Generates a quasi-random sequence of numbers in the range [0,1]. + * They are produced by an additive recurrence with 1/φ as the constant. + * This produces a low-discrepancy sequence which is more evenly + * distribute than random numbers. + *

      + * See Wikipedia: Low-discrepancy Sequences - Additive Recurrence. + *

      + * The sequence is initialized by calling it + * with any positive fractional number; 0 works well for most uses. + * + * @param curr the current number in the sequence + * @return the next value in the sequence + */ + public static double quasirandom( double curr) { + return quasirandom(curr, PHI_INV); + } + + /** + * Generates a quasi-random sequence of numbers in the range [0,1]. + * They are produced by an additive recurrence with constant α. + *

      +   *     R(α) :  tn = { t0 + nα },  n = 1,2,3,...   
      +   * 
      + * When α is irrational this produces a + * Low discrepancy sequence + * which is more evenly + * distributed than random numbers. + *

      + * The sequence is initialized by calling it + * with any positive fractional number. 0 works well for most uses. + * + * @param curr the current number in the sequence + * @param alpha the sequence additive constant + * @return the next value in the sequence + */ + public static double quasirandom( double curr, double alpha) { + double next = curr + alpha; + if (next < 1) return next; + return next - Math.floor(next); + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java index c14e7ca3ed..9c9834ea1b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingNoder.java @@ -17,6 +17,7 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateList; +import org.locationtech.jts.math.MathUtil; import org.locationtech.jts.noding.MCIndexNoder; import org.locationtech.jts.noding.NodedSegmentString; import org.locationtech.jts.noding.Noder; @@ -64,8 +65,9 @@ public SnappingNoder(double snapTolerance) { } /** + * Gets the noded result. + * * @return a Collection of NodedSegmentStrings representing the substrings - * */ public Collection getNodedSubstrings() { @@ -73,6 +75,8 @@ public Collection getNodedSubstrings() } /** + * Computes the noding of a set of {@link SegmentString}s. + * * @param inputSegStrings a Collection of SegmentStrings */ public void computeNodes(Collection inputSegStrings) @@ -82,13 +86,41 @@ public void computeNodes(Collection inputSegStrings) } private List snapVertices(Collection segStrings) { + //Stopwatch sw = new Stopwatch(); sw.start(); + seedSnapIndex(segStrings); + List nodedStrings = new ArrayList(); for (SegmentString ss : segStrings) { nodedStrings.add( snapVertices(ss) ); } + //System.out.format("Index depth = %d Time: %s\n", snapIndex.depth(), sw.getTimeString()); return nodedStrings; } + /** + * Seeds the snap index with a small set of vertices + * chosen quasi-randomly using a low-discrepancy sequence. + * Seeding the snap index KdTree induces a more balanced tree. + * This prevents monotonic runs of vertices + * unbalancing the tree and causing poor query performance. + * + * @param segStrings the segStrings to be noded + */ + private void seedSnapIndex(Collection segStrings) { + final int SEED_SIZE_FACTOR = 100; + + for (SegmentString ss : segStrings) { + Coordinate[] pts = ss.getCoordinates(); + int numPtsToLoad = pts.length / SEED_SIZE_FACTOR; + double rand = 0.0; + for (int i = 0; i < numPtsToLoad; i++) { + rand = MathUtil.quasirandom(rand); + int index = (int) (pts.length * rand); + snapIndex.snap(pts[index]); + } + } + } + private NodedSegmentString snapVertices(SegmentString ss) { Coordinate[] snapCoords = snap(ss.getCoordinates()); return new NodedSegmentString(snapCoords, ss.getData()); diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java index 68496d4dde..dcdf46ab90 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snap/SnappingPointIndex.java @@ -59,8 +59,22 @@ public Coordinate snap(Coordinate p) { return node.getCoordinate(); } + /** + * Gets the snapping tolerance value for the index. + * + * @return the snapping tolerance value + */ public double getTolerance() { return snapTolerance; } + /** + * Computes the depth of the index tree. + * + * @return the depth of the index tree + */ + public int depth() { + return snapPointIndex.depth(); + } + } From 1f9124f72f0e5d4c25fdfa9e9ffd6e92e5070825 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 9 Oct 2021 22:48:03 -0700 Subject: [PATCH 098/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 19c127ae12..384a30f724 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -28,6 +28,7 @@ Distributions for older JTS versions can be obtained at the * Add `Tri` data structure for representing triangulations (#775) * Simplify and fix logic of `BufferParameters.setQuadSegs` (#778) * Improve `KdTree` query code to avoid recursion (#779) +* Add `KdTree` seeding to`SnappingNoder` (#780) # Version 1.18.2 From 1fee2fbbee92d20695130ba2ae3d49496d867d33 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 11 Oct 2021 07:28:47 -0700 Subject: [PATCH 099/275] Fix robustness test precision tolerance Signed-off-by: Martin Davis --- .../org/locationtech/jtstest/testrunner/SimpleReportWriter.java | 2 +- .../resources/testxml/robust/overlay/TestOverlay-pg-4738.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/SimpleReportWriter.java b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/SimpleReportWriter.java index 4052cbcf2e..beed109b6a 100644 --- a/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/SimpleReportWriter.java +++ b/modules/tests/src/main/java/org/locationtech/jtstest/testrunner/SimpleReportWriter.java @@ -91,7 +91,7 @@ else if (! test.isPassed()) { reportBuf.write(" Expected: " + test.getExpectedResult().toFormattedString() + "\n"); } try { - reportBuf.write(" Actual: " + test.getActualResult().toFormattedString() + "\n"); + reportBuf.write(" Actual: " + test.getActualResult().toFormattedString() + "\n"); } catch (Exception e) { Assert.shouldNeverReachHere(e.toString()); diff --git a/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-pg-4738.xml b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-pg-4738.xml index 0913a2c911..256bb4aebd 100644 --- a/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-pg-4738.xml +++ b/modules/tests/src/test/resources/testxml/robust/overlay/TestOverlay-pg-4738.xml @@ -4,6 +4,7 @@ Union of LineStrings fails using simple noding. + 1E-12 From e1767327209a6ee331a55186177b12373fb8419c Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Wed, 13 Oct 2021 07:20:48 +0200 Subject: [PATCH 100/275] Add DiscreteFrechetDistance (#764) * Add DiscreteFrechetDistance * Add FrechetSimilarityMeasure * Fix HausdorffSimilarityMeasure to handle equal `POINT` geometries by using fast exit if `DiscreteHausdorffDistance.distance` returns 0d. Signed-off-by: Felix Obermaier --- .../distance/DiscreteFrechetDistance.java | 569 ++++++++++++++++++ .../algorithm/distance/PointPairDistance.java | 42 +- .../match/FrechetSimilarityMeasure.java | 66 ++ .../match/HausdorffSimilarityMeasure.java | 29 +- .../DiscreteFrechetDistanceLinear.java | 105 ++++ .../distance/DiscreteFrechetDistanceTest.java | 87 +++ .../distance/MatrixStorageImplTests.java | 92 +++ .../match/FrechetSimilarityMeasureTest.java | 63 ++ .../match/HausdorffSimilarityMeasureTest.java | 49 ++ 9 files changed, 1078 insertions(+), 24 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java new file mode 100644 index 0000000000..c934584aa9 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.distance; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.util.Assert; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * The Fréchet distance is a measure of similarity between curves. Thus, it can + * be used like the Hausdorff distance. + *

      + * An analogy for the Fréchet distance taken from + * + * Computing Discrete Fréchet Distance + *

      + * A man is walking a dog on a leash: the man can move
      + * on one curve, the dog on the other; both may vary their
      + * speed, but backtracking is not allowed.
      + * 
      + *

      + * Its metric is better than Hausdorff's because it takes the flow of the curves + * into account. It is possible that two curves have a small Hausdorff but a large + * Fréchet distance. + *

      + * This implementation is base on the following optimized Fréchet distance algorithm: + *

      Thomas Devogele, Maxence Esnault, Laurent Etienne. Distance discrète de Fréchet optimisée. Spatial
      + * Analysis and Geomatics (SAGEO), Nov 2016, Nice, France. hal-02110055
      + *

      + * Several matrix storage implementations are provided + * + * @see Fréchet distance + * @see + * Computing Discrete Fréchet Distance + * @see Distance discrète de Fréchet optimisée + * @see + * Fast Discrete Fréchet Distance + */ +public class DiscreteFrechetDistance { + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1) { + + DiscreteFrechetDistance dist = new DiscreteFrechetDistance(g0, g1); + return dist.distance(); + } + + private final Geometry g0; + private final Geometry g1; + private PointPairDistance ptDist; + + /** + * Creates an instance of this class using the provided geometries. + * + * @param g0 a geometry + * @param g1 a geometry + */ + private DiscreteFrechetDistance(Geometry g0, Geometry g1) { + this.g0 = g0; + this.g1 = g1; + } + + /** + * Compute the {@code Discrete Fréchet Distance} between two geometries + * + * @return the Discrete Fréchet Distance + */ + private double distance() { + Coordinate[] coords0 = g0.getCoordinates(); + Coordinate[] coords1 = g1.getCoordinates(); + + MatrixStorage distances = createMatrixStorage(coords0.length, coords1.length); + int[] diagonal = bresenhamLine(coords0.length, coords1.length); + + HashMap distanceToPair = new HashMap<>(); + computeCoordinateDistances(coords0, coords1, diagonal, distances, distanceToPair); + ptDist = computeFrechet(coords0, coords1, diagonal, distances, distanceToPair); + + return ptDist.getDistance(); + } + + /** + * Creates a matrix storage + * @param rows the number of rows + * @param cols the number of columns + * @return a matrix storage + */ + private MatrixStorage createMatrixStorage(int rows, int cols) { + + int max = Math.max(rows, cols); + // NOTE: these constraints need to be verified + if (max < 1024) + return new RectMatrix(rows, cols, Double.POSITIVE_INFINITY); + + return new CsrMatrix(rows, cols, Double.POSITIVE_INFINITY); + } + + /** + * Gets the pair of Coordinates that are {@link #distance()} apart. + * + * @return the pair of Coordinates that are {@link #distance()} apart + */ + public Coordinate[] getCoordinates() { + if (ptDist == null) + distance(); + + return ptDist.getCoordinates(); + } + + /** + * Compute the Fréchet Distance for the given distance matrix. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param distances a sparse distance matrix + * @param distanceToPair a lookup for distance and a coordinate pair + * @param diagonal an array of alternating row/col index values for the diagonal of the distance matrix + * + */ + private static PointPairDistance computeFrechet(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, + MatrixStorage distances, HashMap distanceToPair) { + for (int d = 0; d < diagonal.length; d += 2) { + int i0 = diagonal[d]; + int j0 = diagonal[d + 1]; + + for (int i = i0; i < coords0.length; i++) { + if (distances.isValueSet(i, j0)) { + double dist = getMinDistanceAtCorner(distances, i, j0); + if (dist > distances.get(i, j0)) + distances.set(i, j0, dist); + } + else { + break; + } + } + for (int j = j0 + 1; j < coords1.length; j++) { + if (distances.isValueSet(i0, j)) { + double dist = getMinDistanceAtCorner(distances, i0, j); + if (dist > distances.get(i0, j)) + distances.set(i0, j, dist); + } + else { + break; + } + } + } + + PointPairDistance result = new PointPairDistance(); + double distance = distances.get(coords0.length-1, coords1.length - 1); + int[] index = distanceToPair.get(distance); + if (index != null) + result.initialize(coords0[index[0]], coords1[index[1]], distance); + else + Assert.shouldNeverReachHere("Pair of points not recorded for computed distance"); + + return result; + } + + /** + * Returns the minimum distance at the corner ({@code i, j}). + * + * @param matrix A sparse matrix + * @param i the column index + * @param j the row index + * @return the minimum distance + */ + private static double getMinDistanceAtCorner(MatrixStorage matrix, int i, int j) { + if (i > 0 && j > 0) { + double d0 = matrix.get(i - 1, j - 1); + double d1 = matrix.get(i - 1, j); + double d2 = matrix.get(i, j - 1); + return Math.min(Math.min(d0, d1), d2); + } + if (i == 0 && j == 0) + return matrix.get(0, 0); + + if (i == 0) + return matrix.get(0, j - 1); + + // j == 0 + return matrix.get(i - 1, 0); + } + + /** + * Computes relevant distances between pairs of {@link Coordinate}s for the + * computation of the {@code Discrete Fréchet Distance}. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param diagonal an array of encoded row/col index values for the diagonal of the distance matrix + * @param distances the sparse distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance. + */ + private void computeCoordinateDistances(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, + MatrixStorage distances, HashMap distanceToPair) { + int numDiag = diagonal.length; + double maxDistOnDiag = 0d; + int imin = 0, jmin = 0; + int numCoords0 = coords0.length; + int numCoords1 = coords1.length; + + // First compute all the distances along the diagonal. + // Record the maximum distance. + + for (int k = 0; k < numDiag; k += 2) { + int i0 = diagonal[k]; + int j0 = diagonal[k + 1]; + double diagDist = coords0[i0].distance(coords1[j0]); + if (diagDist > maxDistOnDiag) maxDistOnDiag = diagDist; + distances.set(i0, j0, diagDist); + distanceToPair.putIfAbsent(diagDist, new int[] {i0, j0}); + } + + // Check for distances shorter than maxDistOnDiag along the diagonal + for (int k = 0; k < numDiag - 2; k += 2) { + // Decode index + int i0 = diagonal[k]; + int j0 = diagonal[k + 1]; + + // Get reference coordinates for col and row + Coordinate coord0 = coords0[i0]; + Coordinate coord1 = coords1[j0]; + + // Check for shorter distances in this row + int i = i0 + 1; + for (; i < numCoords0; i++) { + if (!distances.isValueSet(i, j0)) { + double dist = coords0[i].distance(coord1); + if (dist < maxDistOnDiag || i < imin) { + distances.set(i, j0, dist); + distanceToPair.putIfAbsent(dist, new int[] {i, j0}); + } + else + break; + } + else + break; + } + imin = i; + + // Check for shorter distances in this column + int j = j0 + 1; + for (; j < numCoords1; j++) { + if (!distances.isValueSet(i0, j)) { + double dist = coord0.distance(coords1[j]); + if (dist < maxDistOnDiag || j < jmin) + { + distances.set(i0, j, dist); + distanceToPair.putIfAbsent(dist, new int[] {i0, j}); + } + else + break; + } + else + break; + } + jmin = j; + } + + //System.out.println(distances.toString()); + } + + /** + * Implementation of the + * Bresenham's line algorithm for the diagonal of a {@code numCols x numRows} grid. + * + * @param numCols the number of columns + * @param numRows the number of rows + * @return an array of column and row indices bitwise-or combined. + */ + private static int[] bresenhamLine(int numCols, int numRows) { + int dim = Math.max(numCols, numRows); + int[] pairs = new int[2 * dim]; + + int sx = 0 > numCols ? -1 : 1; + int sy = 0 > numRows ? -1 : 1; + int x = 0; + int y = 0; + + int err; + if (numCols > numRows) { + err = numCols / 2; + for (int i = 0, j = 0; i < numCols; i++) { + pairs[j++] = x; + pairs[j++] = y; + err -= numRows; + if (err < 0) { + y += sy; + err += numCols; + } + x += sx; + } + } else { + err = numRows / 2; + for (int i = 0, j = 0; i < numRows; i++) { + pairs[j++] = x; + pairs[j++] = y; + err -= numCols; + if (err < 0) { + x += sx; + err += numRows; + } + y += sy; + } + } + return pairs; + } + + /** + * Abstract base class for storing 2d matrix data + */ + abstract static class MatrixStorage { + + protected final int numRows; + protected final int numCols; + protected final double defaultValue; + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + public MatrixStorage(int numRows, int numCols, double defaultValue) + { + this.numRows = numRows; + this.numCols = numCols; + this.defaultValue = defaultValue; + } + + /** + * Gets the matrix value at i, j + * @param i the row index + * @param j the column index + * @return The matrix value at i, j + */ + public abstract double get(int i, int j); + + /** + * Sets the matrix value at i, j + * @param i the row index + * @param j the column index + * @param value The matrix value to set at i, j + */ + public abstract void set(int i, int j, double value); + + /** + * Gets a flag indicating if the matrix has a set value, e.g. one that is different + * than {@link MatrixStorage#defaultValue}. + * @param i the row index + * @param j the column index + * @return a flag indicating if the matrix has a set value + */ + public abstract boolean isValueSet(int i, int j); + + /* For debugging purposes only + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < this.numRows; i++) + { + sb.append('['); + for(int j = 0; j < this.numCols; j++) + { + if (j > 0) + sb.append(", "); + sb.append(String.format(java.util.Locale.ROOT, "%8.4f", get(i, j))); + } + sb.append(']'); + if (i < this.numRows - 1) sb.append(",\n"); + } + sb.append(']'); + return sb.toString(); + } + */ + } + + /** + * Straight forward implementation of a rectangular matrix + */ + final static class RectMatrix extends MatrixStorage { + + private final double[] matrix; + + /** + * Creates an instance of this matrix using the given number of rows and columns. + * A default value can be specified + * + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + public RectMatrix(int numRows, int numCols, double defaultValue) + { + super(numRows, numCols, defaultValue); + this.matrix = new double[numRows * numCols]; + Arrays.fill(this.matrix, defaultValue); + } + + public double get(int i, int j) { return this.matrix[i * numCols + j]; } + + public void set(int i, int j, double value) { + this.matrix[i * numCols + j] = value; + } + + public boolean isValueSet(int i, int j) { + return Double.doubleToLongBits(get(i, j)) != Double.doubleToLongBits(this.defaultValue); + } + } + + /** + * A matrix implementation that adheres to the + * + * Compressed sparse row format.
      + * Note: Unfortunately not as fast as expected. + */ + final static class CsrMatrix extends MatrixStorage { + + private double[] v; + private final int[] ri; + private int[] ci; + + public CsrMatrix(int numRows, int numCols, double defaultValue) { + this(numRows, numCols, defaultValue, expectedValuesHeuristic(numRows, numCols)); + } + public CsrMatrix(int numRows, int numCols, double defaultValue, int expectedValues) { + super(numRows, numCols, defaultValue); + this.v = new double[expectedValues]; + this.ci = new int[expectedValues]; + this.ri = new int[numRows + 1]; + } + + /** + * Computes an initial value for the number of expected values + * @param numRows the number of rows + * @param numCols the number of columns + * @return the expected number of values in the sparse matrix + */ + private static int expectedValuesHeuristic(int numRows, int numCols) { + int max = Math.max(numRows, numCols); + return max * max / 10; + } + + private int indexOf(int i, int j) { + int cLow = this.ri[i]; + int cHigh = this.ri[i+1]; + if (cHigh <= cLow) return ~cLow; + + return Arrays.binarySearch(this.ci, cLow, cHigh, j); + } + + @Override + public double get(int i, int j) { + + // get the index in the vector + int vi = indexOf(i, j); + + // if the vector index is negative, return default value + if (vi < 0) + return defaultValue; + + return this.v[vi]; + } + + @Override + public void set(int i, int j, double value) { + + // get the index in the vector + int vi = indexOf(i, j); + + // do we already have a value? + if (vi < 0) + { + // no, we don't, we need to ensure space! + ensureCapacity(this.ri[this.numRows] + 1); + + // update row indices + for (int ii = i + 1; ii <= this.numRows; ii++) + ri[ii] += 1; + + // move and update column indices, move values + vi = ~vi; + for (int ii = this.ri[this.numRows]; ii > vi; ii--) + { + this.ci[ii] = this.ci[ii - 1]; + this.v[ii] = this.v[ii - 1]; + } + + // insert column index + ci[vi] = j; + } + + // set the new value + v[vi] = value; + } + + @Override + public boolean isValueSet(int i, int j) { + return indexOf(i, j) >= 0; + } + + + /** + * Ensures that the column index vector (ci) and value vector (v) are sufficiently large. + * @param required the number of items to store in the matrix + */ + private void ensureCapacity(int required) { + if (required < this.v.length) + return; + + int increment = Math.max(this.numRows, this.numCols); + this.v = Arrays.copyOf(this.v, this.v.length + increment); + this.ci = Arrays.copyOf(this.ci, this.v.length + increment); + } + } + + /** + * A sparse matrix based on java's {@link HashMap}. + */ + final static class HashMapMatrix extends MatrixStorage { + + private final HashMap matrix; + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue a default value + */ + public HashMapMatrix(int numRows, int numCols, double defaultValue) { + super(numRows, numCols, defaultValue); + this.matrix = new HashMap<>(); + } + + public double get(int i, int j) { + long key = (long)i << 32 | j; + return matrix.getOrDefault(key, this.defaultValue); + } + + public void set(int i, int j, double value) { + long key = (long)i << 32 | j; + matrix.put(key, value); + } + + public boolean isValueSet(int i, int j) { + long key = (long)i << 32 | j; + return matrix.containsKey(key); + } + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java index d3adef89fe..3f01fb81c5 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/PointPairDistance.java @@ -22,31 +22,38 @@ */ public class PointPairDistance { - private Coordinate[] pt = { new Coordinate(), new Coordinate() }; + private final Coordinate[] pt = { new Coordinate(), new Coordinate() }; private double distance = Double.NaN; private boolean isNull = true; + /** + * Creates an instance of this class + */ public PointPairDistance() { } + /** + * Initializes this instance. + */ public void initialize() { isNull = true; } - public void initialize(Coordinate p0, Coordinate p1) - { - pt[0].setCoordinate(p0); - pt[1].setCoordinate(p1); - distance = p0.distance(p1); - isNull = false; + /** + * Initializes the points, computing the distance between them. + * @param p0 the 1st point + * @param p1 the 2nd point + */ + public void initialize(Coordinate p0, Coordinate p1) { + initialize(p0, p1, p0.distance(p1)); } /** * Initializes the points, avoiding recomputing the distance. - * @param p0 - * @param p1 + * @param p0 the 1st point + * @param p1 the 2nd point * @param distance the distance between p0 and p1 */ - private void initialize(Coordinate p0, Coordinate p1, double distance) + void initialize(Coordinate p0, Coordinate p1, double distance) { pt[0].setCoordinate(p0); pt[1].setCoordinate(p1); @@ -54,10 +61,23 @@ private void initialize(Coordinate p0, Coordinate p1, double distance) isNull = false; } + /** + * Gets the distance between the paired points + * @return the distance between the paired points + */ public double getDistance() { return distance; } + /** + * Gets the paired points + * @return the paired points + */ public Coordinate[] getCoordinates() { return pt; } + /** + * Gets one of the paired points + * @param i the index of the paired point (0 or 1) + * @return A point + */ public Coordinate getCoordinate(int i) { return pt[i]; } public void setMaximum(PointPairDistance ptDist) @@ -91,7 +111,7 @@ public void setMinimum(Coordinate p0, Coordinate p1) if (dist < distance) initialize(p0, p1, dist); } - + public String toString() { return WKTWriter.toLineString(pt[0], pt[1]); diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java new file mode 100644 index 0000000000..35236e0ea7 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasure.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.MultiPoint; + +/** + * Measures the degree of similarity between two + * {@link Geometry}s using the Fréchet distance metric. + * The measure is normalized to lie in the range [0, 1]. + * Higher measures indicate a great degree of similarity. + *

      + * The measure is computed by computing the Fréchet distance + * between the input geometries, and then normalizing + * this by dividing it by the diagonal distance across + * the envelope of the combined geometries. + *

      + * Note: the input should be normalized, especially when + * measuring {@link MultiPoint} geometries because for the + * Fréchet distance the order of {@link Coordinate}s is + * important. + * + * @author Felix Obermaier + * + */ +public class FrechetSimilarityMeasure implements SimilarityMeasure { + + /** + * Creates an instance of this class. + */ + public FrechetSimilarityMeasure() + { } + + @Override + public double measure(Geometry g1, Geometry g2) { + + // Check if input is of same type + if (!g1.getGeometryType().equals(g2.getGeometryType())) + throw new IllegalArgumentException("g1 and g2 are of different type"); + + // Compute the distance + double frechetDistance = DiscreteFrechetDistance.distance(g1, g2); + if (frechetDistance == 0d) return 1; + + // Compute envelope diagonal size + Envelope env = new Envelope(g1.getEnvelopeInternal()); + env.expandToInclude(g2.getEnvelopeInternal()); + double envDiagSize = HausdorffSimilarityMeasure.diagonalSize(env); + + // normalize so that more similarity produces a measure closer to 1 + return 1 - frechetDistance / envDiagSize; + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java index 74ecedc905..c841fc7389 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasure.java @@ -24,13 +24,13 @@ *

      * The measure is computed by computing the Hausdorff distance * between the input geometries, and then normalizing - * this by dividing it by the diagonal distance across + * this by dividing it by the diagonal distance across * the envelope of the combined geometries. - * + * * @author mbdavis * */ -public class HausdorffSimilarityMeasure +public class HausdorffSimilarityMeasure implements SimilarityMeasure { /* @@ -40,36 +40,39 @@ public static double measure(Geometry a, Geometry b) return gv.measure(); } */ - + public HausdorffSimilarityMeasure() { } - + /* * Densify a small amount to increase accuracy of Hausdorff distance */ private static final double DENSIFY_FRACTION = 0.25; - + public double measure(Geometry g1, Geometry g2) - { + { double distance = DiscreteHausdorffDistance.distance(g1, g2, DENSIFY_FRACTION); - + if (distance == 0d) return 1d; + Envelope env = new Envelope(g1.getEnvelopeInternal()); env.expandToInclude(g2.getEnvelopeInternal()); double envSize = diagonalSize(env); - // normalize so that more similarity produces a measure closer to 1 + + // normalize so that more similarity produces a measure closer to 1 double measure = 1 - distance / envSize; - + //System.out.println("Hausdorff distance = " + distance + ", measure = " + measure); return measure; } - + public static double diagonalSize(Envelope env) { if (env.isNull()) return 0.0; - - double width = env.getWidth(); + + double width = env.getWidth(); double hgt = env.getHeight(); + return Math.sqrt(width * width + hgt * hgt); } } diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java new file mode 100644 index 0000000000..8d6ebb5fc4 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java @@ -0,0 +1,105 @@ +package org.locationtech.jts.algorithm.distance; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; + +/** + * Linear Discrete Fréchet Distance computation + */ +public class DiscreteFrechetDistanceLinear { + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1) { + DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, false); + return dist.distance(); + } + + /** + * Computes the Discrete Fréchet Distance between two {@link Geometry}s + * using a {@code cartesian} distance computation function. + * + * @param g0 the 1st geometry + * @param g1 the 2nd geometry + * @return the cartesian distance between {#g0} and {#g1} + */ + public static double distance(Geometry g0, Geometry g1, boolean getCoordinates) { + DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, getCoordinates); + return dist.distance(); + } + private final Geometry g0; + private final Geometry g1; + private final boolean getCoordinates; + + private DiscreteFrechetDistanceLinear(Geometry g0, Geometry g1, boolean getCoordinates) { + this.g0 = g0; + this.g1 = g1; + this.getCoordinates = getCoordinates; + } + + public double distance() { + + Coordinate[] coords0 = this.g0.getCoordinates(); + Coordinate[] coords1 = this.g1.getCoordinates(); + double[][] distances = new double[coords0.length][]; + for (int i = 0; i < coords0.length; i++) + distances[i] = new double[coords1.length]; + + for (int i = 0; i < coords0.length; i++) { + for (int j = 0; j < coords1.length; j++) + { + double distance = coords0[i].distance(coords1[j]); + if (i > 0 && j > 0) + { + distances[i][j] = Math.max(Math.min(Math.min(distances[i-1][j], distances[i-1][j-1]), distances[i][j-1]), distance); + } + else if (i > 0) + { + distances[i][j] = Math.max(distances[i-1][0], distance); + } + else if (j > 0) + { + distances[i][j] = Math.max(distances[0][j-1], distance); + } + else + { + distances[i][j] = distance; + } + } + } + + //System.out.println(toString(coords0.length, coords1.length, distances)); + //System.out.println(); + return distances[coords0.length-1][coords1.length-1]; + } + + /* + // For debugging purposes only + private static String toString(int numRows, int numCols, + double[][] sparse) { + + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < numRows; i++) + { + sb.append('['); + for(int j = 0; j < numCols; j++) + { + if (j > 0) + sb.append(", "); + sb.append(String.format("%8.4f", sparse[i][j])); + } + sb.append(']'); + if (i < numRows - 1) sb.append(",\n"); + } + sb.append(']'); + return sb.toString(); + } + */ + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java new file mode 100644 index 0000000000..e196a2c7d4 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jts.algorithm.distance; + +import org.junit.Test; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.util.Stopwatch; +import test.jts.GeometryTestCase; + +public class DiscreteFrechetDistanceTest extends GeometryTestCase { + + public DiscreteFrechetDistanceTest(String name) { + super(name); + } + + @Test + public void testLineSegments() + { + runTest( + "LINESTRING(0 0, 1 0.0, 2 0.0, 3 0.0, 4 0)", + "LINESTRING(0 1, 1 1.1, 2 1.2, 3 1.1, 4 1)", 1.2); + } + + @Test + public void testOrientation() + { + runTest( + "LINESTRING(0 0, 10 10, 20 15)", + "LINESTRING(0 1, 8 9, 12 11, 21 15)",2.23606797749979); + } + @Test + public void testFromDHD() { + runTest( + "LINESTRING (130 0, 0 0, 0 150)", + "LINESTRING (10 10, 10 150, 130 10)", 191.049731745428); + } + + public void testDevogeleEtAlPaper() { + runTest("LINESTRING(0.2 2.0, 1.5 2.8, 2.3 1.6, 2.9 1.8, 4.1 3.1, 5.6 2.9, 7.2 1.3, 8.2 1.1)", + "LINESTRING(0.3 1.6, 3.2 3.0, 3.8 1.8, 5.2 3.1, 6.5 2.8, 7.0 0.8, 8.9 0.6)", 1.697056); + } + + public void testLongEifelWalk() { + runTest( + "MultiLineStringZ ((3334206.2900000000372529 5575410.37999999988824129 442.10000000000002274, 3334209.33999999985098839 5575407.62000000011175871 442.80000000000001137, 3334210.75 5575409.12999999988824129 442.69999999999998863, 3334190.06000000005587935 5575386.17999999970197678 443.60000000000002274, 3334184.52000000001862645 5575375.44000000040978193 444.69999999999998863, 3334176.30000000027939677 5575352.2099999999627471 447, 3334149.60999999986961484 5575360.71999999973922968 446.90000000000003411, 3334137.68000000016763806 5575366.88999999966472387 446.5, 3334125.9599999999627471 5575379.83000000007450581 445.30000000000001137, 3334115.97000000020489097 5575386.7099999999627471 444.80000000000001137, 3334106.01000000024214387 5575391.91999999992549419 444.40000000000003411, 3334096.97000000020489097 5575394.66000000014901161 444.30000000000001137, 3334087.49000000022351742 5575394.83999999985098839 444.40000000000003411, 3334078.58999999985098839 5575393 444.69999999999998863, 3334071.85999999986961484 5575390.10000000055879354 444.80000000000001137, 3334059.89999999990686774 5575404.5 442.13999999999998636, 3334046.47999999998137355 5575415.37999999988824129 438.81000000000000227, 3334129.45000000018626451 5575530.35000000055879354 437.62000000000000455, 3334179.47999999998137355 5575497.06000000052154064 439.01999999999998181, 3334196.87000000011175871 5575537.81000000052154064 440.03000000000002956, 3334229.08000000007450581 5575541.70000000018626451 441.62999999999999545, 3334209 5575622.25999999977648258 442.37999999999999545, 3334134.66000000014901161 5575601.10000000055879354 438.60000000000002274, 3334134.07000000029802322 5575602.57000000029802322 438.60000000000002274, 3334131.07000000029802322 5575609.45000000018626451 438.69999999999998863, 3334118.20000000018626451 5575638.02000000048428774 438.90000000000003411, 3334114.66999999992549419 5575645.81000000052154064 438.90000000000003411, 3334102.81000000005587935 5575667.88999999966472387 439.40000000000003411, 3334100.2900000000372529 5575676.42999999970197678 439.69999999999998863, 3334098.2900000000372529 5575683.16999999992549419 440, 3334095.64999999990686774 5575696.71999999973922968 440.69999999999998863, 3334095.16999999992549419 5575710.99000000022351742 441.40000000000003411, 3334095.97999999998137355 5575727.77000000048428774 442.10000000000002274, 3334098.89000000013038516 5575747.71999999973922968 442.90000000000003411, 3334099.78000000026077032 5575751.37000000011175871 443.10000000000002274, 3334101.35000000009313226 5575758 443.30000000000001137, 3334097.9599999999627471 5575759.21999999973922968 443.5, 3334093.58999999985098839 5575760.91000000014901161 443.69999999999998863, 3334081.22999999998137355 5575773.66000000014901161 445.30000000000001137, 3334081.82000000029802322 5575776.53000000026077032 445.40000000000003411, 3334081.87999999988824129 5575783.2099999999627471 445.69999999999998863, 3334072.4599999999627471 5575817.00999999977648258 447.60000000000002274, 3334070.62000000011175871 5575831.32000000029802322 447.90000000000003411, 3334074.35000000009313226 5575863.92999999970197678 447.60000000000002274, 3334078.08000000007450581 5575896.54999999981373549 447.10000000000002274, 3334076.18999999994412065 5575897.61000000033527613 447.19999999999998863, 3334070.14999999990686774 5575905.03000000026077032 447.40000000000003411, 3334062.60999999986961484 5575953.58999999985098839 446.5, 3334055.07000000029802322 5576002.03000000026077032 447.90000000000003411, 3334047.60000000009313226 5576050.4599999999627471 450, 3334039.10000000009313226 5576059.08000000007450581 449.90000000000003411, 3334003.41000000014901161 5576080.90000000037252903 450.19999999999998863, 3333973.05000000027939677 5576099.44000000040978193 453.10000000000002274, 3333967.72999999998137355 5576102.73000000044703484 453.60000000000002274, 3334005.68999999994412065 5576130.92999999970197678 453.5, 3334024.99000000022351742 5576141.4599999999627471 453.30000000000001137, 3334015.52000000001862645 5576159.90000000037252903 455.30000000000001137, 3334010.81000000005587935 5576166.83999999985098839 455.5, 3333996.70000000018626451 5576178.52000000048428774 455.69999999999998863, 3333994.28000000026077032 5576183.16000000014901161 455.80000000000001137, 3333992.4599999999627471 5576186.56000000052154064 456, 3333982.58000000007450581 5576198.88999999966472387 457.10000000000002274, 3333964.35999999986961484 5576218.38999999966472387 460.5, 3333933.55000000027939677 5576245.41000000014901161 466.69999999999998863, 3333902.72999999998137355 5576272.41999999992549419 470.69999999999998863, 3333876.7900000000372529 5576293.27000000048428774 472.19999999999998863, 3333850.85000000009313226 5576314.24000000022351742 474.10000000000002274, 3333845.35000000009313226 5576318.63999999966472387 474.69999999999998863, 3333819.91999999992549419 5576337.69000000040978193 476.30000000000001137, 3333844.28000000026077032 5576355.08000000007450581 477.30000000000001137, 3333868.64999999990686774 5576372.57000000029802322 477.80000000000001137, 3333897.91000000014901161 5576391.46999999973922968 478.60000000000002274, 3333927.08999999985098839 5576410.37000000011175871 479, 3333933.47999999998137355 5576416.17999999970197678 479.10000000000002274, 3333946.58999999985098839 5576438.48000000044703484 479.5, 3333961.68000000016763806 5576472.07000000029802322 479.60000000000002274, 3333976.85999999986961484 5576505.66999999992549419 478.40000000000003411, 3333977.74000000022351742 5576506.53000000026077032 478.30000000000001137, 3333982.37000000011175871 5576510.83999999985098839 478, 3333937.39000000013038516 5576531.83999999985098839 479.80000000000001137, 3333892.47999999998137355 5576552.73000000044703484 481.80000000000001137, 3333853.27000000001862645 5576571.10000000055879354 483.80000000000001137, 3333847.57000000029802322 5576573.73000000044703484 484.10000000000002274, 3333877.45000000018626451 5576603.17999999970197678 482.5, 3333907.32000000029802322 5576632.75 481.10000000000002274, 3333937.18999999994412065 5576662.2099999999627471 480.19999999999998863, 3333967.07000000029802322 5576691.78000000026077032 479.30000000000001137, 3333996.93999999994412065 5576721.23000000044703484 478.69999999999998863, 3334017.08999999985098839 5576736.07000000029802322 478.40000000000003411, 3334053.24000000022351742 5576759.10000000055879354 477, 3334089.33000000007450581 5576782.02000000048428774 473.90000000000003411, 3334133.33999999985098839 5576805.58000000007450581 471, 3334161.47000000020489097 5576824.74000000022351742 469.30000000000001137, 3334189.66000000014901161 5576843.88999999966472387 467.5, 3334196.74000000022351742 5576849.12999999988824129 467.5, 3334191.56000000005587935 5576854.52000000048428774 467.5, 3334200.33000000007450581 5576856.69000000040978193 467.5, 3334224.60999999986961484 5576862.73000000044703484 467.30000000000001137, 3334257.60000000009313226 5576870.92999999970197678 466, 3334286.28000000026077032 5576876.04999999981373549 464.69999999999998863, 3334315.03000000026077032 5576881.16000000014901161 464.80000000000001137, 3334321.03000000026077032 5576881.53000000026077032 464.90000000000003411, 3334328.2099999999627471 5576880.86000000033527613 464.80000000000001137, 3334366.72999999998137355 5576867.62999999988824129 464.19999999999998863, 3334405.85000000009313226 5576855.71999999973922968 463.10000000000002274, 3334445.05000000027939677 5576843.81000000052154064 461.69999999999998863, 3334450.10999999986961484 5576841.42999999970197678 461.60000000000002274, 3334488.43999999994412065 5576863.27000000048428774 462, 3334526.70000000018626451 5576885.12000000011175871 462.69999999999998863, 3334528.83999999985098839 5576887.40000000037252903 462.69999999999998863, 3334530.37999999988824129 5576888.67999999970197678 462.80000000000001137, 3334558.78000000026077032 5576912.2900000000372529 463.40000000000003411, 3334588.7099999999627471 5576937.17999999970197678 464.90000000000003411, 3334618.64999999990686774 5576962.07000000029802322 465.5, 3334648.66000000014901161 5576986.96999999973922968 466.60000000000002274, 3334654.95000000018626451 5576989.78000000026077032 466.80000000000001137, 3334679.01000000024214387 5577002.27000000048428774 466.90000000000003411, 3334703.07000000029802322 5577014.77000000048428774 467.30000000000001137, 3334745.20000000018626451 5577037.38999999966472387 467.5, 3334784.43999999994412065 5577058.87999999988824129 468.80000000000001137, 3334823.60999999986961484 5577080.37000000011175871 469.90000000000003411, 3334862.85000000009313226 5577101.74000000022351742 471.30000000000001137, 3334900.35000000009313226 5577122.28000000026077032 472, 3334902.08999999985098839 5577123.23000000044703484 472.10000000000002274, 3334877.7900000000372529 5577153.27000000048428774 472.30000000000001137, 3334853.47999999998137355 5577183.19000000040978193 471, 3334835.93000000016763806 5577205.56000000052154064 471.69999999999998863, 3334796.37000000011175871 5577235.50999999977648258 472.30000000000001137, 3334756.72999999998137355 5577265.36000000033527613 472.30000000000001137, 3334718.41000000014901161 5577293.83000000007450581 473.10000000000002274, 3334680.08000000007450581 5577322.29999999981373549 473.60000000000002274, 3334665.62999999988824129 5577334.44000000040978193 474, 3334660.56000000005587935 5577338.71999999973922968 474.10000000000002274, 3334688.05000000027939677 5577360.4599999999627471 474.69999999999998863, 3334715.60000000009313226 5577382.20000000018626451 476.19999999999998863, 3334743.16000000014901161 5577403.94000000040978193 478.40000000000003411, 3334776.10999999986961484 5577427.2900000000372529 480.69999999999998863, 3334789.16999999992549419 5577441.36000000033527613 481.10000000000002274, 3334798.64000000013038516 5577454.87000000011175871 481.69999999999998863, 3334823.64000000013038516 5577490.60000000055879354 482.5, 3334850.62999999988824129 5577530.95000000018626451 483.90000000000003411, 3334863.64000000013038516 5577550.36000000033527613 483.90000000000003411, 3334879.57000000029802322 5577574.02000000048428774 482.90000000000003411, 3334895.49000000022351742 5577597.67999999970197678 482, 3334915.58000000007450581 5577627.23000000044703484 481.19999999999998863, 3334932.68999999994412065 5577652.41000000014901161 481.69999999999998863, 3334739.87000000011175871 5577803.70000000018626451 472.40000000000003411, 3334741.5400000000372529 5577811.54999999981373549 472.40000000000003411, 3334745.35000000009313226 5577817.11000000033527613 472.60000000000002274, 3334751.37000000011175871 5577822.7099999999627471 472.90000000000003411, 3334764.9599999999627471 5577833.20000000018626451 473.60000000000002274, 3334775.78000000026077032 5577841.87999999988824129 474.30000000000001137, 3334802.14999999990686774 5577866.77000000048428774 476.10000000000002274, 3334824.31000000005587935 5577884.78000000026077032 477.5, 3334846.39999999990686774 5577902.79999999981373549 478.69999999999998863, 3334858 5577913.7900000000372529 479.30000000000001137, 3334866.75 5577922.08999999985098839 479.69999999999998863, 3334886.20000000018626451 5577935.62000000011175871 480.60000000000002274, 3334917.02000000001862645 5577963.94000000040978193 481.60000000000002274, 3334947.83999999985098839 5577992.25999999977648258 482.69999999999998863, 3334980.27000000001862645 5578019.29999999981373549 483.90000000000003411, 3334997.02000000001862645 5578035.48000000044703484 484.5, 3335011.66000000014901161 5578050.04999999981373549 485.10000000000002274, 3335018.89999999990686774 5578055.83999999985098839 485.5, 3335026.52000000001862645 5578060.28000000026077032 485.80000000000001137, 3335034.10000000009313226 5578063.37999999988824129 486.19999999999998863, 3335042.83000000007450581 5578066.56000000052154064 486.69999999999998863, 3335051.32000000029802322 5578069.29999999981373549 487.19999999999998863, 3335062.22000000020489097 5578071.29999999981373549 487.69999999999998863, 3335071.83000000007450581 5578071 488.30000000000001137, 3335072.49000000022351742 5578075.87999999988824129 488.30000000000001137, 3335069.37000000011175871 5578081.10000000055879354 488, 3335068.56000000005587935 5578082.4599999999627471 488, 3335061.85000000009313226 5578089.12000000011175871 487.5, 3335057.62999999988824129 5578095.49000000022351742 487.19999999999998863, 3335052.24000000022351742 5578105.67999999970197678 486.80000000000001137, 3335049.64000000013038516 5578118.11000000033527613 486.60000000000002274, 3335050.78000000026077032 5578125.20000000018626451 486.60000000000002274, 3335067.66000000014901161 5578145.27000000048428774 487.40000000000003411, 3335069.05000000027939677 5578146.33999999985098839 487.40000000000003411, 3335070.51000000024214387 5578147.52000000048428774 487.5, 3335071.70000000018626451 5578155.83999999985098839 487.5, 3335070.89999999990686774 5578162.2099999999627471 487.40000000000003411, 3335058.05000000027939677 5578200.67999999970197678 486.19999999999998863, 3335055.60999999986961484 5578213.78000000026077032 485.90000000000003411, 3335055.58000000007450581 5578231.25999999977648258 485.5, 3335060.80000000027939677 5578250.02000000048428774 485.19999999999998863, 3335070.4599999999627471 5578262.52000000048428774 485.30000000000001137, 3335088.83000000007450581 5578273.53000000026077032 485.80000000000001137, 3335122.58000000007450581 5578283.83000000007450581 487.19999999999998863, 3335129.10999999986961484 5578287.53000000026077032 487.40000000000003411, 3335135.32000000029802322 5578292.33999999985098839 487.60000000000002274, 3335142.18000000016763806 5578304.37999999988824129 487.60000000000002274, 3335153.49000000022351742 5578328.74000000022351742 487.5, 3335157.57000000029802322 5578333.73000000044703484 487.60000000000002274, 3335166.31000000005587935 5578339.81000000052154064 487.90000000000003411, 3335176.18999999994412065 5578343.62000000011175871 488.40000000000003411, 3335188.18000000016763806 5578348.58999999985098839 489, 3335198.06000000005587935 5578352.17999999970197678 489.5, 3335212.2900000000372529 5578356.19000000040978193 490.30000000000001137, 3335232.75 5578358.56000000052154064 491.60000000000002274, 3335246.12999999988824129 5578360.03000000026077032 492.40000000000003411, 3335261.60000000009313226 5578364.78000000026077032 493.40000000000003411, 3335269.58000000007450581 5578369.2099999999627471 493.80000000000001137, 3335272.35999999986961484 5578373.91000000014901161 493.90000000000003411, 3335273.53000000026077032 5578379.44000000040978193 493.90000000000003411, 3335268.93000000016763806 5578394.28000000026077032 493.30000000000001137, 3335258.22000000020489097 5578407.29999999981373549 492.30000000000001137, 3335250.87999999988824129 5578423.56000000052154064 491.60000000000002274, 3335247.0400000000372529 5578441.94000000040978193 491.19999999999998863, 3335244.08000000007450581 5578470.75 490.90000000000003411, 3335241.12999999988824129 5578499.57000000029802322 490.90000000000003411, 3335241.02000000001862645 5578528.07000000029802322 491.10000000000002274, 3335240.97999999998137355 5578556.4599999999627471 491.40000000000003411, 3335242.97999999998137355 5578568.20000000018626451 491.60000000000002274, 3335250.39999999990686774 5578588.90000000037252903 492.10000000000002274, 3335267.2900000000372529 5578623.44000000040978193 493, 3335282.62999999988824129 5578646.90000000037252903 493.5, 3335293.02000000001862645 5578660.15000000037252903 493.80000000000001137, 3335299.7099999999627471 5578671.19000000040978193 493.90000000000003411, 3335313.74000000022351742 5578689.23000000044703484 494, 3335332.68000000016763806 5578706.90000000037252903 494.30000000000001137, 3335351.55000000027939677 5578724.67999999970197678 494.40000000000003411, 3335359.97999999998137355 5578732.10000000055879354 494.60000000000002274, 3335371.70000000018626451 5578739.98000000044703484 494.80000000000001137, 3335382.33999999985098839 5578742.99000000022351742 495.19999999999998863, 3335393.41000000014901161 5578743.75999999977648258 495.80000000000001137, 3335401.12000000011175871 5578741.74000000022351742 496.19999999999998863, 3335425.22999999998137355 5578728.74000000022351742 497.60000000000002274, 3335444.9599999999627471 5578716.87999999988824129 498.80000000000001137, 3335448.28000000026077032 5578715.66999999992549419 499, 3335452.37000000011175871 5578712.08999999985098839 499.19999999999998863, 3335457.45000000018626451 5578710.37000000011175871 499.5, 3335464.91999999992549419 5578710.13999999966472387 499.90000000000003411, 3335472.25 5578711.81000000052154064 500.10000000000002274, 3335474.93000000016763806 5578713.38999999966472387 500.19999999999998863, 3335487.43999999994412065 5578721.4599999999627471 500.69999999999998863, 3335482.37999999988824129 5578728.29999999981373549 500.10000000000002274, 3335480.02000000001862645 5578730.37999999988824129 499.90000000000003411, 3335482.37999999988824129 5578728.29999999981373549 500.10000000000002274, 3335487.43999999994412065 5578721.4599999999627471 500.69999999999998863, 3335498.16000000014901161 5578711 501.90000000000003411, 3335535.64999999990686774 5578681.11000000033527613 504.69999999999998863, 3335554.72999999998137355 5578668.71999999973922968 505.40000000000003411, 3335562.2900000000372529 5578666.58999999985098839 505.60000000000002274, 3335572.64999999990686774 5578665.27000000048428774 505.90000000000003411, 3335617.82000000029802322 5578662.31000000052154064 506.80000000000001137, 3335630.16000000014901161 5578662.48000000044703484 507.10000000000002274, 3335657.70000000018626451 5578665.96999999973922968 508, 3335682.35999999986961484 5578672.66999999992549419 509.10000000000002274, 3335707.02000000001862645 5578679.46999999973922968 510.60000000000002274, 3335716.41000000014901161 5578683.62999999988824129 511.10000000000002274, 3335737.83999999985098839 5578696.54999999981373549 512.39999999999997726, 3335772.5 5578727.08999999985098839 514.29999999999995453, 3335807.56000000005587935 5578759.17999999970197678 516.20000000000004547, 3335842.62000000011175871 5578791.27000000048428774 518.10000000000002274, 3335877.76000000024214387 5578823.35000000055879354 520.10000000000002274, 3335912.10999999986961484 5578855.4599999999627471 522, 3335946.4599999999627471 5578887.67999999970197678 523.79999999999995453, 3335980.74000000022351742 5578919.91000000014901161 525.10000000000002274, 3336015.08999999985098839 5578952.02000000048428774 526.10000000000002274, 3336041.95000000018626451 5578974.7900000000372529 526.70000000000004547, 3336068.74000000022351742 5578997.56000000052154064 527.20000000000004547, 3336088.93999999994412065 5579019.20000000018626451 527.29999999999995453, 3336130.89999999990686774 5579027.81000000052154064 528.89999999999997726, 3336172.7900000000372529 5579036.41999999992549419 530.60000000000002274, 3336214.66999999992549419 5579044.91999999992549419 532.79999999999995453, 3336256.62999999988824129 5579053.53000000026077032 535.29999999999995453, 3336256.07000000029802322 5579051.44000000040978193 535.29999999999995453, 3336256.62999999988824129 5579053.53000000026077032 535.29999999999995453, 3336302.26000000024214387 5579063.37000000011175871 537.60000000000002274, 3336347.89000000013038516 5579073.08999999985098839 539.10000000000002274, 3336393.52000000001862645 5579082.91999999992549419 540, 3336439.08000000007450581 5579092.75999999977648258 540.5, 3336448.77000000001862645 5579092.90000000037252903 540.5, 3336473.87999999988824129 5579070.53000000026077032 540.20000000000004547, 3336498.99000000022351742 5579048.27000000048428774 539.60000000000002274, 3336504.80000000027939677 5579044.75 539.5, 3336535.35000000009313226 5579025.54999999981373549 538.20000000000004547, 3336549.68000000016763806 5579018.77000000048428774 537.10000000000002274, 3336583.76000000024214387 5578998.23000000044703484 534.29999999999995453, 3336617.89999999990686774 5578977.70000000018626451 531, 3336627.97999999998137355 5578971.60000000055879354 529.89999999999997726, 3336660.87000000011175871 5578951.99000000022351742 526.20000000000004547, 3336687.24000000022351742 5578931.36000000033527613 523, 3336695.5400000000372529 5578923.20000000018626451 521.79999999999995453, 3336703.18999999994412065 5578916.83999999985098839 520.60000000000002274, 3336719.33999999985098839 5578902.2099999999627471 518.10000000000002274, 3336726.20000000018626451 5578893.5400000000372529 517, 3336746.83000000007450581 5578866.74000000022351742 513.5, 3336751.32000000029802322 5578859.70000000018626451 512.79999999999995453, 3336754.32000000029802322 5578853.25999999977648258 512.10000000000002274, 3336756.2900000000372529 5578847.74000000022351742 511.60000000000002274, 3336757.47000000020489097 5578835.02000000048428774 510.69999999999998863, 3336758.87999999988824129 5578806.91999999992549419 509, 3336760.22000000020489097 5578778.7099999999627471 507.69999999999998863, 3336775.47000000020489097 5578769.45000000018626451 505.69999999999998863, 3336787 5578768.75999999977648258 504.40000000000003411, 3336828.33999999985098839 5578771.27000000048428774 500.5, 3336833.91000000014901161 5578771.66000000014901161 500, 3336871.18999999994412065 5578771.83999999985098839 496.10000000000002274, 3336882.47000000020489097 5578769.94000000040978193 495.10000000000002274, 3336905.57000000029802322 5578763.21999999973922968 493.80000000000001137, 3336909.60000000009313226 5578762.08999999985098839 493.60000000000002274, 3336915.16000000014901161 5578760.03000000026077032 493.40000000000003411, 3336920.07000000029802322 5578757.31000000052154064 493.30000000000001137, 3336938.26000000024214387 5578755.53000000026077032 493, 3336963.85000000009313226 5578753.52000000048428774 492.60000000000002274, 3336995.78000000026077032 5578748.63999999966472387 491.90000000000003411, 3337027.70000000018626451 5578743.65000000037252903 490.40000000000003411, 3337037.77000000001862645 5578739.78000000026077032 490, 3337054.26000000024214387 5578729.25 489.60000000000002274, 3337062.87999999988824129 5578721.96999999973922968 489.40000000000003411, 3337070.01000000024214387 5578712.9599999999627471 489.40000000000003411, 3337079.2099999999627471 5578706.21999999973922968 489, 3337103.58000000007450581 5578685.2099999999627471 486.80000000000001137, 3337128.03000000026077032 5578664.20000000018626451 485.19999999999998863, 3337136.2099999999627471 5578654.48000000044703484 484.69999999999998863, 3337149.72999999998137355 5578639.92999999970197678 484.19999999999998863, 3337169.16000000014901161 5578618.07000000029802322 484.80000000000001137, 3337188.58000000007450581 5578596.10000000055879354 484.69999999999998863, 3337199.12999999988824129 5578582.19000000040978193 484.5, 3337206.60000000009313226 5578567.92999999970197678 484.30000000000001137, 3337210.70000000018626451 5578559.90000000037252903 484, 3337217.64999999990686774 5578540.10000000055879354 483.40000000000003411, 3337220.58000000007450581 5578530.87999999988824129 483.10000000000002274, 3337225.10999999986961484 5578509.13999999966472387 482.80000000000001137, 3337228.07000000029802322 5578503.49000000022351742 482.80000000000001137, 3337233 5578498.99000000022351742 482.60000000000002274, 3337238.93000000016763806 5578494.91000000014901161 482.5, 3337246.08000000007450581 5578493.25 482.40000000000003411, 3337253.07000000029802322 5578493.48000000044703484 482.30000000000001137, 3337259.5 5578494.06000000052154064 482.19999999999998863, 3337301.62000000011175871 5578505.57000000029802322 482.40000000000003411, 3337327.77000000001862645 5578512.11000000033527613 482.80000000000001137, 3337371.64000000013038516 5578522.57000000029802322 483.60000000000002274, 3337412.07000000029802322 5578530.12000000011175871 482.69999999999998863, 3337426.83999999985098839 5578533.12000000011175871 482, 3337445.99000000022351742 5578534.75999999977648258 481.30000000000001137, 3337467.58000000007450581 5578534.5400000000372529 480.60000000000002274, 3337479.07000000029802322 5578532.74000000022351742 480.5, 3337502.45000000018626451 5578525.7900000000372529 480.19999999999998863, 3337536.2099999999627471 5578511.06000000052154064 480.19999999999998863, 3337569.2099999999627471 5578492.24000000022351742 480.5, 3337582.7099999999627471 5578481.69000000040978193 480, 3337586.41000000014901161 5578479.24000000022351742 479.90000000000003411, 3337611.68999999994412065 5578464.54999999981373549 477.80000000000001137, 3337636.97000000020489097 5578449.75 474.80000000000001137, 3337635.28000000026077032 5578445.90000000037252903 475.30000000000001137, 3337630.18999999994412065 5578433.15000000037252903 476.69999999999998863, 3337625.18000000016763806 5578414.0400000000372529 477.90000000000003411, 3337624.4599999999627471 5578409.04999999981373549 478, 3337623.97999999998137355 5578407.40000000037252903 478.10000000000002274, 3337174.80000000027939677 5577945.83999999985098839 515.5, 3337159.64999999990686774 5577937.62000000011175871 516.70000000000004547, 3337157.97999999998137355 5577936.78000000026077032 516.79999999999995453, 3337136.99000000022351742 5577924.07000000029802322 518.70000000000004547, 3337106.87999999988824129 5577904.50999999977648258 521.10000000000002274, 3337096.45000000018626451 5577899.15000000037252903 521.70000000000004547, 3337063.07000000029802322 5577875.24000000022351742 524.10000000000002274, 3337038.06000000005587935 5577854.75 525.5, 3337018.52000000001862645 5577840.77000000048428774 526.39999999999997726, 3336996.72999999998137355 5577822.9599999999627471 528, 3336975.85000000009313226 5577806.90000000037252903 529.29999999999995453, 3336937.47999999998137355 5577778.58000000007450581 530.70000000000004547, 3336928.76000000024214387 5577773.06000000052154064 530.89999999999997726, 3336918.9599999999627471 5577762.66999999992549419 531.29999999999995453, 3336916.62000000011175871 5577760.85000000055879354 531.39999999999997726, 3336897.55000000027939677 5577741.07000000029802322 532.29999999999995453, 3336883.32000000029802322 5577728.15000000037252903 532.70000000000004547, 3336874.9599999999627471 5577722.83999999985098839 532.89999999999997726, 3336868.77000000001862645 5577723.25 532.70000000000004547, 3336858.4599999999627471 5577721.67999999970197678 532.39999999999997726, 3336848.10000000009313226 5577718.54999999981373549 532, 3336834.16000000014901161 5577712.74000000022351742 531.60000000000002274, 3336805.22999999998137355 5577696.82000000029802322 530.79999999999995453, 3336776.35999999986961484 5577680.7900000000372529 530.39999999999997726, 3336751.16000000014901161 5577665.87000000011175871 529.89999999999997726, 3336740.97999999998137355 5577661.62000000011175871 529.60000000000002274, 3336718.66000000014901161 5577649.83999999985098839 529.5, 3336696.33999999985098839 5577638.06000000052154064 529.29999999999995453, 3336658.81000000005587935 5577611.37999999988824129 528.29999999999995453, 3336637.93000000016763806 5577597.78000000026077032 527.70000000000004547, 3336627.7099999999627471 5577589.96999999973922968 527.20000000000004547, 3336620.9599999999627471 5577586.06000000052154064 526.89999999999997726, 3336614.82000000029802322 5577581.12000000011175871 526.79999999999995453, 3336603.93000000016763806 5577574.88999999966472387 526.5, 3336593.30000000027939677 5577567.75999999977648258 526.20000000000004547, 3336583.91000000014901161 5577558.91999999992549419 526, 3336552.62999999988824129 5577536.2900000000372529 524.89999999999997726, 3336521.33999999985098839 5577513.65000000037252903 523.39999999999997726, 3336495.5 5577491.74000000022351742 521.89999999999997726, 3336470.58999999985098839 5577467.57000000029802322 520.20000000000004547, 3336462.5400000000372529 5577460.91999999992549419 519.70000000000004547, 3336446.43999999994412065 5577444.94000000040978193 518.60000000000002274, 3336435.85000000009313226 5577434.46999999973922968 517.89999999999997726, 3336428.47000000020489097 5577426.23000000044703484 517.39999999999997726, 3336424.35000000009313226 5577412.88999999966472387 517.29999999999995453, 3336419.75 5577388.65000000037252903 517.20000000000004547, 3336413.91000000014901161 5577349.75999999977648258 514.89999999999997726, 3336405.95000000018626451 5577338.98000000044703484 513.70000000000004547, 3336391.60999999986961484 5577324.73000000044703484 512.39999999999997726, 3336379.16000000014901161 5577313.87000000011175871 511.69999999999998863, 3336388.25 5577310.46999999973922968 511, 3336401.57000000029802322 5577303.37999999988824129 509.90000000000003411, 3336406.75 5577297.77000000048428774 509.30000000000001137, 3336426.43000000016763806 5577261.08999999985098839 506.40000000000003411, 3336430.89000000013038516 5577250.7099999999627471 505.90000000000003411, 3336448.58999999985098839 5577214.5400000000372529 502.60000000000002274, 3336459.14999999990686774 5577191.50999999977648258 500.30000000000001137, 3336469.62999999988824129 5577168.46999999973922968 498.19999999999998863, 3336483.31000000005587935 5577138.21999999973922968 495.90000000000003411, 3336496.91999999992549419 5577107.9599999999627471 493.5, 3336485.72000000020489097 5577100.74000000022351742 493.69999999999998863, 3336460.83999999985098839 5577084.58000000007450581 494, 3336424.68000000016763806 5577061.2099999999627471 494.40000000000003411, 3336409.10999999986961484 5577050.88999999966472387 494.69999999999998863, 3336372.41999999992549419 5577026.53000000026077032 494.60000000000002274, 3336340.64000000013038516 5577001.4599999999627471 494, 3336308.78000000026077032 5576976.50999999977648258 493.19999999999998863, 3336302.85999999986961484 5576971.90000000037252903 493.10000000000002274, 3336263.68999999994412065 5576943.28000000026077032 492.40000000000003411, 3336250.35999999986961484 5576917.75 492.10000000000002274, 3336237.01000000024214387 5576892.12000000011175871 491.69999999999998863, 3336223.74000000022351742 5576864.13999999966472387 491.30000000000001137, 3336211.68999999994412065 5576836.35000000055879354 491.30000000000001137, 3336199.7099999999627471 5576808.44000000040978193 491.5, 3336188.30000000027939677 5576785.41000000014901161 492, 3336176.89000000013038516 5576762.28000000026077032 493.69999999999998863, 3336204.49000000022351742 5576743.95000000018626451 496.19999999999998863, 3336221.08000000007450581 5576729.29999999981373549 497.30000000000001137, 3336228.05000000027939677 5576719.73000000044703484 498, 3336241.25 5576694.61000000033527613 500, 3336248.16000000014901161 5576682.92999999970197678 501, 3336259.7900000000372529 5576676.21999999973922968 501.60000000000002274, 3336245.49000000022351742 5576649.38999999966472387 503.10000000000002274, 3336237.14999999990686774 5576631.06000000052154064 503.90000000000003411, 3336221.27000000001862645 5576606.28000000026077032 503.80000000000001137, 3336208.24000000022351742 5576581.52000000048428774 502.80000000000001137, 3336203.08999999985098839 5576571.99000000022351742 502.19999999999998863, 3336188.9599999999627471 5576543.7099999999627471 501.19999999999998863, 3336186.05000000027939677 5576534.88999999966472387 501, 3336183.64000000013038516 5576521.61000000033527613 501.10000000000002274, 3336186.01000000024214387 5576489.91999999992549419 501.10000000000002274, 3336193.57000000029802322 5576464.53000000026077032 499.90000000000003411, 3336195.89999999990686774 5576457 499.40000000000003411, 3336196.97000000020489097 5576443.04999999981373549 498.80000000000001137, 3336196.89000000013038516 5576431.13999999966472387 498.19999999999998863, 3336195.74000000022351742 5576423.94000000040978193 498, 3336196.45000000018626451 5576398.65000000037252903 496.19999999999998863, 3336199.43000000016763806 5576391.2099999999627471 495.19999999999998863, 3336218.22000000020489097 5576357.7900000000372529 490.19999999999998863, 3336234.33999999985098839 5576320.77000000048428774 485.69999999999998863, 3336241.76000000024214387 5576295.61000000033527613 483.19999999999998863, 3336242.74000000022351742 5576292.91000000014901161 482.90000000000003411, 3336255.18000000016763806 5576252.11000000033527613 479.10000000000002274, 3336256.97000000020489097 5576241.03000000026077032 477.90000000000003411, 3336259.22000000020489097 5576205.45000000018626451 473.69999999999998863, 3336258.70000000018626451 5576193.11000000033527613 472.40000000000003411, 3336257.53000000026077032 5576189.70000000018626451 472.19999999999998863, 3336254.62999999988824129 5576183.54999999981373549 471.80000000000001137, 3336245.75 5576175.58999999985098839 471.69999999999998863, 3336235.85999999986961484 5576169.33000000007450581 471.60000000000002274, 3336199.12999999988824129 5576141.2900000000372529 470.60000000000002274, 3336162.39000000013038516 5576113.25999999977648258 466, 3336159.12000000011175871 5576108.91000000014901161 465.30000000000001137, 3336160.60000000009313226 5576105.86000000033527613 464.80000000000001137, 3336175.18000000016763806 5576088.60000000055879354 463, 3336182.14000000013038516 5576078.58999999985098839 462.19999999999998863, 3336179.08000000007450581 5576069.44000000040978193 460.80000000000001137, 3336174.64999999990686774 5576066.91000000014901161 460, 3336165.03000000026077032 5576064.63999999966472387 458.90000000000003411, 3336150.52000000001862645 5576065.87000000011175871 457.90000000000003411, 3336129.62000000011175871 5576065.74000000022351742 456.40000000000003411, 3336120.02000000001862645 5576064.37000000011175871 455.60000000000002274, 3336113.97000000020489097 5576064.66000000014901161 455.40000000000003411, 3336107.87999999988824129 5576066.08000000007450581 455.40000000000003411, 3336096.77000000001862645 5576068.87000000011175871 455.80000000000001137, 3336090.51000000024214387 5576071.50999999977648258 456.10000000000002274, 3336069.57000000029802322 5576074.83000000007450581 456.90000000000003411, 3336023.22000000020489097 5576074.58999999985098839 458.69999999999998863, 3336007.26000000024214387 5576075.31000000052154064 459.19999999999998863, 3335991.80000000027939677 5576077.7900000000372529 459.80000000000001137, 3335944.06000000005587935 5576090.74000000022351742 459.69999999999998863, 3335947.2900000000372529 5576084.40000000037252903 459.5, 3335950.58999999985098839 5576078.17999999970197678 459.30000000000001137, 3335954.0400000000372529 5576072.06000000052154064 459.19999999999998863, 3335958.28000000026077032 5576063.91000000014901161 459.10000000000002274, 3335978.33999999985098839 5576030.00999999977648258 458.90000000000003411, 3335986.72999999998137355 5576020.16999999992549419 458.90000000000003411, 3336024.12999999988824129 5575988.85000000055879354 457.5, 3336061.60000000009313226 5575957.52000000048428774 455.90000000000003411, 3336093.37000000011175871 5575931.16000000014901161 454.90000000000003411, 3336125.14000000013038516 5575904.67999999970197678 453.90000000000003411, 3336156.99000000022351742 5575878.31000000052154064 453.10000000000002274, 3336188.76000000024214387 5575851.95000000018626451 451.90000000000003411, 3336220.5400000000372529 5575825.58999999985098839 450.19999999999998863, 3336246.77000000001862645 5575804.40000000037252903 449, 3336273 5575783.21999999973922968 447.5, 3336288.2099999999627471 5575772.50999999977648258 447.10000000000002274, 3336319.78000000026077032 5575746.27000000048428774 446.19999999999998863, 3336351.33999999985098839 5575720.02000000048428774 445, 3336382.91000000014901161 5575693.66000000014901161 444.30000000000001137, 3336388.26000000024214387 5575689.16000000014901161 444.30000000000001137, 3336392.78000000026077032 5575685.4599999999627471 444.19999999999998863, 3336421.60000000009313226 5575662.63999999966472387 444.5, 3336457.01000000024214387 5575633.7099999999627471 445.69999999999998863, 3336492.41000000014901161 5575604.7900000000372529 446.80000000000001137, 3336527.89000000013038516 5575575.75 447.69999999999998863, 3336563.30000000027939677 5575546.83000000007450581 447.19999999999998863, 3336584.70000000018626451 5575528.69000000040978193 446.30000000000001137, 3336606.18000000016763806 5575510.66999999992549419 445.60000000000002274, 3336638.53000000026077032 5575484.06000000052154064 444.90000000000003411, 3336643.05000000027939677 5575480.36000000033527613 444.90000000000003411, 3336649.37999999988824129 5575475.16000000014901161 444.90000000000003411, 3336686.28000000026077032 5575443.9599999999627471 447.60000000000002274, 3336696.18000000016763806 5575436.53000000026077032 448.5, 3336721.07000000029802322 5575417.9599999999627471 450.60000000000002274, 3336746.37999999988824129 5575399.13999999966472387 452.19999999999998863, 3336760.9599999999627471 5575391.00999999977648258 453.10000000000002274, 3336777.72000000020489097 5575386.27000000048428774 454.10000000000002274, 3336809.41000000014901161 5575384.73000000044703484 455.5, 3336824.4599999999627471 5575380.15000000037252903 456.10000000000002274, 3336846.91999999992549419 5575363.87999999988824129 456.69999999999998863, 3336861.77000000001862645 5575343.49000000022351742 456.90000000000003411, 3336874.9599999999627471 5575313.36000000033527613 456.80000000000001137, 3336884.85999999986961484 5575280.44000000040978193 456.40000000000003411, 3336897.53000000026077032 5575258.67999999970197678 456, 3336901.95000000018626451 5575253.87000000011175871 455.80000000000001137, 3336924.41000000014901161 5575232.91000000014901161 454.90000000000003411, 3336954.28000000026077032 5575209.40000000037252903 454.60000000000002274, 3336984.08000000007450581 5575185.77000000048428774 453.5, 3337013.95000000018626451 5575162.25 452.80000000000001137, 3337043.75 5575138.62999999988824129 451.80000000000001137, 3337048.12000000011175871 5575136.7099999999627471 451.69999999999998863, 3337055.70000000018626451 5575126.02000000048428774 451.40000000000003411, 3337054.9599999999627471 5575120.25 451.5, 3337054.77000000001862645 5575118.81000000052154064 451.5, 3337029.89000000013038516 5575093.63999999966472387 452.40000000000003411, 3337004.93999999994412065 5575068.4599999999627471 453.10000000000002274, 3336980.06000000005587935 5575043.2900000000372529 453.5, 3336975.95000000018626451 5575039.62999999988824129 453.60000000000002274, 3336978.56000000005587935 5575024.86000000033527613 453.80000000000001137, 3336985.31000000005587935 5575012.17999999970197678 453.80000000000001137, 3336993.08000000007450581 5574995.91000000014901161 453.80000000000001137, 3337004.89000000013038516 5574958.03000000026077032 454.40000000000003411, 3337016.78000000026077032 5574920.0400000000372529 455.40000000000003411, 3337028.60000000009313226 5574882.16000000014901161 456.40000000000003411, 3337041.03000000026077032 5574841.15000000037252903 457.30000000000001137, 3337053.4599999999627471 5574800.12999999988824129 457.69999999999998863, 3337065.89999999990686774 5574759.12000000011175871 458.5, 3337078.26000000024214387 5574718.11000000033527613 458.80000000000001137, 3337082.18999999994412065 5574695.04999999981373549 459.10000000000002274, 3337085.85000000009313226 5574668 459.5, 3337089.87000000011175871 5574624.67999999970197678 460.80000000000001137, 3337087.72000000020489097 5574596.46999999973922968 461.60000000000002274, 3337080.72000000020489097 5574563.62999999988824129 463.10000000000002274, 3337070.7099999999627471 5574530.20000000018626451 464.5, 3337060.76000000024214387 5574496.78000000026077032 466.10000000000002274, 3337062.0400000000372529 5574487.16000000014901161 466.80000000000001137, 3337056.80000000027939677 5574493.11000000033527613 466.30000000000001137, 3337051.66999999992549419 5574497.83999999985098839 465.80000000000001137, 3337043.66000000014901161 5574501.98000000044703484 465.30000000000001137, 3337037.35000000009313226 5574505.40000000037252903 464.80000000000001137, 3337029.43999999994412065 5574510.5400000000372529 464.80000000000001137, 3337027.62999999988824129 5574507.15000000037252903 464.90000000000003411, 3337017.93999999994412065 5574491.29999999981373549 465.60000000000002274, 3336992.25 5574456.25 466.90000000000003411, 3336958.70000000018626451 5574422.87999999988824129 467.30000000000001137, 3336936.89999999990686774 5574407.41000000014901161 467.40000000000003411, 3336915.16999999992549419 5574391.92999999970197678 467.10000000000002274, 3336910.97000000020489097 5574389.95000000018626451 467.10000000000002274, 3336868.83999999985098839 5574369.98000000044703484 466.60000000000002274, 3336832.55000000027939677 5574358.85000000055879354 465.69999999999998863, 3336818.07000000029802322 5574356.62999999988824129 465.5, 3336804.47999999998137355 5574355.48000000044703484 465, 3336788.07000000029802322 5574355.2099999999627471 464.5, 3336782.18000000016763806 5574353.94000000040978193 464.40000000000003411, 3336776.87000000011175871 5574350.66000000014901161 464.5, 3336774.76000000024214387 5574346.83000000007450581 464.80000000000001137, 3336773.35000000009313226 5574340.62999999988824129 465.19999999999998863, 3336786.91000000014901161 5574343 465.19999999999998863, 3336773.35000000009313226 5574340.62999999988824129 465.19999999999998863, 3336733.10000000009313226 5574344.42999999970197678 465.10000000000002274, 3336724.05000000027939677 5574347.27000000048428774 465.10000000000002274, 3336719.35000000009313226 5574349.87000000011175871 464.90000000000003411, 3336714.27000000001862645 5574353.91999999992549419 464.60000000000002274, 3336674.58000000007450581 5574357.48000000044703484 464.60000000000002274, 3336634.82000000029802322 5574361.15000000037252903 464.69999999999998863, 3336588.37000000011175871 5574365.48000000044703484 464.5, 3336541.85000000009313226 5574369.69000000040978193 464.69999999999998863, 3336517.89000000013038516 5574372.2099999999627471 464.90000000000003411, 3336490.55000000027939677 5574378.50999999977648258 464.80000000000001137, 3336463.2099999999627471 5574384.81000000052154064 464.80000000000001137, 3336439.30000000027939677 5574393.66999999992549419 464.60000000000002274, 3336415.39999999990686774 5574402.65000000037252903 464.90000000000003411, 3336393.39999999990686774 5574415.67999999970197678 464.40000000000003411, 3336369.78000000026077032 5574429.10000000055879354 464.19999999999998863, 3336343.27000000001862645 5574446.06000000052154064 464, 3336316.76000000024214387 5574463.02000000048428774 464.5, 3336311.07000000029802322 5574468.32000000029802322 464.90000000000003411, 3336302.41000000014901161 5574471.92999999970197678 465.30000000000001137, 3336293.99000000022351742 5574474.08000000007450581 465.69999999999998863, 3336289.16999999992549419 5574475.23000000044703484 465.80000000000001137, 3336284.32000000029802322 5574477.61000000033527613 466, 3336280.57000000029802322 5574480.73000000044703484 466.19999999999998863, 3336276.72999999998137355 5574485.74000000022351742 466.30000000000001137, 3336246.95000000018626451 5574512.27000000048428774 467.40000000000003411, 3336217.22999999998137355 5574538.67999999970197678 469.40000000000003411, 3336187.45000000018626451 5574565.08999999985098839 470.40000000000003411, 3336165.18000000016763806 5574583.03000000026077032 471.10000000000002274, 3336142.97999999998137355 5574600.98000000044703484 471.80000000000001137, 3336103.99000000022351742 5574627.33999999985098839 471.80000000000001137, 3336071.12000000011175871 5574653.07000000029802322 471.90000000000003411, 3336038.32000000029802322 5574678.79999999981373549 472, 3336005.52000000001862645 5574704.63999999966472387 472.19999999999998863, 3335970.87000000011175871 5574730.53000000026077032 471.80000000000001137, 3335936.22000000020489097 5574756.32000000029802322 471.19999999999998863, 3335901.64999999990686774 5574782.2099999999627471 470.30000000000001137, 3335867 5574808.11000000033527613 469.10000000000002274, 3335832.35000000009313226 5574834.00999999977648258 468.40000000000003411, 3335800.43000000016763806 5574860.37999999988824129 468, 3335768.51000000024214387 5574886.75 467.40000000000003411, 3335736.58999999985098839 5574913.12000000011175871 468, 3335704.66000000014901161 5574939.49000000022351742 467.90000000000003411, 3335701.47000000020489097 5574942.37000000011175871 468, 3335664.37000000011175871 5574971.91000000014901161 468, 3335627.28000000026077032 5575001.56000000052154064 467.69999999999998863, 3335601.16000000014901161 5575017.28000000026077032 468.5, 3335591.97000000020489097 5575022.36000000033527613 468.69999999999998863, 3335571.7099999999627471 5575033.56000000052154064 469.40000000000003411, 3335548.62000000011175871 5575043.2900000000372529 470.10000000000002274, 3335522.22999999998137355 5575052.67999999970197678 471, 3335477.7099999999627471 5575065.87000000011175871 472.40000000000003411, 3335435.31000000005587935 5575076.31000000052154064 472.40000000000003411, 3335417.43000000016763806 5575079.41999999992549419 472.69999999999998863, 3335383.37999999988824129 5575085.37999999988824129 472.5, 3335355.27000000001862645 5575092.0400000000372529 472.19999999999998863, 3335345.97000000020489097 5575095.56000000052154064 472.69999999999998863, 3335311.0400000000372529 5575112.23000000044703484 470.60000000000002274, 3335302.85999999986961484 5575115.15000000037252903 470.5, 3335294.14000000013038516 5575121.44000000040978193 469.90000000000003411, 3335289.72000000020489097 5575137.83000000007450581 468.69999999999998863, 3335244.85000000009313226 5575116.85000000055879354 472.5, 3335236.68999999994412065 5575115.87000000011175871 472.90000000000003411, 3335188.01000000024214387 5575123.95000000018626451 473.40000000000003411, 3335139.26000000024214387 5575131.92999999970197678 473.10000000000002274, 3335090.58999999985098839 5575140.00999999977648258 472.90000000000003411, 3335041.91000000014901161 5575148.08999999985098839 474.10000000000002274, 3334999.33000000007450581 5575157.2099999999627471 475.40000000000003411, 3334956.75 5575166.21999999973922968 477.30000000000001137, 3334914.16999999992549419 5575175.33000000007450581 479.30000000000001137, 3334871.58999999985098839 5575184.45000000018626451 480.19999999999998863, 3334823.58000000007450581 5575195.85000000055879354 482.19999999999998863, 3334775.66000000014901161 5575207.37000000011175871 482.90000000000003411, 3334763.95000000018626451 5575209.40000000037252903 482.90000000000003411, 3334730.95000000018626451 5575214.54999999981373549 481.30000000000001137, 3334697.93999999994412065 5575219.70000000018626451 480.40000000000003411, 3334664.93999999994412065 5575224.9599999999627471 479, 3334640.33000000007450581 5575226.83999999985098839 477.30000000000001137, 3334596.78000000026077032 5575232.31000000052154064 474.69999999999998863, 3334574.66999999992549419 5575236.90000000037252903 474, 3334553.53000000026077032 5575242.7900000000372529 473.60000000000002274, 3334535.97999999998137355 5575249.57000000029802322 472.69999999999998863, 3334518.08999999985098839 5575259.25999999977648258 471.5, 3334513.35000000009313226 5575262.53000000026077032 471, 3334499.68999999994412065 5575275.31000000052154064 470, 3334479.58999999985098839 5575298.31000000052154064 468.40000000000003411, 3334477.66000000014901161 5575300.7099999999627471 468.19999999999998863, 3334471.16000000014901161 5575309.49000000022351742 467.5, 3334466.4599999999627471 5575314.20000000018626451 467.10000000000002274, 3334456.53000000026077032 5575318.17999999970197678 466.69999999999998863, 3334442.80000000027939677 5575305.58000000007450581 466.60000000000002274, 3334438.51000000024214387 5575300.82000000029802322 466.30000000000001137, 3334432.60000000009313226 5575294.2099999999627471 465.90000000000003411, 3334424.10000000009313226 5575282.35000000055879354 465.60000000000002274, 3334408.10000000009313226 5575247.33000000007450581 466.19999999999998863, 3334380.16999999992549419 5575266.4599999999627471 464.19999999999998863, 3334352.16999999992549419 5575285.70000000018626451 460.5, 3334334.43000000016763806 5575293.27000000048428774 458.40000000000003411, 3334327.99000000022351742 5575290.12999999988824129 458.5, 3334314.89999999990686774 5575288.98000000044703484 458.30000000000001137, 3334289.87999999988824129 5575282.53000000026077032 457.80000000000001137, 3334264.85999999986961484 5575276.19000000040978193 450.90000000000003411, 3334258.58000000007450581 5575303.54999999981373549 449, 3334252.22999999998137355 5575330.91000000014901161 447.60000000000002274, 3334248.93999999994412065 5575334.91000000014901161 447, 3334246.99000000022351742 5575338.75 446.60000000000002274, 3334241.76000000024214387 5575356.28000000026077032 445.69999999999998863, 3334229.43999999994412065 5575386.62000000011175871 444, 3334223.16000000014901161 5575397.95000000018626451 443.30000000000001137))", + "MultiLineStringZ ((3334205 5575410 442.10000000000002274, 3334210 5575410 442.80000000000001137, 3334190 5575385 443.60000000000002274, 3334185 5575375 444.69999999999998863, 3334175 5575350 447, 3334150 5575360 446.90000000000003411, 3334140 5575365 446.5, 3334125 5575380 445.30000000000001137, 3334115 5575385 444.80000000000001137, 3334105 5575390 444.40000000000003411, 3334095 5575395 444.30000000000001137, 3334085 5575395 444.40000000000003411, 3334080 5575395 444.69999999999998863, 3334070 5575390 444.80000000000001137, 3334060 5575405 442.13999999999998636, 3334045 5575415 438.81000000000000227, 3334130 5575530 437.62000000000000455, 3334180 5575495 439.01999999999998181, 3334195 5575540 440.03000000000002956, 3334230 5575540 441.62999999999999545, 3334210 5575620 442.37999999999999545, 3334135 5575600 438.60000000000002274, 3334135 5575605 438.60000000000002274, 3334130 5575610 438.69999999999998863, 3334120 5575640 438.90000000000003411, 3334115 5575645 438.90000000000003411, 3334105 5575670 439.40000000000003411, 3334100 5575675 439.69999999999998863, 3334100 5575685 440, 3334095 5575695 440.69999999999998863, 3334095 5575710 441.40000000000003411, 3334095 5575730 442.10000000000002274, 3334100 5575750 442.90000000000003411, 3334100 5575760 443.30000000000001137, 3334095 5575760 443.69999999999998863, 3334080 5575775 445.30000000000001137, 3334080 5575785 445.69999999999998863, 3334070 5575815 447.60000000000002274, 3334070 5575830 447.90000000000003411, 3334075 5575865 447.60000000000002274, 3334080 5575895 447.10000000000002274, 3334075 5575900 447.19999999999998863, 3334070 5575905 447.40000000000003411, 3334065 5575955 446.5, 3334055 5576000 447.90000000000003411, 3334050 5576050 450, 3334040 5576060 449.90000000000003411, 3334005 5576080 450.19999999999998863, 3333975 5576100 453.10000000000002274, 3333970 5576105 453.60000000000002274, 3334005 5576130 453.5, 3334025 5576140 453.30000000000001137, 3334015 5576160 455.30000000000001137, 3334010 5576165 455.5, 3333995 5576180 455.69999999999998863, 3333995 5576185 455.80000000000001137, 3333990 5576185 456, 3333985 5576200 457.10000000000002274, 3333965 5576220 460.5, 3333935 5576245 466.69999999999998863, 3333905 5576270 470.69999999999998863, 3333875 5576295 472.19999999999998863, 3333850 5576315 474.10000000000002274, 3333845 5576320 474.69999999999998863, 3333820 5576340 476.30000000000001137, 3333845 5576355 477.30000000000001137, 3333870 5576375 477.80000000000001137, 3333900 5576390 478.60000000000002274, 3333925 5576410 479, 3333935 5576415 479.10000000000002274, 3333945 5576440 479.5, 3333960 5576470 479.60000000000002274, 3333975 5576505 478.40000000000003411, 3333980 5576505 478.30000000000001137, 3333980 5576510 478, 3333935 5576530 479.80000000000001137, 3333890 5576555 481.80000000000001137, 3333855 5576570 483.80000000000001137, 3333850 5576575 484.10000000000002274, 3333875 5576605 482.5, 3333905 5576635 481.10000000000002274, 3333935 5576660 480.19999999999998863, 3333965 5576690 479.30000000000001137, 3333995 5576720 478.69999999999998863, 3334015 5576735 478.40000000000003411, 3334055 5576760 477, 3334090 5576780 473.90000000000003411, 3334135 5576805 471, 3334160 5576825 469.30000000000001137, 3334190 5576845 467.5, 3334195 5576850 467.5, 3334190 5576855 467.5, 3334200 5576855 467.5, 3334225 5576865 467.30000000000001137, 3334260 5576870 466, 3334285 5576875 464.69999999999998863, 3334315 5576880 464.80000000000001137, 3334320 5576880 464.90000000000003411, 3334330 5576880 464.80000000000001137, 3334365 5576870 464.19999999999998863, 3334405 5576855 463.10000000000002274, 3334445 5576845 461.69999999999998863, 3334450 5576840 461.60000000000002274, 3334490 5576865 462, 3334525 5576885 462.69999999999998863, 3334530 5576885 462.69999999999998863, 3334530 5576890 462.80000000000001137, 3334560 5576910 463.40000000000003411, 3334590 5576935 464.90000000000003411, 3334620 5576960 465.5, 3334650 5576985 466.60000000000002274, 3334655 5576990 466.80000000000001137, 3334680 5577000 466.90000000000003411, 3334705 5577015 467.30000000000001137, 3334745 5577035 467.5, 3334785 5577060 468.80000000000001137, 3334825 5577080 469.90000000000003411, 3334865 5577100 471.30000000000001137, 3334900 5577120 472, 3334900 5577125 472.10000000000002274, 3334880 5577155 472.30000000000001137, 3334855 5577185 471, 3334835 5577205 471.69999999999998863, 3334795 5577235 472.30000000000001137, 3334755 5577265 472.30000000000001137, 3334720 5577295 473.10000000000002274, 3334680 5577320 473.60000000000002274, 3334665 5577335 474, 3334660 5577340 474.10000000000002274, 3334690 5577360 474.69999999999998863, 3334715 5577380 476.19999999999998863, 3334745 5577405 478.40000000000003411, 3334775 5577425 480.69999999999998863, 3334790 5577440 481.10000000000002274, 3334800 5577455 481.69999999999998863, 3334825 5577490 482.5, 3334850 5577530 483.90000000000003411, 3334865 5577550 483.90000000000003411, 3334880 5577575 482.90000000000003411, 3334895 5577600 482, 3334915 5577625 481.19999999999998863, 3334935 5577650 481.69999999999998863, 3334740 5577805 472.40000000000003411, 3334740 5577810 472.40000000000003411, 3334745 5577815 472.60000000000002274, 3334750 5577825 472.90000000000003411, 3334765 5577835 473.60000000000002274, 3334775 5577840 474.30000000000001137, 3334800 5577865 476.10000000000002274, 3334825 5577885 477.5, 3334845 5577905 478.69999999999998863, 3334860 5577915 479.30000000000001137, 3334865 5577920 479.69999999999998863, 3334885 5577935 480.60000000000002274, 3334915 5577965 481.60000000000002274, 3334950 5577990 482.69999999999998863, 3334980 5578020 483.90000000000003411, 3334995 5578035 484.5, 3335010 5578050 485.10000000000002274, 3335020 5578055 485.5, 3335025 5578060 485.80000000000001137, 3335035 5578065 486.19999999999998863, 3335045 5578065 486.69999999999998863, 3335050 5578070 487.19999999999998863, 3335060 5578070 487.69999999999998863, 3335070 5578070 488.30000000000001137, 3335070 5578075 488.30000000000001137, 3335070 5578080 488, 3335060 5578090 487.5, 3335060 5578095 487.19999999999998863, 3335050 5578105 486.80000000000001137, 3335050 5578120 486.60000000000002274, 3335050 5578125 486.60000000000002274, 3335070 5578145 487.40000000000003411, 3335070 5578150 487.5, 3335070 5578155 487.5, 3335070 5578160 487.40000000000003411, 3335060 5578200 486.19999999999998863, 3335055 5578215 485.90000000000003411, 3335055 5578230 485.5, 3335060 5578250 485.19999999999998863, 3335070 5578265 485.30000000000001137, 3335090 5578275 485.80000000000001137, 3335125 5578285 487.19999999999998863, 3335130 5578290 487.40000000000003411, 3335135 5578290 487.60000000000002274, 3335140 5578305 487.60000000000002274, 3335155 5578330 487.5, 3335160 5578335 487.60000000000002274, 3335165 5578340 487.90000000000003411, 3335175 5578345 488.40000000000003411, 3335190 5578350 489, 3335200 5578350 489.5, 3335210 5578355 490.30000000000001137, 3335235 5578360 491.60000000000002274, 3335245 5578360 492.40000000000003411, 3335260 5578365 493.40000000000003411, 3335270 5578370 493.80000000000001137, 3335270 5578375 493.90000000000003411, 3335275 5578380 493.90000000000003411, 3335270 5578395 493.30000000000001137, 3335260 5578405 492.30000000000001137, 3335250 5578425 491.60000000000002274, 3335245 5578440 491.19999999999998863, 3335245 5578470 490.90000000000003411, 3335240 5578500 490.90000000000003411, 3335240 5578530 491.10000000000002274, 3335240 5578555 491.40000000000003411, 3335245 5578570 491.60000000000002274, 3335250 5578590 492.10000000000002274, 3335265 5578625 493, 3335285 5578645 493.5, 3335295 5578660 493.80000000000001137, 3335300 5578670 493.90000000000003411, 3335315 5578690 494, 3335335 5578705 494.30000000000001137, 3335350 5578725 494.40000000000003411, 3335360 5578730 494.60000000000002274, 3335370 5578740 494.80000000000001137, 3335380 5578745 495.19999999999998863, 3335395 5578745 495.80000000000001137, 3335400 5578740 496.19999999999998863, 3335425 5578730 497.60000000000002274, 3335445 5578715 498.80000000000001137, 3335450 5578715 499, 3335450 5578710 499.19999999999998863, 3335455 5578710 499.5, 3335465 5578710 499.90000000000003411, 3335470 5578710 500.10000000000002274, 3335475 5578715 500.19999999999998863, 3335485 5578720 500.69999999999998863, 3335480 5578730 500.10000000000002274, 3335485 5578720 500.69999999999998863, 3335500 5578710 501.90000000000003411, 3335535 5578680 504.69999999999998863, 3335555 5578670 505.40000000000003411, 3335560 5578665 505.60000000000002274, 3335575 5578665 505.90000000000003411, 3335620 5578660 506.80000000000001137, 3335630 5578660 507.10000000000002274, 3335660 5578665 508, 3335680 5578675 509.10000000000002274, 3335705 5578680 510.60000000000002274, 3335715 5578685 511.10000000000002274, 3335740 5578695 512.39999999999997726, 3335775 5578725 514.29999999999995453, 3335810 5578760 516.20000000000004547, 3335845 5578790 518.10000000000002274, 3335880 5578825 520.10000000000002274, 3335910 5578855 522, 3335945 5578890 523.79999999999995453, 3335980 5578920 525.10000000000002274, 3336015 5578950 526.10000000000002274, 3336040 5578975 526.70000000000004547, 3336070 5579000 527.20000000000004547, 3336090 5579020 527.29999999999995453, 3336130 5579030 528.89999999999997726, 3336175 5579035 530.60000000000002274, 3336215 5579045 532.79999999999995453, 3336255 5579055 535.29999999999995453, 3336255 5579050 535.29999999999995453, 3336255 5579055 535.29999999999995453, 3336300 5579065 537.60000000000002274, 3336350 5579075 539.10000000000002274, 3336395 5579085 540, 3336440 5579095 540.5, 3336450 5579095 540.5, 3336475 5579070 540.20000000000004547, 3336500 5579050 539.60000000000002274, 3336505 5579045 539.5, 3336535 5579025 538.20000000000004547, 3336550 5579020 537.10000000000002274, 3336585 5579000 534.29999999999995453, 3336620 5578980 531, 3336630 5578970 529.89999999999997726, 3336660 5578950 526.20000000000004547, 3336685 5578930 523, 3336695 5578925 521.79999999999995453, 3336705 5578915 520.60000000000002274, 3336720 5578900 518.10000000000002274, 3336725 5578895 517, 3336745 5578865 513.5, 3336750 5578860 512.79999999999995453, 3336755 5578855 512.10000000000002274, 3336755 5578850 511.60000000000002274, 3336755 5578835 510.69999999999998863, 3336760 5578805 509, 3336760 5578780 507.69999999999998863, 3336775 5578770 505.69999999999998863, 3336785 5578770 504.40000000000003411, 3336830 5578770 500.5, 3336835 5578770 500, 3336870 5578770 496.10000000000002274, 3336880 5578770 495.10000000000002274, 3336905 5578765 493.80000000000001137, 3336910 5578760 493.60000000000002274, 3336915 5578760 493.40000000000003411, 3336920 5578755 493.30000000000001137, 3336940 5578755 493, 3336965 5578755 492.60000000000002274, 3336995 5578750 491.90000000000003411, 3337030 5578745 490.40000000000003411, 3337040 5578740 490, 3337055 5578730 489.60000000000002274, 3337065 5578720 489.40000000000003411, 3337070 5578715 489.40000000000003411, 3337080 5578705 489, 3337105 5578685 486.80000000000001137, 3337130 5578665 485.19999999999998863, 3337135 5578655 484.69999999999998863, 3337150 5578640 484.19999999999998863, 3337170 5578620 484.80000000000001137, 3337190 5578595 484.69999999999998863, 3337200 5578580 484.5, 3337205 5578570 484.30000000000001137, 3337210 5578560 484, 3337220 5578540 483.40000000000003411, 3337220 5578530 483.10000000000002274, 3337225 5578510 482.80000000000001137, 3337230 5578505 482.80000000000001137, 3337235 5578500 482.60000000000002274, 3337240 5578495 482.5, 3337245 5578495 482.40000000000003411, 3337255 5578495 482.30000000000001137, 3337260 5578495 482.19999999999998863, 3337300 5578505 482.40000000000003411, 3337330 5578510 482.80000000000001137, 3337370 5578525 483.60000000000002274, 3337410 5578530 482.69999999999998863, 3337425 5578535 482, 3337445 5578535 481.30000000000001137, 3337470 5578535 480.60000000000002274, 3337480 5578535 480.5, 3337500 5578525 480.19999999999998863, 3337535 5578510 480.19999999999998863, 3337570 5578490 480.5, 3337585 5578480 480, 3337610 5578465 477.80000000000001137, 3337635 5578450 474.80000000000001137, 3337635 5578445 475.30000000000001137, 3337630 5578435 476.69999999999998863, 3337625 5578415 477.90000000000003411, 3337625 5578410 478, 3337625 5578405 478.10000000000002274, 3337175 5577945 515.5, 3337160 5577940 516.70000000000004547, 3337160 5577935 516.79999999999995453, 3337135 5577925 518.70000000000004547, 3337105 5577905 521.10000000000002274, 3337095 5577900 521.70000000000004547, 3337065 5577875 524.10000000000002274, 3337040 5577855 525.5, 3337020 5577840 526.39999999999997726, 3336995 5577825 528, 3336975 5577805 529.29999999999995453, 3336935 5577780 530.70000000000004547, 3336930 5577775 530.89999999999997726, 3336920 5577765 531.29999999999995453, 3336915 5577760 531.39999999999997726, 3336900 5577740 532.29999999999995453, 3336885 5577730 532.70000000000004547, 3336875 5577725 532.89999999999997726, 3336870 5577725 532.70000000000004547, 3336860 5577720 532.39999999999997726, 3336850 5577720 532, 3336835 5577715 531.60000000000002274, 3336805 5577695 530.79999999999995453, 3336775 5577680 530.39999999999997726, 3336750 5577665 529.89999999999997726, 3336740 5577660 529.60000000000002274, 3336720 5577650 529.5, 3336695 5577640 529.29999999999995453, 3336660 5577610 528.29999999999995453, 3336640 5577600 527.70000000000004547, 3336630 5577590 527.20000000000004547, 3336620 5577585 526.89999999999997726, 3336615 5577580 526.79999999999995453, 3336605 5577575 526.5, 3336595 5577570 526.20000000000004547, 3336585 5577560 526, 3336555 5577535 524.89999999999997726, 3336520 5577515 523.39999999999997726, 3336495 5577490 521.89999999999997726, 3336470 5577470 520.20000000000004547, 3336465 5577460 519.70000000000004547, 3336445 5577445 518.60000000000002274, 3336435 5577435 517.89999999999997726, 3336430 5577425 517.39999999999997726, 3336425 5577415 517.29999999999995453, 3336420 5577390 517.20000000000004547, 3336415 5577350 514.89999999999997726, 3336405 5577340 513.70000000000004547, 3336390 5577325 512.39999999999997726, 3336380 5577315 511.69999999999998863, 3336390 5577310 511, 3336400 5577305 509.90000000000003411, 3336405 5577300 509.30000000000001137, 3336425 5577260 506.40000000000003411, 3336430 5577250 505.90000000000003411, 3336450 5577215 502.60000000000002274, 3336460 5577190 500.30000000000001137, 3336470 5577170 498.19999999999998863, 3336485 5577140 495.90000000000003411, 3336495 5577110 493.5, 3336485 5577100 493.69999999999998863, 3336460 5577085 494, 3336425 5577060 494.40000000000003411, 3336410 5577050 494.69999999999998863, 3336370 5577025 494.60000000000002274, 3336340 5577000 494, 3336310 5576975 493.19999999999998863, 3336305 5576970 493.10000000000002274, 3336265 5576945 492.40000000000003411, 3336250 5576920 492.10000000000002274, 3336235 5576890 491.69999999999998863, 3336225 5576865 491.30000000000001137, 3336210 5576835 491.30000000000001137, 3336200 5576810 491.5, 3336190 5576785 492, 3336175 5576760 493.69999999999998863, 3336205 5576745 496.19999999999998863, 3336220 5576730 497.30000000000001137, 3336230 5576720 498, 3336240 5576695 500, 3336250 5576685 501, 3336260 5576675 501.60000000000002274, 3336245 5576650 503.10000000000002274, 3336235 5576630 503.90000000000003411, 3336220 5576605 503.80000000000001137, 3336210 5576580 502.80000000000001137, 3336205 5576570 502.19999999999998863, 3336190 5576545 501.19999999999998863, 3336185 5576535 501, 3336185 5576520 501.10000000000002274, 3336185 5576490 501.10000000000002274, 3336195 5576465 499.90000000000003411, 3336195 5576455 499.40000000000003411, 3336195 5576445 498.80000000000001137, 3336195 5576430 498.19999999999998863, 3336195 5576425 498, 3336195 5576400 496.19999999999998863, 3336200 5576390 495.19999999999998863, 3336220 5576360 490.19999999999998863, 3336235 5576320 485.69999999999998863, 3336240 5576295 483.19999999999998863, 3336245 5576295 482.90000000000003411, 3336255 5576250 479.10000000000002274, 3336255 5576240 477.90000000000003411, 3336260 5576205 473.69999999999998863, 3336260 5576195 472.40000000000003411, 3336260 5576190 472.19999999999998863, 3336255 5576185 471.80000000000001137, 3336245 5576175 471.69999999999998863, 3336235 5576170 471.60000000000002274, 3336200 5576140 470.60000000000002274, 3336160 5576115 466, 3336160 5576110 465.30000000000001137, 3336160 5576105 464.80000000000001137, 3336175 5576090 463, 3336180 5576080 462.19999999999998863, 3336180 5576070 460.80000000000001137, 3336175 5576065 460, 3336165 5576065 458.90000000000003411, 3336150 5576065 457.90000000000003411, 3336130 5576065 456.40000000000003411, 3336120 5576065 455.60000000000002274, 3336115 5576065 455.40000000000003411, 3336110 5576065 455.40000000000003411, 3336095 5576070 455.80000000000001137, 3336090 5576070 456.10000000000002274, 3336070 5576075 456.90000000000003411, 3336025 5576075 458.69999999999998863, 3336005 5576075 459.19999999999998863, 3335990 5576080 459.80000000000001137, 3335945 5576090 459.69999999999998863, 3335945 5576085 459.5, 3335950 5576080 459.30000000000001137, 3335955 5576070 459.19999999999998863, 3335960 5576065 459.10000000000002274, 3335980 5576030 458.90000000000003411, 3335985 5576020 458.90000000000003411, 3336025 5575990 457.5, 3336060 5575960 455.90000000000003411, 3336095 5575930 454.90000000000003411, 3336125 5575905 453.90000000000003411, 3336155 5575880 453.10000000000002274, 3336190 5575850 451.90000000000003411, 3336220 5575825 450.19999999999998863, 3336245 5575805 449, 3336275 5575785 447.5, 3336290 5575775 447.10000000000002274, 3336320 5575745 446.19999999999998863, 3336350 5575720 445, 3336385 5575695 444.30000000000001137, 3336390 5575690 444.30000000000001137, 3336395 5575685 444.19999999999998863, 3336420 5575665 444.5, 3336455 5575635 445.69999999999998863, 3336490 5575605 446.80000000000001137, 3336530 5575575 447.69999999999998863, 3336565 5575545 447.19999999999998863, 3336585 5575530 446.30000000000001137, 3336605 5575510 445.60000000000002274, 3336640 5575485 444.90000000000003411, 3336645 5575480 444.90000000000003411, 3336650 5575475 444.90000000000003411, 3336685 5575445 447.60000000000002274, 3336695 5575435 448.5, 3336720 5575420 450.60000000000002274, 3336745 5575400 452.19999999999998863, 3336760 5575390 453.10000000000002274, 3336780 5575385 454.10000000000002274, 3336810 5575385 455.5, 3336825 5575380 456.10000000000002274, 3336845 5575365 456.69999999999998863, 3336860 5575345 456.90000000000003411, 3336875 5575315 456.80000000000001137, 3336885 5575280 456.40000000000003411, 3336900 5575260 456, 3336900 5575255 455.80000000000001137, 3336925 5575235 454.90000000000003411, 3336955 5575210 454.60000000000002274, 3336985 5575185 453.5, 3337015 5575160 452.80000000000001137, 3337045 5575140 451.80000000000001137, 3337050 5575135 451.69999999999998863, 3337055 5575125 451.40000000000003411, 3337055 5575120 451.5, 3337030 5575095 452.40000000000003411, 3337005 5575070 453.10000000000002274, 3336980 5575045 453.5, 3336975 5575040 453.60000000000002274, 3336980 5575025 453.80000000000001137, 3336985 5575010 453.80000000000001137, 3336995 5574995 453.80000000000001137, 3337005 5574960 454.40000000000003411, 3337015 5574920 455.40000000000003411, 3337030 5574880 456.40000000000003411, 3337040 5574840 457.30000000000001137, 3337055 5574800 457.69999999999998863, 3337065 5574760 458.5, 3337080 5574720 458.80000000000001137, 3337080 5574695 459.10000000000002274, 3337085 5574670 459.5, 3337090 5574625 460.80000000000001137, 3337090 5574595 461.60000000000002274, 3337080 5574565 463.10000000000002274, 3337070 5574530 464.5, 3337060 5574495 466.10000000000002274, 3337060 5574485 466.80000000000001137, 3337055 5574495 466.30000000000001137, 3337050 5574500 465.80000000000001137, 3337045 5574500 465.30000000000001137, 3337035 5574505 464.80000000000001137, 3337030 5574510 464.80000000000001137, 3337030 5574505 464.90000000000003411, 3337020 5574490 465.60000000000002274, 3336990 5574455 466.90000000000003411, 3336960 5574425 467.30000000000001137, 3336935 5574405 467.40000000000003411, 3336915 5574390 467.10000000000002274, 3336910 5574390 467.10000000000002274, 3336870 5574370 466.60000000000002274, 3336835 5574360 465.69999999999998863, 3336820 5574355 465.5, 3336805 5574355 465, 3336790 5574355 464.5, 3336780 5574355 464.40000000000003411, 3336775 5574350 464.5, 3336775 5574345 464.80000000000001137, 3336775 5574340 465.19999999999998863, 3336785 5574345 465.19999999999998863, 3336775 5574340 465.19999999999998863, 3336735 5574345 465.10000000000002274, 3336725 5574345 465.10000000000002274, 3336720 5574350 464.90000000000003411, 3336715 5574355 464.60000000000002274, 3336675 5574355 464.60000000000002274, 3336635 5574360 464.69999999999998863, 3336590 5574365 464.5, 3336540 5574370 464.69999999999998863, 3336520 5574370 464.90000000000003411, 3336490 5574380 464.80000000000001137, 3336465 5574385 464.80000000000001137, 3336440 5574395 464.60000000000002274, 3336415 5574405 464.90000000000003411, 3336395 5574415 464.40000000000003411, 3336370 5574430 464.19999999999998863, 3336345 5574445 464, 3336315 5574465 464.5, 3336310 5574470 464.90000000000003411, 3336300 5574470 465.30000000000001137, 3336295 5574475 465.69999999999998863, 3336290 5574475 465.80000000000001137, 3336285 5574480 466, 3336280 5574480 466.19999999999998863, 3336275 5574485 466.30000000000001137, 3336245 5574510 467.40000000000003411, 3336215 5574540 469.40000000000003411, 3336185 5574565 470.40000000000003411, 3336165 5574585 471.10000000000002274, 3336145 5574600 471.80000000000001137, 3336105 5574625 471.80000000000001137, 3336070 5574655 471.90000000000003411, 3336040 5574680 472, 3336005 5574705 472.19999999999998863, 3335970 5574730 471.80000000000001137, 3335935 5574755 471.19999999999998863, 3335900 5574780 470.30000000000001137, 3335865 5574810 469.10000000000002274, 3335830 5574835 468.40000000000003411, 3335800 5574860 468, 3335770 5574885 467.40000000000003411, 3335735 5574915 468, 3335705 5574940 467.90000000000003411, 3335700 5574940 468, 3335665 5574970 468, 3335625 5575000 467.69999999999998863, 3335600 5575015 468.5, 3335590 5575020 468.69999999999998863, 3335570 5575035 469.40000000000003411, 3335550 5575045 470.10000000000002274, 3335520 5575055 471, 3335480 5575065 472.40000000000003411, 3335435 5575075 472.40000000000003411, 3335415 5575080 472.69999999999998863, 3335385 5575085 472.5, 3335355 5575090 472.19999999999998863, 3335345 5575095 472.69999999999998863, 3335310 5575110 470.60000000000002274, 3335305 5575115 470.5, 3335295 5575120 469.90000000000003411, 3335290 5575140 468.69999999999998863, 3335245 5575115 472.5, 3335235 5575115 472.90000000000003411, 3335190 5575125 473.40000000000003411, 3335140 5575130 473.10000000000002274, 3335090 5575140 472.90000000000003411, 3335040 5575150 474.10000000000002274, 3335000 5575155 475.40000000000003411, 3334955 5575165 477.30000000000001137, 3334915 5575175 479.30000000000001137, 3334870 5575185 480.19999999999998863, 3334825 5575195 482.19999999999998863, 3334775 5575205 482.90000000000003411, 3334765 5575210 482.90000000000003411, 3334730 5575215 481.30000000000001137, 3334700 5575220 480.40000000000003411, 3334665 5575225 479, 3334640 5575225 477.30000000000001137, 3334595 5575230 474.69999999999998863, 3334575 5575235 474, 3334555 5575245 473.60000000000002274, 3334535 5575250 472.69999999999998863, 3334520 5575260 471.5, 3334515 5575265 471, 3334500 5575275 470, 3334480 5575300 468.40000000000003411, 3334470 5575310 467.5, 3334465 5575315 467.10000000000002274, 3334455 5575320 466.69999999999998863, 3334445 5575305 466.60000000000002274, 3334440 5575300 466.30000000000001137, 3334435 5575295 465.90000000000003411, 3334425 5575280 465.60000000000002274, 3334410 5575245 466.19999999999998863, 3334380 5575265 464.19999999999998863, 3334350 5575285 460.5, 3334335 5575295 458.40000000000003411, 3334330 5575290 458.5, 3334315 5575290 458.30000000000001137, 3334290 5575285 457.80000000000001137, 3334265 5575275 450.90000000000003411, 3334260 5575305 449, 3334250 5575330 447.60000000000002274, 3334250 5575335 447, 3334245 5575340 446.60000000000002274, 3334240 5575355 445.69999999999998863, 3334230 5575385 444, 3334225 5575400 443.30000000000001137))", + 3.36203807 + ); + } + + public void testShort() { + runTest("LINESTRING (1 1, 2 2)", + "LINESTRING (1 4, 2 3)", 3d); + } + private static final double TOLERANCE = 0.00001; + + private void runTest(String wkt1, String wkt2, double expectedDistance) { + Geometry g1 = read(wkt1); + Geometry g2 = read(wkt2); + + DiscreteFrechetDistanceLinear.distance(g1, g2); + Stopwatch sw = new Stopwatch(); + sw.start(); + double distance0 = DiscreteFrechetDistanceLinear.distance(g1, g2); + sw.stop(); + //System.out.println(String.format("DiscreteFrechetDistanceLinear %dms.%n", sw.getTime())); + assertEquals(expectedDistance, distance0, TOLERANCE); + + DiscreteFrechetDistance.distance(g1, g2); + sw.reset(); + sw.start(); + double distance1 = DiscreteFrechetDistance.distance(g1, g2); + sw.stop(); + //System.out.println(String.format("DiscreteFrechetDistance %dms.%n", sw.getTime())); + assertEquals(expectedDistance, distance1, TOLERANCE); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java new file mode 100644 index 0000000000..3ae5b52b53 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jts.algorithm.distance; + +import junit.framework.TestCase; + +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.MatrixStorage; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.HashMapMatrix; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.CsrMatrix; +import org.locationtech.jts.algorithm.distance.DiscreteFrechetDistance.RectMatrix; + +import org.junit.Test; + +public class MatrixStorageImplTests extends TestCase { + + @Test + public void testCsrMatrix() + { + MatrixStorage mat = new CsrMatrix(4, 6, 0d, 8); + runOrderedTest(mat); + mat = new CsrMatrix(4, 6, 0d, 8); + runUnorderedTest(mat); + } + @Test + public void testHashMapMatrix() + { + MatrixStorage mat = new HashMapMatrix(4, 6, 0d); + runOrderedTest(mat); + mat = new HashMapMatrix(4, 6, 0d); + runUnorderedTest(mat); + } + @Test + public void testRectMatrix() + { + MatrixStorage mat = new RectMatrix(4, 6, 0d); + runOrderedTest(mat); + mat = new RectMatrix(4, 6, 0d); + runUnorderedTest(mat); + } + + private static void runOrderedTest(MatrixStorage mat) { + mat.set(0, 0, 10); + mat.set(0, 1, 20); + mat.set(1, 1, 30); + mat.set(1, 3, 40); + mat.set(2, 2, 50); + mat.set(2, 3, 60); + mat.set(2, 4, 70); + mat.set(3, 5, 80); + + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 0, 10d, mat.get(0, 0)), 10d, mat.get(0, 0)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 1, 20d, mat.get(0, 1)), 20d, mat.get(0, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 1, 30d, mat.get(1, 1)), 30d, mat.get(1, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 3, 40d, mat.get(1, 3)), 40d, mat.get(1, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 2, 50d, mat.get(2, 2)), 50d, mat.get(2, 2)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 3, 60d, mat.get(2, 3)), 60d, mat.get(2, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 4, 70d, mat.get(2, 4)), 70d, mat.get(2, 4)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 3, 5, 80d, mat.get(3, 5)), 80d, mat.get(3, 5)); + + } + + private static void runUnorderedTest(MatrixStorage mat) { + mat.set(0, 0, 10); + mat.set(3, 5, 80); + mat.set(0, 1, 20); + mat.set(2, 4, 70); + mat.set(1, 1, 30); + mat.set(2, 3, 60); + mat.set(2, 2, 50); + mat.set(1, 3, 40); + + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 0, 10d, mat.get(0, 0)), 10d, mat.get(0, 0)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 0, 1, 20d, mat.get(0, 1)), 20d, mat.get(0, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 1, 30d, mat.get(1, 1)), 30d, mat.get(1, 1)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 1, 3, 40d, mat.get(1, 3)), 40d, mat.get(1, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 2, 50d, mat.get(2, 2)), 50d, mat.get(2, 2)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 3, 60d, mat.get(2, 3)), 60d, mat.get(2, 3)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 2, 4, 70d, mat.get(2, 4)), 70d, mat.get(2, 4)); + assertEquals(String.format("%1$d -> %2$d = %4$f /= %3$f", 3, 5, 80d, mat.get(3, 5)), 80d, mat.get(3, 5)); + + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java new file mode 100644 index 0000000000..42501f7467 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/FrechetSimilarityMeasureTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.geom.Geometry; +import test.jts.GeometryTestCase; + +public class FrechetSimilarityMeasureTest extends GeometryTestCase { + + public FrechetSimilarityMeasureTest(String name) { + super(name); + } + + public void testDifferentGeometryTypesThrowIAE() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1)"); + + try { + SimilarityMeasure sm = new FrechetSimilarityMeasure(); + sm.measure(g1, g2); + fail("Different geometry types should fail!"); + } + catch (Exception e) { + assertTrue(true); + } + } + + public void testEqualGeometriesReturn1() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("POINT(1 1)"); + assertEquals("Point", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + + g1 = read("LINESTRING(1 1, 2 1)"); + g2 = read("LINESTRING(1 1, 2 1)"); + assertEquals("LineString", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + + g1 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + g2 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + assertEquals("POLYGON", 1d, new FrechetSimilarityMeasure().measure(g1, g2)); + } + + public void testGreaterFrechetDistanceReturnsPoorerSimilarity() + { + Geometry g1 = read("LINESTRING(1 1, 2 1.0, 3 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1.1, 3 1)"); + Geometry g3 = read("LINESTRING(1 1, 2 1.2, 3 1)"); + + SimilarityMeasure sm = new FrechetSimilarityMeasure(); + double m12 = sm.measure(g1, g2); + double m13 = sm.measure(g1, g3); + + assertTrue("Greater distance, poorer similarity", m13 < m12); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java new file mode 100644 index 0000000000..9c805fc266 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/match/HausdorffSimilarityMeasureTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Felix Obermaier. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.match; + +import org.locationtech.jts.geom.Geometry; +import test.jts.GeometryTestCase; + +public class HausdorffSimilarityMeasureTest extends GeometryTestCase { + public HausdorffSimilarityMeasureTest(String name) { + super(name); + } + + public void testEqualGeometriesReturn1() { + Geometry g1 = read("POINT(1 1)"); + Geometry g2 = read("POINT(1 1)"); + assertEquals("Point", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + + g1 = read("LINESTRING(1 1, 2 1)"); + g2 = read("LINESTRING(1 1, 2 1)"); + assertEquals("LineString", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + + g1 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + g2 = read("POLYGON((0 0, 0 10, 10 0, 0 0), (1 1, 7.58 1, 1 7.58, 1 1))"); + assertEquals("POLYGON", 1d, new HausdorffSimilarityMeasure().measure(g1, g2)); + } + + public void testGreaterHausdorffDistanceReturnsPoorerSimilarity() + { + Geometry g1 = read("LINESTRING(1 1, 2 1.0, 3 1)"); + Geometry g2 = read("LINESTRING(1 1, 2 1.1, 3 1)"); + Geometry g3 = read("LINESTRING(1 1, 2 1.2, 3 1)"); + + SimilarityMeasure sm = new HausdorffSimilarityMeasure(); + double m12 = sm.measure(g1, g2); + double m13 = sm.measure(g1, g3); + + assertTrue("Greater distance, poorer similarity", m13 < m12); + } + +} From d481224a430c6fdd25cdc70b9a5eefb8bdf8cd52 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 12 Oct 2021 22:22:18 -0700 Subject: [PATCH 101/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 384a30f724..e3c3844e00 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,6 +29,7 @@ Distributions for older JTS versions can be obtained at the * Simplify and fix logic of `BufferParameters.setQuadSegs` (#778) * Improve `KdTree` query code to avoid recursion (#779) * Add `KdTree` seeding to`SnappingNoder` (#780) +* Add `DiscreteFrechetDistance` (#764) # Version 1.18.2 From 58e2001fb04784c14525ddc7cc7868cfade0a179 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 13 Oct 2021 15:17:17 -0700 Subject: [PATCH 102/275] Add Distance functions for DiscreteFrechetDistance Signed-off-by: Martin Davis --- .../jtstest/function/DistanceFunctions.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/DistanceFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/DistanceFunctions.java index 868e27b4f3..9c60c913f5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/DistanceFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/DistanceFunctions.java @@ -29,12 +29,23 @@ public static Geometry nearestPoints(Geometry a, Geometry b) { return a.getFactory().createLineString(pts); } - public static Geometry discreteHausdorffDistanceLine(Geometry a, Geometry b) - { + public static double discreteFrechetfDistance(Geometry a, Geometry b) + { + return DiscreteFrechetDistance.distance(a, b); + } + + public static Geometry discreteFrechetfDistanceLine(Geometry a, Geometry b) + { + DiscreteFrechetDistance dist = new DiscreteFrechetDistance(a, b); + return a.getFactory().createLineString(dist.getCoordinates()); + } + + public static Geometry discreteHausdorffDistanceLine(Geometry a, Geometry b) + { DiscreteHausdorffDistance dist = new DiscreteHausdorffDistance(a, b); dist.distance(); return a.getFactory().createLineString(dist.getCoordinates()); - } + } public static Geometry densifiedDiscreteHausdorffDistanceLine(Geometry a, Geometry b, double frac) { From b03f0125a71b4134267acfd92544c76bf1dded37 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 13 Oct 2021 15:20:29 -0700 Subject: [PATCH 103/275] Fix DiscreteFrechetDistance constructor visibility Signed-off-by: Martin Davis --- .../jts/algorithm/distance/DiscreteFrechetDistance.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java index c934584aa9..622ec9fdb8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java @@ -74,7 +74,7 @@ public static double distance(Geometry g0, Geometry g1) { * @param g0 a geometry * @param g1 a geometry */ - private DiscreteFrechetDistance(Geometry g0, Geometry g1) { + public DiscreteFrechetDistance(Geometry g0, Geometry g1) { this.g0 = g0; this.g1 = g1; } From 6486f15462a684e0d798423c6639305c3c88c4d2 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 14 Oct 2021 11:45:09 -0700 Subject: [PATCH 104/275] Fix DiscreteFrechetDistance Bresenham algorithm (#783) * Fix DiscreteFrechetDistance Bresenham algorithm * Add DiscreteFrechetDistance bresenhamDiagonal unit test Signed-off-by: Martin Davis --- .../distance/DiscreteFrechetDistance.java | 107 +++++++++--------- .../DiscreteFrechetDistanceDiagonalTest.java | 54 +++++++++ .../distance/DiscreteFrechetDistanceTest.java | 25 ++-- ...eImplTests.java => MatrixStorageTest.java} | 2 +- .../DiscreteFrechetDistanceSimple.java} | 13 ++- 5 files changed, 127 insertions(+), 74 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceDiagonalTest.java rename modules/core/src/test/java/org/locationtech/jts/algorithm/distance/{MatrixStorageImplTests.java => MatrixStorageTest.java} (98%) rename modules/core/src/test/java/{org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java => test/jts/perf/algorithm/distance/DiscreteFrechetDistanceSimple.java} (86%) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java index 622ec9fdb8..a12948950a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistance.java @@ -11,13 +11,12 @@ */ package org.locationtech.jts.algorithm.distance; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.util.Assert; - import java.util.Arrays; import java.util.HashMap; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; + /** * The Fréchet distance is a measure of similarity between curves. Thus, it can * be used like the Hausdorff distance. @@ -31,8 +30,9 @@ * speed, but backtracking is not allowed. * *

      - * Its metric is better than Hausdorff's because it takes the flow of the curves - * into account. It is possible that two curves have a small Hausdorff but a large + * Its metric is better than the Hausdorff distance + * because it takes the directions of the curves into account. + * It is possible that two curves have a small Hausdorff but a large * Fréchet distance. *

      * This implementation is base on the following optimized Fréchet distance algorithm: @@ -52,7 +52,7 @@ public class DiscreteFrechetDistance { /** * Computes the Discrete Fréchet Distance between two {@link Geometry}s - * using a {@code cartesian} distance computation function. + * using a {@code Cartesian} distance computation function. * * @param g0 the 1st geometry * @param g1 the 2nd geometry @@ -80,7 +80,7 @@ public DiscreteFrechetDistance(Geometry g0, Geometry g1) { } /** - * Compute the {@code Discrete Fréchet Distance} between two geometries + * Computes the {@code Discrete Fréchet Distance} between the input geometries * * @return the Discrete Fréchet Distance */ @@ -89,7 +89,7 @@ private double distance() { Coordinate[] coords1 = g1.getCoordinates(); MatrixStorage distances = createMatrixStorage(coords0.length, coords1.length); - int[] diagonal = bresenhamLine(coords0.length, coords1.length); + int[] diagonal = bresenhamDiagonal(coords0.length, coords1.length); HashMap distanceToPair = new HashMap<>(); computeCoordinateDistances(coords0, coords1, diagonal, distances, distanceToPair); @@ -99,12 +99,13 @@ private double distance() { } /** - * Creates a matrix storage + * Creates a matrix to store the computed distances. + * * @param rows the number of rows * @param cols the number of columns * @return a matrix storage */ - private MatrixStorage createMatrixStorage(int rows, int cols) { + private static MatrixStorage createMatrixStorage(int rows, int cols) { int max = Math.max(rows, cols); // NOTE: these constraints need to be verified @@ -115,9 +116,9 @@ private MatrixStorage createMatrixStorage(int rows, int cols) { } /** - * Gets the pair of Coordinates that are {@link #distance()} apart. + * Gets the pair of {@link Coordinate}s at which the distance is obtained. * - * @return the pair of Coordinates that are {@link #distance()} apart + * @return the pair of Coordinates at which the distance is obtained */ public Coordinate[] getCoordinates() { if (ptDist == null) @@ -127,13 +128,13 @@ public Coordinate[] getCoordinates() { } /** - * Compute the Fréchet Distance for the given distance matrix. + * Computes the Fréchet Distance for the given distance matrix. * * @param coords0 an array of {@code Coordinate}s. * @param coords1 an array of {@code Coordinate}s. - * @param distances a sparse distance matrix - * @param distanceToPair a lookup for distance and a coordinate pair - * @param diagonal an array of alternating row/col index values for the diagonal of the distance matrix + * @param diagonal an array of alternating col/row index values for the diagonal of the distance matrix + * @param distances the distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance * */ private static PointPairDistance computeFrechet(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, @@ -167,11 +168,10 @@ private static PointPairDistance computeFrechet(Coordinate[] coords0, Coordinate PointPairDistance result = new PointPairDistance(); double distance = distances.get(coords0.length-1, coords1.length - 1); int[] index = distanceToPair.get(distance); - if (index != null) - result.initialize(coords0[index[0]], coords1[index[1]], distance); - else - Assert.shouldNeverReachHere("Pair of points not recorded for computed distance"); - + if (index == null) { + throw new IllegalStateException("Pair of points not recorded for computed distance"); + } + result.initialize(coords0[index[0]], coords1[index[1]], distance); return result; } @@ -206,9 +206,9 @@ private static double getMinDistanceAtCorner(MatrixStorage matrix, int i, int j) * * @param coords0 an array of {@code Coordinate}s. * @param coords1 an array of {@code Coordinate}s. - * @param diagonal an array of encoded row/col index values for the diagonal of the distance matrix - * @param distances the sparse distance matrix - * @param distanceToPair a lookup for coordinate pairs based on a distance. + * @param diagonal an array of alternating col/row index values for the diagonal of the distance matrix + * @param distances the distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance */ private void computeCoordinateDistances(Coordinate[] coords0, Coordinate[] coords1, int[] diagonal, MatrixStorage distances, HashMap distanceToPair) { @@ -280,49 +280,48 @@ private void computeCoordinateDistances(Coordinate[] coords0, Coordinate[] coord } /** - * Implementation of the - * Bresenham's line algorithm for the diagonal of a {@code numCols x numRows} grid. + * Computes the indices for the diagonal of a {@code numCols x numRows} grid + * using the + * Bresenham line algorithm. * * @param numCols the number of columns * @param numRows the number of rows - * @return an array of column and row indices bitwise-or combined. + * @return a packed array of column and row indices */ - private static int[] bresenhamLine(int numCols, int numRows) { + static int[] bresenhamDiagonal(int numCols, int numRows) { int dim = Math.max(numCols, numRows); - int[] pairs = new int[2 * dim]; - - int sx = 0 > numCols ? -1 : 1; - int sy = 0 > numRows ? -1 : 1; - int x = 0; - int y = 0; + int[] diagXY = new int[2 * dim]; + int dx = numCols - 1; + int dy = numRows - 1; int err; + int i = 0; if (numCols > numRows) { - err = numCols / 2; - for (int i = 0, j = 0; i < numCols; i++) { - pairs[j++] = x; - pairs[j++] = y; - err -= numRows; - if (err < 0) { - y += sy; - err += numCols; + int y = 0; + err = 2 * dy - dx; + for (int x = 0; x < numCols; x++) { + diagXY[i++] = x; + diagXY[i++] = y; + if (err > 0) { + y += 1; + err -= 2 * dx; } - x += sx; + err += 2 * dy; } } else { - err = numRows / 2; - for (int i = 0, j = 0; i < numRows; i++) { - pairs[j++] = x; - pairs[j++] = y; - err -= numCols; - if (err < 0) { - x += sx; - err += numRows; + int x = 0; + err = 2 * dx - dy; + for (int y = 0; y < numRows; y++) { + diagXY[i++] = x; + diagXY[i++] = y; + if (err > 0) { + x += 1; + err -= 2 * dy; } - y += sy; + err += 2 * dx; } } - return pairs; + return diagXY; } /** diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceDiagonalTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceDiagonalTest.java new file mode 100644 index 0000000000..0e64008a18 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceDiagonalTest.java @@ -0,0 +1,54 @@ +package org.locationtech.jts.algorithm.distance; + +import junit.framework.TestCase; + +public class DiscreteFrechetDistanceDiagonalTest extends TestCase { + + public DiscreteFrechetDistanceDiagonalTest(String name) { + super(name); + } + + public void test1x1() { + checkDiagonal(1,1, xy(0,0)); + } + + public void test2x2() { + checkDiagonal(2,2, xy(0,0,1,1)); + } + + public void test3x3() { + checkDiagonal(3,3, xy(0,0, 1,1, 2,2)); + } + + public void test3x4() { + checkDiagonal(3,4, xy(0, 0, 1, 1, 1, 2, 2, 3)); + } + + public void test3x5() { + checkDiagonal(3,5, xy(0,0, 0,1, 1,2, 1,3, 2,4)); + } + + public void test3x6() { + checkDiagonal(3,6, xy(0,0, 0,1, 1,2, 1,3, 2,4, 2,5)); + } + + public void test6x2() { + checkDiagonal(6,2, xy(0,0, 1,0, 2,0, 3,1, 4,1, 5,1)); + } + + public void test2x6() { + checkDiagonal(2,6, xy(0,0, 0,1, 0,2, 1,3, 1,4, 1,5)); + } + + private void checkDiagonal(int cols, int rows, int[] xyExpected) { + int[] xy = DiscreteFrechetDistance.bresenhamDiagonal(cols, rows); + assertEquals(xyExpected.length, xy.length); + for (int i = 0 ; i < xy.length; i++) { + assertEquals(xyExpected[i], xy[i]); + } + } + + private static int[] xy(int... ord) { + return ord; + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java index e196a2c7d4..fbf5e154ad 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceTest.java @@ -14,7 +14,7 @@ import org.junit.Test; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.util.Stopwatch; + import test.jts.GeometryTestCase; public class DiscreteFrechetDistanceTest extends GeometryTestCase { @@ -62,26 +62,25 @@ public void testShort() { runTest("LINESTRING (1 1, 2 2)", "LINESTRING (1 4, 2 3)", 3d); } + + public void testAHasMoreThanTwiceVerticesOfB() { + runTest("LINESTRING (80 260, 170 180, 190 290, 310 350, 330 270, 360 280)", + "LINESTRING (120 90, 380 130)", 230.8679276123039); + } + + public void testA_11_B_3() { + runTest("LINESTRING (0 0, 100 10, 0 20, 100 30, 0 40, 100 50, 0 60, 100 70, 0 80, 100 90, 0 100)", + "LINESTRING (0 0, 50 100, 100 0)", 141.4213562373095); + } + private static final double TOLERANCE = 0.00001; private void runTest(String wkt1, String wkt2, double expectedDistance) { Geometry g1 = read(wkt1); Geometry g2 = read(wkt2); - DiscreteFrechetDistanceLinear.distance(g1, g2); - Stopwatch sw = new Stopwatch(); - sw.start(); - double distance0 = DiscreteFrechetDistanceLinear.distance(g1, g2); - sw.stop(); - //System.out.println(String.format("DiscreteFrechetDistanceLinear %dms.%n", sw.getTime())); - assertEquals(expectedDistance, distance0, TOLERANCE); - DiscreteFrechetDistance.distance(g1, g2); - sw.reset(); - sw.start(); double distance1 = DiscreteFrechetDistance.distance(g1, g2); - sw.stop(); - //System.out.println(String.format("DiscreteFrechetDistance %dms.%n", sw.getTime())); assertEquals(expectedDistance, distance1, TOLERANCE); } } diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageTest.java similarity index 98% rename from modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java rename to modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageTest.java index 3ae5b52b53..a4d177cbb8 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageImplTests.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/MatrixStorageTest.java @@ -21,7 +21,7 @@ import org.junit.Test; -public class MatrixStorageImplTests extends TestCase { +public class MatrixStorageTest extends TestCase { @Test public void testCsrMatrix() diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java b/modules/core/src/test/java/test/jts/perf/algorithm/distance/DiscreteFrechetDistanceSimple.java similarity index 86% rename from modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java rename to modules/core/src/test/java/test/jts/perf/algorithm/distance/DiscreteFrechetDistanceSimple.java index 8d6ebb5fc4..9e9accaf21 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/distance/DiscreteFrechetDistanceLinear.java +++ b/modules/core/src/test/java/test/jts/perf/algorithm/distance/DiscreteFrechetDistanceSimple.java @@ -1,12 +1,13 @@ -package org.locationtech.jts.algorithm.distance; +package test.jts.perf.algorithm.distance; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; /** - * Linear Discrete Fréchet Distance computation + * Discrete Fréchet Distance computation + * using a simple O(n^2) algorithm. */ -public class DiscreteFrechetDistanceLinear { +public class DiscreteFrechetDistanceSimple { /** * Computes the Discrete Fréchet Distance between two {@link Geometry}s @@ -17,7 +18,7 @@ public class DiscreteFrechetDistanceLinear { * @return the cartesian distance between {#g0} and {#g1} */ public static double distance(Geometry g0, Geometry g1) { - DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, false); + DiscreteFrechetDistanceSimple dist = new DiscreteFrechetDistanceSimple(g0, g1, false); return dist.distance(); } @@ -30,14 +31,14 @@ public static double distance(Geometry g0, Geometry g1) { * @return the cartesian distance between {#g0} and {#g1} */ public static double distance(Geometry g0, Geometry g1, boolean getCoordinates) { - DiscreteFrechetDistanceLinear dist = new DiscreteFrechetDistanceLinear(g0, g1, getCoordinates); + DiscreteFrechetDistanceSimple dist = new DiscreteFrechetDistanceSimple(g0, g1, getCoordinates); return dist.distance(); } private final Geometry g0; private final Geometry g1; private final boolean getCoordinates; - private DiscreteFrechetDistanceLinear(Geometry g0, Geometry g1, boolean getCoordinates) { + private DiscreteFrechetDistanceSimple(Geometry g0, Geometry g1, boolean getCoordinates) { this.g0 = g0; this.g1 = g1; this.getCoordinates = getCoordinates; From fb2e4a512c92d48295eb61f219ed873671664f1a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 14 Oct 2021 11:46:25 -0700 Subject: [PATCH 105/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index e3c3844e00..0d9c5209d1 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -29,7 +29,7 @@ Distributions for older JTS versions can be obtained at the * Simplify and fix logic of `BufferParameters.setQuadSegs` (#778) * Improve `KdTree` query code to avoid recursion (#779) * Add `KdTree` seeding to`SnappingNoder` (#780) -* Add `DiscreteFrechetDistance` (#764) +* Add `DiscreteFrechetDistance` (#764, #783) # Version 1.18.2 From 99e04a2124ea76931523fecc106cd3ae18029797 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 14 Oct 2021 17:41:41 -0700 Subject: [PATCH 106/275] Renaming PrecisionReducerTransformer local var Signed-off-by: Martin Davis --- .../precision/PrecisionReducerTransformer.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java b/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java index c9facb368a..5e92f9c7ec 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/PrecisionReducerTransformer.java @@ -58,28 +58,28 @@ protected CoordinateSequence transformCoordinates( /** * Check if the removal of repeated points collapsed the coordinate - * list to an invalid length for the type of the parent geometry. It is not + * list to an invalid size for the type of the parent geometry. It is not * necessary to check for Point collapses, since the coordinate list can - * never collapse to less than one point. If the length is invalid, return - * the full-length coordinate array first computed, or null if collapses are + * never collapse to less than one point. If the size is invalid, return + * the full-size coordinate array first computed, or null if collapses are * being removed. (This may create an invalid geometry - the client must * handle this.) */ - int minLength = 0; + int minSize = 0; if (parent instanceof LineString) - minLength = 2; + minSize = 2; if (parent instanceof LinearRing) - minLength = LinearRing.MINIMUM_VALID_SIZE; + minSize = LinearRing.MINIMUM_VALID_SIZE; /** * Handle collapse. If specified return null so parent geometry is removed or empty, * otherwise extend to required length. */ - if (coordsReduce.length < minLength) { + if (coordsReduce.length < minSize) { if (isRemoveCollapsed) { return null; } - coordsReduce = extend(coordsReduce, minLength); + coordsReduce = extend(coordsReduce, minSize); } return factory.getCoordinateSequenceFactory().create(coordsReduce); } From 7dcf15dab5bad0a8c9454e6f1acb44d8bc08da5e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 14 Oct 2021 18:09:32 -0700 Subject: [PATCH 107/275] Clarify GeometryPrecisionReducer operation modes Signed-off-by: Martin Davis --- .../jtstest/function/PrecisionFunctions.java | 9 ++++ .../precision/GeometryPrecisionReducer.java | 53 +++++++++++++------ .../GeometryPrecisionReducerTest.java | 14 +++-- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java index fb699ce7c4..a907ca9f27 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/PrecisionFunctions.java @@ -37,6 +37,15 @@ public static Geometry reducePrecision(Geometry geom, return reducedGeom; } + public static Geometry reducePrecisionKeepCollapsed(Geometry geom, + @Metadata(title="Scale factor") + double scaleFactor) + { + PrecisionModel pm = new PrecisionModel(scaleFactor); + Geometry reducedGeom = GeometryPrecisionReducer.reduceKeepCollapsed(geom, pm); + return reducedGeom; + } + public static Geometry minClearanceLine(Geometry g) { return MinimumClearance.getLine(g); diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index d0547cc6ab..3b9e102cdb 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -23,12 +23,16 @@ *

      * By default the reduced result is topologically valid * (i.e. {@link Geometry#isValid()} is true). - * To ensure this a polygonal geometry is reduced in a topologically valid fashion + * To ensure this polygonal geometry is reduced in a topologically valid fashion * (technically, by using snap-rounding). * Note that this may change polygonal geometry structure * (e.g. two polygons separated by a distance below the specified precision * will be merged into a single polygon). *

      + * Normally, collapsed components (e.g. lines collapsing to a point) + * are not included in the result. + * This behavior can be changed by using {@link #setRemoveCollapsedComponents(boolean)}. + *

      * In general input must be valid geometry, or an {@link IllegalArgumentException} * will be thrown. However if the invalidity is "mild" or very small then it * may be eliminated by precision reduction. @@ -40,26 +44,23 @@ *

      * By default the geometry precision model is not changed. * This can be overridden by using {@link #setChangePrecisionModel(boolean)}. - *

      - * Normally, collapsed components (e.g. lines collapsing to a point) - * are not included in the result. - * This behavior can be changed by using {@link #setRemoveCollapsedComponents(boolean)}. * * @version 1.12 */ public class GeometryPrecisionReducer { /** - * Convenience method for doing precision reduction - * on a single geometry, - * with collapses removed - * and keeping the geometry precision model the same, - * and preserving polygonal topology. + * Reduces precision of a geometry, + * ensuring output geometry is valid. + * Collapsed linear and polygonal components are removed. + * The geometry precision model is not changed. + * Invalid input geometry may cause an error, + * unless the invalidity is below the scale of the precision reduction. * * @param g the geometry to reduce * @param precModel the precision model to use * @return the reduced geometry - * @throws IllegalArgumentException if the reduction fails due to invalid input geometry is invalid + * @throws IllegalArgumentException if the reduction fails due to invalid input geometry */ public static Geometry reduce(Geometry g, PrecisionModel precModel) { @@ -67,12 +68,32 @@ public static Geometry reduce(Geometry g, PrecisionModel precModel) return reducer.reduce(g); } + /** + * Reduces precision of a geometry, + * ensuring output polygonal geometry is valid, + * but preserving collapsed linear elements. + * The geometry precision model is not changed. + * Invalid input geometry may cause an error, + * unless the invalidity is below the scale of the precision reduction. + * + * @param g the geometry to reduce + * @param precModel the precision model to use + * @return the reduced geometry + * @throws IllegalArgumentException if the reduction fails due to invalid input geometry + */ + public static Geometry reduceKeepCollapsed(Geometry geom, PrecisionModel pm) { + GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); + reducer.setRemoveCollapsedComponents(false); + return reducer.reduce(geom); + } + /** - * Convenience method for doing pointwise precision reduction - * on a single geometry, - * with collapses removed - * and keeping the geometry precision model the same, - * but NOT preserving valid polygonal topology. + * Reduce precision of a geometry in a pointwise way. + * All input geometry elements are preserved in the output, + * including invalid polygons and collapsed polygons and linestrings. + * The output may not be valid, due to collapse or self-intersection. + * The geometry precision model is not changed. + * Invalid input geometry is allowed. * * @param g the geometry to reduce * @param precModel the precision model to use diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java index d0a8cf1c17..4b220dedec 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java @@ -64,7 +64,7 @@ public void testSquareCollapse() public void testSquareKeepCollapse() throws Exception { - checkReduce("POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", + checkReduceKeepCollapse(1, "POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", "POLYGON EMPTY"); } @@ -106,10 +106,6 @@ public void testLineRemoveCollapse() "LINESTRING EMPTY"); } - /** - * Disabled for now. - * @throws Exception - */ public void testLineKeepCollapse() throws Exception { @@ -118,6 +114,14 @@ public void testLineKeepCollapse() "LINESTRING ( 0 0, 0 0 )"); } + public void testLinearRingKeepCollapse() + throws Exception + { + checkReduceKeepCollapse(1, + "LINEARRING ( 0 0, 0 .4, .4 0, 0 0 )", + "LINEARRING ( 0 0, 0 0, 0 0 )"); + } + public void testPoint() throws Exception { From 6bfa065ac224805836e3065e54cb0aba6fdabc9a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 14 Oct 2021 22:03:50 -0700 Subject: [PATCH 108/275] Javadoc Signed-off-by: Martin Davis --- .../jts/precision/GeometryPrecisionReducer.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index 3b9e102cdb..b3c2c08f30 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -52,8 +52,10 @@ public class GeometryPrecisionReducer /** * Reduces precision of a geometry, * ensuring output geometry is valid. - * Collapsed linear and polygonal components are removed. + * Collapsed linear and polygonal components are removed. + * Duplicate vertices are removed. * The geometry precision model is not changed. + *

      * Invalid input geometry may cause an error, * unless the invalidity is below the scale of the precision reduction. * @@ -70,9 +72,11 @@ public static Geometry reduce(Geometry g, PrecisionModel precModel) /** * Reduces precision of a geometry, - * ensuring output polygonal geometry is valid, - * but preserving collapsed linear elements. + * preserving collapsed linear elements. + * and ensuring output polygonal geometry is valid, + * Duplicate vertices are removed. * The geometry precision model is not changed. + *

      * Invalid input geometry may cause an error, * unless the invalidity is below the scale of the precision reduction. * @@ -92,7 +96,9 @@ public static Geometry reduceKeepCollapsed(Geometry geom, PrecisionModel pm) { * All input geometry elements are preserved in the output, * including invalid polygons and collapsed polygons and linestrings. * The output may not be valid, due to collapse or self-intersection. + * Duplicate vertices are not removed. * The geometry precision model is not changed. + *

      * Invalid input geometry is allowed. * * @param g the geometry to reduce From d9b6e93cbadc71ae5e2fa28347c22431e61e1836 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 15 Oct 2021 11:00:43 -0700 Subject: [PATCH 109/275] Add GeometryPrecisionReducer tests Signed-off-by: Martin Davis --- .../precision/GeometryPrecisionReducer.java | 39 ++-- ...GeometryPrecisionReducerPointwiseTest.java | 88 ++++++++ .../GeometryPrecisionReducerTest.java | 191 ++++++++++-------- 3 files changed, 218 insertions(+), 100 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerPointwiseTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java index b3c2c08f30..2192c2ba1f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java +++ b/modules/core/src/main/java/org/locationtech/jts/precision/GeometryPrecisionReducer.java @@ -19,39 +19,53 @@ /** * Reduces the precision of a {@link Geometry} * according to the supplied {@link PrecisionModel}, - * ensuring that the result is valid, unless specified otherwise. + * ensuring that the result is valid (unless specified otherwise). *

      - * By default the reduced result is topologically valid + * By default the geometry precision model is not changed. + * This can be overridden by using {@link #setChangePrecisionModel(boolean)}. + * + * + *

      Topological Precision Reduction

      + * + * The default mode of operation ensures the reduced result is topologically valid * (i.e. {@link Geometry#isValid()} is true). * To ensure this polygonal geometry is reduced in a topologically valid fashion * (technically, by using snap-rounding). * Note that this may change polygonal geometry structure * (e.g. two polygons separated by a distance below the specified precision * will be merged into a single polygon). + * Duplicate vertices are removed. + * This mode is invoked by the static method {@link #reduce(Geometry, PrecisionModel)}. *

      - * Normally, collapsed components (e.g. lines collapsing to a point) + * Normally, collapsed linear components (e.g. lines collapsing to a point) * are not included in the result. - * This behavior can be changed by using {@link #setRemoveCollapsedComponents(boolean)}. + * This behavior can be changed + * by setting {@link #setRemoveCollapsedComponents(boolean)} to false, + * or by using the static method {@link #reduceKeepCollapsed(Geometry, PrecisionModel)}. *

      * In general input must be valid geometry, or an {@link IllegalArgumentException} * will be thrown. However if the invalidity is "mild" or very small then it * may be eliminated by precision reduction. - *

      + * + * + *

      Pointwise Precision Reduction

      + * * Alternatively, geometry can be reduced pointwise by using {@link #setPointwise(boolean)}. - * In this case the result geometry topology may be invalid. * Linear and point geometry are always reduced pointwise (i.e. without further change to * topology or structure), since this does not change validity. + * Invalid inputs are allowed. + * Duplicate vertices are preserved. + * Collapsed components are always included in the result. + * The result geometry may be invalid. *

      - * By default the geometry precision model is not changed. - * This can be overridden by using {@link #setChangePrecisionModel(boolean)}. + * This mode is invoked by the static method {@link #reducePointwise(Geometry, PrecisionModel)}. * * @version 1.12 */ public class GeometryPrecisionReducer { /** - * Reduces precision of a geometry, - * ensuring output geometry is valid. + * Reduces precision of a geometry, ensuring output geometry is valid. * Collapsed linear and polygonal components are removed. * Duplicate vertices are removed. * The geometry precision model is not changed. @@ -71,9 +85,8 @@ public static Geometry reduce(Geometry g, PrecisionModel precModel) } /** - * Reduces precision of a geometry, - * preserving collapsed linear elements. - * and ensuring output polygonal geometry is valid, + * Reduces precision of a geometry, ensuring output polygonal geometry is valid, + * and preserving collapsed linear elements. * Duplicate vertices are removed. * The geometry precision model is not changed. *

      diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerPointwiseTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerPointwiseTest.java new file mode 100644 index 0000000000..e0b0f0613b --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerPointwiseTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.precision; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.PrecisionModel; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + + +/** + * @version 1.12 + */ +public class GeometryPrecisionReducerPointwiseTest + extends GeometryTestCase +{ + + public static void main(String args[]) { + TestRunner.run(GeometryPrecisionReducerPointwiseTest.class); + } + + public GeometryPrecisionReducerPointwiseTest(String name) + { + super(name); + } + + public void testLineWithCollapse() throws Exception { + checkReducePointwise( + "LINESTRING (0 0, 0.1 0, 1 0)", + "LINESTRING (0 0, 0 0, 1 0)"); + } + + public void testLineDuplicatePointsPreserved() throws Exception { + checkReducePointwise( + "LINESTRING (0 0, 0.1 0, 0.1 0, 1 0, 1 0)", + "LINESTRING (0 0, 0 0, 0 0, 1 0, 1 0)"); + } + + public void testLineFullCollapse() throws Exception { + checkReducePointwise( + "LINESTRING (0 0, 0.1 0)", + "LINESTRING (0 0, 0 0)"); + } + + public void testPolygonFullCollapse() throws Exception { + checkReducePointwise( + "POLYGON ((0.1 0.3, 0.3 0.3, 0.3 0.1, 0.1 0.1, 0.1 0.3))", + "POLYGON ((0 0, 0 0, 0 0, 0 0, 0 0))"); + } + + public void testPolygonWithCollapsedLine() throws Exception { + checkReducePointwise( + "POLYGON ((10 10, 100 100, 200 10.1, 300 10, 10 10))", + "POLYGON ((10 10, 100 100, 200 10, 300 10, 10 10))"); + } + + public void testPolygonWithCollapsedPoint() throws Exception { + checkReducePointwise( + "POLYGON ((10 10, 100 100, 200 10.1, 300 100, 400 10, 10 10))", + "POLYGON ((10 10, 100 100, 200 10, 300 100, 400 10, 10 10))"); + } + + //======================================= + + private void checkReducePointwise(String wkt, String wktExpected) { + Geometry g = read(wkt); + Geometry gExpected = read(wktExpected); + PrecisionModel pm = new PrecisionModel(1); + Geometry gReduce = GeometryPrecisionReducer.reducePointwise(g, pm); + assertEqualsExactAndHasSameFactory(gExpected, gReduce); + } + + private void assertEqualsExactAndHasSameFactory(Geometry expected, Geometry actual) + { + checkEqual(expected, actual); + assertTrue("Factories are not the same", expected.getFactory() == actual.getFactory()); + } +} diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java index 4b220dedec..5a560a9914 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java @@ -26,12 +26,6 @@ public class GeometryPrecisionReducerTest extends GeometryTestCase { - private PrecisionModel pmFloat = new PrecisionModel(); - private PrecisionModel pmFixed1 = new PrecisionModel(1); - - private GeometryFactory gfFloat = new GeometryFactory(pmFloat, 0); - WKTReader reader = new WKTReader(gfFloat); - public static void main(String args[]) { TestRunner.run(GeometryPrecisionReducerTest.class); } @@ -41,101 +35,106 @@ public GeometryPrecisionReducerTest(String name) super(name); } - public void testSquare() + public void testPoint() throws Exception { - checkReduce("POLYGON (( 0 0, 0 1.4, 1.4 1.4, 1.4 0, 0 0 ))", - "POLYGON (( 0 0, 0 1, 1 1, 1 0, 0 0 ))"); + checkReduce("POINT(1.1 4.9)", + "POINT(1 5)"); } - public void testTinySquareCollapse() + + public void testMultiPoint() throws Exception { - checkReduce("POLYGON (( 0 0, 0 .4, .4 .4, .4 0, 0 0 ))", - "POLYGON EMPTY"); + checkReduce("MULTIPOINT( (1.1 4.9),(1.2 4.8), (3.3 6.6))", + "MULTIPOINT((1 5), (1 5), (3 7))"); } - public void testSquareCollapse() - throws Exception - { - checkReduce("POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", - "POLYGON EMPTY"); - } + //-------------------------------------------------------------- - public void testSquareKeepCollapse() + public void testLine() throws Exception { - checkReduceKeepCollapse(1, "POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", - "POLYGON EMPTY"); + checkReduce("LINESTRING ( 0 0, 0 1.4 )", + "LINESTRING (0 0, 0 1)"); } - // see https://github.com/locationtech/jts/issues/324 - public void testPolygonCollapsesCompletely() + /** + * Tests that a self-crossing line is not noded by precision reduction. + * + * @throws Exception + */ + public void testLineNotNoded() throws Exception { - checkReduce(1000000, "POLYGON ((-121.816901763 37.3285521, -121.817392418 37.328343575, -121.817876714 37.328137752, -121.818253966 37.327977421, -121.8184719 37.3278848, -121.816901763 37.3285521))", - "POLYGON EMPTY"); + checkReduce( + "LINESTRING( 1 1, 3 3, 9 9, 5.1 5, 2.1 2 )", + "LINESTRING( 1 1, 3 3, 9 9, 5 5, 2 2 )"); } - // see https://sourceforge.net/p/jts-topo-suite/bugs/33/ - public void testPolygonHasValidResult() + public void testLineDuplicatePointsRemoved() throws Exception { - checkReduce(10, -"POLYGON ((563539.9999829987 6395531.999987871, 558495.0908829987 6395531.999987871, 558495.0914829987 6395533.599987871, 558495.1972829987 6395837.757887871, 558496.0278829987 6395838.339487871, 558497.2604829987 6395839.572087871, 558498.2602829987 6395841.000087871, 558498.9968829987 6395842.579887872, 558499.4480829986 6395844.263487872, 558499.5996829987 6395845.920087871, 558499.9998829986 6395895.974887871, 558499.5998829987 6396031.229687871, 558499.4480829986 6396032.936487871, 558498.9968829987 6396034.620287871, 558498.2602829987 6396036.200087871, 558497.2604829987 6396037.627887871, 558496.0278829987 6396038.860487871, 558495.1938829987 6396039.444487872, 558494.8000829987 6396563.233287871, 558494.3998829987 6396631.258887871, 558494.2480829987 6396632.936487871, 558493.7968829987 6396634.620287871, 558493.0602829987 6396636.200087871, 558492.0604829987 6396637.627887871, 558490.8278829987 6396638.860487871, 558490.3932829987 6396639.164887872, 558490.0000829987 6397089.618487871, 558489.5998829987 6397231.228287871, 558489.4480829986 6397232.936487871, 558488.9968829987 6397234.620287871, 558488.2602829987 6397236.200087871, 558487.2604829987 6397237.627887871, 558486.0278829987 6397238.860487871, 558485.1890829987 6397239.447887871, 558484.4000829987 6397831.213287871, 558484.2480829987 6397832.936487871, 558483.7968829987 6397834.620287871, 558483.0602829987 6397836.200087871, 558482.0604829987 6397837.627887871, 558480.8278829987 6397838.860487871, 558479.9948829986 6397839.443887872, 558479.6000829986 6398431.206687871, 558479.4480829986 6398432.936487871, 558478.9968829987 6398434.620287871, 558478.2602829987 6398436.200087871, 558477.2604829987 6398437.627887871, 558476.0278829987 6398438.860487871, 558475.1890829987 6398439.447887871, 558474.4000829987 6399031.213287871, 558474.2480829987 6399032.936487871, 558473.7968829987 6399034.620287871, 558473.0602829987 6399036.200087871, 558472.0604829987 6399037.627887871, 558470.8278829987 6399038.860487871, 558470.3894829986 6399039.167487871, 558469.6000829986 6399631.213287871, 558469.4480829986 6399632.936487871, 558468.9968829987 6399634.620287871, 558468.2602829987 6399636.200087871, 558467.2604829987 6399637.627887871, 558466.0278829987 6399638.860487871, 558465.1890829987 6399639.447887871, 558464.4000829987 6400231.213287871, 558464.2480829987 6400232.936487871, 558463.7968829987 6400234.620287871, 558463.0602829987 6400236.200087871, 558462.0604829987 6400237.627887871, 558460.8278829987 6400238.860487871, 558459.9948829986 6400239.443887872, 558459.6000829986 6400831.206687871, 558459.4480829986 6400832.936487871, 558458.9968829987 6400834.620287871, 558458.2602829987 6400836.200087871, 558457.2604829987 6400837.627887871, 558456.0278829987 6400838.860487871, 558455.1940829987 6400839.444487872, 558454.7998829987 6401378.041287871, 558454.3996829987 6401431.275087872, 558454.2480829987 6401432.936487871, 558453.7968829987 6401434.620287871, 558453.0602829987 6401436.200087871, 558452.0604829987 6401437.627887871, 558450.8278829987 6401438.860487871, 558450.3936829987 6401439.164687871, 558450.0000829987 6401919.622087872, 558449.5998829987 6402031.235887871, 558449.4480829986 6402032.936487871, 558448.9968829987 6402034.620287871, 558448.2602829987 6402036.200087871, 558447.2604829987 6402037.627887871, 558446.0278829987 6402038.860487871, 558445.1890829987 6402039.447887871, 558444.4000829987 6402631.213287871, 558444.2480829987 6402632.936487871, 558443.7968829987 6402634.620287871, 558443.0602829987 6402636.200087871, 558442.0604829987 6402637.627887871, 558440.8278829987 6402638.860487871, 558439.9948829986 6402639.443887872, 558439.6000829986 6403231.206687871, 558439.4480829986 6403232.936487871, 558438.9968829987 6403234.620287871, 558438.2602829987 6403236.200087871, 558437.2604829987 6403237.627887871, 558436.0278829987 6403238.860487871, 558435.1890829987 6403239.447887871, 558434.4000829987 6403831.213287871, 558434.2480829987 6403832.936487871, 558433.7968829987 6403834.620287871, 558433.0602829987 6403836.200087871, 558432.0604829987 6403837.627887871, 558430.8278829987 6403838.860487871, 558430.3894829986 6403839.167487871, 558429.6000829986 6404431.213287871, 558429.4480829986 6404432.936487871, 558428.9968829987 6404434.620287871, 558428.2602829987 6404436.200087871, 558427.2604829987 6404437.627887871, 558426.0278829987 6404438.860487871, 558425.1890829987 6404439.447887871, 558424.4000829987 6405031.213287871, 558424.2480829987 6405032.936487871, 558423.7968829987 6405034.620287871, 558423.0602829987 6405036.200087871, 558422.0604829987 6405037.627887871, 558420.8278829987 6405038.860487871, 558419.9948829986 6405039.443887872, 558419.6000829986 6405631.206687871, 558419.4480829986 6405632.936487871, 558418.9968829987 6405634.620287871, 558418.2602829987 6405636.200087871, 558417.2604829987 6405637.627887871, 558416.0278829987 6405638.860487871, 558415.1940829987 6405639.444487872, 558414.7998829987 6406180.843287871, 558414.3996829987 6406231.279487872, 558414.2480829987 6406232.936487871, 558413.7968829987 6406234.620287871, 558413.0602829987 6406236.200087871, 558412.0604829987 6406237.627887871, 558410.8278829987 6406238.860487871, 558410.3936829987 6406239.164687871, 558410.0000829987 6406725.222887871, 558409.5998829987 6406831.237687871, 558409.4480829986 6406832.936487871, 558408.9968829987 6406834.620287871, 558408.2602829987 6406836.200087871, 558407.2604829987 6406837.627887871, 558406.0278829987 6406838.860487871, 558405.1890829987 6406839.447887871, 558404.4000829987 6407431.213287871, 558404.2480829987 6407432.936487871, 558403.7968829987 6407434.620287871, 558403.0602829987 6407436.200087871, 558402.0604829987 6407437.627887871, 558400.8278829987 6407438.860487871, 558400.3894829986 6407439.167487871, 558399.6000829986 6408031.213287871, 558399.4480829986 6408032.936487871, 558398.9968829987 6408034.620287871, 558398.2602829987 6408036.200087871, 558397.2604829987 6408037.627887871, 558396.0278829987 6408038.860487871, 558394.7946829987 6408039.724087872, 558394.4000829987 6408631.206687871, 558394.2480829987 6408632.936487871, 558393.7968829987 6408634.620287871, 558393.0602829987 6408636.200087871, 558392.0799829987 6408637.599987871, 558392.0604829987 6408637.627887871, 558390.8278829987 6408638.860487871, 558390.3894829986 6408639.167487871, 558390.3894829986 6408639.199987872, 563539.9999829987 6408639.199987872, 563541.5999829987 6408639.199987872, 563541.5999829987 6408637.599987871, 563541.5999829987 6395533.599987871, 563541.5999829987 6395531.999987871, 563539.9999829987 6395531.999987871))", -"POLYGON ((558495.1 6395532, 558495.1 6395533.6, 558495.2 6395837.8, 558496 6395838.3, 558497.3 6395839.6, 558498.3 6395841, 558499 6395842.6, 558499.4 6395844.3, 558499.6 6395845.9, 558500 6395896, 558499.6 6396031.2, 558499.4 6396032.9, 558499 6396034.6, 558498.3 6396036.2, 558497.3 6396037.6, 558496 6396038.9, 558495.2 6396039.4, 558494.8 6396563.2, 558494.4 6396631.3, 558494.2 6396632.9, 558493.8 6396634.6, 558493.1 6396636.2, 558492.1 6396637.6, 558490.8 6396638.9, 558490.4 6396639.2, 558490 6397089.6, 558489.6 6397231.2, 558489.4 6397232.9, 558489 6397234.6, 558488.3 6397236.2, 558487.3 6397237.6, 558486 6397238.9, 558485.2 6397239.4, 558484.4 6397831.2, 558484.2 6397832.9, 558483.8 6397834.6, 558483.1 6397836.2, 558482.1 6397837.6, 558480.8 6397838.9, 558480 6397839.4, 558479.6 6398431.2, 558479.4 6398432.9, 558479 6398434.6, 558478.3 6398436.2, 558477.3 6398437.6, 558476 6398438.9, 558475.2 6398439.4, 558474.4 6399031.2, 558474.2 6399032.9, 558473.8 6399034.6, 558473.1 6399036.2, 558472.1 6399037.6, 558470.8 6399038.9, 558470.4 6399039.2, 558469.6 6399631.2, 558469.4 6399632.9, 558469 6399634.6, 558468.3 6399636.2, 558467.3 6399637.6, 558466 6399638.9, 558465.2 6399639.4, 558464.4 6400231.2, 558464.2 6400232.9, 558463.8 6400234.6, 558463.1 6400236.2, 558462.1 6400237.6, 558460.8 6400238.9, 558460 6400239.4, 558459.6 6400831.2, 558459.4 6400832.9, 558459 6400834.6, 558458.3 6400836.2, 558457.3 6400837.6, 558456 6400838.9, 558455.2 6400839.4, 558454.8 6401378, 558454.4 6401431.3, 558454.2 6401432.9, 558453.8 6401434.6, 558453.1 6401436.2, 558452.1 6401437.6, 558450.8 6401438.9, 558450.4 6401439.2, 558450 6401919.6, 558449.6 6402031.2, 558449.4 6402032.9, 558449 6402034.6, 558448.3 6402036.2, 558447.3 6402037.6, 558446 6402038.9, 558445.2 6402039.4, 558444.4 6402631.2, 558444.2 6402632.9, 558443.8 6402634.6, 558443.1 6402636.2, 558442.1 6402637.6, 558440.8 6402638.9, 558440 6402639.4, 558439.6 6403231.2, 558439.4 6403232.9, 558439 6403234.6, 558438.3 6403236.2, 558437.3 6403237.6, 558436 6403238.9, 558435.2 6403239.4, 558434.4 6403831.2, 558434.2 6403832.9, 558433.8 6403834.6, 558433.1 6403836.2, 558432.1 6403837.6, 558430.8 6403838.9, 558430.4 6403839.2, 558429.6 6404431.2, 558429.4 6404432.9, 558429 6404434.6, 558428.3 6404436.2, 558427.3 6404437.6, 558426 6404438.9, 558425.2 6404439.4, 558424.4 6405031.2, 558424.2 6405032.9, 558423.8 6405034.6, 558423.1 6405036.2, 558422.1 6405037.6, 558420.8 6405038.9, 558420 6405039.4, 558419.6 6405631.2, 558419.4 6405632.9, 558419 6405634.6, 558418.3 6405636.2, 558417.3 6405637.6, 558416 6405638.9, 558415.2 6405639.4, 558414.8 6406180.8, 558414.4 6406231.3, 558414.2 6406232.9, 558413.8 6406234.6, 558413.1 6406236.2, 558412.1 6406237.6, 558410.8 6406238.9, 558410.4 6406239.2, 558410 6406725.2, 558409.6 6406831.2, 558409.4 6406832.9, 558409 6406834.6, 558408.3 6406836.2, 558407.3 6406837.6, 558406 6406838.9, 558405.2 6406839.4, 558404.4 6407431.2, 558404.2 6407432.9, 558403.8 6407434.6, 558403.1 6407436.2, 558402.1 6407437.6, 558400.8 6407438.9, 558400.4 6407439.2, 558399.6 6408031.2, 558399.4 6408032.9, 558399 6408034.6, 558398.3 6408036.2, 558397.3 6408037.6, 558396 6408038.9, 558394.8 6408039.7, 558394.4 6408631.2, 558394.2 6408632.9, 558393.8 6408634.6, 558393.1 6408636.2, 558392.1 6408637.6, 558390.8 6408638.9, 558390.4 6408639.2, 563540 6408639.2, 563541.6 6408639.2, 563541.6 6408637.6, 563541.6 6395533.6, 563541.6 6395532, 563540 6395532, 558495.1 6395532))"); + checkReduce( + "LINESTRING( 1 1, 3 3, 3.1 3, 4 4, 4 4, 9 9 )", + "LINESTRING (1 1, 3 3, 4 4, 9 9)"); } - - public void testLine() + + public void testLineRemoveCollapse() throws Exception { - checkReduce("LINESTRING ( 0 0, 0 1.4 )", - "LINESTRING (0 0, 0 1)"); + checkReduce( + "LINESTRING ( 0 0, 0 .4 )", + "LINESTRING EMPTY"); } - - public void testLineNotNoded() + + public void testMultiLineRemoveCollapse() throws Exception { - checkReduce("LINESTRING(1 1, 3 3, 9 9, 5.1 5, 2.1 2)", - "LINESTRING(1 1, 3 3, 9 9, 5 5, 2 2)"); + checkReduce( + "MULTILINESTRING ((0 0, 9 9), ( 0 0, 0 .4 ))", + "LINESTRING (0 0, 9 9)"); } - - public void testLineRemoveCollapse() + + //-------------------------------------------------------------- + + public void testSquare() throws Exception { - checkReduce("LINESTRING ( 0 0, 0 .4 )", - "LINESTRING EMPTY"); + checkReduce( + "POLYGON (( 0 0, 0 1.4, 1.4 1.4, 1.4 0, 0 0 ))", + "POLYGON (( 0 0, 0 1, 1 1, 1 0, 0 0 ))"); } - - public void testLineKeepCollapse() + public void testTinySquareCollapse() throws Exception { - checkReduceKeepCollapse(1, - "LINESTRING ( 0 0, 0 .4 )", - "LINESTRING ( 0 0, 0 0 )"); + checkReduce("POLYGON (( 0 0, 0 .4, .4 .4, .4 0, 0 0 ))", + "POLYGON EMPTY"); } - - public void testLinearRingKeepCollapse() + + public void testSquareCollapse() throws Exception { - checkReduceKeepCollapse(1, - "LINEARRING ( 0 0, 0 .4, .4 0, 0 0 )", - "LINEARRING ( 0 0, 0 0, 0 0 )"); + checkReduce("POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", + "POLYGON EMPTY"); } - - public void testPoint() + + // see https://github.com/locationtech/jts/issues/324 + public void testPolygonCollapsesCompletely() throws Exception { - checkReduce("POINT(1.1 4.9)", - "POINT(1 5)"); + checkReduce(1000000, "POLYGON ((-121.816901763 37.3285521, -121.817392418 37.328343575, -121.817876714 37.328137752, -121.818253966 37.327977421, -121.8184719 37.3278848, -121.816901763 37.3285521))", + "POLYGON EMPTY"); } - - public void testMultiPoint() + + // see https://sourceforge.net/p/jts-topo-suite/bugs/33/ + public void testPolygonHasValidResult() throws Exception { - checkReduce("MULTIPOINT( (1.1 4.9),(1.2 4.8), (3.3 6.6))", - "MULTIPOINT((1 5), (1 5), (3 7))"); + checkReduce(10, +"POLYGON ((563539.9999829987 6395531.999987871, 558495.0908829987 6395531.999987871, 558495.0914829987 6395533.599987871, 558495.1972829987 6395837.757887871, 558496.0278829987 6395838.339487871, 558497.2604829987 6395839.572087871, 558498.2602829987 6395841.000087871, 558498.9968829987 6395842.579887872, 558499.4480829986 6395844.263487872, 558499.5996829987 6395845.920087871, 558499.9998829986 6395895.974887871, 558499.5998829987 6396031.229687871, 558499.4480829986 6396032.936487871, 558498.9968829987 6396034.620287871, 558498.2602829987 6396036.200087871, 558497.2604829987 6396037.627887871, 558496.0278829987 6396038.860487871, 558495.1938829987 6396039.444487872, 558494.8000829987 6396563.233287871, 558494.3998829987 6396631.258887871, 558494.2480829987 6396632.936487871, 558493.7968829987 6396634.620287871, 558493.0602829987 6396636.200087871, 558492.0604829987 6396637.627887871, 558490.8278829987 6396638.860487871, 558490.3932829987 6396639.164887872, 558490.0000829987 6397089.618487871, 558489.5998829987 6397231.228287871, 558489.4480829986 6397232.936487871, 558488.9968829987 6397234.620287871, 558488.2602829987 6397236.200087871, 558487.2604829987 6397237.627887871, 558486.0278829987 6397238.860487871, 558485.1890829987 6397239.447887871, 558484.4000829987 6397831.213287871, 558484.2480829987 6397832.936487871, 558483.7968829987 6397834.620287871, 558483.0602829987 6397836.200087871, 558482.0604829987 6397837.627887871, 558480.8278829987 6397838.860487871, 558479.9948829986 6397839.443887872, 558479.6000829986 6398431.206687871, 558479.4480829986 6398432.936487871, 558478.9968829987 6398434.620287871, 558478.2602829987 6398436.200087871, 558477.2604829987 6398437.627887871, 558476.0278829987 6398438.860487871, 558475.1890829987 6398439.447887871, 558474.4000829987 6399031.213287871, 558474.2480829987 6399032.936487871, 558473.7968829987 6399034.620287871, 558473.0602829987 6399036.200087871, 558472.0604829987 6399037.627887871, 558470.8278829987 6399038.860487871, 558470.3894829986 6399039.167487871, 558469.6000829986 6399631.213287871, 558469.4480829986 6399632.936487871, 558468.9968829987 6399634.620287871, 558468.2602829987 6399636.200087871, 558467.2604829987 6399637.627887871, 558466.0278829987 6399638.860487871, 558465.1890829987 6399639.447887871, 558464.4000829987 6400231.213287871, 558464.2480829987 6400232.936487871, 558463.7968829987 6400234.620287871, 558463.0602829987 6400236.200087871, 558462.0604829987 6400237.627887871, 558460.8278829987 6400238.860487871, 558459.9948829986 6400239.443887872, 558459.6000829986 6400831.206687871, 558459.4480829986 6400832.936487871, 558458.9968829987 6400834.620287871, 558458.2602829987 6400836.200087871, 558457.2604829987 6400837.627887871, 558456.0278829987 6400838.860487871, 558455.1940829987 6400839.444487872, 558454.7998829987 6401378.041287871, 558454.3996829987 6401431.275087872, 558454.2480829987 6401432.936487871, 558453.7968829987 6401434.620287871, 558453.0602829987 6401436.200087871, 558452.0604829987 6401437.627887871, 558450.8278829987 6401438.860487871, 558450.3936829987 6401439.164687871, 558450.0000829987 6401919.622087872, 558449.5998829987 6402031.235887871, 558449.4480829986 6402032.936487871, 558448.9968829987 6402034.620287871, 558448.2602829987 6402036.200087871, 558447.2604829987 6402037.627887871, 558446.0278829987 6402038.860487871, 558445.1890829987 6402039.447887871, 558444.4000829987 6402631.213287871, 558444.2480829987 6402632.936487871, 558443.7968829987 6402634.620287871, 558443.0602829987 6402636.200087871, 558442.0604829987 6402637.627887871, 558440.8278829987 6402638.860487871, 558439.9948829986 6402639.443887872, 558439.6000829986 6403231.206687871, 558439.4480829986 6403232.936487871, 558438.9968829987 6403234.620287871, 558438.2602829987 6403236.200087871, 558437.2604829987 6403237.627887871, 558436.0278829987 6403238.860487871, 558435.1890829987 6403239.447887871, 558434.4000829987 6403831.213287871, 558434.2480829987 6403832.936487871, 558433.7968829987 6403834.620287871, 558433.0602829987 6403836.200087871, 558432.0604829987 6403837.627887871, 558430.8278829987 6403838.860487871, 558430.3894829986 6403839.167487871, 558429.6000829986 6404431.213287871, 558429.4480829986 6404432.936487871, 558428.9968829987 6404434.620287871, 558428.2602829987 6404436.200087871, 558427.2604829987 6404437.627887871, 558426.0278829987 6404438.860487871, 558425.1890829987 6404439.447887871, 558424.4000829987 6405031.213287871, 558424.2480829987 6405032.936487871, 558423.7968829987 6405034.620287871, 558423.0602829987 6405036.200087871, 558422.0604829987 6405037.627887871, 558420.8278829987 6405038.860487871, 558419.9948829986 6405039.443887872, 558419.6000829986 6405631.206687871, 558419.4480829986 6405632.936487871, 558418.9968829987 6405634.620287871, 558418.2602829987 6405636.200087871, 558417.2604829987 6405637.627887871, 558416.0278829987 6405638.860487871, 558415.1940829987 6405639.444487872, 558414.7998829987 6406180.843287871, 558414.3996829987 6406231.279487872, 558414.2480829987 6406232.936487871, 558413.7968829987 6406234.620287871, 558413.0602829987 6406236.200087871, 558412.0604829987 6406237.627887871, 558410.8278829987 6406238.860487871, 558410.3936829987 6406239.164687871, 558410.0000829987 6406725.222887871, 558409.5998829987 6406831.237687871, 558409.4480829986 6406832.936487871, 558408.9968829987 6406834.620287871, 558408.2602829987 6406836.200087871, 558407.2604829987 6406837.627887871, 558406.0278829987 6406838.860487871, 558405.1890829987 6406839.447887871, 558404.4000829987 6407431.213287871, 558404.2480829987 6407432.936487871, 558403.7968829987 6407434.620287871, 558403.0602829987 6407436.200087871, 558402.0604829987 6407437.627887871, 558400.8278829987 6407438.860487871, 558400.3894829986 6407439.167487871, 558399.6000829986 6408031.213287871, 558399.4480829986 6408032.936487871, 558398.9968829987 6408034.620287871, 558398.2602829987 6408036.200087871, 558397.2604829987 6408037.627887871, 558396.0278829987 6408038.860487871, 558394.7946829987 6408039.724087872, 558394.4000829987 6408631.206687871, 558394.2480829987 6408632.936487871, 558393.7968829987 6408634.620287871, 558393.0602829987 6408636.200087871, 558392.0799829987 6408637.599987871, 558392.0604829987 6408637.627887871, 558390.8278829987 6408638.860487871, 558390.3894829986 6408639.167487871, 558390.3894829986 6408639.199987872, 563539.9999829987 6408639.199987872, 563541.5999829987 6408639.199987872, 563541.5999829987 6408637.599987871, 563541.5999829987 6395533.599987871, 563541.5999829987 6395531.999987871, 563539.9999829987 6395531.999987871))", +"POLYGON ((558495.1 6395532, 558495.1 6395533.6, 558495.2 6395837.8, 558496 6395838.3, 558497.3 6395839.6, 558498.3 6395841, 558499 6395842.6, 558499.4 6395844.3, 558499.6 6395845.9, 558500 6395896, 558499.6 6396031.2, 558499.4 6396032.9, 558499 6396034.6, 558498.3 6396036.2, 558497.3 6396037.6, 558496 6396038.9, 558495.2 6396039.4, 558494.8 6396563.2, 558494.4 6396631.3, 558494.2 6396632.9, 558493.8 6396634.6, 558493.1 6396636.2, 558492.1 6396637.6, 558490.8 6396638.9, 558490.4 6396639.2, 558490 6397089.6, 558489.6 6397231.2, 558489.4 6397232.9, 558489 6397234.6, 558488.3 6397236.2, 558487.3 6397237.6, 558486 6397238.9, 558485.2 6397239.4, 558484.4 6397831.2, 558484.2 6397832.9, 558483.8 6397834.6, 558483.1 6397836.2, 558482.1 6397837.6, 558480.8 6397838.9, 558480 6397839.4, 558479.6 6398431.2, 558479.4 6398432.9, 558479 6398434.6, 558478.3 6398436.2, 558477.3 6398437.6, 558476 6398438.9, 558475.2 6398439.4, 558474.4 6399031.2, 558474.2 6399032.9, 558473.8 6399034.6, 558473.1 6399036.2, 558472.1 6399037.6, 558470.8 6399038.9, 558470.4 6399039.2, 558469.6 6399631.2, 558469.4 6399632.9, 558469 6399634.6, 558468.3 6399636.2, 558467.3 6399637.6, 558466 6399638.9, 558465.2 6399639.4, 558464.4 6400231.2, 558464.2 6400232.9, 558463.8 6400234.6, 558463.1 6400236.2, 558462.1 6400237.6, 558460.8 6400238.9, 558460 6400239.4, 558459.6 6400831.2, 558459.4 6400832.9, 558459 6400834.6, 558458.3 6400836.2, 558457.3 6400837.6, 558456 6400838.9, 558455.2 6400839.4, 558454.8 6401378, 558454.4 6401431.3, 558454.2 6401432.9, 558453.8 6401434.6, 558453.1 6401436.2, 558452.1 6401437.6, 558450.8 6401438.9, 558450.4 6401439.2, 558450 6401919.6, 558449.6 6402031.2, 558449.4 6402032.9, 558449 6402034.6, 558448.3 6402036.2, 558447.3 6402037.6, 558446 6402038.9, 558445.2 6402039.4, 558444.4 6402631.2, 558444.2 6402632.9, 558443.8 6402634.6, 558443.1 6402636.2, 558442.1 6402637.6, 558440.8 6402638.9, 558440 6402639.4, 558439.6 6403231.2, 558439.4 6403232.9, 558439 6403234.6, 558438.3 6403236.2, 558437.3 6403237.6, 558436 6403238.9, 558435.2 6403239.4, 558434.4 6403831.2, 558434.2 6403832.9, 558433.8 6403834.6, 558433.1 6403836.2, 558432.1 6403837.6, 558430.8 6403838.9, 558430.4 6403839.2, 558429.6 6404431.2, 558429.4 6404432.9, 558429 6404434.6, 558428.3 6404436.2, 558427.3 6404437.6, 558426 6404438.9, 558425.2 6404439.4, 558424.4 6405031.2, 558424.2 6405032.9, 558423.8 6405034.6, 558423.1 6405036.2, 558422.1 6405037.6, 558420.8 6405038.9, 558420 6405039.4, 558419.6 6405631.2, 558419.4 6405632.9, 558419 6405634.6, 558418.3 6405636.2, 558417.3 6405637.6, 558416 6405638.9, 558415.2 6405639.4, 558414.8 6406180.8, 558414.4 6406231.3, 558414.2 6406232.9, 558413.8 6406234.6, 558413.1 6406236.2, 558412.1 6406237.6, 558410.8 6406238.9, 558410.4 6406239.2, 558410 6406725.2, 558409.6 6406831.2, 558409.4 6406832.9, 558409 6406834.6, 558408.3 6406836.2, 558407.3 6406837.6, 558406 6406838.9, 558405.2 6406839.4, 558404.4 6407431.2, 558404.2 6407432.9, 558403.8 6407434.6, 558403.1 6407436.2, 558402.1 6407437.6, 558400.8 6407438.9, 558400.4 6407439.2, 558399.6 6408031.2, 558399.4 6408032.9, 558399 6408034.6, 558398.3 6408036.2, 558397.3 6408037.6, 558396 6408038.9, 558394.8 6408039.7, 558394.4 6408631.2, 558394.2 6408632.9, 558393.8 6408634.6, 558393.1 6408636.2, 558392.1 6408637.6, 558390.8 6408638.9, 558390.4 6408639.2, 563540 6408639.2, 563541.6 6408639.2, 563541.6 6408637.6, 563541.6 6395533.6, 563541.6 6395532, 563540 6395532, 558495.1 6395532))"); } - + public void testPolgonWithCollapsedLine() throws Exception { checkReduce("POLYGON ((10 10, 100 100, 200 10.1, 300 10, 10 10))", "POLYGON ((10 10, 100 100, 200 10, 10 10))"); @@ -146,7 +145,7 @@ public void testPolgonWithCollapsedPoint() throws Exception { "MULTIPOLYGON (((10 10, 100 100, 200 10, 10 10)), ((200 10, 300 100, 400 10, 200 10)))"); } - public void testMultiPolgonCollapse() throws Exception { + public void testMultiPolgonCollapseRemoved() throws Exception { checkReduce("MULTIPOLYGON (((1 9, 5 9, 5 1, 1 1, 1 9)), ((5.2 8.7, 9 8.7, 9 1, 5.2 1, 5.2 8.7)))", "POLYGON ((1 1, 1 9, 5 9, 9 9, 9 1, 5 1, 1 1))"); } @@ -172,50 +171,68 @@ public void testGCNested() throws Exception { ); } - public void testPolgonWithCollapsedLinePointwise() throws Exception { - checkReducePointwise("POLYGON ((10 10, 100 100, 200 10.1, 300 10, 10 10))", - "POLYGON ((10 10, 100 100, 200 10, 300 10, 10 10))"); - } + //============================================= + + public void testKeepCollapsedLine() + throws Exception + { + checkReduceKeepCollapsed(1, + "LINESTRING ( 0 0, 0 .4 )", + "LINESTRING ( 0 0, 0 0 )"); + } - public void testPolgonWithCollapsedPointPointwise() throws Exception { - checkReducePointwise("POLYGON ((10 10, 100 100, 200 10.1, 300 100, 400 10, 10 10))", - "POLYGON ((10 10, 100 100, 200 10, 300 100, 400 10, 10 10))"); - } + public void testKeepCollapsedLinearRing() + throws Exception + { + checkReduceKeepCollapsed(1, + "LINEARRING ( 0 0, 0 .4, .4 0, 0 0 )", + "LINEARRING ( 0 0, 0 0, 0 0 )"); + } - //======================================= + public void testKeepCollapsedSquare() + throws Exception + { + checkReduceKeepCollapsed(1, "POLYGON (( 0 0, 0 1.4, .4 .4, .4 0, 0 0 ))", + "POLYGON EMPTY"); + } - private void checkReducePointwise(String wkt, String wktExpected) { - Geometry g = read(wkt); - Geometry gExpected = read(wktExpected); - Geometry gReduce = GeometryPrecisionReducer.reducePointwise(g, pmFixed1); - assertEqualsExactAndHasSameFactory(gExpected, gReduce); + public void testKeepCollapsedPolygon() + throws Exception + { + checkReduceKeepCollapsed(1, + "POLYGON ((0.1 0.3, 0.3 0.3, 0.3 0.1, 0.1 0.1, 0.1 0.3))", + "POLYGON EMPTY"); } - private void assertEqualsExactAndHasSameFactory(Geometry expected, Geometry actual) + public void testKeepCollapsedMultiPolygon() + throws Exception { - checkEqual(expected, actual); - assertTrue("Factories are not the same", expected.getFactory() == actual.getFactory()); + checkReduceKeepCollapsed(1, + "MULTIPOLYGON (((0.1 0.3, 0.3 0.3, 0.3 0.1, 0.1 0.1, 0.1 0.3)), ((1 2, 2.1 2, 2 1.1, 1 1, 1 2)))", + "POLYGON ((2 2, 2 1, 1 1, 1 2, 2 2))"); } + + //======================================= - private void checkReduceKeepCollapse( - double scaleFactor, + private void checkReduce( String wkt, String wktExpected) { + checkReduce(1, wkt, wktExpected); + } + + private void checkReduce(double scaleFactor, String wkt, String wktExpected) { PrecisionModel pm = new PrecisionModel(scaleFactor); GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); - reducer.setRemoveCollapsedComponents(false); checkReduce(reducer, wkt, wktExpected); } - private void checkReduce( + private void checkReduceKeepCollapsed( + double scaleFactor, String wkt, String wktExpected) { - checkReduce(1, wkt, wktExpected); - } - - private void checkReduce(double scaleFactor, String wkt, String wktExpected) { PrecisionModel pm = new PrecisionModel(scaleFactor); GeometryPrecisionReducer reducer = new GeometryPrecisionReducer(pm); + reducer.setRemoveCollapsedComponents(false); checkReduce(reducer, wkt, wktExpected); } From 6dfa2e0ce2c7cb5c9fca1bcba4c58b1ced40def2 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 19 Oct 2021 11:47:25 -0700 Subject: [PATCH 110/275] Fix WKTReader typename parsing (#786) Signed-off-by: Martin Davis --- .../org/locationtech/jts/io/WKTReader.java | 56 ++++++++++-------- .../locationtech/jts/io/WKBReaderTest.java | 2 +- .../jts/io/WKTReaderParseErrorTest.java | 59 +++++++++++++++---- 3 files changed, 80 insertions(+), 37 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java index 4291cbc0f7..c80f3b8232 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java @@ -769,22 +769,15 @@ private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOExce String type; EnumSet ordinateFlags = EnumSet.of(Ordinate.X, Ordinate.Y); - try { - type = getNextWord(tokenizer).toUpperCase(Locale.ROOT); - if (type.endsWith(WKTConstants.ZM)) { - ordinateFlags.add(Ordinate.Z); - ordinateFlags.add(Ordinate.M); - } else if (type.endsWith(WKTConstants.Z)) { - ordinateFlags.add(Ordinate.Z); - } else if (type.endsWith(WKTConstants.M)) { - ordinateFlags.add(Ordinate.M); - } - } catch (IOException e) { - return null; - } catch (ParseException e) { - return null; + type = getNextWord(tokenizer).toUpperCase(Locale.ROOT); + if (type.endsWith(WKTConstants.ZM)) { + ordinateFlags.add(Ordinate.Z); + ordinateFlags.add(Ordinate.M); + } else if (type.endsWith(WKTConstants.Z)) { + ordinateFlags.add(Ordinate.Z); + } else if (type.endsWith(WKTConstants.M)) { + ordinateFlags.add(Ordinate.M); } - return readGeometryTaggedText(tokenizer, type, ordinateFlags); } @@ -808,33 +801,50 @@ private Geometry readGeometryTaggedText(StreamTokenizer tokenizer, String type, geometryFactory.getSRID(), csFactoryXYZM); } - if (type.startsWith(WKTConstants.POINT)) { + if (isTypeName(tokenizer, type, WKTConstants.POINT)) { return readPointText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.LINESTRING)) { + else if (isTypeName(tokenizer, type, WKTConstants.LINESTRING)) { return readLineStringText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.LINEARRING)) { + else if (isTypeName(tokenizer, type, WKTConstants.LINEARRING)) { return readLinearRingText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.POLYGON)) { + else if (isTypeName(tokenizer, type, WKTConstants.POLYGON)) { return readPolygonText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.MULTIPOINT)) { + else if (isTypeName(tokenizer, type, WKTConstants.MULTIPOINT)) { return readMultiPointText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.MULTILINESTRING)) { + else if (isTypeName(tokenizer, type, WKTConstants.MULTILINESTRING)) { return readMultiLineStringText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.MULTIPOLYGON)) { + else if (isTypeName(tokenizer, type, WKTConstants.MULTIPOLYGON)) { return readMultiPolygonText(tokenizer, ordinateFlags); } - else if (type.startsWith(WKTConstants.GEOMETRYCOLLECTION)) { + else if (isTypeName(tokenizer, type, WKTConstants.GEOMETRYCOLLECTION)) { return readGeometryCollectionText(tokenizer, ordinateFlags); } throw parseErrorWithLine(tokenizer, "Unknown geometry type: " + type); } + private boolean isTypeName(StreamTokenizer tokenizer, String type, String typeName) throws ParseException { + if (! type.startsWith(typeName)) + return false; + + String modifiers = type.substring(typeName.length()); + boolean isValidMod = modifiers.length() <= 2 && + (modifiers.length() == 0 + ||modifiers.equals(WKTConstants.Z) + || modifiers.equals(WKTConstants.M) + || modifiers.equals(WKTConstants.ZM)); + if (! isValidMod) { + throw parseErrorWithLine(tokenizer, "Invalid dimension modifiers: " + type); + } + + return true; + } + /** * Creates a Point using the next token in the stream. * diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java index 35d94acb6a..f191a80d60 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKBReaderTest.java @@ -128,7 +128,7 @@ public void testSpatialiteWKB_Z() throws ParseException { "POLYGON Z((0 0 100,0 10 100,10 10 100,10 0 100,0 0 100),(1 1 100,1 9 100,9 9 100,9 1 100,1 1 100))"); // MultiPointZ checkWKBGeometry("01040000A0E61000000200000001010000800000000000000000000000000000F03F00000000000000400101000080000000000000084000000000000010400000000000001440", - "MULTIPOINTS Z(0 1 2, 3 4 5)"); + "MULTIPOINT Z(0 1 2, 3 4 5)"); // MultiLineStringZ checkWKBGeometry("01050000A0E6100000020000000102000080020000000000000000000000000000000000F03F000000000000004000000000000008400000000000001040000000000000144001020000800200000000000000000018400000000000001C400000000000002040000000000000224000000000000024400000000000002640", "MULTILINESTRING Z((0 1 2,3 4 5),(6 7 8,9 10 11))"); diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java index c6a1f88885..c31de4d699 100644 --- a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderParseErrorTest.java @@ -39,37 +39,70 @@ public WKTReaderParseErrorTest(String name) super(name); } - public void testExtraLParen() throws IOException, ParseException + public void testExtraLParen() throws IOException { + readWithParseException("POINT (( 1e01 -1E02)"); + } + + public void testMissingOrdinate() throws IOException + { + readWithParseException("POINT ( 1e01 )"); + } + + public void testBadChar() throws IOException + { + readWithParseException("POINT ( # 1e-04 1E-05)"); + } + + public void testBadExpFormat() throws IOException + { + readWithParseException("POINT (1e0a1 1X02)"); + } + + public void testBadExpPlusSign() throws IOException { - readBad("POINT (( 1e01 -1E02)"); + readWithParseException("POINT (1e+01 1X02)"); } - public void testMissingOrdinate() throws IOException, ParseException + public void testBadPlusSign() throws IOException { - readBad("POINT ( 1e01 )"); + readWithParseException("POINT ( +1e+01 1X02)"); } - public void testBadChar() throws IOException, ParseException + public void testBadCharsInType() throws IOException { - readBad("POINT ( # 1e-04 1E-05)"); + readWithParseException("POINTABC ( 0 0 )"); + readWithParseException("LINESTRINGABC ( 0 0 )"); + readWithParseException("LINEARRINGABC ( 0 0, 0 0, 0 0 )"); + readWithParseException("POLYGONABC (( 0 0, 0 0, 0 0, 0 0 ))"); + readWithParseException("MULTIPOINTABC (( 0 0 ), ( 0 0 ))"); + readWithParseException("MULTILINESTRINGABC (( 0 0, 1 1 ), ( 0 0, 1 1 ))"); + readWithParseException("MULTIPOLYGONABC ((( 0 0, 1 1, 2 2, 0 0 )), (( 0 0, 1 1, 2 2, 0 0 )))"); + readWithParseException("GEOMETRYCOLLECTIONABC (POINT( 0 0 ), LINESTRING( 0 0, 1 1))"); } - public void testBadExpFormat() throws IOException, ParseException + public void testBadCharsInTypeZ() throws IOException { - readBad("POINT (1e0a1 1X02)"); + readWithParseException("POINTABCZ ( 0 0 )"); + readWithParseException("LINESTRINGABCZ ( 0 0 )"); + readWithParseException("LINEARRINGABCZ ( 0 0, 0 0, 0 0 )"); + readWithParseException("POLYGONABCZ (( 0 0, 0 0, 0 0, 0 0 ))"); + readWithParseException("MULTIPOINTABCZ (( 0 0 ), ( 0 0 ))"); + readWithParseException("MULTILINESTRINGABCZ (( 0 0, 1 1 ), ( 0 0, 1 1 ))"); + readWithParseException("MULTIPOLYGONABCZ ((( 0 0, 1 1, 2 2, 0 0 )), (( 0 0, 1 1, 2 2, 0 0 )))"); + readWithParseException("GEOMETRYCOLLECTIONABCZ (POINT( 0 0 ), LINESTRING( 0 0, 1 1))"); } - public void testBadExpPlusSign() throws IOException, ParseException + public void testBadCharsInTypeM() throws IOException { - readBad("POINT (1e+01 1X02)"); + readWithParseException("LINESTRINGABCM ( 0 0 0, 1 1 1 )"); } - public void testBadPlusSign() throws IOException, ParseException + public void testBadCharsInTypeZM() throws IOException { - readBad("POINT ( +1e+01 1X02)"); + readWithParseException("LINESTRINGABCZM ( 0 0 0 0, 1 1 1 1 )"); } - private void readBad(String wkt) + private void readWithParseException(String wkt) throws IOException { boolean threwParseEx = false; From 73ff641b6b5e4fc5e2aea500d134bf79be4bfb24 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 19 Oct 2021 11:49:02 -0700 Subject: [PATCH 111/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 0d9c5209d1..f9722f4c02 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -31,6 +31,10 @@ Distributions for older JTS versions can be obtained at the * Add `KdTree` seeding to`SnappingNoder` (#780) * Add `DiscreteFrechetDistance` (#764, #783) +### Bug Fixes + +* Fix `WKTReader` geometry typename parsing (#786) + # Version 1.18.2 *Release Date: 08/27/2021* From 0ce9c5808e2c579064d605d67fd8ad3454e0f57c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 20 Oct 2021 14:24:35 -0700 Subject: [PATCH 112/275] Move TestBuilder DisplayAB checkbox Signed-off-by: Martin Davis --- .../jtstest/testbuilder/AppIcons.java | 2 ++ .../testbuilder/SpatialFunctionPanel.java | 25 ------------------ .../jtstest/testbuilder/TestCasePanel.java | 17 +++++++++++- .../testbuilder/BinaryGeomFunction.png | Bin 917 -> 4246 bytes 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java index cceac4883c..d929efa0e1 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/AppIcons.java @@ -28,6 +28,8 @@ public class AppIcons { public final static ImageIcon GEOM_INSPECT = IconLoader.icon("InspectGeometry.png"); public final static ImageIcon GEOM_EXCHANGE = IconLoader.icon("ExchangeGeoms.png"); + public final static ImageIcon GEOFUNC_BINARY = IconLoader.icon("BinaryGeomFunction.png"); + public final static ImageIcon DOWN = IconLoader.icon("Down.png"); public final static ImageIcon UP = IconLoader.icon("Up.png"); public final static ImageIcon LEFT = IconLoader.icon("Left.png"); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java index 07076071eb..a695be47fb 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java @@ -103,7 +103,6 @@ public class SpatialFunctionPanel private transient Vector spatialFunctionPanelListeners; private JPanel panelControl = new JPanel(); - private JCheckBox displayAAndBCheckBox = new JCheckBox(); private JCheckBox cbExecEachA = new JCheckBox(); private JCheckBox cbExecEachB = new JCheckBox(); private JCheckBox cbExecRepeat = new JCheckBox(); @@ -153,16 +152,6 @@ void uiInit() throws Exception { panelExecMeta.setLayout(flowLayout2); panelExecParam.setLayout(borderLayout2); - - displayAAndBCheckBox.setSelected(true); - displayAAndBCheckBox.setToolTipText(""); - displayAAndBCheckBox.setText("Display Input"); - displayAAndBCheckBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(ActionEvent e) { - displayAAndBCheckBox_actionPerformed(e); - } - }); - lblFunction.setText("Function"); lblFunction.setHorizontalAlignment(SwingConstants.RIGHT); lblFunction.setBorder(LABEL_BORDER);//top,left,bottom,right @@ -221,10 +210,8 @@ public void actionPerformed(ActionEvent e) { panelParam.add(txtMitreLimit); panelControl.setLayout(flowLayout1); - panelControl.add(displayAAndBCheckBox, null); //panelControl.add(btnClearResult, null); - cbExecEachA.setToolTipText("Compute for each A geometry element"); cbExecEachA.setText("Each A"); @@ -371,10 +358,6 @@ void clearExtended() { cbExecEachA.setSelected(false); cbExecEachB.setSelected(false); } - - void displayAAndBCheckBox_actionPerformed(ActionEvent e) { - JTSTestBuilderController.editPanel().setShowingInput(displayAAndBCheckBox.isSelected()); - } private void setCurrentFunction(GeometryFunction func) { currentFunc = func; @@ -444,14 +427,6 @@ private static int numNonGeomParams(GeometryFunction func) public static int attributeParamOffset(GeometryFunction func) { return func.isBinary() ? 1 : 0; } - - public boolean shouldShowGeometryA() { - return displayAAndBCheckBox.isSelected(); - } - - public boolean shouldShowGeometryB() { - return displayAAndBCheckBox.isSelected(); - } public void clearFunction() { setCurrentFunction(null); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestCasePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestCasePanel.java index 7d3c94b913..f85d6d66da 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestCasePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/TestCasePanel.java @@ -34,6 +34,7 @@ import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; +import org.locationtech.jtstest.testbuilder.controller.JTSTestBuilderController; import org.locationtech.jtstest.testbuilder.event.ValidPanelEvent; import org.locationtech.jtstest.testbuilder.event.ValidPanelListener; import org.locationtech.jtstest.testbuilder.model.*; @@ -276,6 +277,18 @@ public void stateChanged(ChangeEvent e) { editGroupPanel.add(editFramePanel, BorderLayout.CENTER); editGroupPanel.add(statusBarPanel, BorderLayout.SOUTH); + JCheckBox cbDisplayAB = new JCheckBox(); + cbDisplayAB.setSelected(true); + cbDisplayAB.setToolTipText("Dislplay A and B"); + //cbDisplayAB.setText("Display Input"); + cbDisplayAB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + JTSTestBuilderController.editPanel().setShowingInput(cbDisplayAB.isSelected()); + } + }); + JLabel lblDisplayAB = new JLabel(); + lblDisplayAB.setIcon(AppIcons.GEOFUNC_BINARY); + cbRevealTopo.setToolTipText("Reveal Topology - visualize topological detail by stretching geometries"); spStretchDist.setToolTipText("Stretch Distance (pixels)"); spStretchDist.setMaximumSize(new Dimension(20,20)); @@ -284,10 +297,12 @@ public void stateChanged(ChangeEvent e) { jPanelReveal.add(Box.createHorizontalGlue()); jPanelReveal.add(cbRevealTopo); jPanelReveal.add(spStretchDist); + jPanelReveal.add(Box.createHorizontalStrut(8)); + jPanelReveal.add(cbDisplayAB); + jPanelReveal.add(lblDisplayAB); jPanelReveal.add(Box.createHorizontalGlue()); jPanelReveal.setBorder(BorderFactory.createLoweredBevelBorder()); - JButton btnSaveImage = SwingUtil.createButton( AppIcons.SAVE_IMAGE, AppStrings.TIP_SAVE_IMAGE, new java.awt.event.ActionListener() { diff --git a/modules/app/src/main/resources/org/locationtech/jtstest/testbuilder/BinaryGeomFunction.png b/modules/app/src/main/resources/org/locationtech/jtstest/testbuilder/BinaryGeomFunction.png index 9f517eb7cdf922cd0f12aa273880273e568b0df7..e18dd7e98a9c9b299b35057a476f6d10c46c78d4 100644 GIT binary patch literal 4246 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 K%Q~loCN2OX;2Ayu From b8fcc14961cfad530b6e27fb873da625b66d12a9 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 20 Oct 2021 17:16:11 -0700 Subject: [PATCH 113/275] Add TestBuilder Live Execute capability Signed-off-by: Martin Davis --- .../testbuilder/JTSTestBuilderFrame.java | 25 ++---- .../testbuilder/ScalarFunctionPanel.java | 3 +- .../testbuilder/SpatialFunctionPanel.java | 80 +++++++++---------- .../controller/JTSTestBuilderController.java | 18 ++++- .../controller/ResultController.java | 74 +++++++++-------- 5 files changed, 105 insertions(+), 95 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java index 49f275ab34..8d20151558 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java @@ -38,9 +38,6 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.util.Assert; -import org.locationtech.jtstest.testbuilder.controller.ResultController; -import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; -import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelListener; import org.locationtech.jtstest.testbuilder.io.XMLTestWriter; import org.locationtech.jtstest.testbuilder.model.DisplayParameters; import org.locationtech.jtstest.testbuilder.model.GeometryEvent; @@ -65,7 +62,6 @@ public class JTSTestBuilderFrame extends JFrame TestBuilderModel tbModel; - private ResultController resultController = new ResultController(this); private JTSTestBuilderMenuBar tbMenuBar = new JTSTestBuilderMenuBar(this); private JTSTestBuilderToolBar tbToolBar = new JTSTestBuilderToolBar(this); //--------------------------------------------- @@ -108,19 +104,6 @@ public JTSTestBuilderFrame() { enableEvents(AWTEvent.WINDOW_EVENT_MASK); setIconImage(AppIcons.APP.getImage()); jbInit(); - - testCasePanel.spatialFunctionPanel.addSpatialFunctionPanelListener( - new SpatialFunctionPanelListener() { - public void functionExecuted(SpatialFunctionPanelEvent e) { - resultController.spatialFunctionPanel_functionExecuted(e); - } - }); - testCasePanel.scalarFunctionPanel.addSpatialFunctionPanelListener( - new SpatialFunctionPanelListener() { - public void functionExecuted(SpatialFunctionPanelEvent e) { - resultController.executeScalarFunction(); - } - }); testCasePanel.cbRevealTopo.addActionListener( new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { @@ -190,6 +173,11 @@ public static GeometryEditPanel getGeometryEditPanel() return instance().getTestCasePanel().getGeometryEditPanel(); } + public static SpatialFunctionPanel getSpatialFunctionPanel() + { + return instance().getTestCasePanel().spatialFunctionPanel; + } + public TestBuilderModel getModel() { return tbModel; @@ -311,8 +299,7 @@ protected void processWindowEvent(WindowEvent e) { } void model_geometryChanged(GeometryEvent e) { - //testCasePanel.relatePanel.clearResults(); - JTSTestBuilder.controller().geometryViewChanged(); + JTSTestBuilder.controller().geometryChanged(); updateWktPanel(); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ScalarFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ScalarFunctionPanel.java index 6aeb2ae278..847b699130 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ScalarFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ScalarFunctionPanel.java @@ -30,6 +30,7 @@ import org.locationtech.jts.util.Stopwatch; import org.locationtech.jtstest.geomfunction.GeometryFunction; import org.locationtech.jtstest.geomfunction.GeometryFunctionRegistry; +import org.locationtech.jtstest.testbuilder.controller.JTSTestBuilderController; import org.locationtech.jtstest.testbuilder.event.GeometryFunctionEvent; import org.locationtech.jtstest.testbuilder.event.GeometryFunctionListener; import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; @@ -137,7 +138,7 @@ public void execFunction(GeometryFunction func, boolean createNew) { currentFunc = func; if (currentFunc == null) return; - fireFunctionExecuted(new SpatialFunctionPanelEvent(this)); + JTSTestBuilderController.resultController().executeScalarFunction(); } private void functionChanged(GeometryFunction func) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java index a695be47fb..8179b91eea 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java @@ -48,6 +48,7 @@ import org.locationtech.jtstest.testbuilder.event.GeometryFunctionListener; import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelListener; +import org.locationtech.jtstest.testbuilder.model.GeometryEvent; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; import org.locationtech.jtstest.util.ClassUtil; @@ -77,7 +78,7 @@ public class SpatialFunctionPanel - JPanel panelExecControl = new JPanel(); + // GeometryFunctionListPanel geomFuncPanel = new GeometryFunctionListPanel(); GeometryFunctionTreePanel geomFuncPanel = new GeometryFunctionTreePanel(); GridLayout gridLayout1 = new GridLayout(); @@ -91,9 +92,6 @@ public class SpatialFunctionPanel JPanel panelExec = new JPanel(); JPanel panelExecMeta = new JPanel(); JPanel panelExecParam = new JPanel(); - FlowLayout flowLayout = new FlowLayout(); - FlowLayout flowLayout1 = new FlowLayout(); - FlowLayout flowLayout2 = new FlowLayout(); private JButton execButton = new JButton(); private JButton execToNewButton = new JButton(); @@ -102,13 +100,14 @@ public class SpatialFunctionPanel private final ImageIcon expandDownIcon = new ImageIcon(this.getClass().getResource("Expand-Down.png")); private transient Vector spatialFunctionPanelListeners; - private JPanel panelControl = new JPanel(); private JCheckBox cbExecEachA = new JCheckBox(); private JCheckBox cbExecEachB = new JCheckBox(); private JCheckBox cbExecRepeat = new JCheckBox(); private final JTextField txtRepeatCount = new JTextField(); private JButton btnClearResult = new JButton(); - + private JButton btnExecEach; + private JCheckBox cbExecAuto = new JCheckBox(); + private JLabel lblFunctionName = new JLabel(); private JLabel lblFunction = new JLabel(); private JLabel lblDistance = new JLabel(); @@ -127,8 +126,6 @@ public class SpatialFunctionPanel private GeometryFunction currentFunc = null; private Stopwatch timer; - - private JButton btnExecEach; public SpatialFunctionPanel() { try { @@ -148,8 +145,7 @@ void uiInit() throws Exception { panelParam.setLayout(gridLayout2); gridLayout2.setRows(6); gridLayout2.setColumns(2); - panelExec.setLayout(flowLayout); - panelExecMeta.setLayout(flowLayout2); + panelExec.setLayout(new FlowLayout()); panelExecParam.setLayout(borderLayout2); lblFunction.setText("Function"); @@ -182,19 +178,7 @@ void uiInit() throws Exception { txtMitreLimit.setHorizontalAlignment(SwingConstants.RIGHT); initLabels(paramLabel); - - - - btnClearResult.setToolTipText(""); - btnClearResult.setMargin(new Insets(0, 10, 0, 10)); - btnClearResult.setSelected(true); - btnClearResult.setText("Clear Result"); - btnClearResult.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(ActionEvent e) { - clearResultButton_actionPerformed(e); - } - }); panelParam.add(lblFunction); panelParam.add(lblFunctionName); @@ -209,9 +193,6 @@ public void actionPerformed(ActionEvent e) { panelParam.add(lblMitreLimit); panelParam.add(txtMitreLimit); - panelControl.setLayout(flowLayout1); - //panelControl.add(btnClearResult, null); - cbExecEachA.setToolTipText("Compute for each A geometry element"); cbExecEachA.setText("Each A"); @@ -221,6 +202,9 @@ public void actionPerformed(ActionEvent e) { cbExecRepeat.setToolTipText("Repeat function a number of times, incrementing the first parameter"); cbExecRepeat.setText("Repeat"); + cbExecAuto.setToolTipText("Execute function when geometry changes"); + cbExecAuto.setText("Live Exec"); + txtRepeatCount.setMaximumSize(new Dimension(25, 2147483647)); txtRepeatCount.setMinimumSize(new Dimension(30, 21)); txtRepeatCount.setPreferredSize(new Dimension(30, 21)); @@ -230,7 +214,7 @@ public void actionPerformed(ActionEvent e) { execButton = SwingUtil.createButton(AppIcons.EXECUTE, AppStrings.TIP_EXECUTE, new ActionListener() { public void actionPerformed(ActionEvent e) { - execButton_actionPerformed(e); + execFunction(false); } }); execButton.setEnabled(false); @@ -238,7 +222,7 @@ public void actionPerformed(ActionEvent e) { execToNewButton = SwingUtil.createButton("New", AppIcons.EXECUTE, "Compute function result to a new case", new ActionListener() { public void actionPerformed(ActionEvent e) { - execToNewButton_actionPerformed(e); + execFunction(true); } }); execToNewButton.setEnabled(false); @@ -265,16 +249,26 @@ public void actionPerformed(ActionEvent e) { panelExecHolder.add(panelExec, BorderLayout.CENTER); panelExecHolder.add(btnShowExecExt, BorderLayout.EAST); - panelExecMeta.add(cbExecEachA); - panelExecMeta.add(cbExecEachB); - panelExecMeta.add(cbExecRepeat); - panelExecMeta.add(txtRepeatCount); + JPanel panelExecMeta1 = new JPanel(); + panelExecMeta1.setLayout(new FlowLayout()); + panelExecMeta1.add(cbExecEachA); + panelExecMeta1.add(cbExecEachB); + panelExecMeta1.add(cbExecRepeat); + panelExecMeta1.add(txtRepeatCount); + + JPanel panelExecMeta2 = new JPanel(); + panelExecMeta2.setLayout(new FlowLayout()); + panelExecMeta2.add(cbExecAuto); + + panelExecMeta.setLayout(new BoxLayout(panelExecMeta, BoxLayout.Y_AXIS)); + panelExecMeta.add(panelExecMeta1); + panelExecMeta.add(panelExecMeta2); panelExecMeta.setVisible(false); + JPanel panelExecControl = new JPanel(); panelExecControl.setLayout(new BoxLayout(panelExecControl, BoxLayout.Y_AXIS)); panelExecControl.add(panelExecHolder); panelExecControl.add(panelExecMeta); - panelExecControl.add(panelControl); panelExecParam.add(panelParam, BorderLayout.CENTER); panelExecParam.add(panelExecControl, BorderLayout.SOUTH); @@ -314,14 +308,6 @@ void clearResultButton_actionPerformed(ActionEvent e) { clearFunction(); } - void execButton_actionPerformed(ActionEvent e) { - execFunction(getMetaFunction(), false); - } - - void execToNewButton_actionPerformed(ActionEvent e) { - execFunction(getMetaFunction(), true); - } - GeometryFunction getMetaFunction() { GeometryFunction funToRun = geomFuncPanel.getFunction(); if (! isMetaFunctionEnabled()) return funToRun; @@ -365,11 +351,15 @@ private void setCurrentFunction(GeometryFunction func) { fireFunctionExecuted(new SpatialFunctionPanelEvent(this)); } + public void execFunction(boolean createNew) { + execFunction(getMetaFunction(), createNew); + } + public void execFunction(GeometryFunction func, boolean createNew) { currentFunc = func; if (currentFunc == null) return; - fireFunctionExecuted(new SpatialFunctionPanelEvent(this, createNew)); + JTSTestBuilderController.resultController().execute(createNew); } private void functionChanged(GeometryFunction func) @@ -382,6 +372,7 @@ private void functionChanged(GeometryFunction func) execButton.setEnabled(true); execToNewButton.setEnabled(true); + cbExecAuto.setSelected(false); } static void updateParameters(GeometryFunction func, JComponent[] paramComp, JLabel[] paramLabel) { @@ -468,7 +459,12 @@ public static String valOrDefault(String s, String defaultVal) { public boolean isFunctionSelected() { - return currentFunc != null; + return currentFunc != null; + } + + public boolean isAutoExecute() + { + return cbExecAuto.isSelected(); } public GeometryFunction getFunction() { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java index 306e180489..6b0330b750 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java @@ -24,6 +24,7 @@ import org.locationtech.jtstest.testbuilder.JTSTestBuilder; import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; import org.locationtech.jtstest.testbuilder.JTSTestBuilderToolBar; +import org.locationtech.jtstest.testbuilder.SpatialFunctionPanel; import org.locationtech.jtstest.testbuilder.model.GeometryEditModel; import org.locationtech.jtstest.testbuilder.model.LayerList; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; @@ -45,6 +46,8 @@ public class JTSTestBuilderController { + private static ResultController resultController = new ResultController(); + /* private static boolean autoZoomOnNextChange = false; @@ -57,13 +60,18 @@ public static void requestAutoZoom() public JTSTestBuilderController() { } - + public static ResultController resultController() { + return resultController; + } public static TestBuilderModel model() { return frame().getModel(); } public static GeometryEditPanel editPanel() { return JTSTestBuilderFrame.getGeometryEditPanel(); } + public static SpatialFunctionPanel spatialFunctionPanel() { + return JTSTestBuilderFrame.getSpatialFunctionPanel(); + } public static JTSTestBuilderToolBar toolbar() { return frame().getToolbar(); @@ -81,6 +89,14 @@ public void reportException(Exception e) { SwingUtil.reportException(frame(), e); } + public void geometryChanged() + { + if (spatialFunctionPanel().isAutoExecute()) { + resultController.execute(false); + } + geometryViewChanged(); + } + public void geometryViewChanged() { editPanel().updateView(); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java index 420f856906..d28ba518d8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/ResultController.java @@ -25,9 +25,9 @@ import org.locationtech.jtstest.testbuilder.FunctionPanel; import org.locationtech.jtstest.testbuilder.JTSTestBuilder; import org.locationtech.jtstest.testbuilder.JTSTestBuilderFrame; +import org.locationtech.jtstest.testbuilder.ResultWKTPanel; import org.locationtech.jtstest.testbuilder.ScalarFunctionPanel; import org.locationtech.jtstest.testbuilder.SpatialFunctionPanel; -import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; import org.locationtech.jtstest.testbuilder.ui.SwingWorker; @@ -40,48 +40,54 @@ public class ResultController timeFmt.setMinimumFractionDigits(3); } - JTSTestBuilderFrame frame; - TestBuilderModel model = null; - - public ResultController(JTSTestBuilderFrame frame) + public ResultController() { - this.frame = frame; - model = JTSTestBuilder.model(); } - public void spatialFunctionPanel_functionExecuted(SpatialFunctionPanelEvent e) + private static JTSTestBuilderFrame frame() { + return JTSTestBuilderController.frame(); + } + + private static TestBuilderModel model() { + return JTSTestBuilderController.model(); + } + + private ResultWKTPanel resultWKTPanel() { + return frame().getResultWKTPanel(); + } + + public void execute(boolean isCreateNew) { - SpatialFunctionPanel spatialPanel = frame.getTestCasePanel().getSpatialFunctionPanel(); + SpatialFunctionPanel spatialPanel = frame().getTestCasePanel().getSpatialFunctionPanel(); GeometryFunctionInvocation functionDesc = functionInvocation(spatialPanel); - model.setOpName(functionDesc.getSignature()); - frame.getResultWKTPanel().setOpName(model.getOpName()); + model().setOpName(functionDesc.getSignature()); + resultWKTPanel().setOpName(model().getOpName()); // initialize UI view clearResult(); // don't run anything if function is null - if (! frame.getTestCasePanel().getSpatialFunctionPanel().isFunctionSelected()) { + if (! spatialPanel.isFunctionSelected()) { return; } - frame.setCursorWait(); - frame.getTestCasePanel().getSpatialFunctionPanel().enableExecuteControl(false); + frame().setCursorWait(); + spatialPanel.enableExecuteControl(false); startFunctionMonitor(); - boolean isCreateNew = e.isCreateNew(); runFunctionWorker(functionDesc, isCreateNew); // show result unless create new, in which case new case is shown - if (! isCreateNew) frame.showResultWKTTab(); + if (! isCreateNew) frame().showResultWKTTab(); } private GeometryFunctionInvocation functionInvocation(FunctionPanel functionPanel) { GeometryFunctionInvocation functionDesc = new GeometryFunctionInvocation( functionPanel.getFunction(), - model.getGeometryEditModel().getGeometry(0), + model().getGeometryEditModel().getGeometry(0), functionPanel.getFunctionParams()); return functionDesc; } private void clearResult() { - frame.getResultWKTPanel().clearResult(); + resultWKTPanel().clearResult(); // for good measure do a GC System.gc(); updateResult(null,null,null); @@ -96,15 +102,16 @@ private void clearResult() * @param timer */ private void resetUI() { - frame.getTestCasePanel().getSpatialFunctionPanel() + frame().getTestCasePanel().getSpatialFunctionPanel() .enableExecuteControl(true); - frame.setCursorNormal(); - } + frame().setCursorNormal(); + } + private void updateResult(GeometryFunctionInvocation function, Object result, Stopwatch timer) { - model.setResult(result); + model().setResult(result); String timeString = timer != null ? timer.getTimeString() : ""; - frame.getResultWKTPanel().setExecutedTime(timeString); - frame.getResultWKTPanel().setResult(result); + resultWKTPanel().setExecutedTime(timeString); + resultWKTPanel().setResult(result); JTSTestBuilder.controller().geometryViewChanged(); // log it resultLogEntry(function, timeString, result); @@ -142,7 +149,7 @@ private Object computeResult() { try { timer = new Stopwatch(); try { - result = currentFunc.invoke(model.getGeometryEditModel() + result = currentFunc.invoke(model().getGeometryEditModel() .getGeometry(0), functionInvoc.getArgs()); } finally { timer.stop(); @@ -184,6 +191,9 @@ private void clearFunctionWorker() private void startFunctionMonitor() { runMillis = 0; + if (funcTimer != null) { + funcTimer.stop(); + } funcTimer = new Timer(TIMER_DELAY_IN_MILLIS, new ActionListener() { public void actionPerformed(ActionEvent e) { // Stopwatch timer = testCasePanel.getSpatialFunctionPanel().getTimer(); @@ -195,7 +205,7 @@ public void actionPerformed(ActionEvent e) { else { timeStr = timeFmt.format(runMillis/1000.0) + " s"; } - frame.getResultWKTPanel().setRunningTime(timeStr); + resultWKTPanel().setRunningTime(timeStr); } }); funcTimer.setInitialDelay(0); @@ -213,20 +223,20 @@ public void executeScalarFunction() * For now scalar functions are executed on the calling thread. * They are expected to be of short duration */ - ScalarFunctionPanel scalarPanel = frame.getTestCasePanel().getScalarFunctionPanel(); + ScalarFunctionPanel scalarPanel = frame().getTestCasePanel().getScalarFunctionPanel(); String opName = scalarPanel.getOpName(); // initialize UI view - frame.getResultValuePanel().setResult(opName, "", null); - frame.showResultValueTab(); + frame().getResultValuePanel().setResult(opName, "", null); + frame().showResultValueTab(); - frame.setCursorWait(); + frame().setCursorWait(); Object result = scalarPanel.getResult(); - frame.setCursorNormal(); + frame().setCursorNormal(); Stopwatch timer = scalarPanel.getTimer(); String timeString = timer.getTimeString(); - frame.getResultValuePanel().setResult(opName, timer.getTimeString(), result); + frame().getResultValuePanel().setResult(opName, timer.getTimeString(), result); resultLogEntry(functionInvocation(scalarPanel), timeString, result); } From a71e13d9f2521243cb901d27d7004fb46b07bd85 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 21 Oct 2021 13:34:08 -0700 Subject: [PATCH 114/275] Fix CoordinateArrays.reverse to handle zero-length arrays (#787) Signed-off-by: Martin Davis --- .../jts/geom/CoordinateArrays.java | 3 ++ .../jts/geom/CoordinateArraysTest.java | 37 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java index a9ff8a0939..1b499d8571 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/CoordinateArrays.java @@ -471,6 +471,9 @@ public static Coordinate[] removeNull(Coordinate[] coord) { * Reverses the coordinates in an array in-place. */ public static void reverse(Coordinate[] coord) { + if (coord.length <= 1) + return; + int last = coord.length - 1; int mid = last / 2; for (int i = 0; i <= mid; i++) { diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateArraysTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateArraysTest.java index 2145c6c5ba..88dd45d230 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateArraysTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/CoordinateArraysTest.java @@ -12,10 +12,8 @@ package org.locationtech.jts.geom; -import junit.framework.TestCase; import junit.textui.TestRunner; -import org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory; -import org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory; +import test.jts.GeometryTestCase; /** * Unit tests for {@link CoordinateArrays} @@ -23,7 +21,7 @@ * @author Martin Davis * @version 1.7 */ -public class CoordinateArraysTest extends TestCase { +public class CoordinateArraysTest extends GeometryTestCase { public static void main(String args[]) { TestRunner.run(CoordinateArraysTest.class); @@ -84,6 +82,37 @@ public void testIntersection_coords_emptyEnvelope() ); } + public void testReverseEmpty() { + Coordinate[] pts = new Coordinate[0]; + checkReversed(pts); } + + public void testReverseSingleElement() { + Coordinate[] pts = new Coordinate[] { new Coordinate(1, 1) }; + checkReversed(pts); + } + + public void testReverse2() { + Coordinate[] pts = new Coordinate[] { + new Coordinate(1, 1), new Coordinate(2, 2) }; + checkReversed(pts); + } + + public void testReverse3() { + Coordinate[] pts = new Coordinate[] { + new Coordinate(1, 1), new Coordinate(2, 2), new Coordinate(3 ,3) }; + checkReversed(pts); + } + + private void checkReversed(Coordinate[] pts) { + Coordinate[] ptsRev = CoordinateArrays.copyDeep(pts); + CoordinateArrays.reverse(ptsRev); + assertEquals(pts.length, ptsRev.length); + int len = pts.length; + for (int i = 0; i < pts.length; i++) { + checkEqualXY(pts[i], ptsRev[len - 1 - i]); + } + } + public void testScrollRing() { // arrange Coordinate[] sequence = createCircle(new Coordinate(10, 10), 9d); From c49088af2d53c0ddf7b28d8eee685c4c6222469b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 21 Oct 2021 13:34:45 -0700 Subject: [PATCH 115/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index f9722f4c02..7a381cf2d5 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -34,6 +34,7 @@ Distributions for older JTS versions can be obtained at the ### Bug Fixes * Fix `WKTReader` geometry typename parsing (#786) +* Fix `CoordinateArrays.reverse` to handle zero-length arrays #787 # Version 1.18.2 From 45a6deb382ff4062c68fff7e7c0684b5ff91b30e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 26 Oct 2021 09:36:33 -0700 Subject: [PATCH 116/275] Remove GeometryFixer old code Signed-off-by: Martin Davis --- .../jts/geom/util/GeometryFixer.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java index 94a9ba5181..ab5ded4267 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java @@ -348,23 +348,6 @@ private Geometry union(List polys) { return OverlayNGRobust.union(polys); } - private Geometry OLDfixHoles(Polygon geom) { - List holes = new ArrayList(); - for (int i = 0; i < geom.getNumInteriorRing(); i++) { - Geometry holeRep = fixRing(geom.getInteriorRingN(i)); - if (holeRep != null) { - holes.add(holeRep); - } - } - if (holes.size() == 0) return null; - if (holes.size() == 1) { - return holes.get(0); - } - // TODO: replace with holes.union() once OverlayNG is the default - Geometry holesUnion = OverlayNGRobust.union(holes); - return holesUnion; - } - private Geometry fixRing(LinearRing ring) { //-- always execute fix, since it may remove repeated/invalid coords etc // TODO: would it be faster to check ring validity first? From 51280927078a71511ec8883ffb201e4506715f53 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 26 Oct 2021 10:02:13 -0700 Subject: [PATCH 117/275] Fix GeometryFixer to use isKeepCollapsed flag for GCs (#790) Signed-off-by: Martin Davis --- .../locationtech/jts/geom/util/GeometryFixer.java | 12 ++++++++++-- .../jts/geom/util/GeometryFixerTest.java | 9 +++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java index ab5ded4267..4a7653742f 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java @@ -39,6 +39,7 @@ *

      * Input geometries are always processed, so even valid inputs may * have some minor alterations. The output is always a new geometry object. + * *

      Semantic Rules

      *
        *
      1. Vertices with non-finite X or Y ordinates are removed @@ -62,7 +63,8 @@ *
      2. Collapsed lines and polygons are handled as follows, * depending on the keepCollapsed setting: *
          - *
        • false: (default) collapses are converted to empty geometries
        • + *
        • false: (default) collapses are converted to empty geometries + * (and removed if they are elements of collections)
        • *
        • true: collapses are converted to a valid geometry of lower dimension
        • *
        *
      3. @@ -375,8 +377,14 @@ private Geometry fixMultiPolygon(MultiPolygon geom) { private Geometry fixCollection(GeometryCollection geom) { Geometry[] geomRep = new Geometry[geom.getNumGeometries()]; for (int i = 0; i < geom.getNumGeometries(); i++) { - geomRep[i] = fix(geom.getGeometryN(i)); + geomRep[i] = fix(geom.getGeometryN(i), isKeepCollapsed); } return factory.createGeometryCollection(geomRep); } + + private static Geometry fix(Geometry geom, boolean isKeepCollapsed) { + GeometryFixer fix = new GeometryFixer(geom); + fix.setKeepCollapsed(isKeepCollapsed); + return fix.getResult(); + } } diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java index 4f82b08baf..bb5fdf4358 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java @@ -297,16 +297,21 @@ public void testGCWithAllEmpty() { "GEOMETRYCOLLECTION (POINT EMPTY, LINESTRING EMPTY, POLYGON EMPTY)"); } + public void testGCKeepCollapse() { + checkFixKeepCollapse("GEOMETRYCOLLECTION (LINESTRING ( 0 0, 0 0), POINT (1 1))", + "GEOMETRYCOLLECTION (POINT (0 0), POINT (1 1))"); + } + //---------------------------------------- public void testPolygonZBowtie() { checkFixZ("POLYGON Z ((10 90 1, 90 10 9, 90 90 9, 10 10 1, 10 90 1))", - "MULTIPOLYGON Z(((10 10 1, 10 90 1, 50 50 5, 10 10 1)), ((50 50 5, 90 90 9, 90 10 9, 50 50 5)))"); + "MULTIPOLYGON Z (((10 10 1, 10 90 1, 50 50 5, 10 10 1)), ((50 50 5, 90 90 9, 90 10 9, 50 50 5)))"); } public void testPolygonZHoleOverlap() { checkFixZ("POLYGON Z ((10 90 1, 60 90 6, 60 10 6, 10 10 1, 10 90 1), (20 80 2, 90 80 9, 90 20 9, 20 20 2, 20 80 2))", - "POLYGON Z((10 10 1, 10 90 1, 60 90 6, 60 80 6, 20 80 2, 20 20 2, 60 20 6, 60 10 6, 10 10 1))"); + "POLYGON Z ((10 10 1, 10 90 1, 60 90 6, 60 80 6, 20 80 2, 20 20 2, 60 20 6, 60 10 6, 10 10 1))"); } public void testMultiLineStringZKeepCollapse() { From 1329b53a51473b76135bca9a8bebf78ee0151c96 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 26 Oct 2021 10:04:01 -0700 Subject: [PATCH 118/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 7a381cf2d5..b367679d0d 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -35,6 +35,7 @@ Distributions for older JTS versions can be obtained at the * Fix `WKTReader` geometry typename parsing (#786) * Fix `CoordinateArrays.reverse` to handle zero-length arrays #787 +* Fix `GeometryFixer` to appply `isKeepCollapsed` flag to `GeometryCollection` elements (#790) # Version 1.18.2 From 1c8c80479582ea7620f44c0b34665d9eb6744593 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 27 Oct 2021 10:21:31 -0700 Subject: [PATCH 119/275] Rename TestBuilder palette to Spectrum Signed-off-by: Martin Davis --- .../jtstest/testbuilder/LayerStylePanel.java | 10 +++++----- .../jtstest/testbuilder/ui/style/Palette.java | 12 ++++++------ .../org/locationtech/jtstest/util/HSBPalette.java | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java index 84f2e5a4e9..3e4c71f2ea 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerStylePanel.java @@ -531,23 +531,23 @@ public void stateChanged(ChangeEvent e) { } //----------------------------------------- - static String[] paletteNames = { "Basic", "Varying", "Rainbow", "Rainbow Random" }; + static String[] paletteNames = { "Basic", "Varying", "Spectrum", "Spectrum Random" }; private static int getPaletteType(JComboBox comboPal) { String palName = (String)comboPal.getSelectedItem(); int paletteType = Palette.TYPE_BASIC; if (palName.equalsIgnoreCase(paletteNames[1])) paletteType = Palette.TYPE_VARY; - if (palName.equalsIgnoreCase(paletteNames[2])) paletteType = Palette.TYPE_RAINBOW; - if (palName.equalsIgnoreCase(paletteNames[3])) paletteType = Palette.TYPE_RAINBOW_RANDOM; + if (palName.equalsIgnoreCase(paletteNames[2])) paletteType = Palette.TYPE_SPECTRUM; + if (palName.equalsIgnoreCase(paletteNames[3])) paletteType = Palette.TYPE_SPECTRUM_RANDOM; return paletteType; } private static void setPaletteType(JComboBox comboPal, int paletteType) { int index = 0; if (paletteType == Palette.TYPE_VARY) index = 1; - if (paletteType == Palette.TYPE_RAINBOW) index = 2; - if (paletteType == Palette.TYPE_RAINBOW_RANDOM) index = 3; + if (paletteType == Palette.TYPE_SPECTRUM) index = 2; + if (paletteType == Palette.TYPE_SPECTRUM_RANDOM) index = 3; comboPal.setSelectedIndex(index); } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java index 6fbe876314..9ba37128a8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/style/Palette.java @@ -20,8 +20,8 @@ public class Palette { public static final int TYPE_BASIC = 1; public static final int TYPE_VARY = 2; - public static final int TYPE_RAINBOW = 3; - public static final int TYPE_RAINBOW_RANDOM = 4; + public static final int TYPE_SPECTRUM = 3; + public static final int TYPE_SPECTRUM_RANDOM = 4; private static final float BRIGHT_RANGE = 0.1f; private static final float SAT_RANGE = 0.2f; @@ -37,11 +37,11 @@ public static HSBPalette customPalette(int paletteType, Color clrBase, int numHu 3, bright - BRIGHT_RANGE/2, bright + BRIGHT_RANGE/2 ); } - else if (TYPE_RAINBOW == paletteType) { - return HSBPalette.createRainbow(numHues, sat, bright); + else if (TYPE_SPECTRUM == paletteType) { + return HSBPalette.createSpectrum(numHues, sat, bright); } - else if (TYPE_RAINBOW_RANDOM == paletteType) { - return HSBPalette.createRainbowIncremental(0.23f, sat, bright); + else if (TYPE_SPECTRUM_RANDOM == paletteType) { + return HSBPalette.createSpectrumIncremental(0.23f, sat, bright); } return pal; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java b/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java index 62331ed03e..fdd0e224d3 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/util/HSBPalette.java @@ -21,21 +21,21 @@ public class HSBPalette { public static final float HUE_WIDTH = 0.08333f; private static final float HUE_WRAP_MAX = 1 - HUE_WIDTH; - public static HSBPalette createRainbow(int numHue, float s, float b) { + public static HSBPalette createSpectrum(int numHue, float s, float b) { return new HSBPalette(numHue, 0, HUE_WRAP_MAX, 1, s, s, 1, b, b ); } - public static HSBPalette createRainbowWrap(int numHue, float s, float b) { + public static HSBPalette createSpectrumWrap(int numHue, float s, float b) { return new HSBPalette(numHue, 0, 1, 1, s, s, 1, b, b ); } - public static HSBPalette createRainbowIncremental(float hInc, float s, float b) { + public static HSBPalette createSpectrumIncremental(float hInc, float s, float b) { HSBPalette pal = new HSBPalette(50, 0, 1, 1, s, s, 1, b, b From 9604e5de5dae856c9c0d99471662919cee6828ce Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Thu, 28 Oct 2021 18:11:55 +0200 Subject: [PATCH 120/275] Preserve MULTI geometry types for single item results (#791) * Add isKeepMulti flag variable * Add proper setKeepMulti method * Add fix overload with isKeepMulti flag parameter * Wire isKeepMulti in fixMultiXXX methods, make behavior consistent refers to #788 --- .../jts/geom/util/GeometryFixer.java | 125 ++++++++++++------ .../jts/geom/util/GeometryFixerTest.java | 94 +++++++------ 2 files changed, 141 insertions(+), 78 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java index 4a7653742f..7e1c2c0d9d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/util/GeometryFixer.java @@ -33,16 +33,16 @@ import org.locationtech.jts.operation.overlayng.OverlayNGRobust; /** - * Fixes a geometry to be a valid geometry, while preserving as much as + * Fixes a geometry to be a valid geometry, while preserving as much as * possible of the shape and location of the input. * Validity is determined according to {@link Geometry#isValid()}. *

        - * Input geometries are always processed, so even valid inputs may + * Input geometries are always processed, so even valid inputs may * have some minor alterations. The output is always a new geometry object. - * + * *

        Semantic Rules

        *
          - *
        1. Vertices with non-finite X or Y ordinates are removed + *
        2. Vertices with non-finite X or Y ordinates are removed * (as per {@link Coordinate#isValid()}.
        3. *
        4. Repeated points are reduced to a single point
        5. *
        6. Empty atomic geometries are valid and are returned unchanged
        7. @@ -50,14 +50,14 @@ *
        8. Point: keep valid coordinate, or EMPTY
        9. *
        10. LineString: coordinates are fixed
        11. *
        12. LinearRing: coordinates are fixed. Keep valid ring, or else convert into LineString
        13. - *
        14. Polygon: transform into a valid polygon, + *
        15. Polygon: transform into a valid polygon, * preserving as much of the extent and vertices as possible. *
            - *
          • Rings are fixed to ensure they are valid
          • + *
          • Rings are fixed to ensure they are valid
          • *
          • Holes intersecting the shell are subtracted from the shell
          • *
          • Holes outside the shell are converted into polygons
          • *
        16. - *
        17. MultiPolygon: each polygon is fixed, + *
        18. MultiPolygon: each polygon is fixed, * then result made non-overlapping (via union)
        19. *
        20. GeometryCollection: each element is fixed
        21. *
        22. Collapsed lines and polygons are handled as follows, @@ -69,64 +69,94 @@ *
    * * - * + * * @author Martin Davis - * + * * @see Geometry#isValid() */ public class GeometryFixer { + private static final boolean DEFAULT_KEEP_MULTI = true; + /** * Fixes a geometry to be valid. - * + * * @param geom the geometry to be fixed * @return the valid fixed geometry */ public static Geometry fix(Geometry geom) { + return fix(geom, DEFAULT_KEEP_MULTI); + } + + /** + * Fixes a geometry to be valid, allowing to set a flag controlling how + * single item results from fixed {@code MULTI} geometries should be + * returned. + * + * @param geom the geometry to be fixed + * @param isKeepMulti a flag indicating if {@code MULTI} geometries should not be converted to single instance types + * if they consist of only one item. + * @return the valid fixed geometry + */ + public static Geometry fix(Geometry geom, boolean isKeepMulti) { GeometryFixer fix = new GeometryFixer(geom); + fix.setKeepMulti(isKeepMulti); return fix.getResult(); } - + private Geometry geom; private GeometryFactory factory; private boolean isKeepCollapsed = false; + private boolean isKeepMulti = DEFAULT_KEEP_MULTI; /** * Creates a new instance to fix a given geometry. - * + * * @param geom the geometry to be fixed */ public GeometryFixer(Geometry geom) { this.geom = geom; this.factory = geom.getFactory(); } - + /** * Sets whether collapsed geometries are converted to empty, * (which will be removed from collections), * or to a valid geometry of lower dimension. * The default is to convert collapses to empty geometries. - * + * * @param isKeepCollapsed whether collapses should be converted to a lower dimension geometry */ public void setKeepCollapsed(boolean isKeepCollapsed) { this.isKeepCollapsed = isKeepCollapsed; } - + + /** + * Sets whether fixed {@code MULTI} geometries that consist of + * only one item should still be returned as {@code MULTI} geometries. + * + * The default is to keep {@code MULTI} geometries. + * + * @param isKeepMulti flag whether to keep {@code MULTI} geometries. + */ + public void setKeepMulti(boolean isKeepMulti) { + this.isKeepMulti = isKeepMulti; + } + /** * Gets the fixed geometry. - * + * * @return the fixed geometry */ public Geometry getResult() { - /** + /* * Truly empty geometries are simply copied. * Geometry collections with elements are evaluated on a per-element basis. */ if (geom.getNumGeometries() == 0) { return geom.copy(); } - + if (geom instanceof Point) return fixPoint((Point) geom); // LinearRing must come before LineString if (geom instanceof LinearRing) return fixLinearRing((LinearRing) geom); @@ -168,21 +198,25 @@ private Geometry fixMultiPoint(MultiPoint geom) { pts.add(fixPt); } } + + if (!this.isKeepMulti && pts.size() == 1) + return pts.get(0); + return factory.createMultiPoint(GeometryFactory.toPointArray(pts)); } - + private Geometry fixLinearRing(LinearRing geom) { Geometry fix = fixLinearRingElement(geom); if (fix == null) return factory.createLinearRing(); return fix; } - + private Geometry fixLinearRingElement(LinearRing geom) { if (geom.isEmpty()) return null; Coordinate[] pts = geom.getCoordinates(); Coordinate[] ptsFix = fixCoordinates(pts); - if (isKeepCollapsed) { + if (this.isKeepCollapsed) { if (ptsFix.length == 1) { return factory.createPoint(ptsFix[0]); } @@ -209,12 +243,12 @@ private Geometry fixLineString(LineString geom) { return factory.createLineString(); return fix; } - + private Geometry fixLineStringElement(LineString geom) { if (geom.isEmpty()) return null; Coordinate[] pts = geom.getCoordinates(); Coordinate[] ptsFix = fixCoordinates(pts); - if (isKeepCollapsed && ptsFix.length == 1) { + if (this.isKeepCollapsed && ptsFix.length == 1) { return factory.createPoint(ptsFix[0]); } if (ptsFix.length <= 1) { @@ -225,7 +259,7 @@ private Geometry fixLineStringElement(LineString geom) { /** * Returns a clean copy of the input coordinate array. - * + * * @param pts coordinates to clean * @return an array of clean coordinates */ @@ -233,28 +267,32 @@ private static Coordinate[] fixCoordinates(Coordinate[] pts) { Coordinate[] ptsClean = CoordinateArrays.removeRepeatedOrInvalidPoints(pts); return CoordinateArrays.copyDeep(ptsClean); } - + private Geometry fixMultiLineString(MultiLineString geom) { List fixed = new ArrayList(); boolean isMixed = false; for (int i = 0; i < geom.getNumGeometries(); i++) { LineString line = (LineString) geom.getGeometryN(i); if (line.isEmpty()) continue; - + Geometry fix = fixLineStringElement(line); if (fix == null) continue; - + if (! (fix instanceof LineString)) { isMixed = true; } fixed.add(fix); } + if (fixed.size() == 1) { - return fixed.get(0); + if (!this.isKeepMulti || !(fixed.get(0) instanceof LineString)) + return fixed.get(0); } + if (isMixed) { return factory.createGeometryCollection(GeometryFactory.toGeometryArray(fixed)); } + return factory.createMultiLineString(GeometryFactory.toLineStringArray(fixed)); } @@ -264,22 +302,22 @@ private Geometry fixPolygon(Polygon geom) { return factory.createPolygon(); return fix; } - + private Geometry fixPolygonElement(Polygon geom) { LinearRing shell = geom.getExteriorRing(); Geometry fixShell = fixRing(shell); if (fixShell.isEmpty()) { - if (isKeepCollapsed) { + if (this.isKeepCollapsed) { return fixLineString(shell); } //--- if not allowing collapses then return empty polygon - return null; + return null; } //--- if no holes then done if (geom.getNumInteriorRing() == 0) { return fixShell; } - + //--- fix holes, classify, and construct shell-true holes List holesFixed = fixHoles(geom); List holes = new ArrayList(); @@ -289,7 +327,7 @@ private Geometry fixPolygonElement(Polygon geom) { if (shells.size() == 0) { return polyWithHoles; } - + //--- if some holes converted to shells, union all shells shells.add(polyWithHoles); Geometry result = union(shells); @@ -306,7 +344,7 @@ private List fixHoles(Polygon geom) { } return holes; } - + private void classifyHoles(Geometry shell, List holesFixed, List holes, List shells) { PreparedGeometry shellPrep = PreparedGeometryFactory.prepare(shell); for (Geometry hole : holesFixed) { @@ -321,13 +359,13 @@ private void classifyHoles(Geometry shell, List holesFixed, List holes) { - if (holes == null || holes.size() == 0) + if (holes == null || holes.size() == 0) return shell; Geometry holesUnion = union(holes); return OverlayNGRobust.overlay(shell, holesUnion, OverlayNG.DIFFERENCE); @@ -337,7 +375,7 @@ private Geometry difference(Geometry shell, List holes) { * Unions a list of polygonal geometries. * Optimizes case of zero or one input geometries. * Requires that the inputs are net new objects. - * + * * @param polys the polygonal geometries to union * @return the union of the inputs */ @@ -371,20 +409,25 @@ private Geometry fixMultiPolygon(MultiPolygon geom) { } // TODO: replace with polys.union() once OverlayNG is the default Geometry result = union(polys); - return result; + + if (this.isKeepMulti && result instanceof Polygon) + result = factory.createMultiPolygon(new Polygon[]{(Polygon) result}); + + return result; } private Geometry fixCollection(GeometryCollection geom) { Geometry[] geomRep = new Geometry[geom.getNumGeometries()]; for (int i = 0; i < geom.getNumGeometries(); i++) { - geomRep[i] = fix(geom.getGeometryN(i), isKeepCollapsed); + geomRep[i] = fix(geom.getGeometryN(i), this.isKeepCollapsed, this.isKeepMulti); } return factory.createGeometryCollection(geomRep); } - - private static Geometry fix(Geometry geom, boolean isKeepCollapsed) { + + private static Geometry fix(Geometry geom, boolean isKeepCollapsed, boolean isKeepMulti) { GeometryFixer fix = new GeometryFixer(geom); fix.setKeepCollapsed(isKeepCollapsed); + fix.setKeepMulti(isKeepMulti); return fix.getResult(); } } diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java index bb5fdf4358..5b01ffb7e3 100644 --- a/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/geom/util/GeometryFixerTest.java @@ -23,11 +23,11 @@ public class GeometryFixerTest extends GeometryTestCase { public static void main(String args[]) { TestRunner.run(GeometryFixerTest.class); } - + public GeometryFixerTest(String name) { super(name); } - + public void testPoint() { checkFix("POINT (0 0)", "POINT (0 0)"); } @@ -53,7 +53,7 @@ private Point createPoint(double x, double y) { Point pt = getGeometryFactory().createPoint(p); return pt; } - + //---------------------------------------- public void testMultiPointNaN() { @@ -66,11 +66,15 @@ public void testMultiPoint() { "MULTIPOINT ((0 0), (1 1))"); } - public void testMultiPointWithEmpty() { + public void testMultiPointWithEmptyKeepMulti() { checkFix("MULTIPOINT ((0 0), EMPTY)", - "MULTIPOINT ((0 0))"); + "MULTIPOINT ((0 0))", true); } + public void testMultiPointWithEmpty() { + checkFix("MULTIPOINT ((0 0), EMPTY)", + "POINT (0 0)", false); + } public void testMultiPointWithMultiEmpty() { checkFix("MULTIPOINT (EMPTY, EMPTY)", "MULTIPOINT EMPTY"); @@ -110,7 +114,7 @@ public void testLineStringSelfCross() { checkFix("LINESTRING (0 0, 9 9, 9 5, 0 5)", "LINESTRING (0 0, 9 9, 9 5, 0 5)"); } - + //---------------------------------------- public void testLinearRingEmpty() { @@ -155,7 +159,7 @@ public void testLinearRingSelfCross() { checkFix("LINEARRING (10 10, 10 90, 90 10, 90 90, 10 10)", "LINESTRING (10 10, 10 90, 90 10, 90 90, 10 10)"); } - + //---------------------------------------- /** @@ -165,29 +169,34 @@ public void testMultiLineStringSelfCross() { checkFix("MULTILINESTRING ((10 90, 90 10, 90 90), (90 50, 10 50))", "MULTILINESTRING ((10 90, 90 10, 90 90), (90 50, 10 50))"); } - + public void testMultiLineStringWithCollapse() { checkFix("MULTILINESTRING ((10 10, 90 90), (10 10, 10 10, 10 10))", - "LINESTRING (10 10, 90 90))"); + "LINESTRING (10 10, 90 90))", false); } - + + public void testMultiLineStringWithCollapseKeepMulti() { + checkFix("MULTILINESTRING ((10 10, 90 90), (10 10, 10 10, 10 10))", + "MULTILINESTRING ((10 10, 90 90)))", true); + } + public void testMultiLineStringKeepCollapse() { checkFixKeepCollapse("MULTILINESTRING ((10 10, 90 90), (10 10, 10 10, 10 10))", "GEOMETRYCOLLECTION (POINT (10 10), LINESTRING (10 10, 90 90))"); } - + public void testMultiLineStringWithEmpty() { checkFix("MULTILINESTRING ((10 10, 90 90), EMPTY)", - "LINESTRING (10 10, 90 90))"); + "MULTILINESTRING ((10 10, 90 90))"); } - + public void testMultiLineStringWithMultiEmpty() { checkFix("MULTILINESTRING (EMPTY, EMPTY)", "MULTILINESTRING EMPTY"); } - + //---------------------------------------- - + public void testPolygonEmpty() { checkFix("POLYGON EMPTY", "POLYGON EMPTY"); @@ -252,12 +261,12 @@ public void testPolygonHoleKeepCollapse() { checkFixKeepCollapse("POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (80 80, 20 80, 20 20, 20 80, 80 80))", "POLYGON ((10 10, 10 90, 90 90, 90 10, 10 10))"); } - + public void testPolygonHoleOverlapAndOutsideOverlap() { checkFix("POLYGON ((50 90, 80 90, 80 10, 50 10, 50 90), (70 80, 90 80, 90 20, 70 20, 70 80), (40 80, 40 50, 0 50, 0 80, 40 80), (30 40, 10 40, 10 60, 30 60, 30 40), (60 70, 80 70, 80 30, 60 30, 60 70))", "MULTIPOLYGON (((10 40, 10 50, 0 50, 0 80, 40 80, 40 50, 30 50, 30 40, 10 40)), ((70 80, 70 70, 60 70, 60 30, 70 30, 70 20, 80 20, 80 10, 50 10, 50 90, 80 90, 80 80, 70 80)))"); } - + //---------------------------------------- public void testMultiPolygonEmpty() { @@ -275,9 +284,14 @@ public void testMultiPolygonWithEmpty() { "MULTIPOLYGON (((10 40, 40 40, 40 10, 10 10, 10 40)), ((50 40, 80 40, 80 10, 50 10, 50 40)))"); } + public void testMultiPolygonWithCollapseKeepMulti() { + checkFix("MULTIPOLYGON (((10 40, 40 40, 40 10, 10 10, 10 40)), ((50 40, 50 40, 50 40, 50 40, 50 40)))", + "MULTIPOLYGON (((10 10, 10 40, 40 40, 40 10, 10 10)))", true); + } + public void testMultiPolygonWithCollapse() { checkFix("MULTIPOLYGON (((10 40, 40 40, 40 10, 10 10, 10 40)), ((50 40, 50 40, 50 40, 50 40, 50 40)))", - "POLYGON ((10 10, 10 40, 40 40, 40 10, 10 10))"); + "POLYGON ((10 10, 10 40, 40 40, 40 10, 10 10))", false); } public void testMultiPolygonKeepCollapse() { @@ -320,41 +334,47 @@ public void testMultiLineStringZKeepCollapse() { } //================================================ - - + + private void checkFix(String wkt, String wktExpected) { Geometry geom = read(wkt); - checkFix(geom, false, wktExpected); + checkFix(geom, false, true, wktExpected); } - + + private void checkFix(String wkt, String wktExpected, boolean keepMulti) { + Geometry geom = read(wkt); + checkFix(geom, false, keepMulti, wktExpected); + } + private void checkFixKeepCollapse(String wkt, String wktExpected) { Geometry geom = read(wkt); - checkFix(geom, true, wktExpected); + checkFix(geom, true, true, wktExpected); } - + private void checkFix(Geometry input, String wktExpected) { - checkFix(input, false, wktExpected); + checkFix(input, false, true, wktExpected); } - + private void checkFixKeepCollapse(Geometry input, String wktExpected) { - checkFix(input, true, wktExpected); + checkFix(input, true, true, wktExpected); } - - private void checkFix(Geometry input, boolean keepCollapse, String wktExpected) { + + private void checkFix(Geometry input, boolean keepCollapse, boolean keepMulti, String wktExpected) { Geometry actual; if (keepCollapse) { GeometryFixer fixer = new GeometryFixer(input); fixer.setKeepCollapsed(true); + fixer.setKeepMulti(keepMulti); actual = fixer.getResult(); } else { - actual= GeometryFixer.fix(input); + actual= GeometryFixer.fix(input, keepMulti); } - + assertTrue("Result is invalid", actual.isValid()); assertTrue("Input geometry was not copied", input != actual); assertTrue("Result has aliased coordinates", checkDeepCopy(input, actual)); - + Geometry expected = read(wktExpected); checkEqual(expected, actual); } @@ -381,12 +401,12 @@ private void checkFixZ(String wkt, String wktExpected) { Geometry geom = read(wkt); checkFixZ(geom, false, wktExpected); } - + private void checkFixZKeepCollapse(String wkt, String wktExpected) { Geometry geom = read(wkt); checkFixZ(geom, true, wktExpected); } - + private void checkFixZ(Geometry input, boolean keepCollapse, String wktExpected) { Geometry actual; if (keepCollapse) { @@ -397,12 +417,12 @@ private void checkFixZ(Geometry input, boolean keepCollapse, String wktExpected) else { actual= GeometryFixer.fix(input); } - + assertTrue("Result is invalid", actual.isValid()); - + Geometry expected = read(wktExpected); checkEqualXYZ(expected, actual); } - + } From 378459e4dec8e9610b97d7908e58cbd55b8f46e8 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 28 Oct 2021 09:13:25 -0700 Subject: [PATCH 121/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index b367679d0d..2efd432bbe 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -30,6 +30,7 @@ Distributions for older JTS versions can be obtained at the * Improve `KdTree` query code to avoid recursion (#779) * Add `KdTree` seeding to`SnappingNoder` (#780) * Add `DiscreteFrechetDistance` (#764, #783) +* Add `GeometryFixer` option to preserve `Multi` geometry types when collapses occur (#791) ### Bug Fixes From d8d990c5d58826317a47953b3ceea1584d87e560 Mon Sep 17 00:00:00 2001 From: Alex Pinder Date: Fri, 29 Oct 2021 18:21:09 +0100 Subject: [PATCH 122/275] Make Quadtree implementation threadsafe (#792) Queries and insertions to the quadtree can now work in multi-threaded environments. Co-authored-by: Alex Pinder --- .../org/locationtech/jts/index/quadtree/NodeBase.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java index d545e22557..7835ccdcc4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java +++ b/modules/core/src/main/java/org/locationtech/jts/index/quadtree/NodeBase.java @@ -13,7 +13,7 @@ import java.io.Serializable; import java.util.ArrayList; -import java.util.Iterator; +import java.util.Collections; import java.util.List; import org.locationtech.jts.geom.Envelope; @@ -50,7 +50,7 @@ public static int getSubnodeIndex(Envelope env, double centrex, double centrey) return subnodeIndex; } - protected List items = new ArrayList(); + protected List items = Collections.synchronizedList(new ArrayList()); /** * subquads are numbered as follows: @@ -190,8 +190,10 @@ public void visit(Envelope searchEnv, ItemVisitor visitor) private void visitItems(Envelope searchEnv, ItemVisitor visitor) { // would be nice to filter items based on search envelope, but can't until they contain an envelope - for (Iterator i = items.iterator(); i.hasNext(); ) { - visitor.visitItem(i.next()); + synchronized (items) { + for (int i = 0; i < items.size(); i++) { + visitor.visitItem(items.get(i)); + } } } From a728fcc2b366ec0f0576b27cf91ed8932d7c14e8 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 29 Oct 2021 10:22:05 -0700 Subject: [PATCH 123/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 2efd432bbe..2669a6cae9 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -31,6 +31,7 @@ Distributions for older JTS versions can be obtained at the * Add `KdTree` seeding to`SnappingNoder` (#780) * Add `DiscreteFrechetDistance` (#764, #783) * Add `GeometryFixer` option to preserve `Multi` geometry types when collapses occur (#791) +* Make `QuadTree` thread-safe (#792) ### Bug Fixes From aa6236cb9fd5119404ad4d9018d63fe91519c574 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 1 Nov 2021 15:12:50 -0700 Subject: [PATCH 124/275] Fix RectangleIntersects to handle XYZM coordinates (#794) Signed-off-by: Martin Davis --- .../predicate/RectangleIntersects.java | 4 +-- .../predicate/RectangleIntersectsTest.java | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/operation/predicate/RectangleIntersectsTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java index aa52b17ce1..874a7f7f53 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/predicate/RectangleIntersects.java @@ -266,8 +266,6 @@ class RectangleIntersectsSegmentVisitor extends ShortCircuitedGeometryVisitor private RectangleLineIntersector rectIntersector; private boolean hasIntersection = false; - private Coordinate p0 = new Coordinate(); - private Coordinate p1 = new Coordinate(); /** * Creates a visitor for checking rectangle intersection @@ -323,6 +321,8 @@ private void checkIntersectionWithLineStrings(List lines) private void checkIntersectionWithSegments(LineString testLine) { CoordinateSequence seq1 = testLine.getCoordinateSequence(); + Coordinate p0 = seq1.createCoordinate(); + Coordinate p1 = seq1.createCoordinate(); for (int j = 1; j < seq1.size(); j++) { seq1.getCoordinate(j - 1, p0); seq1.getCoordinate(j, p1); diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/predicate/RectangleIntersectsTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/predicate/RectangleIntersectsTest.java new file mode 100644 index 0000000000..0c70e55657 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/operation/predicate/RectangleIntersectsTest.java @@ -0,0 +1,28 @@ +package org.locationtech.jts.operation.predicate; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class RectangleIntersectsTest extends GeometryTestCase { + public static void main(String args[]) { + TestRunner.run(RectangleIntersectsTest.class); + } + + public RectangleIntersectsTest(String name) { super(name); } + + public void testXYZM() throws ParseException { + GeometryFactory geomFact = new GeometryFactory(PackedCoordinateSequenceFactory.DOUBLE_FACTORY); + WKTReader rdr = new WKTReader(geomFact); + Polygon rect = (Polygon) rdr.read("POLYGON ZM ((1 9 2 3, 9 9 2 3, 9 1 2 3, 1 1 2 3, 1 9 2 3))"); + Geometry line = rdr.read("LINESTRING ZM (5 15 5 5, 15 5 5 5)"); + boolean rectIntersects = RectangleIntersects.intersects(rect, line); + assertEquals(false, rectIntersects); + } +} From acd688fbc788c7443317d485a75312986b824c85 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 1 Nov 2021 15:14:09 -0700 Subject: [PATCH 125/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 2669a6cae9..08251b783d 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -38,6 +38,7 @@ Distributions for older JTS versions can be obtained at the * Fix `WKTReader` geometry typename parsing (#786) * Fix `CoordinateArrays.reverse` to handle zero-length arrays #787 * Fix `GeometryFixer` to appply `isKeepCollapsed` flag to `GeometryCollection` elements (#790) +* Fix `RectangleIntersects` to handle XYZM geometry (#794) # Version 1.18.2 From e0a932c1f82793af0da3b11391a83590b103a2fc Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 1 Nov 2021 17:33:45 -0700 Subject: [PATCH 126/275] Fix some algorithms handling of XYZM coordinates (#795) Signed-off-by: Martin Davis --- .../org/locationtech/jts/algorithm/Area.java | 6 +-- .../locationtech/jts/algorithm/Length.java | 2 +- .../jts/geom/GeometryXYZMTest.java | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/geom/GeometryXYZMTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java index 8849fd4a88..386ab8f375 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Area.java @@ -93,9 +93,9 @@ public static double ofRingSigned(CoordinateSequence ring) * Based on the Shoelace formula. * http://en.wikipedia.org/wiki/Shoelace_formula */ - Coordinate p0 = new Coordinate(); - Coordinate p1 = new Coordinate(); - Coordinate p2 = new Coordinate(); + Coordinate p0 = ring.createCoordinate(); + Coordinate p1 = ring.createCoordinate(); + Coordinate p2 = ring.createCoordinate(); ring.getCoordinate(0, p1); ring.getCoordinate(1, p2); double x0 = p1.x; diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Length.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Length.java index f18303daeb..3569d45fd3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Length.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Length.java @@ -37,7 +37,7 @@ public static double ofLine(CoordinateSequence pts) double len = 0.0; - Coordinate p = new Coordinate(); + Coordinate p = pts.createCoordinate(); pts.getCoordinate(0, p); double x0 = p.x; double y0 = p.y; diff --git a/modules/core/src/test/java/org/locationtech/jts/geom/GeometryXYZMTest.java b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryXYZMTest.java new file mode 100644 index 0000000000..56f36d9477 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/geom/GeometryXYZMTest.java @@ -0,0 +1,37 @@ +package org.locationtech.jts.geom; + +import org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory; +import org.locationtech.jts.io.ParseException; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +/** + * Tests to confirm that operations call {@link CoordinateSequence#createCoordinate()} + * to ensure they work correctly with coordinates of any dimension + * (in particular XYZM coordinates, which do not fit in the default {@link Coordinate}). + * + * @author Martin Davis + * + */ +public class GeometryXYZMTest extends GeometryTestCase { + public static void main(String args[]) { + TestRunner.run(GeometryXYZMTest.class); + } + + static GeometryFactory geomFact = new GeometryFactory(PackedCoordinateSequenceFactory.DOUBLE_FACTORY); + + public GeometryXYZMTest(String name) { super(name); } + + public void testArea() { + Polygon geom = (Polygon) read(geomFact, "POLYGON ZM ((1 9 2 3, 9 9 2 3, 9 1 2 3, 1 1 2 3, 1 9 2 3))"); + double area = geom.getArea(); + assertEquals(64.0, area); + } + + public void testLength() { + Polygon geom = (Polygon) read(geomFact, "POLYGON ZM ((1 9 2 3, 9 9 2 3, 9 1 2 3, 1 1 2 3, 1 9 2 3))"); + double len = geom.getLength(); + assertEquals(32.0, len); + } +} From 45dd0c0fba979a9549c3fa9795f363c6535c873a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 1 Nov 2021 17:34:45 -0700 Subject: [PATCH 127/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 08251b783d..35c1c98576 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -39,6 +39,7 @@ Distributions for older JTS versions can be obtained at the * Fix `CoordinateArrays.reverse` to handle zero-length arrays #787 * Fix `GeometryFixer` to appply `isKeepCollapsed` flag to `GeometryCollection` elements (#790) * Fix `RectangleIntersects` to handle XYZM geometry (#794) +* Fix various operations to handle XYZM geometry (#795) # Version 1.18.2 From f82f68dd19ff9fc30e836ae45375d36f79b6131f Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 19 Nov 2021 10:52:29 -0800 Subject: [PATCH 128/275] Remove unneeded unit test Signed-off-by: Martin Davis --- .../snapround/SnapRoundingNoderTest.java | 16 ++ .../snapround/SnapRoundingNoderTestOne.java | 177 ------------------ 2 files changed, 16 insertions(+), 177 deletions(-) delete mode 100644 modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTestOne.java diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java index 6ede6e4f20..e9f23def7a 100644 --- a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java @@ -183,6 +183,22 @@ public void testNearVertexNotNoded() { checkRounding(wkt, 100000000, expected); } + /** + * A vertex lies near interior of horizontal segment. + * Both are moved by rounding, and vertex ends up coincident with segment, + * but node is not created. + * This is very subtle, since because the segment is horizontal the vertex lies exactly on it + * and thus still reports as valid geometry (although a noding check reports failure). + * This is caused by the indexing used in Snap-rounding using exact envelopes. + * What is needed is a small expansion amount to ensure segments within snap distance are tested + * (in MCIndexNoder) + */ + public void testVertexNearHorizSegNotNoded() { + String wkt = "MULTILINESTRING (( 2.5096893 48.9530182, 2.50762932500455 48.95233152500091, 2.5055695 48.9530182 ), ( 2.5090027 48.9523315, 2.5035095 48.9523315 ))"; + String expected = null; + checkRounding(wkt, 1000000, expected); + } + void checkRounding(String wkt, double scale, String expectedWKT) { Geometry geom = read(wkt); diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTestOne.java b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTestOne.java deleted file mode 100644 index 0e22bfa1eb..0000000000 --- a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTestOne.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2019 Martin Davis. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ - -package org.locationtech.jts.noding.snapround; - -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.PrecisionModel; -import org.locationtech.jts.noding.Noder; -import org.locationtech.jts.noding.NodingTestUtil; - -import junit.textui.TestRunner; -import test.jts.GeometryTestCase; - - -/** - * Test Snap Rounding - * - * @version 1.17 - */ -public class SnapRoundingNoderTestOne extends GeometryTestCase { - - - private static Noder getSnapRounder(PrecisionModel pm) { - return new SnapRoundingNoder(pm); - } - - GeometryFactory geomFact = new GeometryFactory(); - - public static void main(String args[]) { - TestRunner.run(SnapRoundingNoderTestOne.class); - } - - public SnapRoundingNoderTestOne(String name) { super(name); } - - public void testBadNoding() { - String wkt = "MULTILINESTRING ((0 3, 3 3, 3 0, 0 0, 0 3), (1 1.2, 1 1.1, 2.3 1.1, 1 1.2), (1 1, 2 1, 2 0, 1 0, 1 1))"; - String expected = "MULTILINESTRING ((0 3, 3 3, 3 0, 2 0), (2 0, 1 0), (1 0, 0 0, 0 3), (1 1, 2 1), (2 1, 1 1), (1 1, 2 1), (2 1, 2 0), (2 0, 1 0), (1 0, 1 1))"; - checkRounding(wkt, 1, expected); - } - - public void xtestPreventAddingVertexNodesWithSnapping() { - String wkt = "MULTILINESTRING ((5 0, 5 10.2, 10 10), (0 3, 10 3))"; - String expected = "MULTILINESTRING ((0 3, 5 3), (5 0, 5 3), (5 3, 5 10, 10 10), (5 3, 10 3))"; - checkRounding(wkt, 1, expected); - } - - public void xtestPreventAddingVertexNodes() { - String wkt = "MULTILINESTRING ((5 0, 5 10, 10 10), (0 3, 10 3))"; - String expected = "MULTILINESTRING ((0 3, 5 3), (5 0, 5 3), (5 3, 5 10, 10 10), (5 3, 10 3))"; - checkRounding(wkt, 1, expected); - } - - public void xtestSlantAndHorizontalLineWithMiddleNode() { - String wkt = "MULTILINESTRING ((0.1565552 49.5277405, 0.1579285 49.5277405, 0.1593018 49.5277405), (0.1568985 49.5280838, 0.1589584 49.5273972))"; - String expected = "MULTILINESTRING ((0.156555 49.527741, 0.157928 49.527741), (0.156899 49.528084, 0.157928 49.527741), (0.157928 49.527741, 0.157929 49.527741), (0.157928 49.527741, 0.158958 49.527397), (0.157929 49.527741, 0.159302 49.527741))"; - checkRounding(wkt, 1_000_000.0, expected); - } - - public void xtestFlatLinesWithMiddleNode() { - String wkt = "MULTILINESTRING ((2.5117493 49.0278625, 2.5144958 49.0278625), (2.511749 49.027863, 2.513123 49.027863, 2.514496 49.027863))"; - String expected = "MULTILINESTRING ((2.511749 49.027863, 2.513123 49.027863), (2.511749 49.027863, 2.513123 49.027863), (2.513123 49.027863, 2.514496 49.027863), (2.513123 49.027863, 2.514496 49.027863))"; - checkRounding(wkt, 1_000_000.0, expected); - } - - public void xtestNearbyCorner() { - - String wkt = "MULTILINESTRING ((0.2 1.1, 1.6 1.4, 1.9 2.9), (0.9 0.9, 2.3 1.7))"; - String expected = "MULTILINESTRING ((0 1, 1 1), (1 1, 2 1), (1 1, 2 1), (2 1, 2 2), (2 1, 2 2), (2 2, 2 3))"; - checkRounding(wkt, 1.0, expected); - } - - public void xtestNearbyShape() { - - String wkt = "MULTILINESTRING ((1.3 0.1, 2.4 3.9), (0 1, 1.53 1.48, 0 4))"; - String expected = "MULTILINESTRING ((1 0, 2 1), (2 1, 2 4), (0 1, 2 1), (2 1, 0 4))"; - checkRounding(wkt, 1.0, expected); - } - - /** - * Currently fails, perhaps due to intersection lying right on a grid cell corner? - * Fixed by ensuring intersections are forced into segments - */ - public void xtestIntOnGridCorner() { - - String wkt = "MULTILINESTRING ((4.30166242 45.53438188, 4.30166243 45.53438187), (4.3011475 45.5328371, 4.3018341 45.5348969))"; - String expected = null; - checkRounding(wkt, 100000000, expected); - } - - /** - * Currently fails, does not node correctly - * Fixed by not snapping line segments when testing against hot pixel - */ - public void xtestVertexCrossesLine() { - - String wkt = "MULTILINESTRING ((2.2164917 48.8864136, 2.2175217 48.8867569), (2.2175217 48.8867569, 2.2182083 48.8874435), (2.2182083 48.8874435, 2.2161484 48.8853836))"; - String expected = null; - checkRounding(wkt, 1000000, expected); - } - - /** - * Currently fails, does not node correctly. - * - * FIXED by NOT rounding lines extracted by Overlay - */ - public void xtestVertexCrossesLine2() { - - String wkt = "MULTILINESTRING ((2.276916574988164 49.06082147500638, 2.2769165 49.0608215), (2.2769165 49.0608215, 2.2755432 49.0608215), (2.2762299 49.0615082, 2.276916574988164 49.06082147500638))"; - String expected = null; - checkRounding(wkt, 1000000, expected); - } - - /** - * Looks like a very short line is stretched between two grid points, - * and for some reason the node at one end is not inserted in a line snapped to it. - * - * FIXED by ensuring that HotPixel intersection tests whether segment - * endpoints lie inside pixel. - */ - public void xtestShortLineNodeNotAdded() { - - String wkt = "LINESTRING (2.1279144 48.8445282, 2.126884443750796 48.84555818124935, 2.1268845 48.8455582, 2.1268845 48.8462448)"; - String expected = "MULTILINESTRING ((2.127914 48.844528, 2.126885 48.845558), (2.126885 48.845558, 2.126884 48.845558), (2.126884 48.845558, 2.126885 48.845558), (2.126885 48.845558, 2.126885 48.846245))"; - checkRounding(wkt, 1000000, expected); - } - - /** - * An A vertex lies very close to a B segment. - * The vertex is snapped across the segment, but the segment is not noded. - * FIXED by adding intersection detection for near vertices to segments - */ - public void xtestNearVertexNotNoded() { - String wkt = "MULTILINESTRING ((2.4829102 48.8726807, 2.4830818249999997 48.873195575, 2.4839401 48.8723373), ( 2.4829102 48.8726807, 2.4832535 48.8737106 ))"; - String expected = null; - checkRounding(wkt, 100000000, expected); - } - - /** - * A vertex lies near interior of horizontal segment. - * Both are moved by rounding, and vertex ends up coincident with segment, - * but node is not created. - * This is very subtle, since because the segment is horizontal the vertex lies exactly on it - * and thus still reports as valid geometry (although a noding check reports failure). - * This is caused by the indexing used in Snap-rounding using exact envelopes. - * What is needed is a small expansion amount to ensure segments within snap distance are tested - * (in MCIndexNoder) - */ - public void xtestVertexNearHorizSegNotNoded() { - String wkt = "MULTILINESTRING (( 2.5096893 48.9530182, 2.50762932500455 48.95233152500091, 2.5055695 48.9530182 ), ( 2.5090027 48.9523315, 2.5035095 48.9523315 ))"; - String expected = null; - checkRounding(wkt, 1000000, expected); - } - - void checkRounding(String wkt, double scale, String expectedWKT) - { - Geometry geom = read(wkt); - PrecisionModel pm = new PrecisionModel(scale); - Noder noder = getSnapRounder(pm); - Geometry result = NodingTestUtil.nodeValidated(geom, null, noder); - - // only check if expected was provided - if (expectedWKT == null) return; - Geometry expected = read(expectedWKT); - checkEqual(expected, result); - } - -} From 2b29ad6c70ee747f050511f20d4826581a0bd2a0 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 19 Nov 2021 11:33:37 -0800 Subject: [PATCH 129/275] Fix SnapRoundingNoder to use tolerance in noding (#802) Signed-off-by: Martin Davis --- .../SnapRoundingIntersectionAdder.java | 20 ++++--------------- .../noding/snapround/SnapRoundingNoder.java | 17 +++++++++++++--- .../snapround/SnapRoundingNoderTest.java | 10 ++++++++++ .../GeometryPrecisionReducerTest.java | 13 ++++++++++++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java index 94e6a5cd14..22f8eba381 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingIntersectionAdder.java @@ -43,16 +43,9 @@ */ public class SnapRoundingIntersectionAdder implements SegmentIntersector -{ - /** - * The division factor used to determine - * nearness distance tolerance for interior intersection detection. - */ - private static final int NEARNESS_FACTOR = 100; - +{ private final LineIntersector li; private final List intersections; - private final PrecisionModel precModel; private final double nearnessTol; @@ -60,16 +53,11 @@ public class SnapRoundingIntersectionAdder * Creates an intersector which finds all snapped interior intersections, * and adds them as nodes. * - * @param pm the precision mode to use + * @param nearnessTol the intersection distance tolerance */ - public SnapRoundingIntersectionAdder(PrecisionModel pm) + public SnapRoundingIntersectionAdder(double nearnessTol) { - precModel = pm; - /** - * Nearness distance tolerance is a small fraction of the snap grid size - */ - double snapGridSize = 1.0 / precModel.getScale(); - nearnessTol = snapGridSize / NEARNESS_FACTOR; + this.nearnessTol = nearnessTol; /** * Intersections are detected and computed using full precision. diff --git a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java index 87ec99fb7b..6dc3543d82 100644 --- a/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java +++ b/modules/core/src/main/java/org/locationtech/jts/noding/snapround/SnapRoundingNoder.java @@ -57,6 +57,12 @@ public class SnapRoundingNoder implements Noder { + /** + * The division factor used to determine + * nearness distance tolerance for intersection detection. + */ + private static final int NEARNESS_FACTOR = 100; + private final PrecisionModel pm; private final HotPixelIndex pixelIndex; @@ -111,9 +117,14 @@ private List snapRound(Collection segStr */ private void addIntersectionPixels(Collection segStrings) { - SnapRoundingIntersectionAdder intAdder = new SnapRoundingIntersectionAdder(pm); - MCIndexNoder noder = new MCIndexNoder(); - noder.setSegmentIntersector(intAdder); + /** + * nearness tolerance is a small fraction of the grid size. + */ + double snapGridSize = 1.0 / pm.getScale(); + double nearnessTol = snapGridSize / NEARNESS_FACTOR; + + SnapRoundingIntersectionAdder intAdder = new SnapRoundingIntersectionAdder(nearnessTol); + MCIndexNoder noder = new MCIndexNoder(intAdder, nearnessTol); noder.computeNodes(segStrings); List intPts = intAdder.getIntersections(); pixelIndex.addNodes(intPts); diff --git a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java index e9f23def7a..305b512b49 100644 --- a/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/noding/snapround/SnapRoundingNoderTest.java @@ -199,6 +199,16 @@ public void testVertexNearHorizSegNotNoded() { checkRounding(wkt, 1000000, expected); } + /** + * Tests that MCIndexNoder tolerance is set correctly. + * See https://trac.osgeo.org/geos/ticket/1127 and https://github.com/libgeos/geos/pull/504 + */ + public void testMCIndexNoderTolerance() { + String wkt = "LINESTRING (3670939.6336634574 3396937.3777869204, 3670995.4715200397 3396926.0316904164, 3671077.280213823 3396905.4302639295, 3671203.8838707027 3396908.120176068, 3671334.962571111 3396904.8310892633, 3670037.299066126 3396904.8310892633, 3670037.299066126 3398075.9808747065, 3670939.6336634574 3396937.3777869204)"; + String expected = "MULTILINESTRING ((3670776.0631373483 3397212.0584320477, 3670776.0631373483 3396600.058421521), (3670776.0631373483 3396600.058421521, 3671388.063147875 3396600.058421521), (3671388.063147875 3396600.058421521, 3671388.063147875 3397212.0584320477), (3671388.063147875 3397212.0584320477, 3671388.063147875 3396600.058421521), (3671388.063147875 3396600.058421521, 3671388.063147875 3397212.0584320477), (3671388.063147875 3397212.0584320477, 3671388.063147875 3396600.058421521), (3671388.063147875 3396600.058421521, 3670776.0631373483 3396600.058421521), (3670776.0631373483 3396600.058421521, 3670164.063126822 3396600.058421521, 3670164.063126822 3397824.058442574, 3670776.0631373483 3397212.0584320477))"; + checkRounding(wkt, 0.0016339869, expected); + } + void checkRounding(String wkt, double scale, String expectedWKT) { Geometry geom = read(wkt); diff --git a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java index 5a560a9914..94712bc855 100644 --- a/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/precision/GeometryPrecisionReducerTest.java @@ -212,6 +212,19 @@ public void testKeepCollapsedMultiPolygon() "POLYGON ((2 2, 2 1, 1 1, 1 2, 2 2))"); } + //------------------------------------------------- + + /** + * Test issue showing bug in SnapRoundingNoder not passing tolerance to MCIndexNoder. + * + * See https://trac.osgeo.org/geos/ticket/1127 + */ + public void testLargeGridsizeFail() + { + checkReduce(1.0/612, "POLYGON((3670939.6336634574 3396937.3777869204, 3670995.4715200397 3396926.0316904164, 3671077.280213823 3396905.4302639295, 3671203.8838707027 3396908.120176068, 3671334.962571111 3396904.8310892633, 3670037.299066126 3396904.8310892633, 3670037.299066126 3398075.9808747065, 3670939.6336634574 3396937.3777869204))", + "POLYGON ((3670164 3396600, 3670164 3397824, 3670776 3397212, 3670776 3396600, 3670164 3396600))"); + } + //======================================= private void checkReduce( From bcdbbcf64a63661cf5c6e689c69036b8dbe363ba Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 19 Nov 2021 11:34:42 -0800 Subject: [PATCH 130/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 35c1c98576..d615ebb7da 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -40,6 +40,7 @@ Distributions for older JTS versions can be obtained at the * Fix `GeometryFixer` to appply `isKeepCollapsed` flag to `GeometryCollection` elements (#790) * Fix `RectangleIntersects` to handle XYZM geometry (#794) * Fix various operations to handle XYZM geometry (#795) +* Fix SnapRoundingNoder to use tolerance in noding (also fixes `GeometryPrecisionReducer`) (#802) # Version 1.18.2 From 1edcd237c8006cda6ad13f34cd769c2a5de9802c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 23 Nov 2021 13:29:21 -0800 Subject: [PATCH 131/275] Add TestBuilder GeometryInspector delete button Signed-off-by: Martin Davis --- .../jtstest/testbuilder/InspectorPanel.java | 48 +++++++------- .../geom/GeometryComponentDeleter.java | 62 +++++++++++++++++++ 2 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java index 7240e59a2d..6604ea643a 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java @@ -26,6 +26,8 @@ import javax.swing.JPanel; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jtstest.testbuilder.controller.JTSTestBuilderController; +import org.locationtech.jtstest.testbuilder.geom.GeometryComponentDeleter; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; @@ -75,44 +77,31 @@ protected void uiInit() { geomTreePanel.setPreferredSize(new Dimension(300, 500)); this.add(geomTreePanel, BorderLayout.CENTER); - btnZoom.setEnabled(true); - btnZoom.setMaximumSize(new Dimension(30, 26)); - //btnZoom.setText("Zoom"); - btnZoom.setIcon(zoomIcon); - btnZoom.setToolTipText("Zoom to component"); - btnZoom.addActionListener(new java.awt.event.ActionListener() { + JButton btnZoom = SwingUtil.createButton(AppIcons.ZOOM, "Zoom to component", new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnZoom_actionPerformed(e); } }); - btnCopy.setEnabled(true); - btnCopy.setMaximumSize(new Dimension(30, 30)); - //btnCopy.setText("Copy"); - btnCopy.setIcon(copyIcon); - btnCopy.setToolTipText("Copy (Ctl-click to copy formatted"); - btnCopy.addActionListener(new java.awt.event.ActionListener() { + JButton btnCopy = SwingUtil.createButton(AppIcons.COPY, "Copy (Ctl-click to copy formatted", new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnCopy_actionPerformed(e); } }); - btnNext.setEnabled(true); - btnNext.setMaximumSize(new Dimension(30, 30)); - btnNext.setToolTipText("Zoom to Next"); - btnNext.setIcon(downIcon); - btnNext.addActionListener(new java.awt.event.ActionListener() { + JButton btnNext = SwingUtil.createButton(AppIcons.DOWN, "Zoom to Next", new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnZoomNext_actionPerformed(e, 1); } }); - btnPrev.setEnabled(true); - btnPrev.setMaximumSize(new Dimension(30, 30)); - btnPrev.setToolTipText("Zoom to Previous"); - btnPrev.setIcon(upIcon); - btnPrev.addActionListener(new java.awt.event.ActionListener() { + JButton btnPrev = SwingUtil.createButton(AppIcons.UP, "Zoom to Previous", new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { btnZoomNext_actionPerformed(e, -1); } }); + JButton btnDelete = SwingUtil.createButton(AppIcons.DELETE, "Delete", new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + deleteGeom(); + } + }); lblGeom.setFont(new java.awt.Font("Dialog", 1, 16)); lblGeom.setText(" "); @@ -131,6 +120,7 @@ public void actionPerformed(ActionEvent e) { btnPanel.add(btnNext); btnPanel.add(Box.createRigidArea(new Dimension(0, BOX_SPACER))); btnPanel.add(btnCopy); + btnPanel.add(btnDelete); this.add(btnPanel, BorderLayout.WEST); if (showExpand) { @@ -199,6 +189,14 @@ private void btnCopy_actionPerformed(ActionEvent e) { SwingUtil.copyToClipboard(geom, isFormatted); } + private void deleteGeom() { + Geometry geomComp = geomTreePanel.getSelectedGeometry(); + if (geomComp == null) return; + Geometry geomEdit = GeometryComponentDeleter.deleteComponent(geometry, geomComp); + JTSTestBuilderController.model().getGeometryEditModel().setGeometry(source, geomEdit); + updateGeometry(geomEdit); + } + public void setGeometry(String tag, Geometry geom, int source) { this.source = source; @@ -210,6 +208,12 @@ public void setGeometry(String tag, Geometry geom, int source) sortNone(); } + private void updateGeometry(Geometry geom) + { + this.geometry = geom; + geomTreePanel.populate(geometry, source); + } + public void sortNone() { sorterLen = null; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java new file mode 100644 index 0000000000..8e1f602735 --- /dev/null +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +package org.locationtech.jtstest.testbuilder.geom; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.util.GeometryEditor; + +/** + * Deletes a component from a geometry. + * + * @author Martin Davis + * + */ +public class GeometryComponentDeleter +{ + + public static Geometry deleteComponent(Geometry geom, Geometry component) + { + GeometryEditor editor = new GeometryEditor(); + DeleteComponentOperation compOp = new DeleteComponentOperation(component); + Geometry compEditGeom = editor.edit(geom, compOp); + if (compOp.isEdited()) return compEditGeom; + return geom; + } + + private static class DeleteComponentOperation + implements GeometryEditor.GeometryEditorOperation + { + private Geometry component; + private boolean isEdited = false; + + public DeleteComponentOperation(Geometry component) + { + this.component = component; + } + + public boolean isEdited() { return isEdited; } + + public Geometry edit(Geometry geometry, GeometryFactory factory) + { + if (geometry == component) { + isEdited = true; + return null; + } + return geometry; + } + + } + + +} From 81673bfa17a82292cc25b766df7049d1ddcaa6c4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 23 Nov 2021 13:36:29 -0800 Subject: [PATCH 132/275] Rename TestBuilder DeleteByBoxTool Signed-off-by: Martin Davis --- .../controller/JTSTestBuilderController.java | 4 ++-- .../testbuilder/geom/GeometryComponentDeleter.java | 2 +- .../{DeleteVertexTool.java => DeleteByBoxTool.java} | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) rename modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/{DeleteVertexTool.java => DeleteByBoxTool.java} (85%) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java index 6b0330b750..1e27ce7977 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/controller/JTSTestBuilderController.java @@ -31,7 +31,7 @@ import org.locationtech.jtstest.testbuilder.ui.ImageUtil; import org.locationtech.jtstest.testbuilder.ui.SwingUtil; import org.locationtech.jtstest.testbuilder.ui.render.ViewStyle; -import org.locationtech.jtstest.testbuilder.ui.tools.DeleteVertexTool; +import org.locationtech.jtstest.testbuilder.ui.tools.DeleteByBoxTool; import org.locationtech.jtstest.testbuilder.ui.tools.EditVertexTool; import org.locationtech.jtstest.testbuilder.ui.tools.ExtractComponentTool; import org.locationtech.jtstest.testbuilder.ui.tools.InfoTool; @@ -261,7 +261,7 @@ public void modeExtractComponent() { } public void modeDeleteVertex() { - setTool(DeleteVertexTool.getInstance()); + setTool(DeleteByBoxTool.getInstance()); } public void modeEditVertex() { setTool(EditVertexTool.getInstance()); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java index 8e1f602735..5df0488692 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryComponentDeleter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Vivid Solutions. + * Copyright (c) 2021 Martin Davis. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteVertexTool.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteByBoxTool.java similarity index 85% rename from modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteVertexTool.java rename to modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteByBoxTool.java index 1b5df208ba..c87dc030af 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteVertexTool.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/tools/DeleteByBoxTool.java @@ -20,16 +20,16 @@ * Deletes vertices or components within a selection box from a geometry component * @version 1.7 */ -public class DeleteVertexTool extends BoxBandTool { - private static DeleteVertexTool singleton = null; +public class DeleteByBoxTool extends BoxBandTool { + private static DeleteByBoxTool singleton = null; - public static DeleteVertexTool getInstance() { + public static DeleteByBoxTool getInstance() { if (singleton == null) - singleton = new DeleteVertexTool(); + singleton = new DeleteByBoxTool(); return singleton; } - private DeleteVertexTool() { + private DeleteByBoxTool() { super(); } From 9b6741f8a831a071bf89ca5440059f1bef9d176c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 24 Nov 2021 10:48:19 -0800 Subject: [PATCH 133/275] Make TestBuilder Inspector read-only for Result Signed-off-by: Martin Davis --- .../testbuilder/GeometryInspectorDialog.java | 7 ++----- .../jtstest/testbuilder/InspectorPanel.java | 21 +++++++------------ .../testbuilder/JTSTestBuilderFrame.java | 8 +++---- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java index 8eaae8908a..b8be2e6a0e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryInspectorDialog.java @@ -57,10 +57,7 @@ void initUI() throws Exception public void setGeometry(int geomIndex, Geometry geometry) { String tag = geomIndex == 0 ? AppStrings.GEOM_LABEL_A : AppStrings.GEOM_LABEL_B; - inspectPanel.setGeometry(tag, geometry, 0); - } - - public void setGeometry(String tag, Geometry geometry) { - inspectPanel.setGeometry(tag, geometry, 0); + inspectPanel.setGeometry(tag, geometry, geomIndex, false); } + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java index 6604ea643a..4aafe9e1bd 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/InspectorPanel.java @@ -35,18 +35,10 @@ public class InspectorPanel extends TestBuilderPanel { private static final int BOX_SPACER = 5; - private static final ImageIcon downIcon = AppIcons.DOWN; - private static final ImageIcon upIcon = AppIcons.UP; - private static final ImageIcon zoomIcon = AppIcons.ZOOM; - private static final ImageIcon copyIcon = AppIcons.COPY; - GeometryTreePanel geomTreePanel; - JButton btnZoom = new JButton(); - JButton btnCopy = new JButton(); - JButton btnNext = new JButton(); - JButton btnPrev = new JButton(); - JButton btnExpand = new JButton(); + private JButton btnExpand = new JButton(); + private JButton btnDelete; JLabel lblGeom = new JLabel(); @@ -97,7 +89,7 @@ public void actionPerformed(ActionEvent e) { btnZoomNext_actionPerformed(e, -1); } }); - JButton btnDelete = SwingUtil.createButton(AppIcons.DELETE, "Delete", new java.awt.event.ActionListener() { + btnDelete = SwingUtil.createButton(AppIcons.DELETE, "Delete", new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { deleteGeom(); } @@ -197,11 +189,12 @@ private void deleteGeom() { updateGeometry(geomEdit); } - public void setGeometry(String tag, Geometry geom, int source) + public void setGeometry(String tag, Geometry geom, int source, boolean isEditable) { this.source = source; this.geometry = geom; - + + btnDelete.setEnabled(isEditable); lblGeom.setText(tag); lblGeom.setForeground(source == 0 ? Color.BLUE : Color.RED); @@ -245,5 +238,7 @@ public void sortByLen() } geomTreePanel.populate(geometry, source, sorterLen); } + + } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java index 8d20151558..3c4f31bb08 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java @@ -328,18 +328,18 @@ public void inspectResult() Object currResult = tbModel.getResult(); if (! (currResult instanceof Geometry)) return; - inspectGeometry((Geometry) currResult, 0, "R"); + inspectGeometry((Geometry) currResult, 0, "R", false); } public void inspectGeometry() { int geomIndex = tbModel.getGeometryEditModel().getGeomIndex(); String tag = geomIndex == 0 ? AppStrings.GEOM_LABEL_A : AppStrings.GEOM_LABEL_B; Geometry geometry = currentCase().getGeometry(geomIndex); - inspectGeometry(geometry, geomIndex, tag); + inspectGeometry(geometry, geomIndex, tag, true); } - private void inspectGeometry(Geometry geometry, int geomIndex, String tag) { - inspectPanel.setGeometry( tag, geometry, geomIndex); + private void inspectGeometry(Geometry geometry, int geomIndex, String tag, boolean isEditable) { + inspectPanel.setGeometry( tag, geometry, geomIndex, isEditable); showTab(AppStrings.TAB_LABEL_INSPECT); } From 963738d22aa74c367ef09c893a136b9daf84541b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 24 Nov 2021 11:34:03 -0800 Subject: [PATCH 134/275] Allow specifying a PrecisionModel grid size (#804) Signed-off-by: Martin Davis --- .../locationtech/jts/geom/PrecisionModel.java | 78 +++++++++++++++---- .../precision/GeometryPrecisionReducer.java | 1 - .../GeometryPrecisionReducerTest.java | 9 ++- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java index 534aac4be3..f8ab294125 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/PrecisionModel.java @@ -19,30 +19,29 @@ /** * Specifies the precision model of the {@link Coordinate}s in a {@link Geometry}. - * In other words, specifies the grid of allowable - * points for all Geometrys. + * In other words, specifies the grid of allowable points for a Geometry. + * A precision model may be floating ({@link #FLOATING} or {@link #FLOATING_SINGLE}), + * in which case normal floating-point value semantics apply. *

    - * The {@link #makePrecise(Coordinate)} method allows rounding a coordinate to + * For a {@link #FIXED} precision model the {@link #makePrecise(Coordinate)} method allows rounding a coordinate to * a "precise" value; that is, one whose * precision is known exactly. *

    * Coordinates are assumed to be precise in geometries. * That is, the coordinates are assumed to be rounded to the * precision model given for the geometry. - * JTS input routines automatically round coordinates to the precision model - * before creating Geometries. * All internal operations * assume that coordinates are rounded to the precision model. * Constructive methods (such as boolean operations) always round computed * coordinates to the appropriate precision model. *

    - * Currently three types of precision model are supported: + * Three types of precision model are supported: *

    + * Offset curves support setting the number of quadrant segments, + * the join style, and the mitre limit (if applicable) via + * the {@link BufferParameters}. * * @author Martin Davis * @@ -61,10 +65,10 @@ public class OffsetCurve { private static final int NEARNESS_FACTOR = 10000; /** - * Computes the offset curve of a linear geometry. + * Computes the offset curve of a geometry at a given distance. * - * @param geom a linear geometry - * @param distance the offset curve distance + * @param geom a geometry + * @param distance the offset distance (positive = left, negative = right) * @return the offset curve */ public static Geometry getCurve(Geometry geom, double distance) { @@ -72,16 +76,69 @@ public static Geometry getCurve(Geometry geom, double distance) { return oc.getCurve(); } + /** + * Computes the offset curve of a geometry at a given distance, + * and for a specified quadrant segments, join style and mitre limit. + * + * @param geom a geometry + * @param distance the offset distance (positive = left, negative = right) + * @param quadSegs the quadrant segments (-1 for default) + * @param joinStyle the join style (-1 for default) + * @param mitreLimit the mitre limit (-1 for default) + * @return the offset curve + */ + public static Geometry getCurve(Geometry geom, double distance, int quadSegs, int joinStyle, double mitreLimit) { + BufferParameters bufferParams = new BufferParameters(); + if (quadSegs >= 0) bufferParams.setQuadrantSegments(quadSegs); + if (joinStyle >= 0) bufferParams.setJoinStyle(joinStyle); + if (mitreLimit >= 0) bufferParams.setMitreLimit(mitreLimit); + OffsetCurve oc = new OffsetCurve(geom, distance, bufferParams); + return oc.getCurve(); + } + private Geometry inputGeom; private double distance; + private BufferParameters bufferParams; private double matchDistance; private GeometryFactory geomFactory; + /** + * Creates a new instance for computing an offset curve for a geometryat a given distance. + * with default quadrant segments ({@link BufferParameters#DEFAULT_QUADRANT_SEGMENTS}) + * and join style ({@link BufferParameters#JOIN_STYLE}). + * + * @param geom the geometry to offset + * @param distance the offset distance (positive = left, negative = right) + * + * @see BufferParameters + */ public OffsetCurve(Geometry geom, double distance) { + this(geom, distance, null); + } + + /** + * Creates a new instance for computing an offset curve for a geometry at a given distance. + * allowing the quadrant segments and join style and mitre limit to be set + * via {@link BufferParameters}. + * + * @param geom + * @param distance + * @param bufParams + */ + public OffsetCurve(Geometry geom, double distance, BufferParameters bufParams) { this.inputGeom = geom; this.distance = distance; + matchDistance = Math.abs(distance) / NEARNESS_FACTOR; geomFactory = inputGeom.getFactory(); + + //-- make new buffer params since the end cap style must be the default + this.bufferParams = new BufferParameters(); + if (bufParams != null) { + bufferParams.setQuadrantSegments(bufParams.getQuadrantSegments()); + bufferParams.setJoinStyle(bufParams.getJoinStyle()); + bufferParams.setMitreLimit(bufParams.getMitreLimit()); + } } /** @@ -119,18 +176,21 @@ private Geometry toLineString(Geometry geom) { /** * Gets the raw offset line. - * This may contain loops and other artifacts which are - * not present in the actual offset curve. - * The raw offset line is used to extract the offset curve - * by matching it to a buffer ring (which is clean). + * The quadrant segments and join style and mitre limit to be set + * via {@link BufferParameters}. + *

    + * The raw offset line may contain loops and other artifacts which are + * not present in the true offset curve. + * The raw offset line is matched to the buffer ring (which is clean) + * to extract the offset curve. * * @param geom the linestring to offset * @param distance the offset distance + * @param bufParams the buffer parameters to use * @return the raw offset line */ - public static Coordinate[] rawOffset(LineString geom, double distance) + public static Coordinate[] rawOffset(LineString geom, double distance, BufferParameters bufParams) { - BufferParameters bufParams = new BufferParameters(); OffsetCurveBuilder ocb = new OffsetCurveBuilder( geom.getFactory().getPrecisionModel(), bufParams ); @@ -138,6 +198,18 @@ public static Coordinate[] rawOffset(LineString geom, double distance) return pts; } + /** + * Gets the raw offset line, with default buffer parameters. + * + * @param geom the linestring to offset + * @param distance the offset distance + * @return the raw offset line + */ + public static Coordinate[] rawOffset(LineString geom, double distance) + { + return rawOffset(geom, distance, new BufferParameters()); + } + private LineString computeCurve(LineString lineGeom, double distance) { //-- first handle special/simple cases if (lineGeom.getNumPoints() < 2 || lineGeom.getLength() == 0.0) { @@ -147,7 +219,7 @@ private LineString computeCurve(LineString lineGeom, double distance) { return offsetSegment(lineGeom.getCoordinates(), distance); } - Coordinate[] rawOffset = rawOffset(lineGeom, distance); + Coordinate[] rawOffset = rawOffset(lineGeom, distance, bufferParams); if (rawOffset.length == 0) { return geomFactory.createLineString(); } @@ -159,7 +231,7 @@ private LineString computeCurve(LineString lineGeom, double distance) { * so not doing this. */ - Polygon bufferPoly = getBufferOriented(lineGeom, distance); + Polygon bufferPoly = getBufferOriented(lineGeom, distance, bufferParams); //-- first try matching shell to raw curve Coordinate[] shell = bufferPoly.getExteriorRing().getCoordinates(); @@ -179,8 +251,8 @@ private LineString offsetSegment(Coordinate[] pts, double distance) { return geomFactory.createLineString(new Coordinate[] { offsetSeg.p0, offsetSeg.p1 }); } - private static Polygon getBufferOriented(LineString geom, double distance) { - Geometry buffer = geom.buffer(Math.abs(distance)); + private static Polygon getBufferOriented(LineString geom, double distance, BufferParameters bufParams) { + Geometry buffer = BufferOp.bufferOp(geom, Math.abs(distance), bufParams); Polygon bufferPoly = extractMaxAreaPolygon(buffer); //-- for negative distances (Right of input) reverse buffer direction to match offset curve if (distance < 0) { diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/OffsetCurveTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/OffsetCurveTest.java index 6d0bb25e30..93158d070c 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/OffsetCurveTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/OffsetCurveTest.java @@ -165,10 +165,45 @@ public void testPolygonWithHole() { } + //--------------------------------------- + + public void testQuadSegs() { + checkOffsetCurve( + "LINESTRING (20 20, 50 50, 80 20)", + 10, 2, -1, -1, + "LINESTRING (12.93 27.07, 42.93 57.07, 50 60, 57.07 57.07, 87.07 27.07)" + ); + } + + public void testJoinBevel() { + checkOffsetCurve( + "LINESTRING (20 20, 50 50, 80 20)", + 10, -1, BufferParameters.JOIN_BEVEL, -1, + "LINESTRING (12.93 27.07, 42.93 57.07, 57.07 57.07, 87.07 27.07)" + ); + } + + public void testJoinMitre() { + checkOffsetCurve( + "LINESTRING (20 20, 50 50, 80 20)", + 10, -1, BufferParameters.JOIN_MITRE, -1, + "LINESTRING (12.93 27.07, 50 64.14, 87.07 27.07)" + ); + } + + //======================================= + private void checkOffsetCurve(String wkt, double distance, String wktExpected) { checkOffsetCurve(wkt, distance, wktExpected, 0.05); } + private void checkOffsetCurve(String wkt, double distance, + int quadSegs, int joinStyle, double mitreLimit, + String wktExpected) + { + checkOffsetCurve(wkt, distance, quadSegs, joinStyle, mitreLimit, wktExpected, 0.05); + } + private void checkOffsetCurve(String wkt, double distance, String wktExpected, double tolerance) { Geometry geom = read(wkt); Geometry result = OffsetCurve.getCurve(geom, distance); @@ -180,4 +215,19 @@ private void checkOffsetCurve(String wkt, double distance, String wktExpected, d Geometry expected = read(wktExpected); checkEqual(expected, result, tolerance); } + + private void checkOffsetCurve(String wkt, double distance, + int quadSegs, int joinStyle, double mitreLimit, + String wktExpected, double tolerance) { + Geometry geom = read(wkt); + Geometry result = OffsetCurve.getCurve(geom, distance, quadSegs, joinStyle, mitreLimit); + //System.out.println(result); + + if (wktExpected == null) + return; + + Geometry expected = read(wktExpected); + checkEqual(expected, result, tolerance); + } + } From 0149c7e8702ce7596ba52f10e3b2591566c2a8c0 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 10 Dec 2021 09:59:38 -0800 Subject: [PATCH 156/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index c4b76a6aad..fda598010d 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -33,7 +33,7 @@ Distributions for older JTS versions can be obtained at the * Add `GeometryFixer` option to preserve `Multi` geometry types when collapses occur (#791) * Make `QuadTree` thread-safe (#792) * Allow specifying a fixed `PrecisionModel` via grid size (#804) -* Add `OffsetCurve` class (#810) +* Add `OffsetCurve` class (#810, #816) ### Bug Fixes From 9442116e13207470a01b4e03620830af42ce96fd Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 14:48:16 -0800 Subject: [PATCH 157/275] Fix the buffer generated for mitred joins. (#818) Signed-off-by: Martin Davis --- .../buffer/OffsetSegmentGenerator.java | 161 ++++++++++++------ .../operation/buffer/BufferParameterTest.java | 89 +++++++++- 2 files changed, 194 insertions(+), 56 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index 72cfd0a726..6666419a6c 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -12,6 +12,7 @@ package org.locationtech.jts.operation.buffer; import org.locationtech.jts.algorithm.Angle; +import org.locationtech.jts.algorithm.Distance; import org.locationtech.jts.algorithm.Intersection; import org.locationtech.jts.algorithm.LineIntersector; import org.locationtech.jts.algorithm.Orientation; @@ -443,101 +444,152 @@ public void addLineEndCap(Coordinate p0, Coordinate p1) } } + /** - * Adds a mitre join connecting the two reflex offset segments. - * The mitre will be beveled if it exceeds the mitre ratio limit. + * Adds a mitre join connecting two convex offset segments. + * The mitre is beveled if it exceeds the mitre limit factor. + * The mitre limit is intended to prevent extremely long corners occurring. + * If the mitre limit is very small it can cause unwanted artifacts around fairly flat corners. + * This is prevented by using a simple bevel join in this case. + * In other words, the limit prevents the corner from getting too long, + * but it won't force it to be very short/flat. * * @param offset0 the first offset segment * @param offset1 the second offset segment * @param distance the offset distance */ - private void addMitreJoin(Coordinate p, + private void addMitreJoin(Coordinate cornerPt, LineSegment offset0, LineSegment offset1, double distance) { + double mitreLimitDistance = bufParams.getMitreLimit() * distance; /** - * This computation is unstable if the offset segments are nearly collinear. + * First try a non-beveled join. + * Compute the intersection point of the lines determined by the offsets. + * Parallel or collinear lines will return a null point ==> need to be beveled + * + * Note: This computation is unstable if the offset segments are nearly collinear. * However, this situation should have been eliminated earlier by the check * for whether the offset segment endpoints are almost coincident */ Coordinate intPt = Intersection.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1); - if (intPt != null) { - double mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance); - if (mitreRatio <= bufParams.getMitreLimit()) { + if (intPt != null && intPt.distance(cornerPt) <= mitreLimitDistance) { segList.addPt(intPt); return; - } } - // at this point either intersection failed or mitre limit was exceeded - addLimitedMitreJoin(offset0, offset1, distance, bufParams.getMitreLimit()); -// addBevelJoin(offset0, offset1); + /** + * In case the mitre limit is very small, try a plain bevel. + * Use it if it's further than the limit. + */ + double bevelDist = Distance.pointToSegment(cornerPt, offset0.p1, offset1.p0); + if (bevelDist >= mitreLimitDistance) { + addBevelJoin(offset0, offset1); + return; + } + /** + * Have to construct a limited mitre bevel. + */ + addLimitedMitreJoin(offset0, offset1, distance, mitreLimitDistance); } - /** - * Adds a limited mitre join connecting the two reflex offset segments. - * A limited mitre is a mitre which is beveled at the distance - * determined by the mitre ratio limit. + * Adds a limited mitre join connecting two convex offset segments. + * A limited mitre join is beveled at the distance + * determined by the mitre limit factor, + * or as a standard bevel join, whichever is further. * * @param offset0 the first offset segment * @param offset1 the second offset segment * @param distance the offset distance - * @param mitreLimit the mitre limit ratio + * @param mitreLimitDistance the mitre limit distance */ private void addLimitedMitreJoin( LineSegment offset0, LineSegment offset1, double distance, - double mitreLimit) + double mitreLimitDistance) { - Coordinate basePt = seg0.p1; - - double ang0 = Angle.angle(basePt, seg0.p0); - - // oriented angle between segments - double angDiff = Angle.angleBetweenOriented(seg0.p0, basePt, seg1.p1); + Coordinate cornerPt = seg0.p1; + // oriented angle of the corner formed by segments + double angInterior = Angle.angleBetweenOriented(seg0.p0, cornerPt, seg1.p1); // half of the interior angle - double angDiffHalf = angDiff / 2; + double angInterior2 = angInterior / 2; - // angle for bisector of the interior angle between the segments - double midAng = Angle.normalize(ang0 + angDiffHalf); - // rotating this by PI gives the bisector of the reflex angle - double mitreMidAng = Angle.normalize(midAng + Math.PI); + // direction of bisector of the interior angle between the segments + double dir0 = Angle.angle(cornerPt, seg0.p0); + double dirBisector = Angle.normalize(dir0 + angInterior2); + // rotating by PI gives the bisector of the outside angle, + // which is the direction of the bevel midpoint from the corner apex + double dirBisectorOut = Angle.normalize(dirBisector + Math.PI); - // the miterLimit determines the distance to the mitre bevel - double mitreDist = mitreLimit * distance; - // the bevel delta is the difference between the buffer distance - // and half of the length of the bevel segment - double bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf)); - double bevelHalfLen = distance - bevelDelta; - // compute the midpoint of the bevel segment - double bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng); - double bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng); - Coordinate bevelMidPt = new Coordinate(bevelMidX, bevelMidY); + Coordinate bevelMidPt = project(cornerPt, mitreLimitDistance, dirBisectorOut); - // compute the mitre midline segment from the corner point to the bevel segment midpoint - LineSegment mitreMidLine = new LineSegment(basePt, bevelMidPt); + // slope angle of bevel segment + double dirBevel = Angle.normalize(dirBisectorOut + Math.PI/2.0); - // finally the bevel segment endpoints are computed as offsets from - // the mitre midline - Coordinate bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen); - Coordinate bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen); + // compute the candidate bevel segment by projecting both sides of the midpoint + Coordinate bevel0 = project(bevelMidPt, distance, dirBevel); + Coordinate bevel1 = project(bevelMidPt, distance, dirBevel + Math.PI); + LineSegment bevel = new LineSegment(bevel0, bevel1); - if (side == Position.LEFT) { - segList.addPt(bevelEndLeft); - segList.addPt(bevelEndRight); - } - else { - segList.addPt(bevelEndRight); - segList.addPt(bevelEndLeft); + //-- compute intersections with extended offset segments + double extendLen = mitreLimitDistance < distance ? distance : mitreLimitDistance; + + LineSegment extend0 = extend(offset0, 2 * extendLen); + LineSegment extend1 = extend(offset1, -2 * extendLen); + Coordinate bevelInt0 = bevel.intersection(extend0); + Coordinate bevelInt1 = bevel.intersection(extend1); + + //-- add the limited bevel, if it intersects the offsets + if (bevelInt0 != null && bevelInt1 != null) { + segList.addPt(bevelInt0); + segList.addPt(bevelInt1); + return; } + /** + * If the corner is very flat or the mitre limit is very small + * the limited bevel segment may not intersect the offsets. + * In this case just bevel the join. + */ + addBevelJoin(offset0, offset1); + } + + /** + * Extends a line segment forwards or backwards a given distance. + * + * @param seg the base line segment + * @param dist the distance to extend by + * @return the extended segment + */ + private static LineSegment extend(LineSegment seg, double dist) { + double distFrac = Math.abs(dist) / seg.getLength(); + double segFrac = dist >= 0 ? 1 + distFrac : 0 - distFrac; + Coordinate extendPt = seg.pointAlong(segFrac); + if (dist > 0) + return new LineSegment(seg.p0, extendPt); + else + return new LineSegment(extendPt, seg.p1); } /** - * Adds a bevel join connecting the two offset segments - * around a reflex corner. + * Projects a point to a given distance in a given direction angle. + * + * @param pt the point to project + * @param d the projection distance + * @param dir the direction angle (in radians) + * @return the projected point + */ + private static Coordinate project(Coordinate pt, double d, double dir) { + double x = pt.x + d * Math.cos(dir); + double y = pt.y + d * Math.sin(dir); + return new Coordinate(x, y); + } + + /** + * Adds a bevel join connecting two offset segments + * around a convex corner. * * @param offset0 the first offset segment * @param offset1 the second offset segment @@ -550,9 +602,8 @@ private void addBevelJoin( segList.addPt(offset1.p0); } - /** - * Add points for a circular fillet around a reflex corner. + * Add points for a circular fillet around a convex corner. * Adds the start and end points * * @param p base point of curve diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java index 6b94aadc2c..fd38b6df14 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferParameterTest.java @@ -50,6 +50,81 @@ public void testQuadSegs2Bevel() { "POLYGON ((70 30, 70 80, 72.92893218813452 87.07106781186548, 80 90, 87.07106781186548 87.07106781186548, 90 80, 90 20, 80 10, 20 10, 12.928932188134523 12.928932188134524, 10 20, 12.928932188134524 27.071067811865476, 20 30, 70 30))"); } + //---------------------------------------------------- + + public void testMitreRight0() { + checkBuffer("LINESTRING (20 20, 20 80, 80 80)", + 10.0, bufParamFlatMitre(0), + "POLYGON ((10 80, 20 90, 80 90, 80 70, 30 70, 30 20, 10 20, 10 80))"); + } + + public void testMitreRight1() { + checkBuffer("LINESTRING (20 20, 20 80, 80 80)", + 10.0, bufParamFlatMitre(1), + "POLYGON ((10 20, 10 84.14213562373095, 15.857864376269049 90, 80 90, 80 70, 30 70, 30 20, 10 20))"); + } + + public void testMitreRight2() { + checkBuffer("LINESTRING (20 20, 20 80, 80 80)", + 10.0, bufParamFlatMitre(2), + "POLYGON ((10 20, 10 90, 80 90, 80 70, 30 70, 30 20, 10 20))"); + } + + public void testMitreNarrow0() { + checkBuffer("LINESTRING (10 20, 20 80, 30 20)", + 10.0, bufParamFlatMitre(0), + "POLYGON ((10.136060761678563 81.64398987305357, 29.863939238321436 81.64398987305357, 39.863939238321436 21.643989873053574, 20.136060761678564 18.356010126946426, 20 19.172374697017812, 19.863939238321436 18.356010126946426, 0.1360607616785625 21.643989873053574, 10.136060761678563 81.64398987305357))"); + } + + public void testMitreNarrow1() { + checkBuffer("LINESTRING (10 20, 20 80, 30 20)", + 10.0, bufParamFlatMitre(1), + "POLYGON ((11.528729116169634 90, 28.47127088383036 90, 39.863939238321436 21.643989873053574, 20.136060761678564 18.356010126946426, 20 19.172374697017812, 19.863939238321436 18.356010126946426, 0.1360607616785625 21.643989873053574, 11.528729116169634 90))"); + } + + public void testMitreNarrow5() { + checkBuffer("LINESTRING (10 20, 20 80, 30 20)", + 10.0, bufParamFlatMitre(5), + "POLYGON ((18.1953957828363 130, 21.804604217163696 130, 39.863939238321436 21.643989873053574, 20.136060761678564 18.356010126946426, 20 19.172374697017812, 19.863939238321436 18.356010126946426, 0.1360607616785625 21.643989873053574, 18.1953957828363 130))"); + } + + public void testMitreNarrow10() { + checkBuffer("LINESTRING (10 20, 20 80, 30 20)", + 10.0, bufParamFlatMitre(10), + "POLYGON ((20 140.82762530298217, 39.863939238321436 21.643989873053574, 20.136060761678564 18.356010126946426, 20 19.172374697017812, 19.863939238321436 18.356010126946426, 0.1360607616785625 21.643989873053574, 20 140.82762530298217))"); + } + + public void testMitreObtuse0() { + checkBuffer("LINESTRING (10 10, 50 20, 90 10)", + 1.0, bufParamFlatMitre(0), + "POLYGON ((49.75746437496367 20.970142500145332, 50.24253562503633 20.970142500145332, 90.24253562503634 10.970142500145332, 89.75746437496366 9.029857499854668, 50 18.969223593595583, 10.242535625036332 9.029857499854668, 9.757464374963668 10.970142500145332, 49.75746437496367 20.970142500145332))"); + } + + public void testMitreObtuse1() { + checkBuffer("LINESTRING (10 10, 50 20, 90 10)", + 1.0, bufParamFlatMitre(1), + "POLYGON ((9.757464374963668 10.970142500145332, 49.876894374382324 21, 50.12310562561766 20.999999999999996, 90.24253562503634 10.970142500145332, 89.75746437496366 9.029857499854668, 50 18.969223593595583, 10.242535625036332 9.029857499854668, 9.757464374963668 10.970142500145332))"); + } + + public void testMitreObtuse2() { + checkBuffer("LINESTRING (10 10, 50 20, 90 10)", + 1.0, bufParamFlatMitre(2), + "POLYGON ((50 21.030776406404417, 90.24253562503634 10.970142500145332, 89.75746437496366 9.029857499854668, 50 18.969223593595583, 10.242535625036332 9.029857499854668, 9.757464374963668 10.970142500145332, 50 21.030776406404417))"); + } + + //---------------------------------------------------- + + public void testMitreSquareCCW1() { + checkBuffer("POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))", + 10.0, bufParamFlatMitre(1), + "POLYGON ((-10 -4.142135623730949, -10 104.14213562373095, -4.142135623730949 110, 104.14213562373095 110, 110 104.14213562373095, 110 -4.142135623730949, 104.14213562373095 -10, -4.142135623730949 -10, -10 -4.142135623730949))"); + } + + public void testMitreSquare1() { + checkBuffer("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))", + 10.0, bufParamFlatMitre(1), + "POLYGON ((-4.14213562373095 -10, -10 -4.14213562373095, -10 104.14213562373095, -4.14213562373095 110, 104.14213562373095 110, 110 104.14213562373095, 110 -4.142135623730951, 104.14213562373095 -10, -4.14213562373095 -10))"); + } private void checkBuffer(String wkt, double dist, int quadSegs, String wktExpected) { checkBuffer( wkt, dist, quadSegs, BufferParameters.JOIN_ROUND, wktExpected); @@ -59,9 +134,21 @@ private void checkBuffer(String wkt, double dist, int quadSegs, int joinStyle, S BufferParameters param = new BufferParameters(); param.setQuadrantSegments(quadSegs); param.setJoinStyle(joinStyle); + checkBuffer(wkt, dist, param, wktExpected); + } + + private void checkBuffer(String wkt, double dist, BufferParameters param, String wktExpected) { Geometry geom = read(wkt); Geometry result = BufferOp.bufferOp(geom, dist, param); Geometry expected = read(wktExpected); - checkEqual(expected, result); + checkEqual(expected, result, 0.00001); + } + + private static BufferParameters bufParamFlatMitre(double mitreLimit) { + BufferParameters param = new BufferParameters(); + param.setJoinStyle(BufferParameters.JOIN_MITRE); + param.setMitreLimit(mitreLimit); + param.setEndCapStyle(BufferParameters.CAP_FLAT); + return param; } } From 460d6a227c2f627ad500f295ef0e00033c5bd8b3 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 14:49:53 -0800 Subject: [PATCH 158/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index fda598010d..15322c905b 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -45,6 +45,7 @@ Distributions for older JTS versions can be obtained at the * Fix `SnapRoundingNoder` to use tolerance in noding (also fixes `GeometryPrecisionReducer`) (#802) * Fix `MaximumInscribedCircle` to avoid infinite-looping on flat collapsed input (#807) * Add OverlayNG result area heuristic check (#812) +* Fix the buffer generated for Mitred Joins (#818) # Version 1.18.2 From ef9d9f65cc7376718226689a1ff7f31c2c9ca15e Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 15:56:08 -0800 Subject: [PATCH 159/275] Add IntersectionLineSegment function (#819) Refactor OffsetSegmentGenerator to use it. Signed-off-by: Martin Davis --- .../jts/algorithm/Intersection.java | 63 ++++++++++++++++--- .../buffer/OffsetSegmentGenerator.java | 31 ++------- .../jts/algorithm/IntersectionTest.java | 49 +++++++++++++++ 3 files changed, 108 insertions(+), 35 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java index 9cfe614138..694bd8d6be 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java @@ -14,7 +14,15 @@ import org.locationtech.jts.geom.Coordinate; /** - * Contains functions to compute intersections between lines. + * Functions to compute intersection points between lines and line segments. + *

    + * In general it is not possible to compute + * the intersection point of two lines exactly, due to numerical roundoff. + * This is particularly true when the lines are nearly parallel. + * These routines uses numerical conditioning on the input values + * to ensure that the computed value is very close to the correct value. + *

    + * The Z-ordinate is ignored, and not populated. * * @author Martin Davis * @@ -25,13 +33,6 @@ public class Intersection { * Computes the intersection point of two lines. * If the lines are parallel or collinear this case is detected * and null is returned. - *

    - * In general it is not possible to accurately compute - * the intersection point of two lines, due to - * numerical roundoff. - * This is particularly true when the input lines are nearly parallel. - * This routine uses numerical conditioning on the input values - * to ensure that the computed value should be very close to the correct value. * * @param p1 an endpoint of line 1 * @param p2 an endpoint of line 1 @@ -97,6 +98,52 @@ public static Coordinate intersection(Coordinate p1, Coordinate p2, Coordinate q return new Coordinate(xInt + midx, yInt + midy); } + /** + * Computes the intersection point of a line and a line segment (if any). + * There will be no intersection point if: + *

      + *
    • the segment does not intersect the line + *
    • the line or the segment are degenerate (have zero length) + *
    + * If the segment is collinear with the line the first segment endpoint is returned. + * + * @param line1 a point on the line + * @param line2 a point on the line + * @param seg1 an endpoint of the line segment + * @param seg2 an endpoint of the line segment + * @return the intersection point, or null if it is not possible to find an intersection + */ + public static Coordinate intersectionLineSegment(Coordinate line1, Coordinate line2, Coordinate seg1, Coordinate seg2) { + int orientS1 = Orientation.index(line1, line2, seg1); + if (orientS1 == 0) return seg1.copy(); + + int orientS2 = Orientation.index(line1, line2, seg2); + if (orientS2 == 0) return seg2.copy(); + + /** + * If segment lies completely on one side of the line, it does not intersect + */ + if ((orientS1 > 0 && orientS2 > 0) || (orientS1 < 0 && orientS2 < 0)) { + return null; + } + + /** + * The segment intersects the line. + * The full line-line intersection is used to compute the intersection point. + */ + Coordinate intPt = intersection(line1, line2, seg1, seg2); + if (intPt != null) return intPt; + + /** + * Due to robustness failure it is possible the intersection computation will return null. + * In this case choose the closest point + */ + double dist1 = Distance.pointToLinePerpendicular(seg1, line1, line2); + double dist2 = Distance.pointToLinePerpendicular(seg2, line1, line2); + if (dist1 < dist2) + return seg1.copy(); + return seg2; + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index 6666419a6c..e7a6829df8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -532,16 +532,10 @@ private void addLimitedMitreJoin( // compute the candidate bevel segment by projecting both sides of the midpoint Coordinate bevel0 = project(bevelMidPt, distance, dirBevel); Coordinate bevel1 = project(bevelMidPt, distance, dirBevel + Math.PI); - LineSegment bevel = new LineSegment(bevel0, bevel1); - - //-- compute intersections with extended offset segments - double extendLen = mitreLimitDistance < distance ? distance : mitreLimitDistance; - - LineSegment extend0 = extend(offset0, 2 * extendLen); - LineSegment extend1 = extend(offset1, -2 * extendLen); - Coordinate bevelInt0 = bevel.intersection(extend0); - Coordinate bevelInt1 = bevel.intersection(extend1); - + + Coordinate bevelInt0 = Intersection.intersectionLineSegment(offset0.p0, offset0.p1, bevel0, bevel1); + Coordinate bevelInt1 = Intersection.intersectionLineSegment(offset1.p0, offset1.p1, bevel0, bevel1); + //-- add the limited bevel, if it intersects the offsets if (bevelInt0 != null && bevelInt1 != null) { segList.addPt(bevelInt0); @@ -555,23 +549,6 @@ private void addLimitedMitreJoin( */ addBevelJoin(offset0, offset1); } - - /** - * Extends a line segment forwards or backwards a given distance. - * - * @param seg the base line segment - * @param dist the distance to extend by - * @return the extended segment - */ - private static LineSegment extend(LineSegment seg, double dist) { - double distFrac = Math.abs(dist) / seg.getLength(); - double segFrac = dist >= 0 ? 1 + distFrac : 0 - distFrac; - Coordinate extendPt = seg.pointAlong(segFrac); - if (dist > 0) - return new LineSegment(seg.p0, extendPt); - else - return new LineSegment(extendPt, seg.p1); - } /** * Projects a point to a given distance in a given direction angle. diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java index d01aede972..ca4b3b9f27 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java @@ -49,6 +49,30 @@ public void testAlmostCollinearCond() { 7.772841461, 60.5339209242 ); } + //------------------------------------------------------------ + + public void testLineSegCross() { + checkIntersectionLineSegment( 0, 0, 0, 1, -1, 9, 1, 9, 0, 9 ); + checkIntersectionLineSegment( 0, 0, 0, 1, -1, 2, 1, 4, 0, 3 ); + } + + public void testLineSegTouch() { + checkIntersectionLineSegment( 0, 0, 0, 1, -1, 9, 0, 9, 0, 9 ); + checkIntersectionLineSegment( 0, 0, 0, 1, 0, 2, 1, 4, 0, 2 ); + } + + public void testLineSegCollinear() { + checkIntersectionLineSegment( 0, 0, 0, 1, 0, 9, 0, 8, 0, 9 ); + } + + public void testLineSegNone() { + checkIntersectionLineSegmentNull( 0, 0, 0, 1, 2, 9, 1, 9 ); + checkIntersectionLineSegmentNull( 0, 0, 0, 1, -2, 9, -1, 9 ); + checkIntersectionLineSegmentNull( 0, 0, 0, 1, 2, 9, 1, 9 ); + } + + //================================================== + private void checkIntersection(double p1x, double p1y, double p2x, double p2y, double q1x, double q1y, double q2x, double q2y, double expectedx, double expectedy) { @@ -73,4 +97,29 @@ private void checkIntersectionNull(double p1x, double p1y, double p2x, double p2 Coordinate actual = Intersection.intersection(p1, p2, q1, q2); assertTrue(actual == null); } + + private void checkIntersectionLineSegment(double p1x, double p1y, double p2x, double p2y, + double q1x, double q1y, double q2x, double q2y, + double expectedx, double expectedy) { + Coordinate p1 = new Coordinate(p1x, p1y); + Coordinate p2 = new Coordinate(p2x, p2y); + Coordinate q1 = new Coordinate(q1x, q1y); + Coordinate q2 = new Coordinate(q2x, q2y); + //Coordinate actual = CGAlgorithmsDD.intersection(p1, p2, q1, q2); + Coordinate actual = Intersection.intersectionLineSegment(p1, p2, q1, q2); + Coordinate expected = new Coordinate( expectedx, expectedy ); + double dist = actual.distance(expected); + //System.out.println("Expected: " + expected + " Actual: " + actual + " Dist = " + dist); + assertTrue(dist <= MAX_ABS_ERROR); + } + + private void checkIntersectionLineSegmentNull(double p1x, double p1y, double p2x, double p2y, + double q1x, double q1y, double q2x, double q2y) { + Coordinate p1 = new Coordinate(p1x, p1y); + Coordinate p2 = new Coordinate(p2x, p2y); + Coordinate q1 = new Coordinate(q1x, q1y); + Coordinate q2 = new Coordinate(q2x, q2y); + Coordinate actual = Intersection.intersectionLineSegment(p1, p2, q1, q2); + assertTrue(actual == null); + } } From 2ff3888f0afa3ff10d21adc32d7706340e89615c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 18:09:01 -0800 Subject: [PATCH 160/275] Slight refactor of limited mitre computation Signed-off-by: Martin Davis --- .../operation/buffer/OffsetSegmentGenerator.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index e7a6829df8..2a13ebecf8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -519,20 +519,18 @@ private void addLimitedMitreJoin( // direction of bisector of the interior angle between the segments double dir0 = Angle.angle(cornerPt, seg0.p0); double dirBisector = Angle.normalize(dir0 + angInterior2); - // rotating by PI gives the bisector of the outside angle, - // which is the direction of the bevel midpoint from the corner apex - double dirBisectorOut = Angle.normalize(dirBisector + Math.PI); - // compute the midpoint of the bevel segment - Coordinate bevelMidPt = project(cornerPt, mitreLimitDistance, dirBisectorOut); + // midpoint of the bevel segment + Coordinate bevelMidPt = project(cornerPt, -mitreLimitDistance, dirBisector); - // slope angle of bevel segment - double dirBevel = Angle.normalize(dirBisectorOut + Math.PI/2.0); + // direction of bevel segment (at right angle to corner bisector) + double dirBevel = Angle.normalize(dirBisector + Math.PI/2.0); // compute the candidate bevel segment by projecting both sides of the midpoint Coordinate bevel0 = project(bevelMidPt, distance, dirBevel); Coordinate bevel1 = project(bevelMidPt, distance, dirBevel + Math.PI); - + + // compute actual bevel segment between the offset lines Coordinate bevelInt0 = Intersection.intersectionLineSegment(offset0.p0, offset0.p1, bevel0, bevel1); Coordinate bevelInt1 = Intersection.intersectionLineSegment(offset1.p0, offset1.p1, bevel0, bevel1); From 5d22d870dbf2c20b9f9fc9d086e595cccea5b1e4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 22:03:37 -0800 Subject: [PATCH 161/275] Rename Intersection.lineSegment Signed-off-by: Martin Davis --- .../java/org/locationtech/jts/algorithm/Intersection.java | 2 +- .../jts/operation/buffer/OffsetSegmentGenerator.java | 4 ++-- .../java/org/locationtech/jts/algorithm/IntersectionTest.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java index 694bd8d6be..cc86506199 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Intersection.java @@ -113,7 +113,7 @@ public static Coordinate intersection(Coordinate p1, Coordinate p2, Coordinate q * @param seg2 an endpoint of the line segment * @return the intersection point, or null if it is not possible to find an intersection */ - public static Coordinate intersectionLineSegment(Coordinate line1, Coordinate line2, Coordinate seg1, Coordinate seg2) { + public static Coordinate lineSegment(Coordinate line1, Coordinate line2, Coordinate seg1, Coordinate seg2) { int orientS1 = Orientation.index(line1, line2, seg1); if (orientS1 == 0) return seg1.copy(); diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java index 2a13ebecf8..ecfa5059a6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/buffer/OffsetSegmentGenerator.java @@ -531,8 +531,8 @@ private void addLimitedMitreJoin( Coordinate bevel1 = project(bevelMidPt, distance, dirBevel + Math.PI); // compute actual bevel segment between the offset lines - Coordinate bevelInt0 = Intersection.intersectionLineSegment(offset0.p0, offset0.p1, bevel0, bevel1); - Coordinate bevelInt1 = Intersection.intersectionLineSegment(offset1.p0, offset1.p1, bevel0, bevel1); + Coordinate bevelInt0 = Intersection.lineSegment(offset0.p0, offset0.p1, bevel0, bevel1); + Coordinate bevelInt1 = Intersection.lineSegment(offset1.p0, offset1.p1, bevel0, bevel1); //-- add the limited bevel, if it intersects the offsets if (bevelInt0 != null && bevelInt1 != null) { diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java index ca4b3b9f27..c26af8b92f 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/IntersectionTest.java @@ -106,7 +106,7 @@ private void checkIntersectionLineSegment(double p1x, double p1y, double p2x, do Coordinate q1 = new Coordinate(q1x, q1y); Coordinate q2 = new Coordinate(q2x, q2y); //Coordinate actual = CGAlgorithmsDD.intersection(p1, p2, q1, q2); - Coordinate actual = Intersection.intersectionLineSegment(p1, p2, q1, q2); + Coordinate actual = Intersection.lineSegment(p1, p2, q1, q2); Coordinate expected = new Coordinate( expectedx, expectedy ); double dist = actual.distance(expected); //System.out.println("Expected: " + expected + " Actual: " + actual + " Dist = " + dist); @@ -119,7 +119,7 @@ private void checkIntersectionLineSegmentNull(double p1x, double p1y, double p2x Coordinate p2 = new Coordinate(p2x, p2y); Coordinate q1 = new Coordinate(q1x, q1y); Coordinate q2 = new Coordinate(q2x, q2y); - Coordinate actual = Intersection.intersectionLineSegment(p1, p2, q1, q2); + Coordinate actual = Intersection.lineSegment(p1, p2, q1, q2); assertTrue(actual == null); } } From 66d4ec37b72d85e02c8f955e4e4685da0cf5c0a3 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 14 Dec 2021 22:10:48 -0800 Subject: [PATCH 162/275] Add TestBuilder lineSegmentIntersection function Signed-off-by: Martin Davis --- .../jtstest/function/LineSegmentFunctions.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/LineSegmentFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/LineSegmentFunctions.java index fa0ce533fd..09ae32e003 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/LineSegmentFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/LineSegmentFunctions.java @@ -13,6 +13,7 @@ package org.locationtech.jtstest.function; import org.locationtech.jts.algorithm.CGAlgorithmsDD; +import org.locationtech.jts.algorithm.Intersection; import org.locationtech.jts.algorithm.RobustLineIntersector; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; @@ -20,7 +21,7 @@ public class LineSegmentFunctions { - public static boolean segmentIntersects(Geometry g1, Geometry g2) + public static boolean intersects(Geometry g1, Geometry g2) { Coordinate[] pt1 = g1.getCoordinates(); Coordinate[] pt2 = g2.getCoordinates(); @@ -29,7 +30,7 @@ public static boolean segmentIntersects(Geometry g1, Geometry g2) return ri.hasIntersection(); } - public static Geometry segmentIntersection(Geometry g1, Geometry g2) + public static Geometry intersection(Geometry g1, Geometry g2) { Coordinate[] pt1 = g1.getCoordinates(); Coordinate[] pt2 = g2.getCoordinates(); @@ -53,7 +54,7 @@ public static Geometry segmentIntersection(Geometry g1, Geometry g2) return null; } - public static Geometry segmentIntersectionDD(Geometry g1, Geometry g2) + public static Geometry intersectionDD(Geometry g1, Geometry g2) { Coordinate[] pt1 = g1.getCoordinates(); Coordinate[] pt2 = g2.getCoordinates(); @@ -95,6 +96,15 @@ public static Geometry lineIntersectionDD(Geometry g1, Geometry g2) return g1.getFactory().createPoint(intPt); } + public static Geometry lineSegmentIntersection(Geometry g1, Geometry g2) + { + Coordinate[] pt1 = g1.getCoordinates(); + Coordinate[] pt2 = g2.getCoordinates(); + + Coordinate intPt = Intersection.lineSegment(pt1[0], pt1[1], pt2[0], pt2[1]); + return g1.getFactory().createPoint(intPt); + } + public static Geometry reflectPoint(Geometry g1, Geometry g2) { Coordinate[] line = g1.getCoordinates(); From e1fc931487dab3701ab0192b6128f52b3fb9f1ea Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 15 Dec 2021 13:55:38 -0800 Subject: [PATCH 163/275] Add OverlayNG failure test cases (#821) * Add test cases Signed-off-by: Martin Davis --- .../test/jts/fail/overlayng/Issue784.java | 156 ++++++++++++++++++ .../test/jts/fail/overlayng/Issue820.java | 50 ++++++ 2 files changed, 206 insertions(+) create mode 100644 modules/core/src/test/java/test/jts/fail/overlayng/Issue784.java create mode 100644 modules/core/src/test/java/test/jts/fail/overlayng/Issue820.java diff --git a/modules/core/src/test/java/test/jts/fail/overlayng/Issue784.java b/modules/core/src/test/java/test/jts/fail/overlayng/Issue784.java new file mode 100644 index 0000000000..039a2bb021 --- /dev/null +++ b/modules/core/src/test/java/test/jts/fail/overlayng/Issue784.java @@ -0,0 +1,156 @@ +package test.jts.fail.overlayng; + +import java.util.Arrays; +import java.util.function.Function; +import java.util.stream.IntStream; + +import org.junit.Test; +import org.locationtech.jts.geom.CoordinateXY; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * Union of high-precision polygons - result misses one input area. + * Original issue was a unary union of 6 polygons. + * Problem is also manifested in a simpler form by two specific polygons. + * + * See https://github.com/locationtech/jts/issues/784 + * + */ +public class Issue784 extends TestCase { + + public static void main(String args[]) { + TestRunner.run(Issue784.class); + } + + private GeometryFactory gf = new GeometryFactory(); + + private Function createPolygon = points -> gf.createPolygon( + IntStream.range(0, points[0].length) + .mapToObj(index -> new CoordinateXY(points[0][index], points[1][index])) + .toArray(CoordinateXY[]::new) + ); + + Polygon p1 = createPolygon.apply(new double[][]{ + {-16.15907384118054, -16.28767148661218, -13.719459554848422, -13.639456293556348, -16.15907384118054}, + {1.0157206673651495, 7.6835050482212575, 5.212481574525698, 1.0643148816523527, 1.0157206673651495} + }); + Polygon p2 = createPolygon.apply(new double[][]{ + {-12.725226307666448, -13.639456293556348, -13.719459768757051, -12.78759751645274, -12.725226307666448}, + {1.0819470369308437, 1.0643148816523527, 5.212481780339414, 4.315883851167196, 1.0819470369308437} + }); + Polygon p3 = createPolygon.apply(new double[][]{ + {-13.621824029083443, -16.14144162383529, -16.15907384118054, -13.639456293556348, -13.621824029083443}, + {0.15008489786842003, 0.10149068267229658, 1.0157206673651493, 1.0643148816523527, 0.15008489786842003} + }); + Polygon p3r = createPolygon.apply(new double[][]{ + {-13.621824029083443, -16.14144162383529, -16.15907384118054, -13.639456293556348, -13.621824029083443}, + {0.1500848978684200, 0.10149068267229658, 1.0157206673651493, 1.0643148816523527, 0.1500848978684200} + }); + Polygon p4 = createPolygon.apply(new double[][]{ + {-17.219533690879903, -16.28767148661218, -16.15907384118054, -17.073303827070436, -17.219533690879903}, + {8.580102931174729, 7.683505048221257, 1.0157206673651495, 0.9980885120866584, 8.580102931174729} + }); + Polygon p5 = createPolygon.apply(new double[][]{ + {-12.707594043193543, -13.621824029083443, -13.639456293556348, -12.725226307666448, -12.707594043193543}, + {0.1677170531469111, 0.15008489786842005, 1.0643148816523527, 1.0819470369308437, 0.1677170531469111} + }); + Polygon p6 = createPolygon.apply(new double[][]{ + {-17.055671609725188, -17.073303827070436, -16.15907384118054, -16.14144162383529, -17.055671609725188}, + {0.0838585273938056, 0.9980885120866584, 1.0157206673651493, 0.1014906826722966, 0.0838585273938056} + }); + + + /** + * Simplest reproducer. + * + * @throws ParseException + */ + public void testUnion_p3p5() throws ParseException { + //checkUnion("35 - p3 Rounded", p3r, p5); + checkUnion("35 - p3 Full", p3, p5); + } + + /** + * Original test case. Simplified version shows same problem. + * + * @throws ParseException + */ + @Test + public void xtestUnionOriginal() throws ParseException { + Polygon expectedUnion = (Polygon) new WKTReader().read( + "POLYGON ((-17.055671609725188 0.0838585273938056, -17.219533690879903 8.580102931174729, -12.78759751645274 4.315883851167196, -12.707594043193543 0.1677170531469111, -17.055671609725188 0.0838585273938056))" + ); + Polygon[] polygons = { + p1, p2, p3, p4, p5, p6 + }; + + Geometry gc = gf.createGeometryCollection(polygons); + System.out.println(gc); + + Geometry gcUnion = gc.union(); + System.out.println(gcUnion); + + Geometry mpUnion = gf.createMultiPolygon(polygons).union(); + + Geometry indUnion = Arrays.stream(polygons) + .map(Geometry.class::cast) + .reduce(Geometry::union) + .orElseThrow(null); + + assertEquals(expectedUnion.getArea(), gcUnion.getArea(), 0.0001); // Fail + assertEquals(expectedUnion.getArea(), mpUnion.getArea(), 0.0001); // Fail + assertEquals(expectedUnion.getArea(), indUnion.getArea(), 0.0001); // Pass + } + + /** + * See simpler case. + * + * @throws ParseException + */ + public void xtestUnion_p2p3p5() throws ParseException { + checkUnion("235 - p3 Rounded", p2, p3r, p5); + checkUnion("235 - p3 Full", p2, p3, p5); + } + + private void checkUnion(String title, Polygon p2, Polygon p3, Polygon p5) { + Geometry p25 = union(p2, p5); + Geometry pall = union(p3, p25); + double areaSum = p2.getArea() + p3.getArea() + p5.getArea(); + + checkAreas(title, pall, areaSum); + } + + private void checkUnion(String title, Polygon p1, Polygon p2) { + Geometry pUnion = union(p1, p2); + double areaSum = p1.getArea() + p2.getArea(); + + checkAreas(title, pUnion, areaSum); + } + + private static Geometry union(Geometry a, Geometry b) { + return OverlayNGRobust.overlay(a, b, OverlayNG.UNION); + } + + private void checkAreas(String title, Geometry union, double areaSum) { + boolean isOk = isAreasClose(union, areaSum); + String status = isOk ? "Success" : "FAILED!"; + System.out.println(title + " - Union status: " + status); + assertTrue(isOk); + } + + private boolean isAreasClose(Geometry geom, double area) { + double geomArea = geom.getArea(); + double areaDelta = Math.abs(geomArea - area); + double deltaFrac = areaDelta / Math.max(geomArea, area); + return deltaFrac < 0.1; + } +} diff --git a/modules/core/src/test/java/test/jts/fail/overlayng/Issue820.java b/modules/core/src/test/java/test/jts/fail/overlayng/Issue820.java new file mode 100644 index 0000000000..e5c2380d4f --- /dev/null +++ b/modules/core/src/test/java/test/jts/fail/overlayng/Issue820.java @@ -0,0 +1,50 @@ +package test.jts.fail.overlayng; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Polygonal; +import org.locationtech.jts.operation.overlayng.OverlayNG; +import org.locationtech.jts.operation.overlayng.OverlayNGRobust; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * Intersection produces point geometry instead of polygonal. + * + * See https://github.com/locationtech/jts/issues/820 + * + */ +public class Issue820 extends TestCase { + + public static void main(String args[]) { + TestRunner.run(Issue820.class); + } + + public void testIntersection() { + Geometry smallerArea = new GeometryFactory().createPolygon(new Coordinate[]{ + new Coordinate(7.04972080711741E-9d, 0.0d), + new Coordinate(2.2101801468358406E-9d, 1.4126466987753772d), + new Coordinate(1.2696784445936622d, 1.41264669888319d), + new Coordinate(1.2696784494332027d, 1.078129033360885E-10d), + new Coordinate(7.04972080711741E-9d, 0.0d), + }); + Polygon biggerArea = new GeometryFactory().createPolygon(new Coordinate[]{ + new Coordinate(7.04972080711741E-9, 0.0), + new Coordinate(0.0, 2.0577913328003916), + new Coordinate(4.498851115482424, 7.063229687109256), + new Coordinate(13.596527845590671, 7.06322968918812), + new Coordinate(19.94490668468672, 1.693592821538914E-9), + new Coordinate(7.04972080711741E-9, 0.0) + }); + + Geometry intersection = OverlayNGRobust.overlay(smallerArea, biggerArea, OverlayNG.INTERSECTION); + + // Expected: POLYGON ((0.0000000022101801 1.4126466987753772, 1.2696784445936622 1.41264669888319, 1.2696784494332027 0.0000000001078129, 0.0000000070497208 0, 0.0000000022101801 1.4126466987753772)) + // Actual: MULTIPOINT ((0.0000000022101801 1.4126466987753772), (0.0000000070497208 0)) + + assertTrue(intersection instanceof Polygonal); + } +} From d33f08388c8406f1cdaf5568c8c534554a1b6b58 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 4 Jan 2022 09:50:04 -0800 Subject: [PATCH 164/275] ConcaveHull for Points (#823) * ConcaveHull for Points, with Length, Length Factor and Area Ratio target criteria Signed-off-by: Martin Davis --- .../function/ConstructionFunctions.java | 55 ++ .../jts/algorithm/hull/ConcaveHull.java | 769 ++++++++++++++++++ .../org/locationtech/jts/geom/Triangle.java | 25 +- .../locationtech/jts/triangulate/tri/Tri.java | 63 +- .../triangulate/tri/TriangulationBuilder.java | 4 +- .../jts/algorithm/hull/ConcaveHullTest.java | 159 ++++ .../locationtech/jts/hull/ConcaveHull.java | 53 -- .../jts/hull/ConcaveHullTest.java | 45 - 8 files changed, 1066 insertions(+), 107 deletions(-) create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java create mode 100644 modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java delete mode 100644 modules/lab/src/main/java/org/locationtech/jts/hull/ConcaveHull.java delete mode 100644 modules/lab/src/test/java/org/locationtech/jts/hull/ConcaveHullTest.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java index 13df15dc05..e3301c8258 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java @@ -16,6 +16,7 @@ import org.locationtech.jts.algorithm.MinimumDiameter; import org.locationtech.jts.algorithm.construct.LargestEmptyCircle; import org.locationtech.jts.algorithm.construct.MaximumInscribedCircle; +import org.locationtech.jts.algorithm.hull.ConcaveHull; import org.locationtech.jts.densify.Densifier; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; @@ -130,4 +131,58 @@ public static Geometry circleByRadiusLine(Geometry radiusLine, return radiusLine.getFactory().createPolygon(circlePts); } + public static Geometry concaveHullByLen(Geometry geom, + @Metadata(title="Length") + double maxLen) { + return ConcaveHull.concaveHullByLength(geom, maxLen); + } + + public static Geometry concaveHullWithHolesByLen(Geometry geom, + @Metadata(title="Length") + double maxLen) { + return ConcaveHull.concaveHullByLength(geom, maxLen, true); + } + + public static Geometry concaveHullByLenFactor(Geometry geom, + @Metadata(title="Length factor") + double maxLen) { + return ConcaveHull.concaveHullByLengthFactor(geom, maxLen); + } + + public static Geometry concaveHullWithHolesByLenFactor(Geometry geom, + @Metadata(title="Length factor") + double maxLen) { + return ConcaveHull.concaveHullByLengthFactor(geom, maxLen, true); + } + + public static Geometry concaveHullByArea(Geometry geom, + @Metadata(title="Area ratio") + double minAreaPct) { + return ConcaveHull.concaveHullByArea(geom, minAreaPct); + } + + public static double concaveHullLenGuess(Geometry geom) { + return ConcaveHull.uniformGridEdgeLength(geom); + } + + /** + * A concaveness measure defined in terms of the perimeter length + * relative to the convex hull perimeter. + *
    +   * C = ( P(geom) - P(CH) ) / P(CH)
    +   * 
    + * Concaveness values are >= 0. + * A convex polygon has C = 0. + * A higher concaveness indicates a more concave polygon. + *

    + * Originally defined by Park & Oh, 2012. + * + * @param geom a polygonal geometry + * @return the concaveness measure of the geometry + */ + public static double concaveness(Geometry geom) { + double convexLen = geom.convexHull().getLength(); + return (geom.getLength() - convexLen) / convexLen; + } + } diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java new file mode 100644 index 0000000000..991ac27c7c --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.hull; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.PriorityQueue; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateList; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Triangle; +import org.locationtech.jts.operation.overlayng.CoverageUnion; +import org.locationtech.jts.triangulate.DelaunayTriangulationBuilder; +import org.locationtech.jts.triangulate.quadedge.QuadEdge; +import org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision; +import org.locationtech.jts.triangulate.quadedge.TriangleVisitor; +import org.locationtech.jts.triangulate.tri.Tri; +import org.locationtech.jts.triangulate.tri.TriangulationBuilder; +import org.locationtech.jts.util.Assert; + +/** + * Constructs a concave hull of a set of points. + * The hull is constructed by removing the longest outer edges + * of the Delaunay Triangulation of the points + * until certain target criteria are reached. + * The target criteria are: + *

      + *
    • Maximum Edge Length - the length of the longest edge of the hull is no larger + * than this value. + *
    • Maximum Edge Length Factor - determine the Maximum Edge Length + * as a fraction of the difference between the longest and shortest edge lengths + * in the Delaunay Triangulation. + * This normalizes the Maximum Edge Length to be scale-independent. + *
    • Maximum Area Ratio - the ratio of the concave hull area to the convex hull area + * will be no larger than this value. + *
    + * Usually only a single criteria is specified, but both may be provided. + * The preferred criteria is the Maximum Edge Length Factor, since it is + * scale-independent, and local (so that no assumption needs to be made about the + * total amount of concavity present). + * Other length criteria can be used by setting the Maximum Edge Length. + * For example, use a length relative to the longest edge length + * in the Minimum Spanning Tree of the point set. + * Or, use a length derived from the {@link #uniformGridEdgeLength(Geometry)} value. + *

    + * The computed hull is always a single connected {@link Polygon} + * (unless it is degenerate, in which case it will be a {@link Point} or a {@link LineString}). + * This constraint may cause the concave hull to fail to meet the target criteria. + *

    + * Optionally the concave hull can be allowed to contain holes. + * Note that this may result in substantially slower computation, + * and it can produce results of low quality. + * + * @author Martin Davis + * + */ +public class ConcaveHull +{ + /** + * Computes the approximate edge length of + * a uniform square grid having the same number of + * points as a geometry and the same area as its convex hull. + * This value can be used to determine a suitable length threshold value + * for computing a concave hull. + * A value from 2 to 4 times the uniform grid length + * seems to produce reasonable results. + * + * @param geom a geometry + * @return the approximate uniform grid length + */ + public static double uniformGridEdgeLength(Geometry geom) { + double areaCH = geom.convexHull().getArea(); + int numPts = geom.getNumPoints(); + return Math.sqrt(areaCH / numPts); + } + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length. + * + * @param geom the input geometry + * @param maxLength the target maximum edge length + * @return the concave hull + */ + public static Geometry concaveHullByLength(Geometry geom, double maxLength) { + return concaveHullByLength(geom, maxLength, false); + } + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length, + * and optionally allowing holes. + * + * @param geom the input geometry + * @param maxLength the target maximum edge length + * @param isHolesAllowed whether holes are allowed in the result + * @return the concave hull + */ + public static Geometry concaveHullByLength(Geometry geom, double maxLength, boolean isHolesAllowed) { + ConcaveHull hull = new ConcaveHull(geom); + hull.setMaximumEdgeLength(maxLength); + hull.setHolesAllowed(isHolesAllowed); + return hull.getHull(); + } + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length factor. + * The edge length factor is a fraction of the length difference + * between the longest and shortest edges + * in the Delaunay Triangulation of the input points. + * + * @param geom the input geometry + * @param lengthFactor the target edge length factor + * @return the concave hull + */ + public static Geometry concaveHullByLengthFactor(Geometry geom, double lengthFactor) { + return concaveHullByLengthFactor(geom, lengthFactor, false); + } + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length factor, + * and optionally allowing holes. + * The edge length factor is a fraction of the length difference + * between the longest and shortest edges + * in the Delaunay Triangulation of the input points. + * + * @param geom the input geometry + * @param maxLength the target maximum edge length + * @param isHolesAllowed whether holes are allowed in the result + * @return the concave hull + */ + public static Geometry concaveHullByLengthFactor(Geometry geom, double lengthFactor, boolean isHolesAllowed) { + ConcaveHull hull = new ConcaveHull(geom); + hull.setMaximumEdgeLengthFactor(lengthFactor); + hull.setHolesAllowed(isHolesAllowed); + return hull.getHull(); + } + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum area ratio. + * + * @param geom the input geometry + * @param areaRatio the target maximum area ratio + * @return the concave hull + */ + public static Geometry concaveHullByArea(Geometry geom, double areaRatio) { + ConcaveHull hull = new ConcaveHull(geom); + hull.setMaximumAreaRatio(areaRatio); + return hull.getHull(); + } + + private Geometry inputGeometry; + private double maxEdgeLength = 0.0; + private double maxEdgeLengthFactor = -1; + private double maxAreaRatio = 0.0; + private boolean isHolesAllowed = false; + private GeometryFactory geomFactory; + + + /** + * Creates a new instance for a given geometry. + * + * @param geom the input geometry + */ + public ConcaveHull(Geometry geom) { + this.inputGeometry = geom; + this.geomFactory = geom.getFactory(); + } + + /** + * Sets the target maximum edge length for the concave hull. + * The length value must be zero or greater. + *

      + *
    • The value 0.0 produces the concave hull of smallest area + * that is still connected. + *
    • Larger values produce less concave results. + * A value equal or greater than the longest Delaunay Triangulation edge length + * produces the convex hull. + *
    + * The {@link #uniformGridEdgeLength(Geometry)} value may be used as + * the basis for estimating an appropriate target maximum edge length. + * + * @param edgeLength a non-negative length + * + * @see #uniformGridEdgeLength(Geometry) + */ + public void setMaximumEdgeLength(double edgeLength) { + if (edgeLength < 0) + throw new IllegalArgumentException("Edge length must be non-negative"); + this.maxEdgeLength = edgeLength; + maxEdgeLengthFactor = -1; + } + + /** + * Sets the target maximum edge length factor for the concave hull. + * The edge length factor is a fraction of the difference + * between the longest and shortest edge lengths + * in the Delaunay Triangulation of the input points. + * It is a value in the range 0 to 1. + *
      + *
    • The value 0.0 produces a concave hull of minimum area + * that is still connected. + *
    • The value 1.0 produces the convex hull. + *
        + * + * @param edgeLengthFactor a length factor value between 0 and 1 + */ + public void setMaximumEdgeLengthFactor(double edgeLengthFactor) { + if (edgeLengthFactor < 0 || edgeLengthFactor > 1) + throw new IllegalArgumentException("Edge length ratio must be in range [0,1]e"); + this.maxEdgeLengthFactor = edgeLengthFactor; + } + + /** + * Sets the target maximum concave hull area as a ratio of the convex hull area. + * It is a value in the range 0 to 1. + *
          + *
        • The value 0.0 produces a concave hull with the smallest area + * that is still connected. + *
        • The value 1.0 produces the convex hull + * (unless a maximum edge length is also specified). + *
        + * + * @param areaRatio a ratio value between 0 and 1 + */ + public void setMaximumAreaRatio(double areaRatio) { + if (areaRatio < 0 || areaRatio > 1) + throw new IllegalArgumentException("Area ratio must be in range [0,1]"); + this.maxAreaRatio = areaRatio; + } + + /** + * Sets whether holes are allowed in the concave hull polygon. + * + * @param isHolesAllowed true if holes are allowed in the result + */ + public void setHolesAllowed(boolean isHolesAllowed) { + this.isHolesAllowed = isHolesAllowed; + } + + /** + * Gets the computed concave hull. + * + * @return the concave hull + */ + public Geometry getHull() { + if (inputGeometry.isEmpty()) { + return geomFactory.createPolygon(); + } + List triList = createDelaunayTriangulation(inputGeometry); + if (maxEdgeLengthFactor >= 0) { + maxEdgeLength = computeTargetEdgeLength(triList, maxEdgeLengthFactor); + } + if (triList.isEmpty()) + return inputGeometry.convexHull(); + computeHull(triList); + Geometry hull = toPolygon(triList, geomFactory); + return hull; + } + + private static double computeTargetEdgeLength(List triList, + double edgeLengthFactor) { + if (edgeLengthFactor == 0) return 0; + double maxEdgeLen = -1; + double minEdgeLen = -1; + for (Tri tri : triList) { + for (int i = 0; i < 3; i++) { + double len = tri.getCoordinate(i).distance(tri.getCoordinate(Tri.next(i))); + if (len > maxEdgeLen) + maxEdgeLen = len; + if (minEdgeLen < 0 || len < minEdgeLen) + minEdgeLen = len; + } + } + //-- ensure all edges are included + if (edgeLengthFactor == 1) return 2 * maxEdgeLen; + return edgeLengthFactor * (maxEdgeLen - minEdgeLen) + minEdgeLen; + } + + private void computeHull(List triList) { + //-- used if area is the threshold criteria + double areaConvex = Tri.area(triList); + double areaConcave = areaConvex; + + PriorityQueue queue = initQueue(triList); + // remove tris in order of decreasing size (edge length) + while (! queue.isEmpty()) { + if (isBelowAreaThreshold(areaConcave, areaConvex)) + break; + + HullTri tri = queue.poll(); + + if (isBelowLengthThreshold(tri)) + break; + + if (isRemovable(tri, triList)) { + //-- the non-null adjacents are now on the border + HullTri adj0 = (HullTri) tri.getAdjacent(0); + HullTri adj1 = (HullTri) tri.getAdjacent(1); + HullTri adj2 = (HullTri) tri.getAdjacent(2); + + //-- remove tri + tri.remove(); + triList.remove(tri); + areaConcave -= tri.getArea(); + + //-- if holes not allowed, add new border adjacents to queue + if (! isHolesAllowed) { + addBorderTri(adj0, queue); + addBorderTri(adj1, queue); + addBorderTri(adj2, queue); + } + } + } + } + + private PriorityQueue initQueue(List triList) { + PriorityQueue queue = new PriorityQueue(); + for (HullTri tri : triList) { + if (! isHolesAllowed) { + //-- add only border triangles which could be eroded + // (if tri has only 1 adjacent it can't be removed because that would isolate a vertex) + if (tri.numAdjacent() != 2) + continue; + tri.setSizeToBorder(); + } + queue.add(tri); + } + return queue; + } + + /** + * Adds a Tri to the queue. + * Only add tris with a single border edge. + * The ordering size is the length of the border edge. + * + * @param tri the Tri to add + * @param queue the priority queue + */ + private void addBorderTri(HullTri tri, PriorityQueue queue) { + if (tri == null) return; + if (tri.numAdjacent() != 2) return; + tri.setSizeToBorder(); + queue.add(tri); + } + + private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { + return areaConcave / areaConvex <= maxAreaRatio; + } + + private boolean isBelowLengthThreshold(HullTri tri) { + double len = 0; + if (isHolesAllowed) { + len = tri.lengthOfLongestEdge(); + } + else { + len = tri.lengthOfBorder(); + } + return len < maxEdgeLength; + } + + /** + * Tests whether a Tri can be removed while preserving + * the connectivity of the hull. + * + * @param tri the Tri to test + * @param triList + * @return true if the Tri can be removed + */ + private boolean isRemovable(HullTri tri, List triList) { + if (isHolesAllowed) { + /** + * Don't remove if that would separate a single vertex + */ + if (hasVertexSingleAdjacent(tri, triList)) + return false; + return HullTri.isConnected(triList, tri); + } + + //-- compute removable for no holes allowed + int numAdj = tri.numAdjacent(); + /** + * Tri must have exactly 2 adjacent tris. + * If it it has only 0 or 1 adjacent then removal would remove a vertex. + * If it has 3 adjacent then it is not on border. + */ + if (numAdj != 2) return false; + /** + * The tri cannot be removed if it is connecting, because + * this would create more than one result polygon. + */ + return ! isConnecting(tri); + } + + private static boolean hasVertexSingleAdjacent(HullTri tri, List triList) { + for (int i = 0; i < 3; i++) { + if (degree(tri.getCoordinate(i), triList) <= 1) + return true; + } + return false; + } + + /** + * The degree of a Tri vertex is the number of tris containing it. + * This must be done by searching the entire triangulation, + * since the containing tris may not be adjacent or edge-connected. + * + * @param v a vertex coordinate + * @param triList a triangulation + * @return the degree of the vertex + */ + private static int degree(Coordinate v, List triList) { + int degree = 0; + for (HullTri tri : triList) { + for (int i = 0; i < 3; i++) { + if (v.equals2D(tri.getCoordinate(i))) + degree++; + } + } + return degree; + } + + /** + * Tests if a tri is the only one connecting its 2 adjacents. + * Assumes that the tri is on the border of the triangulation + * and that the triangulation does not contain holes + * + * @param tri the tri to test + * @return true if the tri is the only connection + */ + private static boolean isConnecting(Tri tri) { + int adj2Index = adjacent2VertexIndex(tri); + boolean isInterior = isInteriorVertex(tri, adj2Index); + return ! isInterior; + } + + /** + * A vertex of a triangle is interior if it + * is fully surrounded by triangles. + * + * @param tri a tri containing the vertex + * @param index the vertex index + * @return true if the vertex is interior + */ + private static boolean isInteriorVertex(Tri triStart, int index) { + Tri curr = triStart; + int currIndex = index; + do { + Tri adj = curr.getAdjacent(currIndex); + if (adj == null) return false; + int adjIndex = adj.getIndex(curr); + curr = adj; + currIndex = Tri.next(adjIndex); + } + while (curr != triStart); + return true; + } + + private static int adjacent2VertexIndex(Tri tri) { + if (tri.hasAdjacent(0) && tri.hasAdjacent(1)) return 1; + if (tri.hasAdjacent(1) && tri.hasAdjacent(2)) return 2; + if (tri.hasAdjacent(2) && tri.hasAdjacent(0)) return 0; + return -1; + } + + private static class HullTri extends Tri + implements Comparable + { + private double size; + private boolean isMarked = false; + + public HullTri(Coordinate p0, Coordinate p1, Coordinate p2) { + super(p0, p1, p2); + this.size = lengthOfLongestEdge(); + } + + public double getSize() { + return size; + } + + /** + * Sets the size to be the length of the border edges. + * This is used when constructing hull without holes, + * by erosion from the triangulation border. + */ + public void setSizeToBorder() { + size = lengthOfBorder(); + } + + public boolean isMarked() { + return isMarked; + } + + public void setMarked(boolean isMarked) { + this.isMarked = isMarked; + } + + public boolean isBorder() { + return isBorder(0) || isBorder(1) || isBorder(2); + } + + public boolean isBorder(int index) { + return ! hasAdjacent(index); + } + + public int borderIndex() { + if (isBorder(0)) return 0; + if (isBorder(1)) return 1; + if (isBorder(2)) return 2; + return -1; + } + + /** + * Gets the most CCW border edge index. + * This assumes there is at least one non-border edge. + * + * @return the CCW border edge index + */ + public int borderIndexCCW() { + int index = borderIndex(); + int prevIndex = prev(index); + if (isBorder(prevIndex)) { + return prevIndex; + } + return index; + } + + /** + * Gets the most CW border edge index. + * This assumes there is at least one non-border edge. + * + * @return the CW border edge index + */ + public int borderIndexCW() { + int index = borderIndex(); + int nextIndex = next(index); + if (isBorder(nextIndex)) { + return nextIndex; + } + return index; + } + + public double lengthOfLongestEdge() { + return Triangle.longestSideLength(p0, p1, p2); + } + + private double lengthOfBorder() { + double len = 0.0; + for (int i = 0; i < 3; i++) { + if (! hasAdjacent(i)) { + len += getCoordinate(i).distance(getCoordinate(Tri.next(i))); + } + } + return len; + } + + public HullTri nextBorderTri() { + HullTri tri = this; + //-- start at first non-border edge CW + int index = next(borderIndexCW()); + //-- scan CCW around vertex for next border tri + do { + HullTri adjTri = (HullTri) tri.getAdjacent(index); + if (adjTri == this) + throw new IllegalStateException("No outgoing border edge found"); + index = next(adjTri.getIndex(tri)); + tri = adjTri; + } + while (! tri.isBorder(index)); + return (tri); + } + + /** + * PriorityQueues sort in ascending order. + * To sort with the largest at the head, + * smaller sizes must compare as greater than larger sizes. + * (i.e. the normal numeric comparison is reversed). + * If the sizes are identical (which should be an infrequent case), + * the areas are compared, with larger areas sorting before smaller. + * (The rationale is that larger areas indicate an area of lower point density, + * which is more likely to be in the exterior of the computed shape.) + * This improves the determinism of the queue ordering. + */ + @Override + public int compareTo(HullTri o) { + /** + * If size is identical compare areas to ensure a (more) deterministic ordering. + * Larger areas sort before smaller ones. + */ + if (size == o.size) { + return -Double.compare(this.getArea(), o.getArea()); + } + return -Double.compare(size, o.size); + } + + public static boolean isConnected(List triList, HullTri exceptTri) { + if (triList.size() == 0) return false; + clearMarks(triList); + HullTri triStart = findTri(triList, exceptTri); + if (triStart == null) return false; + markConnected(triStart, exceptTri); + exceptTri.setMarked(true); + return isAllMarked(triList); + } + + public static void clearMarks(List triList) { + for (HullTri tri : triList) { + tri.setMarked(false); + } + } + + public static HullTri findTri(List triList, Tri exceptTri) { + for (HullTri tri : triList) { + if (tri != exceptTri) return tri; + } + return null; + } + + public static boolean isAllMarked(List triList) { + for (HullTri tri : triList) { + if (! tri.isMarked()) + return false; + } + return true; + } + + public static void markConnected(HullTri triStart, Tri exceptTri) { + Deque queue = new ArrayDeque(); + queue.add(triStart); + while (! queue.isEmpty()) { + HullTri tri = queue.pop(); + tri.setMarked(true); + for (int i = 0; i < 3; i++) { + HullTri adj = (HullTri) tri.getAdjacent(i); + //-- don't connect thru this tri + if (adj == exceptTri) + continue; + if (adj != null && ! adj.isMarked() ) { + queue.add(adj); + } + } + } + } + } + + private static List createDelaunayTriangulation(Geometry geom) { + //TODO: implement a DT on Tris directly? + DelaunayTriangulationBuilder dt = new DelaunayTriangulationBuilder(); + dt.setSites(geom); + QuadEdgeSubdivision subdiv = dt.getSubdivision(); + List triList = toTris(subdiv); + return triList; + } + + private static List toTris(QuadEdgeSubdivision subdiv) { + HullTriVisitor visitor = new HullTriVisitor(); + subdiv.visitTriangles(visitor, false); + List triList = visitor.getTriangles(); + TriangulationBuilder.build(triList); + return triList; + } + + private static class HullTriVisitor implements TriangleVisitor { + private List triList = new ArrayList(); + + public HullTriVisitor() { + } + + public void visit(QuadEdge[] triEdges) { + Coordinate p0 = triEdges[0].orig().getCoordinate(); + Coordinate p1 = triEdges[1].orig().getCoordinate(); + Coordinate p2 = triEdges[2].orig().getCoordinate(); + HullTri tri; + if (Triangle.isCCW(p0, p1, p2)) { + tri = new HullTri(p0, p2, p1); + } + else { + tri = new HullTri(p0, p1, p2); + } + triList.add(tri); + } + + public List getTriangles() { + return triList; + } + } + + private Geometry toPolygon(List triList, GeometryFactory geomFactory) { + if (! isHolesAllowed) { + return extractPolygon(triList, geomFactory); + } + //-- in case holes are present use union (slower but handles holes) + return union(triList, geomFactory); + } + + private Geometry extractPolygon(List triList, GeometryFactory geomFactory) { + if (triList.size() == 1) { + Tri tri = triList.get(0); + return tri.toPolygon(geomFactory); + } + Coordinate[] pts = traceBorder(triList); + return geomFactory.createPolygon(pts); + } + + private static Geometry union(List triList, GeometryFactory geomFactory) { + List polys = new ArrayList(); + for (Tri tri : triList) { + Polygon poly = tri.toPolygon(geomFactory); + polys.add(poly); + } + return CoverageUnion.union(geomFactory.buildGeometry(polys)); + } + + /** + * Extracts the coordinates along the border of a triangulation, + * by tracing CW around the border triangles. + * Assumption: there are at least 2 tris, they are connected, + * and there are no holes. + * So each tri has at least one non-border edge, and there is only one border. + * + * @param triList the triangulation + * @return the border of the triangulation + */ + private static Coordinate[] traceBorder(List triList) { + HullTri triStart = findBorderTri(triList); + CoordinateList coordList = new CoordinateList(); + HullTri tri = triStart; + do { + int borderIndex = tri.borderIndexCCW(); + //-- add border vertex + coordList.add(tri.getCoordinate(borderIndex).copy(), false); + int nextIndex = Tri.next(borderIndex); + //-- if next edge is also border, add it and move to next + if (tri.isBorder(nextIndex)) { + coordList.add(tri.getCoordinate(nextIndex).copy(), false); + borderIndex = nextIndex; + } + //-- find next border tri CCW around non-border edge + tri = tri.nextBorderTri(); + } while (tri != triStart); + coordList.closeRing(); + return coordList.toCoordinateArray(); + } + + public static HullTri findBorderTri(List triList) { + for (HullTri tri : triList) { + if (tri.isBorder()) return tri; + } + Assert.shouldNeverReachHere("No border triangles found"); + return null; + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java index 8cdaa1d16f..8188a166b7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java @@ -290,6 +290,19 @@ public static Coordinate centroid(Coordinate a, Coordinate b, Coordinate c) return new Coordinate(x, y); } + /** + * Compute the length of the perimeter of a triangle + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return the length of the triangle perimeter + */ + public static double length(Coordinate a, Coordinate b, Coordinate c) + { + return a.distance(b) + b.distance(c) + c.distance(a); + } + /** * Computes the length of the longest side of a triangle * @@ -430,7 +443,7 @@ public static double area3D(Coordinate a, Coordinate b, Coordinate c) return area3D; } - + /** * Computes the Z-value (elevation) of an XY point on a three-dimensional * plane defined by a triangle whose vertices have Z-values. The defining @@ -563,6 +576,16 @@ public Coordinate centroid() return centroid(p0, p1, p2); } + /** + * Computes the length of the perimeter of this triangle. + * + * @return the length of the perimeter + */ + public double length() + { + return length(p0, p1, p2); + } + /** * Computes the length of the longest side of this triangle * diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java index 337905e815..e2af1e2d12 100755 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java @@ -19,6 +19,7 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Triangle; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.util.Assert; @@ -49,6 +50,20 @@ public static Geometry toGeometry(List triList, GeometryFactory geomFact) { return geomFact.createGeometryCollection(geoms); } + /** + * Computes the area of a set of Tris. + * + * @param triList a set of Tris + * @return the total area of the triangles + */ + public static double area(List triList) { + double area = 0; + for (Tri tri : triList) { + area += tri.getArea(); + } + return area; + } + /** * Validates a list of Tris. * @@ -84,17 +99,17 @@ public static Tri create(Coordinate[] pts) { return new Tri(pts[0], pts[1], pts[2]); } - private Coordinate p0; - private Coordinate p1; - private Coordinate p2; + protected Coordinate p0; + protected Coordinate p1; + protected Coordinate p2; /** * triN is the adjacent triangle across the edge pN - pNN. * pNN is the next vertex CW from pN. */ - private Tri tri0; - private Tri tri1; - private Tri tri2; + protected Tri tri0; + protected Tri tri1; + protected Tri tri2; /** * Creates a triangle with the given vertices. @@ -246,6 +261,24 @@ private void replace(Tri triOld, Tri triNew) { } } + /** + * Removes this triangle from a triangulation. + * All adjacent references and the references to this + * Tri in the adjacent Tris are set to null triList) { + public static void build(List triList) { new TriangulationBuilder(triList); } @@ -41,7 +41,7 @@ public static void build(List triList) { * * @param triList the list of Tris */ - private TriangulationBuilder(List triList) { + private TriangulationBuilder(List triList) { triMap = new HashMap(); for (Tri tri : triList) { add(tri); diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java new file mode 100644 index 0000000000..6d8f05ba03 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.hull; + +import org.locationtech.jts.geom.Geometry; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +public class ConcaveHullTest extends GeometryTestCase { + + public static void main(String args[]) { + TestRunner.run(ConcaveHullTest.class); + } + + public ConcaveHullTest(String name) { super(name); } + + public void testLengthEmpty() { + checkHullByLength("MULTIPOINT EMPTY", + 70, "POLYGON EMPTY" ); + } + + public void testLengthPoint() { + checkHullByLength("MULTIPOINT ((10 10), (10 10))", + 70, "POINT (10 10)" ); + } + + public void testLengthCollinear() { + checkHullByLength("LINESTRING (10 10, 20 20, 30 30))", + 70, "LINESTRING (10 10, 30 30)" ); + } + + public void testLengthTriangle() { + checkHullByLength("MULTIPOINT ((10 10), (90 10), (30 70))", + 70, "POLYGON ((10 10, 30 70, 90 10, 10 10))" ); + } + + public void testLengthChevron() { + checkHullByLength("MULTIPOINT ((10 10), (90 10), (30 70), (70 70), (50 60))", + 70, "POLYGON ((30 70, 70 70, 90 10, 50 60, 10 10, 30 70))" ); + } + + public void testLengthZero() { + checkHullByLength("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", + 0, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 50 60, 30 30, 10 10))" ); + } + + public void testLengthConvex() { + checkHullByLength("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", + 100, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 10 10))" ); + } + + public void testLengthCShape() { + checkHullByLength("MULTIPOINT ((70 80), (80 90), (90 70), (50 80), (30 70), (20 40), (30 20), (50 10), (90 20), (40 50), (40 30), (41 67))", + 50, "POLYGON ((30 70, 50 80, 80 90, 90 70, 70 80, 40 50, 40 30, 90 20, 50 10, 30 20, 20 40, 30 70))" ); + } + + public void testLengthSShape() { + checkHullByLength("MULTIPOINT ((0 81), (65 86), (70 71), (80 59), (92 49), (107 44), (122 41), (137 40), (152 41), (167 42), (182 47), (195 55), (203 68), (201 83), (188 92), (173 97), (158 100), (143 103), (128 106), (113 109), (98 112), (83 115), (68 120), (53 125), (40 133), (28 143), (18 155), (13 170), (12 185), (16 200), (26 213), (38 223), (51 231), (66 236), (81 240), (96 243), (111 245), (126 245), (141 245), (156 245), (171 244), (186 241), (201 238), (216 233), (229 225), (242 216), (252 204), (259 190), (262 175), (194 171), (189 186), (178 197), (164 203), (149 205), (134 206), (119 205), (104 203), (89 198), (77 188), (80 173), (93 165), (108 160), (123 157), (138 154), (153 151), (168 149), (183 146), (198 142), (213 138), (227 132), (241 126), (253 116), (263 104), (269 90), (271 75), (270 60), (264 46), (254 34), (243 23), (229 16), (215 10), (200 6), (185 3), (170 1), (155 0), (139 0), (123 0), (108 1), (93 3), (78 5), (63 10), (49 16), (35 23), (23 33), (13 45), (6 59), (16 82), (32 83), (48 84), (245 174), (228 173), (211 172), (131 128), (63 148), (222 207), (127 230), (154 131), (240 82), (72 220), (210 32), (90 22), (206 208), (57 202), (195 117), (55 166), (246 55), (201 101), (224 73), (211 192), (42 176), (152 228), (172 113), (24 61), (76 33), (92 216), (46 69), (118 138), (169 23), (213 118), (221 56), (44 192), (118 22), (224 40), (56 57), (192 32), (179 220), (34 44), (145 18), (239 194), (40 155), (92 136), (231 106), (40 207), (108 228), (256 81), (28 185), (54 33), (74 205), (172 132), (221 93), (249 96), (69 47), (78 146), (155 115), (202 223))", + 20, "POLYGON ((16 200, 26 213, 38 223, 51 231, 66 236, 81 240, 96 243, 111 245, 126 245, 141 245, 156 245, 171 244, 186 241, 201 238, 216 233, 229 225, 242 216, 252 204, 259 190, 262 175, 245 174, 228 173, 211 172, 194 171, 189 186, 178 197, 164 203, 149 205, 134 206, 119 205, 104 203, 89 198, 77 188, 80 173, 93 165, 108 160, 123 157, 138 154, 153 151, 168 149, 183 146, 198 142, 213 138, 227 132, 241 126, 253 116, 263 104, 269 90, 271 75, 270 60, 264 46, 254 34, 243 23, 229 16, 215 10, 200 6, 185 3, 170 1, 155 0, 139 0, 123 0, 108 1, 93 3, 78 5, 63 10, 49 16, 35 23, 23 33, 13 45, 6 59, 0 81, 16 82, 32 83, 48 84, 65 86, 70 71, 80 59, 92 49, 107 44, 122 41, 137 40, 152 41, 167 42, 182 47, 195 55, 203 68, 201 83, 188 92, 173 97, 158 100, 143 103, 128 106, 113 109, 98 112, 83 115, 68 120, 53 125, 40 133, 28 143, 18 155, 13 170, 12 185, 16 200))" ); + } + + //------------------------------------------------ + + public void testLengthFactorZero() { + checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + 0, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 60 50, 47 66, 40 60, 30 70))" ); + } + + public void testLengthFactorP5() { + checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + 0.5, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 30 70))" ); + } + + public void testLengthFactorOne() { + checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + 1, "POLYGON ((10 10, 10 90, 90 90, 90 60, 90 10, 10 10))" ); + } + + public void testLengthFactorXYZChevronP5() { + checkHullByLengthFactorXYZ("MULTIPOINT Z ((10 10 1), (90 10 2), (30 70 3), (70 70 4), (50 60 5))", + 0.5, "POLYGON Z ((30 70 3, 70 70 4, 90 10 2, 50 60 5, 10 10 1, 30 70 3))" ); + } + + //------------------------------------------------ + + public void testLengthHolesCircle() { + checkHullWithHolesByLength("MULTIPOINT ((90 20), (80 10), (45 5), (10 20), (20 10), (21 30), (40 20), (11 60), (20 70), (20 90), (40 80), (70 80), (80 60), (90 70), (80 90), (56 95), (95 45), (80 40), (70 20), (15 45), (5 40), (40 96), (60 15))", + 40, "POLYGON ((20 90, 40 96, 56 95, 80 90, 90 70, 95 45, 90 20, 80 10, 45 5, 20 10, 10 20, 5 40, 11 60, 20 90), (20 70, 15 45, 40 20, 70 20, 80 40, 80 60, 70 80, 40 80, 20 70))" ); + } + + public void testLengthHolesCircle0() { + checkHullWithHolesByLength("MULTIPOINT ((90 20), (80 10), (45 5), (10 20), (20 10), (21 30), (40 20), (11 60), (20 70), (20 90), (40 80), (70 80), (80 60), (90 70), (80 90), (56 95), (95 45), (80 40), (70 20), (15 45), (5 40), (40 96), (60 15))", + 0, "POLYGON ((20 90, 40 96, 56 95, 80 90, 90 70, 95 45, 90 20, 80 10, 60 15, 45 5, 20 10, 10 20, 5 40, 11 60, 15 45, 21 30, 40 20, 70 20, 80 40, 80 60, 70 80, 40 80, 20 70, 20 90))" ); + } + + //------------------------------------------------ + + public void testAreaSimple() { + checkHullByArea("MULTIPOINT ((10 10), (90 10), (30 70), (70 70), (50 60))", + .5, "POLYGON ((30 70, 70 70, 90 10, 50 60, 10 10, 30 70))" ); + } + + public void testAreaZero() { + checkHullByArea("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", + 0, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 50 60, 30 30, 10 10))" ); + } + + public void testAreaConvex() { + checkHullByArea("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", + 1, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 10 10))" ); + } + + //========================================================================== + + private void checkHullByLengthFactor(String wkt, double threshold, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConcaveHull.concaveHullByLengthFactor(geom, threshold); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } + + private void checkHullByLengthFactorXYZ(String wkt, double threshold, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConcaveHull.concaveHullByLengthFactor(geom, threshold); + Geometry expected = read(wktExpected); + checkEqualXYZ(expected, actual); + } + + private void checkHullByLength(String wkt, double threshold, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConcaveHull.concaveHullByLength(geom, threshold); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } + + private void checkHullWithHolesByLength(String wkt, double threshold, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConcaveHull.concaveHullByLength(geom, threshold, true); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } + + private void checkHullByArea(String wkt, double threshold, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = ConcaveHull.concaveHullByArea(geom, threshold); + Geometry expected = read(wktExpected); + checkEqual(expected, actual); + } +} diff --git a/modules/lab/src/main/java/org/locationtech/jts/hull/ConcaveHull.java b/modules/lab/src/main/java/org/locationtech/jts/hull/ConcaveHull.java deleted file mode 100644 index da239803b6..0000000000 --- a/modules/lab/src/main/java/org/locationtech/jts/hull/ConcaveHull.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.hull; - -import java.util.List; - -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.triangulate.DelaunayTriangulationBuilder; -import org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision; -import org.locationtech.jts.triangulate.quadedge.QuadEdgeTriangle; - -public class ConcaveHull { - - private Geometry geom; - private double tolerance; - - public ConcaveHull(Geometry geom, double tolerance) { - this.geom = geom; - this.tolerance = tolerance; - } - - public Geometry getResult() { - QuadEdgeSubdivision subdiv = buildDelaunay(); - List tris = extractTriangles(subdiv); - Geometry hull = computeHull(tris); - return hull; - } - - private List extractTriangles(QuadEdgeSubdivision subdiv) { - List qeTris = QuadEdgeTriangle.createOn(subdiv); - return qeTris; - } - - private Geometry computeHull(List tris) { - return null; - - } - - private QuadEdgeSubdivision buildDelaunay() { - DelaunayTriangulationBuilder builder = new DelaunayTriangulationBuilder(); - builder.setSites(geom); - return builder.getSubdivision(); - } -} diff --git a/modules/lab/src/test/java/org/locationtech/jts/hull/ConcaveHullTest.java b/modules/lab/src/test/java/org/locationtech/jts/hull/ConcaveHullTest.java deleted file mode 100644 index 3c8adfbb0e..0000000000 --- a/modules/lab/src/test/java/org/locationtech/jts/hull/ConcaveHullTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016 Vivid Solutions. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html - * and the Eclipse Distribution License is available at - * - * http://www.eclipse.org/org/documents/edl-v10.php. - */ -package org.locationtech.jts.hull; - -import junit.textui.TestRunner; - -import org.locationtech.jts.geom.Geometry; - -import test.jts.GeometryTestCase; - -public class ConcaveHullTest extends GeometryTestCase { - - public static void main(String args[]) { - TestRunner.run(ConcaveHullTest.class); - } - - public ConcaveHullTest(String name) { - super(name); - } - - public void testSimple() { - checkHull( - "POLYGON ((100 200, 200 180, 300 200, 200 190, 100 200))", - 150, - "POLYGON ((100 200, 200 180, 300 200, 200 190, 100 200))" - ); - } - - private void checkHull(String inputWKT, double tolerance, String expectedWKT) { - Geometry input = read(inputWKT); - Geometry expected = read(expectedWKT); - ConcaveHull hull = new ConcaveHull(input, tolerance); - Geometry actual = hull.getResult(); - //checkEqual(expected, actual); - } -} From 922415d4593898cdcf1c93eba48b801191b48463 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 4 Jan 2022 10:35:31 -0800 Subject: [PATCH 165/275] Change ConcaveHull to use lengthRatio terminology Signed-off-by: Martin Davis --- .../function/ConstructionFunctions.java | 12 ++-- .../jts/algorithm/hull/ConcaveHull.java | 58 ++++++++++--------- .../jts/algorithm/hull/ConcaveHullTest.java | 16 ++--- 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java index e3301c8258..97293cf53b 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java @@ -143,16 +143,16 @@ public static Geometry concaveHullWithHolesByLen(Geometry geom, return ConcaveHull.concaveHullByLength(geom, maxLen, true); } - public static Geometry concaveHullByLenFactor(Geometry geom, - @Metadata(title="Length factor") + public static Geometry concaveHullByLenRatio(Geometry geom, + @Metadata(title="Length Ratio") double maxLen) { - return ConcaveHull.concaveHullByLengthFactor(geom, maxLen); + return ConcaveHull.concaveHullByLengthRatio(geom, maxLen); } - public static Geometry concaveHullWithHolesByLenFactor(Geometry geom, - @Metadata(title="Length factor") + public static Geometry concaveHullWithHolesByLenRatio(Geometry geom, + @Metadata(title="Length Ratio") double maxLen) { - return ConcaveHull.concaveHullByLengthFactor(geom, maxLen, true); + return ConcaveHull.concaveHullByLengthRatio(geom, maxLen, true); } public static Geometry concaveHullByArea(Geometry geom, diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java index 991ac27c7c..2b260e094a 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -36,21 +36,21 @@ * Constructs a concave hull of a set of points. * The hull is constructed by removing the longest outer edges * of the Delaunay Triangulation of the points - * until certain target criteria are reached. + * until a target criterium is reached. + *

        * The target criteria are: *

          *
        • Maximum Edge Length - the length of the longest edge of the hull is no larger * than this value. - *
        • Maximum Edge Length Factor - determine the Maximum Edge Length + *
        • Maximum Edge Length Ratio - determine the Maximum Edge Length * as a fraction of the difference between the longest and shortest edge lengths * in the Delaunay Triangulation. * This normalizes the Maximum Edge Length to be scale-independent. *
        • Maximum Area Ratio - the ratio of the concave hull area to the convex hull area * will be no larger than this value. *
        - * Usually only a single criteria is specified, but both may be provided. - * The preferred criteria is the Maximum Edge Length Factor, since it is - * scale-independent, and local (so that no assumption needs to be made about the + * The preferred criterium is the Maximum Edge Length Ratio, since it is + * scale-free and local (so that no assumption needs to be made about the * total amount of concavity present). * Other length criteria can be used by setting the Maximum Edge Length. * For example, use a length relative to the longest edge length @@ -63,7 +63,7 @@ *

        * Optionally the concave hull can be allowed to contain holes. * Note that this may result in substantially slower computation, - * and it can produce results of low quality. + * and it can produce results of lower quality. * * @author Martin Davis * @@ -119,17 +119,17 @@ public static Geometry concaveHullByLength(Geometry geom, double maxLength, bool /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum edge length factor. - * The edge length factor is a fraction of the length difference + * using the target criteria of maximum edge length ratio. + * The edge length ratio is a fraction of the length difference * between the longest and shortest edges * in the Delaunay Triangulation of the input points. * * @param geom the input geometry - * @param lengthFactor the target edge length factor + * @param lengthRatio the target edge length factor * @return the concave hull */ - public static Geometry concaveHullByLengthFactor(Geometry geom, double lengthFactor) { - return concaveHullByLengthFactor(geom, lengthFactor, false); + public static Geometry concaveHullByLengthRatio(Geometry geom, double lengthRatio) { + return concaveHullByLengthRatio(geom, lengthRatio, false); } /** @@ -145,9 +145,9 @@ public static Geometry concaveHullByLengthFactor(Geometry geom, double lengthFac * @param isHolesAllowed whether holes are allowed in the result * @return the concave hull */ - public static Geometry concaveHullByLengthFactor(Geometry geom, double lengthFactor, boolean isHolesAllowed) { + public static Geometry concaveHullByLengthRatio(Geometry geom, double lengthRatio, boolean isHolesAllowed) { ConcaveHull hull = new ConcaveHull(geom); - hull.setMaximumEdgeLengthFactor(lengthFactor); + hull.setMaximumEdgeLengthRatio(lengthRatio); hull.setHolesAllowed(isHolesAllowed); return hull.getHull(); } @@ -168,7 +168,7 @@ public static Geometry concaveHullByArea(Geometry geom, double areaRatio) { private Geometry inputGeometry; private double maxEdgeLength = 0.0; - private double maxEdgeLengthFactor = -1; + private double maxEdgeLengthRatio = -1; private double maxAreaRatio = 0.0; private boolean isHolesAllowed = false; private GeometryFactory geomFactory; @@ -205,12 +205,12 @@ public void setMaximumEdgeLength(double edgeLength) { if (edgeLength < 0) throw new IllegalArgumentException("Edge length must be non-negative"); this.maxEdgeLength = edgeLength; - maxEdgeLengthFactor = -1; + maxEdgeLengthRatio = -1; } /** - * Sets the target maximum edge length factor for the concave hull. - * The edge length factor is a fraction of the difference + * Sets the target maximum edge length ratio for the concave hull. + * The edge length ratio is a fraction of the difference * between the longest and shortest edge lengths * in the Delaunay Triangulation of the input points. * It is a value in the range 0 to 1. @@ -220,12 +220,12 @@ public void setMaximumEdgeLength(double edgeLength) { *

      • The value 1.0 produces the convex hull. *
          * - * @param edgeLengthFactor a length factor value between 0 and 1 + * @param edgeLengthRatio a length factor value between 0 and 1 */ - public void setMaximumEdgeLengthFactor(double edgeLengthFactor) { - if (edgeLengthFactor < 0 || edgeLengthFactor > 1) + public void setMaximumEdgeLengthRatio(double edgeLengthRatio) { + if (edgeLengthRatio < 0 || edgeLengthRatio > 1) throw new IllegalArgumentException("Edge length ratio must be in range [0,1]e"); - this.maxEdgeLengthFactor = edgeLengthFactor; + this.maxEdgeLengthRatio = edgeLengthRatio; } /** @@ -265,8 +265,8 @@ public Geometry getHull() { return geomFactory.createPolygon(); } List triList = createDelaunayTriangulation(inputGeometry); - if (maxEdgeLengthFactor >= 0) { - maxEdgeLength = computeTargetEdgeLength(triList, maxEdgeLengthFactor); + if (maxEdgeLengthRatio >= 0) { + maxEdgeLength = computeTargetEdgeLength(triList, maxEdgeLengthRatio); } if (triList.isEmpty()) return inputGeometry.convexHull(); @@ -276,8 +276,8 @@ public Geometry getHull() { } private static double computeTargetEdgeLength(List triList, - double edgeLengthFactor) { - if (edgeLengthFactor == 0) return 0; + double edgeLengthRatio) { + if (edgeLengthRatio == 0) return 0; double maxEdgeLen = -1; double minEdgeLen = -1; for (Tri tri : triList) { @@ -289,9 +289,11 @@ private static double computeTargetEdgeLength(List triList, minEdgeLen = len; } } - //-- ensure all edges are included - if (edgeLengthFactor == 1) return 2 * maxEdgeLen; - return edgeLengthFactor * (maxEdgeLen - minEdgeLen) + minEdgeLen; + //-- if ratio = 1 ensure all edges are included + if (edgeLengthRatio == 1) + return 2 * maxEdgeLen; + + return edgeLengthRatio * (maxEdgeLen - minEdgeLen) + minEdgeLen; } private void computeHull(List triList) { diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java index 6d8f05ba03..bd68e387a6 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java @@ -72,22 +72,22 @@ public void testLengthSShape() { //------------------------------------------------ public void testLengthFactorZero() { - checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 0, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 60 50, 47 66, 40 60, 30 70))" ); } public void testLengthFactorP5() { - checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 0.5, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 30 70))" ); } public void testLengthFactorOne() { - checkHullByLengthFactor("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", + checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 1, "POLYGON ((10 10, 10 90, 90 90, 90 60, 90 10, 10 10))" ); } public void testLengthFactorXYZChevronP5() { - checkHullByLengthFactorXYZ("MULTIPOINT Z ((10 10 1), (90 10 2), (30 70 3), (70 70 4), (50 60 5))", + checkHullByLengthRatioXYZ("MULTIPOINT Z ((10 10 1), (90 10 2), (30 70 3), (70 70 4), (50 60 5))", 0.5, "POLYGON Z ((30 70 3, 70 70 4, 90 10 2, 50 60 5, 10 10 1, 30 70 3))" ); } @@ -122,16 +122,16 @@ public void testAreaConvex() { //========================================================================== - private void checkHullByLengthFactor(String wkt, double threshold, String wktExpected) { + private void checkHullByLengthRatio(String wkt, double threshold, String wktExpected) { Geometry geom = read(wkt); - Geometry actual = ConcaveHull.concaveHullByLengthFactor(geom, threshold); + Geometry actual = ConcaveHull.concaveHullByLengthRatio(geom, threshold); Geometry expected = read(wktExpected); checkEqual(expected, actual); } - private void checkHullByLengthFactorXYZ(String wkt, double threshold, String wktExpected) { + private void checkHullByLengthRatioXYZ(String wkt, double threshold, String wktExpected) { Geometry geom = read(wkt); - Geometry actual = ConcaveHull.concaveHullByLengthFactor(geom, threshold); + Geometry actual = ConcaveHull.concaveHullByLengthRatio(geom, threshold); Geometry expected = read(wktExpected); checkEqualXYZ(expected, actual); } From 8bc4d766ffac7bb215c3ceb2f4f0004028bf6647 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 4 Jan 2022 10:57:32 -0800 Subject: [PATCH 166/275] ConcaveHull Javadoc Signed-off-by: Martin Davis --- .../org/locationtech/jts/algorithm/hull/ConcaveHull.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java index 2b260e094a..dfc46b00c4 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -44,10 +44,12 @@ * than this value. *
        • Maximum Edge Length Ratio - determine the Maximum Edge Length * as a fraction of the difference between the longest and shortest edge lengths - * in the Delaunay Triangulation. - * This normalizes the Maximum Edge Length to be scale-independent. + * in the Delaunay Triangulation. + * This normalizes the Maximum Edge Length to be scale-free. + * A value of 1 produces the convex hull; a value of 0 produces maximum concaveness. *
        • Maximum Area Ratio - the ratio of the concave hull area to the convex hull area - * will be no larger than this value. + * will be no larger than this value. + * A value of 1 produces the convex hull; a value of 0 produces maximum concaveness. *
        * The preferred criterium is the Maximum Edge Length Ratio, since it is * scale-free and local (so that no assumption needs to be made about the From 4e3d8b2c7fa212f0658e1ceea5371bf4f5bebff9 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 6 Jan 2022 18:54:07 -0800 Subject: [PATCH 167/275] Add LAB BezierCurve Signed-off-by: Martin Davis --- .../jts/algorithm/construct/BezierCurve.java | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java diff --git a/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java b/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java new file mode 100644 index 0000000000..e5c0954329 --- /dev/null +++ b/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java @@ -0,0 +1,275 @@ +package org.locationtech.jts.algorithm.construct; +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.Polygon; + +/** + * Creates a curved line or polygon using Bezier Curves + * defined by the segments of the input. + * + */ +public class BezierCurve { + + /** + * Creates a curved line or polygon using Bezier Curves + * defined by the segments of the input. + * + * @param geom the geometry defining the curve + * @param alpha roundness parameter (0 = linear, 1 = round, 2 = distorted) + * @return + */ + public static Geometry bezierCurve(Geometry geom, double alpha) { + BezierCurve curve = new BezierCurve(geom, alpha); + return curve.getResult(); + } + + private double minSegmentLength = 0.0; + private int numVerticesPerSegment = 10; + + private Geometry inputGeom; + private double alpha; + private final GeometryFactory geomFactory; + + private Coordinate[] bezierCurvePts; + private CubicBezierInterpolationParam[] interpolationParam; + + /** + * Creates a new Bezier Curve instance. + * + * @param geom geometry defining curve + * @param alpha roundness parameter (0 = linear, 1 = round, 2 = distorted) + */ + BezierCurve(Geometry geom, double alpha) { + this.inputGeom = geom; + if ( alpha < 0.0 ) alpha = 0; + this.alpha = alpha; + this.geomFactory = geom.getFactory(); + } + + public Geometry getResult() { + bezierCurvePts = new Coordinate[numVerticesPerSegment]; + interpolationParam = CubicBezierInterpolationParam.compute(numVerticesPerSegment); + + if (inputGeom instanceof LineString) + return bezierLine((LineString) inputGeom); + if (inputGeom instanceof Polygon) + return bezierPolygon((Polygon) inputGeom); + return null; + } + + private LineString bezierLine(LineString ls) { + Coordinate[] coords = ls.getCoordinates(); + + Coordinate[][] control = controlPoints(coords, false, alpha); + + final int N = coords.length; + List curvePts = new ArrayList(); + for (int i = 0; i < N - 1; i++) { + double len = coords[i].distance(coords[i + 1]); + if ( len < minSegmentLength ) { + // segment too short - copy input coordinate + curvePts.add(new Coordinate(coords[i])); + + } else { + cubicBezier(coords[i], coords[i + 1], control[i][1], control[i + 1][0], + interpolationParam, bezierCurvePts); + + int copyN = i < N - 1 ? bezierCurvePts.length - 1 : bezierCurvePts.length; + for (int k = 0; k < copyN; k++) { + curvePts.add(bezierCurvePts[k]); + } + } + } + curvePts.add(coords[N - 1]); + return geomFactory.createLineString(curvePts.toArray(new Coordinate[0])); + } + + private Polygon bezierPolygon(Polygon poly) { + Coordinate[] coords = poly.getExteriorRing().getCoordinates(); + final int N = coords.length - 1; + + Coordinate[][] controlPoints = controlPoints(coords, true, alpha); + List curvePts = new ArrayList(); + for (int i = 0; i < N; i++) { + int next = (i + 1) % N; + + double len = coords[i].distance(coords[next]); + if ( len < minSegmentLength ) { + // segment too short - copy input coordinate + curvePts.add(new Coordinate(coords[i])); + + } else { + cubicBezier(coords[i], coords[next], controlPoints[i][1], controlPoints[next][0], + interpolationParam, bezierCurvePts); + + int copyN = i < N - 1 ? bezierCurvePts.length - 1 : bezierCurvePts.length; + for (int k = 0; k < copyN; k++) { + curvePts.add(bezierCurvePts[k]); + } + } + } + + LinearRing shell = geomFactory.createLinearRing(curvePts.toArray(new Coordinate[0])); + return geomFactory.createPolygon(shell, null); + } + + + private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double alpha) { + final int N = isRing ? coords.length - 1 : coords.length; + double a1 = 1 - alpha; + Coordinate[][] ctrl = new Coordinate[N][2]; + + Coordinate v1 = coords[0]; + Coordinate v2 = coords[1]; + if (isRing) { + v1 = coords[N - 1]; + v2 = coords[0]; + } + + double mid1x = (v1.x + v2.x) / 2.0; + double mid1y = (v1.y + v2.y) / 2.0; + double len1 = v1.distance(v2); + + final int start = isRing ? 0 : 1; + final int end = isRing ? N : N-1; + for (int i = start; i < end; i++) { + v1 = coords[i]; + v2 = coords[i + 1]; + + double mid0x = mid1x; + double mid0y = mid1y; + mid1x = (v1.x + v2.x) / 2.0; + mid1y = (v1.y + v2.y) / 2.0; + + double len0 = len1; + len1 = v1.distance(v2); + + double p = len0 / (len0 + len1); + double anchorx = mid0x + p * (mid1x - mid0x); + double anchory = mid0y + p * (mid1y - mid0y); + double xdelta = anchorx - v1.x; + double ydelta = anchory - v1.y; + + ctrl[i][0] = new Coordinate( + a1 * (v1.x - mid0x + xdelta) + mid0x - xdelta, + a1 * (v1.y - mid0y + ydelta) + mid0y - ydelta); + + ctrl[i][1] = new Coordinate( + a1 * (v1.x - mid1x + xdelta) + mid1x - xdelta, + a1 * (v1.y - mid1y + ydelta) + mid1y - ydelta); + //System.out.println(WKTWriter.toLineString(v[1], ctrl[i][0])); + //System.out.println(WKTWriter.toLineString(v[1], ctrl[i][1])); + } + /** + * For a line, + * use mirrored control points for start and end vertex, + * to produce a symmetric curve for the first and last segments. + */ + if (! isRing) { + ctrl[0][1] = mirrorControlPoint(ctrl[1][0], coords[1], coords[0]); + ctrl[N - 1][0] = mirrorControlPoint(ctrl[N - 2][1], coords[N - 1], coords[N - 2]); + } + return ctrl; + } + + private static Coordinate mirrorControlPoint(Coordinate c, Coordinate p0, Coordinate p1) { + double vlinex = p1.x - p0.x; + double vliney = p1.y - p0.y; + // rotate line vector by 90 + double vrotx = -vliney; + double vroty = vlinex; + + double midx = (p0.x + p1.x) / 2; + double midy = (p0.y + p1.y) / 2; + + return reflectPointInLine(c, new Coordinate(midx, midy), new Coordinate(midx + vrotx, midy + vroty)); + } + + private static Coordinate reflectPointInLine(Coordinate p, Coordinate p0, Coordinate p1) { + double vx = p1.x - p0.x; + double vy = p1.y - p0.y; + double x = p0.x - p.x; + double y = p0.y - p.y; + double r = 1 / (vx * vx + vy * vy); + double rx = p.x + 2 * (x - x * vx * vx * r - y * vx * vy * r); + double ry = p.y + 2 * (y - y * vy * vy * r - x * vx * vy * r); + return new Coordinate(rx, ry); + } + + /** + * Calculates vertices along a cubic Bezier curve. + * + * @param start start point + * @param end end point + * @param ctrl1 first control point + * @param ctrl2 second control point + * @param ip interpolation parameters + * @param curve array to hold generated points + */ + private void cubicBezier(final Coordinate start, + final Coordinate end, final Coordinate ctrl1, + final Coordinate ctrl2, CubicBezierInterpolationParam[] ip, Coordinate[] curve) { + + int n = curve.length; + curve[0] = new Coordinate(start); + curve[n - 1] = new Coordinate(end); + + for (int i = 1; i < n - 1; i++) { + Coordinate c = new Coordinate(); + + c.x = ip[i].t[0] * start.x + ip[i].t[1] * ctrl1.x + ip[i].t[2] * ctrl2.x + ip[i].t[3] * end.x; + c.x /= ip[i].tsum; + c.y = ip[i].t[0] * start.y + ip[i].t[1] * ctrl1.y + ip[i].t[2] * ctrl2.y + ip[i].t[3] * end.y; + c.y /= ip[i].tsum; + + curve[i] = c; + } + } + + private static final class CubicBezierInterpolationParam { + double[] t = new double[4]; + double tsum; + + /** + * Gets the interpolation parameters for a Bezier curve approximated by the + * given number of vertices. + * + * @param n number of vertices + * @return array of {@code InterpPoint} objects holding the parameter values + */ + private static CubicBezierInterpolationParam[] compute(int n) { + CubicBezierInterpolationParam[] param = new CubicBezierInterpolationParam[n]; + + for (int i = 0; i < n; i++) { + double t = (double) i / (n - 1); + double tc = 1.0 - t; + + param[i] = new CubicBezierInterpolationParam(); + param[i].t[0] = tc * tc * tc; + param[i].t[1] = 3.0 * tc * tc * t; + param[i].t[2] = 3.0 * tc * t * t; + param[i].t[3] = t * t * t; + param[i].tsum = param[i].t[0] + param[i].t[1] + param[i].t[2] + param[i].t[3]; + } + return param; + } + } + +} \ No newline at end of file From 7cf996260d60f376bacc2f068d31e45b45368234 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 6 Jan 2022 18:56:39 -0800 Subject: [PATCH 168/275] Fix header Signed-off-by: Martin Davis --- .../org/locationtech/jts/algorithm/construct/BezierCurve.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java b/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java index e5c0954329..4851093c37 100644 --- a/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java +++ b/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java @@ -1,4 +1,3 @@ -package org.locationtech.jts.algorithm.construct; /* * Copyright (c) 2021 Martin Davis. * @@ -10,6 +9,8 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ +package org.locationtech.jts.algorithm.construct; + import java.util.ArrayList; import java.util.List; From b4b3907cc5369ebe52619e82b90f4637c7a73b1b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 7 Jan 2022 12:04:05 -0800 Subject: [PATCH 169/275] Add TestBuilder Bezier Curve function Signed-off-by: Martin Davis --- .../function/CreateShapeFunctions.java | 8 ++ .../locationtech/jts/shape}/BezierCurve.java | 91 ++++++++----------- 2 files changed, 48 insertions(+), 51 deletions(-) rename modules/{lab/src/main/java/org/locationtech/jts/algorithm/construct => core/src/main/java/org/locationtech/jts/shape}/BezierCurve.java (77%) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index d98013a236..4bb40c0c85 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -21,6 +21,7 @@ import org.locationtech.jts.awt.*; import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.util.*; +import org.locationtech.jts.shape.BezierCurve; import org.locationtech.jts.util.GeometricShapeFactory; import org.locationtech.jtstest.geomfunction.Metadata; @@ -333,4 +334,11 @@ private static Coordinate[] genSpiralCycle(Coordinate centre, } return pts; } + + @Metadata(description="Construct a Bezier curve of a line or polygon") + public static Geometry bezierCurve(Geometry geom, + @Metadata(title="Alpha (curveness)") + double alpha) { + return BezierCurve.bezierCurve(geom, alpha); + } } diff --git a/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java similarity index 77% rename from modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java rename to modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java index 4851093c37..6d8f6f68c3 100644 --- a/modules/lab/src/main/java/org/locationtech/jts/algorithm/construct/BezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java @@ -9,17 +9,16 @@ * * http://www.eclipse.org/org/documents/edl-v10.php. */ -package org.locationtech.jts.algorithm.construct; - -import java.util.ArrayList; -import java.util.List; +package org.locationtech.jts.shape; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateList; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.WKTWriter; /** * Creates a curved line or polygon using Bezier Curves @@ -33,7 +32,7 @@ public class BezierCurve { * defined by the segments of the input. * * @param geom the geometry defining the curve - * @param alpha roundness parameter (0 = linear, 1 = round, 2 = distorted) + * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) * @return */ public static Geometry bezierCurve(Geometry geom, double alpha) { @@ -55,7 +54,7 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { * Creates a new Bezier Curve instance. * * @param geom geometry defining curve - * @param alpha roundness parameter (0 = linear, 1 = round, 2 = distorted) + * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) */ BezierCurve(Geometry geom, double alpha) { this.inputGeom = geom; @@ -77,60 +76,51 @@ public Geometry getResult() { private LineString bezierLine(LineString ls) { Coordinate[] coords = ls.getCoordinates(); - Coordinate[][] control = controlPoints(coords, false, alpha); - final int N = coords.length; - List curvePts = new ArrayList(); + CoordinateList curvePts = new CoordinateList(); for (int i = 0; i < N - 1; i++) { - double len = coords[i].distance(coords[i + 1]); - if ( len < minSegmentLength ) { - // segment too short - copy input coordinate - curvePts.add(new Coordinate(coords[i])); - - } else { - cubicBezier(coords[i], coords[i + 1], control[i][1], control[i + 1][0], - interpolationParam, bezierCurvePts); - - int copyN = i < N - 1 ? bezierCurvePts.length - 1 : bezierCurvePts.length; - for (int k = 0; k < copyN; k++) { - curvePts.add(bezierCurvePts[k]); - } - } + addCurve(coords[i], coords[i + 1], control[i][1], control[i + 1][0], curvePts); } - curvePts.add(coords[N - 1]); - return geomFactory.createLineString(curvePts.toArray(new Coordinate[0])); + curvePts.add(coords[N - 1], false); + return geomFactory.createLineString(curvePts.toCoordinateArray()); } - private Polygon bezierPolygon(Polygon poly) { - Coordinate[] coords = poly.getExteriorRing().getCoordinates(); + private LinearRing bezierRing(LinearRing ring) { + Coordinate[] coords = ring.getCoordinates(); + Coordinate[][] control = controlPoints(coords, true, alpha); + CoordinateList curvePts = new CoordinateList(); final int N = coords.length - 1; - - Coordinate[][] controlPoints = controlPoints(coords, true, alpha); - List curvePts = new ArrayList(); for (int i = 0; i < N; i++) { int next = (i + 1) % N; - - double len = coords[i].distance(coords[next]); - if ( len < minSegmentLength ) { - // segment too short - copy input coordinate - curvePts.add(new Coordinate(coords[i])); - - } else { - cubicBezier(coords[i], coords[next], controlPoints[i][1], controlPoints[next][0], - interpolationParam, bezierCurvePts); - - int copyN = i < N - 1 ? bezierCurvePts.length - 1 : bezierCurvePts.length; - for (int k = 0; k < copyN; k++) { - curvePts.add(bezierCurvePts[k]); - } - } + addCurve(coords[i], coords[next], control[i][1], control[next][0], curvePts); } + curvePts.closeRing(); - LinearRing shell = geomFactory.createLinearRing(curvePts.toArray(new Coordinate[0])); + return geomFactory.createLinearRing(curvePts.toCoordinateArray()); + } + + private Polygon bezierPolygon(Polygon poly) { + LinearRing shell = bezierRing(poly.getExteriorRing()); return geomFactory.createPolygon(shell, null); } + private void addCurve(Coordinate p0, Coordinate p1, + Coordinate ctrl0, Coordinate crtl1, + CoordinateList curvePts) { + double len = p0.distance(p1); + if ( len < minSegmentLength ) { + // segment too short - copy input coordinate + curvePts.add(new Coordinate(p0)); + + } else { + cubicBezier(p0, p1, ctrl0, crtl1, + interpolationParam, bezierCurvePts); + for (int i = 0; i < bezierCurvePts.length - 1; i++) { + curvePts.add(bezierCurvePts[i], false); + } + } + } private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double alpha) { final int N = isRing ? coords.length - 1 : coords.length; @@ -175,13 +165,12 @@ private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double ctrl[i][1] = new Coordinate( a1 * (v1.x - mid1x + xdelta) + mid1x - xdelta, a1 * (v1.y - mid1y + ydelta) + mid1y - ydelta); - //System.out.println(WKTWriter.toLineString(v[1], ctrl[i][0])); - //System.out.println(WKTWriter.toLineString(v[1], ctrl[i][1])); + //System.out.println(WKTWriter.toLineString(v1, ctrl[i][0])); + //System.out.println(WKTWriter.toLineString(v1, ctrl[i][1])); } /** - * For a line, - * use mirrored control points for start and end vertex, - * to produce a symmetric curve for the first and last segments. + * For a line, produce a symmetric curve for the first and last segments + * by using mirrored control points for start and end vertex, */ if (! isRing) { ctrl[0][1] = mirrorControlPoint(ctrl[1][0], coords[1], coords[0]); From 022687565a004dacb2d69ff2e8afa7b68c7fbc21 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 7 Jan 2022 17:08:00 -0800 Subject: [PATCH 170/275] Fix ConcaveHull error msg typo Signed-off-by: Martin Davis --- .../java/org/locationtech/jts/algorithm/hull/ConcaveHull.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java index dfc46b00c4..696e204cb6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -226,7 +226,7 @@ public void setMaximumEdgeLength(double edgeLength) { */ public void setMaximumEdgeLengthRatio(double edgeLengthRatio) { if (edgeLengthRatio < 0 || edgeLengthRatio > 1) - throw new IllegalArgumentException("Edge length ratio must be in range [0,1]e"); + throw new IllegalArgumentException("Edge length ratio must be in range [0,1]"); this.maxEdgeLengthRatio = edgeLengthRatio; } From 7c89c9b5b4233347d6aca84b614ba0c887f6738c Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 Jan 2022 11:58:53 -0800 Subject: [PATCH 171/275] Fix WKTReader to produce correct XY coordinate dimension for POLYGON EMPTY (#828) Signed-off-by: Martin Davis --- .../org/locationtech/jts/io/WKTReader.java | 31 ++++++++++++------- .../locationtech/jts/io/WKTReaderTest.java | 26 ++++++++++++++++ 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java index c80f3b8232..6bf6b8bec6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java @@ -55,7 +55,16 @@ * * The WKTReader converts all input numbers to the precise * internal representation. - * + *

        + * As of version 1.15, JTS can read (but not write) WKT syntax + * which specifies coordinate dimension Z, M or ZM as modifiers (e.g. POINT Z) + * or in the name of the geometry type (e.g. LINESTRINGZM). + * If the coordinate dimension is specified it will be set in the created geometry. + * If the coordinate dimension is not specified, the default behaviour is to + * create XYZ geometry (this is backwards compatible with older JTS versions). + * This can be altered to create XY geometry by + * calling {@link #setIsOldJtsCoordinateSyntaxAllowed(boolean)}. + * *

        Notes:

        *
          *
        • Keywords are case-insensitive. @@ -64,17 +73,11 @@ * numbers to floating point. This means it supports the Java * syntax for floating point literals (including scientific notation). *
        - * *

        Syntax

        * The following syntax specification describes the version of Well-Known Text * supported by JTS. * (The specification uses a syntax language similar to that used in * the C and Java language specifications.) - *

        - * As of version 1.15, JTS can read (but not write) WKT Strings including Z, M or ZM - * in the name of the geometry type (ex. POINT Z, LINESTRINGZM). - * Note that it only makes the reader more flexible, but JTS could already read - * 3D coordinates from WKT String and still can't read 4D coordinates. * *

          * WKTGeometry: one of
        @@ -118,7 +121,7 @@
          *         | EMPTY
          *
          * Coordinate:
        - *         Number Number Numberopt
        + *         Number Number Numberopt Numberopt
          *
          * Number: A Java-style floating-point number (including NaN, with arbitrary case)
          *
        @@ -325,14 +328,20 @@ private CoordinateSequence getCoordinate(StreamTokenizer tokenizer, EnumSet ordinateFlags)
                   throws IOException, ParseException {
             if (getNextEmptyOrOpener(tokenizer).equals(WKTConstants.EMPTY))
        -      return this.csFactory.create(0, toDimension(ordinateFlags), ordinateFlags.contains(Ordinate.M) ? 1 : 0);
        +      return createCoordinateSequenceEmpty(ordinateFlags);
             
             ArrayList coordinates = new ArrayList();
             do {
               coordinates.add(getCoordinate(tokenizer, ordinateFlags, false));
             } while (getNextCloserOrComma(tokenizer).equals(COMMA));
         
        -    return mergeSequences(coordinates, ordinateFlags);  }
        +    return mergeSequences(coordinates, ordinateFlags);  
        +  }
        +
        +  private CoordinateSequence createCoordinateSequenceEmpty(EnumSet ordinateFlags)
        +      throws IOException, ParseException {
        +    return csFactory.create(0, toDimension(ordinateFlags), ordinateFlags.contains(Ordinate.M) ? 1 : 0);
        +  }
         
           /**
            * Reads a CoordinateSequence from a stream using the given {@link StreamTokenizer}
        @@ -948,7 +957,7 @@ private MultiPoint readMultiPointText(StreamTokenizer tokenizer, EnumSet ordinateFlags) throws IOException, ParseException {
             String nextToken = getNextEmptyOrOpener(tokenizer);
             if (nextToken.equals(WKTConstants.EMPTY)) {
        -        return geometryFactory.createPolygon();
        +        return geometryFactory.createPolygon(createCoordinateSequenceEmpty(ordinateFlags));
             }
             ArrayList holes = new ArrayList();
             LinearRing shell = readLinearRingText(tokenizer, ordinateFlags);
        diff --git a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java
        index 3efe2d1d35..94276d96aa 100644
        --- a/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java
        +++ b/modules/core/src/test/java/org/locationtech/jts/io/WKTReaderTest.java
        @@ -418,6 +418,27 @@ public void testGeometryCollection() throws Exception {
             assertTrue(gc3.isEmpty());
           }
         
        +  public void testEmptyLineDimOldSyntax() throws ParseException {
        +    WKTReader wktReader = new WKTReader();
        +    LineString geom = (LineString) wktReader.read("LINESTRING EMPTY");
        +    int dim = geom.getCoordinateSequence().getDimension();
        +    checkCSDim(geom.getCoordinateSequence(), 3);
        +  }
        +  
        +  public void testEmptyLineDim() throws ParseException {
        +    WKTReader wktReader = new WKTReader();
        +    wktReader.setIsOldJtsCoordinateSyntaxAllowed(false);
        +    LineString geom = (LineString) wktReader.read("LINESTRING EMPTY");
        +    checkCSDim(geom.getCoordinateSequence(), 2);
        +  }
        +  
        +  public void testEmptyPolygonDim() throws ParseException {
        +    WKTReader wktReader = new WKTReader();
        +    wktReader.setIsOldJtsCoordinateSyntaxAllowed(false);
        +    Polygon geom = (Polygon) wktReader.read("POLYGON EMPTY");
        +    checkCSDim(geom.getExteriorRing().getCoordinateSequence(), 2);
        +  }
        +  
           public void testNaN() throws Exception {
         
             // arrange
        @@ -475,6 +496,11 @@ private void checkEmpty(Geometry geom) {
             }
           }
           
        +  private void checkCSDim(CoordinateSequence cs, int expectedCoordDim) {
        +    int dim = cs.getDimension();
        +    assertEquals(expectedCoordDim, dim);
        +  }
        +  
           private static CoordinateSequence[] createSequences(EnumSet ordinateFlags, double[][] xyarray) {
             CoordinateSequence[] csarray = new CoordinateSequence[xyarray.length];
             for (int i = 0; i < xyarray.length; i++) {
        
        From 7ee414f5133f93932422df0f782f8d6a67845973 Mon Sep 17 00:00:00 2001
        From: Martin Davis 
        Date: Wed, 12 Jan 2022 11:59:52 -0800
        Subject: [PATCH 172/275] Update JTS_Version_History.md
        
        ---
         doc/JTS_Version_History.md | 1 +
         1 file changed, 1 insertion(+)
        
        diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md
        index 15322c905b..73077257a1 100644
        --- a/doc/JTS_Version_History.md
        +++ b/doc/JTS_Version_History.md
        @@ -46,6 +46,7 @@ Distributions for older JTS versions can be obtained at the
         * Fix `MaximumInscribedCircle` to avoid infinite-looping on flat collapsed input (#807)
         * Add OverlayNG result area heuristic check (#812)
         * Fix the buffer generated for Mitred Joins (#818)
        +* Fix WKTReader to produce correct XY coordinate dimension for POLYGON EMPTY (#828)
         
         # Version 1.18.2
         
        
        From 8c74918afeb8798f4853a8e8c1e1072bcf95e5f2 Mon Sep 17 00:00:00 2001
        From: Martin Davis 
        Date: Wed, 12 Jan 2022 13:08:42 -0800
        Subject: [PATCH 173/275] Improve ConcaveHull hole performance (#829)
        
        Signed-off-by: Martin Davis 
        ---
         .../jts/algorithm/hull/ConcaveHull.java       | 623 ++++++------------
         .../jts/algorithm/hull/HullTri.java           | 271 ++++++++
         .../jts/algorithm/hull/HullTriangulation.java | 171 +++++
         .../locationtech/jts/triangulate/tri/Tri.java |  90 ++-
         .../jts/algorithm/hull/ConcaveHullTest.java   |  26 +-
         5 files changed, 746 insertions(+), 435 deletions(-)
         create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTri.java
         create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTriangulation.java
        
        diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java
        index 696e204cb6..c9ef0d9b8a 100644
        --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java
        +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java
        @@ -11,26 +11,15 @@
          */
         package org.locationtech.jts.algorithm.hull;
         
        -import java.util.ArrayDeque;
         import java.util.ArrayList;
        -import java.util.Deque;
         import java.util.List;
         import java.util.PriorityQueue;
         
        -import org.locationtech.jts.geom.Coordinate;
        -import org.locationtech.jts.geom.CoordinateList;
         import org.locationtech.jts.geom.Geometry;
         import org.locationtech.jts.geom.GeometryFactory;
        +import org.locationtech.jts.geom.LineString;
        +import org.locationtech.jts.geom.Point;
         import org.locationtech.jts.geom.Polygon;
        -import org.locationtech.jts.geom.Triangle;
        -import org.locationtech.jts.operation.overlayng.CoverageUnion;
        -import org.locationtech.jts.triangulate.DelaunayTriangulationBuilder;
        -import org.locationtech.jts.triangulate.quadedge.QuadEdge;
        -import org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision;
        -import org.locationtech.jts.triangulate.quadedge.TriangleVisitor;
        -import org.locationtech.jts.triangulate.tri.Tri;
        -import org.locationtech.jts.triangulate.tri.TriangulationBuilder;
        -import org.locationtech.jts.util.Assert;
         
         /**
          * Constructs a concave hull of a set of points.
        @@ -54,7 +43,7 @@
          * The preferred criterium is the Maximum Edge Length Ratio, since it is 
          * scale-free and local (so that no assumption needs to be made about the 
          * total amount of concavity present).
        - * Other length criteria can be used by setting the Maximum Edge Length.
        + * Other length criteria can be used by setting the Maximum Edge Length directly.
          * For example, use a length relative  to the longest edge length
          * in the Minimum Spanning Tree of the point set.
          * Or, use a length derived from the {@link #uniformGridEdgeLength(Geometry)} value.
        @@ -64,8 +53,8 @@
          * This constraint may cause the concave hull to fail to meet the target criteria.
          * 

        * Optionally the concave hull can be allowed to contain holes. - * Note that this may result in substantially slower computation, - * and it can produce results of lower quality. + * Note that when using the area-based criterium + * this may result in substantially slower computation. * * @author Martin Davis * @@ -171,7 +160,7 @@ public static Geometry concaveHullByArea(Geometry geom, double areaRatio) { private Geometry inputGeometry; private double maxEdgeLength = 0.0; private double maxEdgeLengthRatio = -1; - private double maxAreaRatio = 0.0; + private double maxAreaRatio = -1; private boolean isHolesAllowed = false; private GeometryFactory geomFactory; @@ -266,25 +255,31 @@ public Geometry getHull() { if (inputGeometry.isEmpty()) { return geomFactory.createPolygon(); } - List triList = createDelaunayTriangulation(inputGeometry); + List triList = HullTriangulation.createDelaunayTriangulation(inputGeometry); if (maxEdgeLengthRatio >= 0) { maxEdgeLength = computeTargetEdgeLength(triList, maxEdgeLengthRatio); } if (triList.isEmpty()) return inputGeometry.convexHull(); - computeHull(triList); - Geometry hull = toPolygon(triList, geomFactory); + + if (maxAreaRatio >= 0) { + computeHullByArea(triList); + } + else { + computeHullByLength(triList); + } + Geometry hull = toGeometry(triList, geomFactory); return hull; } - private static double computeTargetEdgeLength(List triList, + private static double computeTargetEdgeLength(List triList, double edgeLengthRatio) { if (edgeLengthRatio == 0) return 0; double maxEdgeLen = -1; double minEdgeLen = -1; - for (Tri tri : triList) { + for (HullTri tri : triList) { for (int i = 0; i < 3; i++) { - double len = tri.getCoordinate(i).distance(tri.getCoordinate(Tri.next(i))); + double len = tri.getCoordinate(i).distance(tri.getCoordinate(HullTri.next(i))); if (len > maxEdgeLen) maxEdgeLen = len; if (minEdgeLen < 0 || len < minEdgeLen) @@ -298,12 +293,24 @@ private static double computeTargetEdgeLength(List triList, return edgeLengthRatio * (maxEdgeLen - minEdgeLen) + minEdgeLen; } - private void computeHull(List triList) { + //------------------------------------------------ + + /** + * Forms the concave hull using area ratio as the target criteria. + *

        + * When area is used as the criteria, the boundary and holes + * must be eroded together, since the area is affected by both. + * This means that result connectivity has to be checked after + * every triangle removal, which is very slow. + * + * @param triList + */ + private void computeHullByArea(List triList) { //-- used if area is the threshold criteria - double areaConvex = Tri.area(triList); + double areaConvex = HullTri.area(triList); double areaConcave = areaConvex; - PriorityQueue queue = initQueue(triList); + PriorityQueue queue = createBorderQueue(triList); // remove tris in order of decreasing size (edge length) while (! queue.isEmpty()) { if (isBelowAreaThreshold(areaConcave, areaConvex)) @@ -311,18 +318,13 @@ private void computeHull(List triList) { HullTri tri = queue.poll(); - if (isBelowLengthThreshold(tri)) - break; - - if (isRemovable(tri, triList)) { + if (isRemovableByArea(tri, triList)) { //-- the non-null adjacents are now on the border HullTri adj0 = (HullTri) tri.getAdjacent(0); HullTri adj1 = (HullTri) tri.getAdjacent(1); HullTri adj2 = (HullTri) tri.getAdjacent(2); - //-- remove tri - tri.remove(); - triList.remove(tri); + tri.remove(triList); areaConcave -= tri.getArea(); //-- if holes not allowed, add new border adjacents to queue @@ -334,17 +336,89 @@ private void computeHull(List triList) { } } } - - private PriorityQueue initQueue(List triList) { + + private boolean isRemovableByArea(HullTri tri, List triList) { + if (isHolesAllowed) { + return isRemovableByAreaWithHoles(tri, triList); + } + return isRemovableBorder(tri); + } + + private boolean isRemovableByAreaWithHoles(HullTri tri, List triList) { + /** + * Can't remove if that would separate a vertex from the hull + */ + if (tri.isolatedVertexIndex(triList) != -1) + return false; + /** + * This test is slow for large input. + * It could be omitted if a disconnected result was allowed. + */ + if (! HullTri.isConnected(triList, tri)) + return false; + /** + * If tri touches boundary at a single vertex, it can't be removed + * because that might disconnect the triangulation interior. + */ + return ! tri.hasBoundaryTouch(); + } + + private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { + return areaConcave / areaConvex <= maxAreaRatio; + } + + //------------------------------------------------ + + /** + * Computes the concave hull using edge length as the target criteria. + * The erosion is done in two phases: first the border, then any + * internal holes (if required). + * This allows an fast connection check to be used + * when eroding holes, + * which makes this much more efficient than the area-based algorithm. + * + * @param triList + */ + private void computeHullByLength(List triList) { + computeHullBorder(triList); + if (isHolesAllowed) { + computeHullHoles(triList); + } + } + + private void computeHullBorder(List triList) { + PriorityQueue queue = createBorderQueue(triList); + // remove tris in order of decreasing size (edge length) + while (! queue.isEmpty()) { + HullTri tri = queue.poll(); + + if (isBelowLengthThreshold(tri)) + break; + + if (isRemovableBorder(tri)) { + //-- the non-null adjacents are now on the border + HullTri adj0 = (HullTri) tri.getAdjacent(0); + HullTri adj1 = (HullTri) tri.getAdjacent(1); + HullTri adj2 = (HullTri) tri.getAdjacent(2); + + tri.remove(triList); + + //-- add border adjacents to queue + addBorderTri(adj0, queue); + addBorderTri(adj1, queue); + addBorderTri(adj2, queue); + } + } + } + + private PriorityQueue createBorderQueue(List triList) { PriorityQueue queue = new PriorityQueue(); for (HullTri tri : triList) { - if (! isHolesAllowed) { - //-- add only border triangles which could be eroded - // (if tri has only 1 adjacent it can't be removed because that would isolate a vertex) - if (tri.numAdjacent() != 2) - continue; - tri.setSizeToBorder(); - } + //-- add only border triangles which could be eroded + // (if tri has only 1 adjacent it can't be removed because that would isolate a vertex) + if (tri.numAdjacent() != 2) + continue; + tri.setSizeToBoundary(); queue.add(tri); } return queue; @@ -352,422 +426,129 @@ private PriorityQueue initQueue(List triList) { /** * Adds a Tri to the queue. - * Only add tris with a single border edge. - * The ordering size is the length of the border edge. + * Only add tris with a single border edge, + * sice otherwise that would risk isolating a vertex. + * Sets the ordering size to the length of the border edge. * * @param tri the Tri to add - * @param queue the priority queue + * @param queue the priority queue to add to */ private void addBorderTri(HullTri tri, PriorityQueue queue) { if (tri == null) return; if (tri.numAdjacent() != 2) return; - tri.setSizeToBorder(); + tri.setSizeToBoundary(); queue.add(tri); } - - private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { - return areaConcave / areaConvex <= maxAreaRatio; - } private boolean isBelowLengthThreshold(HullTri tri) { - double len = 0; - if (isHolesAllowed) { - len = tri.lengthOfLongestEdge(); - } - else { - len = tri.lengthOfBorder(); - } - return len < maxEdgeLength; - } - - /** - * Tests whether a Tri can be removed while preserving - * the connectivity of the hull. - * - * @param tri the Tri to test - * @param triList - * @return true if the Tri can be removed - */ - private boolean isRemovable(HullTri tri, List triList) { - if (isHolesAllowed) { - /** - * Don't remove if that would separate a single vertex - */ - if (hasVertexSingleAdjacent(tri, triList)) - return false; - return HullTri.isConnected(triList, tri); - } - - //-- compute removable for no holes allowed - int numAdj = tri.numAdjacent(); - /** - * Tri must have exactly 2 adjacent tris. - * If it it has only 0 or 1 adjacent then removal would remove a vertex. - * If it has 3 adjacent then it is not on border. - */ - if (numAdj != 2) return false; - /** - * The tri cannot be removed if it is connecting, because - * this would create more than one result polygon. - */ - return ! isConnecting(tri); + return tri.lengthOfBoundary() < maxEdgeLength; } - - private static boolean hasVertexSingleAdjacent(HullTri tri, List triList) { - for (int i = 0; i < 3; i++) { - if (degree(tri.getCoordinate(i), triList) <= 1) - return true; + + private void computeHullHoles(List triList) { + List candidateHoles = findCandidateHoles(triList, maxEdgeLength); + // remove tris in order of decreasing size (edge length) + for (HullTri tri : candidateHoles) { + if (tri.isRemoved() + || tri.isBorder() + || tri.hasBoundaryTouch()) + continue; + removeHole(triList, tri); } - return false; } - + /** - * The degree of a Tri vertex is the number of tris containing it. - * This must be done by searching the entire triangulation, - * since the containing tris may not be adjacent or edge-connected. + * Finds tris which may be the start of holes. + * Only tris which have a long enough edge and which do not touch the current hull + * boundary are included. + * This avoids the risk of disconnecting the result polygon. + * The list is sorted in decreasing order of edge length. * - * @param v a vertex coordinate - * @param triList a triangulation - * @return the degree of the vertex + * @param triList + * @param minEdgeLen minimum length of edges to consider + * @return */ - private static int degree(Coordinate v, List triList) { - int degree = 0; + private static List findCandidateHoles(List triList, double minEdgeLen) { + List candidates = new ArrayList(); for (HullTri tri : triList) { - for (int i = 0; i < 3; i++) { - if (v.equals2D(tri.getCoordinate(i))) - degree++; + if (tri.getSize() < minEdgeLen) continue; + boolean isTouchingBoundary = tri.isBorder() || tri.hasBoundaryTouch(); + if (! isTouchingBoundary) { + candidates.add(tri); } } - return degree; + // sort by HullTri comparator - longest edge length first + candidates.sort(null); + return candidates; } - - /** - * Tests if a tri is the only one connecting its 2 adjacents. - * Assumes that the tri is on the border of the triangulation - * and that the triangulation does not contain holes - * - * @param tri the tri to test - * @return true if the tri is the only connection - */ - private static boolean isConnecting(Tri tri) { - int adj2Index = adjacent2VertexIndex(tri); - boolean isInterior = isInteriorVertex(tri, adj2Index); - return ! isInterior; - } - + /** - * A vertex of a triangle is interior if it - * is fully surrounded by triangles. - * - * @param tri a tri containing the vertex - * @param index the vertex index - * @return true if the vertex is interior + * Erodes a hole starting at a given triangle, + * and eroding all adjacent triangles with boundary edge length above target. + * @param triList the triangulation + * @param triHole triangle which is a hole */ - private static boolean isInteriorVertex(Tri triStart, int index) { - Tri curr = triStart; - int currIndex = index; - do { - Tri adj = curr.getAdjacent(currIndex); - if (adj == null) return false; - int adjIndex = adj.getIndex(curr); - curr = adj; - currIndex = Tri.next(adjIndex); + private void removeHole(List triList, HullTri triHole) { + PriorityQueue queue = new PriorityQueue(); + queue.add(triHole); + + while (! queue.isEmpty()) { + HullTri tri = queue.poll(); + + if (tri != triHole && isBelowLengthThreshold(tri)) + break; + + if (tri == triHole || isRemovableHole(tri)) { + //-- the non-null adjacents are now on the border + HullTri adj0 = (HullTri) tri.getAdjacent(0); + HullTri adj1 = (HullTri) tri.getAdjacent(1); + HullTri adj2 = (HullTri) tri.getAdjacent(2); + + tri.remove(triList); + + //-- add border adjacents to queue + addBorderTri(adj0, queue); + addBorderTri(adj1, queue); + addBorderTri(adj2, queue); + } } - while (curr != triStart); - return true; - } - - private static int adjacent2VertexIndex(Tri tri) { - if (tri.hasAdjacent(0) && tri.hasAdjacent(1)) return 1; - if (tri.hasAdjacent(1) && tri.hasAdjacent(2)) return 2; - if (tri.hasAdjacent(2) && tri.hasAdjacent(0)) return 0; - return -1; } - private static class HullTri extends Tri - implements Comparable - { - private double size; - private boolean isMarked = false; - - public HullTri(Coordinate p0, Coordinate p1, Coordinate p2) { - super(p0, p1, p2); - this.size = lengthOfLongestEdge(); - } - - public double getSize() { - return size; - } - + private boolean isRemovableBorder(HullTri tri) { /** - * Sets the size to be the length of the border edges. - * This is used when constructing hull without holes, - * by erosion from the triangulation border. + * Tri must have exactly 2 adjacent tris (i.e. a single boundary edge). + * If it it has only 0 or 1 adjacent then removal would remove a vertex. + * If it has 3 adjacent then it is not on border. */ - public void setSizeToBorder() { - size = lengthOfBorder(); - } - - public boolean isMarked() { - return isMarked; - } - - public void setMarked(boolean isMarked) { - this.isMarked = isMarked; - } - - public boolean isBorder() { - return isBorder(0) || isBorder(1) || isBorder(2); - } - - public boolean isBorder(int index) { - return ! hasAdjacent(index); - } - - public int borderIndex() { - if (isBorder(0)) return 0; - if (isBorder(1)) return 1; - if (isBorder(2)) return 2; - return -1; - } - + if (tri.numAdjacent() != 2) return false; /** - * Gets the most CCW border edge index. - * This assumes there is at least one non-border edge. - * - * @return the CCW border edge index + * The tri cannot be removed if it is connecting, because + * this would create more than one result polygon. */ - public int borderIndexCCW() { - int index = borderIndex(); - int prevIndex = prev(index); - if (isBorder(prevIndex)) { - return prevIndex; - } - return index; - } - + return ! tri.isConnecting(); + } + + private boolean isRemovableHole(HullTri tri) { /** - * Gets the most CW border edge index. - * This assumes there is at least one non-border edge. - * - * @return the CW border edge index + * Tri must have exactly 2 adjacent tris (i.e. a single boundary edge). + * If it it has only 0 or 1 adjacent then removal would remove a vertex. + * If it has 3 adjacent then it is not connected to hole. */ - public int borderIndexCW() { - int index = borderIndex(); - int nextIndex = next(index); - if (isBorder(nextIndex)) { - return nextIndex; - } - return index; - } - - public double lengthOfLongestEdge() { - return Triangle.longestSideLength(p0, p1, p2); - } - - private double lengthOfBorder() { - double len = 0.0; - for (int i = 0; i < 3; i++) { - if (! hasAdjacent(i)) { - len += getCoordinate(i).distance(getCoordinate(Tri.next(i))); - } - } - return len; - } - - public HullTri nextBorderTri() { - HullTri tri = this; - //-- start at first non-border edge CW - int index = next(borderIndexCW()); - //-- scan CCW around vertex for next border tri - do { - HullTri adjTri = (HullTri) tri.getAdjacent(index); - if (adjTri == this) - throw new IllegalStateException("No outgoing border edge found"); - index = next(adjTri.getIndex(tri)); - tri = adjTri; - } - while (! tri.isBorder(index)); - return (tri); - } - + if (tri.numAdjacent() != 2) return false; /** - * PriorityQueues sort in ascending order. - * To sort with the largest at the head, - * smaller sizes must compare as greater than larger sizes. - * (i.e. the normal numeric comparison is reversed). - * If the sizes are identical (which should be an infrequent case), - * the areas are compared, with larger areas sorting before smaller. - * (The rationale is that larger areas indicate an area of lower point density, - * which is more likely to be in the exterior of the computed shape.) - * This improves the determinism of the queue ordering. + * Ensure removal does not disconnect hull area. + * This is a fast check which ensure holes and boundary + * do not touch at single points. + * (But it is slightly over-strict, since it prevents + * any touching holes.) */ - @Override - public int compareTo(HullTri o) { - /** - * If size is identical compare areas to ensure a (more) deterministic ordering. - * Larger areas sort before smaller ones. - */ - if (size == o.size) { - return -Double.compare(this.getArea(), o.getArea()); - } - return -Double.compare(size, o.size); - } - - public static boolean isConnected(List triList, HullTri exceptTri) { - if (triList.size() == 0) return false; - clearMarks(triList); - HullTri triStart = findTri(triList, exceptTri); - if (triStart == null) return false; - markConnected(triStart, exceptTri); - exceptTri.setMarked(true); - return isAllMarked(triList); - } - - public static void clearMarks(List triList) { - for (HullTri tri : triList) { - tri.setMarked(false); - } - } - - public static HullTri findTri(List triList, Tri exceptTri) { - for (HullTri tri : triList) { - if (tri != exceptTri) return tri; - } - return null; - } - - public static boolean isAllMarked(List triList) { - for (HullTri tri : triList) { - if (! tri.isMarked()) - return false; - } - return true; - } - - public static void markConnected(HullTri triStart, Tri exceptTri) { - Deque queue = new ArrayDeque(); - queue.add(triStart); - while (! queue.isEmpty()) { - HullTri tri = queue.pop(); - tri.setMarked(true); - for (int i = 0; i < 3; i++) { - HullTri adj = (HullTri) tri.getAdjacent(i); - //-- don't connect thru this tri - if (adj == exceptTri) - continue; - if (adj != null && ! adj.isMarked() ) { - queue.add(adj); - } - } - } - } - } - - private static List createDelaunayTriangulation(Geometry geom) { - //TODO: implement a DT on Tris directly? - DelaunayTriangulationBuilder dt = new DelaunayTriangulationBuilder(); - dt.setSites(geom); - QuadEdgeSubdivision subdiv = dt.getSubdivision(); - List triList = toTris(subdiv); - return triList; - } - - private static List toTris(QuadEdgeSubdivision subdiv) { - HullTriVisitor visitor = new HullTriVisitor(); - subdiv.visitTriangles(visitor, false); - List triList = visitor.getTriangles(); - TriangulationBuilder.build(triList); - return triList; + return ! tri.hasBoundaryTouch(); } - private static class HullTriVisitor implements TriangleVisitor { - private List triList = new ArrayList(); - - public HullTriVisitor() { - } - - public void visit(QuadEdge[] triEdges) { - Coordinate p0 = triEdges[0].orig().getCoordinate(); - Coordinate p1 = triEdges[1].orig().getCoordinate(); - Coordinate p2 = triEdges[2].orig().getCoordinate(); - HullTri tri; - if (Triangle.isCCW(p0, p1, p2)) { - tri = new HullTri(p0, p2, p1); - } - else { - tri = new HullTri(p0, p1, p2); - } - triList.add(tri); - } - - public List getTriangles() { - return triList; - } - } - - private Geometry toPolygon(List triList, GeometryFactory geomFactory) { + private Geometry toGeometry(List triList, GeometryFactory geomFactory) { if (! isHolesAllowed) { - return extractPolygon(triList, geomFactory); + return HullTriangulation.traceBoundaryPolygon(triList, geomFactory); } //-- in case holes are present use union (slower but handles holes) - return union(triList, geomFactory); - } - - private Geometry extractPolygon(List triList, GeometryFactory geomFactory) { - if (triList.size() == 1) { - Tri tri = triList.get(0); - return tri.toPolygon(geomFactory); - } - Coordinate[] pts = traceBorder(triList); - return geomFactory.createPolygon(pts); - } - - private static Geometry union(List triList, GeometryFactory geomFactory) { - List polys = new ArrayList(); - for (Tri tri : triList) { - Polygon poly = tri.toPolygon(geomFactory); - polys.add(poly); - } - return CoverageUnion.union(geomFactory.buildGeometry(polys)); - } - - /** - * Extracts the coordinates along the border of a triangulation, - * by tracing CW around the border triangles. - * Assumption: there are at least 2 tris, they are connected, - * and there are no holes. - * So each tri has at least one non-border edge, and there is only one border. - * - * @param triList the triangulation - * @return the border of the triangulation - */ - private static Coordinate[] traceBorder(List triList) { - HullTri triStart = findBorderTri(triList); - CoordinateList coordList = new CoordinateList(); - HullTri tri = triStart; - do { - int borderIndex = tri.borderIndexCCW(); - //-- add border vertex - coordList.add(tri.getCoordinate(borderIndex).copy(), false); - int nextIndex = Tri.next(borderIndex); - //-- if next edge is also border, add it and move to next - if (tri.isBorder(nextIndex)) { - coordList.add(tri.getCoordinate(nextIndex).copy(), false); - borderIndex = nextIndex; - } - //-- find next border tri CCW around non-border edge - tri = tri.nextBorderTri(); - } while (tri != triStart); - coordList.closeRing(); - return coordList.toCoordinateArray(); - } - - public static HullTri findBorderTri(List triList) { - for (HullTri tri : triList) { - if (tri.isBorder()) return tri; - } - Assert.shouldNeverReachHere("No border triangles found"); - return null; + return HullTriangulation.union(triList, geomFactory); } } diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTri.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTri.java new file mode 100644 index 0000000000..90dd794476 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTri.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.hull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Triangle; +import org.locationtech.jts.triangulate.tri.Tri; + +/** + * Tris which are used to form a concave hull. + * If a Tri has an edge (or edges) with no adjacent tri + * the tri is on the border of the triangulation. + * The edge is a boundary edge. + * The union of those edges + * forms the (linear) boundary of the triangulation. + * The triangulation area may be a Polygon or MultiPolygon, and may or may not contain holes. + * + * @author Martin Davis + * + */ +class HullTri extends Tri + implements Comparable +{ + private double size; + private boolean isMarked = false; + + public HullTri(Coordinate p0, Coordinate p1, Coordinate p2) { + super(p0, p1, p2); + this.size = lengthOfLongestEdge(); + } + + public double getSize() { + return size; + } + + /** + * Sets the size to be the length of the boundary edges. + * This is used when constructing hull without holes, + * by erosion from the triangulation border. + */ + public void setSizeToBoundary() { + size = lengthOfBoundary(); + } + + public boolean isMarked() { + return isMarked; + } + + public void setMarked(boolean isMarked) { + this.isMarked = isMarked; + } + + public boolean isRemoved() { + return ! hasAdjacent(); + } + + /** + * Gets an index of a boundary edge, if there is one. + * + * @return a boundary edge index, or -1 + */ + public int boundaryIndex() { + if (isBoundary(0)) return 0; + if (isBoundary(1)) return 1; + if (isBoundary(2)) return 2; + return -1; + } + + /** + * Gets the most CCW boundary edge index. + * This assumes there is at least one non-boundary edge. + * + * @return the CCW boundary edge index + */ + public int boundaryIndexCCW() { + int index = boundaryIndex(); + if (index < 0) return -1; + int prevIndex = prev(index); + if (isBoundary(prevIndex)) { + return prevIndex; + } + return index; + } + + /** + * Gets the most CW boundary edge index. + * This assumes there is at least one non-boundary edge. + * + * @return the CW boundary edge index + */ + public int boundaryIndexCW() { + int index = boundaryIndex(); + if (index < 0) return -1; + int nextIndex = next(index); + if (isBoundary(nextIndex)) { + return nextIndex; + } + return index; + } + + /** + * Tests if a tri is the only one connecting its 2 adjacents. + * Assumes that the tri is on the border of the triangulation + * and that the triangulation does not contain holes + * + * @param tri the tri to test + * @return true if the tri is the only connection + */ + public boolean isConnecting() { + int adj2Index = adjacent2VertexIndex(); + boolean isInterior = isInteriorVertex(adj2Index); + return ! isInterior; + } + + /** + * Gets the index of a vertex which is adjacent to two other tris (if any). + * + * @return the vertex index, or -1 + */ + public int adjacent2VertexIndex() { + if (hasAdjacent(0) && hasAdjacent(1)) return 1; + if (hasAdjacent(1) && hasAdjacent(2)) return 2; + if (hasAdjacent(2) && hasAdjacent(0)) return 0; + return -1; + } + + /** + * Tests whether some vertex of this Tri has degree = 1. + * In this case it is not in any other Tris. + * + * @param tri + * @param triList + * @return true if a vertex has degree 1 + */ + public int isolatedVertexIndex(List triList) { + for (int i = 0; i < 3; i++) { + if (degree(i, triList) <= 1) + return i; + } + return -1; + } + + public double lengthOfLongestEdge() { + return Triangle.longestSideLength(p0, p1, p2); + } + + double lengthOfBoundary() { + double len = 0.0; + for (int i = 0; i < 3; i++) { + if (! hasAdjacent(i)) { + len += getCoordinate(i).distance(getCoordinate(Tri.next(i))); + } + } + return len; + } + + /** + * PriorityQueues sort in ascending order. + * To sort with the largest at the head, + * smaller sizes must compare as greater than larger sizes. + * (i.e. the normal numeric comparison is reversed). + * If the sizes are identical (which should be an infrequent case), + * the areas are compared, with larger areas sorting before smaller. + * (The rationale is that larger areas indicate an area of lower point density, + * which is more likely to be in the exterior of the computed shape.) + * This improves the determinism of the queue ordering. + */ + @Override + public int compareTo(HullTri o) { + /** + * If size is identical compare areas to ensure a (more) deterministic ordering. + * Larger areas sort before smaller ones. + */ + if (size == o.size) { + return -Double.compare(this.getArea(), o.getArea()); + } + return -Double.compare(size, o.size); + } + + /** + * Tests if this tri has a vertex which is in the boundary, + * but not in a boundary edge. + * + * @return true if the tri touches the boundary at a vertex + */ + public boolean hasBoundaryTouch() { + for (int i = 0; i < 3; i++) { + if (isBoundaryTouch(i)) + return true; + } + return false; + } + + private boolean isBoundaryTouch(int index) { + //-- If vertex is in a boundary edge it is not a touch + if (isBoundary(index)) return false; + if (isBoundary(prev(index))) return false; + //-- if vertex is not in interior it is on boundary + return ! isInteriorVertex(index); + } + + public static HullTri findTri(List triList, Tri exceptTri) { + for (HullTri tri : triList) { + if (tri != exceptTri) return tri; + } + return null; + } + + public static boolean isAllMarked(List triList) { + for (HullTri tri : triList) { + if (! tri.isMarked()) + return false; + } + return true; + } + + public static void clearMarks(List triList) { + for (HullTri tri : triList) { + tri.setMarked(false); + } + } + + public static void markConnected(HullTri triStart, Tri exceptTri) { + Deque queue = new ArrayDeque(); + queue.add(triStart); + while (! queue.isEmpty()) { + HullTri tri = queue.pop(); + tri.setMarked(true); + for (int i = 0; i < 3; i++) { + HullTri adj = (HullTri) tri.getAdjacent(i); + //-- don't connect thru this tri + if (adj == exceptTri) + continue; + if (adj != null && ! adj.isMarked() ) { + queue.add(adj); + } + } + } + } + + /** + * Tests if a triangulation is edge-connected, if a triangle is removed. + * NOTE: this is a relatively slow operation. + * + * @param triList the triangulation + * @param removedTri the triangle to remove + * @return true if the triangulation is still connnected + */ + public static boolean isConnected(List triList, HullTri removedTri) { + if (triList.size() == 0) return false; + clearMarks(triList); + HullTri triStart = findTri(triList, removedTri); + if (triStart == null) return false; + markConnected(triStart, removedTri); + removedTri.setMarked(true); + return isAllMarked(triList); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTriangulation.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTriangulation.java new file mode 100644 index 0000000000..3a01f1b282 --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/HullTriangulation.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.algorithm.hull; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateList; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.Triangle; +import org.locationtech.jts.operation.overlayng.CoverageUnion; +import org.locationtech.jts.triangulate.DelaunayTriangulationBuilder; +import org.locationtech.jts.triangulate.quadedge.QuadEdge; +import org.locationtech.jts.triangulate.quadedge.QuadEdgeSubdivision; +import org.locationtech.jts.triangulate.quadedge.TriangleVisitor; +import org.locationtech.jts.triangulate.tri.Tri; +import org.locationtech.jts.triangulate.tri.TriangulationBuilder; +import org.locationtech.jts.util.Assert; + +/** + * Functions to operate on triangulations represented as + * lists of {@link HullTri}s. + * + * @author mdavis + * + */ +class HullTriangulation +{ + public static List createDelaunayTriangulation(Geometry geom) { + //TODO: implement a DT on Tris directly? + DelaunayTriangulationBuilder dt = new DelaunayTriangulationBuilder(); + dt.setSites(geom); + QuadEdgeSubdivision subdiv = dt.getSubdivision(); + List triList = toTris(subdiv); + return triList; + } + + private static List toTris(QuadEdgeSubdivision subdiv) { + HullTriVisitor visitor = new HullTriVisitor(); + subdiv.visitTriangles(visitor, false); + List triList = visitor.getTriangles(); + TriangulationBuilder.build(triList); + return triList; + } + + private static class HullTriVisitor implements TriangleVisitor { + private List triList = new ArrayList(); + + public HullTriVisitor() { + } + + public void visit(QuadEdge[] triEdges) { + Coordinate p0 = triEdges[0].orig().getCoordinate(); + Coordinate p1 = triEdges[1].orig().getCoordinate(); + Coordinate p2 = triEdges[2].orig().getCoordinate(); + HullTri tri; + if (Triangle.isCCW(p0, p1, p2)) { + tri = new HullTri(p0, p2, p1); + } + else { + tri = new HullTri(p0, p1, p2); + } + triList.add(tri); + } + + public List getTriangles() { + return triList; + } + } + + /** + * Creates a polygonal geometry representing the area of a triangulation + * which may be disconnected or contain holes. + * + * @param triList the triangulation + * @param geomFactory the geometry factory to use + * @return the area polygonal geometry + */ + public static Geometry union(List triList, GeometryFactory geomFactory) { + List polys = new ArrayList(); + for (Tri tri : triList) { + Polygon poly = tri.toPolygon(geomFactory); + polys.add(poly); + } + return CoverageUnion.union(geomFactory.buildGeometry(polys)); + } + + /** + * Creates a Polygon representing the area of a triangulation + * which is connected and contains no holes. + * + * @param triList the triangulation + * @param geomFactory the geometry factory to use + * @return the area polygon + */ + public static Geometry traceBoundaryPolygon(List triList, GeometryFactory geomFactory) { + if (triList.size() == 1) { + Tri tri = triList.get(0); + return tri.toPolygon(geomFactory); + } + Coordinate[] pts = traceBoundary(triList); + return geomFactory.createPolygon(pts); + } + + /** + * Extracts the coordinates of the edges along the boundary of a triangulation, + * by tracing CW around the border triangles. + * Assumption: there are at least 2 tris, they are connected, + * and there are no holes. + * So each tri has at least one non-boundary edge, and there is only one boundary. + * + * @param triList the triangulation + * @return the points in the boundary of the triangulation + */ + private static Coordinate[] traceBoundary(List triList) { + HullTri triStart = findBorderTri(triList); + CoordinateList coordList = new CoordinateList(); + HullTri tri = triStart; + do { + int boundaryIndex = tri.boundaryIndexCCW(); + //-- add border vertex + coordList.add(tri.getCoordinate(boundaryIndex).copy(), false); + int nextIndex = Tri.next(boundaryIndex); + //-- if next edge is also on boundary, add it and move to next + if (tri.isBoundary(nextIndex)) { + coordList.add(tri.getCoordinate(nextIndex).copy(), false); + boundaryIndex = nextIndex; + } + //-- find next border tri CCW around non-boundary edge + tri = nextBorderTri(tri); + } while (tri != triStart); + coordList.closeRing(); + return coordList.toCoordinateArray(); + } + + private static HullTri findBorderTri(List triList) { + for (HullTri tri : triList) { + if (tri.isBorder()) return tri; + } + Assert.shouldNeverReachHere("No border triangles found"); + return null; + } + + public static HullTri nextBorderTri(HullTri triStart) { + HullTri tri = triStart; + //-- start at first non-border edge CW + int index = Tri.next(tri.boundaryIndexCW()); + //-- scan CCW around vertex for next border tri + do { + HullTri adjTri = (HullTri) tri.getAdjacent(index); + if (adjTri == tri) + throw new IllegalStateException("No outgoing border edge found"); + index = Tri.next(adjTri.getIndex(tri)); + tri = adjTri; + } + while (! tri.isBoundary(index)); + return (tri); + } +} diff --git a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java index e2af1e2d12..15407a45b4 100755 --- a/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java +++ b/modules/core/src/main/java/org/locationtech/jts/triangulate/tri/Tri.java @@ -28,8 +28,12 @@ * Contains three vertices, and links to adjacent Tris for each edge. * Tris are constructed independently, and if needed linked * into a triangulation using {@link TriangulationBuilder}. + *

        + * An edge of a Tri in a triangulation is called a boundary edge + * if it has no adjacent triangle. + * The set of Tris containing boundary edges are called the triangulation border. * - * @author mdavis + * @author Martin Davis * */ public class Tri { @@ -261,6 +265,38 @@ private void replace(Tri triOld, Tri triNew) { } } + /** + * Computes the degree of a Tri vertex, which is the number of tris containing it. + * This must be done by searching the entire triangulation, + * since the containing tris may not be adjacent or edge-connected. + * + * @param index the vertex index + * @param triList the triangulation + * @return the degree of the vertex + */ + public int degree(int index, List triList) { + Coordinate v = getCoordinate(index); + int degree = 0; + for (Tri tri : triList) { + for (int i = 0; i < 3; i++) { + if (v.equals2D(tri.getCoordinate(i))) + degree++; + } + } + return degree; + } + + /** + * Removes this tri from the triangulation containing it. + * All links between the tri and adjacent ones are nulled. + * + * @param triList the triangulation + */ + public void remove(List triList) { + remove(); + triList.remove(this); + } + /** * Removes this triangle from a triangulation. * All adjacent references and the references to this @@ -457,6 +493,16 @@ public Tri getAdjacent(int index) { return null; } + /** + * Tests if this tri has any adjacent tris. + * + * @return true if there is at least one adjacent tri + */ + public boolean hasAdjacent() { + return hasAdjacent(0) + || hasAdjacent(1) || hasAdjacent(2); + } + /** * Tests if there is an adjacent triangle to an edge. * @@ -495,6 +541,48 @@ public int numAdjacent() { return num; } + /** + * Tests if a tri vertex is interior. + * A vertex of a triangle is interior if it + * is fully surrounded by other triangles. + * + * @param index the vertex index + * @return true if the vertex is interior + */ + public boolean isInteriorVertex(int index) { + Tri curr = this; + int currIndex = index; + do { + Tri adj = curr.getAdjacent(currIndex); + if (adj == null) return false; + int adjIndex = adj.getIndex(curr); + curr = adj; + currIndex = Tri.next(adjIndex); + } + while (curr != this); + return true; + } + + /** + * Tests if a tri contains a boundary edge, + * and thus on the border of the triangulation containing it. + * + * @return true if the tri is on the border of the triangulation + */ + public boolean isBorder() { + return isBoundary(0) || isBoundary(1) || isBoundary(2); + } + + /** + * Tests if an edge is on the boundary of a triangulation. + * + * @param index index of an edge + * @return true if the edge is on the boundary + */ + public boolean isBoundary(int index) { + return ! hasAdjacent(index); + } + /** * Computes the vertex or edge index which is the next one * (clockwise) around the triangle. diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java index bd68e387a6..488ee3071b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java @@ -59,34 +59,34 @@ public void testLengthConvex() { 100, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 10 10))" ); } - public void testLengthCShape() { - checkHullByLength("MULTIPOINT ((70 80), (80 90), (90 70), (50 80), (30 70), (20 40), (30 20), (50 10), (90 20), (40 50), (40 30), (41 67))", - 50, "POLYGON ((30 70, 50 80, 80 90, 90 70, 70 80, 40 50, 40 30, 90 20, 50 10, 30 20, 20 40, 30 70))" ); - } + //------------------------------------------------ - public void testLengthSShape() { - checkHullByLength("MULTIPOINT ((0 81), (65 86), (70 71), (80 59), (92 49), (107 44), (122 41), (137 40), (152 41), (167 42), (182 47), (195 55), (203 68), (201 83), (188 92), (173 97), (158 100), (143 103), (128 106), (113 109), (98 112), (83 115), (68 120), (53 125), (40 133), (28 143), (18 155), (13 170), (12 185), (16 200), (26 213), (38 223), (51 231), (66 236), (81 240), (96 243), (111 245), (126 245), (141 245), (156 245), (171 244), (186 241), (201 238), (216 233), (229 225), (242 216), (252 204), (259 190), (262 175), (194 171), (189 186), (178 197), (164 203), (149 205), (134 206), (119 205), (104 203), (89 198), (77 188), (80 173), (93 165), (108 160), (123 157), (138 154), (153 151), (168 149), (183 146), (198 142), (213 138), (227 132), (241 126), (253 116), (263 104), (269 90), (271 75), (270 60), (264 46), (254 34), (243 23), (229 16), (215 10), (200 6), (185 3), (170 1), (155 0), (139 0), (123 0), (108 1), (93 3), (78 5), (63 10), (49 16), (35 23), (23 33), (13 45), (6 59), (16 82), (32 83), (48 84), (245 174), (228 173), (211 172), (131 128), (63 148), (222 207), (127 230), (154 131), (240 82), (72 220), (210 32), (90 22), (206 208), (57 202), (195 117), (55 166), (246 55), (201 101), (224 73), (211 192), (42 176), (152 228), (172 113), (24 61), (76 33), (92 216), (46 69), (118 138), (169 23), (213 118), (221 56), (44 192), (118 22), (224 40), (56 57), (192 32), (179 220), (34 44), (145 18), (239 194), (40 155), (92 136), (231 106), (40 207), (108 228), (256 81), (28 185), (54 33), (74 205), (172 132), (221 93), (249 96), (69 47), (78 146), (155 115), (202 223))", - 20, "POLYGON ((16 200, 26 213, 38 223, 51 231, 66 236, 81 240, 96 243, 111 245, 126 245, 141 245, 156 245, 171 244, 186 241, 201 238, 216 233, 229 225, 242 216, 252 204, 259 190, 262 175, 245 174, 228 173, 211 172, 194 171, 189 186, 178 197, 164 203, 149 205, 134 206, 119 205, 104 203, 89 198, 77 188, 80 173, 93 165, 108 160, 123 157, 138 154, 153 151, 168 149, 183 146, 198 142, 213 138, 227 132, 241 126, 253 116, 263 104, 269 90, 271 75, 270 60, 264 46, 254 34, 243 23, 229 16, 215 10, 200 6, 185 3, 170 1, 155 0, 139 0, 123 0, 108 1, 93 3, 78 5, 63 10, 49 16, 35 23, 23 33, 13 45, 6 59, 0 81, 16 82, 32 83, 48 84, 65 86, 70 71, 80 59, 92 49, 107 44, 122 41, 137 40, 152 41, 167 42, 182 47, 195 55, 203 68, 201 83, 188 92, 173 97, 158 100, 143 103, 128 106, 113 109, 98 112, 83 115, 68 120, 53 125, 40 133, 28 143, 18 155, 13 170, 12 185, 16 200))" ); + public void testLenRatioCShape() { + checkHullByLengthRatio("MULTIPOINT ((70 80), (80 90), (90 70), (50 80), (30 70), (20 40), (30 20), (50 10), (90 20), (40 50), (40 30), (41 67))", + 0.2, "POLYGON ((20 40, 30 70, 50 80, 80 90, 90 70, 70 80, 41 67, 40 50, 40 30, 90 20, 50 10, 30 20, 20 40))" ); } - //------------------------------------------------ + public void testLenRatioSShape() { + checkHullByLengthRatio("MULTIPOINT ((0 81), (65 86), (70 71), (80 59), (92 49), (107 44), (122 41), (137 40), (152 41), (167 42), (182 47), (195 55), (203 68), (201 83), (188 92), (173 97), (158 100), (143 103), (128 106), (113 109), (98 112), (83 115), (68 120), (53 125), (40 133), (28 143), (18 155), (13 170), (12 185), (16 200), (26 213), (38 223), (51 231), (66 236), (81 240), (96 243), (111 245), (126 245), (141 245), (156 245), (171 244), (186 241), (201 238), (216 233), (229 225), (242 216), (252 204), (259 190), (262 175), (194 171), (189 186), (178 197), (164 203), (149 205), (134 206), (119 205), (104 203), (89 198), (77 188), (80 173), (93 165), (108 160), (123 157), (138 154), (153 151), (168 149), (183 146), (198 142), (213 138), (227 132), (241 126), (253 116), (263 104), (269 90), (271 75), (270 60), (264 46), (254 34), (243 23), (229 16), (215 10), (200 6), (185 3), (170 1), (155 0), (139 0), (123 0), (108 1), (93 3), (78 5), (63 10), (49 16), (35 23), (23 33), (13 45), (6 59), (16 82), (32 83), (48 84), (245 174), (228 173), (211 172), (131 128), (63 148), (222 207), (127 230), (154 131), (240 82), (72 220), (210 32), (90 22), (206 208), (57 202), (195 117), (55 166), (246 55), (201 101), (224 73), (211 192), (42 176), (152 228), (172 113), (24 61), (76 33), (92 216), (46 69), (118 138), (169 23), (213 118), (221 56), (44 192), (118 22), (224 40), (56 57), (192 32), (179 220), (34 44), (145 18), (239 194), (40 155), (92 136), (231 106), (40 207), (108 228), (256 81), (28 185), (54 33), (74 205), (172 132), (221 93), (249 96), (69 47), (78 146), (155 115), (202 223))", + 0.1, "POLYGON ((16 200, 26 213, 38 223, 51 231, 66 236, 81 240, 96 243, 111 245, 126 245, 141 245, 156 245, 171 244, 186 241, 201 238, 216 233, 229 225, 242 216, 252 204, 259 190, 262 175, 245 174, 228 173, 211 172, 194 171, 189 186, 178 197, 164 203, 149 205, 134 206, 119 205, 104 203, 89 198, 77 188, 80 173, 93 165, 108 160, 123 157, 138 154, 153 151, 168 149, 183 146, 198 142, 213 138, 227 132, 241 126, 253 116, 263 104, 269 90, 271 75, 270 60, 264 46, 254 34, 243 23, 229 16, 215 10, 200 6, 185 3, 170 1, 155 0, 139 0, 123 0, 108 1, 93 3, 78 5, 63 10, 49 16, 35 23, 23 33, 13 45, 6 59, 0 81, 16 82, 32 83, 48 84, 65 86, 70 71, 80 59, 92 49, 107 44, 122 41, 137 40, 152 41, 167 42, 182 47, 195 55, 203 68, 201 83, 188 92, 173 97, 158 100, 143 103, 128 106, 113 109, 98 112, 83 115, 68 120, 53 125, 40 133, 28 143, 18 155, 13 170, 12 185, 16 200))" ); + } - public void testLengthFactorZero() { + public void testLenRatioZero() { checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 0, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 60 50, 47 66, 40 60, 30 70))" ); } - public void testLengthFactorP5() { + public void testLenRatioP5() { checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 0.5, "POLYGON ((30 70, 10 90, 60 72, 90 90, 90 60, 90 10, 60 30, 10 10, 40 40, 30 70))" ); } - public void testLengthFactorOne() { + public void testLenRatioOne() { checkHullByLengthRatio("MULTIPOINT ((10 90), (10 10), (90 10), (90 90), (40 40), (60 30), (30 70), (40 60), (60 50), (60 72), (47 66), (90 60))", 1, "POLYGON ((10 10, 10 90, 90 90, 90 60, 90 10, 10 10))" ); } - public void testLengthFactorXYZChevronP5() { + public void testLenRatioXYZChevronP5() { checkHullByLengthRatioXYZ("MULTIPOINT Z ((10 10 1), (90 10 2), (30 70 3), (70 70 4), (50 60 5))", 0.5, "POLYGON Z ((30 70 3, 70 70 4, 90 10 2, 50 60 5, 10 10 1, 30 70 3))" ); } @@ -100,7 +100,7 @@ public void testLengthHolesCircle() { public void testLengthHolesCircle0() { checkHullWithHolesByLength("MULTIPOINT ((90 20), (80 10), (45 5), (10 20), (20 10), (21 30), (40 20), (11 60), (20 70), (20 90), (40 80), (70 80), (80 60), (90 70), (80 90), (56 95), (95 45), (80 40), (70 20), (15 45), (5 40), (40 96), (60 15))", - 0, "POLYGON ((20 90, 40 96, 56 95, 80 90, 90 70, 95 45, 90 20, 80 10, 60 15, 45 5, 20 10, 10 20, 5 40, 11 60, 15 45, 21 30, 40 20, 70 20, 80 40, 80 60, 70 80, 40 80, 20 70, 20 90))" ); + 0, "POLYGON ((20 90, 40 96, 56 95, 70 80, 80 90, 90 70, 80 60, 95 45, 80 40, 70 20, 90 20, 80 10, 60 15, 45 5, 40 20, 40 80, 15 45, 21 30, 20 10, 10 20, 5 40, 11 60, 20 70, 20 90))" ); } //------------------------------------------------ From e8b11f104f5dbe1698ef6cb1c4776c5dd8ef410b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 Jan 2022 14:21:26 -0800 Subject: [PATCH 174/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 73077257a1..98a98a3561 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -34,6 +34,7 @@ Distributions for older JTS versions can be obtained at the * Make `QuadTree` thread-safe (#792) * Allow specifying a fixed `PrecisionModel` via grid size (#804) * Add `OffsetCurve` class (#810, #816) +* Add `ConcaveHull` class for points (#823, #829) ### Bug Fixes From f05e202722f6d9a0026684390e236118e9ed6f40 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 Jan 2022 14:25:24 -0800 Subject: [PATCH 175/275] Fix spelling of criterion in ConcaveHull Signed-off-by: Martin Davis --- .../jts/algorithm/hull/ConcaveHull.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java index c9ef0d9b8a..7fc47448a3 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -25,7 +25,7 @@ * Constructs a concave hull of a set of points. * The hull is constructed by removing the longest outer edges * of the Delaunay Triangulation of the points - * until a target criterium is reached. + * until a target criterion is reached. *

        * The target criteria are: *

          @@ -40,9 +40,9 @@ * will be no larger than this value. * A value of 1 produces the convex hull; a value of 0 produces maximum concaveness. *
        - * The preferred criterium is the Maximum Edge Length Ratio, since it is + * The preferred criterion is the Maximum Edge Length Ratio, since it is * scale-free and local (so that no assumption needs to be made about the - * total amount of concavity present). + * total amount of concaveness present). * Other length criteria can be used by setting the Maximum Edge Length directly. * For example, use a length relative to the longest edge length * in the Minimum Spanning Tree of the point set. @@ -50,10 +50,10 @@ *

        * The computed hull is always a single connected {@link Polygon} * (unless it is degenerate, in which case it will be a {@link Point} or a {@link LineString}). - * This constraint may cause the concave hull to fail to meet the target criteria. + * This constraint may cause the concave hull to fail to meet the target criterion. *

        * Optionally the concave hull can be allowed to contain holes. - * Note that when using the area-based criterium + * Note that when using the area-based criterion * this may result in substantially slower computation. * * @author Martin Davis @@ -81,7 +81,7 @@ public static double uniformGridEdgeLength(Geometry geom) { /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum edge length. + * using the target criterion of maximum edge length. * * @param geom the input geometry * @param maxLength the target maximum edge length @@ -93,7 +93,7 @@ public static Geometry concaveHullByLength(Geometry geom, double maxLength) { /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum edge length, + * using the target criterion of maximum edge length, * and optionally allowing holes. * * @param geom the input geometry @@ -110,7 +110,7 @@ public static Geometry concaveHullByLength(Geometry geom, double maxLength, bool /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum edge length ratio. + * using the target criterion of maximum edge length ratio. * The edge length ratio is a fraction of the length difference * between the longest and shortest edges * in the Delaunay Triangulation of the input points. @@ -125,7 +125,7 @@ public static Geometry concaveHullByLengthRatio(Geometry geom, double lengthRati /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum edge length factor, + * using the target criterion of maximum edge length factor, * and optionally allowing holes. * The edge length factor is a fraction of the length difference * between the longest and shortest edges @@ -145,7 +145,7 @@ public static Geometry concaveHullByLengthRatio(Geometry geom, double lengthRati /** * Computes the concave hull of the vertices in a geometry - * using the target criteria of maximum area ratio. + * using the target criterion of maximum area ratio. * * @param geom the input geometry * @param areaRatio the target maximum area ratio @@ -296,9 +296,9 @@ private static double computeTargetEdgeLength(List triList, //------------------------------------------------ /** - * Forms the concave hull using area ratio as the target criteria. + * Forms the concave hull using area ratio as the target criterion. *

        - * When area is used as the criteria, the boundary and holes + * When area is used as the criterion, the boundary and holes * must be eroded together, since the area is affected by both. * This means that result connectivity has to be checked after * every triangle removal, which is very slow. @@ -306,7 +306,7 @@ private static double computeTargetEdgeLength(List triList, * @param triList */ private void computeHullByArea(List triList) { - //-- used if area is the threshold criteria + //-- used if area is the threshold criterion double areaConvex = HullTri.area(triList); double areaConcave = areaConvex; @@ -370,7 +370,7 @@ private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { //------------------------------------------------ /** - * Computes the concave hull using edge length as the target criteria. + * Computes the concave hull using edge length as the target criterion. * The erosion is done in two phases: first the border, then any * internal holes (if required). * This allows an fast connection check to be used From ecac07aa87056c174161aa7e389dbc6d8e6c8600 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 Jan 2022 16:25:56 -0800 Subject: [PATCH 176/275] Rename CubicBezierCurve Signed-off-by: Martin Davis --- .../locationtech/jtstest/function/CreateShapeFunctions.java | 4 ++-- .../jts/shape/{BezierCurve.java => CubicBezierCurve.java} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename modules/core/src/main/java/org/locationtech/jts/shape/{BezierCurve.java => CubicBezierCurve.java} (98%) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index 4bb40c0c85..dcae9227fc 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -21,7 +21,7 @@ import org.locationtech.jts.awt.*; import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.util.*; -import org.locationtech.jts.shape.BezierCurve; +import org.locationtech.jts.shape.CubicBezierCurve; import org.locationtech.jts.util.GeometricShapeFactory; import org.locationtech.jtstest.geomfunction.Metadata; @@ -339,6 +339,6 @@ private static Coordinate[] genSpiralCycle(Coordinate centre, public static Geometry bezierCurve(Geometry geom, @Metadata(title="Alpha (curveness)") double alpha) { - return BezierCurve.bezierCurve(geom, alpha); + return CubicBezierCurve.bezierCurve(geom, alpha); } } diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java similarity index 98% rename from modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java rename to modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java index 6d8f6f68c3..c7ea95dbeb 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/BezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java @@ -25,7 +25,7 @@ * defined by the segments of the input. * */ -public class BezierCurve { +public class CubicBezierCurve { /** * Creates a curved line or polygon using Bezier Curves @@ -36,7 +36,7 @@ public class BezierCurve { * @return */ public static Geometry bezierCurve(Geometry geom, double alpha) { - BezierCurve curve = new BezierCurve(geom, alpha); + CubicBezierCurve curve = new CubicBezierCurve(geom, alpha); return curve.getResult(); } @@ -56,7 +56,7 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { * @param geom geometry defining curve * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) */ - BezierCurve(Geometry geom, double alpha) { + CubicBezierCurve(Geometry geom, double alpha) { this.inputGeom = geom; if ( alpha < 0.0 ) alpha = 0; this.alpha = alpha; From 4e4fa23039f0857f27e2be8dbee184223a8f6ef6 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 12 Jan 2022 16:26:36 -0800 Subject: [PATCH 177/275] Rename TestBuilder cubicBezierCurve function Signed-off-by: Martin Davis --- .../locationtech/jtstest/function/CreateShapeFunctions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index dcae9227fc..9259ccddd6 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -335,8 +335,8 @@ private static Coordinate[] genSpiralCycle(Coordinate centre, return pts; } - @Metadata(description="Construct a Bezier curve of a line or polygon") - public static Geometry bezierCurve(Geometry geom, + @Metadata(description="Construct a cubic Bezier curve of a line or polygon") + public static Geometry cubicBezierCurve(Geometry geom, @Metadata(title="Alpha (curveness)") double alpha) { return CubicBezierCurve.bezierCurve(geom, alpha); From 51c49c68c8a59d98bf20e86530db52cc424b4dde Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 13 Jan 2022 13:05:00 -0800 Subject: [PATCH 178/275] Improve CubicBezierCurve constuction Signed-off-by: Martin Davis --- .../org/locationtech/jts/algorithm/Angle.java | 32 ++++ .../jts/shape/CubicBezierCurve.java | 180 ++++++++++++------ .../locationtech/jts/algorithm/AngleTest.java | 44 +++-- 3 files changed, 187 insertions(+), 69 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java index 20b88be439..1da839b395 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/Angle.java @@ -180,6 +180,24 @@ public static double angleBetweenOriented(Coordinate tip1, Coordinate tail, return angDel - PI_TIMES_2; return angDel; } + + /** + * Computes the angle of the unoriented bisector + * of the smallest angle between two vectors. + * The computed angle will be in the range (-Pi, Pi]. + * + * @param tip1 the tip of v1 + * @param tail the tail of each vector + * @param tip2 the tip of v2 + * @return the angle of the bisector between v1 and v2 + */ + public static double bisector(Coordinate tip1, Coordinate tail, + Coordinate tip2) + { + double angDel = angleBetweenOriented(tip1, tail, tip2); + double angBi = angle(tail, tip1) + angDel / 2; + return normalize(angBi); + } /** * Computes the interior angle between two segments of a ring. The ring is @@ -300,4 +318,18 @@ public static double diff(double ang1, double ang2) { return delAngle; } + + /** + * Projects a point by a given angle and distance. + * + * @param p the point to project + * @param angle the angle at which to project + * @param dist the distance to project + * @return the projected point + */ + public static Coordinate project(Coordinate p, double angle, double dist) { + double x = p.getX() + dist * Math.cos(angle); + double y = p.getY() + dist * Math.sin(angle); + return new Coordinate(x, y); + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java index c7ea95dbeb..37153b1700 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java @@ -11,6 +11,7 @@ */ package org.locationtech.jts.shape; +import org.locationtech.jts.algorithm.Angle; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateList; import org.locationtech.jts.geom.Geometry; @@ -18,21 +19,27 @@ import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.io.WKTWriter; +import org.locationtech.jts.geom.util.GeometryMapper; /** - * Creates a curved line or polygon using Bezier Curves - * defined by the segments of the input. - * + * Creates a curved geometry by replacing the segments + * of the input with Cubic Bezier Curves. + * The Bezier control points are determined from the segments of the geometry + * and the alpha control parameter. + * The Bezier Curves are created to be C2-continuous (smooth) + * at each input vertex. + *

        + * The result is not guaranteed to be valid, since large alpha values + * may cause self-intersections. */ public class CubicBezierCurve { /** - * Creates a curved line or polygon using Bezier Curves + * Creates a curved geometry using Cubic Bezier Curves * defined by the segments of the input. * * @param geom the geometry defining the curve - * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) + * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly distorted) * @return */ public static Geometry bezierCurve(Geometry geom, double alpha) { @@ -41,7 +48,7 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { } private double minSegmentLength = 0.0; - private int numVerticesPerSegment = 10; + private int numVerticesPerSegment = 16; private Geometry inputGeom; private double alpha; @@ -51,27 +58,41 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { private CubicBezierInterpolationParam[] interpolationParam; /** - * Creates a new Bezier Curve instance. + * Creates a new instance. * * @param geom geometry defining curve * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) */ CubicBezierCurve(Geometry geom, double alpha) { this.inputGeom = geom; - if ( alpha < 0.0 ) alpha = 0; + //if ( alpha < 0.0 ) alpha = 0; this.alpha = alpha; this.geomFactory = geom.getFactory(); } + /** + * Gets the computed Bezier curve geometry. + * + * @return the curved geometry + */ public Geometry getResult() { bezierCurvePts = new Coordinate[numVerticesPerSegment]; interpolationParam = CubicBezierInterpolationParam.compute(numVerticesPerSegment); - if (inputGeom instanceof LineString) - return bezierLine((LineString) inputGeom); - if (inputGeom instanceof Polygon) - return bezierPolygon((Polygon) inputGeom); - return null; + return GeometryMapper.flatMap(inputGeom, 1, new GeometryMapper.MapOp() { + + @Override + public Geometry map(Geometry geom) { + if (geom instanceof LineString) { + return bezierLine((LineString) geom); + } + if (geom instanceof Polygon ) { + return bezierPolygon((Polygon) geom); + } + //-- Points + return geom.copy(); + } + }); } private LineString bezierLine(LineString ls) { @@ -102,7 +123,14 @@ private LinearRing bezierRing(LinearRing ring) { private Polygon bezierPolygon(Polygon poly) { LinearRing shell = bezierRing(poly.getExteriorRing()); - return geomFactory.createPolygon(shell, null); + LinearRing[] holes = null; + if (poly.getNumInteriorRing() > 0) { + holes = new LinearRing[poly.getNumInteriorRing()]; + for (int i = 0; i < poly.getNumInteriorRing(); i++) { + holes[i] = bezierRing(poly.getInteriorRingN(i)); + } + } + return geomFactory.createPolygon(shell, holes); } private void addCurve(Coordinate p0, Coordinate p1, @@ -122,63 +150,107 @@ private void addCurve(Coordinate p0, Coordinate p1, } } + //-- makes curve at right-angle corners roughly circular + private static final double CIRCLE_LEN_FACTOR = 3.0 / 8.0; + + /** + * Creates control points for each vertex of curve. + * The control points are collinear with each vertex, + * thus providing C1-continuity. + * By default the control vectors are the same length, + * which provides C2-continuity (same curvature on each + * side of vertex. + * The alpha parameter controls the length of the control vectors. + * Alpha = 0 makes the vectors zero-length, and hence flattens the curves. + * Alpha = 1 makes the curve at right angles roughly circular. + * Alpha > 1 starts to distort the curve and may introduce self-intersections + * + * @param coords + * @param isRing + * @param alpha determines the curviness + * @return + */ private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double alpha) { final int N = isRing ? coords.length - 1 : coords.length; - double a1 = 1 - alpha; Coordinate[][] ctrl = new Coordinate[N][2]; - Coordinate v1 = coords[0]; - Coordinate v2 = coords[1]; + Coordinate v0 = coords[0]; + Coordinate v1 = coords[1]; + Coordinate v2 = coords[2]; if (isRing) { - v1 = coords[N - 1]; - v2 = coords[0]; + v0 = coords[N - 1]; + v1 = coords[0]; + v1 = coords[1]; } - double mid1x = (v1.x + v2.x) / 2.0; - double mid1y = (v1.y + v2.y) / 2.0; - double len1 = v1.distance(v2); - final int start = isRing ? 0 : 1; - final int end = isRing ? N : N-1; + final int end = isRing ? N : N - 1; for (int i = start; i < end; i++) { + int iprev = i == 0 ? N - 1 : i - 1; + v0 = coords[iprev]; v1 = coords[i]; v2 = coords[i + 1]; - double mid0x = mid1x; - double mid0y = mid1y; - mid1x = (v1.x + v2.x) / 2.0; - mid1y = (v1.y + v2.y) / 2.0; - - double len0 = len1; - len1 = v1.distance(v2); - - double p = len0 / (len0 + len1); - double anchorx = mid0x + p * (mid1x - mid0x); - double anchory = mid0y + p * (mid1y - mid0y); - double xdelta = anchorx - v1.x; - double ydelta = anchory - v1.y; - - ctrl[i][0] = new Coordinate( - a1 * (v1.x - mid0x + xdelta) + mid0x - xdelta, - a1 * (v1.y - mid0y + ydelta) + mid0y - ydelta); - - ctrl[i][1] = new Coordinate( - a1 * (v1.x - mid1x + xdelta) + mid1x - xdelta, - a1 * (v1.y - mid1y + ydelta) + mid1y - ydelta); - //System.out.println(WKTWriter.toLineString(v1, ctrl[i][0])); - //System.out.println(WKTWriter.toLineString(v1, ctrl[i][1])); + double interiorAng = Angle.angleBetweenOriented(v0, v1, v2); + double orient = Math.signum(interiorAng); + double angBisect = Angle.bisector(v0, v1, v2); + double ang0 = angBisect - orient * Angle.PI_OVER_2; + double ang1 = angBisect + orient * Angle.PI_OVER_2; + + double len0 = v1.distance(v0); + double len1 = v1.distance(v2); + double lenBase = Math.min(len0, len1); + double intAngAbs = Math.abs(interiorAng); + + //-- make acute corners sharper by shortening tangent + double sharpnessFactor = intAngAbs >= Angle.PI_OVER_2 ? 1 : intAngAbs / Angle.PI_OVER_2; + + double len = alpha * CIRCLE_LEN_FACTOR * sharpnessFactor * lenBase; + + Coordinate cv0 = Angle.project(v1, ang0, len); + Coordinate cv1 = Angle.project(v1, ang1, len); + ctrl[i][0] = cv0; + ctrl[i][1] = cv1; + + //System.out.println(WKTWriter.toLineString(v1, cv0)); + //System.out.println(WKTWriter.toLineString(v1, cv1)); } - /** - * For a line, produce a symmetric curve for the first and last segments - * by using mirrored control points for start and end vertex, - */ if (! isRing) { - ctrl[0][1] = mirrorControlPoint(ctrl[1][0], coords[1], coords[0]); - ctrl[N - 1][0] = mirrorControlPoint(ctrl[N - 2][1], coords[N - 1], coords[N - 2]); + setLineEndControlPoints(coords, ctrl); } return ctrl; } + /** + * Sets the end control points for a line. + * Produce a symmetric curve for the first and last segments + * by using mirrored control points for start and end vertex. + * + * @param coords + * @param ctrl + */ + private void setLineEndControlPoints(Coordinate[] coords, Coordinate[][] ctrl) { + int N = coords.length; + ctrl[0][1] = mirrorControlPoint(ctrl[1][0], coords[1], coords[0]); + ctrl[N - 1][0] = mirrorControlPoint(ctrl[N - 2][1], coords[N - 1], coords[N - 2]); + } + + /** + * Creates a control point aimed at the control point at the opposite end of the segment. + * + * Produces overly flat results, so not used currently. + * + * @param c + * @param p1 + * @param p0 + * @return + */ + private static Coordinate aimedControlPoint(Coordinate c, Coordinate p1, Coordinate p0) { + double len = p1.distance(c); + double ang = Angle.angle(p0, p1); + return Angle.project(p0, ang, len); + } + private static Coordinate mirrorControlPoint(Coordinate c, Coordinate p0, Coordinate p1) { double vlinex = p1.x - p0.x; double vliney = p1.y - p0.y; diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java index 80b38bf7f1..417bb1f538 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/AngleTest.java @@ -40,24 +40,24 @@ public static void main(String args[]) { public void testAngle() { - assertEquals(Angle.angle(new Coordinate(10,0)), 0.0, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(10,10)), Math.PI/4, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(0,10)), Math.PI/2, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(-10,10)), 0.75*Math.PI, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(-10,0)), Math.PI, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(-10,-0.1)), -3.131592986903128, TOLERANCE); - assertEquals(Angle.angle(new Coordinate(-10,-10)), -0.75*Math.PI, TOLERANCE); + assertEquals(Angle.angle(p(10,0)), 0.0, TOLERANCE); + assertEquals(Angle.angle(p(10,10)), Math.PI/4, TOLERANCE); + assertEquals(Angle.angle(p(0,10)), Math.PI/2, TOLERANCE); + assertEquals(Angle.angle(p(-10,10)), 0.75*Math.PI, TOLERANCE); + assertEquals(Angle.angle(p(-10,0)), Math.PI, TOLERANCE); + assertEquals(Angle.angle(p(-10,-0.1)), -3.131592986903128, TOLERANCE); + assertEquals(Angle.angle(p(-10,-10)), -0.75*Math.PI, TOLERANCE); } public void testIsAcute() { - assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(5,10)), true); - assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(5,-10)), true); + assertEquals(Angle.isAcute(p(10,0), p(0,0), p(5,10)), true); + assertEquals(Angle.isAcute(p(10,0), p(0,0), p(5,-10)), true); // angle of 0 - assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(10,0)), true); + assertEquals(Angle.isAcute(p(10,0), p(0,0), p(10,0)), true); - assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(-5,10)), false); - assertEquals(Angle.isAcute(new Coordinate(10,0), new Coordinate(0,0), new Coordinate(-5,-10)), false); + assertEquals(Angle.isAcute(p(10,0), p(0,0), p(-5,10)), false); + assertEquals(Angle.isAcute(p(10,0), p(0,0), p(-5,-10)), false); } public void testNormalizePositive() @@ -103,9 +103,9 @@ public void testNormalize() } public void testInteriorAngle() { - Coordinate p1 = new CoordinateXY(1, 2); - Coordinate p2 = new CoordinateXY(3, 2); - Coordinate p3 = new CoordinateXY(2, 1); + Coordinate p1 = p(1, 2); + Coordinate p2 = p(3, 2); + Coordinate p3 = p(2, 1); // Tests all interior angles of a triangle "POLYGON ((1 2, 3 2, 2 1, 1 2))" assertEquals(45, Math.toDegrees(Angle.interiorAngle(p1, p2, p3)), 0.01); @@ -148,4 +148,18 @@ public void testInteriorAngle_randomTriangles() { ); } } + + public void testAngleBisector() { + assertEquals(45, Math.toDegrees( Angle.bisector(p(0,1), p(0,0), p(1,0))), 0.01); + assertEquals(22.5, Math.toDegrees( Angle.bisector(p(1,1), p(0,0), p(1,0))), 0.01); + assertEquals(67.5, Math.toDegrees( Angle.bisector(p(-1,1), p(0,0), p(1,0))), 0.01); + assertEquals(-45, Math.toDegrees( Angle.bisector(p(0,-1), p(0,0), p(1,0))), 0.01); + assertEquals(180, Math.toDegrees( Angle.bisector(p(-1,-1), p(0,0), p(-1,1))), 0.01); + + assertEquals(45, Math.toDegrees(Angle.bisector(p(13,10), p(10,10), p(10,20))), 0.01); + } + + private static Coordinate p(double x, double y) { + return new Coordinate(x, y); + } } From 2f29ea918ebd33c0a91449782bfe32105ecac67d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 14 Jan 2022 09:48:15 -0800 Subject: [PATCH 179/275] Remove ConcaveHull area-based criterion Signed-off-by: Martin Davis --- .../function/ConstructionFunctions.java | 6 - .../jts/algorithm/hull/ConcaveHull.java | 122 +----------------- .../jts/algorithm/hull/ConcaveHullTest.java | 25 +--- 3 files changed, 4 insertions(+), 149 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java index 97293cf53b..a8cc5f2b78 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/ConstructionFunctions.java @@ -155,12 +155,6 @@ public static Geometry concaveHullWithHolesByLenRatio(Geometry geom, return ConcaveHull.concaveHullByLengthRatio(geom, maxLen, true); } - public static Geometry concaveHullByArea(Geometry geom, - @Metadata(title="Area ratio") - double minAreaPct) { - return ConcaveHull.concaveHullByArea(geom, minAreaPct); - } - public static double concaveHullLenGuess(Geometry geom) { return ConcaveHull.uniformGridEdgeLength(geom); } diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java index 7fc47448a3..dadf067356 100644 --- a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/ConcaveHull.java @@ -36,9 +36,6 @@ * in the Delaunay Triangulation. * This normalizes the Maximum Edge Length to be scale-free. * A value of 1 produces the convex hull; a value of 0 produces maximum concaveness. - *

      • Maximum Area Ratio - the ratio of the concave hull area to the convex hull area - * will be no larger than this value. - * A value of 1 produces the convex hull; a value of 0 produces maximum concaveness. *
      * The preferred criterion is the Maximum Edge Length Ratio, since it is * scale-free and local (so that no assumption needs to be made about the @@ -143,24 +140,9 @@ public static Geometry concaveHullByLengthRatio(Geometry geom, double lengthRati return hull.getHull(); } - /** - * Computes the concave hull of the vertices in a geometry - * using the target criterion of maximum area ratio. - * - * @param geom the input geometry - * @param areaRatio the target maximum area ratio - * @return the concave hull - */ - public static Geometry concaveHullByArea(Geometry geom, double areaRatio) { - ConcaveHull hull = new ConcaveHull(geom); - hull.setMaximumAreaRatio(areaRatio); - return hull.getHull(); - } - private Geometry inputGeometry; private double maxEdgeLength = 0.0; private double maxEdgeLengthRatio = -1; - private double maxAreaRatio = -1; private boolean isHolesAllowed = false; private GeometryFactory geomFactory; @@ -219,24 +201,6 @@ public void setMaximumEdgeLengthRatio(double edgeLengthRatio) { this.maxEdgeLengthRatio = edgeLengthRatio; } - /** - * Sets the target maximum concave hull area as a ratio of the convex hull area. - * It is a value in the range 0 to 1. - *
        - *
      • The value 0.0 produces a concave hull with the smallest area - * that is still connected. - *
      • The value 1.0 produces the convex hull - * (unless a maximum edge length is also specified). - *
      - * - * @param areaRatio a ratio value between 0 and 1 - */ - public void setMaximumAreaRatio(double areaRatio) { - if (areaRatio < 0 || areaRatio > 1) - throw new IllegalArgumentException("Area ratio must be in range [0,1]"); - this.maxAreaRatio = areaRatio; - } - /** * Sets whether holes are allowed in the concave hull polygon. * @@ -262,12 +226,8 @@ public Geometry getHull() { if (triList.isEmpty()) return inputGeometry.convexHull(); - if (maxAreaRatio >= 0) { - computeHullByArea(triList); - } - else { - computeHullByLength(triList); - } + computeHull(triList); + Geometry hull = toGeometry(triList, geomFactory); return hull; } @@ -292,82 +252,6 @@ private static double computeTargetEdgeLength(List triList, return edgeLengthRatio * (maxEdgeLen - minEdgeLen) + minEdgeLen; } - - //------------------------------------------------ - - /** - * Forms the concave hull using area ratio as the target criterion. - *

      - * When area is used as the criterion, the boundary and holes - * must be eroded together, since the area is affected by both. - * This means that result connectivity has to be checked after - * every triangle removal, which is very slow. - * - * @param triList - */ - private void computeHullByArea(List triList) { - //-- used if area is the threshold criterion - double areaConvex = HullTri.area(triList); - double areaConcave = areaConvex; - - PriorityQueue queue = createBorderQueue(triList); - // remove tris in order of decreasing size (edge length) - while (! queue.isEmpty()) { - if (isBelowAreaThreshold(areaConcave, areaConvex)) - break; - - HullTri tri = queue.poll(); - - if (isRemovableByArea(tri, triList)) { - //-- the non-null adjacents are now on the border - HullTri adj0 = (HullTri) tri.getAdjacent(0); - HullTri adj1 = (HullTri) tri.getAdjacent(1); - HullTri adj2 = (HullTri) tri.getAdjacent(2); - - tri.remove(triList); - areaConcave -= tri.getArea(); - - //-- if holes not allowed, add new border adjacents to queue - if (! isHolesAllowed) { - addBorderTri(adj0, queue); - addBorderTri(adj1, queue); - addBorderTri(adj2, queue); - } - } - } - } - - private boolean isRemovableByArea(HullTri tri, List triList) { - if (isHolesAllowed) { - return isRemovableByAreaWithHoles(tri, triList); - } - return isRemovableBorder(tri); - } - - private boolean isRemovableByAreaWithHoles(HullTri tri, List triList) { - /** - * Can't remove if that would separate a vertex from the hull - */ - if (tri.isolatedVertexIndex(triList) != -1) - return false; - /** - * This test is slow for large input. - * It could be omitted if a disconnected result was allowed. - */ - if (! HullTri.isConnected(triList, tri)) - return false; - /** - * If tri touches boundary at a single vertex, it can't be removed - * because that might disconnect the triangulation interior. - */ - return ! tri.hasBoundaryTouch(); - } - - private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { - return areaConcave / areaConvex <= maxAreaRatio; - } - - //------------------------------------------------ /** * Computes the concave hull using edge length as the target criterion. @@ -379,7 +263,7 @@ private boolean isBelowAreaThreshold(double areaConcave, double areaConvex) { * * @param triList */ - private void computeHullByLength(List triList) { + private void computeHull(List triList) { computeHullBorder(triList); if (isHolesAllowed) { computeHullHoles(triList); diff --git a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java index 488ee3071b..da57c2af46 100644 --- a/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/algorithm/hull/ConcaveHullTest.java @@ -103,23 +103,6 @@ public void testLengthHolesCircle0() { 0, "POLYGON ((20 90, 40 96, 56 95, 70 80, 80 90, 90 70, 80 60, 95 45, 80 40, 70 20, 90 20, 80 10, 60 15, 45 5, 40 20, 40 80, 15 45, 21 30, 20 10, 10 20, 5 40, 11 60, 20 70, 20 90))" ); } - //------------------------------------------------ - - public void testAreaSimple() { - checkHullByArea("MULTIPOINT ((10 10), (90 10), (30 70), (70 70), (50 60))", - .5, "POLYGON ((30 70, 70 70, 90 10, 50 60, 10 10, 30 70))" ); - } - - public void testAreaZero() { - checkHullByArea("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", - 0, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 50 60, 30 30, 10 10))" ); - } - - public void testAreaConvex() { - checkHullByArea("MULTIPOINT ((10 10), (90 10), (70 70), (50 60), (50 90), (40 70), (30 30))", - 1, "POLYGON ((10 10, 40 70, 50 90, 70 70, 90 10, 10 10))" ); - } - //========================================================================== private void checkHullByLengthRatio(String wkt, double threshold, String wktExpected) { @@ -149,11 +132,5 @@ private void checkHullWithHolesByLength(String wkt, double threshold, String wkt Geometry expected = read(wktExpected); checkEqual(expected, actual); } - - private void checkHullByArea(String wkt, double threshold, String wktExpected) { - Geometry geom = read(wkt); - Geometry actual = ConcaveHull.concaveHullByArea(geom, threshold); - Geometry expected = read(wktExpected); - checkEqual(expected, actual); - } + } From 1518e5637a89ee2bec45fa4e4f3013150c3f13f7 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Fri, 14 Jan 2022 09:51:12 -0800 Subject: [PATCH 180/275] Add hull package-info Signed-off-by: Martin Davis --- .../jts/algorithm/hull/package-info.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 modules/core/src/main/java/org/locationtech/jts/algorithm/hull/package-info.java diff --git a/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/package-info.java b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/package-info.java new file mode 100644 index 0000000000..1edd16e40b --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/algorithm/hull/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2016 Vivid Solutions. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ + +/** + * Contains classes and interfaces implementing algorithms to compute hulls + * of geometry objects. + *

      + * See also {@link ConvexHull}. + */ +package org.locationtech.jts.algorithm.hull; \ No newline at end of file From f8ba0ed71f729e4de833ea61d19e9d574236d142 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 15 Jan 2022 10:44:18 -0800 Subject: [PATCH 181/275] Add TestBuilder WKTPanel geometry tooltips Signed-off-by: Martin Davis --- .../locationtech/jtstest/testbuilder/WKTPanel.java | 13 ++++++++++--- .../testbuilder/model/GeometryEditModel.java | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java index 2bcdd5eb1f..5f57574788 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java @@ -361,17 +361,24 @@ public void actionPerformed(ActionEvent e) { public void setText(Geometry g, int geomIndex) { + String shortForm = GeometryEditModel.toStringVeryLarge(g); String txt = null; if (g == null) txt = ""; else if (g.getNumPoints() > DisplayParameters.MAX_DISPLAY_POINTS) - txt = GeometryEditModel.toStringVeryLarge(g); + txt = shortForm; else txt = GeometryEditModel.getText(g, GeometryType.WELLKNOWNTEXT); switch (geomIndex) { - case 0: aTextArea.setText(txt); break; - case 1: bTextArea.setText(txt); break; + case 0: + aTextArea.setText(txt); + aLabel.setToolTipText(shortForm); + break; + case 1: + bTextArea.setText(txt); + bLabel.setToolTipText(shortForm); + break; } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java index 52df5704ff..271d6126a0 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java @@ -141,6 +141,7 @@ public static String getText(Geometry geom, int textType) { public static String toStringVeryLarge(Geometry g) { + if (g == null) return ""; return "[[ " + GeometryUtil.structureSummary(g) + " ]]"; } From 2849ad5f9793deee4f3f9ff5db6829ca4bdc9056 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 15 Jan 2022 11:08:08 -0800 Subject: [PATCH 182/275] Improve TestBuilder WKTPanel Geometry tooltip and tab order Signed-off-by: Martin Davis --- .../jtstest/testbuilder/JTSTestBuilderFrame.java | 4 ++-- .../java/org/locationtech/jtstest/testbuilder/WKTPanel.java | 5 +++-- .../locationtech/jtstest/testbuilder/geom/GeometryUtil.java | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java index 3c4f31bb08..e69a2b0e70 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/JTSTestBuilderFrame.java @@ -434,14 +434,14 @@ private void jbInit() throws Exception { //---- Input tabs inputTabbedPane.setTabPlacement(JTabbedPane.LEFT); inputTabbedPane.add(testListPanel, AppStrings.TAB_LABEL_CASES); + inputTabbedPane.add(layerListPanel, AppStrings.TAB_LABEL_LAYERS); inputTabbedPane.add(wktPanel, AppStrings.TAB_LABEL_INPUT); inputTabbedPane.add(resultWKTPanel, AppStrings.TAB_LABEL_RESULT); inputTabbedPane.add(resultValuePanel, AppStrings.TAB_LABEL_VALUE); - inputTabbedPane.add(commandPanel, AppStrings.TAB_LABEL_COMMAND); inputTabbedPane.add(inspectPanel, AppStrings.TAB_LABEL_INSPECT); inputTabbedPane.add(statsPanel, AppStrings.TAB_LABEL_STATS); inputTabbedPane.add(logPanel, AppStrings.TAB_LABEL_LOG); - inputTabbedPane.add(layerListPanel, AppStrings.TAB_LABEL_LAYERS); + inputTabbedPane.add(commandPanel, AppStrings.TAB_LABEL_COMMAND); inputTabbedPane.setSelectedIndex(1); inputTabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java index 5f57574788..660359284c 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/WKTPanel.java @@ -38,6 +38,7 @@ import javax.swing.border.TitledBorder; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jtstest.testbuilder.geom.GeometryUtil; import org.locationtech.jtstest.testbuilder.io.IOUtil; import org.locationtech.jtstest.testbuilder.model.DisplayParameters; import org.locationtech.jtstest.testbuilder.model.GeometryEditModel; @@ -373,11 +374,11 @@ else if (g.getNumPoints() > DisplayParameters.MAX_DISPLAY_POINTS) switch (geomIndex) { case 0: aTextArea.setText(txt); - aLabel.setToolTipText(shortForm); + aLabel.setToolTipText(GeometryUtil.structureSummary(g)); break; case 1: bTextArea.setText(txt); - bLabel.setToolTipText(shortForm); + bLabel.setToolTipText(GeometryUtil.structureSummary(g)); break; } } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java index 874b6ad596..90dc2f6378 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java @@ -20,6 +20,7 @@ public class GeometryUtil { public static String structureSummary(Geometry g) { String structure = ""; + if (g == null) return ""; if (g instanceof Polygon) { int nHoles = ((Polygon) g).getNumInteriorRing(); if (nHoles > 0) structure = nHoles + " holes, " ; From f2102941abb26ab2b9001d61a0cc49e8fd90a062 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 15 Jan 2022 19:27:28 -0800 Subject: [PATCH 183/275] Improve TestBuilder function name Signed-off-by: Martin Davis --- .../testbuilder/SpatialFunctionPanel.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java index 8179b91eea..362c38dd57 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java @@ -12,6 +12,7 @@ package org.locationtech.jtstest.testbuilder; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; @@ -88,6 +89,7 @@ public class SpatialFunctionPanel BorderLayout borderLayout1 = new BorderLayout(); BorderLayout borderLayout2 = new BorderLayout(); + JPanel panelFunction = new JPanel(); JPanel panelParam = new JPanel(); JPanel panelExec = new JPanel(); JPanel panelExecMeta = new JPanel(); @@ -143,7 +145,7 @@ void uiInit() throws Exception { geomFuncPanel.populate(JTSTestBuilder.getFunctionRegistry().getCategorizedGeometryFunctions()); panelParam.setLayout(gridLayout2); - gridLayout2.setRows(6); + gridLayout2.setRows(5); gridLayout2.setColumns(2); panelExec.setLayout(new FlowLayout()); panelExecParam.setLayout(borderLayout2); @@ -152,8 +154,9 @@ void uiInit() throws Exception { lblFunction.setHorizontalAlignment(SwingConstants.RIGHT); lblFunction.setBorder(LABEL_BORDER);//top,left,bottom,right - lblFunctionName.setHorizontalAlignment(SwingConstants.LEFT); - lblFunctionName.setFont(new java.awt.Font("Dialog", Font.BOLD, 12)); + lblFunctionName.setHorizontalAlignment(SwingConstants.CENTER); + lblFunctionName.setFont(new java.awt.Font("Dialog", Font.PLAIN, 14)); + lblFunctionName.setForeground(Color.BLUE); lblDistance.setText("Distance"); @@ -180,8 +183,8 @@ void uiInit() throws Exception { initLabels(paramLabel); - panelParam.add(lblFunction); - panelParam.add(lblFunctionName); + //panelParam.add(lblFunction); + //panelParam.add(lblFunctionName); panelParam.add(lblDistance); panelParam.add(txtDistance); panelParam.add(lblQuadSegs); @@ -193,6 +196,10 @@ void uiInit() throws Exception { panelParam.add(lblMitreLimit); panelParam.add(txtMitreLimit); + panelFunction.setLayout(new BorderLayout()); + panelFunction.add(lblFunctionName, BorderLayout.NORTH); + panelFunction.add(panelParam, BorderLayout.CENTER); + cbExecEachA.setToolTipText("Compute for each A geometry element"); cbExecEachA.setText("Each A"); @@ -270,7 +277,7 @@ public void actionPerformed(ActionEvent e) { panelExecControl.add(panelExecHolder); panelExecControl.add(panelExecMeta); - panelExecParam.add(panelParam, BorderLayout.CENTER); + panelExecParam.add(panelFunction, BorderLayout.CENTER); panelExecParam.add(panelExecControl, BorderLayout.SOUTH); this.add(geomFuncPanel, BorderLayout.CENTER); From 6ed13c71dc49583e6429a8c90955457cb51ce858 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 15 Jan 2022 19:30:26 -0800 Subject: [PATCH 184/275] Improve TestBuilder function name layout Signed-off-by: Martin Davis --- .../locationtech/jtstest/testbuilder/SpatialFunctionPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java index 362c38dd57..4af97f32e9 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java @@ -154,9 +154,10 @@ void uiInit() throws Exception { lblFunction.setHorizontalAlignment(SwingConstants.RIGHT); lblFunction.setBorder(LABEL_BORDER);//top,left,bottom,right - lblFunctionName.setHorizontalAlignment(SwingConstants.CENTER); + lblFunctionName.setHorizontalAlignment(SwingConstants.LEFT); lblFunctionName.setFont(new java.awt.Font("Dialog", Font.PLAIN, 14)); lblFunctionName.setForeground(Color.BLUE); + lblFunctionName.setBorder(new EmptyBorder(0,10,2,0)); lblDistance.setText("Distance"); From beb16a5a68007e42b1bb1e1befd4de613fcc162d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 15 Jan 2022 23:32:20 -0800 Subject: [PATCH 185/275] Add TestBuilder tooltips to Layer List Signed-off-by: Martin Davis --- .../jtstest/testbuilder/LayerListPanel.java | 31 ++++++++++++++----- .../testbuilder/geom/GeometryUtil.java | 2 +- .../testbuilder/model/GeometryEditModel.java | 2 ++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java index 0dcfc40cb3..7406563f60 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java @@ -307,6 +307,10 @@ class LayerItemPanel extends JPanel { self = this; } + public String getToolTipText(MouseEvent e) { + return layer.getNameInfo(); + } + public Layer getLayer() { return layer; } @@ -414,19 +418,30 @@ public void mouseExited(MouseEvent e) { } -class LayerStyleSwatchControl { +class LayerStyleSwatchControl extends JPanel { - public static JPanel create(Layer layer) { - JPanel ctl = new JPanel(); - Dimension dim = new Dimension(16,16); - ctl.setMinimumSize(dim); - ctl.setPreferredSize(dim); - ctl.setMaximumSize(dim); - ctl.setOpaque(true); + public static LayerStyleSwatchControl create(Layer layer) { + LayerStyleSwatchControl ctl = new LayerStyleSwatchControl(layer); //update(ctl, layer); return ctl; } + + private Layer layer; + + public LayerStyleSwatchControl(Layer layer) { + this.layer = layer; + Dimension dim = new Dimension(16,16); + setMinimumSize(dim); + setPreferredSize(dim); + setMaximumSize(dim); + setOpaque(true); + setToolTipText(layer.getName()); + } + public String getToolTipText(MouseEvent e) { + return layer.getNameInfo(); + } + public static void update(JPanel ctl, Layer layer) { Color fillClr = Color.WHITE; diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java index 90dc2f6378..e8cbcbf9c5 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/geom/GeometryUtil.java @@ -23,7 +23,7 @@ public static String structureSummary(Geometry g) if (g == null) return ""; if (g instanceof Polygon) { int nHoles = ((Polygon) g).getNumInteriorRing(); - if (nHoles > 0) structure = nHoles + " holes, " ; + if (nHoles > 0) structure = nHoles + (nHoles > 1 ? " holes, " : " hole, "); } String size = ""; if (g instanceof GeometryCollection) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java index 271d6126a0..79dccfc961 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/GeometryEditModel.java @@ -150,6 +150,7 @@ public static String toStringVeryLarge(Geometry g) public Geometry getResult() { // return result; + if (testCase == null) return null; return testCase.getResult(); } @@ -160,6 +161,7 @@ public Geometry getGeometry() public Geometry getGeometry(int i) { + if (testCase == null) return null; return testCase.getGeometry(i); } From c5c344b95a1ee42c11dc85ca97616f951b141b1a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 07:55:34 -0800 Subject: [PATCH 186/275] Improve TestBuilder layer tooltip Signed-off-by: Martin Davis --- .../jtstest/testbuilder/LayerListPanel.java | 17 +++++++++++++++-- .../jtstest/testbuilder/model/Layer.java | 6 ++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java index 7406563f60..071616fa40 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/LayerListPanel.java @@ -352,7 +352,7 @@ public void actionPerformed(ActionEvent e) { checkbox.setOpaque(false); - lblName = new JLabel(layer.getName()); + lblName = new LayerName(layer); lblName.setAlignmentX(Component.LEFT_ALIGNMENT); lblName.setMinimumSize(new Dimension(100,12)); lblName.setPreferredSize(new Dimension(100,12)); @@ -417,6 +417,19 @@ public void mouseExited(MouseEvent e) { } +class LayerName extends JLabel { + private Layer layer; + + public LayerName(Layer layer) { + super(layer.getName()); + this.layer = layer; + setToolTipText(layer.getName()); + } + + public String getToolTipText(MouseEvent e) { + return layer.getNameSummary(); + } +} class LayerStyleSwatchControl extends JPanel { @@ -439,7 +452,7 @@ public LayerStyleSwatchControl(Layer layer) { } public String getToolTipText(MouseEvent e) { - return layer.getNameInfo(); + return layer.getNameSummary(); } public static void update(JPanel ctl, Layer layer) { diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java index 5e78d84270..0b017a0e81 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/Layer.java @@ -51,6 +51,12 @@ public String getNameInfo() { + " -- " + GeometryUtil.metricsSummary(geomCont.getGeometry()); } + public String getNameSummary() { + if (geomCont.getGeometry() == null) return getName(); + return getName() + + " " + GeometryUtil.structureSummary(geomCont.getGeometry()); + } + public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; From bed6b99194ea7d6f0325ba4280b17e7c9b6f3c80 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 08:06:27 -0800 Subject: [PATCH 187/275] Improve TestBuilder Stats display Signed-off-by: Martin Davis --- .../jtstest/testbuilder/StatsPanel.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java index 7edd893f06..07a00567fa 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/StatsPanel.java @@ -21,6 +21,8 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jtstest.testbuilder.geom.GeometryUtil; +import org.locationtech.jtstest.testbuilder.model.Layer; +import org.locationtech.jtstest.testbuilder.model.LayerList; import org.locationtech.jtstest.testbuilder.model.TestBuilderModel; @@ -67,13 +69,21 @@ public void refresh() { StringBuffer buf = new StringBuffer(); - writeGeomStats("A", tbModel.getCurrentCase().getGeometry(0), buf); - writeGeomStats("B", tbModel.getCurrentCase().getGeometry(1), buf); - writeGeomStats("Result", tbModel.getCurrentCase().getResult(), buf); - + writeGeomStats(JTSTestBuilder.model().getLayers(), buf); + buf.append("\n-------------\n\n"); + writeGeomStats(JTSTestBuilder.model().getLayersTop(), buf); + writeGeomStats(JTSTestBuilder.model().getLayersBase(), buf); + setString(buf.toString()); } + private void writeGeomStats(LayerList lyrList, StringBuffer buf) { + for (int i = 0; i < lyrList.size(); i++) { + Layer lyr = lyrList.getLayer(i); + writeGeomStats(lyr.getName(), lyr.getGeometry(), buf); + } + } + private void writeGeomStats(String label, Geometry g, StringBuffer buf) { From 42d4f6c58c728953a65027d7b2cdb5beebfb6890 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 08:19:35 -0800 Subject: [PATCH 188/275] Add TestBuilder Legend metrics display Signed-off-by: Martin Davis --- .../jtstest/testbuilder/GeometryEditPanel.java | 1 + .../jtstest/testbuilder/GeometryViewStylePanel.java | 12 +++++++++++- .../jtstest/testbuilder/ui/render/LegendElement.java | 9 +++++++++ .../jtstest/testbuilder/ui/render/ViewStyle.java | 10 ++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java index 10a8db07f1..f09fe61369 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryEditPanel.java @@ -627,6 +627,7 @@ public void render(Graphics2D g2) if (viewStyle.isLegendEnabled()) { legendElement.setBorderEnabled(viewStyle.isLegendBorderEnabled()); legendElement.setStatsEnabled(viewStyle.isLegendStatsEnabled()); + legendElement.setMetricsEnabled(viewStyle.isLegendMetricsEnabled()); legendElement.setBorderColor(viewStyle.getBorderColor()); legendElement.setFill(viewStyle.getLegendFill()); legendElement.paint(tbModel.getLayersLegend(), g2); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java index 03fe341ed9..93bed89079 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/GeometryViewStylePanel.java @@ -37,6 +37,7 @@ public class GeometryViewStylePanel extends LabelComponentsPanel { private JPanel ctlBorderClr; private JPanel ctlLegendFillClr; private JCheckBox cbLegendStats; + private JCheckBox cbLegendMetrics; public GeometryViewStylePanel() { try { @@ -110,7 +111,15 @@ public void colorChanged(Color clr) { public void actionPerformed(ActionEvent e) { updateView(); } }); - addRow("Legend", cbLegend, ctlLegendFillClr, "Border", cbLegendBorder, "Stats", cbLegendStats ); + cbLegendMetrics = new JCheckBox(); + cbLegendMetrics.setSelected(viewStyle.isLegendStatsEnabled()); + cbLegendMetrics.setAlignmentX(Component.LEFT_ALIGNMENT); + cbLegendMetrics.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(ActionEvent e) { + updateView(); } + }); + addRow("Legend", cbLegend, ctlLegendFillClr, "Border", cbLegendBorder, + "Stats", cbLegendStats, "Metrics", cbLegendMetrics ); cbViewBorder = new JCheckBox(); cbViewBorder.setSelected(viewStyle.isBorderEnabled()); @@ -164,6 +173,7 @@ private void updateView() { viewStyle.setLegendEnabled(cbLegend.isSelected()); viewStyle.setLegendBorderEnabled(cbLegendBorder.isSelected()); viewStyle.setLegendStatsEnabled(cbLegendStats.isSelected()); + viewStyle.setLegendMetricsEnabled(cbLegendMetrics.isSelected()); viewStyle.setLegendFill(ctlLegendFillClr.getBackground()); JTSTestBuilder.controller().setViewStyle(viewStyle); diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java index db7795d9b6..5b513aab25 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java @@ -43,11 +43,13 @@ public class LegendElement { private boolean isBorderEnabled; private boolean isStatsEnabled = false; + private boolean isMetricsEnabled = false; private Color borderColor; private Color fillClr = Color.WHITE; + public LegendElement(Viewport viewport) { this.viewport = viewport; } @@ -60,6 +62,10 @@ public void setStatsEnabled(boolean isEnabled) { this.isStatsEnabled = isEnabled; } + public void setMetricsEnabled(boolean isEnabled) { + this.isMetricsEnabled = isEnabled; + } + public void setBorder(int borderSize) { this.borderSize = borderSize; } @@ -110,6 +116,9 @@ private String getDescription(Layer layer) { if (isStatsEnabled) { desc += " -- " + GeometryUtil.structureSummary(layer.getGeometry()); } + if (isMetricsEnabled) { + desc += " - " + GeometryUtil.metricsSummary(layer.getGeometry()); + } return desc; } diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java index 152b6be1b4..003f7b022a 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/ViewStyle.java @@ -30,6 +30,7 @@ public class ViewStyle { private boolean isLegendEnabled = false; private boolean isLegendBorderEnabled = true; private boolean isLegendStatsEnabled; + private boolean isLegendMetricsEnabled; private Color legendFillClr = Color.WHITE; private boolean isTitleEnabled = false; @@ -42,6 +43,7 @@ public class ViewStyle { private boolean isBorderEnabled; private Color borderClr = Color.GRAY; + public ViewStyle() { } @@ -137,4 +139,12 @@ public boolean isLegendStatsEnabled() { return isLegendStatsEnabled; } + public void setLegendMetricsEnabled(boolean isEabled) { + this.isLegendMetricsEnabled = isEabled; + } + + public boolean isLegendMetricsEnabled() { + return isLegendMetricsEnabled; + } + } From 905efdcc0c604c9b5a45b5c32fca6274a1eea72b Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 11:51:35 -0800 Subject: [PATCH 189/275] Improve TestBuilder Legend Signed-off-by: Martin Davis --- .../testbuilder/ui/render/LegendElement.java | 89 ++++++++++++------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java index 5b513aab25..0e52b6b3b8 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java @@ -36,9 +36,13 @@ public class LegendElement { private static final int SWATCH_MARGIN = 6; private static final int DEFAULT_FONT_SIZE = 12; + private static final int STAT_FONT_SIZE = 10; + + private static final int DESC_INDENT = 6; private Viewport viewport; - private Font font = new Font(FontGlyphReader.FONT_SANSERIF, Font.PLAIN, DEFAULT_FONT_SIZE); + private Font font = new Font(FontGlyphReader.FONT_SANSERIF, Font.BOLD, DEFAULT_FONT_SIZE); + private Font fontDesc = new Font(FontGlyphReader.FONT_SANSERIF, Font.ITALIC, STAT_FONT_SIZE); private int borderSize = 1; private boolean isBorderEnabled; @@ -86,42 +90,59 @@ public void paint(List layerList, Graphics2D g) { drawEntries(layerList, box, g); } + private boolean hasDesc() { + return isStatsEnabled || isMetricsEnabled; + } + + private int lineHeight() { + return DEFAULT_FONT_SIZE + 6 + (hasDesc() ? DEFAULT_FONT_SIZE : 0 ); + } + private void drawEntries(List layerList, Rectangle box, Graphics2D g) { g.setFont(font); int nameX = box.x + BOX_MARGIN + SWATCH_SIZE + SWATCH_MARGIN; - // have to account for width of outline - int baseY = box.y + BOX_MARGIN - 2 * borderSize; - int lineHeight = DEFAULT_FONT_SIZE + 4; + // have to account for width of border + int topY = box.y + BOX_MARGIN + borderSize; + int n = layerList.size(); for (int i = 0; i < n; i++) { // draw layer name - int nameY = baseY + (i + 1) * lineHeight; - drawEntry(layerList.get(i), nameX, nameY, g); + int entryTopY = topY + i * lineHeight(); + drawEntry(layerList.get(i), nameX, entryTopY, g); } } - private void drawEntry(Layer layer, int nameX, int nameY, Graphics2D g) { - String name = getDescription(layer); + private void drawEntry(Layer layer, int nameX, int topY, Graphics2D g) { g.setPaint(NAME_CLR); - g.drawString(name, nameX, nameY); + g.setFont(font); + g.drawString(getName(layer), nameX, topY + DEFAULT_FONT_SIZE ); + if (hasDesc()) { + g.setFont(fontDesc); + g.drawString(getDescription(layer), nameX + DESC_INDENT, topY + DEFAULT_FONT_SIZE + STAT_FONT_SIZE + 3); + } int swatchX = nameX - SWATCH_SIZE - SWATCH_MARGIN; - int swatchY = nameY - DEFAULT_FONT_SIZE + 2; + int swatchY = topY + 2; drawSwatch(layer, swatchX, swatchY, g); } private String getDescription(Layer layer) { - String desc = layer.getName(); + String desc = ""; if (isStatsEnabled) { - desc += " -- " + GeometryUtil.structureSummary(layer.getGeometry()); + desc += GeometryUtil.structureSummary(layer.getGeometry()); } if (isMetricsEnabled) { - desc += " - " + GeometryUtil.metricsSummary(layer.getGeometry()); + if (desc.length() > 0) desc += " / "; + desc += GeometryUtil.metricsSummary(layer.getGeometry()); } return desc; } + private String getName(Layer layer) { + return layer.getName(); + } + private void drawSwatch(Layer layer, int x, int y, Graphics2D g) { Geometry geom = layer.getGeometry(); switch (geom.getDimension()) { @@ -151,21 +172,21 @@ private void drawSwatchBox(Layer layer, int x, int y, Graphics2D g) { g.fill(box); //--- paint Line - float lineWidth = layer.getGeometryStyle().getStrokeWidth(); - if (layer.getGeometryStyle().getStrokeWidth() > 3) - lineWidth = 3; - - Stroke strokeBox = new BasicStroke(lineWidth, // Width of stroke - BasicStroke.CAP_BUTT, // End cap style - BasicStroke.JOIN_MITER, // Join style - 10, // Miter limit - null, // Dash pattern - 0); // Dash phase - g.setStroke(strokeBox); - - Color lineClr = layer.getGeometryStyle().getLineColor(); - g.setPaint(lineClr); - g.draw(box); + if (layer.getGeometryStyle().isStroked()) { + float lineWidth = layer.getGeometryStyle().getStrokeWidth(); + if (layer.getGeometryStyle().getStrokeWidth() > 3) + lineWidth = 3; + Stroke strokeBox = new BasicStroke(lineWidth, // Width of stroke + BasicStroke.CAP_BUTT, // End cap style + BasicStroke.JOIN_MITER, // Join style + 10, // Miter limit + null, // Dash pattern + 0); // Dash phase + g.setStroke(strokeBox); + Color lineClr = layer.getGeometryStyle().getLineColor(); + g.setPaint(lineClr); + g.draw(box); + } } private void drawSwatchLine(Layer layer, int x, int y, Graphics2D g) { @@ -226,8 +247,7 @@ private void drawBox(Rectangle box, Graphics2D g) { private Rectangle computeBox(List layerList, Graphics2D g) { int width = entryWidth(layerList, g) + 2 * BOX_MARGIN + SWATCH_SIZE + SWATCH_MARGIN; - int lineHeight = DEFAULT_FONT_SIZE + 4; - int height = layerList.size() * lineHeight + 2 * BOX_MARGIN; + int height = layerList.size() * lineHeight() + 2 * BOX_MARGIN; int viewHeight = (int) viewport.getHeightInView(); int viewWidth = (int) viewport.getWidthInView(); @@ -241,10 +261,16 @@ private Rectangle computeBox(List layerList, Graphics2D g) { private int entryWidth(List layerList, Graphics2D g2) { int width = 0; for (Layer layer : layerList) { - String s = getDescription(layer); + String s = getName(layer); int nameWidth = (int) g2.getFontMetrics().getStringBounds(s, g2).getWidth(); if (nameWidth > width) width = nameWidth; + if (hasDesc()) { + String s2 = getDescription(layer); + int statWidth = DESC_INDENT + (int) fontDesc.getStringBounds(s2, g2.getFontRenderContext()).getWidth(); + if (statWidth > width) + width = statWidth; + } } return width; } @@ -255,4 +281,5 @@ private int entryWidth(List layerList, Graphics2D g2) { + } From 370e639b1e51769c0de8b954c3359758e0725a79 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 16:09:51 -0800 Subject: [PATCH 190/275] Improve TestBuilder Legend position Signed-off-by: Martin Davis --- .../jtstest/testbuilder/ui/render/LegendElement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java index 0e52b6b3b8..67f1fe06ea 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/ui/render/LegendElement.java @@ -30,7 +30,7 @@ public class LegendElement { private static final Color NAME_CLR = Color.BLACK; - private static final int BOX_OFFSET = 1; + private static final int BOX_OFFSET = 4; private static final int BOX_MARGIN = 8; private static final int SWATCH_SIZE = 10; private static final int SWATCH_MARGIN = 6; From 9e652938ef7aaa544f9172e40f76acd19c79e012 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 21:38:06 -0800 Subject: [PATCH 191/275] Fix TestBuilder layer moving Signed-off-by: Martin Davis --- .../jtstest/testbuilder/model/LayerList.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java index 2040b98d4f..6176d544c7 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/model/LayerList.java @@ -44,23 +44,23 @@ public static LayerList create(LayerList l1, LayerList l2, LayerList l3) { public static final int LYR_B = 1; public static final int LYR_RESULT = 2; - private List layer = new ArrayList(); + private List layers = new ArrayList(); public LayerList() { } void initFixed() { - layer.add(new Layer("A")); - layer.add(new Layer("B")); - layer.add(new Layer("Result")); + layers.add(new Layer("A")); + layers.add(new Layer("B")); + layers.add(new Layer("Result")); } - public int size() { return layer.size(); } + public int size() { return layers.size(); } public Layer getLayer(int i) { - return layer.get(i); + return layers.get(i); } /** @@ -131,53 +131,54 @@ private List extractLocationGeometry(List locs) public Layer copy(Layer focusLayer) { Layer lyr = new Layer(focusLayer); - layer.add(lyr); + layers.add(lyr); return lyr; } public void remove(Layer lyr) { - layer.remove(lyr); + layers.remove(lyr); } public boolean contains(Layer lyr) { - return layer.contains(lyr); + return layers.contains(lyr); } public boolean isTop(Layer lyr) { - if (layer.isEmpty()) return false; - return layer.get(0) == lyr; + if (layers.isEmpty()) return false; + return layers.get(0) == lyr; } public boolean isBottom(Layer lyr) { - if (layer.isEmpty()) return false; - return layer.get(layer.size() - 1) == lyr; + if (layers.isEmpty()) return false; + return layers.get(layers.size() - 1) == lyr; } public void addTop(Layer lyr) { - layer.add(0, lyr); + layers.add(0, lyr); } public void addBottom(Layer lyr) { - layer.add(lyr); + layers.add(lyr); } public void add(LayerList lyrList) { - layer.addAll(lyrList.layer); + layers.addAll(lyrList.layers); } public void moveUp(Layer lyr) { - int i = layer.indexOf(lyr); + int i = layers.indexOf(lyr); if (i <= 0) return; - Layer tmp = layer.get(i-1); - layer.set(i-1, lyr); - layer.set(i, tmp); + Layer tmp = layers.get(i-1); + layers.set(i-1, lyr); + layers.set(i, tmp); } public void moveDown(Layer lyr) { - int i = layer.indexOf(lyr); - if (i >= layer.size() - 1) return; - Layer tmp = layer.get(i+1); - layer.set(i+1, lyr); - layer.set(i, tmp); + int i = layers.indexOf(lyr); + if (i < 0) return; + if (i >= layers.size() - 1) return; + Layer tmp = layers.get(i+1); + layers.set(i+1, lyr); + layers.set(i, tmp); } } From 73120db457bdf7ea316544ffd3557bb963144883 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sun, 16 Jan 2022 21:57:37 -0800 Subject: [PATCH 192/275] Add TestBuilder function parameter recall Signed-off-by: Martin Davis --- .../testbuilder/SpatialFunctionPanel.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java index 4af97f32e9..d10703ea0e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/testbuilder/SpatialFunctionPanel.java @@ -20,6 +20,8 @@ import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; import java.util.Vector; import javax.swing.BorderFactory; @@ -128,6 +130,7 @@ public class SpatialFunctionPanel private GeometryFunction currentFunc = null; private Stopwatch timer; + private Map funcParamMap = new HashMap(); public SpatialFunctionPanel() { try { @@ -372,17 +375,30 @@ public void execFunction(GeometryFunction func, boolean createNew) { private void functionChanged(GeometryFunction func) { + saveParameter(currentFunc); currentFunc = func; lblFunctionName.setText(func.getName()); lblFunctionName.setToolTipText( GeometryFunctionRegistry.functionDescriptionHTML(func) ); updateParameters(func, paramComp, paramLabel); - + recallParameter(func); + execButton.setEnabled(true); execToNewButton.setEnabled(true); cbExecAuto.setSelected(false); } + + private void recallParameter(GeometryFunction func) { + if (! funcParamMap.containsKey(func)) return; + String val = funcParamMap.get(func); + txtDistance.setText(val); + } + private void saveParameter(GeometryFunction func) { + String val = SwingUtil.value(txtDistance); + funcParamMap.put(func, val); + } + static void updateParameters(GeometryFunction func, JComponent[] paramComp, JLabel[] paramLabel) { int numNonGeomParams = numNonGeomParams(func); int indexOffset = BaseGeometryFunction.firstScalarParamIndex(func); @@ -390,7 +406,7 @@ static void updateParameters(GeometryFunction func, JComponent[] paramComp, JLab boolean isUsed = numNonGeomParams > i; if (isUsed) { paramLabel[i].setText(func.getParameterNames()[i+indexOffset]); - } + } paramComp[i].setVisible(isUsed); paramLabel[i].setVisible(isUsed); SpatialFunctionPanel.setToolTipText(paramComp[i], func, i); From 942cdced66845ea5f2baa389c54090b281148fba Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 18 Jan 2022 14:41:22 -0800 Subject: [PATCH 193/275] Add CubicBezierCurve skew factor Signed-off-by: Martin Davis --- .../function/CreateShapeFunctions.java | 13 +++- .../jts/shape/CubicBezierCurve.java | 72 +++++++++++++++---- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index 9259ccddd6..58deda05a2 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -335,10 +335,19 @@ private static Coordinate[] genSpiralCycle(Coordinate centre, return pts; } - @Metadata(description="Construct a cubic Bezier curve of a line or polygon") - public static Geometry cubicBezierCurve(Geometry geom, + @Metadata(description="Construct a geometry using cubic Bezier curves") + public static Geometry bezier(Geometry geom, @Metadata(title="Alpha (curveness)") double alpha) { return CubicBezierCurve.bezierCurve(geom, alpha); } + + @Metadata(description="Construct a geometry using cubic Bezier curves with a skew") + public static Geometry bezierSkew(Geometry geom, + @Metadata(title="Alpha (curveness)") + double alpha, + @Metadata(title="Skew factor") + double skew) { + return CubicBezierCurve.bezierCurve(geom, alpha, skew); + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java index 37153b1700..bf303addb6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java @@ -20,6 +20,7 @@ import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.util.GeometryMapper; +import org.locationtech.jts.io.WKTWriter; /** * Creates a curved geometry by replacing the segments @@ -35,23 +36,40 @@ public class CubicBezierCurve { /** - * Creates a curved geometry using Cubic Bezier Curves + * Creates a geometry using linearized Cubic Bezier Curves * defined by the segments of the input. * * @param geom the geometry defining the curve - * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly distorted) - * @return + * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly curved) + * @return the curved geometry */ public static Geometry bezierCurve(Geometry geom, double alpha) { CubicBezierCurve curve = new CubicBezierCurve(geom, alpha); return curve.getResult(); } + /** + * Creates a geometry using linearized Cubic Bezier Curves + * defined by the segments of the input, with a skew factor + * affecting the shape at each vertex. + * + * @param geom the geometry defining the curve + * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly curved) + * @param skew the skew parameter (0 is none, positive skews towards longer side, negative towards shorter + * @return the curved geometry + */ + public static Geometry bezierCurve(Geometry geom, double alpha, double skew) { + CubicBezierCurve curve = new CubicBezierCurve(geom, alpha); + curve.setSkew(skew); + return curve.getResult(); + } + private double minSegmentLength = 0.0; private int numVerticesPerSegment = 16; private Geometry inputGeom; private double alpha; + private double skewFactor = 0;; private final GeometryFactory geomFactory; private Coordinate[] bezierCurvePts; @@ -70,6 +88,16 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { this.geomFactory = geom.getFactory(); } + /** + * Sets a skew factor influencing the shape of the curve corners. + * 0 is no skew, positive skews towards longer edges, negative skews towards shorter. + * + * @param skewFactor the skew factor + */ + public void setSkew(double skewFactor) { + this.skewFactor = skewFactor; + } + /** * Gets the computed Bezier curve geometry. * @@ -197,23 +225,37 @@ private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double double ang0 = angBisect - orient * Angle.PI_OVER_2; double ang1 = angBisect + orient * Angle.PI_OVER_2; - double len0 = v1.distance(v0); - double len1 = v1.distance(v2); - double lenBase = Math.min(len0, len1); + double dist0 = v1.distance(v0); + double dist1 = v1.distance(v2); + double lenBase = Math.min(dist0, dist1); + double intAngAbs = Math.abs(interiorAng); - //-- make acute corners sharper by shortening tangent + //-- make acute corners sharper by shortening tangent vectors double sharpnessFactor = intAngAbs >= Angle.PI_OVER_2 ? 1 : intAngAbs / Angle.PI_OVER_2; double len = alpha * CIRCLE_LEN_FACTOR * sharpnessFactor * lenBase; - - Coordinate cv0 = Angle.project(v1, ang0, len); - Coordinate cv1 = Angle.project(v1, ang1, len); - ctrl[i][0] = cv0; - ctrl[i][1] = cv1; - - //System.out.println(WKTWriter.toLineString(v1, cv0)); - //System.out.println(WKTWriter.toLineString(v1, cv1)); + double stretch0 = 1; + double stretch1 = 1; + if (skewFactor != 0) { + double stretch = Math.abs(dist0 - dist1) / Math.max(dist0, dist1); + int skewIndex = dist0 > dist1 ? 0 : 1; + if (skewFactor < 0) skewIndex = 1 - skewIndex; + if (skewIndex == 0) { + stretch0 += Math.abs(skewFactor) * stretch; + } + else { + stretch1 += Math.abs(skewFactor) * stretch; + } + } + Coordinate ctl0 = Angle.project(v1, ang0, stretch0 * len); + Coordinate ctl1 = Angle.project(v1, ang1, stretch1 * len); + + ctrl[i][0] = ctl0; + ctrl[i][1] = ctl1; + + //System.out.println(WKTWriter.toLineString(v1, ctl0)); + //System.out.println(WKTWriter.toLineString(v1, ctl1)); } if (! isRing) { setLineEndControlPoints(coords, ctrl); From fb914f3c451386cdab5cfd08ec026be8831cde62 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 24 Jan 2022 21:46:52 -0800 Subject: [PATCH 194/275] Improve CubicBezierCurve code (#832) Signed-off-by: Martin Davis --- .../function/CreateShapeFunctions.java | 4 +- .../jts/shape/CubicBezierCurve.java | 172 +++++++++--------- .../jts/shape/CubicBezierCurveTest.java | 60 ++++++ 3 files changed, 146 insertions(+), 90 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index 58deda05a2..561bd9d612 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -336,14 +336,14 @@ private static Coordinate[] genSpiralCycle(Coordinate centre, } @Metadata(description="Construct a geometry using cubic Bezier curves") - public static Geometry bezier(Geometry geom, + public static Geometry bezierCurve(Geometry geom, @Metadata(title="Alpha (curveness)") double alpha) { return CubicBezierCurve.bezierCurve(geom, alpha); } @Metadata(description="Construct a geometry using cubic Bezier curves with a skew") - public static Geometry bezierSkew(Geometry geom, + public static Geometry bezierCurveSkew(Geometry geom, @Metadata(title="Alpha (curveness)") double alpha, @Metadata(title="Skew factor") diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java index bf303addb6..90c4db018b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java @@ -37,10 +37,11 @@ public class CubicBezierCurve { /** * Creates a geometry using linearized Cubic Bezier Curves - * defined by the segments of the input. + * defined by the segments of the input and a parameter + * controlling how curved the result should be. * * @param geom the geometry defining the curve - * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly curved) + * @param alpha curvedness parameter (0 is linear, 1 is round, >1 is increasingly curved) * @return the curved geometry */ public static Geometry bezierCurve(Geometry geom, double alpha) { @@ -50,11 +51,12 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { /** * Creates a geometry using linearized Cubic Bezier Curves - * defined by the segments of the input, with a skew factor - * affecting the shape at each vertex. + * defined by the segments of the input and a parameter + * controlling how curved the result should be, with a skew factor + * affecting the curve shape at each vertex. * * @param geom the geometry defining the curve - * @param alpha curviness parameter (0 is linear, 1 is round, >1 is increasingly curved) + * @param alpha curvedness parameter (0 is linear, 1 is round, >1 is increasingly curved) * @param skew the skew parameter (0 is none, positive skews towards longer side, negative towards shorter * @return the curved geometry */ @@ -73,7 +75,7 @@ public static Geometry bezierCurve(Geometry geom, double alpha, double skew) { private final GeometryFactory geomFactory; private Coordinate[] bezierCurvePts; - private CubicBezierInterpolationParam[] interpolationParam; + private double[][] interpolationParam; /** * Creates a new instance. @@ -105,7 +107,7 @@ public void setSkew(double skewFactor) { */ public Geometry getResult() { bezierCurvePts = new Coordinate[numVerticesPerSegment]; - interpolationParam = CubicBezierInterpolationParam.compute(numVerticesPerSegment); + interpolationParam = computeIterpolationParameters(numVerticesPerSegment); return GeometryMapper.flatMap(inputGeom, 1, new GeometryMapper.MapOp() { @@ -125,27 +127,15 @@ public Geometry map(Geometry geom) { private LineString bezierLine(LineString ls) { Coordinate[] coords = ls.getCoordinates(); - Coordinate[][] control = controlPoints(coords, false, alpha); - final int N = coords.length; - CoordinateList curvePts = new CoordinateList(); - for (int i = 0; i < N - 1; i++) { - addCurve(coords[i], coords[i + 1], control[i][1], control[i + 1][0], curvePts); - } - curvePts.add(coords[N - 1], false); + CoordinateList curvePts = bezierCurve(coords, false); + curvePts.add(coords[coords.length - 1].copy(), false); return geomFactory.createLineString(curvePts.toCoordinateArray()); } private LinearRing bezierRing(LinearRing ring) { Coordinate[] coords = ring.getCoordinates(); - Coordinate[][] control = controlPoints(coords, true, alpha); - CoordinateList curvePts = new CoordinateList(); - final int N = coords.length - 1; - for (int i = 0; i < N; i++) { - int next = (i + 1) % N; - addCurve(coords[i], coords[next], control[i][1], control[next][0], curvePts); - } + CoordinateList curvePts = bezierCurve(coords, true); curvePts.closeRing(); - return geomFactory.createLinearRing(curvePts.toCoordinateArray()); } @@ -161,6 +151,16 @@ private Polygon bezierPolygon(Polygon poly) { return geomFactory.createPolygon(shell, holes); } + private CoordinateList bezierCurve(Coordinate[] coords, boolean isRing) { + Coordinate[] control = controlPoints(coords, false, alpha, skewFactor); + CoordinateList curvePts = new CoordinateList(); + for (int i = 0; i < coords.length - 1; i++) { + int ctrlIndex = 2 * i; + addCurve(coords[i], coords[i + 1], control[ctrlIndex], control[ctrlIndex + 1], curvePts); + } + return curvePts; + } + private void addCurve(Coordinate p0, Coordinate p1, Coordinate ctrl0, Coordinate crtl1, CoordinateList curvePts) { @@ -178,7 +178,7 @@ private void addCurve(Coordinate p0, Coordinate p1, } } - //-- makes curve at right-angle corners roughly circular + //-- chosen to make curve at right-angle corners roughly circular private static final double CIRCLE_LEN_FACTOR = 3.0 / 8.0; /** @@ -191,33 +191,32 @@ private void addCurve(Coordinate p0, Coordinate p1, * The alpha parameter controls the length of the control vectors. * Alpha = 0 makes the vectors zero-length, and hence flattens the curves. * Alpha = 1 makes the curve at right angles roughly circular. - * Alpha > 1 starts to distort the curve and may introduce self-intersections + * Alpha > 1 starts to distort the curve and may introduce self-intersections. + *

      + * The control point array contains a pair of coordinates for each input segment. * * @param coords * @param isRing * @param alpha determines the curviness - * @return + * @return the control point array */ - private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double alpha) { - final int N = isRing ? coords.length - 1 : coords.length; - Coordinate[][] ctrl = new Coordinate[N][2]; - - Coordinate v0 = coords[0]; - Coordinate v1 = coords[1]; - Coordinate v2 = coords[2]; + private Coordinate[] controlPoints(Coordinate[] coords, boolean isRing, double alpha, double skew) { + int N = coords.length; + int start = 1; + int end = N - 1; if (isRing) { - v0 = coords[N - 1]; - v1 = coords[0]; - v1 = coords[1]; + N = coords.length - 1; + start = 0; + end = N; } - final int start = isRing ? 0 : 1; - final int end = isRing ? N : N - 1; + int nControl = 2 * coords.length - 2; + Coordinate[] ctrl = new Coordinate[nControl]; for (int i = start; i < end; i++) { int iprev = i == 0 ? N - 1 : i - 1; - v0 = coords[iprev]; - v1 = coords[i]; - v2 = coords[i + 1]; + Coordinate v0 = coords[iprev]; + Coordinate v1 = coords[i]; + Coordinate v2 = coords[i + 1]; double interiorAng = Angle.angleBetweenOriented(v0, v1, v2); double orient = Math.signum(interiorAng); @@ -237,22 +236,25 @@ private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double double len = alpha * CIRCLE_LEN_FACTOR * sharpnessFactor * lenBase; double stretch0 = 1; double stretch1 = 1; - if (skewFactor != 0) { + if (skew != 0) { double stretch = Math.abs(dist0 - dist1) / Math.max(dist0, dist1); int skewIndex = dist0 > dist1 ? 0 : 1; - if (skewFactor < 0) skewIndex = 1 - skewIndex; + if (skew < 0) skewIndex = 1 - skewIndex; if (skewIndex == 0) { - stretch0 += Math.abs(skewFactor) * stretch; + stretch0 += Math.abs(skew) * stretch; } else { - stretch1 += Math.abs(skewFactor) * stretch; + stretch1 += Math.abs(skew) * stretch; } } Coordinate ctl0 = Angle.project(v1, ang0, stretch0 * len); Coordinate ctl1 = Angle.project(v1, ang1, stretch1 * len); - ctrl[i][0] = ctl0; - ctrl[i][1] = ctl1; + int index = 2 * i - 1; + // for a ring case the first control point is for last segment + int i0 = index < 0 ? nControl - 1 : index; + ctrl[i0] = ctl0; + ctrl[index + 1] = ctl1; //System.out.println(WKTWriter.toLineString(v1, ctl0)); //System.out.println(WKTWriter.toLineString(v1, ctl1)); @@ -271,10 +273,11 @@ private Coordinate[][] controlPoints(Coordinate[] coords, boolean isRing, double * @param coords * @param ctrl */ - private void setLineEndControlPoints(Coordinate[] coords, Coordinate[][] ctrl) { - int N = coords.length; - ctrl[0][1] = mirrorControlPoint(ctrl[1][0], coords[1], coords[0]); - ctrl[N - 1][0] = mirrorControlPoint(ctrl[N - 2][1], coords[N - 1], coords[N - 2]); + private void setLineEndControlPoints(Coordinate[] coords, Coordinate[] ctrl) { + int N = ctrl.length; + ctrl[0] = mirrorControlPoint(ctrl[1], coords[1], coords[0]); + ctrl[N - 1] = mirrorControlPoint(ctrl[N - 2], + coords[coords.length - 1], coords[coords.length - 2]); } /** @@ -320,60 +323,53 @@ private static Coordinate reflectPointInLine(Coordinate p, Coordinate p0, Coordi /** * Calculates vertices along a cubic Bezier curve. * - * @param start start point - * @param end end point + * @param p0 start point + * @param p1 end point * @param ctrl1 first control point * @param ctrl2 second control point - * @param ip interpolation parameters + * @param param interpolation parameters * @param curve array to hold generated points */ - private void cubicBezier(final Coordinate start, - final Coordinate end, final Coordinate ctrl1, - final Coordinate ctrl2, CubicBezierInterpolationParam[] ip, Coordinate[] curve) { + private void cubicBezier(final Coordinate p0, + final Coordinate p1, final Coordinate ctrl1, + final Coordinate ctrl2, double[][] param, + Coordinate[] curve) { int n = curve.length; - curve[0] = new Coordinate(start); - curve[n - 1] = new Coordinate(end); + curve[0] = new Coordinate(p0); + curve[n - 1] = new Coordinate(p1); for (int i = 1; i < n - 1; i++) { Coordinate c = new Coordinate(); - - c.x = ip[i].t[0] * start.x + ip[i].t[1] * ctrl1.x + ip[i].t[2] * ctrl2.x + ip[i].t[3] * end.x; - c.x /= ip[i].tsum; - c.y = ip[i].t[0] * start.y + ip[i].t[1] * ctrl1.y + ip[i].t[2] * ctrl2.y + ip[i].t[3] * end.y; - c.y /= ip[i].tsum; + double sum = param[i][0] + param[i][1] +param[i][2] +param[i][3]; + c.x = param[i][0] * p0.x + param[i][1] * ctrl1.x + param[i][2] * ctrl2.x + param[i][3] * p1.x; + c.x /= sum; + c.y = param[i][0] * p0.y + param[i][1] * ctrl1.y + param[i][2] * ctrl2.y + param[i][3] * p1.y; + c.y /= sum; curve[i] = c; } } - private static final class CubicBezierInterpolationParam { - double[] t = new double[4]; - double tsum; - - /** - * Gets the interpolation parameters for a Bezier curve approximated by the - * given number of vertices. - * - * @param n number of vertices - * @return array of {@code InterpPoint} objects holding the parameter values - */ - private static CubicBezierInterpolationParam[] compute(int n) { - CubicBezierInterpolationParam[] param = new CubicBezierInterpolationParam[n]; - - for (int i = 0; i < n; i++) { - double t = (double) i / (n - 1); - double tc = 1.0 - t; + /** + * Gets the interpolation parameters for a Bezier curve approximated by a + * given number of vertices. + * + * @param n number of vertices + * @return array of double[4] holding the parameter values + */ + private static double[][] computeIterpolationParameters(int n) { + double[][] param = new double[n][4]; + for (int i = 0; i < n; i++) { + double t = (double) i / (n - 1); + double tc = 1.0 - t; - param[i] = new CubicBezierInterpolationParam(); - param[i].t[0] = tc * tc * tc; - param[i].t[1] = 3.0 * tc * tc * t; - param[i].t[2] = 3.0 * tc * t * t; - param[i].t[3] = t * t * t; - param[i].tsum = param[i].t[0] + param[i].t[1] + param[i].t[2] + param[i].t[3]; - } - return param; + param[i][0] = tc * tc * tc; + param[i][1] = 3.0 * tc * tc * t; + param[i][2] = 3.0 * tc * t * t; + param[i][3] = t * t * t; } + return param; } } \ No newline at end of file diff --git a/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java b/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java new file mode 100644 index 0000000000..6490aa5c10 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Martin Davis. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html + * and the Eclipse Distribution License is available at + * + * http://www.eclipse.org/org/documents/edl-v10.php. + */ +package org.locationtech.jts.shape; + +import org.locationtech.jts.geom.Geometry; + +import junit.textui.TestRunner; +import test.jts.GeometryTestCase; + +/** + * Tests {@link CubicBezierCurve}. + * + * Because of the inherent approximation and variability of Bezier Curves, + * this class is not intended to check exact results, only that results are reasonable. + * + * @author mdavis + * + */ +public class CubicBezierCurveTest +extends GeometryTestCase { + public static void main(String args[]) { + TestRunner.run(CubicBezierCurveTest.class); + } + + public CubicBezierCurveTest(String name) + { + super(name); + } + + public void testSquare() { + checkCurve("POLYGON ((40 60, 60 60, 60 40, 40 40, 40 60))", 1, + "POLYGON ((40 60, 41.1 61, 42.3 61.8, 43.6 62.5, 45 63.1, 46.4 63.5, 47.8 63.8, 49.3 64, 50.7 64, 52.2 63.8, 53.6 63.5, 55 63.1, 56.4 62.5, 57.7 61.8, 58.9 61, 60 60, 61 58.9, 61.8 57.7, 62.5 56.4, 63.1 55, 63.5 53.6, 63.8 52.2, 64 50.7, 64 49.3, 63.8 47.8, 63.5 46.4, 63.1 45, 62.5 43.6, 61.8 42.3, 61 41.1, 60 40, 58.9 39, 57.7 38.2, 56.4 37.5, 55 36.9, 53.6 36.5, 52.2 36.2, 50.7 36, 49.3 36, 47.8 36.2, 46.4 36.5, 45 36.9, 43.6 37.5, 42.3 38.2, 41.1 39, 40 40, 39 41.1, 38.2 42.3, 37.5 43.6, 36.9 45, 36.5 46.4, 36.2 47.8, 36 49.3, 36 50.7, 36.2 52.2, 36.5 53.6, 36.9 55, 37.5 56.4, 38.2 57.7, 39 58.9, 40 60))"); + } + + public void testRightAngle() { + checkCurve("LINESTRING (30 40, 40 50, 50 40)", 1, + "LINESTRING (30 40, 30.1 41.1, 30.2 42.1, 30.5 43.1, 30.9 44, 31.4 44.9, 32 45.8, 32.7 46.6, 33.4 47.3, 34.2 48, 35.1 48.6, 36 49.1, 36.9 49.5, 37.9 49.8, 38.9 49.9, 40 50, 41.1 49.9, 42.1 49.8, 43.1 49.5, 44 49.1, 44.9 48.6, 45.8 48, 46.6 47.3, 47.3 46.6, 48 45.8, 48.6 44.9, 49.1 44, 49.5 43.1, 49.8 42.1, 49.9 41.1, 50 40)"); + } + + public void testRightZigzag() { + checkCurve("LINESTRING (10 10, 20 19, 30 10, 40 20)", 1, + "LINESTRING (10 10, 10.2 11, 10.4 11.9, 10.8 12.9, 11.2 13.7, 11.7 14.6, 12.3 15.3, 13 16, 13.7 16.7, 14.5 17.3, 15.3 17.8, 16.2 18.2, 17.1 18.5, 18 18.8, 19 18.9, 20 19, 20.9 18.9, 21.8 18.6, 22.5 18.1, 23.1 17.4, 23.7 16.6, 24.2 15.8, 24.8 14.9, 25.2 14, 25.8 13.1, 26.3 12.3, 26.9 11.5, 27.5 10.9, 28.2 10.4, 29.1 10.1, 30 10, 31 10.1, 32 10.3, 33 10.6, 33.9 11, 34.8 11.5, 35.7 12.1, 36.5 12.8, 37.2 13.5, 37.9 14.3, 38.5 15.2, 39 16.1, 39.4 17, 39.7 18, 39.9 19, 40 20)"); + } + + private void checkCurve(String wkt, double alpha, String wktExpected) { + Geometry geom = read(wkt); + Geometry actual = CubicBezierCurve.bezierCurve(geom, alpha); + Geometry expected = read(wktExpected); + checkEqual(expected, actual, 0.5); + } +} From 388374187ff432086791fb0e52670392e0aae25d Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Tue, 25 Jan 2022 13:54:35 -0800 Subject: [PATCH 195/275] Add CubicBezierCurve support for explicit control points Signed-off-by: Martin Davis --- .../function/CreateShapeFunctions.java | 5 + .../jts/shape/CubicBezierCurve.java | 113 ++++++++++++++---- .../jts/shape/CubicBezierCurveTest.java | 20 +++- 3 files changed, 114 insertions(+), 24 deletions(-) diff --git a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java index 561bd9d612..f2b5f36c2e 100644 --- a/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java +++ b/modules/app/src/main/java/org/locationtech/jtstest/function/CreateShapeFunctions.java @@ -350,4 +350,9 @@ public static Geometry bezierCurveSkew(Geometry geom, double skew) { return CubicBezierCurve.bezierCurve(geom, alpha, skew); } + + @Metadata(description="Construct a geometry using cubic Bezier curves with control points") + public static Geometry bezierCurveControl(Geometry geom, Geometry controlPoints) { + return CubicBezierCurve.bezierCurve(geom, controlPoints); + } } diff --git a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java index 90c4db018b..0dfac1d1c9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java +++ b/modules/core/src/main/java/org/locationtech/jts/shape/CubicBezierCurve.java @@ -26,23 +26,26 @@ * Creates a curved geometry by replacing the segments * of the input with Cubic Bezier Curves. * The Bezier control points are determined from the segments of the geometry - * and the alpha control parameter. + * and the alpha control parameter controlling curvedness, and + * the optional skew parameter controlling the shape of the curve at vertices. * The Bezier Curves are created to be C2-continuous (smooth) * at each input vertex. *

      + * Alternatively, the Bezier control points can be supplied explicitly. + *

      * The result is not guaranteed to be valid, since large alpha values * may cause self-intersections. */ public class CubicBezierCurve { /** - * Creates a geometry using linearized Cubic Bezier Curves + * Creates a geometry of linearized Cubic Bezier Curves * defined by the segments of the input and a parameter * controlling how curved the result should be. * * @param geom the geometry defining the curve * @param alpha curvedness parameter (0 is linear, 1 is round, >1 is increasingly curved) - * @return the curved geometry + * @return the linearized curved geometry */ public static Geometry bezierCurve(Geometry geom, double alpha) { CubicBezierCurve curve = new CubicBezierCurve(geom, alpha); @@ -50,7 +53,7 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { } /** - * Creates a geometry using linearized Cubic Bezier Curves + * Creates a geometry of linearized Cubic Bezier Curves * defined by the segments of the input and a parameter * controlling how curved the result should be, with a skew factor * affecting the curve shape at each vertex. @@ -58,11 +61,30 @@ public static Geometry bezierCurve(Geometry geom, double alpha) { * @param geom the geometry defining the curve * @param alpha curvedness parameter (0 is linear, 1 is round, >1 is increasingly curved) * @param skew the skew parameter (0 is none, positive skews towards longer side, negative towards shorter - * @return the curved geometry + * @return the linearized curved geometry */ public static Geometry bezierCurve(Geometry geom, double alpha, double skew) { - CubicBezierCurve curve = new CubicBezierCurve(geom, alpha); - curve.setSkew(skew); + CubicBezierCurve curve = new CubicBezierCurve(geom, alpha, skew); + return curve.getResult(); + } + + /** + * Creates a geometry of linearized Cubic Bezier Curves + * defined by the segments of the input + * and a list (or lists) of control points. + *

      + * Typically the control point geometry + * is a {@link LineString} or {@link MultiLineString} + * containing an element for each line or ring in the input geometry. + * The list of control points for each linear element must contain two + * vertices for each segment (and thus 2 * npts - 2). + * + * @param geom the geometry defining the curve + * @param controlPoints a geometry containing the control point elements. + * @return the linearized curved geometry + */ + public static Geometry bezierCurve(Geometry geom, Geometry controlPoints) { + CubicBezierCurve curve = new CubicBezierCurve(geom, controlPoints); return curve.getResult(); } @@ -70,40 +92,68 @@ public static Geometry bezierCurve(Geometry geom, double alpha, double skew) { private int numVerticesPerSegment = 16; private Geometry inputGeom; - private double alpha; - private double skewFactor = 0;; + private double alpha =-1; + private double skew = 0; + private Geometry controlPoints = null; private final GeometryFactory geomFactory; private Coordinate[] bezierCurvePts; private double[][] interpolationParam; + private int controlPointIndex = 0; /** - * Creates a new instance. + * Creates a new instance producing a Bezier curve defined by a geometry + * and an alpha curvedness value. * * @param geom geometry defining curve - * @param alpha curviness parameter (0 = linear, 1 = round, 2 = distorted) + * @param alpha curvedness parameter (0 = linear, 1 = round, 2 = distorted) */ CubicBezierCurve(Geometry geom, double alpha) { this.inputGeom = geom; - //if ( alpha < 0.0 ) alpha = 0; - this.alpha = alpha; this.geomFactory = geom.getFactory(); + if ( alpha < 0.0 ) alpha = 0; + this.alpha = alpha; } /** - * Sets a skew factor influencing the shape of the curve corners. - * 0 is no skew, positive skews towards longer edges, negative skews towards shorter. + * Creates a new instance producing a Bezier curve defined by a geometry, + * an alpha curvedness value, and a skew factor. * - * @param skewFactor the skew factor + * @param geom geometry defining curve + * @param alpha curvedness parameter (0 is linear, 1 is round, >1 is increasingly curved) + * @param skew the skew parameter (0 is none, positive skews towards longer side, negative towards shorter */ - public void setSkew(double skewFactor) { - this.skewFactor = skewFactor; + CubicBezierCurve(Geometry geom, double alpha, double skew) { + this.inputGeom = geom; + this.geomFactory = geom.getFactory(); + if ( alpha < 0.0 ) alpha = 0; + this.alpha = alpha; + this.skew = skew; + } + + /** + * Creates a new instance producing a Bezier curve defined by a geometry, + * and a list (or lists) of control points. + *

      + * Typically the control point geometry + * is a {@link LineString} or {@link MultiLineString} + * containing an element for each line or ring in the input geometry. + * The list of control points for each linear element must contain two + * vertices for each segment (and thus 2 * npts - 2). + * + * @param geom geometry defining curve + * @param controlPoints the geometry containing the control points + */ + CubicBezierCurve(Geometry geom, Geometry controlPoints) { + this.inputGeom = geom; + this.geomFactory = geom.getFactory(); + this.controlPoints = controlPoints; } /** - * Gets the computed Bezier curve geometry. + * Gets the computed linearized Bezier curve geometry. * - * @return the curved geometry + * @return a linearized curved geometry */ public Geometry getResult() { bezierCurvePts = new Coordinate[numVerticesPerSegment]; @@ -152,7 +202,7 @@ private Polygon bezierPolygon(Polygon poly) { } private CoordinateList bezierCurve(Coordinate[] coords, boolean isRing) { - Coordinate[] control = controlPoints(coords, false, alpha, skewFactor); + Coordinate[] control = controlPoints(coords, isRing); CoordinateList curvePts = new CoordinateList(); for (int i = 0; i < coords.length - 1; i++) { int ctrlIndex = 2 * i; @@ -161,6 +211,27 @@ private CoordinateList bezierCurve(Coordinate[] coords, boolean isRing) { return curvePts; } + private Coordinate[] controlPoints(Coordinate[] coords, boolean isRing) { + if (controlPoints != null) { + if (controlPointIndex >= controlPoints.getNumGeometries()) { + throw new IllegalArgumentException("Too few control point elements"); + } + Geometry ctrlPtsGeom = controlPoints.getGeometryN(controlPointIndex++); + Coordinate[] ctrlPts = ctrlPtsGeom.getCoordinates(); + + int expectedNum1 = 2 * coords.length - 2; + int expectedNum2 = isRing ? coords.length - 1 : coords.length; + if (expectedNum1 != ctrlPts.length && expectedNum2 != ctrlPts.length) { + throw new IllegalArgumentException( + String.format("Wrong number of control points for element %d - expected %d or %d, found %d", + controlPointIndex-1, expectedNum1, expectedNum2, ctrlPts.length + )); + } + return ctrlPts; + } + return controlPoints(coords, isRing, alpha, skew); + } + private void addCurve(Coordinate p0, Coordinate p1, Coordinate ctrl0, Coordinate crtl1, CoordinateList curvePts) { diff --git a/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java b/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java index 6490aa5c10..1eb08ec5b6 100644 --- a/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/shape/CubicBezierCurveTest.java @@ -36,25 +36,39 @@ public CubicBezierCurveTest(String name) super(name); } - public void testSquare() { + public void testAlphaSquare() { checkCurve("POLYGON ((40 60, 60 60, 60 40, 40 40, 40 60))", 1, "POLYGON ((40 60, 41.1 61, 42.3 61.8, 43.6 62.5, 45 63.1, 46.4 63.5, 47.8 63.8, 49.3 64, 50.7 64, 52.2 63.8, 53.6 63.5, 55 63.1, 56.4 62.5, 57.7 61.8, 58.9 61, 60 60, 61 58.9, 61.8 57.7, 62.5 56.4, 63.1 55, 63.5 53.6, 63.8 52.2, 64 50.7, 64 49.3, 63.8 47.8, 63.5 46.4, 63.1 45, 62.5 43.6, 61.8 42.3, 61 41.1, 60 40, 58.9 39, 57.7 38.2, 56.4 37.5, 55 36.9, 53.6 36.5, 52.2 36.2, 50.7 36, 49.3 36, 47.8 36.2, 46.4 36.5, 45 36.9, 43.6 37.5, 42.3 38.2, 41.1 39, 40 40, 39 41.1, 38.2 42.3, 37.5 43.6, 36.9 45, 36.5 46.4, 36.2 47.8, 36 49.3, 36 50.7, 36.2 52.2, 36.5 53.6, 36.9 55, 37.5 56.4, 38.2 57.7, 39 58.9, 40 60))"); } - public void testRightAngle() { + public void testAlphaRightAngle() { checkCurve("LINESTRING (30 40, 40 50, 50 40)", 1, "LINESTRING (30 40, 30.1 41.1, 30.2 42.1, 30.5 43.1, 30.9 44, 31.4 44.9, 32 45.8, 32.7 46.6, 33.4 47.3, 34.2 48, 35.1 48.6, 36 49.1, 36.9 49.5, 37.9 49.8, 38.9 49.9, 40 50, 41.1 49.9, 42.1 49.8, 43.1 49.5, 44 49.1, 44.9 48.6, 45.8 48, 46.6 47.3, 47.3 46.6, 48 45.8, 48.6 44.9, 49.1 44, 49.5 43.1, 49.8 42.1, 49.9 41.1, 50 40)"); } - public void testRightZigzag() { + public void testAlphaRightZigzag() { checkCurve("LINESTRING (10 10, 20 19, 30 10, 40 20)", 1, "LINESTRING (10 10, 10.2 11, 10.4 11.9, 10.8 12.9, 11.2 13.7, 11.7 14.6, 12.3 15.3, 13 16, 13.7 16.7, 14.5 17.3, 15.3 17.8, 16.2 18.2, 17.1 18.5, 18 18.8, 19 18.9, 20 19, 20.9 18.9, 21.8 18.6, 22.5 18.1, 23.1 17.4, 23.7 16.6, 24.2 15.8, 24.8 14.9, 25.2 14, 25.8 13.1, 26.3 12.3, 26.9 11.5, 27.5 10.9, 28.2 10.4, 29.1 10.1, 30 10, 31 10.1, 32 10.3, 33 10.6, 33.9 11, 34.8 11.5, 35.7 12.1, 36.5 12.8, 37.2 13.5, 37.9 14.3, 38.5 15.2, 39 16.1, 39.4 17, 39.7 18, 39.9 19, 40 20)"); } + public void testCtrlRightZigzag() { + checkCurve("LINESTRING (10 10, 20 20, 30 10, 40 20)", + "LINESTRING (10 15, 15 20, 25 20, 28 10, 32 10, 40 25)", + "LINESTRING (10 10, 10.1 11, 10.3 12, 10.6 13, 11 13.9, 11.5 14.8, 12.1 15.7, 12.8 16.5, 13.5 17.2, 14.3 17.9, 15.2 18.5, 16.1 19, 17 19.4, 18 19.7, 19 19.9, 20 20, 21 19.9, 21.9 19.5, 22.8 19, 23.6 18.2, 24.4 17.4, 25.1 16.5, 25.8 15.5, 26.4 14.5, 27.1 13.5, 27.6 12.6, 28.2 11.8, 28.7 11, 29.1 10.5, 29.6 10.1, 30 10, 30.5 10.2, 31.1 10.7, 31.8 11.5, 32.6 12.5, 33.5 13.7, 34.4 15, 35.3 16.2, 36.2 17.5, 37.1 18.6, 37.9 19.6, 38.6 20.4, 39.2 20.9, 39.6 21, 39.9 20.7, 40 20)"); + } + private void checkCurve(String wkt, double alpha, String wktExpected) { Geometry geom = read(wkt); Geometry actual = CubicBezierCurve.bezierCurve(geom, alpha); Geometry expected = read(wktExpected); checkEqual(expected, actual, 0.5); } + + private void checkCurve(String wkt, String wktCtrl, String wktExpected) { + Geometry geom = read(wkt); + Geometry ctrl = read(wktCtrl); + Geometry actual = CubicBezierCurve.bezierCurve(geom, ctrl); + Geometry expected = read(wktExpected); + checkEqual(expected, actual, 0.5); + } } From 3952526ed2847c817b883ea74178e513fd6c46b0 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 16 Feb 2022 10:27:41 -0800 Subject: [PATCH 196/275] Fix RelateOp for a snapped line boundary point (#839) * Fix RelateOp handling of a snapped line endpoint Also remove obsolete code Signed-off-by: Martin Davis --- .../jts/geomgraph/GeometryGraph.java | 18 +------- .../geomgraph/index/SegmentIntersector.java | 30 ++++++------- .../jts/operation/relate/RelateTest.java | 43 ++++++++++++++----- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java index 6fc618162b..6e3f824ef9 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/GeometryGraph.java @@ -340,20 +340,6 @@ public void addPoint(Coordinate pt) { insertPoint(argIndex, pt, Location.INTERIOR); } - - /** - * Compute self-nodes, taking advantage of the Geometry type to - * minimize the number of intersection tests. (E.g. rings are - * not tested for self-intersection, since they are assumed to be valid). - * - * @param li the LineIntersector to use - * @param computeRingSelfNodes if false, intersection checks are optimized to not test rings for self-intersection - * @return the computed SegmentIntersector containing information about the intersections found - */ - public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes) - { - return computeSelfNodes(li, computeRingSelfNodes, false); - } /** * Compute self-nodes, taking advantage of the Geometry type to @@ -362,13 +348,11 @@ public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRi * * @param li the LineIntersector to use * @param computeRingSelfNodes if false, intersection checks are optimized to not test rings for self-intersection - * @param isDoneIfProperInt short-circuit the intersection computation if a proper intersection is found * @return the computed SegmentIntersector containing information about the intersections found */ - public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes, boolean isDoneIfProperInt) + public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes) { SegmentIntersector si = new SegmentIntersector(li, true, false); - si.setIsDoneIfProperInt(isDoneIfProperInt); EdgeSetIntersector esi = createEdgeSetIntersector(); // optimize intersection search for valid Polygons and LinearRings boolean isRings = parentGeom instanceof LinearRing diff --git a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java index 3794a0c50c..9fe329f864 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java +++ b/modules/core/src/main/java/org/locationtech/jts/geomgraph/index/SegmentIntersector.java @@ -55,9 +55,6 @@ public static boolean isAdjacentSegments(int i1, int i2) public int numTests = 0; private Collection[] bdyNodes; - private boolean isDone = false; - private boolean isDoneWhenProperInt = false; - public SegmentIntersector(LineIntersector li, boolean includeProper, boolean recordIsolated) { @@ -73,13 +70,9 @@ public void setBoundaryNodes( Collection bdyNodes0, bdyNodes[0] = bdyNodes0; bdyNodes[1] = bdyNodes1; } - - public void setIsDoneIfProperInt(boolean isDoneWhenProperInt) { - this.isDoneWhenProperInt = isDoneWhenProperInt; - } public boolean isDone() { - return isDone; + return false; } /** * @return the proper intersection point, or null if none was found @@ -172,22 +165,27 @@ public void addIntersections( // only intersection. if (! isTrivialIntersection(e0, segIndex0, e1, segIndex1)) { hasIntersection = true; - if (includeProper || ! li.isProper() ) { -//Debug.println(li); + /** + * In certain cases two line segments test as having a proper intersection + * via the robust orientation check, but due to roundoff + * the computed intersection point is equal to an endpoint. + * If the endpoint is a boundary point + * the computed point must be included as a node. + * If it is not a boundary point the intersection + * is recorded as properInterior by logic below. + */ + boolean isBoundaryPt = isBoundaryPoint(li, bdyNodes); + boolean isNotProper = ! li.isProper() || isBoundaryPt; + if (includeProper || isNotProper ) { e0.addIntersections(li, segIndex0, 0); e1.addIntersections(li, segIndex1, 1); } if (li.isProper()) { properIntersectionPoint = li.getIntersection(0).copy(); hasProper = true; - if (isDoneWhenProperInt) { - isDone = true; - } - if (! isBoundaryPoint(li, bdyNodes)) + if (! isBoundaryPt) hasProperInterior = true; } - //if (li.isCollinear()) - //hasCollinear = true; } } } diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/relate/RelateTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/relate/RelateTest.java index 75f5e2db48..d3f8fee011 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/relate/RelateTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/relate/RelateTest.java @@ -20,6 +20,7 @@ import junit.framework.TestCase; import junit.textui.TestRunner; +import test.jts.GeometryTestCase; /** @@ -29,15 +30,12 @@ * @version 1.7 */ public class RelateTest - extends TestCase + extends GeometryTestCase { public static void main(String args[]) { TestRunner.run(RelateTest.class); } - private GeometryFactory fact = new GeometryFactory(); - private WKTReader rdr = new WKTReader(fact); - public RelateTest(String name) { super(name); @@ -48,11 +46,8 @@ public RelateTest(String name) * * The cause is that the longer line nodes the single-segment line. * The node then tests as not lying precisely on the original longer line. - * - * @throws Exception */ public void testContainsIncorrectIMMatrix() - throws Exception { String a = "LINESTRING (1 0, 0 2, 0 0, 2 2)"; String b = "LINESTRING (0 0, 2 2)"; @@ -62,11 +57,39 @@ public void testContainsIncorrectIMMatrix() runRelateTest(a, b, "001F001F2" ); } + /** + * Tests case where segments intersect properly, but computed intersection point + * snaps to a boundary endpoint due to roundoff. + * Fixed by detecting that computed intersection snapped to a boundary node. + * + * See https://lists.osgeo.org/pipermail/postgis-users/2022-February/045266.html + */ + public void testIntersectsSnappedEndpoint1() + { + String a = "LINESTRING (-29796.696826656284 138522.76848210802, -29804.3911369969 138519.3504205817)"; + String b = "LINESTRING (-29802.795222153436 138520.05937757515, -29802.23305474065 138518.7938969792)"; + runRelateTest(a, b, "F01FF0102" ); + } + + /** + * Tests case where segments intersect properly, but computed intersection point + * snaps to a boundary endpoint due to roundoff. + * Fixed by detecting that computed intersection snapped to a boundary node. + * + * See https://lists.osgeo.org/pipermail/postgis-users/2022-February/045277.html + */ + public void testIntersectsSnappedEndpoint2() + { + String a = "LINESTRING (-57.2681216 49.4063466, -57.267725199999994 49.406617499999996, -57.26747895046037 49.406750916517765)"; + String b = "LINESTRING (-57.267475399999995 49.4067465, -57.2675701 49.406864299999995, -57.267989 49.407135399999994)"; + runRelateTest(a, b, "FF10F0102" ); + } + + void runRelateTest(String wkt1, String wkt2, String expectedIM) - throws ParseException { - Geometry g1 = rdr.read(wkt1); - Geometry g2 = rdr.read(wkt2); + Geometry g1 = read(wkt1); + Geometry g2 = read(wkt2); IntersectionMatrix im = RelateOp.relate(g1, g2); String imStr = im.toString(); //System.out.println(imStr); From 8b7521df1bfc4c813eddfe779450463ee25bb9e5 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 16 Feb 2022 10:29:26 -0800 Subject: [PATCH 197/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 98a98a3561..1586ee4443 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -48,6 +48,7 @@ Distributions for older JTS versions can be obtained at the * Add OverlayNG result area heuristic check (#812) * Fix the buffer generated for Mitred Joins (#818) * Fix WKTReader to produce correct XY coordinate dimension for POLYGON EMPTY (#828) +* Fix RelateOp for a snapped line boundary point (#839) # Version 1.18.2 From 50824cff20489db186867255028103a415e63626 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 11:09:25 -0800 Subject: [PATCH 198/275] Update JTSTestBuilder.md --- doc/JTSTestBuilder.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/JTSTestBuilder.md b/doc/JTSTestBuilder.md index 7cdae8dcc6..cfb4c5f51b 100644 --- a/doc/JTSTestBuilder.md +++ b/doc/JTSTestBuilder.md @@ -2,10 +2,10 @@ The TestBuilder is a GUI application which allows creating, editing and visualizing geometries, and executing JTS functions on them. -* Run (from project root): +* Run (from project root), with larger memory: - java -jar modules/app/target/JTSTestBuilder.jar + java -jar modules/app/target/JTSTestBuilder.jar -Xmx2000M * Run (with Metal L&F - useful on MacOS) - java -Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel -jar modules/app/target/JTSTestBuilder.jar + java -Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel -jar modules/app/target/JTSTestBuilder.jar -Xmx2000M From 1926e6c6303e653243b321589d78d812e67110d0 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 11:11:33 -0800 Subject: [PATCH 199/275] Update JTSTestBuilder.md --- doc/JTSTestBuilder.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/JTSTestBuilder.md b/doc/JTSTestBuilder.md index cfb4c5f51b..03041ab7ed 100644 --- a/doc/JTSTestBuilder.md +++ b/doc/JTSTestBuilder.md @@ -9,3 +9,5 @@ The TestBuilder is a GUI application which allows creating, editing and visualiz * Run (with Metal L&F - useful on MacOS) java -Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel -jar modules/app/target/JTSTestBuilder.jar -Xmx2000M + +![](JTSTestuilder.png) From 144dd17631868cf1ffd624adb9bc811febf97da4 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 11:12:03 -0800 Subject: [PATCH 200/275] Add files via upload --- doc/JTSTestBuilder.png | Bin 0 -> 111998 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/JTSTestBuilder.png diff --git a/doc/JTSTestBuilder.png b/doc/JTSTestBuilder.png new file mode 100644 index 0000000000000000000000000000000000000000..93844119544fb1bca9dc231f55ac19a156ae3576 GIT binary patch literal 111998 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 z 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 z z1F`K4$fKX%d?%miP&c`OU*^x~+JFtQbRJ9{p#s%#Pq_Ja$!e*5!gg}>Jwo&Zf6EHo z91nJ)NY>#TVzGc4_JVysUWjtTS3ZI4+c)2+7TNkk8m~WhR22GONLviY{Dpyxi!J&4 zo6D0f`|elQwTF?p10|Jq7_WMB6J-#3u?bU(P+({KMIOrkq6V~Ncp`mLvv9t{#vle; zBfIz0$3LGx@>O{2`b^&c)!fFKof)Oy;utnrzP+cXKwh+=Tm2ZW6Q=Im+P5H1Y z(5Ggh^5#+ZK@R6&ZsCY3Z1i|AmN-jrw7loC#zi&eLxyR@>)u_NBqLu%j_yjA%G^3| zb_QJ8Az)XD5OkeoM$4smQj2!gAJX$&etDBod1HKS!RDle;c}%emE+n0uG}igz#(k4 z{GljfyN<(%2=YNUw-6U1a?W$){x${A)noeUQ{$roQTiv652t@0a;uCLcTexO-)RxT

      )-TK-m-pMdvwjJTbGue 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

      Q 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 literal 0 HcmV?d00001 From c6c83a81c89188c45d788a99b53da7c65e6b1387 Mon Sep 17 00:00:00 2001 From: Danish Nawab <37218067+danishnawab@users.noreply.github.com> Date: Fri, 18 Feb 2022 06:33:21 +0100 Subject: [PATCH 201/275] add support for GeoJSON's Feature and FeatureCollection types (#837) --- .../jts/io/geojson/GeoJsonConstants.java | 4 ++ .../jts/io/geojson/GeoJsonReader.java | 36 ++++++++++++++++ .../jts/io/geojson/GeoJsonReaderTest.java | 43 +++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonConstants.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonConstants.java index aa5b632c01..03e62a118d 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonConstants.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonConstants.java @@ -19,6 +19,8 @@ */ public class GeoJsonConstants { + public static final String NAME_GEOMETRY = "geometry"; + public static final String NAME_FEATURES = "features"; public static final String NAME_GEOMETRIES = "geometries"; public static final String NAME_CRS = "crs"; public static final String NAME_PROPERTIES = "properties"; @@ -32,5 +34,7 @@ public class GeoJsonConstants { public static final String NAME_MULTIPOLYGON = "MultiPolygon"; public static final String NAME_MULTILINESTRING = "MultiLineString"; public static final String NAME_MULTIPOINT = "MultiPoint"; + public static final String NAME_FEATURE = "Feature"; + public static final String NAME_FEATURECOLLECTION = "FeatureCollection"; } diff --git a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonReader.java b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonReader.java index c87c1a18e7..05d9acbc92 100644 --- a/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonReader.java +++ b/modules/io/common/src/main/java/org/locationtech/jts/io/geojson/GeoJsonReader.java @@ -167,6 +167,12 @@ private Geometry create(Map geometryMap, } else if (GeoJsonConstants.NAME_GEOMETRYCOLLECTION.equals(type)) { result = createGeometryCollection(geometryMap, geometryFactory); + } else if (GeoJsonConstants.NAME_FEATURE.equals(type)) { + result = createFeature(geometryMap, geometryFactory); + + } else if (GeoJsonConstants.NAME_FEATURECOLLECTION.equals(type)) { + result = createFeatureCollection(geometryMap, geometryFactory); + } else { throw new ParseException( "Could not parse Geometry from GeoJson string. Unsupported 'type':" @@ -177,6 +183,36 @@ private Geometry create(Map geometryMap, return result; } + private Geometry createFeatureCollection(Map geometryMap, + GeometryFactory geometryFactory) throws ParseException { + try { + @SuppressWarnings("unchecked") + List> features = (List>) geometryMap.get(GeoJsonConstants.NAME_FEATURES); + + Geometry[] geometries = new Geometry[features.size()]; + int i = 0; + for (Map featureMap : features) { + geometries[i] = createFeature(featureMap, geometryFactory); + ++i; + } + + return geometryFactory.createGeometryCollection(geometries); + } catch (RuntimeException e) { + throw new ParseException("Could not parse FeatureCollection from GeoJson string.", e); + } + } + + private Geometry createFeature(Map geometryMap, + GeometryFactory geometryFactory) throws ParseException { + try { + @SuppressWarnings("unchecked") + Map innerGeometryMap = (Map) geometryMap.get(GeoJsonConstants.NAME_GEOMETRY); + return create(innerGeometryMap, geometryFactory); + } catch (RuntimeException e) { + throw new ParseException("Could not parse Feature from GeoJson string.", e); + } + } + private Geometry createGeometryCollection(Map geometryMap, GeometryFactory geometryFactory) throws ParseException { diff --git a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonReaderTest.java b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonReaderTest.java index f90fcaa05c..889d8e9c48 100644 --- a/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonReaderTest.java +++ b/modules/io/common/src/test/java/org/locationtech/jts/io/geojson/GeoJsonReaderTest.java @@ -12,11 +12,17 @@ package org.locationtech.jts.io.geojson; +import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.io.ParseException; import test.jts.GeometryTestCase; +import java.util.Arrays; + public class GeoJsonReaderTest extends GeometryTestCase { private GeoJsonReader geoJsonRdr; @@ -62,6 +68,43 @@ public void testNullCoordinatesPolygon() throws ParseException { runTest("{\"type\":\"Polygon\",\"coordinates\":null}", "POLYGON EMPTY"); } + public void testEmptyFeatureCollection() throws ParseException { + runTest("{ \"type\": \"FeatureCollection\", \"features\": [] }", "GEOMETRYCOLLECTION EMPTY"); + } + + public void testFeatureCollection() throws ParseException { + final String featureCollectionTemplate = "{ \"type\": \"FeatureCollection\", \"features\": [ %s, %s ] }"; + String polygonFeature = "{ \"type\": \"Feature\", \"geometry\": { \"type\": \"Polygon\", \"coordinates\": [ [ [ 10, 20, 0 ], [ 11, 21, 0 ], [ 10, 20, 0 ] ] ] }, \"properties\": { \"name\": \"Some polygonGeometry property\" } }"; + String pointFeature = "{ \"type\": \"Feature\", \"geometry\": { \"type\": \"Point\", \"coordinates\": [ 12, 13, 1 ] }, \"properties\": { \"name\": \"Some point property\" } }"; + final String featureCollection = String.format(featureCollectionTemplate, polygonFeature, pointFeature); + + final Geometry geometryCollection = geoJsonRdr.read(featureCollection); + assertEquals(GeometryCollection.TYPENAME_GEOMETRYCOLLECTION, geometryCollection.getGeometryType()); + assertEquals(2, geometryCollection.getNumGeometries()); + + final Geometry polygon = geometryCollection.getGeometryN(0); + assertEquals(GeometryCollection.TYPENAME_POLYGON, polygon.getGeometryType()); + assertEquals(Polygon.class, polygon.getClass()); + assertEquals(1, polygon.getNumGeometries()); + final Coordinate[] polygonCoordinates = polygon.getCoordinates(); + assertEquals(3, polygonCoordinates.length); + final Coordinate[] expectedPolygonCoordinates = { + new Coordinate(10, 20, 0), + new Coordinate(11, 21, 0), + new Coordinate(10, 20, 0) + }; + assertTrue(Arrays.equals(expectedPolygonCoordinates, polygonCoordinates)); + + final Geometry point = geometryCollection.getGeometryN(1); + assertEquals(GeometryCollection.TYPENAME_POINT, point.getGeometryType()); + assertEquals(Point.class, point.getClass()); + assertEquals(1, point.getNumGeometries()); + final Coordinate[] pointCoordinates = point.getCoordinates(); + assertEquals(1, pointCoordinates.length); + final Coordinate[] expectedPointCoordinates = {new Coordinate(12, 13, 1)}; + assertTrue(Arrays.equals(expectedPointCoordinates, pointCoordinates)); + } + private void runParseEx(String json) { try { Geometry geom = geoJsonRdr.read(json); From 8dbb0744457f81ed157aa6cf06aa119655815ad6 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 21:34:10 -0800 Subject: [PATCH 202/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 1586ee4443..c0ef3c288f 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -35,6 +35,7 @@ Distributions for older JTS versions can be obtained at the * Allow specifying a fixed `PrecisionModel` via grid size (#804) * Add `OffsetCurve` class (#810, #816) * Add `ConcaveHull` class for points (#823, #829) +* Add support for GeoJSON Feature and FeatureCollection types (#837) ### Bug Fixes From cac1a446992c67977b28d73f6f0c51d76c2d77ec Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Fri, 18 Feb 2022 06:34:46 +0100 Subject: [PATCH 203/275] Interpolate Z values in Densifier class (#835) --- .../org/locationtech/jts/densify/Densifier.java | 5 ++++- .../org/locationtech/jts/densify/DensifierTest.java | 13 ++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/densify/Densifier.java b/modules/core/src/main/java/org/locationtech/jts/densify/Densifier.java index d36845c072..47b91759cf 100644 --- a/modules/core/src/main/java/org/locationtech/jts/densify/Densifier.java +++ b/modules/core/src/main/java/org/locationtech/jts/densify/Densifier.java @@ -83,7 +83,10 @@ private static Coordinate[] densifyPoints(Coordinate[] pts, for (int j = 1; j < densifiedSegCount; j++) { double segFract = (j * densifiedSegLen) / len; Coordinate p = seg.pointAlong(segFract); - precModel.makePrecise(p); + if(!Double.isNaN(seg.p0.z) && !Double.isNaN(seg.p1.z)) { + p.setZ(seg.p0.z + segFract * (seg.p1.z - seg.p0.z)); + } + precModel.makePrecise(p); coordList.add(p, false); } } diff --git a/modules/core/src/test/java/org/locationtech/jts/densify/DensifierTest.java b/modules/core/src/test/java/org/locationtech/jts/densify/DensifierTest.java index a14d28edcb..dbf8a7770a 100644 --- a/modules/core/src/test/java/org/locationtech/jts/densify/DensifierTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/densify/DensifierTest.java @@ -68,6 +68,11 @@ public void testBoxNoValidate() { checkDensifyNoValidate("POLYGON ((10 30, 30 30, 30 10, 10 10, 10 30))", 10, "POLYGON ((10 10, 10 20, 10 30, 20 30, 30 30, 30 20, 30 10, 20 10, 10 10))"); } + + public void testLineDensify3D() { + checkDensifyXYZ("POLYGON Z((10 30 10, 30 30 10, 30 10 15, 10 10 10, 10 30 20))", + 10, "POLYGON Z((10 30 10, 20 30 10, 30 30 10, 30 20 12.5, 30 10 15, 20 10 12.5, 10 10 10, 10 20 15, 10 30 20))"); + } public void testDimension2d() { GeometryFactory gf = new GeometryFactory(); @@ -96,7 +101,13 @@ private void checkDensify(String wkt, double distanceTolerance, String wktExpect Geometry actual = Densifier.densify(geom, distanceTolerance); checkEqual(expected, actual, TOLERANCE); } - + + private void checkDensifyXYZ(String wkt, double distanceTolerance, String wktExpected) { + Geometry geom = read(wkt); + Geometry expected = read(wktExpected); + Geometry actual = Densifier.densify(geom, distanceTolerance); + checkEqualXYZ(expected, actual); + } /** * Note: it's hard to construct a geometry which would actually be invalid when densified. * This test just checks that the code path executes. From 307f4312f0aa0ca4fb618511da96aa482efc4b24 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 21:35:53 -0800 Subject: [PATCH 204/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index c0ef3c288f..89b03f9401 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -35,8 +35,10 @@ Distributions for older JTS versions can be obtained at the * Allow specifying a fixed `PrecisionModel` via grid size (#804) * Add `OffsetCurve` class (#810, #816) * Add `ConcaveHull` class for points (#823, #829) +* Improve `Densifier` to interpolate Z values (#835) * Add support for GeoJSON Feature and FeatureCollection types (#837) + ### Bug Fixes * Fix `WKTReader` geometry typename parsing (#786) From 50983e2a089048750c540f5fddae5d1106b0ddbf Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 17 Feb 2022 21:37:02 -0800 Subject: [PATCH 205/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 89b03f9401..86bc1d494d 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -48,10 +48,10 @@ Distributions for older JTS versions can be obtained at the * Fix various operations to handle XYZM geometry (#795) * Fix `SnapRoundingNoder` to use tolerance in noding (also fixes `GeometryPrecisionReducer`) (#802) * Fix `MaximumInscribedCircle` to avoid infinite-looping on flat collapsed input (#807) -* Add OverlayNG result area heuristic check (#812) -* Fix the buffer generated for Mitred Joins (#818) -* Fix WKTReader to produce correct XY coordinate dimension for POLYGON EMPTY (#828) -* Fix RelateOp for a snapped line boundary point (#839) +* Add `OverlayNG` result area heuristic check (#812) +* Fix the buffers generated for mitred joins (#818) +* Fix `WKTReader` to produce correct XY coordinate dimension for POLYGON EMPTY (#828) +* Fix `RelateOp` for a snapped line boundary point (#839) # Version 1.18.2 From 5f8607f530337ba39d10d7862ebfceb5f66fd99f Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 26 Feb 2022 11:40:56 -0800 Subject: [PATCH 206/275] Fix IsValidOp for repeated points (#845) * Fix IsValidOp for repeated points Signed-off-by: Martin Davis --- .../valid/IndexedNestedHoleTester.java | 12 +- .../valid/IndexedNestedPolygonTester.java | 34 +++-- .../jts/operation/valid/IsValidOp.java | 9 +- .../valid/PolygonTopologyAnalyzer.java | 137 +++++++++++++----- .../jts/operation/valid/IsValidTest.java | 21 +++ .../resources/testxml/general/TestValid.xml | 48 ++++++ 6 files changed, 196 insertions(+), 65 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java index c8d4773385..6806e19fc8 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedHoleTester.java @@ -88,15 +88,9 @@ public boolean isNested() if (! testHole.getEnvelopeInternal().covers( hole.getEnvelopeInternal()) ) continue; - /** - * Checks nesting via a point-in-polygon test, - * or if the point lies on the boundary via - * the topology of the incident edges. - */ - Coordinate holePt0 = hole.getCoordinateN(0); - Coordinate holePt1 = hole.getCoordinateN(1); - if (PolygonTopologyAnalyzer.isSegmentInRing(holePt0, holePt1, testHole)) { - nestedPt = holePt0; + if (PolygonTopologyAnalyzer.isRingNested(hole, testHole)) { + //TODO: find a hole point known to be inside + nestedPt = hole.getCoordinateN(0); return true; } } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java index cce97b212a..7435aa5455 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IndexedNestedPolygonTester.java @@ -25,11 +25,12 @@ /** * Tests whether a MultiPolygon has any element polygon - * nested inside another polygon, using a spatial + * improperly nested inside another polygon, using a spatial * index to speed up the comparisons. *

      - * The logic assumes that the polygons do not overlap and have no collinear segments - * (so they are properly nested, and there are no duplicate rings). + * The logic assumes that the polygons do not overlap and have no collinear segments. + * So the polygon rings may touch at discrete points, + * but they are properly nested, and there are no duplicate rings. */ class IndexedNestedPolygonTester { @@ -75,7 +76,7 @@ private IndexedPointInAreaLocator getLocator(int polyIndex) { public Coordinate getNestedPoint() { return nestedPt; } /** - * Tests if any polygon is nested (contained) within another polygon. + * Tests if any polygon is improperly nested (contained) within another polygon. * This is invalid. * The nested point will be set to reflect this. * @return true if some polygon is nested @@ -106,6 +107,14 @@ public boolean isNested() return false; } + /** + * Finds an improperly nested point, if one exists. + * + * @param shell the test polygon shell + * @param possibleOuterPoly a polygon which may contain it + * @param locator the locator for the outer polygon + * @return a nested point, if one exists, or null + */ private Coordinate findNestedPoint(LinearRing shell, Polygon possibleOuterPoly, IndexedPointInAreaLocator locator) { @@ -119,7 +128,7 @@ private Coordinate findNestedPoint(LinearRing shell, return shellPt0; } - Coordinate shellPt1 = shell.getCoordinateN(0); + Coordinate shellPt1 = shell.getCoordinateN(1); int loc1 = locator.locate(shellPt1); if (loc1 == Location.EXTERIOR) return null; if (loc1 == Location.INTERIOR) { @@ -131,27 +140,24 @@ private Coordinate findNestedPoint(LinearRing shell, * the polygon. * Nesting can be checked via the topology of the incident edges. */ - return findSegmentInPolygon(shell, possibleOuterPoly); + return findIncidentSegmentNestedPoint(shell, possibleOuterPoly); } /** * Finds a point of a shell segment which lies inside a polygon, if any. - * The shell is assume to touch the polyon only at shell vertices, + * The shell is assumed to touch the polygon only at shell vertices, * and does not cross the polygon. * * @param shell the shell to test * @param poly the polygon to test against * @return an interior segment point, or null if the shell is nested correctly */ - private static Coordinate findSegmentInPolygon(LinearRing shell, Polygon poly) + private static Coordinate findIncidentSegmentNestedPoint(LinearRing shell, Polygon poly) { LinearRing polyShell = poly.getExteriorRing(); if (polyShell.isEmpty()) return null; - Coordinate shell0 = shell.getCoordinateN(0); - Coordinate shell1 = shell.getCoordinateN(1); - - if (! PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, polyShell)) + if (! PolygonTopologyAnalyzer.isRingNested(shell, polyShell)) return null; /** @@ -161,7 +167,7 @@ private static Coordinate findSegmentInPolygon(LinearRing shell, Polygon poly) for (int i = 0; i < poly.getNumInteriorRing(); i++) { LinearRing hole = poly.getInteriorRingN(i); if (hole.getEnvelopeInternal().covers(shell.getEnvelopeInternal()) - && PolygonTopologyAnalyzer.isSegmentInRing(shell0, shell1, hole)) { + && PolygonTopologyAnalyzer.isRingNested(shell, hole)) { return null; } } @@ -170,6 +176,6 @@ private static Coordinate findSegmentInPolygon(LinearRing shell, Polygon poly) * The shell is contained in the polygon, but is not contained in a hole. * This is invalid. */ - return shell0; + return shell.getCoordinateN(0); } } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 5dee0eefff..8b69c4f297 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -471,15 +471,16 @@ private void checkHolesOutsideShell(Polygon poly) */ private Coordinate findHoleOutsideShellPoint(LinearRing hole, LinearRing shell) { Coordinate holePt0 = hole.getCoordinateN(0); - Coordinate holePt1 = hole.getCoordinateN(1); /** * If hole envelope is not covered by shell, it must be outside */ if (! shell.getEnvelopeInternal().covers( hole.getEnvelopeInternal() )) - return holePt0; + //TODO: find hole pt outside shell env + return holePt0; - if (PolygonTopologyAnalyzer.isSegmentInRing(holePt0, holePt1, shell)) - return null; + if (PolygonTopologyAnalyzer.isRingNested(hole, shell)) + return null; + //TODO: find hole point outside shell return holePt0; } diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java index 32149af73c..2d632bbfe1 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java @@ -43,45 +43,47 @@ class PolygonTopologyAnalyzer { /** - * Finds a self-intersection (if any) in a {@link LinearRing}. - * - * @param ring the ring to analyze - * @return a self-intersection point if one exists, or null - */ - public static Coordinate findSelfIntersection(LinearRing ring) { - PolygonTopologyAnalyzer ata = new PolygonTopologyAnalyzer(ring, false); - if (ata.hasInvalidIntersection()) - return ata.getInvalidLocation(); - return null; - } - - /** - * Tests whether a segment p0-p1 is inside or outside a ring. + * Tests whether a ring is nested inside another ring. *

      * Preconditions: *

        - *
      • The segment intersects the ring only at the endpoints - *
      • One, none or both of the segment endpoints may lie on the ring - *
      • The ring does not self-cross, but it may self-touch + *
      • The rings do not cross (i.e. the test is wholly inside or outside the target) + *
      • The rings may touch at discrete points only + *
      • The target ring does not self-cross, but it may self-touch *
      + * If the test ring start point is properly inside or outside, that provides the result. + * Otherwise the start point is on the target ring, + * and the incident start segment (accounting for repeated points) is + * tested for its topology relative to the target ring. * - * @param p0 a segment vertex - * @param p1 a segment vertex - * @param ring the ring to test - * @return true if the segment lies inside the ring + * @param test the ring to test + * @param target the ring to test against + * @return true if the test ring lies inside the target ring */ - public static boolean isSegmentInRing(Coordinate p0, Coordinate p1, LinearRing ring) { - Coordinate[] ringPts = ring.getCoordinates(); - int loc = PointLocation.locateInRing(p0, ringPts); + public static boolean isRingNested(LinearRing test, LinearRing target) { + Coordinate p0 = test.getCoordinateN(0); + Coordinate[] targetPts = target.getCoordinates(); + int loc = PointLocation.locateInRing(p0, targetPts); if (loc == Location.EXTERIOR) return false; if (loc == Location.INTERIOR) return true; /** - * The segment point is on the boundary of the ring. + * The start point is on the boundary of the ring. * Use the topology at the node to check if the segment * is inside or outside the ring. */ - return isIncidentSegmentInRing(p0, p1, ringPts); + Coordinate p1 = findNonEqualVertex(test, p0); + return isIncidentSegmentInRing(p0, p1, targetPts); + } + + private static Coordinate findNonEqualVertex(LinearRing ring, Coordinate p) { + int i = 1; + Coordinate next = ring.getCoordinateN(i); + while (next.equals2D(p) && i < ring.getNumPoints() - 1) { + i += 1; + next = ring.getCoordinateN(i); + } + return next; } /** @@ -96,21 +98,18 @@ public static boolean isSegmentInRing(Coordinate p0, Coordinate p1, LinearRing r * This works for both shells and holes, but the caller must know * the ring role. * - * @param p0 the first vertex of the segment + * @param p0 the touching vertex of the segment * @param p1 the second vertex of the segment * @param ringPts the points of the ring * @return true if the segment is inside the ring. */ - public static boolean isIncidentSegmentInRing(Coordinate p0, Coordinate p1, Coordinate[] ringPts) { + private static boolean isIncidentSegmentInRing(Coordinate p0, Coordinate p1, Coordinate[] ringPts) { int index = intersectingSegIndex(ringPts, p0); if (index < 0) { throw new IllegalArgumentException("Segment vertex does not intersect ring"); } - Coordinate rPrev = ringPts[index]; - Coordinate rNext = ringPts[index + 1]; - if (p0.equals2D(ringPts[index])) { - rPrev = ringPts[ringIndexPrev(ringPts, index)]; - } + Coordinate rPrev = findRingVertexPrev(ringPts, index, p0); + Coordinate rNext = findRingVertexNext(ringPts, index, p0); /** * If ring orientation is not normalized, flip the corner orientation */ @@ -122,6 +121,61 @@ public static boolean isIncidentSegmentInRing(Coordinate p0, Coordinate p1, Coor } return PolygonNode.isInteriorSegment(p0, rPrev, rNext, p1); } + + /** + * Finds the ring vertex previous to a node point on a ring + * (which is contained in the index'th segment, + * as either the start vertex or an interior point). + * Repeated points are skipped over. + * @param ringPts the ring + * @param index the index of the segment containing the node + * @param node the node point + * + * @return the previous ring vertex + */ + private static Coordinate findRingVertexPrev(Coordinate[] ringPts, int index, Coordinate node) { + int iPrev = index; + Coordinate prev = ringPts[iPrev]; + while (node.equals2D(prev)) { + iPrev = ringIndexPrev(ringPts, iPrev); + prev = ringPts[iPrev]; + } + return prev; + } + + private static int ringIndexPrev(Coordinate[] ringPts, int index) { + int iPrev = index - 1; + if (iPrev < 0) iPrev = ringPts.length - 2; + return iPrev; + } + + /** + * Finds the ring vertex next from a node point on a ring + * (which is contained in the index'th segment, + * as either the start vertex or an interior point). + * Repeated points are skipped over. + * @param ringPts the ring + * @param index the index of the segment containing the node + * @param node the node point + * + * @return the next ring vertex + */ + private static Coordinate findRingVertexNext(Coordinate[] ringPts, int index, Coordinate node) { + //-- safe, since index is always the start of a ring segment + int iNext = index + 1; + Coordinate next = ringPts[iNext]; + while (node.equals2D(next)) { + iNext = ringIndexNext(ringPts, iNext); + next = ringPts[iNext]; + } + return next; + } + + private static int ringIndexNext(Coordinate[] ringPts, int index) { + int iNext = index + 1; + if (iNext > ringPts.length - 2) iNext = 0; + return iNext; + } /** * Computes the index of the segment which intersects a given point. @@ -143,11 +197,18 @@ private static int intersectingSegIndex(Coordinate[] ringPts, Coordinate pt) { } return -1; } - - private static int ringIndexPrev(Coordinate[] ringPts, int index) { - int iPrev = index - 1; - if (index == 0) iPrev = ringPts.length - 2; - return iPrev; + + /** + * Finds a self-intersection (if any) in a {@link LinearRing}. + * + * @param ring the ring to analyze + * @return a self-intersection point if one exists, or null + */ + public static Coordinate findSelfIntersection(LinearRing ring) { + PolygonTopologyAnalyzer ata = new PolygonTopologyAnalyzer(ring, false); + if (ata.hasInvalidIntersection()) + return ata.getInvalidLocation(); + return null; } private boolean isInvertedRingValid; diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java index 5dc904da4e..b73d28ecf1 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/valid/IsValidTest.java @@ -156,6 +156,27 @@ public void testLinearRingSelfCrossing2() { "LINEARRING (0 0, 100 100, 100 0, 0 100, 0 0)"); } + /** + * Tests that repeated points at nodes are handled correctly. + * + * See https://github.com/locationtech/jts/issues/843 + */ + public void testPolygonHoleWithRepeatedShellPointTouch() { + checkValid( "POLYGON ((90 10, 10 10, 50 90, 50 90, 90 10), (50 90, 60 30, 40 30, 50 90))"); + } + + public void testPolygonHoleWithRepeatedShellPointTouchMultiple() { + checkValid( "POLYGON ((90 10, 10 10, 50 90, 50 90, 50 90, 50 90, 90 10), (50 90, 60 30, 40 30, 50 90))"); + } + + public void testPolygonHoleWithRepeatedTouchEndPoint() { + checkValid( "POLYGON ((90 10, 10 10, 50 90, 90 10, 90 10), (90 10, 40 30, 60 50, 90 10))"); + } + + public void testPolygonHoleWithRepeatedHolePointTouch() { + checkValid( "POLYGON ((50 90, 10 10, 90 10, 50 90), (50 90, 50 90, 60 40, 60 40, 40 40, 50 90))"); + } + //============================================= private void checkValid(String wkt) { diff --git a/modules/tests/src/test/resources/testxml/general/TestValid.xml b/modules/tests/src/test/resources/testxml/general/TestValid.xml index 15924a222f..7173219169 100644 --- a/modules/tests/src/test/resources/testxml/general/TestValid.xml +++ b/modules/tests/src/test/resources/testxml/general/TestValid.xml @@ -171,6 +171,54 @@ POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (40 80, 60 80, 50 50, 40 80), (20 true + + A - hole touches shell at repeated shell point (valid) + +POLYGON ((90 10, 10 10, 50 90, 50 90, 90 10), (50 90, 60 30, 40 30, 50 90)) + + true + + + + A - hole touches shell at repeated shell end point (valid) + +POLYGON ((90 10, 10 10, 50 90, 90 10, 90 10), (90 10, 40 30, 60 50, 90 10)) + + true + + + + A - hole touches shell at repeated hole point (valid) + +POLYGON ((50 90, 10 10, 90 10, 50 90), (60 40, 40 40, 50 90, 50 90, 60 40)) + + true + + + + A - hole touches shell at repeated hole end point (valid) + +POLYGON ((50 90, 10 10, 90 10, 50 90), (50 90, 50 90, 60 40, 60 40, 40 40, 50 90)) + + true + + + + A - hole touches hole at repeated point on hole (valid) + +POLYGON ((10 90, 90 90, 90 10, 10 10, 10 90), (70 80, 20 50, 80 20, 40 50, 40 50, 70 80), (40 50, 40 50, 70 40, 70 60, 40 50)) + + true + + + + mA - shell touches shell at repeated point (valid) + +MULTIPOLYGON (((70 80, 20 50, 80 20, 40 50, 40 50, 70 80)), ((40 50, 40 50, 70 40, 70 60, 40 50))) + + true + + mA - shell inside hole, no touch (valid) From d778894e2dc10901afba8fc8b8efe68bde19c21a Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 26 Feb 2022 15:22:39 -0800 Subject: [PATCH 207/275] PolygonTopologyAnalyzer minor code simplification Signed-off-by: Martin Davis --- .../valid/PolygonTopologyAnalyzer.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java index 2d632bbfe1..d3b47e687b 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/PolygonTopologyAnalyzer.java @@ -142,12 +142,6 @@ private static Coordinate findRingVertexPrev(Coordinate[] ringPts, int index, Co } return prev; } - - private static int ringIndexPrev(Coordinate[] ringPts, int index) { - int iPrev = index - 1; - if (iPrev < 0) iPrev = ringPts.length - 2; - return iPrev; - } /** * Finds the ring vertex next from a node point on a ring @@ -171,10 +165,16 @@ private static Coordinate findRingVertexNext(Coordinate[] ringPts, int index, Co return next; } + private static int ringIndexPrev(Coordinate[] ringPts, int index) { + if (index == 0) + return ringPts.length - 2; + return index - 1; + } + private static int ringIndexNext(Coordinate[] ringPts, int index) { - int iNext = index + 1; - if (iNext > ringPts.length - 2) iNext = 0; - return iNext; + if (index >= ringPts.length - 2) + return 0; + return index + 1; } /** From f8ff2660180427a278baa73158f98c52afad12ec Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 26 Feb 2022 15:23:37 -0800 Subject: [PATCH 208/275] Update JTS_Version_History.md --- doc/JTS_Version_History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/JTS_Version_History.md b/doc/JTS_Version_History.md index 86bc1d494d..d61bcfd6f4 100644 --- a/doc/JTS_Version_History.md +++ b/doc/JTS_Version_History.md @@ -52,6 +52,7 @@ Distributions for older JTS versions can be obtained at the * Fix the buffers generated for mitred joins (#818) * Fix `WKTReader` to produce correct XY coordinate dimension for POLYGON EMPTY (#828) * Fix `RelateOp` for a snapped line boundary point (#839) +* Fix IsValidOp for repeated node points (#845) # Version 1.18.2 From 23cb0cefc9f6c9d7cf7b4946c19165aca2e78000 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Sat, 26 Feb 2022 16:40:13 -0800 Subject: [PATCH 209/275] Rename IsValidOp check methods Signed-off-by: Martin Davis --- .../jts/operation/valid/IsValidOp.java | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java index 8b69c4f297..5bd7904316 100644 --- a/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java +++ b/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java @@ -174,7 +174,7 @@ private boolean isValidGeometry(Geometry g) */ private boolean isValid(Point g) { - checkCoordinateInvalid(g.getCoordinates()); + checkCoordinatesValid(g.getCoordinates()); if (hasInvalidError()) return false; return true; } @@ -184,7 +184,7 @@ private boolean isValid(Point g) */ private boolean isValid(MultiPoint g) { - checkCoordinateInvalid(g.getCoordinates()); + checkCoordinatesValid(g.getCoordinates()); if (hasInvalidError()) return false; return true; } @@ -195,9 +195,9 @@ private boolean isValid(MultiPoint g) */ private boolean isValid(LineString g) { - checkCoordinateInvalid(g.getCoordinates()); + checkCoordinatesValid(g.getCoordinates()); if (hasInvalidError()) return false; - checkTooFewPoints(g, MIN_SIZE_LINESTRING); + checkPointSize(g, MIN_SIZE_LINESTRING); if (hasInvalidError()) return false; return true; } @@ -207,16 +207,16 @@ private boolean isValid(LineString g) */ private boolean isValid(LinearRing g) { - checkCoordinateInvalid(g.getCoordinates()); + checkCoordinatesValid(g.getCoordinates()); if (hasInvalidError()) return false; - checkRingNotClosed(g); + checkRingClosed(g); if (hasInvalidError()) return false; - checkRingTooFewPoints(g); + checkRingPointSize(g); if (hasInvalidError()) return false; - checkSelfIntersectingRing(g); + checkRingSimple(g); return validErr == null; } @@ -226,13 +226,13 @@ private boolean isValid(LinearRing g) */ private boolean isValid(Polygon g) { - checkCoordinateInvalid(g); + checkCoordinatesValid(g); if (hasInvalidError()) return false; - checkRingsNotClosed(g); + checkRingsClosed(g); if (hasInvalidError()) return false; - checkRingsTooFewPoints(g); + checkRingsPointSize(g); if (hasInvalidError()) return false; PolygonTopologyAnalyzer areaAnalyzer = new PolygonTopologyAnalyzer(g, isInvertedRingValid); @@ -240,13 +240,13 @@ private boolean isValid(Polygon g) checkAreaIntersections(areaAnalyzer); if (hasInvalidError()) return false; - checkHolesOutsideShell(g); + checkHolesInShell(g); if (hasInvalidError()) return false; - checkHolesNested(g); + checkHolesNotNested(g); if (hasInvalidError()) return false; - checkInteriorDisconnected(areaAnalyzer); + checkInteriorConnected(areaAnalyzer); if (hasInvalidError()) return false; return true; @@ -262,12 +262,12 @@ private boolean isValid(MultiPolygon g) { for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkCoordinateInvalid(p); + checkCoordinatesValid(p); if (hasInvalidError()) return false; - checkRingsNotClosed(p); + checkRingsClosed(p); if (hasInvalidError()) return false; - checkRingsTooFewPoints(p); + checkRingsPointSize(p); if (hasInvalidError()) return false; } @@ -278,18 +278,18 @@ private boolean isValid(MultiPolygon g) for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkHolesOutsideShell(p); + checkHolesInShell(p); if (hasInvalidError()) return false; } for (int i = 0; i < g.getNumGeometries(); i++) { Polygon p = (Polygon) g.getGeometryN(i); - checkHolesNested(p); + checkHolesNotNested(p); if (hasInvalidError()) return false; } - checkShellsNested(g); + checkShellsNotNested(g); if (hasInvalidError()) return false; - checkInteriorDisconnected(areaAnalyzer); + checkInteriorConnected(areaAnalyzer); if (hasInvalidError()) return false; return true; @@ -310,7 +310,7 @@ private boolean isValid(GeometryCollection gc) return true; } - private void checkCoordinateInvalid(Coordinate[] coords) + private void checkCoordinatesValid(Coordinate[] coords) { for (int i = 0; i < coords.length; i++) { if (! isValid(coords[i])) { @@ -320,17 +320,17 @@ private void checkCoordinateInvalid(Coordinate[] coords) } } - private void checkCoordinateInvalid(Polygon poly) + private void checkCoordinatesValid(Polygon poly) { - checkCoordinateInvalid(poly.getExteriorRing().getCoordinates()); + checkCoordinatesValid(poly.getExteriorRing().getCoordinates()); if (hasInvalidError()) return; for (int i = 0; i < poly.getNumInteriorRing(); i++) { - checkCoordinateInvalid(poly.getInteriorRingN(i).getCoordinates()); + checkCoordinatesValid(poly.getInteriorRingN(i).getCoordinates()); if (hasInvalidError()) return; } } - private void checkRingNotClosed(LinearRing ring) + private void checkRingClosed(LinearRing ring) { if (ring.isEmpty()) return; if (! ring.isClosed() ) { @@ -340,29 +340,29 @@ private void checkRingNotClosed(LinearRing ring) } } - private void checkRingsNotClosed(Polygon poly) + private void checkRingsClosed(Polygon poly) { - checkRingNotClosed(poly.getExteriorRing()); + checkRingClosed(poly.getExteriorRing()); if (hasInvalidError()) return; for (int i = 0; i < poly.getNumInteriorRing(); i++) { - checkRingNotClosed(poly.getInteriorRingN(i)); + checkRingClosed(poly.getInteriorRingN(i)); if (hasInvalidError()) return; } } - private void checkRingsTooFewPoints(Polygon poly) + private void checkRingsPointSize(Polygon poly) { - checkRingTooFewPoints(poly.getExteriorRing()); + checkRingPointSize(poly.getExteriorRing()); if (hasInvalidError()) return; for (int i = 0; i < poly.getNumInteriorRing(); i++) { - checkRingTooFewPoints(poly.getInteriorRingN(i)); + checkRingPointSize(poly.getInteriorRingN(i)); if (hasInvalidError()) return; } } - private void checkRingTooFewPoints(LinearRing ring) { + private void checkRingPointSize(LinearRing ring) { if (ring.isEmpty()) return; - checkTooFewPoints(ring, MIN_SIZE_RING); + checkPointSize(ring, MIN_SIZE_RING); } /** @@ -371,7 +371,7 @@ private void checkRingTooFewPoints(LinearRing ring) { * @param line * @param minSize */ - private void checkTooFewPoints(LineString line, int minSize) { + private void checkPointSize(LineString line, int minSize) { if (! isNonRepeatedSizeAtLeast(line, minSize) ) { Coordinate pt = line.getNumPoints() >= 1 ? line.getCoordinateN(0) : null; logInvalid(TopologyValidationError.TOO_FEW_POINTS, pt); @@ -412,7 +412,7 @@ private void checkAreaIntersections(PolygonTopologyAnalyzer areaAnalyzer) { * * @param ring the linear ring to check */ - private void checkSelfIntersectingRing(LinearRing ring) + private void checkRingSimple(LinearRing ring) { Coordinate intPt = PolygonTopologyAnalyzer.findSelfIntersection(ring); if (intPt != null) { @@ -431,7 +431,7 @@ private void checkSelfIntersectingRing(LinearRing ring) * * @param poly the polygon to be tested for hole inclusion */ - private void checkHolesOutsideShell(Polygon poly) + private void checkHolesInShell(Polygon poly) { // skip test if no holes are present if (poly.getNumInteriorRing() <= 0) return; @@ -491,7 +491,7 @@ private Coordinate findHoleOutsideShellPoint(LinearRing hole, LinearRing shell) * * @param poly the polygon with holes to test */ - private void checkHolesNested(Polygon poly) + private void checkHolesNotNested(Polygon poly) { // skip test if no holes are present if (poly.getNumInteriorRing() <= 0) return; @@ -514,7 +514,7 @@ private void checkHolesNested(Polygon poly) *
    * These have been confirmed by the {@link PolygonTopologyAnalyzer}. */ - private void checkShellsNested(MultiPolygon mp) + private void checkShellsNotNested(MultiPolygon mp) { // skip test if only one shell present if (mp.getNumGeometries() <= 1) return; @@ -526,7 +526,7 @@ private void checkShellsNested(MultiPolygon mp) } } - private void checkInteriorDisconnected(PolygonTopologyAnalyzer analyzer) { + private void checkInteriorConnected(PolygonTopologyAnalyzer analyzer) { if (analyzer.isInteriorDisconnected()) { logInvalid(TopologyValidationError.DISCONNECTED_INTERIOR, analyzer.getDisconnectionLocation()); From 96e76995c9b61fd5f47bec0c509beb2ed394e818 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Thu, 10 Mar 2022 13:23:18 -0800 Subject: [PATCH 210/275] Add WKTReader fixStructure ability (#848) Signed-off-by: Martin Davis --- .../org/locationtech/jts/geom/LineString.java | 12 +- .../org/locationtech/jts/io/WKTReader.java | 256 +++++++----------- .../jts/io/WKTReaderFixStructureTest.java | 70 +++++ .../locationtech/jts/io/WKTReaderTest.java | 5 + 4 files changed, 177 insertions(+), 166 deletions(-) create mode 100644 modules/core/src/test/java/org/locationtech/jts/io/WKTReaderFixStructureTest.java diff --git a/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java b/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java index ca0eeb8a74..bf79badbc6 100644 --- a/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java +++ b/modules/core/src/main/java/org/locationtech/jts/geom/LineString.java @@ -36,6 +36,13 @@ public class LineString implements Lineal { private static final long serialVersionUID = 3110669828065365560L; + + /** + * The minimum number of vertices allowed in a valid non-empty linestring. + * Empty linestrings with 0 vertices are also valid. + */ + public static final int MINIMUM_VALID_SIZE = 2; + /** * The points of this LineString. */ @@ -77,12 +84,13 @@ private void init(CoordinateSequence points) if (points == null) { points = getFactory().getCoordinateSequenceFactory().create(new Coordinate[]{}); } - if (points.size() == 1) { + if (points.size() > 0 && points.size() < MINIMUM_VALID_SIZE) { throw new IllegalArgumentException("Invalid number of points in LineString (found " - + points.size() + " - must be 0 or >= 2)"); + + points.size() + " - must be 0 or >= " + MINIMUM_VALID_SIZE + ")"); } this.points = points; } + public Coordinate[] getCoordinates() { return points.toCoordinateArray(); } diff --git a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java index 6bf6b8bec6..38ccfa3a80 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/WKTReader.java @@ -18,12 +18,15 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import java.util.Locale; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; import org.locationtech.jts.geom.CoordinateSequenceFactory; - +import org.locationtech.jts.geom.CoordinateXY; +import org.locationtech.jts.geom.CoordinateXYM; +import org.locationtech.jts.geom.CoordinateXYZM; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFactory; @@ -64,6 +67,11 @@ * create XYZ geometry (this is backwards compatible with older JTS versions). * This can be altered to create XY geometry by * calling {@link #setIsOldJtsCoordinateSyntaxAllowed(boolean)}. + *

    + * A reader can be set to ensure the input is structurally valid + * by calling {@link #setFixStructure(boolean)}. + * This ensures that geometry can be constructed without errors due to missing coordinates. + * The created geometry may still be topologically invalid. * *

    Notes:

    *