diff --git a/server/src/main/java/org/opensearch/common/geo/GeoJson.java b/server/src/main/java/org/opensearch/common/geo/GeoJson.java index a3cbb4c121acd..4d6786813d8e0 100644 --- a/server/src/main/java/org/opensearch/common/geo/GeoJson.java +++ b/server/src/main/java/org/opensearch/common/geo/GeoJson.java @@ -125,8 +125,13 @@ public XContentBuilder visit(Line line) throws IOException { } @Override - public XContentBuilder visit(LinearRing ring) { - throw new UnsupportedOperationException("linearRing cannot be serialized using GeoJson"); + public XContentBuilder visit(LinearRing ring) throws IOException { + builder.field(ShapeParser.FIELD_COORDINATES.getPreferredName()); + builder.startArray(); + for (int i = 0; i < ring.length(); i++) { + coordinatesToXContent(ring.getLat(i), ring.getLon(i), ring.getAlt(i)); + } + return builder.endArray(); } @Override @@ -254,7 +259,18 @@ public Void visit(Line line) { @Override public Void visit(LinearRing ring) { - throw new UnsupportedOperationException("linearRing cannot be serialized using GeoJson"); + List points = new ArrayList<>(ring.length()); + for (int i = 0; i < ring.length(); i++) { + List point = new ArrayList<>(); + point.add(ring.getX(i)); + point.add(ring.getY(i)); + if (ring.hasZ()) { + point.add(ring.getZ(i)); + } + points.add(point); + } + root.put(ShapeParser.FIELD_COORDINATES.getPreferredName(), points); + return null; } @Override @@ -428,6 +444,9 @@ private static Geometry createGeometry( case LINESTRING: verifyNulls(type, geometries, orientation, radius); return coordinates.asLineString(coerce); + case LINEARRING: + verifyNulls(type, geometries, orientation, radius); + return coordinates.asLinearRing(orientation != null ? orientation : defaultOrientation, coerce); case MULTILINESTRING: verifyNulls(type, geometries, orientation, radius); return coordinates.asMultiLineString(coerce); @@ -558,7 +577,7 @@ public String visit(Line line) { @Override public String visit(LinearRing ring) { - throw new UnsupportedOperationException("line ring cannot be serialized using GeoJson"); + return "LinearRing"; } @Override diff --git a/server/src/test/java/org/opensearch/common/geo/GeoJsonSerializationTests.java b/server/src/test/java/org/opensearch/common/geo/GeoJsonSerializationTests.java index 480d8dba8f78f..e1aaaacb33d7d 100644 --- a/server/src/test/java/org/opensearch/common/geo/GeoJsonSerializationTests.java +++ b/server/src/test/java/org/opensearch/common/geo/GeoJsonSerializationTests.java @@ -56,6 +56,7 @@ import static org.opensearch.geo.GeometryTestUtils.randomCircle; import static org.opensearch.geo.GeometryTestUtils.randomGeometryCollection; import static org.opensearch.geo.GeometryTestUtils.randomLine; +import static org.opensearch.geo.GeometryTestUtils.randomLinearRing; import static org.opensearch.geo.GeometryTestUtils.randomMultiLine; import static org.opensearch.geo.GeometryTestUtils.randomMultiPoint; import static org.opensearch.geo.GeometryTestUtils.randomMultiPolygon; @@ -121,6 +122,10 @@ public void testLineString() throws IOException { xContentTest(() -> randomLine(randomBoolean())); } + public void testLinearRing() throws IOException { + xContentTest(() -> randomLinearRing(randomBoolean())); + } + public void testMultiLineString() throws IOException { xContentTest(() -> randomMultiLine(randomBoolean())); } diff --git a/server/src/test/java/org/opensearch/search/geo/GeoPointShapeQueryTests.java b/server/src/test/java/org/opensearch/search/geo/GeoPointShapeQueryTests.java index b5d34a78ab5a4..b00f36ef52d4a 100644 --- a/server/src/test/java/org/opensearch/search/geo/GeoPointShapeQueryTests.java +++ b/server/src/test/java/org/opensearch/search/geo/GeoPointShapeQueryTests.java @@ -100,6 +100,7 @@ public void testProcessRelationSupport() throws Exception { client().prepareSearch("test") .setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, rectangle).relation(shapeRelation)) .get(); + fail("Expected " + shapeRelation + " query relation not supported for Field [" + defaultGeoFieldName + "]"); } catch (SearchPhaseExecutionException e) { assertThat( e.getCause().getMessage(), @@ -119,6 +120,7 @@ public void testQueryLine() throws Exception { try { client().prepareSearch("test").setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, line)).get(); + fail("Expected field [" + defaultGeoFieldName + "] does not support LINEARRING queries"); } catch (SearchPhaseExecutionException e) { assertThat(e.getCause().getMessage(), containsString("does not support " + GeoShapeType.LINESTRING + " queries")); } @@ -138,13 +140,12 @@ public void testQueryLinearRing() throws Exception { searchRequestBuilder.setQuery(queryBuilder); searchRequestBuilder.setIndices("test"); searchRequestBuilder.get(); + fail("Expected field [" + defaultGeoFieldName + "] does not support LINEARRING queries"); } catch (SearchPhaseExecutionException e) { assertThat( e.getCause().getMessage(), containsString("Field [" + defaultGeoFieldName + "] does not support LINEARRING queries") ); - } catch (UnsupportedOperationException e) { - assertThat(e.getMessage(), containsString("line ring cannot be serialized using GeoJson")); } } @@ -162,6 +163,7 @@ public void testQueryMultiLine() throws Exception { try { client().prepareSearch("test").setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiline)).get(); + fail("Expected field [" + defaultGeoFieldName + "] does not support " + GeoShapeType.MULTILINESTRING + " queries"); } catch (Exception e) { assertThat(e.getCause().getMessage(), containsString("does not support " + GeoShapeType.MULTILINESTRING + " queries")); } @@ -177,6 +179,7 @@ public void testQueryMultiPoint() throws Exception { try { client().prepareSearch("test").setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, multiPoint)).get(); + fail("Expected field [" + defaultGeoFieldName + "] does not support " + GeoShapeType.MULTIPOINT + " queries"); } catch (Exception e) { assertThat(e.getCause().getMessage(), containsString("does not support " + GeoShapeType.MULTIPOINT + " queries")); } @@ -192,6 +195,7 @@ public void testQueryPoint() throws Exception { try { client().prepareSearch("test").setQuery(QueryBuilders.geoShapeQuery(defaultGeoFieldName, point)).get(); + fail("Expected field [" + defaultGeoFieldName + "] does not support " + GeoShapeType.POINT + " queries"); } catch (Exception e) { assertThat(e.getCause().getMessage(), containsString("does not support " + GeoShapeType.POINT + " queries")); } diff --git a/test/framework/src/main/java/org/opensearch/geo/GeometryTestUtils.java b/test/framework/src/main/java/org/opensearch/geo/GeometryTestUtils.java index b588243803d30..36e80fd2484ec 100644 --- a/test/framework/src/main/java/org/opensearch/geo/GeometryTestUtils.java +++ b/test/framework/src/main/java/org/opensearch/geo/GeometryTestUtils.java @@ -102,6 +102,30 @@ public static Line randomLine(boolean hasAlts) { return new Line(lons, lats); } + public static LinearRing randomLinearRing(boolean hasAlts) { + // we use nextPolygon because it guarantees no duplicate points + org.apache.lucene.geo.Polygon lucenePolygon = GeoTestUtil.nextPolygon(); + int size = lucenePolygon.numPoints() - 1; + double[] lats = new double[size + 1]; + double[] lons = new double[size + 1]; + double[] alts = hasAlts ? new double[size + 1] : null; + for (int i = 0; i < size; i++) { + lats[i] = lucenePolygon.getPolyLat(i); + lons[i] = lucenePolygon.getPolyLon(i); + if (hasAlts) { + alts[i] = randomAlt(); + } + } + // first and last points of the linear ring must be the same + lats[size] = lats[0]; + lons[size] = lons[0]; + if (hasAlts) { + alts[size] = alts[0]; + return new LinearRing(lons, lats, alts); + } + return new LinearRing(lons, lats); + } + public static Point randomPoint() { return randomPoint(OpenSearchTestCase.randomBoolean()); }