mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Maps] use geo-tile aggregation instead of geohash precision (#29776)
This commit is contained in:
parent
cdf3266f23
commit
3ccf6793df
17 changed files with 292 additions and 169 deletions
63
src/ui/public/agg_types/buckets/geo_tile.js
Normal file
63
src/ui/public/agg_types/buckets/geo_tile.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { BucketAggType } from './_bucket_agg_type';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const geoTileBucketAgg = new BucketAggType({
|
||||
name: 'geotile_grid',
|
||||
title: i18n.translate('common.ui.aggTypes.buckets.geotileGridTitle', {
|
||||
defaultMessage: 'Geotile',
|
||||
}),
|
||||
params: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'field',
|
||||
filterFieldTypes: 'geo_point'
|
||||
},
|
||||
{
|
||||
name: 'useGeocentroid',
|
||||
default: true,
|
||||
write: _.noop
|
||||
},
|
||||
{
|
||||
name: 'precision',
|
||||
default: 0,
|
||||
}
|
||||
],
|
||||
getRequestAggs: function (agg) {
|
||||
const aggs = [];
|
||||
const params = agg.params;
|
||||
|
||||
aggs.push(agg);
|
||||
|
||||
if (params.useGeocentroid) {
|
||||
aggs.push(agg.aggConfigs.createAggConfig({
|
||||
type: 'geo_centroid',
|
||||
enabled: true,
|
||||
params: {
|
||||
field: agg.getField()
|
||||
}
|
||||
}, { addToAggConfigs: false }));
|
||||
}
|
||||
|
||||
return aggs;
|
||||
}
|
||||
});
|
|
@ -47,6 +47,7 @@ import { filterBucketAgg } from './buckets/filter';
|
|||
import { filtersBucketAgg } from './buckets/filters';
|
||||
import { significantTermsBucketAgg } from './buckets/significant_terms';
|
||||
import { geoHashBucketAgg } from './buckets/geo_hash';
|
||||
import { geoTileBucketAgg } from './buckets/geo_tile';
|
||||
import { bucketSumMetricAgg } from './metrics/bucket_sum';
|
||||
import { bucketAvgMetricAgg } from './metrics/bucket_avg';
|
||||
import { bucketMinMetricAgg } from './metrics/bucket_min';
|
||||
|
@ -86,7 +87,8 @@ const aggs = {
|
|||
filterBucketAgg,
|
||||
filtersBucketAgg,
|
||||
significantTermsBucketAgg,
|
||||
geoHashBucketAgg
|
||||
geoHashBucketAgg,
|
||||
geoTileBucketAgg,
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
@ -99,23 +99,6 @@ export function geoPointToGeometry(value) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
export function makeGeohashGridPolygon(geohashGridFeature) {
|
||||
const esBbox = geohashGridFeature.properties.geohash_meta.rectangle;
|
||||
return {
|
||||
type: 'Polygon',
|
||||
coordinates: [
|
||||
[
|
||||
[esBbox[0][1], esBbox[0][0]],
|
||||
[esBbox[1][1], esBbox[1][0]],
|
||||
[esBbox[2][1], esBbox[2][0]],
|
||||
[esBbox[3][1], esBbox[3][0]],
|
||||
[esBbox[0][1], esBbox[0][0]],
|
||||
]
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export function geoShapeToGeometry(value) {
|
||||
if (!value) {
|
||||
return [];
|
||||
|
|
|
@ -4,116 +4,101 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { decodeGeoHash } from 'ui/utils/decode_geo_hash';
|
||||
import { gridDimensions } from 'ui/vis/map/grid_dimensions';
|
||||
import { RENDER_AS } from './render_as';
|
||||
import { getTileBoundingBox } from './geo_tile_utils';
|
||||
|
||||
/*
|
||||
* Fork of ui/public/vis/map/convert_to_geojson.js that supports multiple metrics
|
||||
*/
|
||||
export function convertToGeoJson(tabifiedResponse) {
|
||||
const EMPTY_FEATURE_COLLECTION = {
|
||||
type: 'FeatureCollection',
|
||||
features: []
|
||||
};
|
||||
|
||||
let features;
|
||||
const min = Infinity;
|
||||
const max = -Infinity;
|
||||
let geoAgg;
|
||||
export function convertToGeoJson({ table, renderAs }) {
|
||||
|
||||
if (tabifiedResponse && tabifiedResponse.rows) {
|
||||
|
||||
const table = tabifiedResponse;
|
||||
const geohashColumn = table.columns.find(column => column.aggConfig.type.dslName === 'geohash_grid');
|
||||
|
||||
if (!geohashColumn) {
|
||||
features = [];
|
||||
} else {
|
||||
|
||||
geoAgg = geohashColumn.aggConfig;
|
||||
|
||||
const metricColumns = table.columns.filter(column => {
|
||||
return column.aggConfig.type.type === 'metrics'
|
||||
&& column.aggConfig.type.dslName !== 'geo_centroid';
|
||||
});
|
||||
const geocentroidColumn = table.columns.find(column => column.aggConfig.type.dslName === 'geo_centroid');
|
||||
|
||||
features = table.rows.map(row => {
|
||||
|
||||
const geohash = row[geohashColumn.id];
|
||||
if (!geohash) return false;
|
||||
const geohashLocation = decodeGeoHash(geohash);
|
||||
|
||||
let pointCoordinates;
|
||||
if (geocentroidColumn) {
|
||||
const location = row[geocentroidColumn.id];
|
||||
pointCoordinates = [location.lon, location.lat];
|
||||
} else {
|
||||
pointCoordinates = [geohashLocation.longitude[2], geohashLocation.latitude[2]];
|
||||
}
|
||||
|
||||
const rectangle = [
|
||||
[geohashLocation.latitude[0], geohashLocation.longitude[0]],
|
||||
[geohashLocation.latitude[0], geohashLocation.longitude[1]],
|
||||
[geohashLocation.latitude[1], geohashLocation.longitude[1]],
|
||||
[geohashLocation.latitude[1], geohashLocation.longitude[0]],
|
||||
];
|
||||
|
||||
const centerLatLng = [
|
||||
geohashLocation.latitude[2],
|
||||
geohashLocation.longitude[2]
|
||||
];
|
||||
|
||||
if (geoAgg.params.useGeocentroid) {
|
||||
// see https://github.com/elastic/elasticsearch/issues/24694 for why clampGrid is used
|
||||
pointCoordinates[0] = clampGrid(pointCoordinates[0], geohashLocation.longitude[0], geohashLocation.longitude[1]);
|
||||
pointCoordinates[1] = clampGrid(pointCoordinates[1], geohashLocation.latitude[0], geohashLocation.latitude[1]);
|
||||
}
|
||||
|
||||
const metrics = {};
|
||||
metricColumns.forEach(metricColumn => {
|
||||
metrics[metricColumn.aggConfig.id] = row[metricColumn.id];
|
||||
});
|
||||
//const value = row[metricColumn.id];
|
||||
//min = Math.min(min, value);
|
||||
//max = Math.max(max, value);
|
||||
|
||||
return {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: pointCoordinates
|
||||
},
|
||||
properties: {
|
||||
geohash: geohash,
|
||||
geohash_meta: {
|
||||
center: centerLatLng,
|
||||
rectangle: rectangle
|
||||
},
|
||||
...metrics
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}).filter(row => row);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
features = [];
|
||||
if (!table || !table.rows) {
|
||||
return EMPTY_FEATURE_COLLECTION;
|
||||
}
|
||||
|
||||
const featureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: features
|
||||
};
|
||||
const geoGridColumn = table.columns.find(column => column.aggConfig.type.dslName === 'geotile_grid');
|
||||
if (!geoGridColumn) {
|
||||
return EMPTY_FEATURE_COLLECTION;
|
||||
}
|
||||
|
||||
const metricColumns = table.columns.filter(column => {
|
||||
return column.aggConfig.type.type === 'metrics'
|
||||
&& column.aggConfig.type.dslName !== 'geo_centroid';
|
||||
});
|
||||
const geocentroidColumn = table.columns.find(column => column.aggConfig.type.dslName === 'geo_centroid');
|
||||
if (!geocentroidColumn) {
|
||||
return EMPTY_FEATURE_COLLECTION;
|
||||
}
|
||||
|
||||
const features = [];
|
||||
table.rows.forEach(row => {
|
||||
const gridKey = row[geoGridColumn.id];
|
||||
if (!gridKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const properties = {};
|
||||
metricColumns.forEach(metricColumn => {
|
||||
properties[metricColumn.aggConfig.id] = row[metricColumn.id];
|
||||
});
|
||||
|
||||
features.push({
|
||||
type: 'Feature',
|
||||
geometry: rowToGeometry({
|
||||
row,
|
||||
gridKey,
|
||||
geocentroidColumn,
|
||||
renderAs,
|
||||
}),
|
||||
properties
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
featureCollection: featureCollection,
|
||||
meta: {
|
||||
min: min,
|
||||
max: max,
|
||||
geohashGridDimensionsAtEquator: geoAgg && gridDimensions(geoAgg.params.precision)
|
||||
featureCollection: {
|
||||
type: 'FeatureCollection',
|
||||
features: features
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function rowToGeometry({
|
||||
row,
|
||||
gridKey,
|
||||
geocentroidColumn,
|
||||
renderAs,
|
||||
}) {
|
||||
const { top, bottom, right, left } = getTileBoundingBox(gridKey);
|
||||
|
||||
if (renderAs === RENDER_AS.GRID) {
|
||||
return {
|
||||
type: 'Polygon',
|
||||
coordinates: [
|
||||
[
|
||||
[right, top],
|
||||
[left, top],
|
||||
[left, bottom],
|
||||
[right, bottom],
|
||||
[right, top],
|
||||
]
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// see https://github.com/elastic/elasticsearch/issues/24694 for why clampGrid is used
|
||||
const pointCoordinates = [
|
||||
clampGrid(row[geocentroidColumn.id].lon, left, right),
|
||||
clampGrid(row[geocentroidColumn.id].lat, bottom, top)
|
||||
];
|
||||
|
||||
return {
|
||||
type: 'Point',
|
||||
coordinates: pointCoordinates
|
||||
};
|
||||
}
|
||||
|
||||
function clampGrid(val, min, max) {
|
||||
if (val > max) val = max;
|
||||
else if (val < min) val = min;
|
||||
|
|
|
@ -12,7 +12,6 @@ import { AbstractESSource } from '../es_source';
|
|||
import { HeatmapLayer } from '../../heatmap_layer';
|
||||
import { VectorLayer } from '../../vector_layer';
|
||||
import { Schemas } from 'ui/vis/editors/default/schemas';
|
||||
import { makeGeohashGridPolygon } from '../../../../elasticsearch_geo_utils';
|
||||
import { AggConfigs } from 'ui/vis/agg_configs';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify';
|
||||
import { convertToGeoJson } from './convert_to_geojson';
|
||||
|
@ -21,10 +20,10 @@ import { RENDER_AS } from './render_as';
|
|||
import { CreateSourceEditor } from './create_source_editor';
|
||||
import { UpdateSourceEditor } from './update_source_editor';
|
||||
import { GRID_RESOLUTION } from '../../grid_resolution';
|
||||
import { getGeohashPrecisionForZoom } from './zoom_to_precision';
|
||||
|
||||
const COUNT_PROP_LABEL = 'Count';
|
||||
const COUNT_PROP_NAME = 'doc_count';
|
||||
const MAX_GEOTILE_LEVEL = 29;
|
||||
|
||||
const aggSchemas = new Schemas([
|
||||
{
|
||||
|
@ -41,8 +40,8 @@ const aggSchemas = new Schemas([
|
|||
{
|
||||
group: 'buckets',
|
||||
name: 'segment',
|
||||
title: 'Geo Coordinates',
|
||||
aggFilter: 'geohash_grid',
|
||||
title: 'Geo Grid',
|
||||
aggFilter: 'geotile_grid',
|
||||
min: 1,
|
||||
max: 1
|
||||
}
|
||||
|
@ -119,20 +118,21 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
}
|
||||
|
||||
getGeoGridPrecision(zoom) {
|
||||
return getGeohashPrecisionForZoom(zoom) + this._getGeoGridPrecisionResolutionDelta();
|
||||
const targetGeotileLevel = Math.ceil(zoom) + this._getGeoGridPrecisionResolutionDelta();
|
||||
return Math.min(targetGeotileLevel, MAX_GEOTILE_LEVEL);
|
||||
}
|
||||
|
||||
_getGeoGridPrecisionResolutionDelta() {
|
||||
if (this._descriptor.resolution === GRID_RESOLUTION.COARSE) {
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (this._descriptor.resolution === GRID_RESOLUTION.FINE) {
|
||||
return 1;
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (this._descriptor.resolution === GRID_RESOLUTION.MOST_FINE) {
|
||||
return 2;
|
||||
return 4;
|
||||
}
|
||||
|
||||
throw new Error(`Grid resolution param not recognized: ${this._descriptor.resolution}`);
|
||||
|
@ -147,13 +147,6 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
query: searchFilters.query,
|
||||
});
|
||||
|
||||
if (this._descriptor.requestType === RENDER_AS.GRID) {
|
||||
featureCollection.features.forEach((feature) => {
|
||||
//replace geometries with the polygon
|
||||
feature.geometry = makeGeohashGridPolygon(feature);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
data: featureCollection,
|
||||
meta: {
|
||||
|
@ -168,7 +161,6 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
async getGeoJsonPoints({ layerName }, { geogridPrecision, buffer, timeFilters, query }) {
|
||||
|
||||
const indexPattern = await this._getIndexPattern();
|
||||
|
@ -178,7 +170,10 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
const esResponse = await this._runEsQuery(layerName, searchSource, 'Elasticsearch geohash_grid aggregation request');
|
||||
|
||||
const tabifiedResp = tabifyAggResponse(aggConfigs, esResponse);
|
||||
const { featureCollection } = convertToGeoJson(tabifiedResp);
|
||||
const { featureCollection } = convertToGeoJson({
|
||||
table: tabifiedResp,
|
||||
renderAs: this._descriptor.requestType,
|
||||
});
|
||||
|
||||
return featureCollection;
|
||||
}
|
||||
|
@ -234,16 +229,14 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
{
|
||||
id: 'grid',
|
||||
enabled: true,
|
||||
type: 'geohash_grid',
|
||||
type: 'geotile_grid',
|
||||
schema: 'segment',
|
||||
params: {
|
||||
field: this._descriptor.geoField,
|
||||
isFilteredByCollar: false, // map extent filter is in query so no need to filter in aggregation
|
||||
useGeocentroid: true, // TODO make configurable
|
||||
autoPrecision: false, // false so we can define our own precision levels based on styling
|
||||
useGeocentroid: true,
|
||||
precision: precision,
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { DECIMAL_DEGREES_PRECISION } from '../../../../../common/constants';
|
||||
|
||||
const ZOOM_TILE_KEY_INDEX = 0;
|
||||
const X_TILE_KEY_INDEX = 1;
|
||||
const Y_TILE_KEY_INDEX = 2;
|
||||
|
||||
export function parseTileKey(tileKey) {
|
||||
const tileKeyParts = tileKey.split('/');
|
||||
|
||||
if (tileKeyParts.length !== 3) {
|
||||
throw new Error(`Invalid tile key, expecting "zoom/x/y" format but got ${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);
|
||||
|
||||
if (x >= tileCount) {
|
||||
throw new Error(`Tile key is malformed, expected x to be less than ${tileCount}, you provided ${x}`);
|
||||
}
|
||||
if (y >= tileCount) {
|
||||
throw new Error(`Tile key is malformed, expected y to be less than ${tileCount}, you provided ${y}`);
|
||||
}
|
||||
|
||||
return { x, y, zoom, tileCount };
|
||||
}
|
||||
|
||||
function sinh(x) {
|
||||
return (Math.exp(x) - Math.exp(-x)) / 2;
|
||||
}
|
||||
|
||||
function tileToLatitude(y, tileCount) {
|
||||
const radians = Math.atan(sinh(Math.PI - (2 * Math.PI * y / tileCount)));
|
||||
const lat = 180 / Math.PI * radians;
|
||||
return _.round(lat, DECIMAL_DEGREES_PRECISION);
|
||||
}
|
||||
|
||||
function tileToLongitude(x, tileCount) {
|
||||
const lon = (x / tileCount * 360) - 180;
|
||||
return _.round(lon, DECIMAL_DEGREES_PRECISION);
|
||||
}
|
||||
|
||||
export function getTileBoundingBox(tileKey) {
|
||||
const { x, y, tileCount } = parseTileKey(tileKey);
|
||||
|
||||
return {
|
||||
top: tileToLatitude(y, tileCount),
|
||||
bottom: tileToLatitude(y + 1, tileCount),
|
||||
left: tileToLongitude(x, tileCount),
|
||||
right: tileToLongitude(x + 1, tileCount),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { parseTileKey, getTileBoundingBox } from './geo_tile_utils';
|
||||
|
||||
it('Should parse tile key', () => {
|
||||
expect(parseTileKey('15/23423/1867')).toEqual({
|
||||
zoom: 15,
|
||||
x: 23423,
|
||||
y: 1867,
|
||||
tileCount: Math.pow(2, 15)
|
||||
});
|
||||
});
|
||||
|
||||
it('Should convert tile key to geojson Polygon', () => {
|
||||
const geometry = getTileBoundingBox('15/23423/1867');
|
||||
expect(geometry).toEqual({
|
||||
top: 82.92546,
|
||||
bottom: 82.92411,
|
||||
right: 77.34375,
|
||||
left: 77.33276
|
||||
});
|
||||
});
|
|
@ -55,7 +55,6 @@ export class UpdateSourceEditor extends Component {
|
|||
};
|
||||
|
||||
_renderMetricsEditor() {
|
||||
|
||||
const metricsFilter = (this.props.renderAs === RENDER_AS.HEATMAP) ? ((metric) => {
|
||||
//these are countable metrics, where blending heatmap color blobs make sense
|
||||
return ['count', 'sum'].includes(metric.value);
|
||||
|
|
|
@ -21,10 +21,9 @@ const NO_TILEMAP_LAYER_MSG =
|
|||
|
||||
export class CreateSourceEditor extends Component {
|
||||
|
||||
|
||||
state = {
|
||||
url: null
|
||||
}
|
||||
};
|
||||
|
||||
_loadUrl = async () => {
|
||||
const tilemap = await getKibanaTileMap();
|
||||
|
@ -32,9 +31,9 @@ export class CreateSourceEditor extends Component {
|
|||
this.setState({
|
||||
url: tilemap.url
|
||||
});
|
||||
this.props.previewTilemap(this.props.url);
|
||||
this.props.previewTilemap(this.state.url);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
|
|
|
@ -42,11 +42,11 @@ export class HeatmapStyle {
|
|||
setMBPaintProperties({ alpha, mbMap, layerId, propertyName, resolution }) {
|
||||
let radius;
|
||||
if (resolution === GRID_RESOLUTION.COARSE) {
|
||||
radius = 64;
|
||||
radius = 128;
|
||||
} else if (resolution === GRID_RESOLUTION.FINE) {
|
||||
radius = 32;
|
||||
radius = 64;
|
||||
} else if (resolution === GRID_RESOLUTION.MOST_FINE) {
|
||||
radius = 16;
|
||||
radius = 32;
|
||||
} else {
|
||||
throw new Error(`Refinement param not recognized: ${this._descriptor.refinement}`);
|
||||
}
|
||||
|
|
|
@ -3,10 +3,14 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import _ from 'lodash';
|
||||
|
||||
export class DataRequest {
|
||||
|
||||
constructor(descriptor) {
|
||||
this._descriptor = descriptor;
|
||||
this._descriptor = {
|
||||
...descriptor
|
||||
};
|
||||
}
|
||||
|
||||
getData() {
|
||||
|
@ -18,7 +22,7 @@ export class DataRequest {
|
|||
}
|
||||
|
||||
getMeta() {
|
||||
return this._descriptor.dataMeta;
|
||||
return _.get(this._descriptor, 'dataMeta', {});
|
||||
}
|
||||
|
||||
hasDataOrRequestInProgress() {
|
||||
|
|
|
@ -15,7 +15,8 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
describe('layer geo grid aggregation source', () => {
|
||||
|
||||
const EXPECTED_NUMBER_FEATURES = 6;
|
||||
const EXPECTED_NUMBER_FEATURES_ZOOMED_OUT = 4;
|
||||
const EXPECTED_NUMBER_FEATURES_ZOOMED_IN = 6;
|
||||
const DATA_CENTER_LON = -98;
|
||||
const DATA_CENTER_LAT = 38;
|
||||
|
||||
|
@ -36,20 +37,28 @@ export default function ({ getPageObjects, getService }) {
|
|||
beforeTimestamp = await getRequestTimestamp();
|
||||
});
|
||||
|
||||
it('should not rerequest when zoom changes do not cause geohash precision to change', async () => {
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 2);
|
||||
it('should not rerequest when pan changes do not move map view area outside of buffer', async () => {
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT + 10, DATA_CENTER_LON + 10, 1);
|
||||
const afterTimestamp = await getRequestTimestamp();
|
||||
expect(afterTimestamp).to.equal(beforeTimestamp);
|
||||
});
|
||||
|
||||
it('should rerequest when zoom changes causes the geohash precision to change', async () => {
|
||||
it('should not rerequest when zoom changes do not cause geotile_grid precision to change', async () => {
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 1.2);
|
||||
const beforeSameZoom = await getRequestTimestamp();
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 1.8);
|
||||
const afterTimestamp = await getRequestTimestamp();
|
||||
expect(afterTimestamp).to.equal(beforeSameZoom);
|
||||
});
|
||||
|
||||
it('should rerequest when zoom changes causes the geotile_grid precision to change', async () => {
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 4);
|
||||
const afterTimestamp = await getRequestTimestamp();
|
||||
expect(afterTimestamp).not.to.equal(beforeTimestamp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geoprecision - data', async ()=> {
|
||||
describe('geotile grid precision - data', async ()=> {
|
||||
|
||||
beforeEach(async () => {
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 1);
|
||||
|
@ -63,11 +72,11 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it ('should request the data when the map covers the databounds', async () => {
|
||||
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES);
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES_ZOOMED_OUT);
|
||||
});
|
||||
|
||||
it ('should request only partial data when the map only covers part of the databounds', async () => {
|
||||
//todo this verifies the extent-filtering behavior (not really the correct application of geohash-precision), and should ideally be moved to its own section
|
||||
//todo this verifies the extent-filtering behavior (not really the correct application of geotile_grid-precision), and should ideally be moved to its own section
|
||||
await PageObjects.maps.setView(DATA_CENTER_LAT, DATA_CENTER_LON, 6);
|
||||
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(2);
|
||||
|
@ -77,13 +86,13 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
describe('heatmap', () => {
|
||||
before(async () => {
|
||||
await PageObjects.maps.loadSavedMap('geohashgrid heatmap example');
|
||||
await PageObjects.maps.loadSavedMap('geo grid heatmap example');
|
||||
});
|
||||
|
||||
const LAYER_ID = '3xlvm';
|
||||
const HEATMAP_PROP_NAME = '__kbn_heatmap_weight__';
|
||||
|
||||
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
|
||||
it('should re-fetch geotile_grid aggregation with refresh timer', async () => {
|
||||
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
|
||||
expect(beforeRefreshTimerTimestamp.length).to.be(24);
|
||||
await PageObjects.maps.triggerSingleRefresh(1000);
|
||||
|
@ -93,7 +102,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it('should decorate feature properties with scaled doc_count property', async () => {
|
||||
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES);
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES_ZOOMED_IN);
|
||||
|
||||
mapboxStyle.sources[LAYER_ID].data.features.forEach(({ properties }) => {
|
||||
expect(properties.hasOwnProperty(HEATMAP_PROP_NAME)).to.be(true);
|
||||
|
@ -115,7 +124,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await queryBar.submitQuery();
|
||||
});
|
||||
|
||||
it('should apply query to geohashgrid aggregation request', async () => {
|
||||
it('should apply query to geotile_grid aggregation request', async () => {
|
||||
await PageObjects.maps.openInspectorRequestsView();
|
||||
const requestStats = await inspector.getTableData();
|
||||
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
|
||||
|
@ -129,7 +138,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await inspector.close();
|
||||
});
|
||||
|
||||
it('should contain geohashgrid aggregation elasticsearch request', async () => {
|
||||
it('should contain geotile_grid aggregation elasticsearch request', async () => {
|
||||
await PageObjects.maps.openInspectorRequestsView();
|
||||
const requestStats = await inspector.getTableData();
|
||||
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
|
||||
|
@ -150,14 +159,14 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
describe('vector(grid)', () => {
|
||||
before(async () => {
|
||||
await PageObjects.maps.loadSavedMap('geohashgrid vector grid example');
|
||||
await PageObjects.maps.loadSavedMap('geo grid vector grid example');
|
||||
});
|
||||
|
||||
const LAYER_ID = 'g1xkv';
|
||||
|
||||
const MAX_OF_BYTES_PROP_NAME = 'max_of_bytes';
|
||||
|
||||
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
|
||||
it('should re-fetch geotile_grid aggregation with refresh timer', async () => {
|
||||
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
|
||||
expect(beforeRefreshTimerTimestamp.length).to.be(24);
|
||||
await PageObjects.maps.triggerSingleRefresh(1000);
|
||||
|
@ -167,7 +176,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
|
||||
it('should decorate feature properties with metrics properterties', async () => {
|
||||
const mapboxStyle = await PageObjects.maps.getMapboxStyle();
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES);
|
||||
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES_ZOOMED_IN);
|
||||
|
||||
mapboxStyle.sources[LAYER_ID].data.features.forEach(({ properties }) => {
|
||||
expect(properties.hasOwnProperty(MAX_OF_BYTES_PROP_NAME)).to.be(true);
|
||||
|
@ -190,7 +199,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await queryBar.submitQuery();
|
||||
});
|
||||
|
||||
it('should apply query to geohashgrid aggregation request', async () => {
|
||||
it('should apply query to geotile_grid aggregation request', async () => {
|
||||
await PageObjects.maps.openInspectorRequestsView();
|
||||
const requestStats = await inspector.getTableData();
|
||||
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
|
||||
|
@ -204,7 +213,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
await inspector.close();
|
||||
});
|
||||
|
||||
it('should contain geohashgrid aggregation elasticsearch request', async () => {
|
||||
it('should contain geotile_grid aggregation elasticsearch request', async () => {
|
||||
await PageObjects.maps.openInspectorRequestsView();
|
||||
const requestStats = await inspector.getTableData();
|
||||
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
|
||||
|
|
|
@ -32,7 +32,7 @@ export default function ({ getPageObjects, getService }) {
|
|||
return hits;
|
||||
}
|
||||
|
||||
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
|
||||
it('should re-fetch documents with refresh timer', async () => {
|
||||
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
|
||||
expect(beforeRefreshTimerTimestamp.length).to.be(24);
|
||||
await PageObjects.maps.triggerSingleRefresh(1000);
|
||||
|
|
|
@ -190,7 +190,7 @@
|
|||
"source": {
|
||||
"type" : "map",
|
||||
"map" : {
|
||||
"title" : "geohashgrid heatmap example",
|
||||
"title" : "geo grid heatmap example",
|
||||
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
|
||||
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{\"alphaValue\":1},\"previousStyle\":null},\"type\":\"HEATMAP\"}]",
|
||||
"uiStateJSON" : "{\"isDarkMode\":false}",
|
||||
|
@ -220,7 +220,7 @@
|
|||
"source": {
|
||||
"type" : "map",
|
||||
"map" : {
|
||||
"title" : "geohashgrid vector grid example",
|
||||
"title" : "geo grid vector grid example",
|
||||
"description" : "",
|
||||
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
|
||||
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"resolution\": \"COARSE\",\"type\":\"ES_GEO_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}},\"alphaValue\":1},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 106 KiB |
Binary file not shown.
Before Width: | Height: | Size: 468 KiB After Width: | Height: | Size: 72 KiB |
Loading…
Add table
Add a link
Reference in a new issue