Skip to content

Commit 8bcf539

Browse files
committed
[Geo] Add Well Known Text (WKT) Parsing Support to ShapeBuilders
This commit adds WKT support to Geo ShapeBuilders. This supports the following format: POINT (30 10) LINESTRING (30 10, 10 30, 40 40) BBOX (-10, 10, 10, -10) POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10)) POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30)) MULTIPOINT ((10 40), (40 30), (20 20), (30 10)) MULTIPOINT (10 40, 40 30, 20 20, 30 10) MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10)) MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5))) MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))) GEOMETRYCOLLECTION (POINT (30 10), MULTIPOINT ((10 40), (40 30), (20 20), (30 10))) closes #9120
1 parent 5b03e3b commit 8bcf539

File tree

14 files changed

+933
-56
lines changed

14 files changed

+933
-56
lines changed

core/src/main/java/org/elasticsearch/common/geo/GeoShapeType.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
241241
}
242242
return coordinates;
243243
}
244+
245+
@Override
246+
public String wktName() {
247+
return BBOX;
248+
}
244249
},
245250
CIRCLE("circle") {
246251
@Override
@@ -273,11 +278,13 @@ CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
273278

274279
private final String shapename;
275280
private static Map<String, GeoShapeType> shapeTypeMap = new HashMap<>();
281+
private static final String BBOX = "BBOX";
276282

277283
static {
278284
for (GeoShapeType type : values()) {
279285
shapeTypeMap.put(type.shapename, type);
280286
}
287+
shapeTypeMap.put(ENVELOPE.wktName().toLowerCase(Locale.ROOT), ENVELOPE);
281288
}
282289

283290
GeoShapeType(String shapename) {
@@ -300,6 +307,11 @@ public abstract ShapeBuilder getBuilder(CoordinateNode coordinates, DistanceUnit
300307
ShapeBuilder.Orientation orientation, boolean coerce);
301308
abstract CoordinateNode validate(CoordinateNode coordinates, boolean coerce);
302309

310+
/** wkt shape name */
311+
public String wktName() {
312+
return this.shapename;
313+
}
314+
303315
public static List<Entry> getShapeWriteables() {
304316
List<Entry> namedWriteables = new ArrayList<>();
305317
namedWriteables.add(new Entry(ShapeBuilder.class, PointBuilder.TYPE.shapeName(), PointBuilder::new));
@@ -313,4 +325,9 @@ public static List<Entry> getShapeWriteables() {
313325
namedWriteables.add(new Entry(ShapeBuilder.class, GeometryCollectionBuilder.TYPE.shapeName(), GeometryCollectionBuilder::new));
314326
return namedWriteables;
315327
}
328+
329+
@Override
330+
public String toString() {
331+
return this.shapename;
332+
}
316333
}

core/src/main/java/org/elasticsearch/common/geo/builders/CircleBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ public GeoShapeType type() {
168168
return TYPE;
169169
}
170170

171+
@Override
172+
public String toWKT() {
173+
throw new UnsupportedOperationException("The WKT spec does not support CIRCLE geometry");
174+
}
175+
171176
@Override
172177
public int hashCode() {
173178
return Objects.hash(center, radius, unit.ordinal());

core/src/main/java/org/elasticsearch/common/geo/builders/EnvelopeBuilder.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.common.geo.builders;
2121

2222
import org.elasticsearch.common.geo.GeoShapeType;
23+
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
2324
import org.elasticsearch.common.geo.parsers.ShapeParser;
2425
import org.locationtech.spatial4j.shape.Rectangle;
2526
import com.vividsolutions.jts.geom.Coordinate;
@@ -70,6 +71,28 @@ public Coordinate bottomRight() {
7071
return this.bottomRight;
7172
}
7273

74+
@Override
75+
protected StringBuilder contentToWKT() {
76+
StringBuilder sb = new StringBuilder();
77+
78+
sb.append(GeoWKTParser.LPAREN);
79+
// minX, maxX, maxY, minY
80+
sb.append(topLeft.x);
81+
sb.append(GeoWKTParser.COMMA);
82+
sb.append(GeoWKTParser.SPACE);
83+
sb.append(bottomRight.x);
84+
sb.append(GeoWKTParser.COMMA);
85+
sb.append(GeoWKTParser.SPACE);
86+
// TODO support Z??
87+
sb.append(topLeft.y);
88+
sb.append(GeoWKTParser.COMMA);
89+
sb.append(GeoWKTParser.SPACE);
90+
sb.append(bottomRight.y);
91+
sb.append(GeoWKTParser.RPAREN);
92+
93+
return sb;
94+
}
95+
7396
@Override
7497
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
7598
builder.startObject();

core/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.common.geo.GeoShapeType;
2323
import org.elasticsearch.common.geo.parsers.ShapeParser;
24+
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
2425
import org.locationtech.spatial4j.shape.Shape;
2526

2627
import org.elasticsearch.ElasticsearchException;
@@ -136,6 +137,23 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
136137
return builder.endObject();
137138
}
138139

140+
@Override
141+
protected StringBuilder contentToWKT() {
142+
StringBuilder sb = new StringBuilder();
143+
if (shapes.isEmpty()) {
144+
sb.append(GeoWKTParser.EMPTY);
145+
} else {
146+
sb.append(GeoWKTParser.LPAREN);
147+
sb.append(shapes.get(0).toWKT());
148+
for (int i = 1; i < shapes.size(); ++i) {
149+
sb.append(GeoWKTParser.COMMA);
150+
sb.append(shapes.get(i).toWKT());
151+
}
152+
sb.append(GeoWKTParser.RPAREN);
153+
}
154+
return sb;
155+
}
156+
139157
@Override
140158
public GeoShapeType type() {
141159
return TYPE;

core/src/main/java/org/elasticsearch/common/geo/builders/MultiLineStringBuilder.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
package org.elasticsearch.common.geo.builders;
2121

2222
import org.elasticsearch.common.geo.GeoShapeType;
23+
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
2324
import org.elasticsearch.common.geo.parsers.ShapeParser;
24-
import org.locationtech.spatial4j.shape.Shape;
2525
import com.vividsolutions.jts.geom.Coordinate;
2626
import com.vividsolutions.jts.geom.Geometry;
2727
import com.vividsolutions.jts.geom.LineString;
@@ -82,6 +82,25 @@ public GeoShapeType type() {
8282
return TYPE;
8383
}
8484

85+
@Override
86+
protected StringBuilder contentToWKT() {
87+
final StringBuilder sb = new StringBuilder();
88+
if (lines.isEmpty()) {
89+
sb.append(GeoWKTParser.EMPTY);
90+
} else {
91+
sb.append(GeoWKTParser.LPAREN);
92+
if (lines.size() > 0) {
93+
sb.append(ShapeBuilder.coordinateListToWKT(lines.get(0).coordinates));
94+
}
95+
for (int i = 1; i < lines.size(); ++i) {
96+
sb.append(GeoWKTParser.COMMA);
97+
sb.append(ShapeBuilder.coordinateListToWKT(lines.get(i).coordinates));
98+
}
99+
sb.append(GeoWKTParser.RPAREN);
100+
}
101+
return sb;
102+
}
103+
85104
@Override
86105
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
87106
builder.startObject();

core/src/main/java/org/elasticsearch/common/geo/builders/MultiPolygonBuilder.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.common.geo.GeoShapeType;
2323
import org.elasticsearch.common.geo.parsers.ShapeParser;
24+
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
2425
import org.locationtech.spatial4j.shape.Shape;
2526
import com.vividsolutions.jts.geom.Coordinate;
2627

@@ -101,6 +102,37 @@ public List<PolygonBuilder> polygons() {
101102
return polygons;
102103
}
103104

105+
private static String polygonCoordinatesToWKT(PolygonBuilder polygon) {
106+
StringBuilder sb = new StringBuilder();
107+
sb.append(GeoWKTParser.LPAREN);
108+
sb.append(ShapeBuilder.coordinateListToWKT(polygon.shell().coordinates));
109+
for (LineStringBuilder hole : polygon.holes()) {
110+
sb.append(GeoWKTParser.COMMA);
111+
sb.append(ShapeBuilder.coordinateListToWKT(hole.coordinates));
112+
}
113+
sb.append(GeoWKTParser.RPAREN);
114+
return sb.toString();
115+
}
116+
117+
@Override
118+
protected StringBuilder contentToWKT() {
119+
final StringBuilder sb = new StringBuilder();
120+
if (polygons.isEmpty()) {
121+
sb.append(GeoWKTParser.EMPTY);
122+
} else {
123+
sb.append(GeoWKTParser.LPAREN);
124+
if (polygons.size() > 0) {
125+
sb.append(polygonCoordinatesToWKT(polygons.get(0)));
126+
}
127+
for (int i = 1; i < polygons.size(); ++i) {
128+
sb.append(GeoWKTParser.COMMA);
129+
sb.append(polygonCoordinatesToWKT(polygons.get(i)));
130+
}
131+
sb.append(GeoWKTParser.RPAREN);
132+
}
133+
return sb;
134+
}
135+
104136
@Override
105137
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
106138
builder.startObject();

core/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,19 @@ private static void translate(Coordinate[] points) {
729729
}
730730
}
731731

732+
@Override
733+
protected StringBuilder contentToWKT() {
734+
StringBuilder sb = new StringBuilder();
735+
sb.append('(');
736+
sb.append(ShapeBuilder.coordinateListToWKT(shell.coordinates));
737+
for (LineStringBuilder hole : holes) {
738+
sb.append(", ");
739+
sb.append(ShapeBuilder.coordinateListToWKT(hole.coordinates));
740+
}
741+
sb.append(')');
742+
return sb;
743+
}
744+
732745
@Override
733746
public int hashCode() {
734747
return Objects.hash(shell, holes, orientation);

core/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.Assertions;
2828
import org.elasticsearch.common.Strings;
2929
import org.elasticsearch.common.geo.GeoShapeType;
30+
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
3031
import org.elasticsearch.common.io.stream.NamedWriteable;
3132
import org.elasticsearch.common.io.stream.StreamInput;
3233
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -339,6 +340,47 @@ public String toString() {
339340
}
340341
}
341342

343+
protected StringBuilder contentToWKT() {
344+
return coordinateListToWKT(this.coordinates);
345+
}
346+
347+
public String toWKT() {
348+
StringBuilder sb = new StringBuilder();
349+
sb.append(type().wktName());
350+
sb.append(GeoWKTParser.SPACE);
351+
sb.append(contentToWKT());
352+
return sb.toString();
353+
}
354+
355+
protected static StringBuilder coordinateListToWKT(final List<Coordinate> coordinates) {
356+
final StringBuilder sb = new StringBuilder();
357+
358+
if (coordinates.isEmpty()) {
359+
sb.append(GeoWKTParser.EMPTY);
360+
} else {
361+
// walk through coordinates:
362+
sb.append(GeoWKTParser.LPAREN);
363+
sb.append(coordinateToWKT(coordinates.get(0)));
364+
for (int i = 1; i < coordinates.size(); ++i) {
365+
sb.append(GeoWKTParser.COMMA);
366+
sb.append(GeoWKTParser.SPACE);
367+
sb.append(coordinateToWKT(coordinates.get(i)));
368+
}
369+
sb.append(GeoWKTParser.RPAREN);
370+
}
371+
372+
return sb;
373+
}
374+
375+
private static String coordinateToWKT(final Coordinate coordinate) {
376+
final StringBuilder sb = new StringBuilder();
377+
sb.append(coordinate.x + GeoWKTParser.SPACE + coordinate.y);
378+
if (Double.isNaN(coordinate.z) == false) {
379+
sb.append(GeoWKTParser.SPACE + coordinate.z);
380+
}
381+
return sb.toString();
382+
}
383+
342384
protected static final IntersectionOrder INTERSECTION_ORDER = new IntersectionOrder();
343385

344386
private static final class IntersectionOrder implements Comparator<Edge> {

0 commit comments

Comments
 (0)