/* * 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.buffer.validate; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygonal; /** * Validates that the result of a buffer operation * is geometrically correct, within a computed tolerance. * <p> * 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.) * <p> * This test may be (much) more expensive than the original * buffer computation. * * @author Martin Davis */ public class BufferResultValidator { /** * Maximum allowable fraction of buffer distance the * actual distance can differ by. * 1% sometimes causes an error - 1.2% should be safe. */ private static final double MAX_ENV_DIFF_FRAC = .012; private static boolean VERBOSE = false; public static boolean isValid(final Geometry g, final double distance, final Geometry result) { final BufferResultValidator validator = new BufferResultValidator(g, distance, result); if (validator.isValid()) { return true; } return false; } /** * Checks whether the geometry buffer is valid, * and returns an error message if not. * * @param g * @param distance * @param result * @return an appropriate error message * or null if the buffer is valid */ public static String isValidMsg(final Geometry g, final double distance, final Geometry result) { final BufferResultValidator validator = new BufferResultValidator(g, distance, result); if (!validator.isValid()) { return validator.getErrorMessage(); } return null; } private final double distance; private Geometry errorIndicator = null; private Point errorLocation = null; private String errorMsg = null; private final Geometry input; private boolean isValid = true; private final Geometry result; public BufferResultValidator(final Geometry input, final double distance, final Geometry result) { this.input = input; this.distance = distance; this.result = result; } private void checkArea() { final double inputArea = this.input.getArea(); final double resultArea = this.result.getArea(); if (this.distance > 0.0 && inputArea > resultArea) { this.isValid = false; this.errorMsg = "Area of positive buffer is smaller than input"; this.errorIndicator = this.result; } if (this.distance < 0.0 && inputArea < resultArea) { this.isValid = false; this.errorMsg = "Area of negative buffer is larger than input"; this.errorIndicator = this.result; } report("Area"); } private void checkDistance() { final BufferDistanceValidator distValid = new BufferDistanceValidator(this.input, this.distance, this.result); if (!distValid.isValid()) { this.isValid = false; this.errorMsg = distValid.getErrorMessage(); this.errorLocation = distValid.getErrorLocation(); this.errorIndicator = distValid.getErrorIndicator(); } report("Distance"); } private void checkEnvelope() { if (this.distance < 0.0) { return; } double padding = this.distance * MAX_ENV_DIFF_FRAC; if (padding == 0.0) { padding = 0.001; } final BoundingBox expectedEnv = this.input.getBoundingBox().expand(this.distance); final BoundingBox bufEnv = this.result.getBoundingBox().expand(padding); if (!bufEnv.covers(expectedEnv)) { this.isValid = false; this.errorMsg = "Buffer envelope is incorrect"; final GeometryFactory r = this.input.getGeometryFactory(); this.errorIndicator = bufEnv.toGeometry(); } report("Envelope"); } private void checkExpectedEmpty() { // can't check areal features if (this.input.getDimension() >= 2) { return; } // can't check positive distances if (this.distance > 0.0) { return; } // at this point can expect an empty result if (!this.result.isEmpty()) { this.isValid = false; this.errorMsg = "Result is non-empty"; this.errorIndicator = this.result; } report("ExpectedEmpty"); } private void checkPolygonal() { if (!(this.result instanceof Polygonal)) { this.isValid = false; } this.errorMsg = "Result is not polygonal"; this.errorIndicator = this.result; report("Polygonal"); } /** * Gets a geometry which indicates the location and nature of a validation failure. * <p> * If the failure is due to the buffer curve being too far or too close * to the input, the indicator is a line segment showing the location and size * of the discrepancy. * * @return a geometric error indicator * or null if no error was found */ public Geometry getErrorIndicator() { return this.errorIndicator; } public Point getErrorLocation() { return this.errorLocation; } public String getErrorMessage() { return this.errorMsg; } public boolean isValid() { checkPolygonal(); if (!this.isValid) { return this.isValid; } checkExpectedEmpty(); if (!this.isValid) { return this.isValid; } checkEnvelope(); if (!this.isValid) { return this.isValid; } checkArea(); if (!this.isValid) { return this.isValid; } checkDistance(); return this.isValid; } private void report(final String checkName) { if (!VERBOSE) { return; } System.out.println("Check " + checkName + ": " + (this.isValid ? "passed" : "FAILED")); } }