LUCENE-8220: Switch over to using GeoComplexPolygon if we can't tile a polygon.

This commit is contained in:
Karl Wright 2018-03-25 08:17:08 -04:00
parent 5e1d6f36f7
commit 273a829c46
2 changed files with 48 additions and 32 deletions

View File

@ -164,32 +164,44 @@ public class GeoPolygonFactory {
return null;
}
//First approximation to find a point
final GeoPoint centerOfMass = getCenterOfMass(planetModel, filteredPointList);
final Boolean isCenterOfMassInside = isInsidePolygon(centerOfMass, filteredPointList);
if (isCenterOfMassInside != null) {
return generateGeoPolygon(planetModel, filteredPointList, holes, centerOfMass, isCenterOfMassInside);
}
//System.err.println("points="+pointList);
// Create a random number generator. Effectively this furnishes us with a repeatable sequence
// of points to use for poles.
final Random generator = new Random(1234);
for (int counter = 0; counter < 1000000; counter++) {
//counter++;
// Pick the next random pole
final GeoPoint pole = pickPole(generator, planetModel, filteredPointList);
// Is it inside or outside?
final Boolean isPoleInside = isInsidePolygon(pole, filteredPointList);
if (isPoleInside != null) {
// Legal pole
//System.out.println("Took "+counter+" iterations to find pole");
//System.out.println("Pole = "+pole+"; isInside="+isPoleInside+"; pointList = "+pointList);
return generateGeoPolygon(planetModel, filteredPointList, holes, pole, isPoleInside);
try {
//First approximation to find a point
final GeoPoint centerOfMass = getCenterOfMass(planetModel, filteredPointList);
final Boolean isCenterOfMassInside = isInsidePolygon(centerOfMass, filteredPointList);
if (isCenterOfMassInside != null) {
return generateGeoPolygon(planetModel, filteredPointList, holes, centerOfMass, isCenterOfMassInside);
}
// If pole choice was illegal, try another one
//System.err.println("points="+pointList);
// Create a random number generator. Effectively this furnishes us with a repeatable sequence
// of points to use for poles.
final Random generator = new Random(1234);
for (int counter = 0; counter < 1000000; counter++) {
//counter++;
// Pick the next random pole
final GeoPoint pole = pickPole(generator, planetModel, filteredPointList);
// Is it inside or outside?
final Boolean isPoleInside = isInsidePolygon(pole, filteredPointList);
if (isPoleInside != null) {
// Legal pole
//System.out.println("Took "+counter+" iterations to find pole");
//System.out.println("Pole = "+pole+"; isInside="+isPoleInside+"; pointList = "+pointList);
return generateGeoPolygon(planetModel, filteredPointList, holes, pole, isPoleInside);
}
// If pole choice was illegal, try another one
}
throw new IllegalArgumentException("cannot find a point that is inside the polygon "+filteredPointList);
} catch (TileException e) {
// Couldn't tile the polygon; use GeoComplexPolygon instead, if we can.
if (holes != null && holes.size() > 0) {
// We currently cannot get the list of points that went into making a hole back out, so don't allow this case.
// In order to support it, we really need to change the API contract, which is a bigger deal.
throw new IllegalArgumentException(e.getMessage());
}
final List<PolygonDescription> description = new ArrayList<>(1);
description.add(new PolygonDescription(pointList));
return makeLargeGeoPolygon(planetModel, description);
}
throw new IllegalArgumentException("cannot find a point that is inside the polygon "+filteredPointList);
}
/** Generate a point at the center of mass of a list of points.
@ -372,7 +384,7 @@ public class GeoPolygonFactory {
final List<GeoPoint> filteredPointList,
final List<GeoPolygon> holes,
final GeoPoint testPoint,
final boolean testPointInside) {
final boolean testPointInside) throws TileException {
// We will be trying twice to find the right GeoPolygon, using alternate siding choices for the first polygon
// side. While this looks like it might be 2x as expensive as it could be, there's really no other choice I can
// find.
@ -750,7 +762,7 @@ public class GeoPolygonFactory {
final int endPointIndex,
final SidedPlane startingEdge,
final List<GeoPolygon> holes,
final GeoPoint testPoint) {
final GeoPoint testPoint) throws TileException {
// It could be the case that we need a concave polygon. So we need to try and look for that case
// as part of the general code for constructing complex polygons.
@ -1003,7 +1015,7 @@ public class GeoPolygonFactory {
if (foundBadEdge) {
// Unaddressed bad edge
throw new IllegalArgumentException("Could not tile polygon; found a pathological coplanarity that couldn't be addressed");
throw new TileException("Could not tile polygon; found a pathological coplanarity that couldn't be addressed");
}
// No violations found: we know it's a legal concave polygon.
@ -1686,4 +1698,11 @@ public class GeoPolygonFactory {
public boolean value = false;
}
/** Exception we throw when we can't tile a polygon due to numerical precision issues.
*/
private static class TileException extends Exception {
public TileException(final String msg) {
super(msg);
}
}
}

View File

@ -22,7 +22,6 @@ import java.util.BitSet;
import java.util.Collections;
import org.junit.Test;
import org.junit.Ignore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@ -623,7 +622,7 @@ shape:
}
@Test
public void testPolygonFactoryCase3() {
public void testPolygonFactoryCase3() throws Exception {
/*
This one failed to be detected as convex:
@ -1102,7 +1101,6 @@ shape:
}
@Test
@Ignore
public void testCoplanarityTileConvex() throws Exception {
// This test has been disabled because it is possible that the polygon specified actually intersects itself.
//POLYGON((24.39398 65.77519,24.3941 65.77498,24.394024 65.77497,24.393976 65.77495,24.393963 65.77493,24.394068 65.774925,24.394156 65.77495,24.394201 65.77495,24.394234 65.77496,24.394266 65.77498,24.394318 65.77498,24.39434 65.774956,24.394377 65.77495,24.394451 65.77494,24.394476 65.77495,24.394457 65.77498,24.39398 65.77519))"
@ -1123,12 +1121,11 @@ shape:
points.add(new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(65.77494), Geo3DUtil.fromDegrees(24.394451)));
points.add(new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(65.77495), Geo3DUtil.fromDegrees(24.394476)));
points.add(new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(65.77498), Geo3DUtil.fromDegrees(24.394457)));
GeoCompositePolygon polygon = (GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
assertTrue(polygon != null);
}
@Test
@Ignore
public void testCoplanarityConcave() throws Exception {
//POLYGON((-52.18851 64.53777,-52.18853 64.53828,-52.18675 64.53829,-52.18676 64.53855,-52.18736 64.53855,-52.18737 64.53881,-52.18677 64.53881,-52.18683 64.54009,-52.18919 64.53981,-52.18916 64.53905,-52.19093 64.53878,-52.19148 64.53775,-52.18851 64.53777))
List<GeoPoint> points = new ArrayList<>();