Skip to content

Commit

Permalink
Fix GeometryPrecisionReducer to allow keeping collapsed components (#738
Browse files Browse the repository at this point in the history
)

Signed-off-by: Martin Davis <[email protected]>
  • Loading branch information
dr-jts authored Jun 9, 2021
1 parent e6cf602 commit 745b94e
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* By default the reduced result is topologically valid
* (i.e. {@link Geometry#isValid()} is true).
Expand All @@ -42,7 +42,7 @@
* By default the geometry precision model is not changed.
* This can be overridden by using {@link #setChangePrecisionModel(boolean)}.
* <p>
* 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)}.
*
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -12,45 +23,42 @@
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 <code>isRemoveCollapsed</code as <code>true</code>.
*
* @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(
CoordinateSequence coordinates, Geometry parent) {
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
Expand All @@ -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
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -43,7 +40,6 @@ public static void main(String args[]) {
public GeometryPrecisionReducerTest(String name)
{
super(name);
reducerKeepCollapse.setRemoveCollapsedComponents(false);
}

public void testSquare()
Expand Down Expand Up @@ -93,47 +89,47 @@ 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");
}

/**
* 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 )");
}

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))");
}

Expand Down Expand Up @@ -185,55 +181,41 @@ public void testPolgonWithCollapsedPointPointwise() throws Exception {

//=======================================



private void checkReducePointwise(String wkt, String wktExpected) {
Geometry g = read(wkt);
Geometry gExpected = read(wktExpected);
Geometry gReduce = GeometryPrecisionReducer.reducePointwise(g, pmFixed1);
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,
Expand Down

0 comments on commit 745b94e

Please sign in to comment.