All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vividsolutions.jts.precision.MinimumClearance Maven / Gradle / Ivy

There is a newer version: 0.1.4
Show newest version
/*
 * 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.vividsolutions.jts.precision;

import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Lineal;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.index.strtree.ItemBoundable;
import com.vividsolutions.jts.index.strtree.ItemDistance;
import com.vividsolutions.jts.index.strtree.STRtree;
import com.vividsolutions.jts.operation.distance.FacetSequence;
import com.vividsolutions.jts.operation.distance.FacetSequenceTreeBuilder;

/**
 * Computes the Minimum Clearance of a {@link Geometry}.
 * 

* The Minimum Clearance is a measure of * what magnitude of perturbation of * the vertices of a geometry can be tolerated * before the geometry becomes topologically invalid. * The smaller the Minimum Clearance distance, * the less vertex pertubation the geometry can tolerate * before becoming invalid. *

* The concept was introduced by Thompson and Van Oosterom * [TV06], based on earlier work by Milenkovic [Mi88]. *

* The Minimum Clearance of a geometry G * is defined to be the value r * such that "the movement of all points by a distance * of r in any direction will * guarantee to leave the geometry valid" [TV06]. * An equivalent constructive definition [Mi88] is that * r is the largest value such: *

    *
  1. No two distinct vertices of G are closer than r *
  2. No vertex of G is closer than r to an edge of G * of which the vertex is not an endpoint *
* The following image shows an example of the Minimum Clearance * of a simple polygon. *

*

*

* If G has only a single vertex (i.e. is a * {@link Point}), the value of the minimum clearance * is {@link Double#MAX_VALUE}. *

* If G is a {@link Puntal} or {@link Lineal} geometry, * then in fact no amount of perturbation * will render the geometry invalid. * In this case a Minimum Clearance is still computed * based on the vertex and segment distances * according to the constructive definition. *

* It is possible for no Minimum Clearance to exist. * For instance, a {@link MultiPoint} with all members identical * has no Minimum Clearance * (i.e. no amount of perturbation will cause * the member points to become non-identical). * Empty geometries also have no such distance. * The lack of a meaningful MinimumClearance distance is detected * and suitable values are returned by * {@link #getDistance()} and {@link #getLine()}. *

* The computation of Minimum Clearance utilizes * the {@link STRtree#nearestNeighbour(ItemDistance)} * method to provide good performance even for * large inputs. *

* An interesting note is that for the case of {@link MultiPoint}s, * the computed Minimum Clearance line * effectively determines the Nearest Neighbours in the collection. * *

References

*
    *
  • [Mi88] Milenkovic, V. J., * Verifiable implementations of geometric algorithms * using finite precision arithmetic. * in Artificial Intelligence, 377-401. 1988 *
  • [TV06] Thompson, Rod and van Oosterom, Peter, * Interchange of Spatial Data-Inhibiting Factors, * Agile 2006, Visegrad, Hungary. 2006 *
* * @author Martin Davis * */ public class MinimumClearance { /** * Computes the Minimum Clearance distance for * the given Geometry. * * @param g the input geometry * @return the Minimum Clearance distance */ public static double getDistance(Geometry g) { MinimumClearance rp = new MinimumClearance(g); return rp.getDistance(); } /** * Gets a LineString containing two points * which are at the Minimum Clearance distance * for the given Geometry. * * @param g the input geometry * @return the value of the minimum clearance distance * or LINESTRING EMPTY if no Minimum Clearance distance exists */ public static Geometry getLine(Geometry g) { MinimumClearance rp = new MinimumClearance(g); return rp.getLine(); } private Geometry inputGeom; private double minClearance; private Coordinate[] minClearancePts; /** * Creates an object to compute the Minimum Clearance * for the given Geometry * * @param geom the input geometry */ public MinimumClearance(Geometry geom) { inputGeom = geom; } /** * Gets the Minimum Clearance distance. *

* If no distance exists * (e.g. in the case of two identical points) * Double.MAX_VALUE is returned. * * @return the value of the minimum clearance distance * or Double.MAX_VALUE if no Minimum Clearance distance exists */ public double getDistance() { compute(); return minClearance; } /** * Gets a LineString containing two points * which are at the Minimum Clearance distance. *

* If no distance could be found * (e.g. in the case of two identical points) * LINESTRING EMPTY is returned. * * @return the value of the minimum clearance distance * or LINESTRING EMPTY if no Minimum Clearance distance exists */ public LineString getLine() { compute(); // return empty line string if no min pts where found if (minClearancePts == null || minClearancePts[0] == null) return inputGeom.getFactory().createLineString((Coordinate[]) null); return inputGeom.getFactory().createLineString(minClearancePts); } private void compute() { // already computed if (minClearancePts != null) return; // initialize to "No Distance Exists" state minClearancePts = new Coordinate[2]; minClearance = Double.MAX_VALUE; // handle empty geometries if (inputGeom.isEmpty()) { return; } STRtree geomTree = FacetSequenceTreeBuilder.build(inputGeom); Object[] nearest = geomTree.nearestNeighbour(new MinClearanceDistance()); MinClearanceDistance mcd = new MinClearanceDistance(); minClearance = mcd.distance( (FacetSequence) nearest[0], (FacetSequence) nearest[1]); minClearancePts = mcd.getCoordinates(); } /** * Implements the MinimumClearance distance function: *

    *
  • dist(p1, p2) = *
      *
    • p1 != p2 : p1.distance(p2) *
    • p1 == p2 : Double.MAX *
    *
  • dist(p, seg) = *
      *
    • p != seq.p1 && p != seg.p2 : seg.distance(p) *
    • ELSE : Double.MAX *
    *
* Also computes the values of the nearest points, if any. * * @author Martin Davis * */ private static class MinClearanceDistance implements ItemDistance { private double minDist = Double.MAX_VALUE; private Coordinate[] minPts = new Coordinate[2]; public Coordinate[] getCoordinates() { return minPts; } public double distance(ItemBoundable b1, ItemBoundable b2) { FacetSequence fs1 = (FacetSequence) b1.getItem(); FacetSequence fs2 = (FacetSequence) b2.getItem(); minDist = Double.MAX_VALUE; return distance(fs1, fs2); } public double distance(FacetSequence fs1, FacetSequence fs2) { // compute MinClearance distance metric vertexDistance(fs1, fs2); if (fs1.size() == 1 && fs2.size() == 1) return minDist; if (minDist <= 0.0) return minDist; segmentDistance(fs1, fs2); if (minDist <= 0.0) return minDist; segmentDistance(fs2, fs1); return minDist; } private double vertexDistance(FacetSequence fs1, FacetSequence fs2) { for (int i1 = 0; i1 < fs1.size(); i1++) { for (int i2 = 0; i2 < fs2.size(); i2++) { Coordinate p1 = fs1.getCoordinate(i1); Coordinate p2 = fs2.getCoordinate(i2); if (! p1.equals2D(p2)) { double d = p1.distance(p2); if (d < minDist) { minDist = d; minPts[0] = p1; minPts[1] = p2; if (d == 0.0) return d; } } } } return minDist; } private double segmentDistance(FacetSequence fs1, FacetSequence fs2) { for (int i1 = 0; i1 < fs1.size(); i1++) { for (int i2 = 1; i2 < fs2.size(); i2++) { Coordinate p = fs1.getCoordinate(i1); Coordinate seg0 = fs2.getCoordinate(i2-1); Coordinate seg1 = fs2.getCoordinate(i2); if (! (p.equals2D(seg0) || p.equals2D(seg1))) { double d = CGAlgorithms.distancePointLine(p, seg0, seg1); if (d < minDist) { minDist = d; updatePts(p, seg0, seg1); if (d == 0.0) return d; } } } } return minDist; } private void updatePts(Coordinate p, Coordinate seg0, Coordinate seg1) { minPts[0] = p; LineSegment seg = new LineSegment(seg0, seg1); minPts[1] = new Coordinate(seg.closestPoint(p)); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy