Skip to content

Commit

Permalink
Rasterize shape during geotilegrid computation (#49065)
Browse files Browse the repository at this point in the history
This PR mainly modifies the existing GeoTileGridTiler to rasterize
the GeometryTree instead of iterating through all the tiles found in the 
bounding box of the shape.

This PR also fixes a bug where containsFully was not being calculated correctly and simplifies all the relating logic to one `relate` method

relates #37206.
  • Loading branch information
talevy authored Nov 22, 2019
1 parent b560af1 commit 5fed23d
Show file tree
Hide file tree
Showing 15 changed files with 471 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class EdgeTreeReader implements ShapeTreeReader {
private final ByteBufferStreamInput input;
private final int startPosition;
private final boolean hasArea;
private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false);
private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true);
private static final Optional<Boolean> OPTIONAL_EMPTY = Optional.empty();

public EdgeTreeReader(ByteBufferStreamInput input, boolean hasArea) throws IOException {
this.startPosition = input.position();
Expand All @@ -48,25 +51,27 @@ public Extent getExtent() throws IOException {
/**
* Returns true if the rectangle query and the edge tree's shape overlap
*/
public boolean intersects(Extent extent) throws IOException {
if (hasArea) {
return containsBottomLeft(extent) || crosses(extent);
} else {
return crosses(extent);
@Override
public GeoRelation relate(Extent extent) throws IOException {
if (crosses(extent)) {
return GeoRelation.QUERY_CROSSES;
} else if (hasArea && containsBottomLeft(extent)){
return GeoRelation.QUERY_INSIDE;
}
return GeoRelation.QUERY_DISJOINT;
}

static Optional<Boolean> checkExtent(Extent treeExtent, Extent extent) throws IOException {
if (treeExtent.minY() > extent.maxY() || treeExtent.maxX() < extent.minX()
|| treeExtent.maxY() < extent.minY() || treeExtent.minX() > extent.maxX()) {
return Optional.of(false); // tree and bbox-query are disjoint
return OPTIONAL_FALSE; // tree and bbox-query are disjoint
}

if (extent.minX() <= treeExtent.minX() && extent.minY() <= treeExtent.minY()
&& extent.maxX() >= treeExtent.maxX() && extent.maxY() >= treeExtent.maxY()) {
return Optional.of(true); // bbox-query fully contains tree's extent.
return OPTIONAL_TRUE; // bbox-query fully contains tree's extent.
}
return Optional.empty();
return OPTIONAL_EMPTY;
}

boolean containsBottomLeft(Extent extent) throws IOException {
Expand All @@ -80,12 +85,6 @@ boolean containsBottomLeft(Extent extent) throws IOException {
return containsBottomLeft(readRoot(input.position()), extent);
}

boolean containsFully(Extent extent) throws IOException {
resetInputPosition();
input.position(input.position() + Extent.WRITEABLE_SIZE_IN_BYTES); // skip extent
return containsFully(readRoot(input.position()), extent);
}

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

Expand Down Expand Up @@ -151,37 +150,6 @@ private boolean containsBottomLeft(Edge root, Extent extent) throws IOException
return res;
}

/**
* Returns true if every corner in the rectangle query is contained within the tree's edges.
*/
private boolean containsFully(Edge root, Extent extent) throws IOException {
boolean res = false;
if (root.maxY >= extent.minY()) {
// is bbox-query contained within linearRing
// cast infinite ray to the right from each corner of the extent
if (lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.minX(), extent.minY(),
Integer.MAX_VALUE, extent.minY())
&& lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.minX(), extent.maxY(),
Integer.MAX_VALUE, extent.maxY())
&& lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.maxX(), extent.minY(),
Integer.MAX_VALUE, extent.minY())
&& lineCrossesLineWithBoundary(root.x1, root.y1, root.x2, root.y2, extent.maxX(), extent.maxY(),
Integer.MAX_VALUE, extent.maxY())
) {
res = true;
}

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

if (root.rightOffset >= 0 && extent.maxY() >= root.minY) { /* no right node if rightOffset == -1 */
res ^= containsBottomLeft(readRight(root), extent);
}
}
return res;
}

/**
* Returns true if the box crosses any edge in this edge subtree
* */
Expand Down
29 changes: 29 additions & 0 deletions server/src/main/java/org/elasticsearch/common/geo/GeoRelation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.geo;

/**
* Enum for capturing relationships between a shape
* and a query
*/
public enum GeoRelation {
QUERY_CROSSES,
QUERY_INSIDE,
QUERY_DISJOINT
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* This class supports checking bounding box
* relations against the serialized geometry tree.
*/
public class GeometryTreeReader {
public class GeometryTreeReader implements ShapeTreeReader {

private final int extentOffset = 8;
private final ByteBufferStreamInput input;
Expand All @@ -43,6 +43,17 @@ public GeometryTreeReader(BytesRef bytesRef, CoordinateEncoder coordinateEncoder
this.coordinateEncoder = coordinateEncoder;
}

public double getCentroidX() throws IOException {
input.position(0);
return coordinateEncoder.decodeX(input.readInt());
}

public double getCentroidY() throws IOException {
input.position(4);
return coordinateEncoder.decodeY(input.readInt());
}

@Override
public Extent getExtent() throws IOException {
input.position(extentOffset);
Extent extent = input.readOptionalWriteable(Extent::new);
Expand All @@ -55,35 +66,33 @@ public Extent getExtent() throws IOException {
return reader.getExtent();
}

public double getCentroidX() throws IOException {
input.position(0);
return coordinateEncoder.decodeX(input.readInt());
}

public double getCentroidY() throws IOException {
input.position(4);
return coordinateEncoder.decodeY(input.readInt());
}

public boolean intersects(Extent extent) throws IOException {
@Override
public GeoRelation relate(Extent extent) throws IOException {
GeoRelation relation = GeoRelation.QUERY_DISJOINT;
input.position(extentOffset);
boolean hasExtent = input.readBoolean();
if (hasExtent) {
Optional<Boolean> extentCheck = EdgeTreeReader.checkExtent(new Extent(input), extent);
if (extentCheck.isPresent()) {
return extentCheck.get();
return extentCheck.get() ? GeoRelation.QUERY_INSIDE : GeoRelation.QUERY_DISJOINT;
}
}

int numTrees = input.readVInt();
for (int i = 0; i < numTrees; i++) {
ShapeType shapeType = input.readEnum(ShapeType.class);
ShapeTreeReader reader = getReader(shapeType, input);
if (reader.intersects(extent)) {
return true;
GeoRelation shapeRelation = reader.relate(extent);
if (GeoRelation.QUERY_CROSSES == shapeRelation ||
(GeoRelation.QUERY_DISJOINT == shapeRelation && GeoRelation.QUERY_INSIDE == relation)
) {
return GeoRelation.QUERY_CROSSES;
} else {
relation = shapeRelation;
}
}
return false;

return relation;
}

private static ShapeTreeReader getReader(ShapeType shapeType, ByteBufferStreamInput input) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public Extent getExtent() throws IOException {
}
}

public boolean intersects(Extent extent) throws IOException {
@Override
public GeoRelation relate(Extent extent) throws IOException {
Deque<Integer> stack = new ArrayDeque<>();
stack.push(0);
stack.push(size - 1);
Expand All @@ -66,7 +67,7 @@ public boolean intersects(Extent extent) throws IOException {
int x = readX(i);
int y = readY(i);
if (x >= extent.minX() && x <= extent.maxX() && y >= extent.minY() && y <= extent.maxY()) {
return true;
return GeoRelation.QUERY_CROSSES;
}
}
continue;
Expand All @@ -76,7 +77,7 @@ public boolean intersects(Extent extent) throws IOException {
int x = readX(middle);
int y = readY(middle);
if (x >= extent.minX() && x <= extent.maxX() && y >= extent.minY() && y <= extent.maxY()) {
return true;
return GeoRelation.QUERY_CROSSES;
}
if ((axis == 0 && extent.minX() <= x) || (axis == 1 && extent.minY() <= y)) {
stack.push(left);
Expand All @@ -90,7 +91,7 @@ public boolean intersects(Extent extent) throws IOException {
}
}

return false;
return GeoRelation.QUERY_DISJOINT;
}

private int readX(int pointIdx) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,16 @@ public Extent getExtent() throws IOException {
* Returns true if the rectangle query and the edge tree's shape overlap
*/
@Override
public boolean intersects(Extent extent) throws IOException {
public GeoRelation relate(Extent extent) throws IOException {
if (holes != null) {
boolean onlyInHole = holes.containsFully(extent);
if (onlyInHole) {
return false;
GeoRelation relation = holes.relate(extent);
if (GeoRelation.QUERY_CROSSES == relation) {
return GeoRelation.QUERY_CROSSES;
}
if (GeoRelation.QUERY_INSIDE == relation) {
return GeoRelation.QUERY_DISJOINT;
}
}
return outerShell.intersects(extent);
return outerShell.relate(extent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
public interface ShapeTreeReader {

Extent getExtent() throws IOException;
boolean intersects(Extent extent) throws IOException;
GeoRelation relate(Extent extent) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
*/
package org.elasticsearch.index.fielddata;

import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.elasticsearch.common.geo.CoordinateEncoder;
import org.elasticsearch.common.geo.Extent;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoRelation;
import org.elasticsearch.common.geo.GeoShapeCoordinateEncoder;
import org.elasticsearch.common.geo.GeometryTreeReader;
import org.elasticsearch.common.geo.GeometryTreeWriter;
Expand Down Expand Up @@ -96,8 +98,12 @@ public BoundingBox boundingBox() {
}

@Override
public boolean intersects(Rectangle rectangle) {
throw new UnsupportedOperationException("intersect is unsupported for geo_point doc values");
public GeoRelation relate(Rectangle rectangle) {
if (GeoRelationUtils.pointInRectPrecise(geoPoint.lat(), geoPoint.lon(),
rectangle.getMinLat(), rectangle.getMaxLat(), rectangle.getMinLon(), rectangle.getMaxLon())) {
return GeoRelation.QUERY_CROSSES;
}
return GeoRelation.QUERY_DISJOINT;
}

@Override
Expand Down Expand Up @@ -141,14 +147,14 @@ public BoundingBox boundingBox() {
* @return the latitude of the centroid of the shape
*/
@Override
public boolean intersects(Rectangle rectangle) {
public GeoRelation relate(Rectangle rectangle) {
int minX = GeoShapeCoordinateEncoder.INSTANCE.encodeX(rectangle.getMinX());
int maxX = GeoShapeCoordinateEncoder.INSTANCE.encodeX(rectangle.getMaxX());
int minY = GeoShapeCoordinateEncoder.INSTANCE.encodeY(rectangle.getMinY());
int maxY = GeoShapeCoordinateEncoder.INSTANCE.encodeY(rectangle.getMaxY());
Extent extent = new Extent(maxY, minY, minX, maxX, minX, maxX);
try {
return reader.intersects(extent);
return reader.relate(extent);
} catch (IOException e) {
throw new IllegalStateException("unable to check intersection", e);
}
Expand Down Expand Up @@ -194,7 +200,7 @@ public interface GeoValue {
double lat();
double lon();
BoundingBox boundingBox();
boolean intersects(Rectangle rectangle);
GeoRelation relate(Rectangle rectangle);
}

public static class BoundingBox {
Expand Down
Loading

0 comments on commit 5fed23d

Please sign in to comment.