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 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
146 changes: 78 additions & 68 deletions server/src/main/java/org/elasticsearch/common/geo/EdgeTreeReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,71 +18,78 @@
*/
package org.elasticsearch.common.geo;

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;
final int startPosition;

public EdgeTreeReader(StreamInput input) throws IOException {
int treeBytesSize = input.readVInt();
this.bytesRef = input.readBytesRef(treeBytesSize);
public EdgeTreeReader(ByteBufferStreamInput input) throws IOException {
this.startPosition = input.position();
this.input = input;
}

public Extent getExtent() throws IOException {
resetInputPosition();
return new Extent(input);
}

/**
* 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) || this.crosses(extent);
}

boolean containsBottomLeft(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();
static Optional<Boolean> checkExtent(StreamInput input, Extent extent) throws IOException {
Extent edgeExtent = new Extent(input);

if (thisMinY > maxY || thisMaxX < minX || thisMaxY < minY || thisMinX > maxX) {
return false; // tree and bbox-query are disjoint
if (edgeExtent.minY > extent.maxY || edgeExtent.maxX < extent.minX
|| edgeExtent.maxY < extent.minY || edgeExtent.minX > 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 <= edgeExtent.minX && extent.minY <= edgeExtent.minY
&& extent.maxX >= edgeExtent.maxX && extent.maxY >= edgeExtent.maxY) {
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) throws IOException {
resetInputPosition();

if (thisMinY > maxY || thisMaxX < minX || thisMaxY < minY || thisMinX > maxX) {
return false; // tree and bbox-query are disjoint
Optional<Boolean> extentCheck = checkExtent(input, extent);
if (extentCheck.isPresent()) {
return extentCheck.get();
}

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

public boolean crosses(Extent extent) throws IOException {
resetInputPosition();

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 @@ -95,33 +102,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 @@ -130,44 +137,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;
}

private void resetInputPosition() throws IOException {
input.position(startPosition);
}

private static class Edge {
int streamOffset;
int x1;
int y1;
int x2;
int y2;
int minY;
int maxY;
int rightOffset;
private static final class Edge {
final int streamOffset;
final int x1;
final int y1;
final int x2;
final int y2;
final int minY;
final int maxY;
final int rightOffset;

/**
* Object representing an edge node read from bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,69 +34,44 @@ public class EdgeTreeWriter implements Writeable {
*/
static final int EDGE_SIZE_IN_BYTES = 28;

int minX;
int minY;
int maxX;
int maxY;
Extent extent;
final Edge tree;

public EdgeTreeWriter(int[] x, int[] y) {
minX = minY = Integer.MAX_VALUE;
maxX = maxY = Integer.MIN_VALUE;
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
Edge edges[] = new Edge[y.length - 1];
for (int i = 1; i < y.length; i++) {
int y1 = y[i-1];
int x1 = x[i-1];
int y2 = y[i];
int x2 = x[i];
int minY, maxY;
int edgeMinY, edgeMaxY;
if (y1 < y2) {
minY = y1;
maxY = y2;
edgeMinY = y1;
edgeMaxY = y2;
} else {
minY = y2;
maxY = y1;
edgeMinY = y2;
edgeMaxY = y1;
}
edges[i - 1] = new Edge(x1, y1, x2, y2, minY, maxY);
this.minX = Math.min(this.minX, Math.min(x1, x2));
this.minY = Math.min(this.minY, Math.min(y1, y2));
this.maxX = Math.max(this.maxX, Math.max(x1, x2));
this.maxY = Math.max(this.maxY, Math.max(y1, y2));
edges[i - 1] = new Edge(x1, y1, x2, y2, edgeMinY, edgeMaxY);
minX = Math.min(minX, Math.min(x1, x2));
minY = Math.min(minY, Math.min(y1, y2));
maxX = Math.max(maxX, Math.max(x1, x2));
maxY = Math.max(maxY, Math.max(y1, y2));
}
Arrays.sort(edges);
this.extent = new Extent(minX, minY, maxX, maxY);
this.tree = createTree(edges, 0, edges.length - 1);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(4 * 4 + EDGE_SIZE_IN_BYTES * tree.size);
out.writeInt(minX);
out.writeInt(minY);
out.writeInt(maxX);
out.writeInt(maxY);
writeTree(tree, out);
}

private void writeTree(Edge edge, StreamOutput output) throws IOException {
if (edge == null) {
return;
}
output.writeInt(edge.minY);
output.writeInt(edge.maxY);
output.writeInt(edge.x1);
output.writeInt(edge.y1);
output.writeInt(edge.x2);
output.writeInt(edge.y2);
// left node is next node, write offset of right node
if (edge.left != null) {
output.writeInt(edge.left.size * EDGE_SIZE_IN_BYTES);
} else if (edge.right == null){
output.writeInt(-1);
} else {
output.writeInt(0);
}
writeTree(edge.left, output);
writeTree(edge.right, output);
//out.writeVInt(4 * 4 + EDGE_SIZE_IN_BYTES * tree.size);
extent.writeTo(out);
tree.writeTo(out);
}

private static Edge createTree(Edge edges[], int low, int high) {
Expand Down Expand Up @@ -126,7 +101,7 @@ private static Edge createTree(Edge edges[], int low, int high) {
/**
* Object representing an in-memory edge-tree to be serialized
*/
static class Edge implements Comparable<Edge> {
static class Edge implements Comparable<Edge>, Writeable {
final int x1;
final int y1;
final int x2;
Expand Down Expand Up @@ -154,5 +129,29 @@ public int compareTo(Edge other) {
}
return ret;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeInt(minY);
out.writeInt(maxY);
out.writeInt(x1);
out.writeInt(y1);
out.writeInt(x2);
out.writeInt(y2);
// left node is next node, write offset of right node
if (left != null) {
out.writeInt(left.size * EDGE_SIZE_IN_BYTES);
} else if (right == null){
out.writeInt(-1);
} else {
out.writeInt(0);
}
if (left != null) {
left.writeTo(out);
}
if (right != null) {
right.writeTo(out);
}
}
}
}
Loading