Skip to content

Commit 44cd52e

Browse files
authored
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.
1 parent 045cc20 commit 44cd52e

File tree

3 files changed

+75
-33
lines changed

3 files changed

+75
-33
lines changed

docs/changelog/91105.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 91105
2+
summary: Fix handling indexed envelopes crossing the dateline in mvt API
3+
area: Geo
4+
type: bug
5+
issues:
6+
- 91060

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

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,61 @@ private Geometry normalize(Geometry geometry) {
150150
}
151151
}
152152

153-
public void testFetchSourceValueDateLine() throws IOException {
153+
public void testFetchSourcePolygonDateLine() throws IOException {
154+
assertFetchSourceGeometry(
155+
"POLYGON((170 -10, -170 -10, -170 10, 170 10, 170 -10))",
156+
"MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
157+
+ "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))",
158+
Map.of(
159+
"type",
160+
"MultiPolygon",
161+
"coordinates",
162+
List.of(
163+
List.of(
164+
List.of(
165+
List.of(180.0, -10.0),
166+
List.of(180.0, 10.0),
167+
List.of(170.0, 10.0),
168+
List.of(170.0, -10.0),
169+
List.of(180.0, -10.0)
170+
)
171+
),
172+
List.of(
173+
List.of(
174+
List.of(-180.0, 10.0),
175+
List.of(-180.0, -10.0),
176+
List.of(-170.0, -10.0),
177+
List.of(-170.0, 10.0),
178+
List.of(-180.0, 10.0)
179+
)
180+
)
181+
)
182+
),
183+
"MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
184+
+ "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))"
185+
);
186+
}
187+
188+
public void testFetchSourceEnvelope() throws IOException {
189+
assertFetchSourceGeometry(
190+
"BBOX(-10, 10, 10, -10)",
191+
"BBOX (-10.0, 10.0, 10.0, -10.0)",
192+
Map.of("type", "Envelope", "coordinates", List.of(List.of(-10.0, 10.0), List.of(10.0, -10.0))),
193+
"POLYGON ((-10 -10, 10 -10, 10 10, -10 10, -10 -10)))"
194+
);
195+
}
196+
197+
public void testFetchSourceEnvelopeDateLine() throws IOException {
198+
assertFetchSourceGeometry(
199+
"BBOX(10, -10, 10, -10)",
200+
"BBOX (10.0, -10.0, 10.0, -10.0)",
201+
Map.of("type", "Envelope", "coordinates", List.of(List.of(10.0, 10.0), List.of(-10.0, -10.0))),
202+
"MULTIPOLYGON (((-180 -10, -10 -10, -10 10, -180 10, -180 -10)), ((10 -10, 180 -10, 180 10, 10 10, 10 -10)))"
203+
);
204+
}
205+
206+
private void assertFetchSourceGeometry(Object sourceValue, String wktValue, Map<String, Object> jsonValue, String mvtEquivalentAsWKT)
207+
throws IOException {
154208
final GeoFormatterFactory<Geometry> geoFormatterFactory = new GeoFormatterFactory<>(
155209
new SpatialGeometryFormatterExtension().getGeometryFormatterFactories()
156210
);
@@ -161,38 +215,13 @@ public void testFetchSourceValueDateLine() throws IOException {
161215
false,
162216
geoFormatterFactory
163217
).build(MapperBuilderContext.ROOT).fieldType();
164-
// Test a polygon crossing the dateline
165-
Object sourceValue = "POLYGON((170 -10, -170 -10, -170 10, 170 10, 170 -10))";
166-
String polygonDateLine = "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
167-
+ "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))";
168-
Map<String, Object> jsonPolygonDateLine = Map.of(
169-
"type",
170-
"MultiPolygon",
171-
"coordinates",
172-
List.of(
173-
List.of(
174-
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))
175-
),
176-
List.of(
177-
List.of(
178-
List.of(-180.0, 10.0),
179-
List.of(-180.0, -10.0),
180-
List.of(-170.0, -10.0),
181-
List.of(-170.0, 10.0),
182-
List.of(-180.0, 10.0)
183-
)
184-
)
185-
)
186-
);
187-
188-
assertEquals(List.of(jsonPolygonDateLine), fetchSourceValue(mapper, sourceValue, null));
189-
assertEquals(List.of(polygonDateLine), fetchSourceValue(mapper, sourceValue, "wkt"));
190218

191-
String mvtPolygonDateLine = "MULTIPOLYGON (((180.0 -10.0, 180.0 10.0, 170.0 10.0, 170.0 -10.0, 180.0 -10.0)),"
192-
+ "((-180.0 10.0, -180.0 -10.0, -170.0 -10.0, -170.0 10.0, -180.0 10.0)))";
219+
assertEquals(List.of(jsonValue), fetchSourceValue(mapper, sourceValue, null));
220+
assertEquals(List.of(wktValue), fetchSourceValue(mapper, sourceValue, "wkt"));
193221

194-
List<?> mvtExpected = fetchSourceValue(mapper, mvtPolygonDateLine, "mvt(0/0/0@256)");
195-
List<?> mvt = fetchSourceValue(mapper, sourceValue, "mvt(0/0/0@256)");
222+
final int extent = randomIntBetween(256, 4096);
223+
List<?> mvtExpected = fetchSourceValue(mapper, mvtEquivalentAsWKT, "mvt(0/0/0@" + extent + ")");
224+
List<?> mvt = fetchSourceValue(mapper, sourceValue, "mvt(0/0/0@" + extent + ")");
196225
assertThat(mvt.size(), Matchers.equalTo(1));
197226
assertThat(mvt.size(), Matchers.equalTo(mvtExpected.size()));
198227
assertThat(mvtExpected.get(0), Matchers.instanceOf(byte[].class));

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,15 @@ public org.locationtech.jts.geom.Geometry visit(Rectangle rectangle) throws Runt
288288
final double yMin = SphericalMercatorUtils.latToSphericalMercator(rectangle.getMinY());
289289
final double xMax = SphericalMercatorUtils.lonToSphericalMercator(rectangle.getMaxX());
290290
final double yMax = SphericalMercatorUtils.latToSphericalMercator(rectangle.getMaxY());
291-
final Envelope envelope = new Envelope(xMin, xMax, yMin, yMax);
292-
return geomFactory.toGeometry(envelope);
291+
if (rectangle.getMinX() > rectangle.getMaxX()) {
292+
// crosses dateline
293+
final Envelope westEnvelope = new Envelope(-SphericalMercatorUtils.MERCATOR_BOUNDS, xMax, yMin, yMax);
294+
final Envelope eastEnvelope = new Envelope(xMin, SphericalMercatorUtils.MERCATOR_BOUNDS, yMin, yMax);
295+
return geomFactory.buildGeometry(List.of(geomFactory.toGeometry(westEnvelope), geomFactory.toGeometry(eastEnvelope)));
296+
} else {
297+
final Envelope envelope = new Envelope(xMin, xMax, yMin, yMax);
298+
return geomFactory.toGeometry(envelope);
299+
}
293300
}
294301
}
295302

0 commit comments

Comments
 (0)