Skip to content

Commit 7de52b2

Browse files
committed
SQL: Switch server side for geosql from ShapeBuilder to libs/geo
Switches the server side of the geosql processing from using ShapeBuilder to libs/geo geometry objects. Relates to elastic#29872
1 parent 01b6c79 commit 7de52b2

File tree

3 files changed

+62
-38
lines changed

3 files changed

+62
-38
lines changed

x-pack/plugin/sql/qa/src/main/resources/ogc/ogc.sql-spec

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,25 @@ SELECT fid, UCASE(ST_GeometryType(boundary)) type FROM named_places ORDER BY fid
6161
selectMapNeatLinesProps
6262
SELECT fid, UCASE(ST_GeometryType(neatline)) type FROM map_neatlines ORDER BY fid;
6363

64-
// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
65-
// selectLakesXY
66-
// SELECT fid, ST_X(shore) x, ST_Y(shore) y FROM lakes ORDER BY fid;
64+
selectLakesXY
65+
SELECT fid, ST_X(shore) x, ST_Y(shore) y FROM lakes ORDER BY fid;
6766
selectRoadSegmentsXY
6867
SELECT fid, ST_X(centerline) x, ST_Y(centerline) y FROM road_segments ORDER BY fid;
6968
selectDividedRoutesXY
7069
SELECT fid, ST_X(centerlines) x, ST_Y(centerlines) y FROM divided_routes ORDER BY fid;
71-
// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
72-
// selectForestsXY
73-
// SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM forests ORDER BY fid;
70+
selectForestsXY
71+
SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM forests ORDER BY fid;
7472
selectBridgesPositionsXY
7573
SELECT fid, ST_X(position) x, ST_Y(position) y FROM bridges ORDER BY fid;
7674
selectStreamsXY
7775
SELECT fid, ST_X(centerline) x, ST_Y(centerline) y FROM streams ORDER BY fid;
7876
selectBuildingsXY
7977
SELECT fid, ST_X(position) x, ST_Y(position) y FROM buildings ORDER BY fid;
80-
// AwaitsFix https://github.com/elastic/elasticsearch/issues/40908
81-
// selectBuildingsFootprintsXY
82-
// SELECT fid, ST_X(footprint) x, ST_Y(footprint) y FROM buildings ORDER BY fid;
83-
// selectPondsXY
84-
// SELECT fid, ST_X(shores) x, ST_Y(shores) y FROM ponds ORDER BY fid;
85-
// selectNamedPlacesXY
86-
// SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM named_places ORDER BY fid;
87-
// selectMapNeatLinesXY
88-
// SELECT fid, ST_X(neatline) x, ST_Y(neatline) y FROM map_neatlines ORDER BY fid;
78+
selectBuildingsFootprintsXY
79+
SELECT fid, ST_X(footprint) x, ST_Y(footprint) y FROM buildings ORDER BY fid;
80+
selectPondsXY
81+
SELECT fid, ST_X(shores) x, ST_Y(shores) y FROM ponds ORDER BY fid;
82+
selectNamedPlacesXY
83+
SELECT fid, ST_X(boundary) x, ST_Y(boundary) y FROM named_places ORDER BY fid;
84+
selectMapNeatLinesXY
85+
SELECT fid, ST_X(neatline) x, ST_Y(neatline) y FROM map_neatlines ORDER BY fid;

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/geo/GeoShape.java

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55
*/
66
package org.elasticsearch.xpack.sql.expression.function.scalar.geo;
77

8+
import org.elasticsearch.ElasticsearchParseException;
9+
import org.elasticsearch.common.bytes.BytesReference;
810
import org.elasticsearch.common.geo.GeoUtils;
9-
import org.elasticsearch.common.geo.builders.PointBuilder;
10-
import org.elasticsearch.common.geo.builders.ShapeBuilder;
11-
import org.elasticsearch.common.geo.parsers.ShapeParser;
11+
import org.elasticsearch.common.geo.GeometryParser;
1212
import org.elasticsearch.common.io.stream.NamedWriteable;
1313
import org.elasticsearch.common.io.stream.StreamInput;
1414
import org.elasticsearch.common.io.stream.StreamOutput;
15+
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
16+
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
1517
import org.elasticsearch.common.xcontent.ToXContentFragment;
1618
import org.elasticsearch.common.xcontent.XContentBuilder;
19+
import org.elasticsearch.common.xcontent.XContentParser;
20+
import org.elasticsearch.common.xcontent.json.JsonXContent;
1721
import org.elasticsearch.geo.geometry.Circle;
1822
import org.elasticsearch.geo.geometry.Geometry;
1923
import org.elasticsearch.geo.geometry.GeometryCollection;
@@ -26,9 +30,12 @@
2630
import org.elasticsearch.geo.geometry.Point;
2731
import org.elasticsearch.geo.geometry.Polygon;
2832
import org.elasticsearch.geo.geometry.Rectangle;
33+
import org.elasticsearch.geo.utils.WellKnownText;
2934
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
3035

3136
import java.io.IOException;
37+
import java.io.InputStream;
38+
import java.text.ParseException;
3239
import java.util.Objects;
3340

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

4249
public static final String NAME = "geo";
4350

44-
private final ShapeBuilder<?, ?, ?> shapeBuilder;
51+
private final Geometry shape;
4552

4653
public GeoShape(double lon, double lat) {
47-
shapeBuilder = new PointBuilder(lon, lat);
54+
shape = new Point(lat, lon);
4855
}
4956

5057
public GeoShape(Object value) throws IOException {
51-
shapeBuilder = ShapeParser.parse(value);
58+
try {
59+
shape = parse(value);
60+
} catch (ParseException ex) {
61+
throw new ElasticsearchParseException("cannot load shape", ex);
62+
}
5263
}
5364

5465
public GeoShape(StreamInput in) throws IOException {
55-
shapeBuilder = ShapeParser.parse(in.readString());
66+
try {
67+
shape = parse(in.readString());
68+
} catch (ParseException ex) {
69+
throw new ElasticsearchParseException("cannot load shape", ex);
70+
}
5671
}
5772

5873
@Override
5974
public void writeTo(StreamOutput out) throws IOException {
60-
out.writeString(shapeBuilder.toWKT());
75+
out.writeString(WellKnownText.toWKT(shape));
6176
}
6277

6378
@Override
6479
public String toString() {
65-
return shapeBuilder.toWKT();
80+
return WellKnownText.toWKT(shape);
6681
}
6782

6883
@Override
6984
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
70-
return builder.value(shapeBuilder.toWKT());
85+
return builder.value(WellKnownText.toWKT(shape));
7186
}
7287

7388
public Geometry toGeometry() {
74-
return shapeBuilder.buildGeometry();
89+
return shape;
7590
}
7691

7792
public Point firstPoint() {
78-
return shapeBuilder.buildGeometry().visit(new GeometryVisitor<Point, RuntimeException>() {
93+
return shape.visit(new GeometryVisitor<Point, RuntimeException>() {
7994
@Override
8095
public Point visit(Circle circle) {
8196
return new Point(circle.getLat(), circle.getLon(), circle.hasAlt() ? circle.getAlt() : Double.NaN);
@@ -149,16 +164,16 @@ public String getGeometryType() {
149164
}
150165

151166
public static double distance(GeoShape shape1, GeoShape shape2) {
152-
if (shape1.shapeBuilder instanceof PointBuilder == false) {
167+
if (shape1.shape instanceof Point == false) {
153168
throw new SqlIllegalArgumentException("distance calculation is only supported for points; received [{}]", shape1);
154169
}
155-
if (shape2.shapeBuilder instanceof PointBuilder == false) {
170+
if (shape2.shape instanceof Point == false) {
156171
throw new SqlIllegalArgumentException("distance calculation is only supported for points; received [{}]", shape2);
157172
}
158-
double srcLat = ((PointBuilder) shape1.shapeBuilder).latitude();
159-
double srcLon = ((PointBuilder) shape1.shapeBuilder).longitude();
160-
double dstLat = ((PointBuilder) shape2.shapeBuilder).latitude();
161-
double dstLon = ((PointBuilder) shape2.shapeBuilder).longitude();
173+
double srcLat = ((Point) shape1.shape).getLat();
174+
double srcLon = ((Point) shape1.shape).getLon();
175+
double dstLat = ((Point) shape2.shape).getLat();
176+
double dstLon = ((Point) shape2.shape).getLon();
162177
return GeoUtils.arcDistance(srcLat, srcLon, dstLat, dstLon);
163178
}
164179

@@ -171,17 +186,32 @@ public boolean equals(Object o) {
171186
return false;
172187
}
173188
GeoShape geoShape = (GeoShape) o;
174-
return shapeBuilder.equals(geoShape.shapeBuilder);
189+
return shape.equals(geoShape.shape);
175190
}
176191

177192
@Override
178193
public int hashCode() {
179-
return Objects.hash(shapeBuilder);
194+
return Objects.hash(shape);
180195
}
181196

182197
@Override
183198
public String getWriteableName() {
184199
return NAME;
185200
}
186201

202+
private static Geometry parse(Object value) throws IOException, ParseException {
203+
XContentBuilder content = JsonXContent.contentBuilder();
204+
content.startObject();
205+
content.field("value", value);
206+
content.endObject();
207+
208+
try (InputStream stream = BytesReference.bytes(content).streamInput();
209+
XContentParser parser = JsonXContent.jsonXContent.createParser(
210+
NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, stream)) {
211+
parser.nextToken(); // start object
212+
parser.nextToken(); // field name
213+
parser.nextToken(); // field value
214+
return GeometryParser.parse(parser, true, true, true);
215+
}
216+
}
187217
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/geo/GeoProcessorTests.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ public void testApplyGetXY() throws Exception {
7777
new GeoShape("geometrycollection (point (20.0 10.0),point (1.0 2.0))")));
7878
}
7979

80-
// That doesn't work correctly at the moment because shape builder changes the order or points in polygons, so the second point
81-
// sometimes becomes the first
82-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40908")
8380
public void testApplyGetXYToPolygons() throws Exception {
8481
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))")));
8582
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))")));

0 commit comments

Comments
 (0)