Skip to content

Commit

Permalink
[Maps] clamp MVT too many features polygon to tile boundary (elastic#…
Browse files Browse the repository at this point in the history
…90444) (elastic#90674)

* [Maps] clamp MVT too many features polygon to tile boundary

* add mapbox_styles to index.js

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
nreese and kibanamachine authored Feb 8, 2021
1 parent fa5933f commit deab7aa
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 235 deletions.
84 changes: 29 additions & 55 deletions x-pack/plugins/maps/server/mvt/get_tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import { convertRegularRespToGeoJson, hitsToGeoJson } from '../../common/elasticsearch_util';
import { flattenHit } from './util';
import { ESBounds, tile2lat, tile2long, tileToESBbox } from '../../common/geo_tile_utils';
import { ESBounds, tileToESBbox } from '../../common/geo_tile_utils';
import { getCentroidFeatures } from '../../common/get_centroid_features';

export async function getGridTile({
Expand Down Expand Up @@ -53,35 +53,14 @@ export async function getGridTile({
geoFieldType: ES_GEO_FIELD_TYPE;
searchSessionId?: string;
}): Promise<Buffer | null> {
const esBbox: ESBounds = tileToESBbox(x, y, z);
try {
let bboxFilter;
if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT) {
bboxFilter = {
geo_bounding_box: {
[geometryFieldName]: esBbox,
},
};
} else if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_SHAPE) {
const geojsonPolygon = tileToGeoJsonPolygon(x, y, z);
bboxFilter = {
geo_shape: {
[geometryFieldName]: {
shape: geojsonPolygon,
relation: 'INTERSECTS',
},
},
};
} else {
throw new Error(`${geoFieldType} is not valid geo field-type`);
}
requestBody.query.bool.filter.push(bboxFilter);

const tileBounds: ESBounds = tileToESBbox(x, y, z);
requestBody.query.bool.filter.push(getTileSpatialFilter(geometryFieldName, tileBounds));
requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.precision = Math.min(
z + SUPER_FINE_ZOOM_DELTA,
MAX_ZOOM
);
requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.bounds = esBbox;
requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.bounds = tileBounds;

const response = await context
.search!.search(
Expand Down Expand Up @@ -134,14 +113,9 @@ export async function getTile({
}): Promise<Buffer | null> {
let features: Feature[];
try {
requestBody.query.bool.filter.push({
geo_shape: {
[geometryFieldName]: {
shape: tileToGeoJsonPolygon(x, y, z),
relation: 'INTERSECTS',
},
},
});
requestBody.query.bool.filter.push(
getTileSpatialFilter(geometryFieldName, tileToESBbox(x, y, z))
);

const searchOptions = {
sessionId: searchSessionId,
Expand Down Expand Up @@ -193,7 +167,8 @@ export async function getTile({
[KBN_TOO_MANY_FEATURES_PROPERTY]: true,
},
geometry: esBboxToGeoJsonPolygon(
bboxResponse.rawResponse.aggregations.data_bounds.bounds
bboxResponse.rawResponse.aggregations.data_bounds.bounds,
tileToESBbox(x, y, z)
),
},
];
Expand Down Expand Up @@ -244,32 +219,31 @@ export async function getTile({
}
}

function tileToGeoJsonPolygon(x: number, y: number, z: number): Polygon {
const wLon = tile2long(x, z);
const sLat = tile2lat(y + 1, z);
const eLon = tile2long(x + 1, z);
const nLat = tile2lat(y, z);

function getTileSpatialFilter(geometryFieldName: string, tileBounds: ESBounds): unknown {
return {
type: 'Polygon',
coordinates: [
[
[wLon, sLat],
[wLon, nLat],
[eLon, nLat],
[eLon, sLat],
[wLon, sLat],
],
],
geo_shape: {
[geometryFieldName]: {
shape: {
type: 'envelope',
// upper left and lower right points of the shape to represent a bounding rectangle in the format [[minLon, maxLat], [maxLon, minLat]]
coordinates: [
[tileBounds.top_left.lon, tileBounds.top_left.lat],
[tileBounds.bottom_right.lon, tileBounds.bottom_right.lat],
],
},
relation: 'INTERSECTS',
},
},
};
}

function esBboxToGeoJsonPolygon(esBounds: ESBounds): Polygon {
let minLon = esBounds.top_left.lon;
const maxLon = esBounds.bottom_right.lon;
function esBboxToGeoJsonPolygon(esBounds: ESBounds, tileBounds: ESBounds): Polygon {
// Intersecting geo_shapes may push bounding box outside of tile so need to clamp to tile bounds.
let minLon = Math.max(esBounds.top_left.lon, tileBounds.top_left.lon);
const maxLon = Math.min(esBounds.bottom_right.lon, tileBounds.bottom_right.lon);
minLon = minLon > maxLon ? minLon - 360 : minLon; // fixes an ES bbox to straddle dateline
const minLat = esBounds.bottom_right.lat;
const maxLat = esBounds.top_left.lat;
const minLat = Math.max(esBounds.bottom_right.lat, tileBounds.bottom_right.lat);
const maxLat = Math.min(esBounds.top_left.lat, tileBounds.top_left.lat);

return {
type: 'Polygon',
Expand Down
1 change: 1 addition & 0 deletions x-pack/test/functional/apps/maps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default function ({ loadTestFile, getService }) {
loadTestFile(require.resolve('./es_geo_grid_source'));
loadTestFile(require.resolve('./es_pew_pew_source'));
loadTestFile(require.resolve('./joins'));
loadTestFile(require.resolve('./mapbox_styles'));
loadTestFile(require.resolve('./mvt_scaling'));
loadTestFile(require.resolve('./mvt_super_fine'));
loadTestFile(require.resolve('./add_layer_panel'));
Expand Down
34 changes: 0 additions & 34 deletions x-pack/test/functional/apps/maps/joins.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

import expect from '@kbn/expect';

import { MAPBOX_STYLES } from './mapbox_styles';

const JOIN_PROPERTY_NAME = '__kbnjoin__max_of_prop1__855ccb86-fe42-11e8-8eb2-f2801f1b9fd1';
const EXPECTED_JOIN_VALUES = {
alpha: 10,
Expand All @@ -18,10 +16,6 @@ const EXPECTED_JOIN_VALUES = {
};

const VECTOR_SOURCE_ID = 'n1t6f';
const CIRCLE_STYLE_LAYER_INDEX = 0;
const FILL_STYLE_LAYER_INDEX = 2;
const LINE_STYLE_LAYER_INDEX = 3;
const TOO_MANY_FEATURES_LAYER_INDEX = 4;

export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['maps']);
Expand Down Expand Up @@ -95,34 +89,6 @@ export default function ({ getPageObjects, getService }) {
});
});

it('should style fills, points, lines, and bounding-boxes independently', async () => {
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
const layersForVectorSource = mapboxStyle.layers.filter((mbLayer) => {
return mbLayer.id.startsWith(VECTOR_SOURCE_ID);
});

//circle layer for points
expect(layersForVectorSource[CIRCLE_STYLE_LAYER_INDEX]).to.eql(MAPBOX_STYLES.POINT_LAYER);

//fill layer
expect(layersForVectorSource[FILL_STYLE_LAYER_INDEX]).to.eql(MAPBOX_STYLES.FILL_LAYER);

//line layer for borders
expect(layersForVectorSource[LINE_STYLE_LAYER_INDEX]).to.eql(MAPBOX_STYLES.LINE_LAYER);

//Too many features layer (this is a static style config)
expect(layersForVectorSource[TOO_MANY_FEATURES_LAYER_INDEX]).to.eql({
id: 'n1t6f_toomanyfeatures',
type: 'fill',
source: 'n1t6f',
minzoom: 0,
maxzoom: 24,
filter: ['==', ['get', '__kbn_too_many_features__'], true],
layout: { visibility: 'visible' },
paint: { 'fill-pattern': '__kbn_too_many_features_image_id__', 'fill-opacity': 0.75 },
});
});

it('should flag only the joined features as visible', async () => {
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
const vectorSource = mapboxStyle.sources[VECTOR_SOURCE_ID];
Expand Down
Loading

0 comments on commit deab7aa

Please sign in to comment.