Skip to content

Commit

Permalink
SQL: Switch server side for geosql from ShapeBuilder to libs/geo
Browse files Browse the repository at this point in the history
Switches the server side of the geosql processing from using
ShapeBuilder to libs/geo geometry objects.

Relates to elastic#29872
  • Loading branch information
imotov committed Apr 30, 2019
1 parent 01b6c79 commit 7de52b2
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 38 deletions.
27 changes: 12 additions & 15 deletions x-pack/plugin/sql/qa/src/main/resources/ogc/ogc.sql-spec
Original file line number Diff line number Diff line change
Expand Up @@ -61,28 +61,25 @@ SELECT fid, UCASE(ST_GeometryType(boundary)) type FROM named_places ORDER BY fid
selectMapNeatLinesProps
SELECT fid, UCASE(ST_GeometryType(neatline)) type FROM map_neatlines ORDER BY fid;

// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
// selectLakesXY
// SELECT fid, ST_X(shore) x, ST_Y(shore) y FROM lakes ORDER BY fid;
selectLakesXY
SELECT fid, ST_X(shore) x, ST_Y(shore) y FROM lakes ORDER BY fid;
selectRoadSegmentsXY
SELECT fid, ST_X(centerline) x, ST_Y(centerline) y FROM road_segments ORDER BY fid;
selectDividedRoutesXY
SELECT fid, ST_X(centerlines) x, ST_Y(centerlines) y FROM divided_routes ORDER BY fid;
// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
// selectForestsXY
// SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM forests ORDER BY fid;
selectForestsXY
SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM forests ORDER BY fid;
selectBridgesPositionsXY
SELECT fid, ST_X(position) x, ST_Y(position) y FROM bridges ORDER BY fid;
selectStreamsXY
SELECT fid, ST_X(centerline) x, ST_Y(centerline) y FROM streams ORDER BY fid;
selectBuildingsXY
SELECT fid, ST_X(position) x, ST_Y(position) y FROM buildings ORDER BY fid;
// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
// selectBuildingsFootprintsXY
// SELECT fid, ST_X(footprint) x, ST_Y(footprint) y FROM buildings ORDER BY fid;
// selectPondsXY
// SELECT fid, ST_X(shores) x, ST_Y(shores) y FROM ponds ORDER BY fid;
// selectNamedPlacesXY
// SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM named_places ORDER BY fid;
// selectMapNeatLinesXY
// SELECT fid, ST_X(neatline) x, ST_Y(neatline) y FROM map_neatlines ORDER BY fid;
selectBuildingsFootprintsXY
SELECT fid, ST_X(footprint) x, ST_Y(footprint) y FROM buildings ORDER BY fid;
selectPondsXY
SELECT fid, ST_X(shores) x, ST_Y(shores) y FROM ponds ORDER BY fid;
selectNamedPlacesXY
SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM named_places ORDER BY fid;
selectMapNeatLinesXY
SELECT fid, ST_X(neatline) x, ST_Y(neatline) y FROM map_neatlines ORDER BY fid;
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.geo;

import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.geo.GeometryParser;
import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
Expand All @@ -26,9 +30,12 @@
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Polygon;
import org.elasticsearch.geo.geometry.Rectangle;
import org.elasticsearch.geo.utils.WellKnownText;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Objects;

/**
Expand All @@ -41,41 +48,49 @@ public class GeoShape implements ToXContentFragment, NamedWriteable {

public static final String NAME = "geo";

private final ShapeBuilder<?, ?, ?> shapeBuilder;
private final Geometry shape;

public GeoShape(double lon, double lat) {
shapeBuilder = new PointBuilder(lon, lat);
shape = new Point(lat, lon);
}

public GeoShape(Object value) throws IOException {
shapeBuilder = ShapeParser.parse(value);
try {
shape = parse(value);
} catch (ParseException ex) {
throw new ElasticsearchParseException("cannot load shape", ex);
}
}

public GeoShape(StreamInput in) throws IOException {
shapeBuilder = ShapeParser.parse(in.readString());
try {
shape = parse(in.readString());
} catch (ParseException ex) {
throw new ElasticsearchParseException("cannot load shape", ex);
}
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(shapeBuilder.toWKT());
out.writeString(WellKnownText.toWKT(shape));
}

@Override
public String toString() {
return shapeBuilder.toWKT();
return WellKnownText.toWKT(shape);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.value(shapeBuilder.toWKT());
return builder.value(WellKnownText.toWKT(shape));
}

public Geometry toGeometry() {
return shapeBuilder.buildGeometry();
return shape;
}

public Point firstPoint() {
return shapeBuilder.buildGeometry().visit(new GeometryVisitor<Point, RuntimeException>() {
return shape.visit(new GeometryVisitor<Point, RuntimeException>() {
@Override
public Point visit(Circle circle) {
return new Point(circle.getLat(), circle.getLon(), circle.hasAlt() ? circle.getAlt() : Double.NaN);
Expand Down Expand Up @@ -149,16 +164,16 @@ public String getGeometryType() {
}

public static double distance(GeoShape shape1, GeoShape shape2) {
if (shape1.shapeBuilder instanceof PointBuilder == false) {
if (shape1.shape instanceof Point == false) {
throw new SqlIllegalArgumentException("distance calculation is only supported for points; received [{}]", shape1);
}
if (shape2.shapeBuilder instanceof PointBuilder == false) {
if (shape2.shape instanceof Point == false) {
throw new SqlIllegalArgumentException("distance calculation is only supported for points; received [{}]", shape2);
}
double srcLat = ((PointBuilder) shape1.shapeBuilder).latitude();
double srcLon = ((PointBuilder) shape1.shapeBuilder).longitude();
double dstLat = ((PointBuilder) shape2.shapeBuilder).latitude();
double dstLon = ((PointBuilder) shape2.shapeBuilder).longitude();
double srcLat = ((Point) shape1.shape).getLat();
double srcLon = ((Point) shape1.shape).getLon();
double dstLat = ((Point) shape2.shape).getLat();
double dstLon = ((Point) shape2.shape).getLon();
return GeoUtils.arcDistance(srcLat, srcLon, dstLat, dstLon);
}

Expand All @@ -171,17 +186,32 @@ public boolean equals(Object o) {
return false;
}
GeoShape geoShape = (GeoShape) o;
return shapeBuilder.equals(geoShape.shapeBuilder);
return shape.equals(geoShape.shape);
}

@Override
public int hashCode() {
return Objects.hash(shapeBuilder);
return Objects.hash(shape);
}

@Override
public String getWriteableName() {
return NAME;
}

private static Geometry parse(Object value) throws IOException, ParseException {
XContentBuilder content = JsonXContent.contentBuilder();
content.startObject();
content.field("value", value);
content.endObject();

try (InputStream stream = BytesReference.bytes(content).streamInput();
XContentParser parser = JsonXContent.jsonXContent.createParser(
NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, stream)) {
parser.nextToken(); // start object
parser.nextToken(); // field name
parser.nextToken(); // field value
return GeometryParser.parse(parser, true, true, true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ public void testApplyGetXY() throws Exception {
new GeoShape("geometrycollection (point (20.0 10.0),point (1.0 2.0))")));
}

// That doesn't work correctly at the moment because shape builder changes the order or points in polygons, so the second point
// sometimes becomes the first
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40908")
public void testApplyGetXYToPolygons() throws Exception {
assertEquals(3.0, new GeoProcessor(GeoOperation.X).process(new GeoShape("polygon ((3.0 1.0, 4.0 2.0, 4.0 3.0, 3.0 1.0))")));
assertEquals(1.0, new GeoProcessor(GeoOperation.Y).process(new GeoShape("polygon ((3.0 1.0, 4.0 2.0, 4.0 3.0, 3.0 1.0))")));
Expand Down

0 comments on commit 7de52b2

Please sign in to comment.