Class SimpleMinimumClearance

  1 /*
  2  * Copyright (c) 2016 Martin Davis.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12 package org.locationtech.jts.precision;
 13  
 14 import org.locationtech.jts.algorithm.Distance;
 15 import org.locationtech.jts.geom.Coordinate;
 16 import org.locationtech.jts.geom.CoordinateFilter;
 17 import org.locationtech.jts.geom.CoordinateSequence;
 18 import org.locationtech.jts.geom.CoordinateSequenceFilter;
 19 import org.locationtech.jts.geom.Geometry;
 20 import org.locationtech.jts.geom.LineSegment;
 21 import org.locationtech.jts.geom.LineString;
 22  
 23 /**
 24  * Computes the minimum clearance of a geometry or 
 25  * set of geometries.
 26  * <p>
 27  * The <b>Minimum Clearance</b> is a measure of
 28  * what magnitude of perturbation of its vertices can be tolerated
 29  * by a geometry before it becomes topologically invalid.
 30  * <p>
 31  * This class uses an inefficient O(N^2) scan.  
 32  * It is primarily for testing purposes.
 33  * 
 34  * 
 35  * @see MinimumClearance
 36  * @author Martin Davis
 37  *
 38  */
 39 public class SimpleMinimumClearance 
 40 {
 41   public static double getDistance(Geometry g)
 42   {
 43     SimpleMinimumClearance rp = new SimpleMinimumClearance(g);
 44     return rp.getDistance();
 45   }
 46   
 47   public static Geometry getLine(Geometry g)
 48   {
 49     SimpleMinimumClearance rp = new SimpleMinimumClearance(g);
 50     return rp.getLine();
 51   }
 52   
 53   private Geometry inputGeom;
 54   private double minClearance;
 55   private Coordinate[] minClearancePts;
 56   
 57   public SimpleMinimumClearance(Geometry geom)
 58   {
 59     inputGeom = geom;
 60   }
 61   
 62   public double getDistance()
 63   {
 64     compute();
 65     return minClearance;
 66   }
 67   
 68   public LineString getLine()
 69   {
 70     compute();
 71     return inputGeom.getFactory().createLineString(minClearancePts);
 72   }
 73   
 74   private void compute()
 75   {
 76     if (minClearancePts != nullreturn;
 77     minClearancePts = new Coordinate[2];
 78     minClearance = Double.MAX_VALUE;
 79     inputGeom.apply(new VertexCoordinateFilter(this));
 80   }
 81   
 82   private void updateClearance(double candidateValue, Coordinate p0, Coordinate p1)
 83   {
 84     if (candidateValue < minClearance) {
 85       minClearance = candidateValue;
 86       minClearancePts[0] = new Coordinate(p0);
 87       minClearancePts[1] = new Coordinate(p1);
 88     }
 89   }
 90   
 91   private void updateClearance(double candidateValue, Coordinate p, 
 92       Coordinate seg0, Coordinate seg1)
 93   {
 94     if (candidateValue < minClearance) {
 95       minClearance = candidateValue;
 96       minClearancePts[0] = new Coordinate(p);
 97       LineSegment seg = new LineSegment(seg0, seg1);
 98       minClearancePts[1] = new Coordinate(seg.closestPoint(p));
 99     }
100   }
101   
102   private static class VertexCoordinateFilter 
103   implements CoordinateFilter
104   {
105     SimpleMinimumClearance smc;
106     
107     public VertexCoordinateFilter(SimpleMinimumClearance smc)
108     {
109       this.smc = smc;
110     }
111     
112     public void filter(Coordinate coord) {
113       smc.inputGeom.apply(new ComputeMCCoordinateSequenceFilter(smc, coord));
114     }
115   }
116   
117   private static class ComputeMCCoordinateSequenceFilter 
118   implements CoordinateSequenceFilter 
119   {
120     SimpleMinimumClearance smc;
121     private Coordinate queryPt;
122     
123     public ComputeMCCoordinateSequenceFilter(SimpleMinimumClearance smc, Coordinate queryPt)
124     {
125       this.smc = smc;
126       this.queryPt = queryPt;
127     }
128     public void filter(CoordinateSequence seq, int i) {
129       // compare to vertex
130       checkVertexDistance(seq.getCoordinate(i));
131       
132       // compare to segment, if this is one
133       if (i > 0) {
134         checkSegmentDistance(seq.getCoordinate(i - 1), seq.getCoordinate(i));
135       }
136     }
137     
138     private void checkVertexDistance(Coordinate vertex)
139     {
140       double vertexDist = vertex.distance(queryPt);
141       if (vertexDist > 0) {
142         smc.updateClearance(vertexDist, queryPt, vertex);
143       }
144     }
145     
146     private void checkSegmentDistance(Coordinate seg0, Coordinate seg1)
147     {
148         if (queryPt.equals2D(seg0) || queryPt.equals2D(seg1))
149           return;
150         double segDist = Distance.pointToSegment(queryPt, seg1, seg0);
151         if (segDist > 0
152           smc.updateClearance(segDist, queryPt, seg1, seg0);
153     }
154     
155     public boolean isDone() {
156       return false;
157     }
158     
159     public boolean isGeometryChanged() {
160       return false;
161     }
162     
163   }
164 }
165