Skip to content

Commit

Permalink
Vector tiles: order hits by geometry size by default (elastic#75621)
Browse files Browse the repository at this point in the history
_mvt end point gives priority to geometries with larger area
  • Loading branch information
iverase authored and ywangd committed Jul 30, 2021
1 parent 839ed48 commit bccca8b
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Geometry {
int getDimensionalType()
org.elasticsearch.common.geo.GeoPoint getCentroid()
org.elasticsearch.common.geo.GeoBoundingBox getBoundingBox()
double getMercatorWidth()
double getMercatorHeight()
}

class org.elasticsearch.index.fielddata.ScriptDocValues$GeoPoints {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,20 @@ setup:
source: "doc['geo_point'].getDimensionalType()"
- match: { hits.hits.0.fields.type.0: 0 }

- do:
search:
rest_total_hits_as_int: true
body:
script_fields:
width:
script:
source: "doc['geo_point'].getMercatorWidth()"
height:
script:
source: "doc['geo_point'].getMercatorHeight()"
- match: { hits.hits.0.fields.width.0: 0.0 }
- match: { hits.hits.0.fields.height.0: 0.0 }

---
"ip":
- do:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.xpack.vectortile.feature;
package org.elasticsearch.common.geo;

import org.elasticsearch.geometry.Rectangle;

/**
* Utility functions to transforms WGS84 coordinates into spherical mercator.
*/
class SphericalMercatorUtils {
public class SphericalMercatorUtils {

private static double MERCATOR_FACTOR = 20037508.34 / 180.0;
public static final double MERCATOR_BOUNDS = 20037508.34;
private static final double MERCATOR_FACTOR = MERCATOR_BOUNDS / 180.0;

/**
* Transforms WGS84 longitude to a Spherical mercator longitude
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ public abstract static class Geometry<T> extends ScriptDocValues<T> {
public abstract GeoBoundingBox getBoundingBox();
/** Returns the centroid of this geometry */
public abstract GeoPoint getCentroid();
/** Returns the width of the bounding box diagonal in the spherical Mercator projection (meters) */
public abstract double getMercatorWidth();
/** Returns the height of the bounding box diagonal in the spherical Mercator projection (meters) */
public abstract double getMercatorHeight();
}

public static final class GeoPoints extends Geometry<GeoPoint> {
Expand Down Expand Up @@ -418,6 +422,16 @@ public GeoPoint getCentroid() {
return size() == 0 ? null : centroid;
}

@Override
public double getMercatorWidth() {
return 0;
}

@Override
public double getMercatorHeight() {
return 0;
}

@Override
public GeoBoundingBox getBoundingBox() {
return size() == 0 ? null : boundingBox;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.common.geo;

import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;

import static org.elasticsearch.common.geo.SphericalMercatorUtils.MERCATOR_BOUNDS;
import static org.elasticsearch.common.geo.SphericalMercatorUtils.lonToSphericalMercator;
import static org.elasticsearch.common.geo.SphericalMercatorUtils.latToSphericalMercator;
import static org.elasticsearch.common.geo.SphericalMercatorUtils.recToSphericalMercator;

public class SphericalMercatorUtilTests extends ESTestCase {

public void testLon() {
assertThat(lonToSphericalMercator(180.0), Matchers.equalTo(MERCATOR_BOUNDS));
assertThat(lonToSphericalMercator(-180.0), Matchers.equalTo(-MERCATOR_BOUNDS));
assertThat(lonToSphericalMercator(0.0), Matchers.equalTo(0.0));
final double lon = lonToSphericalMercator(GeometryTestUtils.randomLon());
assertThat(lon, Matchers.greaterThanOrEqualTo(-MERCATOR_BOUNDS));
assertThat(lon, Matchers.lessThanOrEqualTo(MERCATOR_BOUNDS));
}

public void testLat() {
assertThat(latToSphericalMercator(GeoTileUtils.LATITUDE_MASK), Matchers.closeTo(MERCATOR_BOUNDS, 1e-7));
assertThat(latToSphericalMercator(-GeoTileUtils.LATITUDE_MASK), Matchers.closeTo(-MERCATOR_BOUNDS, 1e-7));
assertThat(latToSphericalMercator(0.0), Matchers.closeTo(0, 1e-7));
final double lat = latToSphericalMercator(randomValueOtherThanMany(
l -> l >= GeoTileUtils.LATITUDE_MASK || l <= -GeoTileUtils.LATITUDE_MASK,
GeometryTestUtils::randomLat
));
assertThat(lat, Matchers.greaterThanOrEqualTo(-MERCATOR_BOUNDS));
assertThat(lat, Matchers.lessThanOrEqualTo(MERCATOR_BOUNDS));
}

public void testRectangle() {
Rectangle rect = GeometryTestUtils.randomRectangle();
Rectangle mercatorRect = recToSphericalMercator(rect);
assertThat(mercatorRect.getMinX(), Matchers.equalTo(lonToSphericalMercator(rect.getMinX())));
assertThat(mercatorRect.getMaxX(), Matchers.equalTo(lonToSphericalMercator(rect.getMaxX())));
assertThat(mercatorRect.getMinY(), Matchers.equalTo(latToSphericalMercator(rect.getMinY())));
assertThat(mercatorRect.getMaxY(), Matchers.equalTo(latToSphericalMercator(rect.getMaxY())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.util.Collection;
import java.util.Collections;

import static org.elasticsearch.common.geo.SphericalMercatorUtils.latToSphericalMercator;
import static org.elasticsearch.common.geo.SphericalMercatorUtils.lonToSphericalMercator;

public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoShapeFieldData {

@Override
Expand Down Expand Up @@ -88,6 +91,16 @@ public GeoPoint getCentroid() {
return value == null ? null : centroid;
}

@Override
public double getMercatorWidth() {
return lonToSphericalMercator(boundingBox.right()) - lonToSphericalMercator(boundingBox.left());
}

@Override
public double getMercatorHeight() {
return latToSphericalMercator(boundingBox.top()) - latToSphericalMercator(boundingBox.bottom());
}

@Override
public GeoBoundingBox getBoundingBox() {
return value == null ? null : boundingBox;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,19 @@ setup:
source: "doc['geo_shape'].get(0)"

- match: { error.root_cause.0.reason: "cannot write xcontent for geo_shape doc value" }

---
"diagonal length":
- do:
search:
rest_total_hits_as_int: true
body:
script_fields:
width:
script:
source: "doc['geo_shape'].getMercatorWidth()"
height:
script:
source: "doc['geo_shape'].getMercatorHeight()"
- match: { hits.hits.0.fields.width.0: 389.62170283915475 }
- match: { hits.hits.0.fields.height.0: 333.37976840604097 }
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public void testBasicGet() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testIndexAllGet() throws Exception {
Expand All @@ -248,7 +248,7 @@ public void testIndexAllGet() throws Exception {
// 33 points, 1 polygon and two from geometry collection
assertLayer(tile, HITS_LAYER, 4096, 36, 1);
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testExtent() throws Exception {
Expand All @@ -258,7 +258,7 @@ public void testExtent() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 256, 33, 1);
assertLayer(tile, AGGS_LAYER, 256, 1, 1);
assertLayer(tile, META_LAYER, 256, 1, 14);
assertLayer(tile, META_LAYER, 256, 1, 13);
}

public void testExtentURL() throws Exception {
Expand All @@ -271,7 +271,7 @@ public void testExtentURL() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 512, 33, 1);
assertLayer(tile, AGGS_LAYER, 512, 1, 1);
assertLayer(tile, META_LAYER, 512, 1, 14);
assertLayer(tile, META_LAYER, 512, 1, 13);
}

public void testExactBounds() throws Exception {
Expand Down Expand Up @@ -327,7 +327,7 @@ public void testGridPrecision() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}
{
final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS + "/_mvt/location/" + z + "/" + x + "/" + y);
Expand All @@ -345,7 +345,7 @@ public void testGridType() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POINT);
}
{
Expand All @@ -355,7 +355,7 @@ public void testGridType() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POLYGON);
}
{
Expand All @@ -376,7 +376,7 @@ public void testGridTypeURL() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
assertFeatureType(tile, AGGS_LAYER, VectorTile.Tile.GeomType.POLYGON);
}

Expand All @@ -386,7 +386,7 @@ public void testNoAggLayer() throws Exception {
final VectorTile.Tile tile = execute(mvtRequest);
assertThat(tile.getLayersCount(), Matchers.equalTo(2));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, META_LAYER, 4096, 1, 9);
assertLayer(tile, META_LAYER, 4096, 1, 8);
}

public void testNoAggLayerURL() throws Exception {
Expand All @@ -398,7 +398,7 @@ public void testNoAggLayerURL() throws Exception {
final VectorTile.Tile tile = execute(mvtRequest);
assertThat(tile.getLayersCount(), Matchers.equalTo(2));
assertLayer(tile, HITS_LAYER, 4096, 33, 1);
assertLayer(tile, META_LAYER, 4096, 1, 9);
assertLayer(tile, META_LAYER, 4096, 1, 8);
}

public void testNoHitsLayer() throws Exception {
Expand All @@ -419,6 +419,31 @@ public void testNoHitsLayerURL() throws Exception {
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testDefaultSort() throws Exception {
{
final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS_SHAPES + "/_mvt/location/" + z + "/" + x + "/" + y);
mvtRequest.setJsonEntity("{\"size\": 100 }");
final VectorTile.Tile tile = execute(mvtRequest);
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 34, 1);
final VectorTile.Tile.Layer layer = getLayer(tile, HITS_LAYER);
assertThat(layer.getFeatures(0).getType(), Matchers.equalTo(VectorTile.Tile.GeomType.POLYGON));
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}
{
final Request mvtRequest = new Request(getHttpMethod(), INDEX_POINTS_SHAPES + "/_mvt/location/" + z + "/" + x + "/" + y);
mvtRequest.setJsonEntity("{\"size\": 100, \"sort\" : []}"); // override default sort
final VectorTile.Tile tile = execute(mvtRequest);
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 34, 1);
final VectorTile.Tile.Layer layer = getLayer(tile, HITS_LAYER);
assertThat(layer.getFeatures(0).getType(), Matchers.equalTo(VectorTile.Tile.GeomType.POINT));
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
}
}

public void testRuntimeFieldWithSort() throws Exception {
String runtimeMapping = "\"runtime_mappings\": {\n"
+ " \"width\": {\n"
Expand Down Expand Up @@ -498,7 +523,7 @@ public void testBasicQueryGet() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 1, 1);
assertLayer(tile, AGGS_LAYER, 4096, 1, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testBasicShape() throws Exception {
Expand All @@ -507,7 +532,7 @@ public void testBasicShape() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 1, 1);
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testWithFields() throws Exception {
Expand All @@ -517,7 +542,7 @@ public void testWithFields() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 1, 3);
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 1);
assertLayer(tile, META_LAYER, 4096, 1, 14);
assertLayer(tile, META_LAYER, 4096, 1, 13);
}

public void testMinAgg() throws Exception {
Expand All @@ -537,7 +562,7 @@ public void testMinAgg() throws Exception {
assertThat(tile.getLayersCount(), Matchers.equalTo(3));
assertLayer(tile, HITS_LAYER, 4096, 1, 1);
assertLayer(tile, AGGS_LAYER, 4096, 256 * 256, 2);
assertLayer(tile, META_LAYER, 4096, 1, 19);
assertLayer(tile, META_LAYER, 4096, 1, 18);
}

private String getHttpMethod() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.wdtinc.mapbox_vector_tile.build.MvtLayerParams;
import com.wdtinc.mapbox_vector_tile.build.MvtLayerProps;

import org.elasticsearch.common.geo.SphericalMercatorUtils;
import org.elasticsearch.geometry.Circle;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package org.elasticsearch.xpack.vectortile.feature;

import org.apache.lucene.util.BitUtil;
import org.elasticsearch.common.geo.SphericalMercatorUtils;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Rectangle;
Expand Down
Loading

0 comments on commit bccca8b

Please sign in to comment.