1 /*
2  * Copyright (c) 2016 Vivid Solutions.
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.jtstest.geomop;
13 
14 import static org.locationtech.jts.operation.overlayng.OverlayNG.DIFFERENCE;
15 import static org.locationtech.jts.operation.overlayng.OverlayNG.INTERSECTION;
16 import static org.locationtech.jts.operation.overlayng.OverlayNG.SYMDIFFERENCE;
17 import static org.locationtech.jts.operation.overlayng.OverlayNG.UNION;
18 
19 import java.util.Collection;
20 
21 import org.locationtech.jts.densify.Densifier;
22 import org.locationtech.jts.geom.Geometry;
23 import org.locationtech.jts.geom.PrecisionModel;
24 import org.locationtech.jts.geom.util.LinearComponentExtracter;
25 import org.locationtech.jts.operation.buffer.BufferOp;
26 import org.locationtech.jts.operation.buffer.BufferParameters;
27 import org.locationtech.jts.operation.overlayng.OverlayNG;
28 import org.locationtech.jts.operation.polygonize.Polygonizer;
29 import org.locationtech.jts.precision.MinimumClearance;
30 
31 /**
32  * Geometry functions which
33  * augment the existing methods on {@link Geometry},
34  * for use in XML Test files.
35  * This is the default used in the TestRunner,
36  * and thus all the operations
37  * in this class should be named differently to the Geometry methods
38  * (otherwise they will shadow the real Geometry methods).
39  * <p>
40  * If replacing a Geometry method is desired, this
41  * can be done via the -geomfunc argument to the TestRunner.
42  *
43  * @author Martin Davis
44  *
45  */
46 public class TestCaseGeometryFunctions
47 {
bufferMitredJoin(Geometry g, double distance)48 	public static Geometry bufferMitredJoin(Geometry g, double distance)
49 	{
50     BufferParameters bufParams = new BufferParameters();
51     bufParams.setJoinStyle(BufferParameters.JOIN_MITRE);
52 
53     return BufferOp.bufferOp(g, distance, bufParams);
54 	}
55 
densify(Geometry g, double distance)56   public static Geometry densify(Geometry g, double distance)
57   {
58     return Densifier.densify(g, distance);
59   }
60 
minClearance(Geometry g)61   public static double minClearance(Geometry g)
62   {
63     return MinimumClearance.getDistance(g);
64   }
65 
minClearanceLine(Geometry g)66   public static Geometry minClearanceLine(Geometry g)
67   {
68     return MinimumClearance.getLine(g);
69   }
70 
polygonize(Geometry g, boolean extractOnlyPolygonal)71   private static Geometry polygonize(Geometry g, boolean extractOnlyPolygonal) {
72     Collection lines = LinearComponentExtracter.getLines(g);
73     Polygonizer polygonizer = new Polygonizer(extractOnlyPolygonal);
74     polygonizer.add(lines);
75     return polygonizer.getGeometry();
76   }
77 
polygonize(Geometry g)78   public static Geometry polygonize(Geometry g) {
79     return polygonize(g, false);
80   }
81 
polygonizeValidPolygonal(Geometry g)82   public static Geometry polygonizeValidPolygonal(Geometry g) {
83     return polygonize(g, true);
84   }
85 
intersectionNG(Geometry geom0, Geometry geom1)86   public static Geometry intersectionNG(Geometry geom0, Geometry geom1) {
87     return OverlayNG.overlay(geom0, geom1, OverlayNG.INTERSECTION);
88   }
unionNG(Geometry geom0, Geometry geom1)89   public static Geometry unionNG(Geometry geom0, Geometry geom1) {
90     return OverlayNG.overlay(geom0, geom1, OverlayNG.UNION);
91   }
differenceNG(Geometry geom0, Geometry geom1)92   public static Geometry differenceNG(Geometry geom0, Geometry geom1) {
93     return OverlayNG.overlay(geom0, geom1, OverlayNG.DIFFERENCE);
94   }
symDifferenceNG(Geometry geom0, Geometry geom1)95   public static Geometry symDifferenceNG(Geometry geom0, Geometry geom1) {
96     return OverlayNG.overlay(geom0, geom1, OverlayNG.SYMDIFFERENCE);
97   }
98 
intersectionSR(Geometry geom0, Geometry geom1, double scale)99   public static Geometry intersectionSR(Geometry geom0, Geometry geom1, double scale) {
100     PrecisionModel pm = new PrecisionModel(scale);
101     return OverlayNG.overlay(geom0, geom1, OverlayNG.INTERSECTION, pm);
102   }
unionSr(Geometry geom0, Geometry geom1, double scale)103   public static Geometry unionSr(Geometry geom0, Geometry geom1, double scale) {
104     PrecisionModel pm = new PrecisionModel(scale);
105     return OverlayNG.overlay(geom0, geom1, OverlayNG.UNION, pm);
106   }
differenceSR(Geometry geom0, Geometry geom1, double scale)107   public static Geometry differenceSR(Geometry geom0, Geometry geom1, double scale) {
108     PrecisionModel pm = new PrecisionModel(scale);
109     return OverlayNG.overlay(geom0, geom1, OverlayNG.DIFFERENCE, pm);
110   }
symDifferenceSR(Geometry geom0, Geometry geom1, double scale)111   public static Geometry symDifferenceSR(Geometry geom0, Geometry geom1, double scale) {
112     PrecisionModel pm = new PrecisionModel(scale);
113     return OverlayNG.overlay(geom0, geom1, OverlayNG.SYMDIFFERENCE, pm);
114   }
115 
unionArea(Geometry geom)116   public static double unionArea(Geometry geom) {
117     return geom.union().getArea();
118   }
119 
unionLength(Geometry geom)120   public static double unionLength(Geometry geom) {
121     return geom.union().getLength();
122   }
123 
overlayAreaTest(Geometry a, Geometry b)124   public static boolean overlayAreaTest(Geometry a, Geometry b) {
125     double areaDelta = areaDelta(a, b);
126     return areaDelta < 1e-6;
127   }
128 
129   /**
130    * Computes the maximum area delta value
131    * resulting from identity equations over the overlay operations.
132    * The delta value is normalized to the total area of the geometries.
133    * If the overlay operations are computed correctly
134    * the area delta is expected to be very small (e.g. < 1e-6).
135    *
136    * @param a a geometry
137    * @param b a geometry
138    * @return the computed maximum area delta
139    */
areaDelta(Geometry a, Geometry b)140   private static double areaDelta(Geometry a, Geometry b) {
141 
142     double areaA = a == null ? 0 : a.getArea();
143     double areaB = b == null ? 0 : b.getArea();
144 
145     // if an input is non-polygonal delta is 0
146     if (areaA == 0 || areaB == 0)
147       return 0;
148 
149     double areaU   = a.union( b ).getArea();
150     double areaI   = a.intersection( b ).getArea();
151     double areaDab = a.difference( b ).getArea();
152     double areaDba = b.difference( a ).getArea();
153     double areaSD  = a.symDifference( b ).getArea();
154 
155     double maxDelta = 0;
156 
157     // & : intersection
158     // - : difference
159     // + : union
160     // ^ : symdifference
161 
162 
163     // A = ( A & B ) + ( A - B )
164     double delta = Math.abs( areaA - areaI - areaDab );
165     if (delta > maxDelta) {
166         maxDelta = delta;
167     }
168 
169     // B = ( A & B ) + ( B - A )
170     delta = Math.abs( areaB - areaI - areaDba );
171     if (delta > maxDelta) {
172         maxDelta = delta;
173     }
174 
175     //  ( A ^ B ) = ( A - B ) + ( B - A )
176     delta = Math.abs( areaDab + areaDba - areaSD );
177     if (delta > maxDelta) {
178         maxDelta = delta;
179     }
180 
181     //  ( A + B ) = ( A & B ) + ( A ^ B )
182     delta = Math.abs( areaI + areaSD - areaU );
183     if (delta > maxDelta) {
184         maxDelta = delta;
185     }
186 
187     //  ( A + B ) = ( A & B ) + ( A - B ) + ( A - B )
188     delta = Math.abs( areaU - areaI - areaDab - areaDba );
189     if (delta > maxDelta) {
190         maxDelta = delta;
191     }
192 
193     // normalize the area delta value
194     return maxDelta / (areaA + areaB);
195   }
196 
197 }
198 ;