From 75d6842a7173b4062d49218a3686bd2990fe3ee0 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 14 Jan 2020 18:28:47 -0500 Subject: [PATCH] [Maps] expand extent filter to tile boundaries (#54276) * [Maps] expand extent filter to tile boundaries * fix functional test * simplify expandToTileBoundaries Co-authored-by: Elastic Machine --- .../es_geo_grid_source/geo_tile_utils.js | 37 ++++++++++++++++++- .../es_geo_grid_source/geo_tile_utils.test.js | 18 ++++++++- .../maps/public/layers/sources/es_source.js | 6 ++- .../test/functional/page_objects/gis_page.js | 12 ++++-- 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js index 58694be132b1e0..da0bc1685f2237 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js @@ -11,6 +11,10 @@ const ZOOM_TILE_KEY_INDEX = 0; const X_TILE_KEY_INDEX = 1; const Y_TILE_KEY_INDEX = 2; +function getTileCount(zoom) { + return Math.pow(2, zoom); +} + export function parseTileKey(tileKey) { const tileKeyParts = tileKey.split('/'); @@ -21,7 +25,7 @@ export function parseTileKey(tileKey) { const zoom = parseInt(tileKeyParts[ZOOM_TILE_KEY_INDEX], 10); const x = parseInt(tileKeyParts[X_TILE_KEY_INDEX], 10); const y = parseInt(tileKeyParts[Y_TILE_KEY_INDEX], 10); - const tileCount = Math.pow(2, zoom); + const tileCount = getTileCount(zoom); if (x >= tileCount) { throw new Error( @@ -77,3 +81,34 @@ export function getTileBoundingBox(tileKey) { right: tileToLongitude(x + 1, tileCount), }; } + +function sec(value) { + return 1 / Math.cos(value); +} + +function latitudeToTile(lat, tileCount) { + const radians = (lat * Math.PI) / 180; + const y = ((1 - Math.log(Math.tan(radians) + sec(radians)) / Math.PI) / 2) * tileCount; + return Math.floor(y); +} + +function longitudeToTile(lon, tileCount) { + const x = ((lon + 180) / 360) * tileCount; + return Math.floor(x); +} + +export function expandToTileBoundaries(extent, zoom) { + const tileCount = getTileCount(zoom); + + const upperLeftX = longitudeToTile(extent.minLon, tileCount); + const upperLeftY = latitudeToTile(Math.min(extent.maxLat, 90), tileCount); + const lowerRightX = longitudeToTile(extent.maxLon, tileCount); + const lowerRightY = latitudeToTile(Math.max(extent.minLat, -90), tileCount); + + return { + minLon: tileToLongitude(upperLeftX, tileCount), + minLat: tileToLatitude(lowerRightY + 1, tileCount), + maxLon: tileToLongitude(lowerRightX + 1, tileCount), + maxLat: tileToLatitude(upperLeftY, tileCount), + }; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js index ad5ed994b695ce..ae2623e1687663 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parseTileKey, getTileBoundingBox } from './geo_tile_utils'; +import { parseTileKey, getTileBoundingBox, expandToTileBoundaries } from './geo_tile_utils'; it('Should parse tile key', () => { expect(parseTileKey('15/23423/1867')).toEqual({ @@ -34,3 +34,19 @@ it('Should convert tile key to geojson Polygon with extra precision', () => { left: -73.9839292, }); }); + +it('Should expand extent to align boundaries with tile boundaries', () => { + const extent = { + maxLat: 12.5, + maxLon: 102.5, + minLat: 2.5, + minLon: 92.5, + }; + const tileAlignedExtent = expandToTileBoundaries(extent, 7); + expect(tileAlignedExtent).toEqual({ + maxLat: 13.9234, + maxLon: 104.0625, + minLat: 0, + minLon: 90, + }); +}); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 0399bd74086f63..26cc7ece667539 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -19,6 +19,7 @@ import uuid from 'uuid/v4'; import { copyPersistentState } from '../../reducers/util'; import { ES_GEO_FIELD_TYPE, METRIC_TYPE } from '../../../common/constants'; import { DataRequestAbortError } from '../util/data_request'; +import { expandToTileBoundaries } from './es_geo_grid_source/geo_tile_utils'; export class AbstractESSource extends AbstractVectorSource { static icon = 'logoElasticsearch'; @@ -117,7 +118,10 @@ export class AbstractESSource extends AbstractVectorSource { if (this.isFilterByMapBounds() && searchFilters.buffer) { //buffer can be empty const geoField = await this._getGeoField(); - allFilters.push(createExtentFilter(searchFilters.buffer, geoField.name, geoField.type)); + const buffer = this.isGeoGridPrecisionAware() + ? expandToTileBoundaries(searchFilters.buffer, searchFilters.geogridPrecision) + : searchFilters.buffer; + allFilters.push(createExtentFilter(buffer, geoField.name, geoField.type)); } if (isTimeAware) { allFilters.push(timefilter.createFilter(indexPattern, searchFilters.timeFilters)); diff --git a/x-pack/test/functional/page_objects/gis_page.js b/x-pack/test/functional/page_objects/gis_page.js index 386f3ff780389e..16cf665bf82eeb 100644 --- a/x-pack/test/functional/page_objects/gis_page.js +++ b/x-pack/test/functional/page_objects/gis_page.js @@ -213,12 +213,16 @@ export function GisPageProvider({ getService, getPageObjects }) { return links.length; } + async isSetViewPopoverOpen() { + return await testSubjects.exists('mapSetViewForm', { timeout: 500 }); + } + async openSetViewPopover() { - const isOpen = await testSubjects.exists('mapSetViewForm'); + const isOpen = await this.isSetViewPopoverOpen(); if (!isOpen) { await retry.try(async () => { await testSubjects.click('toggleSetViewVisibilityButton'); - const isOpenAfterClick = await testSubjects.exists('mapSetViewForm'); + const isOpenAfterClick = await this.isSetViewPopoverOpen(); if (!isOpenAfterClick) { throw new Error('set view popover not opened'); } @@ -227,11 +231,11 @@ export function GisPageProvider({ getService, getPageObjects }) { } async closeSetViewPopover() { - const isOpen = await testSubjects.exists('mapSetViewForm'); + const isOpen = await this.isSetViewPopoverOpen(); if (isOpen) { await retry.try(async () => { await testSubjects.click('toggleSetViewVisibilityButton'); - const isOpenAfterClick = await testSubjects.exists('mapSetViewForm'); + const isOpenAfterClick = await this.isSetViewPopoverOpen(); if (isOpenAfterClick) { throw new Error('set view popover not closed'); }