/* * Copyright (c) 2016 Vivid Solutions. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * * http://www.eclipse.org/org/documents/edl-v10.php. */ package test.jts.perf.operation.valid; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.util.AffineTransformation; import org.locationtech.jts.operation.valid.IsValidOp; import org.locationtech.jts.util.Stopwatch; /** * Stress-tests {@link IsValidOp} * by running it on an invalid MultiPolygon with many intersections. * In JTS 1.14 and earlier this takes a very long time to run, * since all intersections are computed before the invalid result is returned. * In fact it is only necessary to detect a single intersection in order * to determine invalidity, and this provides much faster performance. * * @author mdavis * */ public class ValidStressTest { public static void main(String args[]) { (new ValidStressTest()).runComb(); (new ValidStressTest()).runStarCrossPoly(); (new ValidStressTest()).runStarCrossRing(); } public ValidStressTest() { } public static int SIZE = 10000; static GeometryFactory geomFact = new GeometryFactory(); public void runComb() { int size = 400; Envelope env = new Envelope(0,100,0,100); Geometry geom = Comb.crossedComb(env, size, geomFact); System.out.println(geom); checkValid("Crossed combs (size = " + size + " )", geom); } public void runStarCrossPoly() { int size = 1000; Envelope env = new Envelope(0,100,0,100); Polygon geom = StarCross.star(env, size, geomFact); //System.out.println(geom); checkValid("StarCross " + geom.getGeometryType() + " (size = " + size + " )", geom); } public void runStarCrossRing() { int size = 1000; Envelope env = new Envelope(0,100,0,100); Polygon poly = StarCross.star(env, size, geomFact); Geometry geom = poly.getBoundary(); //System.out.println(geom); checkValid("StarCross " + geom.getGeometryType() + " (size = " + size + " )", geom); } public void checkValid(String name, Geometry g) { System.out.println("Running " + name); Stopwatch sw = new Stopwatch(); boolean isValid = g.isValid(); System.out.println("Is Valid = " + isValid + " Time: " + sw.getTimeString() ); } } class StarCross { public static Polygon star(Envelope env, int nSeg, GeometryFactory geomFact) { Coordinate[] pts = new Coordinate[nSeg + 1]; Coordinate centre = env.centre(); double len = 0.5 * Math.min(env.getHeight(), env.getWidth()); double angInc = Math.PI + 2 * Math.PI / nSeg; double ang = 0; for (int i = 0; i < nSeg; i++) { double x = centre.x + len * Math.cos(ang); double y = centre.x + len * Math.sin(ang); pts[i] = new Coordinate(x, y); ang += angInc; } pts[nSeg] = new Coordinate(pts[0]); return geomFact.createPolygon(pts); } } /** * Creates comb-like geometries. * Crossed combs provide a geometry with a very high ratio of intersections to edges. * * @author Martin Davis * */ class Comb { public static MultiPolygon crossedComb(Envelope env, int size, GeometryFactory geomFact) { Polygon comb1 = comb(env, size, geomFact); Coordinate centre = env.centre(); AffineTransformation trans = AffineTransformation.rotationInstance(0.5 * Math.PI, centre.x, centre.y); Polygon comb2 = (Polygon) trans.transform(comb1); MultiPolygon mp = geomFact.createMultiPolygon(new Polygon[] { comb1, comb2 } ); return mp; } public static Polygon comb(Envelope env, int nArms, GeometryFactory geomFact) { int npts = 4 * (nArms - 1) + 2 + 2 + 1; Coordinate[] pts = new Coordinate[npts]; double armWidth = env.getWidth() / (2 * nArms - 1); double armLen = env.getHeight() - armWidth; double xBase = env.getMinX(); double yBase = env.getMinY(); int ipts = 0; for (int i = 0; i < nArms; i++) { double x1 = xBase + i * 2 * armWidth; double y1 = yBase + armLen + armWidth; pts[ipts++] = new Coordinate(x1, y1); pts[ipts++] = new Coordinate(x1 + armWidth, y1); if (i < nArms - 1) { pts[ipts++] = new Coordinate(x1 + armWidth, yBase + armWidth); pts[ipts++] = new Coordinate(x1 + 2 * armWidth, yBase + armWidth); } } pts[ipts++] = new Coordinate(env.getMaxX(), yBase); pts[ipts++] = new Coordinate(xBase, yBase); pts[ipts++] = new Coordinate(pts[0]); return geomFact.createPolygon(pts); } }