diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoJson.java b/server/src/main/java/org/elasticsearch/common/geo/GeoJson.java index a8f554be514b6..1f00bc31d577e 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoJson.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoJson.java @@ -48,8 +48,10 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; @@ -206,6 +208,143 @@ private XContentBuilder coordinatesToXContent(Polygon polygon) throws IOExceptio return builder.endObject(); } + /** + * Produces that same GeoJSON as toXContent only in parsed map form + */ + public static Map toMap(Geometry geometry) { + Map root = new HashMap<>(); + root.put(FIELD_TYPE.getPreferredName(), getGeoJsonName(geometry)); + + geometry.visit(new GeometryVisitor() { + @Override + public Void visit(Circle circle) { + root.put(FIELD_RADIUS.getPreferredName(), DistanceUnit.METERS.toString(circle.getRadiusMeters())); + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), coordinatesToList(circle.getY(), circle.getX(), circle.getZ())); + return null; + } + + @Override + public Void visit(GeometryCollection collection) { + List geometries = new ArrayList<>(collection.size()); + + for (Geometry g : collection) { + geometries.add(toMap(g)); + } + root.put(FIELD_GEOMETRIES.getPreferredName(), geometries); + return null; + } + + @Override + public Void visit(Line line) { + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), coordinatesToList(line)); + return null; + } + + @Override + public Void visit(LinearRing ring) { + throw new UnsupportedOperationException("linearRing cannot be serialized using GeoJson"); + } + + @Override + public Void visit(MultiLine multiLine) { + List lines = new ArrayList<>(multiLine.size()); + for (int i = 0; i < multiLine.size(); i++) { + lines.add(coordinatesToList(multiLine.get(i))); + } + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), lines); + return null; + } + + @Override + public Void visit(MultiPoint multiPoint) { + List points = new ArrayList<>(multiPoint.size()); + for (int i = 0; i < multiPoint.size(); i++) { + Point p = multiPoint.get(i); + List point = new ArrayList<>(); + point.add(p.getX()); + point.add(p.getY()); + if (p.hasZ()) { + point.add(p.getZ()); + } + points.add(point); + } + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), points); + return null; + } + + @Override + public Void visit(MultiPolygon multiPolygon) { + List polygons = new ArrayList<>(); + for (int i = 0; i < multiPolygon.size(); i++) { + polygons.add(coordinatesToList(multiPolygon.get(i))); + } + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), polygons); + return null; + } + + @Override + public Void visit(Point point) { + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), coordinatesToList(point.getY(), point.getX(), point.getZ())); + return null; + } + + @Override + public Void visit(Polygon polygon) { + List coords = new ArrayList<>(polygon.getNumberOfHoles() + 1); + coords.add(coordinatesToList(polygon.getPolygon())); + for (int i = 0; i < polygon.getNumberOfHoles(); i++) { + coords.add(coordinatesToList(polygon.getHole(i))); + } + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), coords); + return null; + } + + @Override + public Void visit(Rectangle rectangle) { + List coords = new ArrayList<>(2); + coords.add(coordinatesToList(rectangle.getMaxY(), rectangle.getMinX(), rectangle.getMinZ())); // top left + coords.add(coordinatesToList(rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxZ())); // bottom right + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), coords); + return null; + } + + private List coordinatesToList(double lat, double lon, double alt) { + List coords = new ArrayList<>(3); + coords.add(lon); + coords.add(lat); + if (Double.isNaN(alt) == false) { + coords.add(alt); + } + return coords; + } + + private List coordinatesToList(Line line) { + List lines = new ArrayList<>(line.length()); + for (int i = 0; i < line.length(); i++) { + List coords = new ArrayList<>(3); + coords.add(line.getX(i)); + coords.add(line.getY(i)); + if (line.hasZ()) { + coords.add(line.getZ(i)); + } + lines.add(coords); + } + return lines; + } + + private List coordinatesToList(Polygon polygon) { + List coords = new ArrayList<>(polygon.getNumberOfHoles() + 1); + coords.add(coordinatesToList(polygon.getPolygon())); + for (int i = 0; i < polygon.getNumberOfHoles(); i++) { + coords.add(coordinatesToList(polygon.getHole(i))); + } + return coords; + } + + }); + return root; + } + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("geojson", true, (a, c) -> { String type = (String) a[0]; diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoJsonGeometryFormat.java b/server/src/main/java/org/elasticsearch/common/geo/GeoJsonGeometryFormat.java index fa6614d3b7419..0bc220462c042 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoJsonGeometryFormat.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoJsonGeometryFormat.java @@ -19,19 +19,12 @@ package org.elasticsearch.common.geo; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.geometry.Geometry; import java.io.IOException; -import java.io.UncheckedIOException; public class GeoJsonGeometryFormat implements GeometryFormat { public static final String NAME = "geojson"; @@ -66,17 +59,6 @@ public XContentBuilder toXContent(Geometry geometry, XContentBuilder builder, To @Override public Object toXContentAsObject(Geometry geometry) { - try { - XContentBuilder builder = XContentFactory.jsonBuilder(); - GeoJson.toXContent(geometry, builder, ToXContent.EMPTY_PARAMS); - StreamInput input = BytesReference.bytes(builder).streamInput(); - - try (XContentParser parser = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, input)) { - return parser.map(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return GeoJson.toMap(geometry); } } diff --git a/server/src/test/java/org/elasticsearch/common/geo/GeoJsonSerializationTests.java b/server/src/test/java/org/elasticsearch/common/geo/GeoJsonSerializationTests.java index e3974872dee69..de3d23f75b9bf 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/GeoJsonSerializationTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/GeoJsonSerializationTests.java @@ -19,10 +19,16 @@ package org.elasticsearch.common.geo; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.geo.GeometryTestUtils; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.utils.GeographyValidator; @@ -30,6 +36,7 @@ import org.elasticsearch.test.ESTestCase; import java.io.IOException; +import java.util.Map; import java.util.Objects; import java.util.function.Supplier; @@ -41,12 +48,13 @@ import static org.elasticsearch.geo.GeometryTestUtils.randomMultiPolygon; import static org.elasticsearch.geo.GeometryTestUtils.randomPoint; import static org.elasticsearch.geo.GeometryTestUtils.randomPolygon; +import static org.hamcrest.Matchers.equalTo; public class GeoJsonSerializationTests extends ESTestCase { private static class GeometryWrapper implements ToXContentObject { - private Geometry geometry; + private final Geometry geometry; private static final GeoJson PARSER = new GeoJson(true, false, new GeographyValidator(true)); GeometryWrapper(Geometry geometry) { @@ -126,4 +134,19 @@ public void testGeometryCollection() throws IOException { public void testCircle() throws IOException { xContentTest(() -> randomCircle(randomBoolean())); } + + public void testToMap() throws IOException { + for (int i = 0; i < 10; i++) { + Geometry geometry = GeometryTestUtils.randomGeometry(randomBoolean()); + XContentBuilder builder = XContentFactory.jsonBuilder(); + GeoJson.toXContent(geometry, builder, ToXContent.EMPTY_PARAMS); + StreamInput input = BytesReference.bytes(builder).streamInput(); + + try (XContentParser parser = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, input)) { + Map map = GeoJson.toMap(geometry); + assertThat(parser.map(), equalTo(map)); + } + } + } }