Skip to content

Commit 77a7ef2

Browse files
committed
[GEO] Add optional left/right parameter to GeoJSON
This feature adds an optional orientation parameter to the GeoJSON document and geo_shape mapping enabling users to explicitly define how they want Elasticsearch to interpret vertex ordering. The default uses the right-hand rule (counterclockwise for outer ring, clockwise for inner ring) complying with OGC Simple Feature Access standards. The parameter can be explicitly specified for an entire index using the geo_shape mapping by adding "orientation":{"left"|"right"|"cw"|"ccw"|"clockwise"|"counterclockwise"} and/or overridden on each insert by adding the same parameter to the GeoJSON document. closes #8764
1 parent fb6c3b7 commit 77a7ef2

File tree

9 files changed

+395
-38
lines changed

9 files changed

+395
-38
lines changed

docs/reference/mapping/types/geo-shape-type.asciidoc

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ You can query documents using this type using
1111
or <<query-dsl-geo-shape-query,geo_shape
1212
Query>>.
1313

14+
[[geo-shape-mapping-options]]
1415
[float]
1516
==== Mapping Options
1617

@@ -46,6 +47,17 @@ via the mapping API even if you use the precision parameter.
4647
|`distance_error_pct` |Used as a hint to the PrefixTree about how
4748
precise it should be. Defaults to 0.025 (2.5%) with 0.5 as the maximum
4849
supported value.
50+
51+
|`orientation` |Optionally define how to interpret vertex order for
52+
polygons / multipolygons. This parameter defines one of two coordinate
53+
system rules (Right-hand or Left-hand) each of which can be specified in three
54+
different ways. 1. Right-hand rule (default): `right`, `ccw`, `counterclockwise`,
55+
2. Left-hand rule: `left`, `cw`, `clockwise`. The default orientation
56+
(`counterclockwise`) complies with the OGC standard which defines
57+
outer ring vertices in counterclockwise order with inner ring(s) vertices (holes)
58+
in clockwise order. Setting this parameter in the geo_shape mapping explicitly
59+
sets vertex order for the coordinate list of a geo_shape field but can be
60+
overridden in each individual GeoJSON document.
4961
|=======================================================================
5062

5163
[float]
@@ -246,7 +258,7 @@ defines the following vertex ordering:
246258

247259
For polygons that do not cross the dateline, vertex order will not matter in
248260
Elasticsearch. For polygons that do cross the dateline, Elasticsearch requires
249-
vertex orderinging comply with the OGC specification. Otherwise, an unintended polygon
261+
vertex ordering to comply with the OGC specification. Otherwise, an unintended polygon
250262
may be created and unexpected query/filter results will be returned.
251263

252264
The following provides an example of an ambiguous polygon. Elasticsearch will apply
@@ -265,6 +277,24 @@ OGC standards to eliminate ambiguity resulting in a polygon that crosses the dat
265277
}
266278
--------------------------------------------------
267279

280+
An `orientation` parameter can be defined when setting the geo_shape mapping (see <<geo-shape-mapping-options>>). This will define vertex
281+
order for the coordinate list on the mapped geo_shape field. It can also be overridden on each document. The following is an example for
282+
overriding the orientation on a document:
283+
284+
[source,js]
285+
--------------------------------------------------
286+
{
287+
"location" : {
288+
"type" : "polygon",
289+
"orientation" : "clockwise",
290+
"coordinates" : [
291+
[ [-177.0, 10.0], [176.0, 15.0], [172.0, 0.0], [176.0, -15.0], [-177.0, -10.0], [-177.0, 10.0] ],
292+
[ [178.2, 8.2], [-178.8, 8.2], [-180.8, -8.8], [178.2, 8.8] ]
293+
]
294+
}
295+
}
296+
--------------------------------------------------
297+
268298
[float]
269299
===== http://www.geojson.org/geojson-spec.html#id5[MultiPoint]
270300

src/main/java/org/elasticsearch/common/geo/builders/BasePolygonBuilder.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ public abstract class BasePolygonBuilder<E extends BasePolygonBuilder<E>> extend
4848
// List of linear rings defining the holes of the polygon
4949
protected final ArrayList<BaseLineStringBuilder<?>> holes = new ArrayList<>();
5050

51+
public BasePolygonBuilder(Orientation orientation) {
52+
super(orientation);
53+
}
54+
5155
@SuppressWarnings("unchecked")
5256
private E thisRef() {
5357
return (E)this;
@@ -125,9 +129,9 @@ public Coordinate[][][] coordinates() {
125129

126130
Edge[] edges = new Edge[numEdges];
127131
Edge[] holeComponents = new Edge[holes.size()];
128-
int offset = createEdges(0, false, shell, null, edges, 0);
132+
int offset = createEdges(0, orientation.getValue(), shell, null, edges, 0);
129133
for (int i = 0; i < holes.size(); i++) {
130-
int length = createEdges(i+1, true, shell, this.holes.get(i), edges, offset);
134+
int length = createEdges(i+1, orientation.getValue(), shell, this.holes.get(i), edges, offset);
131135
holeComponents[i] = edges[offset];
132136
offset += length;
133137
}
@@ -453,11 +457,14 @@ private static void connect(Edge in, Edge out) {
453457
}
454458
}
455459

456-
private static int createEdges(int component, boolean direction, BaseLineStringBuilder<?> shell, BaseLineStringBuilder<?> hole,
460+
private static int createEdges(int component, boolean orientation, BaseLineStringBuilder<?> shell,
461+
BaseLineStringBuilder<?> hole,
457462
Edge[] edges, int offset) {
463+
// inner rings (holes) have an opposite direction than the outer rings
464+
boolean direction = (component != 0) ? !orientation : orientation;
458465
// set the points array accordingly (shell or hole)
459466
Coordinate[] points = (hole != null) ? hole.coordinates(false) : shell.coordinates(false);
460-
Edge.ring(component, direction, shell, points, 0, edges, offset, points.length-1);
467+
Edge.ring(component, direction, orientation, shell, points, 0, edges, offset, points.length-1);
461468
return points.length-1;
462469
}
463470

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ public class EnvelopeBuilder extends ShapeBuilder {
3232
protected Coordinate topLeft;
3333
protected Coordinate bottomRight;
3434

35+
public EnvelopeBuilder() {
36+
this(Orientation.RIGHT);
37+
}
38+
39+
public EnvelopeBuilder(Orientation orientation) {
40+
super(orientation);
41+
}
42+
3543
public EnvelopeBuilder topLeft(Coordinate topLeft) {
3644
this.topLeft = topLeft;
3745
return this;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ public class GeometryCollectionBuilder extends ShapeBuilder {
3333

3434
protected final ArrayList<ShapeBuilder> shapes = new ArrayList<>();
3535

36+
public GeometryCollectionBuilder() {
37+
this(Orientation.RIGHT);
38+
}
39+
40+
public GeometryCollectionBuilder(Orientation orientation) {
41+
super(orientation);
42+
}
43+
3644
public GeometryCollectionBuilder shape(ShapeBuilder shape) {
3745
this.shapes.add(shape);
3846
return this;

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,25 @@ public class MultiPolygonBuilder extends ShapeBuilder {
3535

3636
protected final ArrayList<BasePolygonBuilder<?>> polygons = new ArrayList<>();
3737

38+
public MultiPolygonBuilder() {
39+
this(Orientation.RIGHT);
40+
}
41+
42+
public MultiPolygonBuilder(Orientation orientation) {
43+
super(orientation);
44+
}
45+
3846
public MultiPolygonBuilder polygon(BasePolygonBuilder<?> polygon) {
3947
this.polygons.add(polygon);
4048
return this;
4149
}
4250

4351
public InternalPolygonBuilder polygon() {
44-
InternalPolygonBuilder polygon = new InternalPolygonBuilder(this);
52+
return polygon(Orientation.RIGHT);
53+
}
54+
55+
public InternalPolygonBuilder polygon(Orientation orientation) {
56+
InternalPolygonBuilder polygon = new InternalPolygonBuilder(this, orientation);
4557
this.polygon(polygon);
4658
return polygon;
4759
}
@@ -92,8 +104,8 @@ public static class InternalPolygonBuilder extends BasePolygonBuilder<InternalPo
92104

93105
private final MultiPolygonBuilder collection;
94106

95-
private InternalPolygonBuilder(MultiPolygonBuilder collection) {
96-
super();
107+
private InternalPolygonBuilder(MultiPolygonBuilder collection, Orientation orientation) {
108+
super(orientation);
97109
this.collection = collection;
98110
this.shell = new Ring<>(this);
99111
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@
2626
public class PolygonBuilder extends BasePolygonBuilder<PolygonBuilder> {
2727

2828
public PolygonBuilder() {
29-
this(new ArrayList<Coordinate>());
29+
this(new ArrayList<Coordinate>(), Orientation.RIGHT);
3030
}
3131

32-
protected PolygonBuilder(ArrayList<Coordinate> points) {
33-
super();
32+
public PolygonBuilder(Orientation orientation) {
33+
this(new ArrayList<Coordinate>(), orientation);
34+
}
35+
36+
protected PolygonBuilder(ArrayList<Coordinate> points, Orientation orientation) {
37+
super(orientation);
3438
this.shell = new Ring<>(this, points);
3539
}
3640

0 commit comments

Comments
 (0)