/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.operation.overlay.validate;
import java.util.ArrayList;
import java.util.List;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.Location;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.operation.overlay.OverlayOp;
import com.revolsys.geometry.operation.overlay.snap.GeometrySnapper;
/**
* Validates that the result of an overlay operation is
* geometrically correct, within a determined tolerance.
* Uses fuzzy point location to find points which are
* definitely in either the interior or exterior of the result
* geometry, and compares these results with the expected ones.
* <p>
* This algorithm is only useful where the inputs are polygonal.
* This is a heuristic test, and may return false positive results
* (I.e. it may fail to detect an invalid result.)
* It should never return a false negative result, however
* (I.e. it should never report a valid result as invalid.)
*
* @author Martin Davis
* @version 1.7
* @see OverlayOp
*/
public class OverlayResultValidator {
private static final double TOLERANCE = 0.000001;
private static double computeBoundaryDistanceTolerance(final Geometry g0, final Geometry g1) {
return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0),
GeometrySnapper.computeSizeBasedSnapTolerance(g1));
}
private static boolean hasLocation(final Location[] location, final Location loc) {
for (int i = 0; i < 3; i++) {
if (location[i] == loc) {
return true;
}
}
return false;
}
public static boolean isValid(final Geometry a, final Geometry b, final int overlayOp,
final Geometry result) {
final OverlayResultValidator validator = new OverlayResultValidator(a, b, result);
return validator.isValid(overlayOp);
}
private double boundaryDistanceTolerance = TOLERANCE;
private final Geometry[] geom;
private Point invalidLocation = null;
private final Location[] location = new Location[3];
private final FuzzyPointLocator[] locFinder;
private final List<Point> testCoords = new ArrayList<>();
public OverlayResultValidator(final Geometry a, final Geometry b, final Geometry result) {
/**
* The tolerance to use needs to depend on the size of the geometries.
* It should not be more precise than double-precision can support.
*/
this.boundaryDistanceTolerance = computeBoundaryDistanceTolerance(a, b);
this.geom = new Geometry[] {
a, b, result
};
this.locFinder = new FuzzyPointLocator[] {
new FuzzyPointLocator(this.geom[0], this.boundaryDistanceTolerance),
new FuzzyPointLocator(this.geom[1], this.boundaryDistanceTolerance),
new FuzzyPointLocator(this.geom[2], this.boundaryDistanceTolerance)
};
}
private void addTestPts(final Geometry g) {
final OffsetPointGenerator ptGen = new OffsetPointGenerator(g);
this.testCoords.addAll(ptGen.getPoints(5 * this.boundaryDistanceTolerance));
}
private boolean checkValid(final int overlayOp) {
for (int i = 0; i < this.testCoords.size(); i++) {
final Point pt = this.testCoords.get(i);
if (!checkValid(overlayOp, pt)) {
this.invalidLocation = pt;
return false;
}
}
return true;
}
private boolean checkValid(final int overlayOp, final Point pt) {
this.location[0] = this.locFinder[0].getLocation(pt);
this.location[1] = this.locFinder[1].getLocation(pt);
this.location[2] = this.locFinder[2].getLocation(pt);
/**
* If any location is on the Boundary, can't deduce anything, so just return true
*/
if (hasLocation(this.location, Location.BOUNDARY)) {
return true;
}
return isValidResult(overlayOp, this.location);
}
public Point getInvalidLocation() {
return this.invalidLocation;
}
public boolean isValid(final int overlayOp) {
addTestPts(this.geom[0]);
addTestPts(this.geom[1]);
final boolean isValid = checkValid(overlayOp);
/*
* System.out.println("OverlayResultValidator: " + isValid); System.out.println("G0");
* System.out.println(geom[0]); System.out.println("G1"); System.out.println(geom[1]);
* System.out.println("Result"); System.out.println(geom[2]);
*/
return isValid;
}
private boolean isValidResult(final int overlayOp, final Location[] location) {
final boolean expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
final boolean resultInInterior = location[2] == Location.INTERIOR;
// MD use simpler: boolean isValid = (expectedInterior == resultInInterior);
final boolean isValid = !(expectedInterior ^ resultInInterior);
if (!isValid) {
reportResult(overlayOp, location, expectedInterior);
}
return isValid;
}
private void reportResult(final int overlayOp, final Location[] location,
final boolean expectedInterior) {
System.out.println("Overlay result invalid - A:" + Location.toLocationSymbol(location[0])
+ " B:" + Location.toLocationSymbol(location[1]) + " expected:"
+ (expectedInterior ? 'i' : 'e') + " actual:" + Location.toLocationSymbol(location[2]));
}
}