/* * 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.distance; import java.util.List; import com.revolsys.geometry.algorithm.PointLocator; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Location; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; /** * Find two points on two {@link Geometry}s which lie * within a given distance, or else are the nearest points * on the geometries (in which case this also * provides the distance between the geometries). * <p> * The distance computation also finds a pair of points in the input geometries * which have the minimum distance between them. * If a point lies in the interior of a line segment, * the coordinate computed is a close * approximation to the exact point. * <p> * The algorithms used are straightforward O(n^2) * comparisons. This worst-case performance could be improved on * by using Voronoi techniques or spatial indexes. * * @version 1.7 */ public class DistanceOp { private boolean computed = false; private final Geometry geometry1; private final Geometry geometry2; private double minDistance = Double.MAX_VALUE; // working private final PointLocator pointLocator = new PointLocator(); private double terminateDistance = 0.0; /** * Constructs a DistanceOp that computes the distance and nearest points between * the two specified geometries. * @param geometry1 a Geometry * @param geometry2 a Geometry * @param terminateDistance the distance on which to terminate the search */ public DistanceOp(final Geometry geometry1, final Geometry geometry2, final double terminateDistance) { if (geometry1 == null || geometry2 == null) { throw new IllegalArgumentException("null geometries are not supported"); } this.geometry1 = geometry1; this.geometry2 = geometry2; this.terminateDistance = terminateDistance; } private boolean computeContainmentDistance() { if (computeContainmentDistance(this.geometry1, this.geometry2)) { return true; } else if (computeContainmentDistance(this.geometry2, this.geometry1)) { return true; } else { return false; } } private boolean computeContainmentDistance(final Geometry geometry1, final Geometry geometry2) { final List<Polygon> polys = geometry1.getGeometries(Polygon.class); if (polys.size() > 0) { final List<Point> insidePoints = ConnectedElementLocationFilter.getPoints(geometry2); if (computeContainmentDistance(insidePoints, polys)) { return true; } } return false; } private boolean computeContainmentDistance(final List<Point> points, final List<Polygon> polygons) { for (final Point point : points) { for (final Polygon polygon : polygons) { if (computeContainmentDistance(point, polygon)) { return true; } } } return false; } private boolean computeContainmentDistance(final Point point, final Polygon poly) { // if pt is not in exterior, distance to geom is 0 if (Location.EXTERIOR != poly.locate(point)) { this.minDistance = 0.0; return true; } else { return false; } } /** * Computes distance between facets (lines and points) * of input geometries. * */ private void computeFacetDistance() { /** * Geometries are not wholly inside, so compute distance from lines and points * of one to lines and points of the other */ final List<LineString> lines0 = this.geometry1.getGeometryComponents(LineString.class); final List<LineString> lines1 = this.geometry2.getGeometryComponents(LineString.class); if (!computeLinesLines(lines0, lines1)) { final List<Point> points1 = this.geometry2.getGeometries(Point.class); if (!computeLinesPoints(lines0, points1)) { final List<Point> points0 = this.geometry1.getGeometries(Point.class); if (!computeLinesPoints(lines1, points0)) { computePointsPoints(points0, points1); } } } } private boolean computeLineLine(final LineString line1, final LineString line2) { if (this.minDistance == Double.MAX_VALUE || line1.getBoundingBox().distance(line2.getBoundingBox()) <= this.minDistance) { final double distance = line1.distance(line2); if (distance < this.minDistance) { this.minDistance = distance; if (this.minDistance <= this.terminateDistance) { return true; } } } return false; } private boolean computeLinePoint(final LineString line, final Point point) { if (this.minDistance == Double.MAX_VALUE || line.getBoundingBox().distance(point) <= this.minDistance) { final double distance = line.distance(point, this.terminateDistance); if (distance < this.minDistance) { this.minDistance = distance; if (this.minDistance <= this.terminateDistance) { return true; } } } return false; } private boolean computeLinesLines(final List<LineString> lines1, final List<LineString> lines2) { for (final LineString line1 : lines1) { for (final LineString line2 : lines2) { if (computeLineLine(line1, line2)) { return true; } } } return false; } private boolean computeLinesPoints(final List<LineString> lines, final List<Point> points) { for (final LineString line : lines) { for (final Point point : points) { if (computeLinePoint(line, point)) { return true; } } } return false; } private boolean computePointsPoints(final List<Point> points1, final List<Point> points2) { for (final Point point1 : points1) { for (final Point point2 : points2) { final double distance = point1.distancePoint(point2); if (distance < this.minDistance) { this.minDistance = distance; if (this.minDistance <= this.terminateDistance) { return true; } } } } return false; } /** * Report the distance between the nearest points on the input geometries. * * @return the distance between the geometries * or 0 if either input geometry is empty * @throws IllegalArgumentException if either input geometry is null */ public double distance() { if (!this.computed) { if (this.geometry1.isEmpty() || this.geometry2.isEmpty()) { this.minDistance = 0; } else { this.computed = true; if (!computeContainmentDistance()) { computeFacetDistance(); } } } return this.minDistance; } }