Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/com/github/davidmoten/rtree2/RTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.github.davidmoten.rtree2.geometry.Intersects;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.Comparators;
import com.github.davidmoten.rtree2.internal.NodeAndEntries;
Expand Down Expand Up @@ -674,6 +675,10 @@ public Iterable<Entry<T, S>> search(Circle circle) {
return search(circle, Intersects.geometryIntersectsCircle);
}

public Iterable<Entry<T, S>> search(Polygon polygon) {
return search(polygon, Intersects.geometryIntersectsPolygon);
}

public Iterable<Entry<T, S>> search(Line line) {
return search(line, Intersects.geometryIntersectsLine);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface Circle extends Geometry {

boolean intersects(Line line);

boolean intersects(Polygon polygon);

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.github.davidmoten.rtree2.geometry.internal.LineFloat;
import com.github.davidmoten.rtree2.geometry.internal.PointDouble;
import com.github.davidmoten.rtree2.geometry.internal.PointFloat;
import com.github.davidmoten.rtree2.geometry.internal.PolygonDouble;
import com.github.davidmoten.rtree2.geometry.internal.RectangleDouble;
import com.github.davidmoten.rtree2.geometry.internal.RectangleFloat;

Expand Down Expand Up @@ -58,6 +59,10 @@ private static Rectangle rectangleDouble(double x1, double y1, double x2, double
return RectangleDouble.create(x1, y1, x2, y2);
}

public static Polygon polygon(double[] coordinates) {
return PolygonDouble.create(coordinates);
}

public static Circle circle(double x, double y, double radius) {
return CircleDouble.create(x, y, radius);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,27 @@ else if (geometry instanceof Point)
return line.intersects((Point) geometry);
else if (geometry instanceof Rectangle)
return line.intersects((Rectangle) geometry);
else if (geometry instanceof Polygon)
return line.intersects((Polygon) geometry);
else
throw new RuntimeException("unrecognized geometry: " + geometry);
}
};

public static final BiPredicate<Geometry, Polygon> geometryIntersectsPolygon = new BiPredicate<Geometry, Polygon>() {

@Override
public boolean test(Geometry geometry, Polygon polygon) {
if (geometry instanceof Line)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing check on geometry being instanceof Polygon. Also required on other geometryIntersects* methods

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what you mean (latest commit)?

return polygon.intersects((Line) geometry);
else if (geometry instanceof Circle)
return polygon.intersects((Circle) geometry);
else if (geometry instanceof Point)
return polygon.intersects((Point) geometry);
else if (geometry instanceof Rectangle)
return polygon.intersects((Rectangle) geometry);
else if (geometry instanceof Polygon)
return polygon.intersects((Polygon) geometry);
else
throw new RuntimeException("unrecognized geometry: " + geometry);
}
Expand All @@ -123,6 +144,8 @@ else if (geometry instanceof Point)
return circle.intersects((Point) geometry);
else if (geometry instanceof Rectangle)
return circle.intersects((Rectangle) geometry);
else if (geometry instanceof Polygon)
return circle.intersects((Polygon) geometry);
else
throw new RuntimeException("unrecognized geometry: " + geometry);
}
Expand All @@ -147,7 +170,9 @@ else if (geometry instanceof Circle)
else if (geometry instanceof Point)
return geometry.intersects(r);
else if (geometry instanceof Rectangle)
return r.intersects((Rectangle) geometry);
Comment thread
viktor76525 marked this conversation as resolved.
return geometry.intersects(r);
else if (geometry instanceof Polygon)
return geometry.intersects(r);
else
throw new RuntimeException("unrecognized geometry: " + geometry);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/github/davidmoten/rtree2/geometry/Line.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface Line extends Geometry {

boolean intersects(Circle circle);

boolean intersects(Polygon polygon);

}
13 changes: 13 additions & 0 deletions src/main/java/com/github/davidmoten/rtree2/geometry/Polygon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.davidmoten.rtree2.geometry;

public interface Polygon extends Geometry {

boolean intersects(Line b);

boolean intersects(Point point);

boolean intersects(Circle circle);

boolean intersects(Polygon polygon);

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.github.davidmoten.rtree2.geometry.Circle;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.util.ObjectsHelper;

Expand Down Expand Up @@ -59,6 +60,11 @@ public boolean intersects(Circle c) {
return GeometryUtil.distanceSquared(x, y, c.x(), c.y()) <= total * total;
}

@Override
public boolean intersects(Polygon p) {
return p.intersects(this);
}

@Override
public int hashCode() {
return Objects.hashCode(x, y, radius);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.github.davidmoten.rtree2.geometry.Circle;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.util.ObjectsHelper;

Expand Down Expand Up @@ -59,6 +60,11 @@ public boolean intersects(Circle c) {
return GeometryUtil.distanceSquared(x, y, c.x(), c.y()) <= total * total;
}

@Override
public boolean intersects(Polygon p) {
return p.intersects(this);
}

@Override
public int hashCode() {
return Objects.hashCode(x, y, radius);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.github.davidmoten.rtree2.geometry.Geometries;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.Line2D;
import com.github.davidmoten.rtree2.internal.RectangleUtil;
Expand Down Expand Up @@ -113,6 +114,11 @@ public boolean intersects(Circle circle) {
return GeometryUtil.lineIntersects(x1, y1, x2, y2, circle);
}

@Override
public boolean intersects(Polygon p) {
return p.intersects(this);
}

@Override
public int hashCode() {
return Objects.hashCode(x1, y1, x2, y2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.github.davidmoten.rtree2.geometry.Geometries;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.Line2D;
import com.github.davidmoten.rtree2.internal.RectangleUtil;
Expand Down Expand Up @@ -113,6 +114,11 @@ public boolean intersects(Circle circle) {
return GeometryUtil.lineIntersects(x1, y1, x2, y2, circle);
}

@Override
public boolean intersects(Polygon p) {
return p.intersects(this);
}

@Override
public int hashCode() {
return Objects.hashCode(x1, y1, x2, y2);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package com.github.davidmoten.rtree2.geometry.internal;

import com.github.davidmoten.rtree2.geometry.Circle;
import com.github.davidmoten.rtree2.geometry.Line;
import com.github.davidmoten.rtree2.geometry.Point;
import com.github.davidmoten.rtree2.geometry.Polygon;
import com.github.davidmoten.rtree2.geometry.Rectangle;
import com.github.davidmoten.rtree2.internal.util.ObjectsHelper;

import java.util.ArrayList;

/**
* A polygon shell.
*/
public final class PolygonDouble implements Polygon {

private final ArrayList<PointDouble> points;
private final Rectangle mbr;

private static final double PRECISION = 0.00000001;

private PolygonDouble(double[] coordinates) {
Comment thread
viktor76525 marked this conversation as resolved.
if (coordinates.length % 2 != 0 || coordinates.length < 6)
throw new IllegalArgumentException("expecting an even number of coordinate points of at least 6");

points = new ArrayList<>();
double minX = coordinates[0];
double maxX = coordinates[0];
double minY = coordinates[1];
double maxY = coordinates[1];
PointDouble lastPoint = PointDouble.create(coordinates[0], coordinates[1]);
points.add(lastPoint);
for (int i = 2; i < coordinates.length - 1; i += 2) {
// be nice to users and just ignore duplicate points
if (coordinates[i] == lastPoint.x() && coordinates[i + 1] == lastPoint.y()) continue;
minX = Math.min(minX, coordinates[i]);
maxX = Math.max(maxX, coordinates[i]);
minY = Math.min(minY, coordinates[i + 1]);
maxY = Math.max(maxY, coordinates[i + 1]);
lastPoint = PointDouble.create(coordinates[i], coordinates[i + 1]);
points.add(lastPoint);
}
// don't break if the polygon was closed
if (points.get(0).equals(lastPoint))
points.remove(points.size() - 1);
// final check to make sure the polygon remains valid after removing duplicates
if (points.size() < 3) throw new IllegalArgumentException("fewer than 3 unique polygon points provided");
// currently the implementation is only for convex polygons
if (!convexPoints()) throw new UnsupportedOperationException("only implemented for convex polygons");
mbr = RectangleDouble.create(minX, minY, maxX, maxY);
}

/**
* Constructor for a polygon shell.
* @param coordinates An array of x1, y1, x2, y2, ... xn, yn that make the un-closed shell of a convex polygon.
* As it is a polygon, at least 3 pairs of points are required (6 values).
* @return convex polygon shell
* @throws IllegalArgumentException if there are not enough unique points or there is an odd amount of points
* @throws UnsupportedOperationException if the resulting polygon is not convex
*/
public static PolygonDouble create(double[] coordinates) {
Comment thread
viktor76525 marked this conversation as resolved.
return new PolygonDouble(coordinates);
}

@Override
public Rectangle mbr() {
return mbr;
}

@Override
public double distance(Rectangle r) {
throw new UnsupportedOperationException("not implemented");
}

@Override
public boolean intersects(Rectangle r) {
throw new UnsupportedOperationException("not implemented");
}

@Override
public boolean intersects(Circle c) {
throw new UnsupportedOperationException("not implemented");
}

@Override
public boolean intersects(Polygon p) {
throw new UnsupportedOperationException("not implemented");
}

@Override
public int hashCode() {
return points.hashCode();
}

@Override
public boolean equals(Object obj) {
PolygonDouble other = ObjectsHelper.asClass(obj, PolygonDouble.class);
if (other!=null) {
return points.equals(other.points);
} else
return false;
}

@Override
public boolean intersects(Point point) {
int n = points.size();
Comment thread
davidmoten marked this conversation as resolved.

int firstDirection = pointsDirection(points.get(n - 1), point, points.get(0));
if (firstDirection == 0)
return true;

for (int i = 0; i < n - 1; i++) {
int direction = pointsDirection(points.get(i), point, points.get(i + 1));
if (direction == 0) return true;
if (direction != firstDirection) return false;
}

return true;
}

private boolean convexPoints() {
int n = points.size();
int overallDirection = 0;

for (int i = 0; i < n - 1; i++) {
int direction = pointsDirection(points.get(i), points.get((i + 2) % n), points.get(i + 1));
if (direction != 0) {
if (overallDirection == 0) overallDirection = direction;
else if (direction != overallDirection) return false;
}
}
return true;
}

@Override
public boolean intersects(Line line) {
// handle the case when the line is fully inside the polygon
if (intersects(PointDouble.create(line.x1(), line.y1())))
Comment thread
viktor76525 marked this conversation as resolved.
return true;
// check if the line intersects any of the edges of this polygon
int n = points.size();
for (int i = 0; i < n; i++) {
PointDouble cur = points.get(i);
PointDouble next = points.get((i + 1) % n);
LineDouble edge = LineDouble.create(cur.x(), cur.y(), next.x(), next.y());
if (line.intersects(edge))
return true;
}
return false;
}

@Override
public boolean isDoublePrecision() {
return true;
}

/**
* Return the orientation of point b relative to the path a -> c.
* @param a origin point
* @param b point to determine if it is to the left, right or along the path
* @param c point we are facing from a
* @return 0 if b is on the line a->c, -1 if b is to the left of a facing c or 1 if to the right.
*/
private static int pointsDirection(Point a, Point b, Point c) {
double crossProduct = (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
if (Math.abs(crossProduct) < PRECISION) return 0;
return crossProduct > 0.0 ? 1 : -1;
}
}
12 changes: 12 additions & 0 deletions src/test/java/com/github/davidmoten/rtree2/RTreeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,18 @@ public void testSearchWithLineFindsNone() {
assertEquals(0, list.size());
}

@Test
public void testSearchWithPolygonFindsPoint() {
RTree<Integer, Point> tree = RTree.<Integer, Point>create().add(
1, point(1, 1)).add(2, point(2, 2)).add(3, point(3, 3)
);
List<Entry<Integer, Point>> list = Iterables.toList(tree.search(
Geometries.polygon(new double[] {1.5, 1.5, 1.5, 2.5, 2.5, 2.5, 2.5, 1.5}))
);
assertEquals(1, list.size());
assertEquals(2, (int) list.get(0).value());
}

@Test
public void testRTreeRootMbrWhenRTreeEmpty() {
assertFalse(RTree.create().mbr().isPresent());
Expand Down
Loading