diff --git a/x-pack/legacy/plugins/maps/common/constants.ts b/x-pack/legacy/plugins/maps/common/constants.ts index ab9a696fa3a17..7325286051280 100644 --- a/x-pack/legacy/plugins/maps/common/constants.ts +++ b/x-pack/legacy/plugins/maps/common/constants.ts @@ -27,6 +27,9 @@ export const APP_ID = 'maps'; export const APP_ICON = 'gisApp'; export const TELEMETRY_TYPE = 'maps-telemetry'; +export const MVT_GETTILE_API_PATH = 'mvt/getTile'; +export const MVT_GETGRIDTILE_API_PATH = 'mvt/getGridTile'; + export const MAP_APP_PATH = `app/${APP_ID}`; export const GIS_API_PATH = `api/${APP_ID}`; export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`; @@ -37,9 +40,13 @@ export function createMapPath(id: string) { return `${MAP_BASE_URL}/${id}`; } +export const MVT_SOURCE_ID = 'geojsonLayer'; +export const KBN_TOO_MANY_FEATURES_PROPERTY = '__kbn_too_many_features__'; + export const LAYER_TYPE = { TILE: 'TILE', VECTOR: 'VECTOR', + TILED_VECTOR: 'TILED_VECTOR', VECTOR_TILE: 'VECTOR_TILE', HEATMAP: 'HEATMAP', }; @@ -49,11 +56,14 @@ export const SORT_ORDER = { DESC: 'desc', }; +// sources export const EMS_TMS = 'EMS_TMS'; export const EMS_FILE = 'EMS_FILE'; export const ES_GEO_GRID = 'ES_GEO_GRID'; export const ES_SEARCH = 'ES_SEARCH'; export const ES_PEW_PEW = 'ES_PEW_PEW'; +export const ES_MVT_SEARCH = 'ES_MVT_SEARCH'; +export const ES_MVT_GEO_GRID = 'ES_MVT_GEO_GRID'; export const FIELD_ORIGIN = { SOURCE: 'source', @@ -158,3 +168,5 @@ export const SYMBOLIZE_AS_TYPES = { }; export const DEFAULT_ICON = 'airfield'; + +export const DEFAULT_COUNTABLE_SCALE = 2048; diff --git a/x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js b/x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js index a8c2908e75424..0a43e3d7f4efe 100644 --- a/x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js +++ b/x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js @@ -7,7 +7,7 @@ import React from 'react'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; -export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQuery }) { +export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQuery, enableSwitch}) { const onApplyGlobalQueryChange = event => { setApplyGlobalQuery(event.target.checked); }; @@ -15,6 +15,7 @@ export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQu return ( ); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js index 0b2b838f9feb7..8efd4537f22a2 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js @@ -35,10 +35,11 @@ export class FeatureProperties extends React.Component { this._fetchProperties({ nextFeatureId: this.props.featureId, nextLayerId: this.props.layerId, + meta: this.props.meta }); }; - _fetchProperties = async ({ nextLayerId, nextFeatureId }) => { + _fetchProperties = async ({ nextLayerId, nextFeatureId, meta }) => { if (this.prevLayerId === nextLayerId && this.prevFeatureId === nextFeatureId) { // do not reload same feature properties return; @@ -64,6 +65,7 @@ export class FeatureProperties extends React.Component { properties = await this.props.loadFeatureProperties({ layerId: nextLayerId, featureId: nextFeatureId, + meta: meta, }); } catch (error) { if (this._isMounted) { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js index 8a1b556d21c1f..6e4c78eb42a10 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js @@ -93,6 +93,7 @@ export class FeaturesTooltip extends React.Component { return this.props.loadPreIndexedShape({ layerId: this.state.currentFeature.layerId, featureId: this.state.currentFeature.id, + meta: this.state.currentFeature.meta }); }; @@ -104,6 +105,7 @@ export class FeaturesTooltip extends React.Component { const currentFeatureGeometry = this.props.loadFeatureGeometry({ layerId: this.state.currentFeature.layerId, featureId: this.state.currentFeature.id, + meta: this.state.currentFeature.meta, }); const geoFields = this._filterGeoFields(currentFeatureGeometry); @@ -132,6 +134,7 @@ export class FeaturesTooltip extends React.Component { { + _loadFeatureGeometry = ({ layerId, featureId, meta }) => { + const tooltipLayer = this._findLayerById(layerId); if (!tooltipLayer) { return null; } - const targetFeature = tooltipLayer.getFeatureById(featureId); - if (!targetFeature) { - return null; - } - - return targetFeature.geometry; + return tooltipLayer.getGeometryByFeatureId(featureId); }; - _loadFeatureProperties = async ({ layerId, featureId }) => { + _loadFeatureProperties = async ({ layerId, featureId, meta }) => { const tooltipLayer = this._findLayerById(layerId); if (!tooltipLayer) { return []; } - const targetFeature = tooltipLayer.getFeatureById(featureId); - if (!targetFeature) { - return []; - } - return await tooltipLayer.getPropertiesForTooltip(targetFeature.properties); + + return await tooltipLayer.getFeaturePropertiesByFeatureId(featureId, meta); }; - _loadPreIndexedShape = async ({ layerId, featureId }) => { + _loadPreIndexedShape = async ({ layerId, featureId, meta}) => { const tooltipLayer = this._findLayerById(layerId); if (!tooltipLayer) { return null; } - - const targetFeature = tooltipLayer.getFeatureById(featureId); - if (!targetFeature) { - return null; - } - - return await tooltipLayer.getSource().getPreIndexedShape(targetFeature.properties); + return await tooltipLayer.loadPreIndexedShapeByFeatureId(featureId, meta); }; _findLayerById = layerId => { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js index 1e44c7225a564..661078998d9d3 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js @@ -143,6 +143,18 @@ export class MBMapContainer extends React.Component { mbMap.addControl(new mapboxgl.NavigationControl({ showCompass: false }), 'top-left'); } + const hatchImageBase64 = + 'data:image/png;base64,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'; + // 'data:image/png;base64,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'; + + const hatchImage = new Image(); + hatchImage.onload = () => { + mbMap.addImage('__kbn_too_many_features__', hatchImage); + }; + hatchImage.src = hatchImageBase64; + + + let emptyImage; mbMap.on('styleimagemissing', e => { if (emptyImage) { @@ -155,6 +167,7 @@ export class MBMapContainer extends React.Component { emptyImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; emptyImage.crossOrigin = 'anonymous'; + resolve(mbMap); }); }); diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js index 65109cb99809f..82ce2bce90437 100644 --- a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js +++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js @@ -5,7 +5,7 @@ */ import { AbstractField } from './field'; -import { COUNT_AGG_TYPE } from '../../../common/constants'; +import { COUNT_AGG_TYPE, METRIC_TYPE } from '../../../common/constants'; import { isMetricCountable } from '../util/is_metric_countable'; import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property'; @@ -79,10 +79,23 @@ export class ESAggMetricField extends AbstractField { supportsFieldMeta() { // count and sum aggregations are not within field bounds so they do not support field meta. - return !isMetricCountable(this.getAggType()); + // return !isMetricCountable(this.getAggType()); + return ![METRIC_TYPE.SUM, METRIC_TYPE.UNIQUE_COUNT].includes(this.getAggType()); } async getOrdinalFieldMetaRequest(config) { - return this._esDocField.getOrdinalFieldMetaRequest(config); + if (this._esDocField) { + return this._esDocField.getOrdinalFieldMetaRequest(config); + } else { + return { + type_count: { + value_count: { + script: { + source: '1', + }, + }, + }, + }; + } } } diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js index b76f1ebce15d2..13fa564f7f034 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/layer.js @@ -81,7 +81,7 @@ export class AbstractLayer { } supportsElasticsearchFilters() { - return this._source.isESSource(); + return this._source.supportsESFilters(); } async supportsFitToBounds() { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js b/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js index 6a518609dd77f..3957e85c0e75d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js @@ -14,8 +14,14 @@ import { KibanaTilemapSource } from './kibana_tilemap_source'; import { ESGeoGridSource } from './es_geo_grid_source'; import { ESSearchSource } from './es_search_source'; import { ESPewPewSource } from './es_pew_pew_source/es_pew_pew_source'; +import { MVTVectorSource } from './mvt_vector_source/mvt_vector_source'; +import { ESMVTSearchSource } from './es_mvt_search_source/es_mvt_search_source'; +import {ESMVTGeoGridSource} from "./es_mvt_geo_grid_source/es_mvt_geo_grid_source"; export const ALL_SOURCES = [ + // MVTVectorSource, + ESMVTSearchSource, + ESMVTGeoGridSource, GeojsonFileSource, ESSearchSource, ESGeoGridSource, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js index 967a3c41aec26..203e74be78036 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js @@ -99,7 +99,9 @@ export class AbstractESAggSource extends AbstractESSource { } createMetricAggConfigs() { - return this.getMetricFields().map(esAggMetric => esAggMetric.makeMetricAggConfig()); + const mf = this.getMetricFields().map(esAggMetric => esAggMetric.makeMetricAggConfig()); + + return mf; } async getNumberFields() { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js index bd074386edb3f..4dd526884b0d9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js @@ -24,6 +24,12 @@ import { npStart } from 'ui/new_platform'; const { IndexPatternSelect } = npStart.plugins.data.ui; const requestTypeOptions = [ + { + label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', { + defaultMessage: 'clusters', + }), + value: RENDER_AS.POINT, + }, { label: i18n.translate('xpack.maps.source.esGeoGrid.gridRectangleDropdownOption', { defaultMessage: 'grid rectangles', @@ -36,12 +42,6 @@ const requestTypeOptions = [ }), value: RENDER_AS.HEATMAP, }, - { - label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', { - defaultMessage: 'clusters', - }), - value: RENDER_AS.POINT, - }, ]; export class CreateSourceEditor extends Component { @@ -184,6 +184,8 @@ export class CreateSourceEditor extends Component { return null; } + const options = this.props.clustersOnly ? requestTypeOptions.slice(0, 1) : requestTypeOptions; + return ( - {this._renderMetricsPanel()} -
@@ -112,6 +114,15 @@ export class UpdateSourceEditor extends Component { onChange={this._onResolutionChange} /> + + ); + } + + render() { + return ( + + {this._renderMetricsPanel()} + {this._renderResolutionEditor()} ); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_geo_grid_source/es_mvt_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_geo_grid_source/es_mvt_geo_grid_source.js new file mode 100644 index 0000000000000..e8959db9800fe --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_geo_grid_source/es_mvt_geo_grid_source.js @@ -0,0 +1,193 @@ +/* + * 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 { ESSearchSource } from '../es_search_source'; +import { AggConfigs, Schemas } from 'ui/agg_types'; +import { + ES_MVT_SEARCH, + GIS_API_PATH, + MVT_GETTILE_API_PATH, + ES_GEO_FIELD_TYPE, + ES_MVT_GEO_GRID, + COUNT_PROP_NAME, + SOURCE_DATA_ID_ORIGIN, + COLOR_MAP_TYPE, + MVT_SOURCE_ID, MVT_GETGRIDTILE_API_PATH, +} from '../../../../common/constants'; +import { TiledVectorLayer } from '../../tiled_vector_layer'; +import uuid from 'uuid/v4'; +import { CreateSourceEditor } from '../es_geo_grid_source/create_source_editor'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { loadIndexSettings } from '../es_search_source/load_index_settings'; +import rison from 'rison-node'; +import { UpdateSourceEditor } from '../es_geo_grid_source/update_source_editor'; +import { ESGeoGridSource, aggSchemas } from '../es_geo_grid_source'; +import { GRID_RESOLUTION } from '../../grid_resolution'; +import { RENDER_AS } from '../es_geo_grid_source/render_as'; +import { HeatmapLayer } from '../../heatmap_layer'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { VectorLayer } from '../../vector_layer'; +import { + getDefaultDynamicProperties, + VECTOR_STYLES, +} from '../../styles/vector/vector_style_defaults'; +import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; +import { COLOR_GRADIENTS } from '../../styles/color_utils'; +import { StaticStyleProperty } from '../../styles/vector/properties/static_style_property'; + +export class ESMVTGeoGridSource extends ESGeoGridSource { + static type = ES_MVT_GEO_GRID; + static title = i18n.translate('xpack.maps.source.esMvtSearchTitle', { + defaultMessage: 'Grids as tiles', + }); + static description = i18n.translate('xpack.maps.source.esMvtSearchDescription', { + defaultMessage: 'Grid agg with tiles', + }); + + static createDescriptor({ indexPatternId, geoField, requestType, resolution }) { + return { + type: ESMVTGeoGridSource.type, + id: uuid(), + indexPatternId: indexPatternId, + geoField: geoField, + requestType: requestType, + resolution: resolution ? resolution : GRID_RESOLUTION.COARSE, + }; + } + + static renderEditor({ onPreviewSource, inspectorAdapters }) { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const sourceDescriptor = ESMVTGeoGridSource.createDescriptor(sourceConfig); + const source = new ESMVTGeoGridSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ; + } + + _makeAggConfigs(precision) { + const metricAggConfigs = this.createMetricAggConfigs(); + return [ + ...metricAggConfigs, + { + id: 'grid', + enabled: true, + type: 'geotile_grid', + schema: 'segment', + params: { + field: this._descriptor.geoField, + useGeocentroid: true, + precision: precision, + }, + }, + ]; + } + + async _makeSearchSourceWithAggConfigs(searchFilters) { + const indexPattern = await this.getIndexPattern(); + const searchSource = await this._makeSearchSource(searchFilters, 0); + const aggConfigs = new AggConfigs( + indexPattern, + this._makeAggConfigs(searchFilters.geogridPrecision), + aggSchemas.all + ); + searchSource.setField('aggs', aggConfigs.toDsl()); + return { searchSource, aggConfigs }; + } + + async getUrlTemplate(searchFilters) { + + const indexPattern = await this.getIndexPattern(); + + const geoFieldName = this._descriptor.geoField; + + const { searchSource, aggConfigs } = await this._makeSearchSourceWithAggConfigs(searchFilters); + + + const dsl = await searchSource.getSearchRequestBody(); + const risonDsl = rison.encode(dsl); + + const aggConfigNames = aggConfigs.aggs.map(config => config.id); + const aggNames = aggConfigNames.join(','); + const ipTitle = indexPattern.title; + + + return `../${GIS_API_PATH}/${MVT_GETGRIDTILE_API_PATH}?x={x}&y={y}&z={z}&geometryFieldName=${geoFieldName}&aggNames=${aggNames}&indexPattern=${ipTitle}&requestBody=${risonDsl}`; + } + + getMvtSourceLayer() { + return MVT_SOURCE_ID; + } + + isTileSource() { + return true; + } + + _createTiledVectorLayerDescriptor(options) { + const descriptor = TiledVectorLayer.createDescriptor({ + sourceDescriptor: this._descriptor, + ...options, + }); + descriptor.style = VectorStyle.createDescriptor({}); + return descriptor; + } + + createDefaultLayer(options) { + const layerDescriptor = this._createTiledVectorLayerDescriptor(options); + const style = new VectorStyle(layerDescriptor.style, this); + return new TiledVectorLayer({ + layerDescriptor, + source: this, + style, + }); + } + + renderSourceSettingsEditor({ onChange }) { + return ( + + ); + } + + isFilterByMapBounds() { + return false; + } + + isFilterByMapBoundsConfigurable() { + return false; + } + + isBoundsAware() { + return false; + } + + isRefreshTimerAware() { + return false; + } + + async getImmutableProperties() { + const ip = await super.getImmutableProperties(); + ip.push({ + label: i18n.translate('xpack.maps.source.esSearch.isMvt', { + defaultMessage: 'Is MBVT?', + }), + value: 'yes is mvt', + }); + return ip; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_search_source/es_mvt_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_search_source/es_mvt_search_source.js new file mode 100644 index 0000000000000..fdcc1fcb59243 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_mvt_search_source/es_mvt_search_source.js @@ -0,0 +1,157 @@ +/* + * 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 { ESSearchSource } from '../es_search_source'; +import { + ES_MVT_SEARCH, + GIS_API_PATH, + MVT_GETTILE_API_PATH, + ES_GEO_FIELD_TYPE, MVT_SOURCE_ID, +} from '../../../../common/constants'; +import { TiledVectorLayer } from '../../tiled_vector_layer'; +import uuid from 'uuid/v4'; +import { CreateSourceEditor } from '../es_search_source/create_source_editor'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { loadIndexSettings } from '../es_search_source/load_index_settings'; +import rison from 'rison-node'; +import { UpdateSourceEditor } from '../es_search_source/update_source_editor'; + +export class ESMVTSearchSource extends ESSearchSource { + static type = ES_MVT_SEARCH; + static title = i18n.translate('xpack.maps.source.esMvtSearchTitle', { + defaultMessage: 'Documents as tiles', + }); + static description = i18n.translate('xpack.maps.source.esMvtSearchDescription', { + defaultMessage: 'Vector data from a Kibana index pattern (tiled)', + }); + + static renderEditor({ onPreviewSource, inspectorAdapters }) { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const source = new ESMVTSearchSource( + { + id: uuid(), + ...sourceConfig, + type: ESMVTSearchSource.type, + applyGlobalQuery: true, + }, + inspectorAdapters + ); + + onPreviewSource(source); + }; + return ( + + ); + } + + async getUrlTemplate(searchFilters) { + const indexPattern = await this.getIndexPattern(); + const indexSettings = await loadIndexSettings(indexPattern.title); + + //assuming only geo_shape fields for now + const initialSearchContext = { + docvalue_fields: await this._getDateDocvalueFields(searchFilters.fieldNames), + }; + const geoField = await this._getGeoField(); + const docValueFields = await this._excludeDateFields(searchFilters.fieldNames); + const withoutGeoField = docValueFields.filter(field => field !== geoField.name); + + initialSearchContext.docvalue_fields.push(...withoutGeoField); + + const searchSource = await this._makeSearchSource( + searchFilters, + indexSettings.maxResultWindow, + initialSearchContext + ); + searchSource.setField('fields', searchFilters.fieldNames); + window._ss = searchSource; + + const ipTitle = indexPattern.title; + const geometryFieldBame = this._descriptor.geoField; + const fields = ['_id']; + const fieldsParam = fields.join(','); + + const dsl = await searchSource.getSearchRequestBody(); + const risonDsl = rison.encode(dsl); + + return `../${GIS_API_PATH}/${MVT_GETTILE_API_PATH}?x={x}&y={y}&z={z}&geometryFieldName=${geometryFieldBame}&indexPattern=${ipTitle}&fields=${fieldsParam}&requestBody=${risonDsl}`; + } + + getMvtSourceLayer() { + return MVT_SOURCE_ID; + } + + isTileSource() { + return true; + } + + _createDefaultLayerDescriptor(options) { + const tvl = TiledVectorLayer.createDescriptor({ + sourceDescriptor: this._descriptor, + ...options, + }); + + return tvl; + } + + renderSourceSettingsEditor({ onChange }) { + return ( + + ); + } + + isFilterByMapBounds() { + return false; + } + + isFilterByMapBoundsConfigurable() { + return false; + } + + isBoundsAware() { + return false; + } + + isRefreshTimerAware() { + return false; + } + + isJoinable() { + return false; + } + + async getImmutableProperties() { + const ip = await super.getImmutableProperties(); + ip.push({ + label: i18n.translate('xpack.maps.source.esSearch.isMvt', { + defaultMessage: 'Is MBVT?', + }), + value: 'yes is mvt', + }); + return ip; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js index da6248099c9c1..5e74b53b57110 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js @@ -192,7 +192,7 @@ export class CreateSourceEditor extends Component { } _renderFilterByMapBounds() { - if (!this.state.showFilterByBoundsSwitch) { + if (!this.state.showFilterByBoundsSwitch || this.props.showBoundsFilter === false) { return null; } @@ -268,7 +268,7 @@ export class CreateSourceEditor extends Component { defaultMessage: 'Select index pattern', } )} - fieldTypes={[ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]} + fieldTypes={this.props.geoTypes ?this.props.geoTypes : [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index b8644adddcf7e..e90d2ba064d14 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -49,6 +49,7 @@ export class ESSearchSource extends AbstractESSource { { id: uuid(), ...sourceConfig, + type: ESSearchSource.type }, inspectorAdapters ); @@ -62,7 +63,7 @@ export class ESSearchSource extends AbstractESSource { { ...descriptor, id: descriptor.id, - type: ESSearchSource.type, + // type: ESSearchSource.type, indexPatternId: descriptor.indexPatternId, geoField: descriptor.geoField, filterByMapBounds: _.get(descriptor, 'filterByMapBounds', DEFAULT_FILTER_BY_MAP_BOUNDS), @@ -100,6 +101,7 @@ export class ESSearchSource extends AbstractESSource { useTopHits={this._descriptor.useTopHits} topHitsSplitField={this._descriptor.topHitsSplitField} topHitsSize={this._descriptor.topHitsSize} + showSorting={true} /> ); } @@ -361,6 +363,11 @@ export class ESSearchSource extends AbstractESSource { searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields } else { // geo_shape fields do not support docvalue_fields yet, so still have to be pulled from _source + const fields = await this._excludeDateFields(searchFilters.fieldNames); + const withoutGeoField = fields.filter(field => field !== geoField.name); + initialSearchContext.docvalue_fields.push( + ...(withoutGeoField) + ); searchSource = await this._makeSearchSource( searchFilters, maxResultWindow, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js index fdebbe4c81911..fc683909035fe 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js @@ -231,6 +231,11 @@ export class UpdateSourceEditor extends Component { } _renderSortPanel() { + + if (!this.props.showSorting) { + return null; + } + return ( 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 26cc7ece66753..3797c443d4144 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 @@ -61,6 +61,10 @@ export class AbstractESSource extends AbstractVectorSource { return true; } + supportsESFilters() { + return this.isESSource(); + } + destroy() { this._inspectorAdapters.requests.resetRequest(this.getId()); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source.js new file mode 100644 index 0000000000000..fd2887d9338cd --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source.js @@ -0,0 +1,64 @@ +/* + * 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 { AbstractSource } from '../source'; +import { MVTVectorSourceEditor } from './mvt_vector_source_editor'; +import { i18n } from '@kbn/i18n'; +import uuid from 'uuid/v4'; +import React from 'react'; +import {TileLayer} from "../../tile_layer"; +import {TiledVectorLayer} from "../../tiled_vector_layer"; + +export class MVTVectorSource extends AbstractSource { + static type = 'TiledVectorSource'; + static title = i18n.translate('xpack.maps.source.tiledVectorTitle', { + defaultMessage: 'Tiled vector', + }); + static description = i18n.translate('xpack.maps.source.tiledVectorDescription', { + defaultMessage: 'Tiled vector with url template', + }); + + static icon = 'logoElasticsearch'; + + static createDescriptor({ urlTemplate }) { + return { + type: MVTVectorSource.type, + id: uuid(), + urlTemplate, + }; + } + + static renderEditor({ onPreviewSource, inspectorAdapters }) { + const onSourceConfigChange = sourceConfig => { + const sourceDescriptor = MVTVectorSource.createDescriptor(sourceConfig); + const source = new MVTVectorSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + return ; + } + + renderSourceSettingsEditor({ onChange }) { + return (
No source settings to edit
); + } + + _createDefaultLayerDescriptor(options) { + return TiledVectorLayer.createDescriptor({ + sourceDescriptor: this._descriptor, + ...options, + }); + } + + createDefaultLayer(options) { + return new TiledVectorLayer({ + layerDescriptor: this._createDefaultLayerDescriptor(options), + source: this, + }); + } + + async getUrlTemplate() { + return this._descriptor.urlTemplate; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source_editor.js new file mode 100644 index 0000000000000..2daddaaed7b97 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/mvt_vector_source/mvt_vector_source_editor.js @@ -0,0 +1,46 @@ +import React, {Fragment} from "react"; +import _ from "lodash"; +import {EuiFieldText, EuiFormRow} from "@elastic/eui"; +import {i18n} from "@kbn/i18n"; + +export class MVTVectorSourceEditor extends React.Component { + state = { + mvtInput: '', + mvtCanPreview: false, + }; + + _sourceConfigChange = _.debounce(updatedSourceConfig => { + if (this.state.mvtCanPreview) { + this.props.onSourceConfigChange(updatedSourceConfig); + } + }, 2000); + + _handleMVTInputChange(e) { + const url = e.target.value; + + const canPreview = + url.indexOf('{x}') >= 0 && url.indexOf('{y}') >= 0 && url.indexOf('{z}') >= 0; + this.setState( + { + mvtInput: url, + mvtCanPreview: canPreview, + }, + () => this._sourceConfigChange({ urlTemplate: url }) + ); + } + + render() { + const example = `http://localhost:8080/?x={x}&y={y}&z={z}&index=ky_roads&geometry=geometry&size=10000&fields=fclass`; + return ( + +
{example}
+ + this._handleMVTInputChange(e)} + /> + +
+ ); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.js b/x-pack/legacy/plugins/maps/public/layers/sources/source.js index cc5d62bbdfeef..75d1aca9dbe0a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.js @@ -82,6 +82,10 @@ export class AbstractSource { return false; } + isQueryAwareTogglable() { + return true; + } + getFieldNames() { return []; } @@ -126,6 +130,10 @@ export class AbstractSource { return false; } + supportsESFilters() { + return false; + } + // Returns geo_shape indexed_shape context for spatial quering by pre-indexed shapes async getPreIndexedShape(/* properties */) { return null; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index 3952aacf03b33..b0e7b3ad519e2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -160,6 +160,10 @@ export class AbstractVectorSource extends AbstractSource { return true; } + isTileSource() { + return false; + } + async getSupportedShapeTypes() { return [VECTOR_SHAPE_TYPES.POINT, VECTOR_SHAPE_TYPES.LINE, VECTOR_SHAPE_TYPES.POLYGON]; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js index fc305f8daed59..55278a918fb04 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js @@ -64,13 +64,19 @@ export function getColorRampCenterColor(colorRampName) { // Returns an array of color stops // [ stop_input_1: number, stop_output_1: color, stop_input_n: number, stop_output_n: color ] -export function getOrdinalColorRampStops(colorRampName, numberColors = GRADIENT_INTERVALS) { +export function getOrdinalColorRampStops( + colorRampName, + scale = 1, + offset = 0 +) { + + const numberColors = GRADIENT_INTERVALS; if (!colorRampName) { return null; } return getHexColorRangeStrings(colorRampName, numberColors).reduce( (accu, stopColor, idx, srcArr) => { - const stopNumber = idx / srcArr.length; // number between 0 and 1, increasing as index increases + const stopNumber = offset + ((idx * scale) / srcArr.length); // number between 0 and 1, increasing as index increases return [...accu, stopNumber, stopColor]; }, [] diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 441ebfb2d53bf..837b728760b4f 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -36,7 +36,7 @@ export class VectorStyleEditor extends Component { defaultDynamicProperties: getDefaultDynamicProperties(), defaultStaticProperties: getDefaultStaticProperties(), supportedFeatures: undefined, - selectedFeatureType: undefined, + selectedFeature: undefined, }; componentWillUnmount() { @@ -99,11 +99,17 @@ export class VectorStyleEditor extends Component { return; } - let selectedFeature = VECTOR_SHAPE_TYPES.POLYGON; + let selectedFeature; if (this.props.isPointsOnly) { selectedFeature = VECTOR_SHAPE_TYPES.POINT; } else if (this.props.isLinesOnly) { selectedFeature = VECTOR_SHAPE_TYPES.LINE; + } else { + if (this.state.selectedFeature !== undefined) { + selectedFeature = this.state.selectedFeature; + } else { + selectedFeature = VECTOR_SHAPE_TYPES.POLYGON; + } } if ( diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js index 70e905907bc79..51d5eb04d8546 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -102,7 +102,8 @@ export class DynamicColorProperty extends DynamicStyleProperty { _getMbColor() { const isDynamicConfigComplete = - _.has(this._options, 'field.name') && _.has(this._options, 'color'); + _.has(this._options, 'field.name') && + (_.has(this._options, 'color') || _.has(this._options, 'useCustomColorRamp')); if (!isDynamicConfigComplete) { return null; } @@ -111,11 +112,11 @@ export class DynamicColorProperty extends DynamicStyleProperty { if (this.isCategorical()) { return this._getMbDataDrivenCategoricalColor({ targetName }); } else { - return this._getMbDataDrivenOrdinalColor({ targetName }); + return this._getMbDataDrivenOrdinalColor({ targetName, fieldName: this._options.field.name }); } } - _getMbDataDrivenOrdinalColor({ targetName }) { + _getMbDataDrivenOrdinalColor({ targetName, fieldName }) { if ( this._options.useCustomColorRamp && (!this._options.customColorRamp || !this._options.customColorRamp.length) @@ -123,29 +124,60 @@ export class DynamicColorProperty extends DynamicStyleProperty { return null; } - const colorStops = this._getMbOrdinalColorStops(); - if (!colorStops) { - return null; + let colorStops; + let getFunction; + let propFieldName; + if (this._style.isBackedByTileSource()) { + const fieldMeta = this._getFieldMeta(fieldName); + + if (this._options.useCustomColorRamp) { + getFunction = 'get'; + propFieldName = fieldName; + colorStops = this._getMbOrdinalColorStops(); + + } else { + if (!fieldMeta) { + return null; + } + + colorStops = this._getMbOrdinalColorStops(fieldMeta.max - fieldMeta.min, fieldMeta.min); + getFunction = 'get'; + propFieldName = fieldName; + } + } else { + colorStops = this._getMbOrdinalColorStops(); + getFunction = 'feature-state'; + propFieldName = targetName; } + // if (!colorStops) { + // return null; + // } + if (this._options.useCustomColorRamp) { const firstStopValue = colorStops[0]; const lessThenFirstStopValue = firstStopValue - 1; return [ 'step', - ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], + // ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], + ['coalesce', [getFunction, propFieldName], lessThenFirstStopValue], 'rgba(0,0,0,0)', // MB will assign the base value to any features that is below the first stop value ...colorStops, ]; + } else { + if (!colorStops) { + return null; + } + return [ + 'interpolate', + ['linear'], + // ['coalesce', ['feature-state', targetName], -1], + ['coalesce', [getFunction, propFieldName], -1], + -1, + 'rgba(0,0,0,0)', + ...colorStops, + ]; } - return [ - 'interpolate', - ['linear'], - ['coalesce', ['feature-state', targetName], -1], - -1, - 'rgba(0,0,0,0)', - ...colorStops, - ]; } _getColorPaletteStops() { @@ -226,13 +258,13 @@ export class DynamicColorProperty extends DynamicStyleProperty { return ['match', ['to-string', ['get', this._options.field.name]], ...mbStops]; } - _getMbOrdinalColorStops() { + _getMbOrdinalColorStops(scale, offset) { if (this._options.useCustomColorRamp) { return this._options.customColorRamp.reduce((accumulatedStops, nextStop) => { return [...accumulatedStops, nextStop.stop, nextStop.color]; }, []); } else { - return getOrdinalColorRampStops(this._options.color); + return getOrdinalColorRampStops(this._options.color, scale, offset); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index 37db54389866d..3a036740c3af9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -7,7 +7,11 @@ import _ from 'lodash'; import { AbstractStyleProperty } from './style_property'; import { DEFAULT_SIGMA } from '../vector_style_defaults'; -import { COLOR_PALETTE_MAX_SIZE, STYLE_TYPE } from '../../../../../common/constants'; +import { + COLOR_PALETTE_MAX_SIZE, + DEFAULT_COUNTABLE_SCALE, + STYLE_TYPE, +} from '../../../../../common/constants'; import { scaleValue, getComputedFieldName } from '../style_util'; import React from 'react'; import { OrdinalLegend } from './components/ordinal_legend'; @@ -17,11 +21,12 @@ import { OrdinalFieldMetaOptionsPopover } from '../components/ordinal_field_meta export class DynamicStyleProperty extends AbstractStyleProperty { static type = STYLE_TYPE.DYNAMIC; - constructor(options, styleName, field, getFieldMeta, getFieldFormatter) { + constructor(options, styleName, field, getFieldMeta, getFieldFormatter, style) { super(options, styleName); this._field = field; this._getFieldMeta = getFieldMeta; this._getFieldFormatter = getFieldFormatter; + this._style = style; } getFieldMeta() { @@ -78,7 +83,8 @@ export class DynamicStyleProperty extends AbstractStyleProperty { supportsFieldMeta() { if (this.isOrdinal()) { - return this.isComplete() && this.isOrdinalScaled() && this._field.supportsFieldMeta(); + // return this.isComplete() && this.isOrdinalScaled() && this._field.supportsFieldMeta(); + return this.isComplete() && this._field.supportsFieldMeta(); } else if (this.isCategorical()) { return this.isComplete() && this._field.supportsFieldMeta(); } else { @@ -177,6 +183,19 @@ export class DynamicStyleProperty extends AbstractStyleProperty { const realFieldName = this._field.getESDocFieldName ? this._field.getESDocFieldName() : this._field.getName(); + + if (fieldMetaData.type_count && typeof fieldMetaData.type_count.value === 'number') { + const min = 0; + const max = Math.ceil(fieldMetaData.type_count.value / DEFAULT_COUNTABLE_SCALE); + return { + min: min, + max: max, + delta: max - min, + isMinOutsideStdRange: false, + isMaxOutsideStdRange: false, + }; + } + const stats = fieldMetaData[realFieldName]; if (!stats) { return null; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 97259a908f1e4..85be14a324cc1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -138,6 +138,10 @@ export class VectorStyle extends AbstractStyle { ]; } + isBackedByTileSource() { + return this._source.isTileSource(); + } + renderEditor({ layer, onStyleDescriptorChange }) { const rawProperties = this.getRawProperties(); const handlePropertyChange = (propertyName, settings) => { @@ -380,6 +384,7 @@ export class VectorStyle extends AbstractStyle { return fieldMeta ? fieldMeta : fieldMetaFromLocalFeatures; }; + _getFieldFormatter = fieldName => { const dynamicProp = this._getDynamicPropertyByFieldName(fieldName); if (!dynamicProp) { @@ -631,7 +636,8 @@ export class VectorStyle extends AbstractStyle { styleName, field, this._getFieldMeta, - this._getFieldFormatter + this._getFieldFormatter, + this ); } else { throw new Error(`${descriptor} not implemented`); diff --git a/x-pack/legacy/plugins/maps/public/layers/tiled_vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/tiled_vector_layer.js new file mode 100644 index 0000000000000..37a7a1645626e --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/tiled_vector_layer.js @@ -0,0 +1,192 @@ +/* + * 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 React from 'react'; +import { VectorStyle } from './styles/vector/vector_style'; +import { + SOURCE_DATA_ID_ORIGIN, + LAYER_TYPE, + FEATURE_ID_PROPERTY_NAME, +} from '../../common/constants'; +import { VectorLayer } from './vector_layer'; +import { EuiIcon } from '@elastic/eui'; +import { canSkipSourceUpdate } from './util/can_skip_fetch'; +import _ from 'lodash'; + +export class TiledVectorLayer extends VectorLayer { + static type = LAYER_TYPE.TILED_VECTOR; + + static createDescriptor(options, mapColors) { + const layerDescriptor = super.createDescriptor(options); + layerDescriptor.type = TiledVectorLayer.type; + + if (!options.style) { + const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors); + layerDescriptor.style = VectorStyle.createDescriptor(styleProperties); + } + + return layerDescriptor; + } + + constructor(options) { + super(options); + this._style = new VectorStyle(this._descriptor.style, this._source, this); + } + + destroy() { + if (this._source) { + this._source.destroy(); + } + } + + getCustomIconAndTooltipContent() { + return { + icon: , + }; + } + + _getSearchFilters(dataFilters) { + const fieldNames = [...this._source.getFieldNames(), ...this._style.getSourceFieldNames()]; + + return { + ...dataFilters, + fieldNames: _.uniq(fieldNames).sort(), + sourceQuery: this.getQuery(), + applyGlobalQuery: this._source.getApplyGlobalQuery(), + }; + } + + async _syncMVTUrlTemplate({ + startLoading, + stopLoading, + onLoadError, + registerCancelCallback, + dataFilters, + }) { + const requestToken = Symbol(`layer-${this.getId()}-${SOURCE_DATA_ID_ORIGIN}`); + const searchFilters = this._getSearchFilters(dataFilters); + const prevDataRequest = this.getSourceDataRequest(); + const canSkip = await canSkipSourceUpdate({ + source: this._source, + prevDataRequest, + nextMeta: searchFilters, + }); + if (canSkip) { + return null; + } + + startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, searchFilters); + try { + const url = await this._source.getUrlTemplate(searchFilters); + stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, url, {}); + } catch (error) { + onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message); + } + } + + async syncData(syncContext) { + if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { + return; + } + + await this._syncSourceStyleMeta(syncContext); + await this._syncSourceFormatters(syncContext); + await this._syncMVTUrlTemplate(syncContext); + } + + _syncSourceBindingWithMb(mbMap) { + const mbSource = mbMap.getSource(this.getId()); + if (!mbSource) { + const sourceDataRequest = this.getSourceDataRequest(); + if (!sourceDataRequest) { + //this is possible if the layer was invisible at startup. + //the actions will not perform any data=syncing as an optimization when a layer is invisible + //when turning the layer back into visible, it's possible the url has not been resovled yet. + return; + } + + const url = sourceDataRequest.getData(); + if (!url) { + return; + } + + const sourceId = this.getId(); + mbMap.addSource(sourceId, { + type: 'vector', + tiles: [url], + }); + } + } + + _syncStylePropertiesWithMb(mbMap) { + const mbSource = mbMap.getSource(this.getId()); + if (!mbSource) { + return; + } + + const options = { mvtSourceLayer: this._source.getMvtSourceLayer() }; + this._setMbPointsProperties(mbMap, options); + this._setMbLinePolygonProperties(mbMap, options); + } + + _requiresPrevSourceCleanup(mbMap) { + const tileSource = mbMap.getSource(this.getId()); + if (!tileSource) { + return false; + } + const dataRequest = this.getSourceDataRequest(); + if (!dataRequest) { + return false; + } + const newUrl = dataRequest.getData(); + if (tileSource.tiles[0] === newUrl) { + //TileURL captures all the state. If this does not change, no updates are required. + return false; + } + + return true; + } + + syncLayerWithMB(mbMap) { + const requiresCleanup = this._requiresPrevSourceCleanup(mbMap); + if (requiresCleanup) { + const mbStyle = mbMap.getStyle(); + mbStyle.layers.forEach(mbLayer => { + if (this.ownsMbLayerId(mbLayer.id)) { + mbMap.removeLayer(mbLayer.id); + } + }); + Object.keys(mbStyle.sources).some(mbSourceId => { + if (this.ownsMbSourceId(mbSourceId)) { + mbMap.removeSource(mbSourceId); + } + }); + } + + this._syncSourceBindingWithMb(mbMap); + this._syncStylePropertiesWithMb(mbMap); + } + + getJoins() { + return []; + } + + getGeometryByFeatureId(featureId, meta) { + return null; + } + + async getFeaturePropertiesByFeatureId(featureId, meta) { + const test = await this._source.filterAndFormatPropertiesToHtml({ + _id: meta.docId, + _index: meta.indexName, + }); + return test; + } + + async loadPreIndexedShapeByFeatureId(featureId, meta) { + return null; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js index a943b0b22a189..bce4ff66e10f5 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js @@ -5,7 +5,10 @@ */ import _ from 'lodash'; -import { FEATURE_ID_PROPERTY_NAME } from '../../../common/constants'; +import { + FEATURE_ID_PROPERTY_NAME, + KBN_TOO_MANY_FEATURES_PROPERTY, +} from '../../../common/constants'; let idCounter = 0; @@ -36,17 +39,19 @@ export function assignFeatureIds(featureCollection) { for (let i = 0; i < featureCollection.features.length; i++) { const numericId = randomizedIds[i]; const feature = featureCollection.features[i]; - features.push({ + const nf = { type: 'Feature', geometry: feature.geometry, // do not copy geometry, this object can be massive properties: { // preserve feature id provided by source so features can be referenced across fetches [FEATURE_ID_PROPERTY_NAME]: feature.id == null ? numericId : feature.id, + [KBN_TOO_MANY_FEATURES_PROPERTY]: false, // create new object for properties so original is not polluted with kibana internal props ...feature.properties, }, id: numericId, // Mapbox feature state id, must be integer - }); + }; + features.push(nf); } return { diff --git a/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js b/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js index 36841dc727dd3..d96d0dd8627b4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js @@ -4,44 +4,68 @@ * you may not use this file except in compliance with the Elastic License. */ -import { GEO_JSON_TYPE, FEATURE_VISIBLE_PROPERTY_NAME } from '../../../common/constants'; +import { + GEO_JSON_TYPE, + FEATURE_VISIBLE_PROPERTY_NAME, + KBN_TOO_MANY_FEATURES_PROPERTY, +} from '../../../common/constants'; -const VISIBILITY_FILTER_CLAUSE = ['all', ['==', ['get', FEATURE_VISIBLE_PROPERTY_NAME], true]]; +const VISIBILITY_FILTER_CLAUSE_PREFIX = [ + 'all', + ['==', ['get', FEATURE_VISIBLE_PROPERTY_NAME], true], +]; + +const TOO_MANY_FEATURES_FILTER = ['all', ['==', ['get', KBN_TOO_MANY_FEATURES_PROPERTY], false]]; const CLOSED_SHAPE_MB_FILTER = [ - 'any', - ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], - ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON], + ...TOO_MANY_FEATURES_FILTER, + [ + 'any', + ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], + ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON], + ], ]; -const VISIBLE_CLOSED_SHAPE_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, CLOSED_SHAPE_MB_FILTER]; +const VISIBLE_JOINED_CLOSED_SHAPE_MB_FILTER = [ + ...VISIBILITY_FILTER_CLAUSE_PREFIX, + CLOSED_SHAPE_MB_FILTER, +]; const ALL_SHAPE_MB_FILTER = [ - 'any', - ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], - ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON], - ['==', ['geometry-type'], GEO_JSON_TYPE.LINE_STRING], - ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_LINE_STRING], + ...TOO_MANY_FEATURES_FILTER, + [ + 'any', + ['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON], + ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON], + ['==', ['geometry-type'], GEO_JSON_TYPE.LINE_STRING], + ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_LINE_STRING], + ], ]; -const VISIBLE_ALL_SHAPE_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, ALL_SHAPE_MB_FILTER]; +const VISIBLE_JOINED_ALL_SHAPE_MB_FILTER = [ + ...VISIBILITY_FILTER_CLAUSE_PREFIX, + ALL_SHAPE_MB_FILTER, +]; const POINT_MB_FILTER = [ - 'any', - ['==', ['geometry-type'], GEO_JSON_TYPE.POINT], - ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POINT], + ...TOO_MANY_FEATURES_FILTER, + [ + 'any', + ['==', ['geometry-type'], GEO_JSON_TYPE.POINT], + ['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POINT], + ], ]; -const VISIBLE_POINT_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, POINT_MB_FILTER]; +const VISIBLE_JOINED_POINT_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE_PREFIX, POINT_MB_FILTER]; export function getFillFilterExpression(hasJoins) { - return hasJoins ? VISIBLE_CLOSED_SHAPE_MB_FILTER : CLOSED_SHAPE_MB_FILTER; + return hasJoins ? VISIBLE_JOINED_CLOSED_SHAPE_MB_FILTER : CLOSED_SHAPE_MB_FILTER; } export function getLineFilterExpression(hasJoins) { - return hasJoins ? VISIBLE_ALL_SHAPE_MB_FILTER : ALL_SHAPE_MB_FILTER; + return hasJoins ? VISIBLE_JOINED_ALL_SHAPE_MB_FILTER : ALL_SHAPE_MB_FILTER; } export function getPointFilterExpression(hasJoins) { - return hasJoins ? VISIBLE_POINT_MB_FILTER : POINT_MB_FILTER; + return hasJoins ? VISIBLE_JOINED_POINT_MB_FILTER : POINT_MB_FILTER; } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index 31c3831fb612a..e012381923d51 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -19,6 +19,7 @@ import { LAYER_TYPE, FIELD_ORIGIN, LAYER_STYLE_TYPE, + KBN_TOO_MANY_FEATURES_PROPERTY, } from '../../common/constants'; import _ from 'lodash'; import { JoinTooltipProperty } from './tooltips/join_tooltip_property'; @@ -410,9 +411,9 @@ export class VectorLayer extends AbstractLayer { } async _syncSourceStyleMeta(syncContext) { - if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { - return; - } + // if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { + // return; + // } return this._syncStyleMeta({ source: this._source, @@ -459,6 +460,7 @@ export class VectorLayer extends AbstractLayer { onLoadError, registerCancelCallback, }) { + if (!source.isESSource() || dynamicStyleProps.length === 0) { return; } @@ -572,13 +574,7 @@ export class VectorLayer extends AbstractLayer { } } - async syncData(syncContext) { - if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { - return; - } - - await this._syncSourceStyleMeta(syncContext); - await this._syncSourceFormatters(syncContext); + async _syncGeoJsonSource(syncContext) { const sourceResult = await this._syncSource(syncContext); if ( !sourceResult.featureCollection || @@ -592,6 +588,16 @@ export class VectorLayer extends AbstractLayer { await this._performInnerJoins(sourceResult, joinStates, syncContext.updateSourceData); } + async syncData(syncContext) { + if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { + return; + } + + await this._syncSourceStyleMeta(syncContext); + await this._syncSourceFormatters(syncContext); + await this._syncGeoJsonSource(syncContext); + } + _getSourceFeatureCollection() { const sourceDataRequest = this.getSourceDataRequest(); return sourceDataRequest ? sourceDataRequest.getData() : null; @@ -623,7 +629,7 @@ export class VectorLayer extends AbstractLayer { } } - _setMbPointsProperties(mbMap) { + _setMbPointsProperties(mbMap, options) { const pointLayerId = this._getMbPointLayerId(); const symbolLayerId = this._getMbSymbolLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); @@ -640,7 +646,7 @@ export class VectorLayer extends AbstractLayer { if (symbolLayer) { mbMap.setLayoutProperty(symbolLayerId, 'visibility', 'none'); } - this._setMbCircleProperties(mbMap); + this._setMbCircleProperties(mbMap, options); } else { markerLayerId = symbolLayerId; textLayerId = symbolLayerId; @@ -648,7 +654,7 @@ export class VectorLayer extends AbstractLayer { mbMap.setLayoutProperty(pointLayerId, 'visibility', 'none'); mbMap.setLayoutProperty(this._getMbTextLayerId(), 'visibility', 'none'); } - this._setMbSymbolProperties(mbMap); + this._setMbSymbolProperties(mbMap, options); } this.syncVisibilityWithMb(mbMap, markerLayerId); @@ -659,27 +665,35 @@ export class VectorLayer extends AbstractLayer { } } - _setMbCircleProperties(mbMap) { + _setMbCircleProperties(mbMap, { mvtSourceLayer }) { const sourceId = this.getId(); const pointLayerId = this._getMbPointLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); if (!pointLayer) { - mbMap.addLayer({ + const mbLayer = { id: pointLayerId, type: 'circle', source: sourceId, paint: {}, - }); + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); } const textLayerId = this._getMbTextLayerId(); const textLayer = mbMap.getLayer(textLayerId); if (!textLayer) { - mbMap.addLayer({ + const mbLayer = { id: textLayerId, type: 'symbol', source: sourceId, - }); + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); } const filterExpr = getPointFilterExpression(this._hasJoins()); @@ -701,17 +715,21 @@ export class VectorLayer extends AbstractLayer { }); } - _setMbSymbolProperties(mbMap) { + _setMbSymbolProperties(mbMap, { mvtSourceLayer }) { const sourceId = this.getId(); const symbolLayerId = this._getMbSymbolLayerId(); const symbolLayer = mbMap.getLayer(symbolLayerId); if (!symbolLayer) { - mbMap.addLayer({ + const mbLayer = { id: symbolLayerId, type: 'symbol', source: sourceId, - }); + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); } const filterExpr = getPointFilterExpression(this._hasJoins()); @@ -732,27 +750,57 @@ export class VectorLayer extends AbstractLayer { }); } - _setMbLinePolygonProperties(mbMap) { + _setMbLinePolygonProperties(mbMap, { mvtSourceLayer }) { const sourceId = this.getId(); const fillLayerId = this._getMbPolygonLayerId(); const lineLayerId = this._getMbLineLayerId(); + const tooManyFeaturesLayerId = this._getMbTooManyFeaturesLayerId(); const hasJoins = this._hasJoins(); if (!mbMap.getLayer(fillLayerId)) { - mbMap.addLayer({ + const mbLayer = { id: fillLayerId, type: 'fill', source: sourceId, paint: {}, - }); + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); } if (!mbMap.getLayer(lineLayerId)) { - mbMap.addLayer({ + const mbLayer = { id: lineLayerId, type: 'line', source: sourceId, paint: {}, - }); + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); } + if (!mbMap.getLayer(tooManyFeaturesLayerId)) { + const mbLayer = { + id: tooManyFeaturesLayerId, + type: 'fill', + source: sourceId, + paint: {}, + }; + if (mvtSourceLayer) { + mbLayer['source-layer'] = mvtSourceLayer; + } + mbMap.addLayer(mbLayer); + mbMap.setFilter(tooManyFeaturesLayerId, [ + '==', + ['get', KBN_TOO_MANY_FEATURES_PROPERTY], + true, + ]); + // mbMap.setPaintProperty(tooManyFeaturesLayerId, 'fill-color', 'rgb(255,0,0)'); + mbMap.setPaintProperty(tooManyFeaturesLayerId, 'fill-pattern', '__kbn_too_many_features__'); + mbMap.setPaintProperty(tooManyFeaturesLayerId, 'fill-opacity', this.getAlpha(),); + } + this._style.setMBPaintProperties({ alpha: this.getAlpha(), mbMap, @@ -773,11 +821,18 @@ export class VectorLayer extends AbstractLayer { if (lineFilterExpr !== mbMap.getFilter(lineLayerId)) { mbMap.setFilter(lineLayerId, lineFilterExpr); } + + this.syncVisibilityWithMb(mbMap, tooManyFeaturesLayerId); + mbMap.setLayerZoomRange( + tooManyFeaturesLayerId, + this._descriptor.minZoom, + this._descriptor.maxZoom + ); } _syncStylePropertiesWithMb(mbMap) { - this._setMbPointsProperties(mbMap); - this._setMbLinePolygonProperties(mbMap); + this._setMbPointsProperties(mbMap, {}); + this._setMbLinePolygonProperties(mbMap, {}); } _syncSourceBindingWithMb(mbMap) { @@ -816,6 +871,10 @@ export class VectorLayer extends AbstractLayer { return this.makeMbLayerId('fill'); } + _getMbTooManyFeaturesLayerId() { + return this.makeMbLayerId('toomanyfeatures'); + } + getMbLayerIds() { return [ this._getMbPointLayerId(), @@ -823,6 +882,7 @@ export class VectorLayer extends AbstractLayer { this._getMbSymbolLayerId(), this._getMbLineLayerId(), this._getMbPolygonLayerId(), + this._getMbTooManyFeaturesLayerId(), ]; } @@ -874,4 +934,30 @@ export class VectorLayer extends AbstractLayer { return feature.properties[FEATURE_ID_PROPERTY_NAME] === id; }); } + + getGeometryByFeatureId(featureId) { + const targetFeature = this.getFeatureById(featureId); + if (!targetFeature) { + return null; + } + + return targetFeature.geometry; + } + + async getFeaturePropertiesByFeatureId(featureId) { + const targetFeature = this.getFeatureById(featureId); + if (!targetFeature) { + return []; + } + return await this.getPropertiesForTooltip(targetFeature.properties); + } + + async loadPreIndexedShapeByFeatureId(featureId) { + const targetFeature = this.getFeatureById(featureId); + if (!targetFeature) { + return null; + } + + return await this.getSource().getPreIndexedShape(targetFeature.properties); + } } diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js index 4b3d1355e4264..ca23b5afac5a6 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js @@ -9,6 +9,7 @@ import _ from 'lodash'; import { TileLayer } from '../layers/tile_layer'; import { VectorTileLayer } from '../layers/vector_tile_layer'; import { VectorLayer } from '../layers/vector_layer'; +import { TiledVectorLayer } from '../layers/tiled_vector_layer'; import { HeatmapLayer } from '../layers/heatmap_layer'; import { ALL_SOURCES } from '../layers/sources/all_sources'; import { timefilter } from 'ui/timefilter'; @@ -27,6 +28,8 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) { return new VectorTileLayer({ layerDescriptor, source }); case HeatmapLayer.type: return new HeatmapLayer({ layerDescriptor, source }); + case TiledVectorLayer.type: + return new TiledVectorLayer({layerDescriptor, source}); default: throw new Error(`Unrecognized layerType ${layerDescriptor.type}`); } diff --git a/x-pack/legacy/plugins/maps/server/mvt/get_tile.js b/x-pack/legacy/plugins/maps/server/mvt/get_tile.js new file mode 100644 index 0000000000000..011bbd06a3148 --- /dev/null +++ b/x-pack/legacy/plugins/maps/server/mvt/get_tile.js @@ -0,0 +1,341 @@ +/* + * 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 geojsonvt from 'geojson-vt'; +import vtpbf from 'vt-pbf'; +import _ from 'lodash'; +import { + FEATURE_ID_PROPERTY_NAME, + MVT_SOURCE_ID, + KBN_TOO_MANY_FEATURES_PROPERTY, +} from '../../common/constants'; + +export async function getTile({ + server, + request, + indexPattern, + geometryFieldName, + x, + y, + z, + requestBody = {}, +}) { + const polygon = toBoundingBox(x, y, z); + + let resultFeatures; + + try { + let result; + try { + const geoShapeFilter = { + geo_shape: { + [geometryFieldName]: { + shape: polygon, + relation: 'INTERSECTS', + }, + }, + }; + + requestBody.query.bool.filter.push(geoShapeFilter); + + const esQuery = { + index: indexPattern, + body: requestBody, + }; + + const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); + // server.log('info', JSON.stringify(esQuery)); + const countQuery = { + index: indexPattern, + body: { + query: requestBody.query, + }, + }; + + // console.time('es-count') + const beforeCount = Date.now(); + const countResult = await callWithRequest(request, 'count', countQuery); + server.log('info', `count: ${Date.now() - beforeCount}`); + // server.log('info', `count`); + // server.log('info', countResult, requestBody.size); + + if (countResult.count > requestBody.size) { + // server.log('info', 'do NOT do search'); + resultFeatures = [ + { + type: 'Feature', + properties: { + [KBN_TOO_MANY_FEATURES_PROPERTY]: true, + }, + geometry: polygon, + }, + ]; + } else { + // server.log('info', 'do search'); + const beforeSearch = Date.now(); + result = await callWithRequest(request, 'search', esQuery); + server.log('info', `search ${Date.now() - beforeSearch}`); + + // server.log('info', `result length ${result.hits.hits.length}`); + + const feats = result.hits.hits.map(hit => { + let geomType; + const geometry = hit._source[geometryFieldName]; + if (geometry.type === 'polygon' || geometry.type === 'Polygon') { + geomType = 'Polygon'; + } else if (geometry.type === 'multipolygon' || geometry.type === 'MultiPolygon') { + geomType = 'MultiPolygon'; + } else if (geometry.type === 'linestring' || geometry.type === 'LineString') { + geomType = 'LineString'; + } else if (geometry.type === 'multilinestring' || geometry.type === 'MultiLineString') { + geomType = 'MultiLineString'; + } else if (geometry.type === 'point' || geometry.type === 'Point') { + geomType = 'Point'; + } else if (geometry.type === 'MultiPoint' || geomType.type === 'multipoint') { + geomType = 'MultiPoint'; + } else { + return null; + } + const geometryGeoJson = { + type: geomType, + coordinates: geometry.coordinates, + }; + + const firstFields = {}; + if (hit.fields) { + const fields = hit.fields; + Object.keys(fields).forEach(key => { + const value = fields[key]; + if (Array.isArray(value)) { + firstFields[key] = value[0]; + } else { + firstFields[key] = value; + } + }); + } + + const properties = { + ...hit._source, + ...firstFields, + _id: hit._id, + _index: hit._index, + [FEATURE_ID_PROPERTY_NAME]: hit._id, + [KBN_TOO_MANY_FEATURES_PROPERTY]: false, + }; + delete properties[geometryFieldName]; + + return { + type: 'Feature', + id: hit._id, + geometry: geometryGeoJson, + properties: properties, + }; + }); + + resultFeatures = feats.filter(f => !!f); + } + } catch (e) { + server.log('info', e.message); + throw e; + } + + const featureCollection = { + features: resultFeatures, + type: 'FeatureCollection', + }; + + // server.log('info', `feature length ${featureCollection.features.length}`); + + const beforeTile = Date.now(); + const tileIndex = geojsonvt(featureCollection, { + maxZoom: 24, // max zoom to preserve detail on; can't be higher than 24 + tolerance: 3, // simplification tolerance (higher means simpler) + extent: 4096, // tile extent (both width and height) + buffer: 64, // tile buffer on each side + debug: 0, // logging level (0 to disable, 1 or 2) + lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features + promoteId: null, // name of a feature property to promote to feature.id. Cannot be used with `generateId` + generateId: false, // whether to generate feature ids. Cannot be used with `promoteId` + indexMaxZoom: 5, // max zoom in the initial tile index + indexMaxPoints: 100000, // max number of points per tile in the index + }); + const tile = tileIndex.getTile(z, x, y); + server.log('info', `tile ${Date.now() - beforeTile}`); + + if (tile) { + const beforeBuffer = Date.now(); + + const pbf = vtpbf.fromGeojsonVt({ [MVT_SOURCE_ID]: tile }, { version: 2 }); + const buffer = Buffer.from(pbf); + server.log('info', `buffer ${Date.now() - beforeBuffer}`); + + // server.log('info', `bytelength: ${buffer.byteLength}`); + + return buffer; + } else { + return null; + } + } catch (e) { + return null; + } +} + +export async function getGridTile({ + server, + size, + request, + indexPattern, + geometryFieldName, + aggNames, + x, + y, + z, + fields = [], + requestBody = {}, +}) { + // server.log('info', { indexPattern, aggNames, requestBody, geometryFieldName, x, y, z, fields }); + const polygon = toBoundingBox(x, y, z); + + const wLon = tile2long(x, z); + const sLat = tile2lat(y + 1, z); + const eLon = tile2long(x + 1, z); + const nLat = tile2lat(y, z); + + try { + let result; + try { + const geoBoundingBox = { + geo_bounding_box: { + [geometryFieldName]: { + top_left: [wLon, nLat], + bottom_right: [eLon, sLat], + }, + }, + }; + + requestBody.query.bool.filter.push(geoBoundingBox); + + requestBody.aggs.grid.geotile_grid.precision = Math.min(z + 6, 24); + + const esQuery = { + index: indexPattern, + body: requestBody, + }; + // server.log('info', JSON.stringify(esQuery)); + + const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); + const beforeAgg = Date.now(); + result = await callWithRequest(request, 'search', esQuery); + server.log('info', `geotile_search ${Date.now() - beforeAgg}`); + + // server.log('info', JSON.stringify(result)); + } catch (e) { + server.log('warning', e.message); + throw e; + } + + const beforeTile = Date.now(); + const ffeats = []; + result.aggregations.grid.buckets.forEach(bucket => { + const feature = { + type: 'Feature', + properties: {}, + geometry: null, + }; + + for (let i = 0; i < aggNames.length; i++) { + const aggName = aggNames[i]; + if (aggName === 'doc_count') { + feature.properties[aggName] = bucket[aggName]; + } else if (aggName === 'grid') { + //do nothing + } else { + feature.properties[aggName] = bucket[aggName].value; + } + } + + feature.properties[KBN_TOO_MANY_FEATURES_PROPERTY] = false; + + const centroid = { + type: 'Point', + coordinates: [parseFloat(bucket['1'].location.lon), parseFloat(bucket['1'].location.lat)], + }; + + feature.geometry = centroid; + + ffeats.push(feature); + }); + + const featureCollection = { + features: ffeats, + type: 'FeatureCollection', + }; + + // server.log('info', `feature length ${featureCollection.features.length}`); + + const tileIndex = geojsonvt(featureCollection, { + maxZoom: 24, // max zoom to preserve detail on; can't be higher than 24 + tolerance: 3, // simplification tolerance (higher means simpler) + extent: 4096, // tile extent (both width and height) + buffer: 64, // tile buffer on each side + debug: 0, // logging level (0 to disable, 1 or 2) + lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features + promoteId: null, // name of a feature property to promote to feature.id. Cannot be used with `generateId` + generateId: false, // whether to generate feature ids. Cannot be used with `promoteId` + indexMaxZoom: 5, // max zoom in the initial tile index + indexMaxPoints: 100000, // max number of points per tile in the index + }); + const tile = tileIndex.getTile(z, x, y); + server.log('info', `tile ${Date.now() - beforeTile}`); + + if (tile) { + const beforeBuff = Date.now(); + const pbf = vtpbf.fromGeojsonVt({ [MVT_SOURCE_ID]: tile }, { version: 2 }); + const buffer = Buffer.from(pbf); + server.log('info', `buffer ${Date.now() - beforeBuff}`); + + return buffer; + } else { + return null; + } + } catch (e) { + return null; + } +} + +function toBoundingBox(x, y, z) { + const wLon = tile2long(x, z); + const sLat = tile2lat(y + 1, z); + const eLon = tile2long(x + 1, z); + const nLat = tile2lat(y, z); + + const polygon = { + type: 'Polygon', + coordinates: [ + [ + [wLon, sLat], + [wLon, nLat], + [eLon, nLat], + [eLon, sLat], + [wLon, sLat], + ], + ], + }; + + return polygon; +} + +tile2long(0, 1); +tile2lat(1, 20); + +function tile2long(x, z) { + return (x / Math.pow(2, z)) * 360 - 180; +} + +function tile2lat(y, z) { + const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z); + return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); +} diff --git a/x-pack/legacy/plugins/maps/server/mvt_routes.js b/x-pack/legacy/plugins/maps/server/mvt_routes.js new file mode 100644 index 0000000000000..8ad1ed0688b54 --- /dev/null +++ b/x-pack/legacy/plugins/maps/server/mvt_routes.js @@ -0,0 +1,105 @@ +/* + * 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 { GIS_API_PATH, MVT_GETTILE_API_PATH, MVT_GETGRIDTILE_API_PATH } from '../common/constants'; +import { getTile, getGridTile } from './mvt/get_tile'; +import rison from 'rison-node'; + +const ROOT = `/${GIS_API_PATH}`; + +export function initMVTRoutes(server) { + + server.route({ + method: 'GET', + path: `${ROOT}/${MVT_GETTILE_API_PATH}`, + handler: async (request, h) => { + const { server, query } = request; + + const indexPattern = query.indexPattern; + + const x = parseInt(query.x); + const y = parseInt(query.y); + const z = parseInt(query.z); + + const geometryFieldName = query.geometryFieldName; + const fields = query.fields ? query.fields.split(',') : []; + const size = parseInt(query.size) || 10000; + + const requestBodyDSL = rison.decode(query.requestBody); + // server.log('info',requestBodyDSL); + const tile = await getTile({ + server, + request, + size, + geometryFieldName, + fields, + x, + y, + z, + indexPattern, + requestBody: requestBodyDSL, + }); + + // server.log('info', tile); + + if (!tile) { + return null; + } + let response = h.response(tile); + response = response.bytes(tile.length); + response = response.header('Content-Disposition', 'inline'); + response.header('Content-Type', 'application/x-protobuf'); + response = response.encoding('binary'); + + return response; + }, + }); + + server.route({ + method: 'GET', + path: `${ROOT}/${MVT_GETGRIDTILE_API_PATH}`, + handler: async (request, h) => { + const { server, query } = request; + + const indexPattern = query.indexPattern; + + const x = parseInt(query.x); + const y = parseInt(query.y); + const z = parseInt(query.z); + + const geometryFieldName = query.geometryFieldName; + const fields = query.fields ? query.fields.split(',') : []; + const size = parseInt(query.size) || 10000; + + const requestBodyDSL = rison.decode(query.requestBody); + // server.log('info',requestBodyDSL); + const tile = await getGridTile({ + server, + request, + aggNames: query.aggNames.split(','), + size, + geometryFieldName, + fields, + x, + y, + z, + indexPattern, + requestBody: requestBodyDSL, + }); + + if (!tile) { + return null; + } + let response = h.response(tile); + response = response.bytes(tile.length); + response = response.header('Content-Disposition', 'inline'); + response.header('Content-Type', 'application/x-protobuf'); + response = response.encoding('binary'); + + return response; + }, + }); +} diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js index 757750dbb0813..f18a7bbe8c26e 100644 --- a/x-pack/legacy/plugins/maps/server/routes.js +++ b/x-pack/legacy/plugins/maps/server/routes.js @@ -27,6 +27,7 @@ import { i18n } from '@kbn/i18n'; import { getIndexPatternSettings } from './lib/get_index_pattern_settings'; import Boom from 'boom'; +import { initMVTRoutes } from './mvt_routes'; const ROOT = `/${GIS_API_PATH}`; @@ -65,6 +66,8 @@ export function initRoutes(server, licenseUid) { }; } + initMVTRoutes(server); + server.route({ method: 'GET', path: `${ROOT}/${EMS_FILES_API_PATH}/${EMS_FILES_DEFAULT_JSON_PATH}`, @@ -433,6 +436,8 @@ export function initRoutes(server, licenseUid) { handler: async (request, h) => { const { server, query } = request; + server.log('warning', 'getting index settings'); + if (!query.indexPatternTitle) { server.log('warning', `Required query parameter 'indexPatternTitle' not provided.`); return h.response().code(400); diff --git a/x-pack/package.json b/x-pack/package.json index 921f6ad991188..1bc789392a472 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -233,6 +233,7 @@ "formsy-react": "^1.1.5", "fp-ts": "^2.3.1", "geojson-rewind": "^0.3.1", + "geojson-vt": "^3.2.1", "get-port": "4.2.0", "getos": "^3.1.0", "git-url-parse": "11.1.2", @@ -345,6 +346,7 @@ "uuid": "3.3.2", "venn.js": "0.2.20", "vscode-languageserver": "^5.2.1", + "vt-pbf": "^3.1.1", "webpack": "4.41.0", "wellknown": "^0.5.0", "xml2js": "^0.4.22",