Skip to content

Commit f129c33

Browse files
committed
Clean up Extent logic in Geo Tree readers (#42968)
- min and max values of coordinates were difficult to track, this fixes that by introducing a new Extent object - Instead of re-wrapping ByteRef into a StreamInput, a stream input is made once - a new getExtent() method is introduced for use by aggregations like geo_bounds - re-use bounding-box containment checks
1 parent d6d8924 commit f129c33

File tree

7 files changed

+235
-137
lines changed

7 files changed

+235
-137
lines changed

server/src/main/java/org/elasticsearch/common/geo/EdgeTreeReader.java

Lines changed: 78 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,71 +18,78 @@
1818
*/
1919
package org.elasticsearch.common.geo;
2020

21-
import org.apache.lucene.util.BytesRef;
2221
import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
2322
import org.elasticsearch.common.io.stream.StreamInput;
2423

2524
import java.io.IOException;
26-
import java.nio.ByteBuffer;
25+
import java.util.Optional;
2726

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

3029
public class EdgeTreeReader {
31-
final BytesRef bytesRef;
30+
final ByteBufferStreamInput input;
31+
final int startPosition;
3232

33-
public EdgeTreeReader(StreamInput input) throws IOException {
34-
int treeBytesSize = input.readVInt();
35-
this.bytesRef = input.readBytesRef(treeBytesSize);
33+
public EdgeTreeReader(ByteBufferStreamInput input) throws IOException {
34+
this.startPosition = input.position();
35+
this.input = input;
36+
}
37+
38+
public Extent getExtent() throws IOException {
39+
resetInputPosition();
40+
return new Extent(input);
3641
}
3742

3843
/**
3944
* Returns true if the rectangle query and the edge tree's shape overlap
4045
*/
4146
public boolean containedInOrCrosses(int minX, int minY, int maxX, int maxY) throws IOException {
42-
return this.containsBottomLeft(minX, minY, maxX, maxY) || this.crosses(minX, minY, maxX, maxY);
47+
Extent extent = new Extent(minX, minY, maxX, maxY);
48+
return this.containsBottomLeft(extent) || this.crosses(extent);
4349
}
4450

45-
boolean containsBottomLeft(int minX, int minY, int maxX, int maxY) throws IOException {
46-
ByteBufferStreamInput input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
47-
int thisMinX = input.readInt();
48-
int thisMinY = input.readInt();
49-
int thisMaxX = input.readInt();
50-
int thisMaxY = input.readInt();
51+
static Optional<Boolean> checkExtent(StreamInput input, Extent extent) throws IOException {
52+
Extent edgeExtent = new Extent(input);
5153

52-
if (thisMinY > maxY || thisMaxX < minX || thisMaxY < minY || thisMinX > maxX) {
53-
return false; // tree and bbox-query are disjoint
54+
if (edgeExtent.minY > extent.maxY || edgeExtent.maxX < extent.minX
55+
|| edgeExtent.maxY < extent.minY || edgeExtent.minX > extent.maxX) {
56+
return Optional.of(false); // tree and bbox-query are disjoint
5457
}
5558

56-
if (minX <= thisMinX && minY <= thisMinY && maxX >= thisMaxX && maxY >= thisMaxY) {
57-
return true; // bbox-query fully contains tree's extent.
59+
if (extent.minX <= edgeExtent.minX && extent.minY <= edgeExtent.minY
60+
&& extent.maxX >= edgeExtent.maxX && extent.maxY >= edgeExtent.maxY) {
61+
return Optional.of(true); // bbox-query fully contains tree's extent.
5862
}
59-
60-
return containsBottomLeft(input, readRoot(input, input.position()), minX, minY, maxX, maxY);
63+
return Optional.empty();
6164
}
6265

63-
public boolean crosses(int minX, int minY, int maxX, int maxY) throws IOException {
64-
ByteBufferStreamInput input = new ByteBufferStreamInput(ByteBuffer.wrap(bytesRef.bytes, bytesRef.offset, bytesRef.length));
65-
int thisMinX = input.readInt();
66-
int thisMinY = input.readInt();
67-
int thisMaxX = input.readInt();
68-
int thisMaxY = input.readInt();
66+
boolean containsBottomLeft(Extent extent) throws IOException {
67+
resetInputPosition();
6968

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

74-
if (minX <= thisMinX && minY <= thisMinY && maxX >= thisMaxX && maxY >= thisMaxY) {
75-
return true; // bbox-query fully contains tree's extent.
74+
return containsBottomLeft(readRoot(input.position()), extent);
75+
}
76+
77+
public boolean crosses(Extent extent) throws IOException {
78+
resetInputPosition();
79+
80+
Optional<Boolean> extentCheck = checkExtent(input, extent);
81+
if (extentCheck.isPresent()) {
82+
return extentCheck.get();
7683
}
7784

78-
return crosses(input, readRoot(input, input.position()), minX, minY, maxX, maxY);
85+
return crosses(readRoot(input.position()), extent);
7986
}
8087

81-
public Edge readRoot(ByteBufferStreamInput input, int position) throws IOException {
82-
return readEdge(input, position);
88+
public Edge readRoot(int position) throws IOException {
89+
return readEdge(position);
8390
}
8491

85-
private static Edge readEdge(ByteBufferStreamInput input, int position) throws IOException {
92+
private Edge readEdge(int position) throws IOException {
8693
input.position(position);
8794
int minY = input.readInt();
8895
int maxY = input.readInt();
@@ -95,33 +102,33 @@ private static Edge readEdge(ByteBufferStreamInput input, int position) throws I
95102
}
96103

97104

98-
Edge readLeft(ByteBufferStreamInput input, Edge root) throws IOException {
99-
return readEdge(input, root.streamOffset);
105+
Edge readLeft(Edge root) throws IOException {
106+
return readEdge(root.streamOffset);
100107
}
101108

102-
Edge readRight(ByteBufferStreamInput input, Edge root) throws IOException {
103-
return readEdge(input, root.streamOffset + root.rightOffset);
109+
Edge readRight(Edge root) throws IOException {
110+
return readEdge(root.streamOffset + root.rightOffset);
104111
}
105112

106113
/**
107114
* Returns true if the bottom-left point of the rectangle query is contained within the
108115
* tree's edges.
109116
*/
110-
private boolean containsBottomLeft(ByteBufferStreamInput input, Edge root, int minX, int minY, int maxX, int maxY) throws IOException {
117+
private boolean containsBottomLeft(Edge root, Extent extent) throws IOException {
111118
boolean res = false;
112-
if (root.maxY >= minY) {
119+
if (root.maxY >= extent.minY) {
113120
// is bbox-query contained within linearRing
114121
// cast infinite ray to the right from bottom-left of bbox-query to see if it intersects edge
115-
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,minX, minY, Integer.MAX_VALUE, minY)) {
122+
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.minX, extent.minY, Integer.MAX_VALUE, extent.minY)) {
116123
res = true;
117124
}
118125

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

123-
if (root.rightOffset > 0 && maxY >= root.minY) { /* no right node if rightOffset == -1 */
124-
res ^= containsBottomLeft(input, readRight(input, root), minX, minY, maxX, maxY);
130+
if (root.rightOffset > 0 && extent.maxY >= root.minY) { /* no right node if rightOffset == -1 */
131+
res ^= containsBottomLeft(readRight(root), extent);
125132
}
126133
}
127134
return res;
@@ -130,44 +137,47 @@ private boolean containsBottomLeft(ByteBufferStreamInput input, Edge root, int m
130137
/**
131138
* Returns true if the box crosses any edge in this edge subtree
132139
* */
133-
private boolean crosses(ByteBufferStreamInput input, Edge root, int minX, int minY, int maxX, int maxY) throws IOException {
134-
boolean res = false;
140+
private boolean crosses(Edge root, Extent extent) throws IOException {
135141
// we just have to cross one edge to answer the question, so we descend the tree and return when we do.
136-
if (root.maxY >= minY) {
142+
if (root.maxY >= extent.minY) {
137143

138144
// does rectangle's edges intersect or reside inside polygon's edge
139-
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, minX, minY, maxX, minY) ||
140-
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, maxX, minY, maxX, maxY) ||
141-
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, maxX, maxY, minX, maxY) ||
142-
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, minX, maxY, minX, minY)) {
145+
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
146+
extent.minX, extent.minY, extent.maxX, extent.minY) ||
147+
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
148+
extent.maxX, extent.minY, extent.maxX, extent.maxY) ||
149+
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
150+
extent.maxX, extent.maxY, extent.minX, extent.maxY) ||
151+
lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2,
152+
extent.minX, extent.maxY, extent.minX, extent.minY)) {
143153
return true;
144154
}
145-
146-
if (root.rightOffset > 0) { /* has left node */
147-
if (crosses(input, readLeft(input, root), minX, minY, maxX, maxY)) {
148-
return true;
149-
}
155+
/* has left node */
156+
if (root.rightOffset > 0 && crosses(readLeft(root), extent)) {
157+
return true;
150158
}
151159

152-
if (root.rightOffset > 0 && maxY >= root.minY) { /* no right node if rightOffset == -1 */
153-
if (crosses(input, readRight(input, root), minX, minY, maxX, maxY)) {
154-
return true;
155-
}
160+
/* no right node if rightOffset == -1 */
161+
if (root.rightOffset > 0 && extent.maxY >= root.minY && crosses(readRight(root), extent)) {
162+
return true;
156163
}
157164
}
158165
return false;
159166
}
160167

168+
private void resetInputPosition() throws IOException {
169+
input.position(startPosition);
170+
}
161171

162-
private static class Edge {
163-
int streamOffset;
164-
int x1;
165-
int y1;
166-
int x2;
167-
int y2;
168-
int minY;
169-
int maxY;
170-
int rightOffset;
172+
private static final class Edge {
173+
final int streamOffset;
174+
final int x1;
175+
final int y1;
176+
final int x2;
177+
final int y2;
178+
final int minY;
179+
final int maxY;
180+
final int rightOffset;
171181

172182
/**
173183
* Object representing an edge node read from bytes

server/src/main/java/org/elasticsearch/common/geo/EdgeTreeWriter.java

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,69 +34,44 @@ public class EdgeTreeWriter implements Writeable {
3434
*/
3535
static final int EDGE_SIZE_IN_BYTES = 28;
3636

37-
int minX;
38-
int minY;
39-
int maxX;
40-
int maxY;
37+
Extent extent;
4138
final Edge tree;
4239

4340
public EdgeTreeWriter(int[] x, int[] y) {
44-
minX = minY = Integer.MAX_VALUE;
45-
maxX = maxY = Integer.MIN_VALUE;
41+
int minX = Integer.MAX_VALUE;
42+
int minY = Integer.MAX_VALUE;
43+
int maxX = Integer.MIN_VALUE;
44+
int maxY = Integer.MIN_VALUE;
4645
Edge edges[] = new Edge[y.length - 1];
4746
for (int i = 1; i < y.length; i++) {
4847
int y1 = y[i-1];
4948
int x1 = x[i-1];
5049
int y2 = y[i];
5150
int x2 = x[i];
52-
int minY, maxY;
51+
int edgeMinY, edgeMaxY;
5352
if (y1 < y2) {
54-
minY = y1;
55-
maxY = y2;
53+
edgeMinY = y1;
54+
edgeMaxY = y2;
5655
} else {
57-
minY = y2;
58-
maxY = y1;
56+
edgeMinY = y2;
57+
edgeMaxY = y1;
5958
}
60-
edges[i - 1] = new Edge(x1, y1, x2, y2, minY, maxY);
61-
this.minX = Math.min(this.minX, Math.min(x1, x2));
62-
this.minY = Math.min(this.minY, Math.min(y1, y2));
63-
this.maxX = Math.max(this.maxX, Math.max(x1, x2));
64-
this.maxY = Math.max(this.maxY, Math.max(y1, y2));
59+
edges[i - 1] = new Edge(x1, y1, x2, y2, edgeMinY, edgeMaxY);
60+
minX = Math.min(minX, Math.min(x1, x2));
61+
minY = Math.min(minY, Math.min(y1, y2));
62+
maxX = Math.max(maxX, Math.max(x1, x2));
63+
maxY = Math.max(maxY, Math.max(y1, y2));
6564
}
6665
Arrays.sort(edges);
66+
this.extent = new Extent(minX, minY, maxX, maxY);
6767
this.tree = createTree(edges, 0, edges.length - 1);
6868
}
6969

7070
@Override
7171
public void writeTo(StreamOutput out) throws IOException {
72-
out.writeVInt(4 * 4 + EDGE_SIZE_IN_BYTES * tree.size);
73-
out.writeInt(minX);
74-
out.writeInt(minY);
75-
out.writeInt(maxX);
76-
out.writeInt(maxY);
77-
writeTree(tree, out);
78-
}
79-
80-
private void writeTree(Edge edge, StreamOutput output) throws IOException {
81-
if (edge == null) {
82-
return;
83-
}
84-
output.writeInt(edge.minY);
85-
output.writeInt(edge.maxY);
86-
output.writeInt(edge.x1);
87-
output.writeInt(edge.y1);
88-
output.writeInt(edge.x2);
89-
output.writeInt(edge.y2);
90-
// left node is next node, write offset of right node
91-
if (edge.left != null) {
92-
output.writeInt(edge.left.size * EDGE_SIZE_IN_BYTES);
93-
} else if (edge.right == null){
94-
output.writeInt(-1);
95-
} else {
96-
output.writeInt(0);
97-
}
98-
writeTree(edge.left, output);
99-
writeTree(edge.right, output);
72+
//out.writeVInt(4 * 4 + EDGE_SIZE_IN_BYTES * tree.size);
73+
extent.writeTo(out);
74+
tree.writeTo(out);
10075
}
10176

10277
private static Edge createTree(Edge edges[], int low, int high) {
@@ -126,7 +101,7 @@ private static Edge createTree(Edge edges[], int low, int high) {
126101
/**
127102
* Object representing an in-memory edge-tree to be serialized
128103
*/
129-
static class Edge implements Comparable<Edge> {
104+
static class Edge implements Comparable<Edge>, Writeable {
130105
final int x1;
131106
final int y1;
132107
final int x2;
@@ -154,5 +129,29 @@ public int compareTo(Edge other) {
154129
}
155130
return ret;
156131
}
132+
133+
@Override
134+
public void writeTo(StreamOutput out) throws IOException {
135+
out.writeInt(minY);
136+
out.writeInt(maxY);
137+
out.writeInt(x1);
138+
out.writeInt(y1);
139+
out.writeInt(x2);
140+
out.writeInt(y2);
141+
// left node is next node, write offset of right node
142+
if (left != null) {
143+
out.writeInt(left.size * EDGE_SIZE_IN_BYTES);
144+
} else if (right == null){
145+
out.writeInt(-1);
146+
} else {
147+
out.writeInt(0);
148+
}
149+
if (left != null) {
150+
left.writeTo(out);
151+
}
152+
if (right != null) {
153+
right.writeTo(out);
154+
}
155+
}
157156
}
158157
}

0 commit comments

Comments
 (0)