mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-23 13:26:02 +00:00
parent
76b7b68605
commit
b2286915cd
@ -143,13 +143,13 @@ polygon and a minimum of `4` vertices.
|
||||
points.
|
||||
|`MultiLineString` |`multilinestring` |An array of separate linestrings.
|
||||
|`MultiPolygon` |`multipolygon` |An array of separate polygons.
|
||||
|`GeometryCollection` |`geometrycollection` | A GeoJSON shape similar to the
|
||||
`multi*` shapes except that multiple types can coexist (e.g., a Point
|
||||
and a LineString).
|
||||
|`N/A` |`envelope` |A bounding rectangle, or envelope, specified by
|
||||
specifying only the top left and bottom right points.
|
||||
|`N/A` |`circle` |A circle specified by a center point and radius with
|
||||
units, which default to `METERS`.
|
||||
|`GeometryCollection` |`N/A` | An unsupported GeoJSON shape similar to the
|
||||
`multi*` shapes except that multiple types can coexist (e.g., a Point
|
||||
and a LineString).
|
||||
|=======================================================================
|
||||
|
||||
[NOTE]
|
||||
@ -291,6 +291,31 @@ A list of geojson polygons.
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
[float]
|
||||
===== http://geojson.org/geojson-spec.html#geometrycollection[Geometry Collection]
|
||||
|
||||
A collection of geojson geometry objects.
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"location" : {
|
||||
"type": "geometrycollection",
|
||||
"geometries": [
|
||||
{
|
||||
"type": "point",
|
||||
"coordinates": [100.0, 0.0]
|
||||
},
|
||||
{
|
||||
"type": "linestring",
|
||||
"coordinates": [ [101.0, 0.0], [102.0, 1.0] ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
|
||||
[float]
|
||||
===== Envelope
|
||||
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.builders;
|
||||
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.ShapeCollection;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GeometryCollectionBuilder extends ShapeBuilder {
|
||||
|
||||
public static final GeoShapeType TYPE = GeoShapeType.GEOMETRYCOLLECTION;
|
||||
|
||||
protected final ArrayList<ShapeBuilder> shapes = new ArrayList<>();
|
||||
|
||||
public GeometryCollectionBuilder shape(ShapeBuilder shape) {
|
||||
this.shapes.add(shape);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder point(PointBuilder point) {
|
||||
this.shapes.add(point);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder multiPoint(MultiPointBuilder multiPoint) {
|
||||
this.shapes.add(multiPoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder line(BaseLineStringBuilder<?> line) {
|
||||
this.shapes.add(line);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder multiLine(MultiLineStringBuilder multiLine) {
|
||||
this.shapes.add(multiLine);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder polygon(BasePolygonBuilder<?> polygon) {
|
||||
this.shapes.add(polygon);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder multiPolygon(MultiPolygonBuilder multiPolygon) {
|
||||
this.shapes.add(multiPolygon);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder envelope(EnvelopeBuilder envelope) {
|
||||
this.shapes.add(envelope);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GeometryCollectionBuilder circle(CircleBuilder circle) {
|
||||
this.shapes.add(circle);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(FIELD_TYPE, TYPE.shapename);
|
||||
builder.startArray(FIELD_GEOMETRIES);
|
||||
for (ShapeBuilder shape : shapes) {
|
||||
shape.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeType type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape build() {
|
||||
|
||||
List<Shape> shapes = new ArrayList<>(this.shapes.size());
|
||||
|
||||
for (ShapeBuilder shape : this.shapes) {
|
||||
shapes.add(shape.build());
|
||||
}
|
||||
|
||||
if (shapes.size() == 1)
|
||||
return shapes.get(0);
|
||||
else
|
||||
return new ShapeCollection<>(shapes, SPATIAL_CONTEXT);
|
||||
//note: ShapeCollection is probably faster than a Multi* geom.
|
||||
}
|
||||
|
||||
}
|
@ -150,6 +150,14 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
return new MultiPolygonBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new GeometryCollection
|
||||
* @return a new {@link GeometryCollectionBuilder}
|
||||
*/
|
||||
public static GeometryCollectionBuilder newGeometryCollection() {
|
||||
return new GeometryCollectionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new Circle
|
||||
* @return a new {@link CircleBuilder}
|
||||
@ -498,6 +506,7 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
|
||||
public static final String FIELD_TYPE = "type";
|
||||
public static final String FIELD_COORDINATES = "coordinates";
|
||||
public static final String FIELD_GEOMETRIES = "geometries";
|
||||
|
||||
protected static final boolean debugEnabled() {
|
||||
return LOGGER.isDebugEnabled() || DEBUG;
|
||||
@ -513,6 +522,7 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
MULTILINESTRING("multilinestring"),
|
||||
POLYGON("polygon"),
|
||||
MULTIPOLYGON("multipolygon"),
|
||||
GEOMETRYCOLLECTION("geometrycollection"),
|
||||
ENVELOPE("envelope"),
|
||||
CIRCLE("circle");
|
||||
|
||||
@ -542,6 +552,7 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
GeoShapeType shapeType = null;
|
||||
Distance radius = null;
|
||||
CoordinateNode node = null;
|
||||
GeometryCollectionBuilder geometryCollections = null;
|
||||
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
@ -554,6 +565,9 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
} else if (FIELD_COORDINATES.equals(fieldName)) {
|
||||
parser.nextToken();
|
||||
node = parseCoordinates(parser);
|
||||
} else if (FIELD_GEOMETRIES.equals(fieldName)) {
|
||||
parser.nextToken();
|
||||
geometryCollections = parseGeometries(parser);
|
||||
} else if (CircleBuilder.FIELD_RADIUS.equals(fieldName)) {
|
||||
parser.nextToken();
|
||||
radius = Distance.parseDistance(parser.text());
|
||||
@ -566,8 +580,10 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
|
||||
if (shapeType == null) {
|
||||
throw new ElasticsearchParseException("Shape type not included");
|
||||
} else if (node == null) {
|
||||
} else if (node == null && GeoShapeType.GEOMETRYCOLLECTION != shapeType) {
|
||||
throw new ElasticsearchParseException("Coordinates not included");
|
||||
} else if (geometryCollections == null && GeoShapeType.GEOMETRYCOLLECTION == shapeType) {
|
||||
throw new ElasticsearchParseException("geometries not included");
|
||||
} else if (radius != null && GeoShapeType.CIRCLE != shapeType) {
|
||||
throw new ElasticsearchParseException("Field [" + CircleBuilder.FIELD_RADIUS + "] is supported for [" + CircleBuilder.TYPE
|
||||
+ "] only");
|
||||
@ -582,6 +598,7 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
case MULTIPOLYGON: return parseMultiPolygon(node);
|
||||
case CIRCLE: return parseCircle(node, radius);
|
||||
case ENVELOPE: return parseEnvelope(node);
|
||||
case GEOMETRYCOLLECTION: return geometryCollections;
|
||||
default:
|
||||
throw new ElasticsearchParseException("Shape type [" + shapeType + "] not included");
|
||||
}
|
||||
@ -639,5 +656,28 @@ public abstract class ShapeBuilder implements ToXContent {
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the geometries array of a GeometryCollection
|
||||
*
|
||||
* @param parser Parser that will be read from
|
||||
* @return Geometry[] geometries of the GeometryCollection
|
||||
* @throws IOException Thrown if an error occurs while reading from the XContentParser
|
||||
*/
|
||||
protected static GeometryCollectionBuilder parseGeometries(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
|
||||
throw new ElasticsearchParseException("Geometries must be an array of geojson objects");
|
||||
}
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
GeometryCollectionBuilder geometryCollection = newGeometryCollection();
|
||||
while (token != XContentParser.Token.END_ARRAY) {
|
||||
ShapeBuilder shapeBuilder = GeoShapeType.parse(parser);
|
||||
geometryCollection.shape(shapeBuilder);
|
||||
token = parser.nextToken();
|
||||
}
|
||||
|
||||
return geometryCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,34 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
|
||||
assertGeometryEquals(jtsGeom(expected), lineGeoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_multiLineString() throws IOException {
|
||||
String multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "MultiLineString")
|
||||
.startArray("coordinates")
|
||||
.startArray()
|
||||
.startArray().value(100.0).value(0.0).endArray()
|
||||
.startArray().value(101.0).value(1.0).endArray()
|
||||
.endArray()
|
||||
.startArray()
|
||||
.startArray().value(102.0).value(2.0).endArray()
|
||||
.startArray().value(103.0).value(3.0).endArray()
|
||||
.endArray()
|
||||
.endArray()
|
||||
.endObject().string();
|
||||
|
||||
MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(new LineString[]{
|
||||
GEOMETRY_FACTORY.createLineString(new Coordinate[]{
|
||||
new Coordinate(100, 0),
|
||||
new Coordinate(101, 1),
|
||||
}),
|
||||
GEOMETRY_FACTORY.createLineString(new Coordinate[]{
|
||||
new Coordinate(102, 2),
|
||||
new Coordinate(103, 3),
|
||||
}),
|
||||
});
|
||||
assertGeometryEquals(jtsGeom(expected), multilinesGeoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_polygonNoHoles() throws IOException {
|
||||
String polygonGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "Polygon")
|
||||
@ -227,6 +255,39 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
|
||||
assertGeometryEquals(expected, multiPolygonGeoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_geometryCollection() throws IOException {
|
||||
String geometryCollectionGeoJson = XContentFactory.jsonBuilder().startObject()
|
||||
.field("type","GeometryCollection")
|
||||
.startArray("geometries")
|
||||
.startObject()
|
||||
.field("type", "LineString")
|
||||
.startArray("coordinates")
|
||||
.startArray().value(100.0).value(0.0).endArray()
|
||||
.startArray().value(101.0).value(1.0).endArray()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("type", "Point")
|
||||
.startArray("coordinates").value(102.0).value(2.0).endArray()
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.string();
|
||||
|
||||
Shape[] expected = new Shape[2];
|
||||
LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new Coordinate[]{
|
||||
new Coordinate(100, 0),
|
||||
new Coordinate(101, 1),
|
||||
});
|
||||
expected[0] = jtsGeom(expectedLineString);
|
||||
Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0));
|
||||
expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT);
|
||||
|
||||
//equals returns true only if geometries are in the same order
|
||||
assertGeometryEquals(shapeCollection(expected), geometryCollectionGeoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatParserExtractsCorrectTypeAndCoordinatesFromArbitraryJson() throws IOException {
|
||||
String pointGeoJson = XContentFactory.jsonBuilder().startObject()
|
||||
|
@ -23,6 +23,7 @@ import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.FilterBuilders;
|
||||
import org.elasticsearch.index.query.GeoShapeFilterBuilder;
|
||||
@ -376,4 +377,55 @@ public class GeoShapeIntegrationTests extends ElasticsearchIntegrationTest {
|
||||
assertThat(coordinates.get(1).get(1).doubleValue(), equalTo(-45.0));
|
||||
assertThat(locationMap.size(), equalTo(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShapeFilter_geometryCollection() throws Exception {
|
||||
createIndex("shapes");
|
||||
assertAcked(prepareCreate("test").addMapping("type", "location", "type=geo_shape"));
|
||||
|
||||
XContentBuilder docSource = jsonBuilder().startObject().startObject("location")
|
||||
.field("type", "geometrycollection")
|
||||
.startArray("geometries")
|
||||
.startObject()
|
||||
.field("type", "point")
|
||||
.startArray("coordinates")
|
||||
.value(100.0).value(0.0)
|
||||
.endArray()
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("type", "linestring")
|
||||
.startArray("coordinates")
|
||||
.startArray()
|
||||
.value(101.0).value(0.0)
|
||||
.endArray()
|
||||
.startArray()
|
||||
.value(102.0).value(1.0)
|
||||
.endArray()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject().endObject();
|
||||
indexRandom(true,
|
||||
client().prepareIndex("test", "type", "1")
|
||||
.setSource(docSource));
|
||||
ensureSearchable("test");
|
||||
|
||||
GeoShapeFilterBuilder filter = FilterBuilders.geoShapeFilter("location", ShapeBuilder.newGeometryCollection().polygon(ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0).point(99.0, -1.0)), ShapeRelation.INTERSECTS);
|
||||
SearchResponse result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||
.setPostFilter(filter).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 1);
|
||||
filter = FilterBuilders.geoShapeFilter("location", ShapeBuilder.newGeometryCollection().polygon(ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0).point(199.0, -11.0)), ShapeRelation.INTERSECTS);
|
||||
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||
.setPostFilter(filter).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 0);
|
||||
filter = FilterBuilders.geoShapeFilter("location", ShapeBuilder.newGeometryCollection()
|
||||
.polygon(ShapeBuilder.newPolygon().point(99.0, -1.0).point(99.0, 3.0).point(103.0, 3.0).point(103.0, -1.0).point(99.0, -1.0))
|
||||
.polygon(ShapeBuilder.newPolygon().point(199.0, -11.0).point(199.0, 13.0).point(193.0, 13.0).point(193.0, -11.0).point(199.0, -11.0)), ShapeRelation.INTERSECTS);
|
||||
result = client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())
|
||||
.setPostFilter(filter).get();
|
||||
assertSearchResponse(result);
|
||||
assertHitCount(result, 1);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
package org.elasticsearch.test.hamcrest;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.RandomizedTest;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.ShapeCollection;
|
||||
import com.spatial4j.core.shape.jts.JtsGeometry;
|
||||
@ -129,6 +128,10 @@ public class ElasticsearchGeoAssertions {
|
||||
assertEquals(l1.getCoordinates(), l2.getCoordinates());
|
||||
}
|
||||
|
||||
public static void assertEquals(MultiLineString l1, MultiLineString l2) {
|
||||
assertEquals(l1.getCoordinates(), l2.getCoordinates());
|
||||
}
|
||||
|
||||
public static void assertEquals(Polygon p1, Polygon p2) {
|
||||
Assert.assertEquals(p1.getNumInteriorRing(), p2.getNumInteriorRing());
|
||||
|
||||
@ -166,6 +169,9 @@ public class ElasticsearchGeoAssertions {
|
||||
} else if (s1 instanceof MultiPolygon && s2 instanceof MultiPolygon) {
|
||||
assertEquals((MultiPolygon) s1, (MultiPolygon) s2);
|
||||
|
||||
} else if (s1 instanceof MultiLineString && s2 instanceof MultiLineString) {
|
||||
assertEquals((MultiLineString) s1, (MultiLineString) s2);
|
||||
|
||||
} else {
|
||||
throw new RuntimeException("equality of shape types not supported [" + s1.getClass().getName() + " and " + s2.getClass().getName() + "]");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user