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 ;