DATACOUCH-165 - Add utilities to remove geo false positives

PointInShapeEvaluator can be used to check answers and remove false positives from answers that have been approximated (circle and polygon).

One implementations are provided, based on awt.

Hinted at the existence of the PointInShapeEvaluator in the message that warns about bounding box approximations.

Added mention of the AwtPointInShapeEvaluator in the asciidoc with code sample.
This commit is contained in:
Simon Baslé
2015-10-09 18:42:51 +02:00
parent a2f26f4310
commit c6aa84ced4
6 changed files with 592 additions and 2 deletions

View File

@@ -0,0 +1,232 @@
package org.springframework.data.couchbase.repository.query.support;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
/**
* An abstract base for testing {@link PointInShapeEvaluator} implementations.
* Each implementation can be commonly tested by extending this base and instantiating the evaluator in createEvaluator.
*
* @author Simon Baslé
*/
public abstract class AbstractPointInShapeEvaluatorTest {
public abstract PointInShapeEvaluator createEvaluator();
private PointInShapeEvaluator evaluator;
protected static final class LocatedValue {
public final Point location;
public final String name;
public LocatedValue(Point location, String name) {
this.location = location;
this.name = name;
}
@Override
public String toString() {
return name;
}
}
protected static final Converter<LocatedValue, Point> LOCATED_VALUE_POINT_CONVERTER = new Converter<LocatedValue, Point>() {
@Override
public Point convert(LocatedValue source) {
return source.location;
}
};
@Before
public void init() {
evaluator = createEvaluator();
}
@Test
public void testPointInPolygon() throws Exception {
Polygon openTriangle = new Polygon(
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0)
);
Polygon closedTriangle = new Polygon(
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0),
new Point(1.0, 2.0) //explicitly closing the shape
);
Point inside = new Point(1.6, 1.8);
Point outside = new Point(1.1, 0.3);
Point edge = new Point(1.0, 2.0);
assertTrue("point inside open polygon failed", evaluator.pointInPolygon(inside, openTriangle));
assertFalse("point outside open polygon failed", evaluator.pointInPolygon(outside, openTriangle));
assertFalse("point on edge of open polygon should not be considered within the polygon",
evaluator.pointInPolygon(edge, openTriangle));
assertTrue("point inside closed polygon failed", evaluator.pointInPolygon(inside, closedTriangle));
assertFalse("point outside closed polygon failed", evaluator.pointInPolygon(outside, closedTriangle));
assertFalse("point on edge of closed polygon should not be considered within the polygon",
evaluator.pointInPolygon(edge, closedTriangle));
}
@Test
public void testPointInPolygonArray() throws Exception {
Point[] openTriangle = new Point[] {
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0)
};
Point[] closedTriangle = new Point[] {
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0),
new Point(1.0, 2.0) //explicitly closing the shape
};
Point inside = new Point(1.6, 1.8);
Point outside = new Point(1.1, 0.3);
Point edge = new Point(1.0, 2.0);
assertTrue("point inside open polygon failed", evaluator.pointInPolygon(inside, openTriangle));
assertFalse("point outside open polygon failed", evaluator.pointInPolygon(outside, openTriangle));
assertFalse("point on edge of open polygon should not be considered within the polygon",
evaluator.pointInPolygon(edge, openTriangle));
assertTrue("point inside closed polygon failed", evaluator.pointInPolygon(inside, closedTriangle));
assertFalse("point outside closed polygon failed", evaluator.pointInPolygon(outside, closedTriangle));
assertFalse("point on edge of closed polygon should not be considered within the polygon",
evaluator.pointInPolygon(edge, closedTriangle));
}
@Test
public void testPointInCircle() throws Exception {
Circle circle = new Circle(new Point(0, 0), new Distance(2));
Point inside = new Point(-0.3, 1.4);
Point outside = new Point(1.3, 2d);
Point onEdge = new Point(-2d, 0d);
assertTrue("point inside failed", evaluator.pointInCircle(inside, circle));
assertFalse("point outside failed", evaluator.pointInCircle(outside, circle));
assertTrue("point on edge of circle should be considered within / near", evaluator.pointInCircle(onEdge, circle));
}
@Test
public void testPointInCircleCenterRadius() throws Exception {
Point center = new Point(0, 0);
Distance radius = new Distance(2);
Point inside = new Point(-0.3, 1.8);
Point outside = new Point(1.3, 2d);
Point onEdge = new Point(-2d, 0d);
assertTrue("point inside failed", evaluator.pointInCircle(inside, center, radius));
assertFalse("point outside failed", evaluator.pointInCircle(outside, center, radius));
assertTrue("point on edge of circle should be considered within / near", evaluator.pointInCircle(onEdge, center, radius));
}
@Test
public void testRemoveFalsePositivesPolygon() throws Exception {
Polygon openTriangle = new Polygon(
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0)
);
Polygon closedTriangle = new Polygon(
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0),
new Point(1.0, 2.0) //explicitly closing the shape
);
LocatedValue inside = new LocatedValue(new Point(1.6, 1.8), "inside");
LocatedValue outside = new LocatedValue(new Point(1.1, 0.3), "outside");
LocatedValue edge = new LocatedValue(new Point(1.5, 0d), "edge");
List<LocatedValue> expected = Collections.singletonList(inside);
List<LocatedValue> tested = Arrays.asList(inside, outside, edge);
List<LocatedValue> filteredOpen = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, openTriangle);
List<LocatedValue> filteredClosed = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, closedTriangle);
assertEquals(expected, filteredOpen);
assertEquals(expected, filteredClosed);
}
@Test
public void testRemoveFalsePositivesPolygonArray() throws Exception {
Point[] openTriangle = new Point[] {
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0)
};
Point[] closedTriangle = new Point[] {
new Point(1.0, 2.0),
new Point(2.0, 2.0),
new Point(1.5, 0.0),
new Point(1.0, 2.0) //explicitly closing the shape
};
LocatedValue inside = new LocatedValue(new Point(1.6, 1.8), "inside");
LocatedValue outside = new LocatedValue(new Point(1.1, 0.3), "outside");
LocatedValue edge = new LocatedValue(new Point(1.5, 0d), "edge");
List<LocatedValue> expected = Collections.singletonList(inside);
List<LocatedValue> tested = Arrays.asList(inside, outside, edge);
List<LocatedValue> filteredOpen = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, openTriangle);
List<LocatedValue> filteredClosed = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, closedTriangle);
assertEquals(expected, filteredOpen);
assertEquals(expected, filteredClosed);
}
@Test
public void testRemoveFalsePositivesCircle() throws Exception {
Circle circle = new Circle(new Point(0, 0), new Distance(2));
LocatedValue inside = new LocatedValue(new Point(-0.3, 1.4), "inside");
LocatedValue outside = new LocatedValue(new Point(1.3, 2d), "outside");
LocatedValue edge = new LocatedValue(new Point(-2d, 0d), "edge");
List<LocatedValue> expected = Arrays.asList(inside, edge);
List<LocatedValue> tested = Arrays.asList(inside, outside, edge);
List<LocatedValue> filtered = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, circle);
assertEquals(expected, filtered);
}
@Test
public void testRemoveFalsePositivesCircleCenterRadius() throws Exception {
Point center = new Point(0, 0);
Distance radius = new Distance(2);
LocatedValue inside = new LocatedValue(new Point(-0.3, 1.4), "inside");
LocatedValue outside = new LocatedValue(new Point(1.3, 2d), "outside");
LocatedValue edge = new LocatedValue(new Point(-2d, 0d), "edge");
List<LocatedValue> expected = Arrays.asList(inside, edge);
List<LocatedValue> tested = Arrays.asList(inside, outside, edge);
List<LocatedValue> filtered = evaluator.removeFalsePositives(tested, LOCATED_VALUE_POINT_CONVERTER, center, radius);
assertEquals(expected, filtered);
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.couchbase.repository.query.support;
/**
* Test case for the {@link AwtPointInShapeEvaluator}.
*
* @author Simon Baslé
*/
public class AwtPointInShapeEvaluatorTest extends AbstractPointInShapeEvaluatorTest {
@Override
public PointInShapeEvaluator createEvaluator() {
return new AwtPointInShapeEvaluator();
}
}