Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up Extent logic in Geo Tree readers #42968

Merged
merged 6 commits into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
131 changes: 82 additions & 49 deletions server/src/main/java/org/elasticsearch/common/geo/EdgeTreeReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,67 +20,87 @@

import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
import org.elasticsearch.common.io.stream.StreamInput;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Optional;

import static org.apache.lucene.geo.GeoUtils.lineCrossesLineWithBoundary;

public class EdgeTreeReader {
final BytesRef bytesRef;
final ByteBufferStreamInput input;

public EdgeTreeReader(BytesRef bytesRef) {
this.bytesRef = bytesRef;
talevy marked this conversation as resolved.
Show resolved Hide resolved
this.input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that you merged #42910 that just doesn't look right. Perhaps we can brainstorm a bit on how to improve that.

}

public Extent getExtent() throws IOException {
input.position(0);
int thisMinX = input.readInt();
int thisMinY = input.readInt();
int thisMaxX = input.readInt();
int thisMaxY = input.readInt();
return new Extent(thisMinX, thisMinY, thisMaxX, thisMaxY);
}

/**
* Returns true if the rectangle query and the edge tree's shape overlap
*/
public boolean containedInOrCrosses(int minX, int minY, int maxX, int maxY) throws IOException {
return this.containsBottomLeft(minX, minY, maxX, maxY) || this.crosses(minX, minY, maxX, maxY);
Extent extent = new Extent(minX, minY, maxX, maxY);
return this.containsBottomLeft(extent, true) || this.crosses(extent, true);
}

boolean containsBottomLeft(int minX, int minY, int maxX, int maxY) throws IOException {
ByteBufferStreamInput input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
static Optional<Boolean> checkExtent(StreamInput input, Extent extent) throws IOException {
int thisMinX = input.readInt();
int thisMinY = input.readInt();
int thisMaxX = input.readInt();
int thisMaxY = input.readInt();

if (thisMinY > maxY || thisMaxX < minX || thisMaxY < minY || thisMinX > maxX) {
return false; // tree and bbox-query are disjoint
if (thisMinY > extent.maxY || thisMaxX < extent.minX || thisMaxY < extent.minY || thisMinX > extent.maxX) {
return Optional.of(false); // tree and bbox-query are disjoint
}

if (minX <= thisMinX && minY <= thisMinY && maxX >= thisMaxX && maxY >= thisMaxY) {
return true; // bbox-query fully contains tree's extent.
if (extent.minX <= thisMinX && extent.minY <= thisMinY && extent.maxX >= thisMaxX && extent.maxY >= thisMaxY) {
return Optional.of(true); // bbox-query fully contains tree's extent.
}

return containsBottomLeft(input, readRoot(input, input.position()), minX, minY, maxX, maxY);
return Optional.empty();
}

public boolean crosses(int minX, int minY, int maxX, int maxY) throws IOException {
ByteBufferStreamInput input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
int thisMinX = input.readInt();
int thisMinY = input.readInt();
int thisMaxX = input.readInt();
int thisMaxY = input.readInt();
boolean containsBottomLeft(Extent extent, boolean resetInput) throws IOException {
if (resetInput) {
talevy marked this conversation as resolved.
Show resolved Hide resolved
input.position(0);
}

Optional<Boolean> extentCheck = checkExtent(input, extent);
if (extentCheck.isPresent()) {
return extentCheck.get();
}

return containsBottomLeft(readRoot(input.position()), extent);
}

if (thisMinY > maxY || thisMaxX < minX || thisMaxY < minY || thisMinX > maxX) {
return false; // tree and bbox-query are disjoint
public boolean crosses(Extent extent, boolean resetInput) throws IOException {
if (resetInput) {
input.position(0);
}

if (minX <= thisMinX && minY <= thisMinY && maxX >= thisMaxX && maxY >= thisMaxY) {
return true; // bbox-query fully contains tree's extent.
Optional<Boolean> extentCheck = checkExtent(input, extent);
if (extentCheck.isPresent()) {
return extentCheck.get();
}

return crosses(input, readRoot(input, input.position()), minX, minY, maxX, maxY);
return crosses(readRoot(input.position()), extent);
}

public Edge readRoot(ByteBufferStreamInput input, int position) throws IOException {
return readEdge(input, position);
public Edge readRoot(int position) throws IOException {
return readEdge(position);
}

private static Edge readEdge(ByteBufferStreamInput input, int position) throws IOException {
private Edge readEdge(int position) throws IOException {
input.position(position);
int minY = input.readInt();
int maxY = input.readInt();
Expand All @@ -93,33 +113,33 @@ private static Edge readEdge(ByteBufferStreamInput input, int position) throws I
}


Edge readLeft(ByteBufferStreamInput input, Edge root) throws IOException {
return readEdge(input, root.streamOffset);
Edge readLeft(Edge root) throws IOException {
return readEdge(root.streamOffset);
}

Edge readRight(ByteBufferStreamInput input, Edge root) throws IOException {
return readEdge(input, root.streamOffset + root.rightOffset);
Edge readRight(Edge root) throws IOException {
return readEdge(root.streamOffset + root.rightOffset);
}

/**
* Returns true if the bottom-left point of the rectangle query is contained within the
* tree's edges.
*/
private boolean containsBottomLeft(ByteBufferStreamInput input, Edge root, int minX, int minY, int maxX, int maxY) throws IOException {
private boolean containsBottomLeft(Edge root, Extent extent) throws IOException {
boolean res = false;
if (root.maxY >= minY) {
if (root.maxY >= extent.minY) {
// is bbox-query contained within linearRing
// cast infinite ray to the right from bottom-left of bbox-query to see if it intersects edge
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,minX, minY, Integer.MAX_VALUE, minY)) {
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.minX, extent.minY, Integer.MAX_VALUE, extent.minY)) {
res = true;
}

if (root.rightOffset > 0) { /* has left node */
res ^= containsBottomLeft(input, readLeft(input, root), minX, minY, maxX, maxY);
res ^= containsBottomLeft(readLeft(root), extent);
}

if (root.rightOffset > 0 && maxY >= root.minY) { /* no right node if rightOffset == -1 */
res ^= containsBottomLeft(input, readRight(input, root), minX, minY, maxX, maxY);
if (root.rightOffset > 0 && extent.maxY >= root.minY) { /* no right node if rightOffset == -1 */
res ^= containsBottomLeft(readRight(root), extent);
}
}
return res;
Expand All @@ -128,34 +148,47 @@ private boolean containsBottomLeft(ByteBufferStreamInput input, Edge root, int m
/**
* Returns true if the box crosses any edge in this edge subtree
* */
private boolean crosses(ByteBufferStreamInput input, Edge root, int minX, int minY, int maxX, int maxY) throws IOException {
boolean res = false;
private boolean crosses(Edge root, Extent extent) throws IOException {
// we just have to cross one edge to answer the question, so we descend the tree and return when we do.
if (root.maxY >= minY) {
if (root.maxY >= extent.minY) {

// does rectangle's edges intersect or reside inside polygon's edge
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, minX, minY, maxX, minY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, maxX, minY, maxX, maxY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, maxX, maxY, minX, maxY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, minX, maxY, minX, minY)) {
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
extent.minX, extent.minY, extent.maxX, extent.minY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
extent.maxX, extent.minY, extent.maxX, extent.maxY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
extent.maxX, extent.maxY, extent.minX, extent.maxY) ||
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
extent.minX, extent.maxY, extent.minX, extent.minY)) {
return true;
}

if (root.rightOffset > 0) { /* has left node */
if (crosses(input, readLeft(input, root), minX, minY, maxX, maxY)) {
return true;
}
/* has left node */
if (root.rightOffset > 0 && crosses(readLeft(root), extent)) {
return true;
}

if (root.rightOffset > 0 && maxY >= root.minY) { /* no right node if rightOffset == -1 */
if (crosses(input, readRight(input, root), minX, minY, maxX, maxY)) {
return true;
}
/* no right node if rightOffset == -1 */
if (root.rightOffset > 0 && extent.maxY >= root.minY && crosses(readRight(root), extent)) {
return true;
}
}
return false;
}

static final class Extent {
talevy marked this conversation as resolved.
Show resolved Hide resolved
final int minX;
final int minY;
final int maxX;
final int maxY;

Extent(int minX, int minY, int maxX, int maxY) {
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
}

private static class Edge {
int streamOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Optional;

/**
* A tree reader.
Expand All @@ -33,28 +34,41 @@
*/
public class GeometryTreeReader {

private final BytesRef bytesRef;
private final ByteBufferStreamInput input;

public GeometryTreeReader(BytesRef bytesRef) {
this.bytesRef = bytesRef;
this.input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
}

public boolean containedInOrCrosses(int minLon, int minLat, int maxLon, int maxLat) throws IOException {
ByteBufferStreamInput input = new ByteBufferStreamInput(
ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
public EdgeTreeReader.Extent getExtent() throws IOException {
input.position(0);
boolean hasExtent = input.readBoolean();
if (hasExtent) {
int thisMinLon = input.readInt();
int thisMinLat = input.readInt();
int thisMaxLon = input.readInt();
int thisMaxLat = input.readInt();
return new EdgeTreeReader.Extent(thisMinLon, thisMinLat, thisMaxLon, thisMaxLat);
}
assert input.readVInt() == 1;
ShapeType shapeType = input.readEnum(ShapeType.class);
if (ShapeType.POLYGON.equals(shapeType)) {
BytesRef treeRef = input.readBytesRef();
EdgeTreeReader reader = new EdgeTreeReader(treeRef);
return reader.getExtent();
} else {
throw new UnsupportedOperationException("only polygons supported -- TODO");
}
}

if (thisMinLat > maxLat || thisMaxLon < minLon || thisMaxLat < minLat || thisMinLon > maxLon) {
return false; // tree and bbox-query are disjoint
}

if (minLon <= thisMinLon && minLat <= thisMinLat && maxLon >= thisMaxLon && maxLat >= thisMaxLat) {
return true; // bbox-query fully contains tree's extent.
public boolean containedInOrCrosses(int minLon, int minLat, int maxLon, int maxLat) throws IOException {
input.position(0);
boolean hasExtent = input.readBoolean();
if (hasExtent) {
Optional<Boolean> extentCheck = EdgeTreeReader.checkExtent(input,
new EdgeTreeReader.Extent(minLon, minLat, maxLon, maxLat));
if (extentCheck.isPresent()) {
return extentCheck.get();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void testPacMan() throws Exception {
// test cell crossing poly
EdgeTreeWriter writer = new EdgeTreeWriter(px, py);
EdgeTreeReader reader = new EdgeTreeReader(writer.toBytesRef());
assertTrue(reader.containsBottomLeft(xMin, yMin, xMax, yMax));
assertTrue(reader.containsBottomLeft(new EdgeTreeReader.Extent(xMin, yMin, xMax, yMax), true));
}

private int[] asIntArray(double[] doub) {
Expand Down