Browse Source

Fix handling indexed envelopes crossing the dateline in mvt API (#91105)

This commit makes sure an envelope crossing the dateline generates a mutipolygon when creating a mvt feature 
so it is render properly.
Ignacio Vera 3 years ago
parent
commit
44cd52ee15

+ 6 - 0
docs/changelog/91105.yaml

@@ -0,0 +1,6 @@
+pr: 91105
+summary: Fix handling indexed envelopes crossing the dateline in mvt API
+area: Geo
+type: bug
+issues:
+ - 91060

+ 60 - 31
x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldTypeTests.java

@@ -150,7 +150,61 @@ public class GeoShapeWithDocValuesFieldTypeTests extends FieldTypeTestCase {
         }
     }
 
-    public void testFetchSourceValueDateLine() throws IOException {
+    public void testFetchSourcePolygonDateLine() throws IOException {
+        assertFetchSourceGeometry(
+            "POLYGON((170 -10, -170 -10, -170 10, 170 10, 170 -10))",
+            "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
+                + "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))",
+            Map.of(
+                "type",
+                "MultiPolygon",
+                "coordinates",
+                List.of(
+                    List.of(
+                        List.of(
+                            List.of(180.0, -10.0),
+                            List.of(180.0, 10.0),
+                            List.of(170.0, 10.0),
+                            List.of(170.0, -10.0),
+                            List.of(180.0, -10.0)
+                        )
+                    ),
+                    List.of(
+                        List.of(
+                            List.of(-180.0, 10.0),
+                            List.of(-180.0, -10.0),
+                            List.of(-170.0, -10.0),
+                            List.of(-170.0, 10.0),
+                            List.of(-180.0, 10.0)
+                        )
+                    )
+                )
+            ),
+            "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
+                + "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))"
+        );
+    }
+
+    public void testFetchSourceEnvelope() throws IOException {
+        assertFetchSourceGeometry(
+            "BBOX(-10, 10, 10, -10)",
+            "BBOX (-10.0, 10.0, 10.0, -10.0)",
+            Map.of("type", "Envelope", "coordinates", List.of(List.of(-10.0, 10.0), List.of(10.0, -10.0))),
+            "POLYGON ((-10 -10, 10 -10, 10 10, -10 10, -10 -10)))"
+        );
+    }
+
+    public void testFetchSourceEnvelopeDateLine() throws IOException {
+        assertFetchSourceGeometry(
+            "BBOX(10, -10, 10, -10)",
+            "BBOX (10.0, -10.0, 10.0, -10.0)",
+            Map.of("type", "Envelope", "coordinates", List.of(List.of(10.0, 10.0), List.of(-10.0, -10.0))),
+            "MULTIPOLYGON (((-180 -10, -10 -10, -10 10, -180 10, -180 -10)), ((10 -10, 180 -10, 180 10, 10 10, 10 -10)))"
+        );
+    }
+
+    private void assertFetchSourceGeometry(Object sourceValue, String wktValue, Map<String, Object> jsonValue, String mvtEquivalentAsWKT)
+        throws IOException {
         final GeoFormatterFactory<Geometry> geoFormatterFactory = new GeoFormatterFactory<>(
             new SpatialGeometryFormatterExtension().getGeometryFormatterFactories()
         );
@@ -161,38 +215,13 @@ public class GeoShapeWithDocValuesFieldTypeTests extends FieldTypeTestCase {
             false,
             geoFormatterFactory
         ).build(MapperBuilderContext.ROOT).fieldType();
-        // Test a polygon crossing the dateline
-        Object sourceValue = "POLYGON((170 -10, -170 -10, -170 10, 170 10, 170 -10))";
-        String polygonDateLine = "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
-            + "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))";
-        Map<String, Object> jsonPolygonDateLine = Map.of(
-            "type",
-            "MultiPolygon",
-            "coordinates",
-            List.of(
-                List.of(
-                    List.of(List.of(180.0, -10.0), List.of(180.0, 10.0), List.of(170.0, 10.0), List.of(170.0, -10.0), List.of(180.0, -10.0))
-                ),
-                List.of(
-                    List.of(
-                        List.of(-180.0, 10.0),
-                        List.of(-180.0, -10.0),
-                        List.of(-170.0, -10.0),
-                        List.of(-170.0, 10.0),
-                        List.of(-180.0, 10.0)
-                    )
-                )
-            )
-        );
-
-        assertEquals(List.of(jsonPolygonDateLine), fetchSourceValue(mapper, sourceValue, null));
-        assertEquals(List.of(polygonDateLine), fetchSourceValue(mapper, sourceValue, "wkt"));
 
-        String mvtPolygonDateLine = "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
-            + "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))";
+        assertEquals(List.of(jsonValue), fetchSourceValue(mapper, sourceValue, null));
+        assertEquals(List.of(wktValue), fetchSourceValue(mapper, sourceValue, "wkt"));
 
-        List<?> mvtExpected = fetchSourceValue(mapper, mvtPolygonDateLine, "mvt(0/0/0@256)");
-        List<?> mvt = fetchSourceValue(mapper, sourceValue, "mvt(0/0/0@256)");
+        final int extent = randomIntBetween(256, 4096);
+        List<?> mvtExpected = fetchSourceValue(mapper, mvtEquivalentAsWKT, "mvt(0/0/0@" + extent + ")");
+        List<?> mvt = fetchSourceValue(mapper, sourceValue, "mvt(0/0/0@" + extent + ")");
         assertThat(mvt.size(), Matchers.equalTo(1));
         assertThat(mvt.size(), Matchers.equalTo(mvtExpected.size()));
         assertThat(mvtExpected.get(0), Matchers.instanceOf(byte[].class));

+ 9 - 2
x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/feature/FeatureFactory.java

@@ -288,8 +288,15 @@ public class FeatureFactory {
             final double yMin = SphericalMercatorUtils.latToSphericalMercator(rectangle.getMinY());
             final double xMax = SphericalMercatorUtils.lonToSphericalMercator(rectangle.getMaxX());
             final double yMax = SphericalMercatorUtils.latToSphericalMercator(rectangle.getMaxY());
-            final Envelope envelope = new Envelope(xMin, xMax, yMin, yMax);
-            return geomFactory.toGeometry(envelope);
+            if (rectangle.getMinX() > rectangle.getMaxX()) {
+                // crosses dateline
+                final Envelope westEnvelope = new Envelope(-SphericalMercatorUtils.MERCATOR_BOUNDS, xMax, yMin, yMax);
+                final Envelope eastEnvelope = new Envelope(xMin, SphericalMercatorUtils.MERCATOR_BOUNDS, yMin, yMax);
+                return geomFactory.buildGeometry(List.of(geomFactory.toGeometry(westEnvelope), geomFactory.toGeometry(eastEnvelope)));
+            } else {
+                final Envelope envelope = new Envelope(xMin, xMax, yMin, yMax);
+                return geomFactory.toGeometry(envelope);
+            }
         }
     }