package mil.nga.giat.geowave.analytic;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import mil.nga.giat.geowave.analytic.GeometryDataSetGenerator.CurvedDensityDataGeneratorTool;
import mil.nga.giat.geowave.analytic.GeometryGenerator.DistortationFn;
import mil.nga.giat.geowave.analytic.distance.CoordinateCircleDistanceFn;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vividsolutions.jts.algorithm.ConvexHull;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.PrecisionModel;
import com.vividsolutions.jts.io.WKTReader;
public class GeometryHullToolTest
{
protected static final Logger LOGGER = LoggerFactory.getLogger(GeometryHullToolTest.class);
GeometryFactory factory = new GeometryFactory();
@Test
public void testDistance() {
final double distance1 = GeometryHullTool.calcDistance(
new Coordinate(
3,
3),
new Coordinate(
6,
6),
new Coordinate(
5,
5.5));
final double distance2 = GeometryHullTool.calcDistance(
new Coordinate(
3,
3),
new Coordinate(
6,
6),
new Coordinate(
5,
4.5));
assertEquals(
distance1,
distance2,
0.0001);
final double distance3 = GeometryHullTool.calcDistance(
new Coordinate(
4,
6),
new Coordinate(
6,
12),
new Coordinate(
5,
8));
assertTrue(distance3 > 0);
final double distance4 = GeometryHullTool.calcDistance(
new Coordinate(
4,
6),
new Coordinate(
6,
12),
new Coordinate(
5,
9));
assertEquals(
0.0,
distance4,
0.001);
final double distance5 = GeometryHullTool.calcDistance(
new Coordinate(
5,
7),
new Coordinate(
11,
3),
new Coordinate(
6,
10));
assertTrue(distance5 < 0);
final double distance6 = GeometryHullTool.calcDistance(
new Coordinate(
5,
7),
new Coordinate(
11,
3),
new Coordinate(
7,
6.5));
final double distance7 = GeometryHullTool.calcDistance(
new Coordinate(
5,
7),
new Coordinate(
11,
3),
new Coordinate(
7,
5.0));
assertTrue(distance7 < distance6);
}
@Test
public void testAngles() {
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
41.2)) > 0);
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
43)) < 0);
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
41.2)) < GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
41.1)));
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
43)) > GeometryHullTool.calcAngle(
new Coordinate(
39,
41.5),
new Coordinate(
41,
41),
new Coordinate(
38,
44)));
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
42,
42),
new Coordinate(
41,
41),
new Coordinate(
42.5,
44)) > 0);
assertTrue(GeometryHullTool.calcAngle(
new Coordinate(
42,
42),
new Coordinate(
41,
41),
new Coordinate(
42.5,
40.5)) < 0);
assertEquals(
-90.0,
GeometryHullTool.calcAngle(
new Coordinate(
41,
42),
new Coordinate(
41,
41),
new Coordinate(
42,
41)),
0.001);
assertEquals(
90.0,
GeometryHullTool.calcAngle(
new Coordinate(
42,
41),
new Coordinate(
41,
41),
new Coordinate(
41,
42)),
0.001);
assertEquals(
-180,
GeometryHullTool.calcAngle(
new Coordinate(
42,
42),
new Coordinate(
41,
41),
new Coordinate(
40,
40)),
0.001);
assertEquals(
0,
GeometryHullTool.calcAngle(
new Coordinate(
42,
42),
new Coordinate(
41,
41),
new Coordinate(
42,
42)),
0.001);
assertEquals(
-315,
GeometryHullTool.calcAngle(
new Coordinate(
41,
41),
new Coordinate(
42,
41),
new Coordinate(
41,
40)),
0.001);
assertEquals(
-45,
GeometryHullTool.calcAngle(
new Coordinate(
42,
41),
new Coordinate(
41,
41),
new Coordinate(
42,
40)),
0.001);
assertEquals(
-45,
GeometryHullTool.calcAngle(
new Coordinate(
41,
42),
new Coordinate(
41,
41),
new Coordinate(
42,
42)),
0.001);
}
@Test
public void testConcaveHullBulkTest() {
long time = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
assertTrue(getHull(
factory.createLineString(new Coordinate[] {
new Coordinate(
41.2,
40.8),
new Coordinate(
40.8,
40.6)
}),
"po1",
false,
true).isSimple() || true);
}
System.out.println(System.currentTimeMillis() - time);
time = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
assertTrue(getHull(
factory.createLineString(new Coordinate[] {
new Coordinate(
41.2,
40.8),
new Coordinate(
40.8,
40.6)
}),
"er1",
false,
false).isSimple() || true);
}
System.out.println(System.currentTimeMillis() - time);
}
private final Random r = new Random(
7777);
private Coordinate pickOneAndAugmentOne(
final Coordinate[] list ) {
final Coordinate select = list[(Math.abs(r.nextInt()) % list.length)];
return new Coordinate(
select.x + r.nextGaussian(),
select.y + r.nextGaussian(),
select.z);
}
final Coordinate[] poly1 = new Coordinate[] {
new Coordinate(
40,
40),
new Coordinate(
40.1,
40.1),
new Coordinate(
39.2,
41.2), // selected top (2)
new Coordinate(
39,
40.7),
new Coordinate(
38.7,
40.1),
new Coordinate(
38.4,
39.5),
new Coordinate(
// selected bottom (6)
39.3,
39.2),
new Coordinate(
40,
40)
};
final Coordinate[] poly2 = new Coordinate[] {
new Coordinate(
40.2,
40),
new Coordinate(
40.5,
41), // selected top (1)
new Coordinate(
41.2,
40.8),
new Coordinate(
40.8,
40.6),
new Coordinate(
40.6,
39.6),
new Coordinate(
40.3,
39.8), // selected
// bottom(5)
new Coordinate(
40.2,
40)
};
@Test
public void testLRPolygons() {
final Geometry leftShape = factory.createPolygon(poly1);
final Geometry rightShape = factory.createPolygon(poly2);
assertTrue(GeometryHullTool.clockwise(leftShape.getCoordinates()));
assertFalse(GeometryHullTool.clockwise(rightShape.getCoordinates()));
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
final Geometry geo = cg.connect(
leftShape,
rightShape);
assertEquals(
"POLYGON ((39.2 41.2, 39 40.7, 38.7 40.1, 38.4 39.5, 39.3 39.2, 40.6 39.6, 40.8 40.6, 41.2 40.8, 40.5 41, 39.2 41.2))",
geo.toString());
}
@Test
public void testRLPolygons() {
final Geometry leftShape = factory.createPolygon(poly2);
final Geometry rightShape = factory.createPolygon(poly1);
assertFalse(GeometryHullTool.clockwise(leftShape.getCoordinates()));
assertTrue(GeometryHullTool.clockwise(rightShape.getCoordinates()));
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
final Geometry geo = cg.connect(
leftShape,
rightShape);
assertEquals(
"POLYGON ((39.2 41.2, 39 40.7, 38.7 40.1, 38.4 39.5, 39.3 39.2, 40.6 39.6, 40.8 40.6, 41.2 40.8, 40.5 41, 39.2 41.2))",
geo.toString());
}
public void testRandomConnect()
throws IOException {
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
final Iterator<Geometry> it1 = GeometryGenerator.generate(
1000,
Arrays.asList(1.0),
new DistortationFn() {
final Random r = new Random(
7777);
@Override
public double distort() {
return 0.5 + (0.5 * r.nextDouble());
}
},
5,
new Envelope(
45,
55,
35,
45));
final Iterator<Geometry> it2 = GeometryGenerator.generate(
1000,
Arrays.asList(1.0),
new DistortationFn() {
final Random r = new Random(
7777);
@Override
public double distort() {
return 0.5 + (0.5 * r.nextDouble());
}
},
5,
new Envelope(
30,
47,
20,
37));
while (it1.hasNext()) {
Geometry rightShape = it1.next();
Geometry leftShape = it2.next();
if (rightShape.intersects(leftShape)) {
Geometry inter = rightShape.intersection(leftShape);
rightShape = rightShape.difference(inter);
leftShape = leftShape.difference(inter);
}
ShapefileTool.writeShape(
"test_random",
new File(
"./target/test_randoms"),
new Geometry[] {
leftShape,
rightShape
});
Geometry geo = cg.connect(
leftShape,
rightShape);
ShapefileTool.writeShape(
"test_random",
new File(
"./target/test_random"),
new Geometry[] {
geo
});
if (!geo.isSimple()) {
// assertTrue(false);
geo = cg.connect(
leftShape,
rightShape);
ShapefileTool.writeShape(
"test_random2",
new File(
"./target/test_random2"),
new Geometry[] {
geo
});
}
}
}
private Coordinate[] reversed(
final Coordinate[] poly ) {
final Coordinate polyReversed[] = new Coordinate[poly.length];
for (int i = 0; i < poly.length; i++) {
polyReversed[i] = poly[poly.length - i - 1];
}
return polyReversed;
}
@Test
public void interesectEdges() {
final GeometryHullTool.Edge e1 = new GeometryHullTool.Edge(
new Coordinate(
20.0,
20.0),
new Coordinate(
21.5,
21),
0);
final GeometryHullTool.Edge e2 = new GeometryHullTool.Edge(
new Coordinate(
20.4,
19.0),
new Coordinate(
21.0,
22),
0);
assertTrue(GeometryHullTool.edgesIntersect(
e1,
e2));
final GeometryHullTool.Edge e3 = new GeometryHullTool.Edge(
new Coordinate(
20.4,
19.0),
new Coordinate(
21.0,
19.5),
0);
assertTrue(!GeometryHullTool.edgesIntersect(
e1,
e3));
}
@Test
public void testRLSamePolygons() {
final Geometry leftShape = factory.createPolygon(reversed(poly1));
final Geometry rightShape = factory.createPolygon(reversed(poly2));
assertFalse(GeometryHullTool.clockwise(leftShape.getCoordinates()));
assertTrue(GeometryHullTool.clockwise(rightShape.getCoordinates()));
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
final Geometry geo = cg.connect(
leftShape,
rightShape);
assertEquals(
"POLYGON ((39.2 41.2, 39 40.7, 38.7 40.1, 38.4 39.5, 39.3 39.2, 40.6 39.6, 40.8 40.6, 41.2 40.8, 40.5 41, 39.2 41.2))",
geo.toString());
}
@Test
public void testPolygonConnection() {
final boolean save = true;
final Geometry concave1 = getHull(
factory.createLineString(new Coordinate[] {
new Coordinate(
41.2,
40.8),
new Coordinate(
40.8,
40.6)
}),
"p1",
save,
false);
final Geometry concave2 = getHull(
factory.createLineString(new Coordinate[] {
new Coordinate(
39.9,
40.6),
new Coordinate(
40.8,
40.6)
}),
"p2",
save,
false);
final Geometry concave3 = getHull(
factory.createLineString(new Coordinate[] {
new Coordinate(
42.0,
42.0),
new Coordinate(
41.2,
40.8)
}),
"p3",
save,
false);
final Geometry hull = concave1.union(
concave2).union(
concave3);
assertTrue(hull.isSimple());
writeToShapeFile(
"final_phull",
hull);
coversPoints(
hull,
concave1);
coversPoints(
hull,
concave2);
coversPoints(
hull,
concave3);
}
private Geometry getHull(
final LineString str,
final String name,
final boolean save,
final boolean parkandOh ) {
final List<Point> points = CurvedDensityDataGeneratorTool.generatePoints(
str,
0.4,
1000);
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
final Coordinate[] coordinates = new Coordinate[points.size()];
int i = 0;
for (final Point point : points) {
coordinates[i++] = point.getCoordinate();
}
final ConvexHull convexHull = new ConvexHull(
coordinates,
factory);
final Geometry concaveHull = parkandOh ? cg.concaveHullParkOhMethod(
convexHull.getConvexHull(),
Arrays.asList(coordinates)) : cg.concaveHull(
convexHull.getConvexHull(),
Arrays.asList(coordinates));
if (save || !concaveHull.isSimple()) {
writeToShapeFile(
"setx_" + name,
points.toArray(new Geometry[points.size()]));
writeToShapeFile(
"chullx_" + name,
concaveHull);
writeToShapeFile(
"hullx_" + name,
convexHull.getConvexHull());
}
// final Geometry concaveHull1 = cg.concaveHull1(
// convexHull.getConvexHull(),
// Arrays.asList(coordinates));
// if (save || !concaveHull1.isSimple()) {
// writeToShapeFile(
// "chull_" + name,
// concaveHull1);
// }
return concaveHull;
}
private static void writeToShapeFile(
final String name,
final Geometry... geos ) {
if (true) { // LOGGER.isDebugEnabled()) {
try {
ShapefileTool.writeShape(
name,
new File(
"./target/test_" + name),
geos);
}
catch (final IOException e) {
e.printStackTrace();
}
}
}
private static boolean coversPoints(
final Geometry coverer,
final Geometry pointsToCover ) {
for (final Coordinate coordinate : pointsToCover.getCoordinates()) {
if (!coverer.covers(coverer.getFactory().createPoint(
coordinate))) {
return false;
}
}
return true;
}
@Test
public void testCreateHullFromGeometry() {
final GeometryHullTool cg = new GeometryHullTool();
cg.setDistanceFnForCoordinate(new CoordinateCircleDistanceFn());
for (int i = 2; i < 10; i++) {
final Coordinate[] coords = new Coordinate[i];
for (int p = 0; p < i; p++) {
coords[p] = new Coordinate(
p,
p);
}
final Geometry lineString1 = factory.createLineString(coords);
final Geometry concaveHull1 = cg.createHullFromGeometry(
lineString1,
Arrays.asList(coords[0]),
true);
assertEquals(
"straigh line size=" + i + " geo=" + lineString1.toText(),
2,
concaveHull1.getCoordinates().length);
}
final Geometry lineString3 = factory.createLineString(new Coordinate[] {
new Coordinate(
1,
1),
new Coordinate(
2,
2),
new Coordinate(
3,
1)
});
final Geometry concaveHull3 = cg.createHullFromGeometry(
lineString3,
Arrays.asList(lineString3.getCoordinates()[0]),
true);
assertEquals(
"expecting a triangle " + concaveHull3.toText(),
4,
concaveHull3.getCoordinates().length);
assertTrue(
"expecting a triangle " + concaveHull3.toText(),
concaveHull3.getArea() > 0.0);
assertTrue(concaveHull3.isSimple());
assertEquals(
"expecting identical result",
lineString3,
cg.createHullFromGeometry(
lineString3,
Collections.<Coordinate> emptyList(),
true));
final Geometry[] newPoints = new Geometry[900];
for (int j = 0; j < 10; j++) {
final Coordinate[] newCoords = new Coordinate[900];
final Coordinate[] geoCoords = new Coordinate[100];
final Random rand = new Random(
73634 + j);
for (int i = 0; i < 100; i++) {
geoCoords[i] = new Coordinate(
rand.nextGaussian() * 0.001,
rand.nextGaussian() * 0.001);
}
for (int i = 0; i < 900; i++) {
newCoords[i] = new Coordinate(
rand.nextGaussian() * 0.01,
rand.nextGaussian() * 0.01);
newPoints[i] = factory.createPoint(newCoords[i]);
}
ConvexHull hull = new ConvexHull(
geoCoords,
factory);
final Geometry concaveHull = cg.createHullFromGeometry(
hull.getConvexHull(),
Arrays.asList(newCoords),
true);
assertTrue(concaveHull.isSimple());
int error = 0;
for (Geometry newPoint : newPoints) {
error += concaveHull.intersects(newPoint) ? 0 : 1;
}
assertTrue(error < 3);
final Geometry concaveHull2 = cg.createHullFromGeometry(
hull.getConvexHull(),
Arrays.asList(newCoords),
false);
assertTrue(concaveHull2.isSimple());
error = 0;
for (Geometry newPoint : newPoints) {
error += concaveHull2.intersects(newPoint) ? 0 : 1;
}
assertTrue(error < 1);
}
}
}