diff --git a/modules/core/src/main/java/org/locationtech/jts/densify/LittleThumblingDensifier.java b/modules/core/src/main/java/org/locationtech/jts/densify/LittleThumblingDensifier.java new file mode 100644 index 0000000000..98c717c4dd --- /dev/null +++ b/modules/core/src/main/java/org/locationtech/jts/densify/LittleThumblingDensifier.java @@ -0,0 +1,154 @@ +/* + * 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 org.locationtech.jts.densify; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.util.GeometryTransformer; + +/** + * Densify a geometry using the "Little Thumbling" strategy: Add a vertex for each step along the line. + * + * @author julien Gaffuri + * + */ +public class LittleThumblingDensifier { + + /** + * Densify a geometry: Walk along the line step by step and record the positions at each step + * (like the "Little Thumbling" would do). + * Return the line composed of the recorded positions. + * The vertices of the input geometry are not kept, which can result in an output line with shorter length. + * The result is different from org.locationtech.jts.densify.Densifier + * + * @param geom + * @param stepLength the step length + * @return the densified geometry + */ + public static Geometry densify(Geometry geom, double stepLength) { + LittleThumblingDensifier densifier = new LittleThumblingDensifier(geom); + densifier.setStepLength(stepLength); + return densifier.getResultGeometry(); + } + + /** + * Densifies a coordinate sequence. + * + * @param pts + * @param stepLength the step length + * @return the densified coordinate sequence + */ + private static Coordinate[] densifyPoints(Coordinate[] pts, double stepLength, PrecisionModel precModel) { + //out coords + LineString line = new GeometryFactory(precModel).createLineString(pts); + int nb = (int) (line.getLength()/stepLength); + Coordinate[] out = new Coordinate[nb+1]; + + double d=0.0, a=0.0, dTot; + int densIndex=0; + for(int i=0; i 0.25*length ) { + if(isClosed){ + //return tiny triangle nearby center point + Coordinate c = line.getCentroid().getCoordinate(); + length *= 0.01; + return line.getFactory().createLineString(new Coordinate[]{ new Coordinate(c.x-length,c.y-length), new Coordinate(c.x,c.y+length), new Coordinate(c.x+length,c.y-length), new Coordinate(c.x-length,c.y-length) }); + } else { + //return segment + return line.getFactory().createLineString(new Coordinate[]{ line.getCoordinateN(0), line.getCoordinateN(line.getNumPoints()-1) }); + } + } + + //compute densified line + Coordinate[] densifiedCoords = LittleThumblingDensifier.densify(line, densifiedResolution).getCoordinates(); + + //build ouput line structure + int nb = (int) (length/densifiedResolution); + Coordinate[] out = new Coordinate[nb+1]; + + //prepare gaussian coefficients + int n = 7*3; //it should be: E(7*sigma/densifiedResolution), which is 7*3; + double gcs[] = new double[n+1]; + { + double a = sigmaM*Math.sqrt(2*Math.PI); + double b = sigmaM*sigmaM*2; + double d = densifiedResolution*densifiedResolution; + for(int i=0; inb) { + if(isClosed) { + //make loop to get the right point + q = q%nb; if(q==0) q=nb; + Coordinate c = densifiedCoords[q]; + xq = c.x; + yq = c.y; + } else { + //get symetric point + q = nb-q%nb; if(q==nb) q=0; + Coordinate c = densifiedCoords[q]; + xq = 2*cN.x-c.x; + yq = 2*cN.y-c.y; + } + } else { + //general case (most frequent) + Coordinate c = densifiedCoords[q]; + xq = c.x; + yq = c.y; + } + //get gaussian coefficient + double gc = gcs[j>=0?j:-j]; + //add contribution of point q to new position of point i + x += xq*gc; + y += yq*gc; + } + //assign smoothed position of point i + out[i] = new Coordinate(x*densifiedResolution, y*densifiedResolution); + } + + //handle start and end points + if(isClosed) { + //ensure start and end locations are the same + out[nb] = out[0]; + } else { + //ensure start and end points are at the same position as the initial geometry + out[0] = densifiedCoords[0]; + out[nb] = densifiedCoords[densifiedCoords.length-1]; + } + + //prepare final line, applying some filtering + LineString lsOut = line.getFactory().createLineString(out); + if(resolution<0) resolution = densifiedResolution /3; + lsOut = (LineString) DouglasPeuckerSimplifier.simplify( lsOut , resolution); + + return lsOut; + } + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/densify/LittleThumblingDensifierTest.java b/modules/core/src/test/java/org/locationtech/jts/densify/LittleThumblingDensifierTest.java new file mode 100644 index 0000000000..8907700d38 --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/densify/LittleThumblingDensifierTest.java @@ -0,0 +1,61 @@ +/* + * 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 org.locationtech.jts.densify; + +import java.util.Collection; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.WKTFileReader; +import org.locationtech.jts.io.WKTReader; + +import junit.framework.TestCase; + +/** + * @author Julien Gaffuri + * + */ +public class LittleThumblingDensifierTest extends TestCase { + private final WKTReader wr = new WKTReader(); + + public LittleThumblingDensifierTest(String name) { super(name); } + + + public static void main(String[] args) { + junit.textui.TestRunner.run(LittleThumblingDensifierTest.class); + } + + public void test0() throws Exception{ + Geometry g = wr.read("LINESTRING(0 0, 1 0)"); + for(double step : new double[] {0.1, 0.05, 0.5, 0.7, 0.005}) { + Geometry g_ = LittleThumblingDensifier.densify(g, step); + assertEquals(g.getLength(), g_.getLength()); + assertEquals((int)(1.0/step)+1, g_.getNumPoints()); + } + } + + public void test1() throws Exception{ + Geometry g = LittleThumblingDensifier.densify(wr.read("LINESTRING(0 0, 1 1)"), 0.1); + assertEquals(Math.sqrt(2), g.getLength()); + assertEquals(15, g.getNumPoints()); + } + + public void test2() throws Exception{ + WKTFileReader wfr = new WKTFileReader("src/test/resources/testdata/plane.wkt", new WKTReader()); + Collection gs = wfr.read(); + Geometry g = (Geometry) gs.iterator().next(); + Geometry g_ = LittleThumblingDensifier.densify(g, 0.1); + + assertTrue(g.getLength()>g_.getLength()); + assertEquals(2612, g_.getNumPoints()); + } + +} diff --git a/modules/core/src/test/java/org/locationtech/jts/simplify/GaussianLineSmoothingTest.java b/modules/core/src/test/java/org/locationtech/jts/simplify/GaussianLineSmoothingTest.java new file mode 100644 index 0000000000..e5872ff27b --- /dev/null +++ b/modules/core/src/test/java/org/locationtech/jts/simplify/GaussianLineSmoothingTest.java @@ -0,0 +1,74 @@ +/* + * 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 org.locationtech.jts.simplify; + +import java.util.Collection; + +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.WKTFileReader; +import org.locationtech.jts.io.WKTReader; + +import junit.framework.TestCase; + +/** + * @author Julien Gaffuri + * + */ +public class GaussianLineSmoothingTest extends TestCase { + private final WKTReader wr = new WKTReader(); + + public GaussianLineSmoothingTest(String name) { super(name); } + + public static void main(String[] args) { + junit.textui.TestRunner.run(GaussianLineSmoothingTest.class); + } + + public void testEmptyLine() throws Exception { + LineString gIn = new GeometryFactory().createLineString(); + LineString gOut = GaussianLineSmoothing.get(gIn, 10, 0.1 ); + assertTrue(gOut.isEmpty()); + } + + public void test1() throws Exception{ + WKTFileReader wfr = new WKTFileReader("src/test/resources/testdata/plane.wkt", wr); + Collection gs = wfr.read(); + LineString ls = (LineString) gs.iterator().next(); + for(double sigmaM : new double[]{1,2,4,6,8,10,20,30,40,50,60,70,80,90,100,150,200,100000}){ + LineString ls_ = GaussianLineSmoothing.get(ls, sigmaM, 0.1); + assertTrue(ls_.getLength() gs = wfr.read(); + for(Object g_ : gs) { + LineString ls = null; + if(g_ instanceof Polygon) + ls = ((Polygon)g_).getExteriorRing(); + else if(g_ instanceof MultiPolygon) + ls = ((Polygon)((MultiPolygon)g_).getGeometryN(0)).getExteriorRing(); + else + continue; + for(double sigmaM : new double[]{0.01, 0.05, 0.1, 0.5, 1, 100, 100000}){ + LineString ls_ = GaussianLineSmoothing.get(ls, sigmaM, 0.1); + assertTrue(ls_.getLength()