diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 5de21099c93404..0f2ce2c9177384 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -85,7 +85,6 @@ export const SOURCE_DATA_REQUEST_ID = 'source'; export const SOURCE_META_DATA_REQUEST_ID = `${SOURCE_DATA_REQUEST_ID}_${META_DATA_REQUEST_ID_SUFFIX}`; export const SOURCE_FORMATTERS_DATA_REQUEST_ID = `${SOURCE_DATA_REQUEST_ID}_${FORMATTERS_DATA_REQUEST_ID_SUFFIX}`; export const SOURCE_BOUNDS_DATA_REQUEST_ID = `${SOURCE_DATA_REQUEST_ID}_bounds`; -export const SUPPORTS_FEATURE_EDITING_REQUEST_ID = 'SUPPORTS_FEATURE_EDITING_REQUEST_ID'; export const MIN_ZOOM = 0; export const MAX_ZOOM = 24; diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index d921f9748f65cd..9ec9a42986fbbc 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -61,7 +61,7 @@ import { MapSettings } from '../reducers/map'; import { DrawState, MapCenterAndZoom, MapExtent, Timeslice } from '../../common/descriptor_types'; import { INITIAL_LOCATION } from '../../common/constants'; import { updateTooltipStateForLayer } from './tooltip_actions'; -import { VectorLayer } from '../classes/layers/vector_layer'; +import { isVectorLayer, IVectorLayer } from '../classes/layers/vector_layer'; import { SET_DRAW_MODE } from './ui_actions'; import { expandToTileBoundaries } from '../classes/util/geo_tile_utils'; import { getToasts } from '../kibana_services'; @@ -357,12 +357,12 @@ export function addNewFeatureToIndex(geometry: Geometry | Position[]) { return; } const layer = getLayerById(layerId, getState()); - if (!layer || !(layer instanceof VectorLayer)) { + if (!layer || !isVectorLayer(layer)) { return; } try { - await layer.addFeature(geometry); + await (layer as IVectorLayer).addFeature(geometry); await dispatch(syncDataForLayerDueToDrawing(layer)); } catch (e) { getToasts().addError(e, { @@ -385,11 +385,11 @@ export function deleteFeatureFromIndex(featureId: string) { return; } const layer = getLayerById(layerId, getState()); - if (!layer || !(layer instanceof VectorLayer)) { + if (!layer || !isVectorLayer(layer)) { return; } try { - await layer.deleteFeature(featureId); + await (layer as IVectorLayer).deleteFeature(featureId); await dispatch(syncDataForLayerDueToDrawing(layer)); } catch (e) { getToasts().addError(e, { diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts index 6b91e4812a1d60..ad507aa1716312 100644 --- a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts @@ -23,7 +23,7 @@ import { ESSearchSourceDescriptor, } from '../../../../common/descriptor_types'; import { VectorStyle } from '../../styles/vector/vector_style'; -import { VectorLayer } from '../vector_layer'; +import { GeoJsonVectorLayer } from '../vector_layer'; import { EMSFileSource } from '../../sources/ems_file_source'; // @ts-ignore import { ESSearchSource } from '../../sources/es_search_source'; @@ -51,7 +51,7 @@ function createChoroplethLayerDescriptor({ aggFieldName: '', rightSourceId: joinId, }); - return VectorLayer.createDescriptor({ + return GeoJsonVectorLayer.createDescriptor({ joins: [ { leftField, diff --git a/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts index 408460de28aeb8..19d9567a3480a0 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts @@ -22,7 +22,7 @@ import { } from '../../../common/constants'; import { VectorStyle } from '../styles/vector/vector_style'; import { EMSFileSource } from '../sources/ems_file_source'; -import { VectorLayer } from './vector_layer'; +import { GeoJsonVectorLayer } from './vector_layer'; import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults'; import { NUMERICAL_COLOR_PALETTES } from '../styles/color_palettes'; import { getJoinAggKey } from '../../../common/get_agg_key'; @@ -97,7 +97,7 @@ export function createRegionMapLayerDescriptor({ if (termsSize !== undefined) { termSourceDescriptor.size = termsSize; } - return VectorLayer.createDescriptor({ + return GeoJsonVectorLayer.createDescriptor({ label, joins: [ { diff --git a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts index 98217a5f28ad85..676ba4e8c88b18 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts @@ -24,7 +24,7 @@ import { } from '../../../common/constants'; import { VectorStyle } from '../styles/vector/vector_style'; import { ESGeoGridSource } from '../sources/es_geo_grid_source'; -import { VectorLayer } from './vector_layer'; +import { GeoJsonVectorLayer } from './vector_layer'; import { HeatmapLayer } from './heatmap_layer'; import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults'; import { NUMERICAL_COLOR_PALETTES } from '../styles/color_palettes'; @@ -162,7 +162,7 @@ export function createTileMapLayerDescriptor({ }; } - return VectorLayer.createDescriptor({ + return GeoJsonVectorLayer.createDescriptor({ label, sourceDescriptor: geoGridSourceDescriptor, style: VectorStyle.createDescriptor(styleProperties), diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index 87747d915af4a7..f29a4c3a55e285 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -12,7 +12,7 @@ import { FeatureCollection } from 'geojson'; import { EuiPanel } from '@elastic/eui'; import { DEFAULT_MAX_RESULT_WINDOW, SCALING_TYPES } from '../../../../common/constants'; import { GeoJsonFileSource } from '../../sources/geojson_file_source'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; import { createDefaultLayerDescriptor } from '../../sources/es_search_source'; import { RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { FileUploadGeoResults } from '../../../../../file_upload/public'; @@ -113,7 +113,7 @@ export class ClientFileCreateSourceEditor extends Component) { const heatmapLayerDescriptor = super.createDescriptor(options); - heatmapLayerDescriptor.type = HeatmapLayer.type; + heatmapLayerDescriptor.type = LAYER_TYPE.HEATMAP; heatmapLayerDescriptor.style = HeatmapStyle.createDescriptor(); return heatmapLayerDescriptor; } diff --git a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/wizard.tsx index 100e9dfa45c1db..d26853850c3872 100644 --- a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/wizard.tsx @@ -10,7 +10,7 @@ import { EuiPanel, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { createNewIndexAndPattern } from './create_new_index_pattern'; import { RenderWizardArguments } from '../layer_wizard_registry'; -import { VectorLayer } from '../vector_layer'; +import { GeoJsonVectorLayer } from '../vector_layer'; import { ESSearchSource } from '../../sources/es_search_source'; import { ADD_LAYER_STEP_ID } from '../../../connected_components/add_layer_panel/view'; import { getFileUpload, getIndexNameFormComponent } from '../../../kibana_services'; @@ -127,7 +127,7 @@ export class NewVectorLayerEditor extends Component { +jest.mock('../../../../kibana_services', () => { return { getIsDarkMode() { return false; diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts similarity index 88% rename from x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts index a158892be9d09f..e4c0ccdca09a4b 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts @@ -6,11 +6,12 @@ */ import { i18n } from '@kbn/i18n'; -import { IVectorLayer, VectorLayer } from '../vector_layer'; -import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style'; -import { getDefaultDynamicProperties } from '../../styles/vector/vector_style_defaults'; -import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; -import { IStyleProperty } from '../../styles/vector/properties/style_property'; +import { IVectorLayer } from '../vector_layer'; +import { GeoJsonVectorLayer } from '../geojson_vector_layer'; +import { IVectorStyle, VectorStyle } from '../../../styles/vector/vector_style'; +import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style_defaults'; +import { IDynamicStyleProperty } from '../../../styles/vector/properties/dynamic_style_property'; +import { IStyleProperty } from '../../../styles/vector/properties/style_property'; import { COUNT_PROP_LABEL, COUNT_PROP_NAME, @@ -21,13 +22,13 @@ import { VECTOR_STYLES, LAYER_STYLE_TYPE, FIELD_ORIGIN, -} from '../../../../common/constants'; -import { ESGeoGridSource } from '../../sources/es_geo_grid_source/es_geo_grid_source'; -import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; -import { IESSource } from '../../sources/es_source'; -import { ISource } from '../../sources/source'; -import { DataRequestContext } from '../../../actions'; -import { DataRequestAbortError } from '../../util/data_request'; +} from '../../../../../common/constants'; +import { ESGeoGridSource } from '../../../sources/es_geo_grid_source/es_geo_grid_source'; +import { canSkipSourceUpdate } from '../../../util/can_skip_fetch'; +import { IESSource } from '../../../sources/es_source'; +import { ISource } from '../../../sources/source'; +import { DataRequestContext } from '../../../../actions'; +import { DataRequestAbortError } from '../../../util/data_request'; import { VectorStyleDescriptor, SizeDynamicOptions, @@ -37,11 +38,11 @@ import { VectorLayerDescriptor, VectorSourceRequestMeta, VectorStylePropertiesDescriptor, -} from '../../../../common/descriptor_types'; -import { IVectorSource } from '../../sources/vector_source'; -import { LICENSED_FEATURES } from '../../../licensed_features'; -import { ESSearchSource } from '../../sources/es_search_source/es_search_source'; -import { isSearchSourceAbortError } from '../../sources/es_source/es_source'; +} from '../../../../../common/descriptor_types'; +import { IVectorSource } from '../../../sources/vector_source'; +import { LICENSED_FEATURES } from '../../../../licensed_features'; +import { ESSearchSource } from '../../../sources/es_search_source/es_search_source'; +import { isSearchSourceAbortError } from '../../../sources/es_source/es_source'; const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID'; @@ -170,14 +171,12 @@ export interface BlendedVectorLayerArguments { layerDescriptor: VectorLayerDescriptor; } -export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { - static type = LAYER_TYPE.BLENDED_VECTOR; - +export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLayer { static createDescriptor( options: Partial, mapColors: string[] ): VectorLayerDescriptor { - const layerDescriptor = VectorLayer.createDescriptor(options, mapColors); + const layerDescriptor = GeoJsonVectorLayer.createDescriptor(options, mapColors); layerDescriptor.type = LAYER_TYPE.BLENDED_VECTOR; return layerDescriptor; } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/index.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/index.ts new file mode 100644 index 00000000000000..a8079e06358d53 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { BlendedVectorLayer } from './blended_vector_layer'; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/assign_feature_ids.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts similarity index 100% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/assign_feature_ids.test.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/assign_feature_ids.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts similarity index 100% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/assign_feature_ids.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx new file mode 100644 index 00000000000000..80da6ceecf3a65 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx @@ -0,0 +1,405 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIcon } from '@elastic/eui'; +import { Feature, FeatureCollection } from 'geojson'; +import type { Map as MbMap, GeoJSONSource as MbGeoJSONSource } from '@kbn/mapbox-gl'; +import { + EMPTY_FEATURE_COLLECTION, + FEATURE_VISIBLE_PROPERTY_NAME, + FIELD_ORIGIN, + LAYER_TYPE, +} from '../../../../../common/constants'; +import { + StyleMetaDescriptor, + Timeslice, + VectorJoinSourceRequestMeta, + VectorLayerDescriptor, +} from '../../../../../common/descriptor_types'; +import { PropertiesMap } from '../../../../../common/elasticsearch_util'; +import { TimesliceMaskConfig } from '../../../util/mb_filter_expressions'; +import { DataRequestContext } from '../../../../actions'; +import { IVectorStyle, VectorStyle } from '../../../styles/vector/vector_style'; +import { ISource } from '../../../sources/source'; +import { IVectorSource } from '../../../sources/vector_source'; +import { AbstractLayer, CustomIconAndTooltipContent } from '../../layer'; +import { InnerJoin } from '../../../joins/inner_join'; +import { + AbstractVectorLayer, + noResultsIcon, + NO_RESULTS_ICON_AND_TOOLTIPCONTENT, +} from '../vector_layer'; +import { DataRequestAbortError } from '../../../util/data_request'; +import { canSkipSourceUpdate } from '../../../util/can_skip_fetch'; +import { getFeatureCollectionBounds } from '../../../util/get_feature_collection_bounds'; +import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids'; +import { addGeoJsonMbSource, syncVectorSource } from './utils'; +import { JoinState, performInnerJoins } from './perform_inner_joins'; +import { buildVectorRequestMeta } from '../../build_vector_request_meta'; + +export const SUPPORTS_FEATURE_EDITING_REQUEST_ID = 'SUPPORTS_FEATURE_EDITING_REQUEST_ID'; + +export class GeoJsonVectorLayer extends AbstractVectorLayer { + static createDescriptor( + options: Partial, + mapColors?: string[] + ): VectorLayerDescriptor { + const layerDescriptor = super.createDescriptor(options) as VectorLayerDescriptor; + layerDescriptor.type = LAYER_TYPE.VECTOR; + + if (!options.style) { + const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); + layerDescriptor.style = VectorStyle.createDescriptor(styleProperties); + } + + if (!options.joins) { + layerDescriptor.joins = []; + } + + return layerDescriptor; + } + + supportsFeatureEditing(): boolean { + const dataRequest = this.getDataRequest(SUPPORTS_FEATURE_EDITING_REQUEST_ID); + const data = dataRequest?.getData() as { supportsFeatureEditing: boolean } | undefined; + return data ? data.supportsFeatureEditing : false; + } + + async getBounds(syncContext: DataRequestContext) { + const isStaticLayer = !this.getSource().isBoundsAware(); + return isStaticLayer || this.hasJoins() + ? getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins()) + : super.getBounds(syncContext); + } + + getCustomIconAndTooltipContent(): CustomIconAndTooltipContent { + const featureCollection = this._getSourceFeatureCollection(); + + if (!featureCollection || featureCollection.features.length === 0) { + return NO_RESULTS_ICON_AND_TOOLTIPCONTENT; + } + + if ( + this.getJoins().length && + !featureCollection.features.some( + (feature) => feature.properties?.[FEATURE_VISIBLE_PROPERTY_NAME] + ) + ) { + return { + icon: noResultsIcon, + tooltipContent: i18n.translate('xpack.maps.vectorLayer.noResultsFoundInJoinTooltip', { + defaultMessage: `No matching results found in term joins`, + }), + }; + } + + const sourceDataRequest = this.getSourceDataRequest(); + const { tooltipContent, areResultsTrimmed, isDeprecated } = + this.getSource().getSourceTooltipContent(sourceDataRequest); + return { + icon: isDeprecated ? ( + + ) : ( + this.getCurrentStyle().getIcon() + ), + tooltipContent, + areResultsTrimmed, + }; + } + + getFeatureId(feature: Feature): string | number | undefined { + return feature.properties?.[GEOJSON_FEATURE_ID_PROPERTY_NAME]; + } + + getFeatureById(id: string | number) { + const featureCollection = this._getSourceFeatureCollection(); + if (!featureCollection) { + return null; + } + + const targetFeature = featureCollection.features.find((feature) => { + return this.getFeatureId(feature) === id; + }); + return targetFeature ? targetFeature : null; + } + + async getStyleMetaDescriptorFromLocalFeatures(): Promise { + const sourceDataRequest = this.getSourceDataRequest(); + const style = this.getCurrentStyle(); + if (!style || !sourceDataRequest) { + return null; + } + return await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest); + } + + syncLayerWithMB(mbMap: MbMap, timeslice?: Timeslice) { + addGeoJsonMbSource(this._getMbSourceId(), this.getMbLayerIds(), mbMap); + + this._syncFeatureCollectionWithMb(mbMap); + + const timesliceMaskConfig = this._getTimesliceMaskConfig(timeslice); + this._setMbLabelProperties(mbMap, undefined, timesliceMaskConfig); + this._setMbPointsProperties(mbMap, undefined, timesliceMaskConfig); + this._setMbLinePolygonProperties(mbMap, undefined, timesliceMaskConfig); + } + + _syncFeatureCollectionWithMb(mbMap: MbMap) { + const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource; + const featureCollection = this._getSourceFeatureCollection(); + const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId()); + + if (!featureCollection) { + if (featureCollectionOnMap) { + this.getCurrentStyle().clearFeatureState(featureCollectionOnMap, mbMap, this.getId()); + } + mbGeoJSONSource.setData(EMPTY_FEATURE_COLLECTION); + return; + } + + // "feature-state" data expressions are not supported with layout properties. + // To work around this limitation, + // scaled layout properties (like icon-size) must fall back to geojson property values :( + const hasGeoJsonProperties = this.getCurrentStyle().setFeatureStateAndStyleProps( + featureCollection, + mbMap, + this.getId() + ); + if (featureCollection !== featureCollectionOnMap || hasGeoJsonProperties) { + mbGeoJSONSource.setData(featureCollection); + } + } + + _getTimesliceMaskConfig(timeslice?: Timeslice): TimesliceMaskConfig | undefined { + if (!timeslice || this.hasJoins()) { + return; + } + + const prevMeta = this.getSourceDataRequest()?.getMeta(); + return prevMeta !== undefined && prevMeta.timesliceMaskField !== undefined + ? { + timesliceMaskField: prevMeta.timesliceMaskField, + timeslice, + } + : undefined; + } + + async syncData(syncContext: DataRequestContext) { + await this._syncData(syncContext, this.getSource(), this.getCurrentStyle()); + } + + // TLDR: Do not call getSource or getCurrentStyle in syncData flow. Use 'source' and 'style' arguments instead. + // + // 1) State is contained in the redux store. Layer instance state is readonly. + // 2) Even though data request descriptor updates trigger new instances for rendering, + // syncing data executes on a single object instance. Syncing data can not use updated redux store state. + // + // Blended layer data syncing branches on the source/style depending on whether clustering is used or not. + // Given 1 above, which source/style to use can not be stored in Layer instance state. + // Given 2 above, which source/style to use can not be pulled from data request state. + // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. + async _syncData(syncContext: DataRequestContext, source: IVectorSource, style: IVectorStyle) { + if (this.isLoadingBounds()) { + return; + } + + try { + await this._syncSourceStyleMeta(syncContext, source, style); + await this._syncSourceFormatters(syncContext, source, style); + const sourceResult = await syncVectorSource({ + layerId: this.getId(), + layerName: await this.getDisplayName(source), + prevDataRequest: this.getSourceDataRequest(), + requestMeta: await this._getVectorSourceRequestMeta( + syncContext.isForceRefresh, + syncContext.dataFilters, + source, + style + ), + syncContext, + source, + getUpdateDueToTimeslice: (timeslice?: Timeslice) => { + return this._getUpdateDueToTimesliceFromSourceRequestMeta(source, timeslice); + }, + }); + await this._syncSupportsFeatureEditing({ syncContext, source }); + if ( + !sourceResult.featureCollection || + !sourceResult.featureCollection.features.length || + !this.hasJoins() + ) { + return; + } + + const joinStates = await this._syncJoins(syncContext, style); + performInnerJoins( + sourceResult, + joinStates, + syncContext.updateSourceData, + syncContext.onJoinError + ); + } catch (error) { + if (!(error instanceof DataRequestAbortError)) { + throw error; + } + } + } + + async _syncJoin({ + join, + startLoading, + stopLoading, + onLoadError, + registerCancelCallback, + dataFilters, + isForceRefresh, + }: { join: InnerJoin } & DataRequestContext): Promise { + const joinSource = join.getRightJoinSource(); + const sourceDataId = join.getSourceDataRequestId(); + const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`); + + const joinRequestMeta: VectorJoinSourceRequestMeta = buildVectorRequestMeta( + joinSource, + joinSource.getFieldNames(), + dataFilters, + joinSource.getWhereQuery(), + isForceRefresh + ) as VectorJoinSourceRequestMeta; + + const prevDataRequest = this.getDataRequest(sourceDataId); + const canSkipFetch = await canSkipSourceUpdate({ + source: joinSource, + prevDataRequest, + nextRequestMeta: joinRequestMeta, + extentAware: false, // join-sources are term-aggs that are spatially unaware (e.g. ESTermSource/TableSource). + getUpdateDueToTimeslice: () => { + return true; + }, + }); + + if (canSkipFetch) { + return { + dataHasChanged: false, + join, + propertiesMap: prevDataRequest?.getData() as PropertiesMap, + }; + } + + try { + startLoading(sourceDataId, requestToken, joinRequestMeta); + const leftSourceName = await this._source.getDisplayName(); + const propertiesMap = await joinSource.getPropertiesMap( + joinRequestMeta, + leftSourceName, + join.getLeftField().getName(), + registerCancelCallback.bind(null, requestToken) + ); + stopLoading(sourceDataId, requestToken, propertiesMap); + return { + dataHasChanged: true, + join, + propertiesMap, + }; + } catch (error) { + if (!(error instanceof DataRequestAbortError)) { + onLoadError(sourceDataId, requestToken, `Join error: ${error.message}`); + } + throw error; + } + } + + async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) { + const joinSyncs = this.getValidJoins().map(async (join) => { + await this._syncJoinStyleMeta(syncContext, join, style); + await this._syncJoinFormatters(syncContext, join, style); + return this._syncJoin({ join, ...syncContext }); + }); + + return await Promise.all(joinSyncs); + } + + async _syncJoinStyleMeta(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) { + const joinSource = join.getRightJoinSource(); + return this._syncStyleMeta({ + source: joinSource, + style, + sourceQuery: joinSource.getWhereQuery(), + dataRequestId: join.getSourceMetaDataRequestId(), + dynamicStyleProps: this.getCurrentStyle() + .getDynamicPropertiesArray() + .filter((dynamicStyleProp) => { + const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName()); + return ( + dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && + !!matchingField && + dynamicStyleProp.isFieldMetaEnabled() + ); + }), + ...syncContext, + }); + } + + async _syncJoinFormatters(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) { + const joinSource = join.getRightJoinSource(); + return this._syncFormatters({ + source: joinSource, + dataRequestId: join.getSourceFormattersDataRequestId(), + fields: style + .getDynamicPropertiesArray() + .filter((dynamicStyleProp) => { + const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName()); + return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField; + }) + .map((dynamicStyleProp) => { + return dynamicStyleProp.getField()!; + }), + ...syncContext, + }); + } + + async _syncSupportsFeatureEditing({ + syncContext, + source, + }: { + syncContext: DataRequestContext; + source: IVectorSource; + }) { + if (syncContext.dataFilters.isReadOnly) { + return; + } + const { startLoading, stopLoading, onLoadError } = syncContext; + const dataRequestId = SUPPORTS_FEATURE_EDITING_REQUEST_ID; + const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); + const prevDataRequest = this.getDataRequest(dataRequestId); + if (prevDataRequest) { + return; + } + try { + startLoading(dataRequestId, requestToken); + const supportsFeatureEditing = await source.supportsFeatureEditing(); + stopLoading(dataRequestId, requestToken, { supportsFeatureEditing }); + } catch (error) { + onLoadError(dataRequestId, requestToken, error.message); + throw error; + } + } + + _getSourceFeatureCollection() { + const sourceDataRequest = this.getSourceDataRequest(); + return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null; + } + + _getUpdateDueToTimesliceFromSourceRequestMeta(source: ISource, timeslice?: Timeslice) { + const prevDataRequest = this.getSourceDataRequest(); + const prevMeta = prevDataRequest?.getMeta(); + if (!prevMeta) { + return true; + } + return source.getUpdateDueToTimeslice(prevMeta, timeslice); + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/get_centroid_features.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/get_centroid_features.test.ts similarity index 100% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/get_centroid_features.test.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/get_centroid_features.test.ts diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/get_centroid_features.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/get_centroid_features.ts similarity index 99% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/get_centroid_features.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/get_centroid_features.ts index 6afe61f8a16b9c..48df2661d269bc 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/get_centroid_features.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/get_centroid_features.ts @@ -21,7 +21,7 @@ import turfArea from '@turf/area'; import turfCenterOfMass from '@turf/center-of-mass'; import turfLength from '@turf/length'; import { lineString, polygon } from '@turf/helpers'; -import { GEO_JSON_TYPE, KBN_IS_CENTROID_FEATURE } from '../../../../common/constants'; +import { GEO_JSON_TYPE, KBN_IS_CENTROID_FEATURE } from '../../../../../common/constants'; export function getCentroidFeatures(featureCollection: FeatureCollection): Feature[] { const centroids = []; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/index.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/index.ts new file mode 100644 index 00000000000000..36566ba6c54ab7 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { GeoJsonVectorLayer } from './geojson_vector_layer'; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.test.ts similarity index 95% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.test.ts index 9346bb1621e44e..1049c4373c9335 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.test.ts @@ -8,16 +8,16 @@ import sinon from 'sinon'; import _ from 'lodash'; import { FeatureCollection } from 'geojson'; -import { ESTermSourceDescriptor } from '../../../../common/descriptor_types'; +import { ESTermSourceDescriptor } from '../../../../../common/descriptor_types'; import { AGG_TYPE, FEATURE_VISIBLE_PROPERTY_NAME, SOURCE_TYPES, -} from '../../../../common/constants'; +} from '../../../../../common/constants'; import { performInnerJoins } from './perform_inner_joins'; -import { InnerJoin } from '../../joins/inner_join'; -import { IVectorSource } from '../../sources/vector_source'; -import { IField } from '../../fields/field'; +import { InnerJoin } from '../../../joins/inner_join'; +import { IVectorSource } from '../../../sources/vector_source'; +import { IField } from '../../../fields/field'; const LEFT_FIELD = 'leftKey'; const COUNT_PROPERTY_NAME = '__kbnjoin__count__d3625663-5b34-4d50-a784-0d743f676a0c'; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.ts similarity index 94% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.ts index 23c6527d3e8180..3dd2a5ddb377ea 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/perform_inner_joins.ts @@ -7,10 +7,10 @@ import { FeatureCollection } from 'geojson'; import { i18n } from '@kbn/i18n'; -import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../../common/constants'; -import { DataRequestContext } from '../../../actions'; -import { InnerJoin } from '../../joins/inner_join'; -import { PropertiesMap } from '../../../../common/elasticsearch_util'; +import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../../../common/constants'; +import { DataRequestContext } from '../../../../actions'; +import { InnerJoin } from '../../../joins/inner_join'; +import { PropertiesMap } from '../../../../../common/elasticsearch_util'; interface SourceResult { refreshed: boolean; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/utils.tsx similarity index 92% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx rename to x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/utils.tsx index cc30f30fe98987..4385adbd4de652 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/utils.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/utils.tsx @@ -13,19 +13,19 @@ import { SOURCE_BOUNDS_DATA_REQUEST_ID, SOURCE_DATA_REQUEST_ID, VECTOR_SHAPE_TYPE, -} from '../../../../common/constants'; +} from '../../../../../common/constants'; import { DataRequestMeta, MapExtent, Timeslice, VectorSourceRequestMeta, -} from '../../../../common/descriptor_types'; -import { DataRequestContext } from '../../../actions'; -import { IVectorSource } from '../../sources/vector_source'; -import { DataRequestAbortError } from '../../util/data_request'; -import { DataRequest } from '../../util/data_request'; +} from '../../../../../common/descriptor_types'; +import { DataRequestContext } from '../../../../actions'; +import { IVectorSource } from '../../../sources/vector_source'; +import { DataRequestAbortError } from '../../../util/data_request'; +import { DataRequest } from '../../../util/data_request'; import { getCentroidFeatures } from './get_centroid_features'; -import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; +import { canSkipSourceUpdate } from '../../../util/can_skip_fetch'; import { assignFeatureIds } from './assign_feature_ids'; export function addGeoJsonMbSource(mbSourceId: string, mbLayerIds: string[], mbMap: MbMap) { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/index.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/index.ts index 2b14b78f929464..b3d7c47fbc71f4 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/index.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/index.ts @@ -5,6 +5,14 @@ * 2.0. */ -export { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from './utils'; +export { + addGeoJsonMbSource, + getVectorSourceBounds, + syncVectorSource, +} from './geojson_vector_layer/utils'; export type { IVectorLayer, VectorLayerArguments } from './vector_layer'; -export { isVectorLayer, VectorLayer, NO_RESULTS_ICON_AND_TOOLTIPCONTENT } from './vector_layer'; +export { isVectorLayer, NO_RESULTS_ICON_AND_TOOLTIPCONTENT } from './vector_layer'; + +export { BlendedVectorLayer } from './blended_vector_layer'; +export { GeoJsonVectorLayer } from './geojson_vector_layer'; +export { MvtVectorLayer } from './mvt_vector_layer'; diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/__snapshots__/tiled_vector_layer.test.tsx.snap b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/__snapshots__/mvt_vector_layer.test.tsx.snap similarity index 100% rename from x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/__snapshots__/tiled_vector_layer.test.tsx.snap rename to x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/__snapshots__/mvt_vector_layer.test.tsx.snap diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/index.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/index.ts new file mode 100644 index 00000000000000..85ff76f716a7b0 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { MvtVectorLayer } from './mvt_vector_layer'; diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx similarity index 85% rename from x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx rename to x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx index fd78ea2ebde596..60001cb9e8b1d2 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { MockSyncContext } from '../__fixtures__/mock_sync_context'; +import { MockSyncContext } from '../../__fixtures__/mock_sync_context'; import sinon from 'sinon'; import url from 'url'; -jest.mock('../../../kibana_services', () => { +jest.mock('../../../../kibana_services', () => { return { getIsDarkMode() { return false; @@ -20,14 +20,14 @@ jest.mock('../../../kibana_services', () => { import { shallow } from 'enzyme'; import { Feature } from 'geojson'; -import { MVTSingleLayerVectorSource } from '../../sources/mvt_single_layer_vector_source'; +import { MVTSingleLayerVectorSource } from '../../../sources/mvt_single_layer_vector_source'; import { DataRequestDescriptor, TiledSingleLayerVectorSourceDescriptor, VectorLayerDescriptor, -} from '../../../../common/descriptor_types'; -import { SOURCE_TYPES } from '../../../../common/constants'; -import { TiledVectorLayer } from './tiled_vector_layer'; +} from '../../../../../common/descriptor_types'; +import { SOURCE_TYPES } from '../../../../../common/constants'; +import { MvtVectorLayer } from './mvt_vector_layer'; const defaultConfig = { urlTemplate: 'https://example.com/{x}/{y}/{z}.pbf', @@ -41,7 +41,7 @@ function createLayer( sourceOptions: Partial = {}, isTimeAware: boolean = false, includeToken: boolean = false -): TiledVectorLayer { +): MvtVectorLayer { const sourceDescriptor: TiledSingleLayerVectorSourceDescriptor = { type: SOURCE_TYPES.MVT_SINGLE_LAYER, ...defaultConfig, @@ -76,28 +76,28 @@ function createLayer( ...layerOptions, sourceDescriptor, }; - const layerDescriptor = TiledVectorLayer.createDescriptor(defaultLayerOptions); - return new TiledVectorLayer({ layerDescriptor, source: mvtSource }); + const layerDescriptor = MvtVectorLayer.createDescriptor(defaultLayerOptions); + return new MvtVectorLayer({ layerDescriptor, source: mvtSource }); } describe('visiblity', () => { it('should get minzoom from source', async () => { - const layer: TiledVectorLayer = createLayer({}, {}); + const layer: MvtVectorLayer = createLayer({}, {}); expect(layer.getMinZoom()).toEqual(4); }); it('should get maxzoom from default', async () => { - const layer: TiledVectorLayer = createLayer({}, {}); + const layer: MvtVectorLayer = createLayer({}, {}); expect(layer.getMaxZoom()).toEqual(24); }); it('should get maxzoom from layer options', async () => { - const layer: TiledVectorLayer = createLayer({ maxZoom: 10 }, {}); + const layer: MvtVectorLayer = createLayer({ maxZoom: 10 }, {}); expect(layer.getMaxZoom()).toEqual(10); }); }); describe('getCustomIconAndTooltipContent', () => { it('Layers with non-elasticsearch sources should display icon', async () => { - const layer: TiledVectorLayer = createLayer({}, {}); + const layer: MvtVectorLayer = createLayer({}, {}); const iconAndTooltipContent = layer.getCustomIconAndTooltipContent(); const component = shallow(iconAndTooltipContent.icon); @@ -107,7 +107,7 @@ describe('getCustomIconAndTooltipContent', () => { describe('getFeatureById', () => { it('should return null feature', async () => { - const layer: TiledVectorLayer = createLayer({}, {}); + const layer: MvtVectorLayer = createLayer({}, {}); const feature = layer.getFeatureById('foobar') as Feature; expect(feature).toEqual(null); }); @@ -115,7 +115,7 @@ describe('getFeatureById', () => { describe('syncData', () => { it('Should sync with source-params', async () => { - const layer: TiledVectorLayer = createLayer({}, {}); + const layer: MvtVectorLayer = createLayer({}, {}); const syncContext = new MockSyncContext({ dataFilters: {} }); @@ -138,7 +138,7 @@ describe('syncData', () => { data: { ...defaultConfig }, dataId: 'source', }; - const layer: TiledVectorLayer = createLayer( + const layer: MvtVectorLayer = createLayer( { __dataRequests: [dataRequestDescriptor], }, @@ -157,7 +157,7 @@ describe('syncData', () => { data: { ...defaultConfig }, dataId: 'source', }; - const layer: TiledVectorLayer = createLayer( + const layer: MvtVectorLayer = createLayer( { __dataRequests: [dataRequestDescriptor], }, @@ -187,7 +187,7 @@ describe('syncData', () => { data: defaultConfig, dataId: 'source', }; - const layer: TiledVectorLayer = createLayer( + const layer: MvtVectorLayer = createLayer( { __dataRequests: [dataRequestDescriptor], }, @@ -217,7 +217,7 @@ describe('syncData', () => { const uuidRegex = /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; it(`should add token in url`, async () => { - const layer: TiledVectorLayer = createLayer({}, {}, false, true); + const layer: MvtVectorLayer = createLayer({}, {}, false, true); const syncContext = new MockSyncContext({ dataFilters: {} }); diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx similarity index 95% rename from x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx rename to x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx index 4b881228f79b53..237bab80ce758c 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.tsx @@ -16,38 +16,33 @@ import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import { parse as parseUrl } from 'url'; import { euiThemeVars } from '@kbn/ui-shared-deps-src/theme'; -import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style'; -import { LAYER_TYPE, SOURCE_DATA_REQUEST_ID, SOURCE_TYPES } from '../../../../common/constants'; +import { IVectorStyle, VectorStyle } from '../../../styles/vector/vector_style'; +import { LAYER_TYPE, SOURCE_DATA_REQUEST_ID, SOURCE_TYPES } from '../../../../../common/constants'; import { NO_RESULTS_ICON_AND_TOOLTIPCONTENT, - VectorLayer, + AbstractVectorLayer, VectorLayerArguments, } from '../vector_layer'; -import { ITiledSingleLayerVectorSource } from '../../sources/tiled_single_layer_vector_source'; -import { DataRequestContext } from '../../../actions'; +import { ITiledSingleLayerVectorSource } from '../../../sources/tiled_single_layer_vector_source'; +import { DataRequestContext } from '../../../../actions'; import { StyleMetaDescriptor, TileMetaFeature, Timeslice, VectorLayerDescriptor, VectorSourceRequestMeta, -} from '../../../../common/descriptor_types'; -import { MVTSingleLayerVectorSourceConfig } from '../../sources/mvt_single_layer_vector_source/types'; -import { ESSearchSource } from '../../sources/es_search_source'; -import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; -import { CustomIconAndTooltipContent } from '../layer'; +} from '../../../../../common/descriptor_types'; +import { MVTSingleLayerVectorSourceConfig } from '../../../sources/mvt_single_layer_vector_source/types'; +import { ESSearchSource } from '../../../sources/es_search_source'; +import { canSkipSourceUpdate } from '../../../util/can_skip_fetch'; +import { CustomIconAndTooltipContent } from '../../layer'; const ES_MVT_META_LAYER_NAME = 'meta'; const ES_MVT_HITS_TOTAL_RELATION = 'hits.total.relation'; const ES_MVT_HITS_TOTAL_VALUE = 'hits.total.value'; const MAX_RESULT_WINDOW_DATA_REQUEST_ID = 'maxResultWindow'; -/* - * MVT vector layer - */ -export class TiledVectorLayer extends VectorLayer { - static type = LAYER_TYPE.TILED_VECTOR; - +export class MvtVectorLayer extends AbstractVectorLayer { static createDescriptor( descriptor: Partial, mapColors?: string[] diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.test.tsx index 618be0b21cd73d..bd2c8a036bf596 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.test.tsx @@ -27,7 +27,7 @@ import { import { ESTermSourceDescriptor, VectorStyleDescriptor } from '../../../../common/descriptor_types'; import { getDefaultDynamicProperties } from '../../styles/vector/vector_style_defaults'; import { IVectorSource } from '../../sources/vector_source'; -import { VectorLayer } from './vector_layer'; +import { AbstractVectorLayer } from './vector_layer'; class MockSource { cloneDescriptor() { @@ -64,7 +64,7 @@ describe('cloneDescriptor', () => { }; test('Should update data driven styling properties using join fields', async () => { - const layerDescriptor = VectorLayer.createDescriptor({ + const layerDescriptor = AbstractVectorLayer.createDescriptor({ style: styleDescriptor, joins: [ { @@ -83,7 +83,7 @@ describe('cloneDescriptor', () => { }, ], }); - const layer = new VectorLayer({ + const layer = new AbstractVectorLayer({ layerDescriptor, source: new MockSource() as unknown as IVectorSource, }); @@ -105,7 +105,7 @@ describe('cloneDescriptor', () => { }); test('Should update data driven styling properties using join fields when metrics are not provided', async () => { - const layerDescriptor = VectorLayer.createDescriptor({ + const layerDescriptor = AbstractVectorLayer.createDescriptor({ style: styleDescriptor, joins: [ { @@ -120,7 +120,7 @@ describe('cloneDescriptor', () => { }, ], }); - const layer = new VectorLayer({ + const layer = new AbstractVectorLayer({ layerDescriptor, source: new MockSource() as unknown as IVectorSource, }); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 434743ef7ac9e9..59078c076433e8 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -7,13 +7,9 @@ import React from 'react'; import uuid from 'uuid/v4'; -import type { - Map as MbMap, - AnyLayer as MbLayer, - GeoJSONSource as MbGeoJSONSource, -} from '@kbn/mapbox-gl'; +import type { Map as MbMap, AnyLayer as MbLayer } from '@kbn/mapbox-gl'; import type { Query } from 'src/plugins/data/common'; -import { Feature, FeatureCollection, GeoJsonProperties, Geometry, Position } from 'geojson'; +import { Feature, GeoJsonProperties, Geometry, Position } from 'geojson'; import _ from 'lodash'; import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -23,24 +19,16 @@ import { AGG_TYPE, SOURCE_META_DATA_REQUEST_ID, SOURCE_FORMATTERS_DATA_REQUEST_ID, - FEATURE_VISIBLE_PROPERTY_NAME, - EMPTY_FEATURE_COLLECTION, LAYER_TYPE, FIELD_ORIGIN, FieldFormatter, SOURCE_TYPES, STYLE_TYPE, - SUPPORTS_FEATURE_EDITING_REQUEST_ID, VECTOR_STYLES, } from '../../../../common/constants'; import { JoinTooltipProperty } from '../../tooltips/join_tooltip_property'; import { DataRequestAbortError } from '../../util/data_request'; -import { - canSkipSourceUpdate, - canSkipStyleMetaUpdate, - canSkipFormattersUpdate, -} from '../../util/can_skip_fetch'; -import { getFeatureCollectionBounds } from '../../util/get_feature_collection_bounds'; +import { canSkipStyleMetaUpdate, canSkipFormattersUpdate } from '../../util/can_skip_fetch'; import { getLabelFilterExpression, getFillFilterExpression, @@ -55,13 +43,10 @@ import { ESTermSourceDescriptor, JoinDescriptor, StyleMetaDescriptor, - Timeslice, VectorLayerDescriptor, VectorSourceRequestMeta, VectorStyleRequestMeta, - VectorJoinSourceRequestMeta, } from '../../../../common/descriptor_types'; -import { ISource } from '../../sources/source'; import { IVectorSource } from '../../sources/vector_source'; import { CustomIconAndTooltipContent, ILayer } from '../layer'; import { InnerJoin } from '../../joins/inner_join'; @@ -70,13 +55,10 @@ import { DataRequestContext } from '../../../actions'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { IESSource } from '../../sources/es_source'; -import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { ITermJoinSource } from '../../sources/term_join_source'; -import { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from './utils'; -import { JoinState, performInnerJoins } from './perform_inner_joins'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { getJoinAggKey } from '../../../../common/get_agg_key'; -import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids'; +import { getVectorSourceBounds } from './geojson_vector_layer/utils'; export function isVectorLayer(layer: ILayer) { return (layer as IVectorLayer).canShowTooltip !== undefined; @@ -114,7 +96,7 @@ export interface IVectorLayer extends ILayer { deleteFeature(featureId: string): Promise; } -const noResultsIcon = ; +export const noResultsIcon = ; export const NO_RESULTS_ICON_AND_TOOLTIPCONTENT = { icon: noResultsIcon, tooltipContent: i18n.translate('xpack.maps.vectorLayer.noResultsFoundTooltip', { @@ -122,12 +104,7 @@ export const NO_RESULTS_ICON_AND_TOOLTIPCONTENT = { }), }; -/* - * Geojson vector layer - */ -export class VectorLayer extends AbstractLayer implements IVectorLayer { - static type = LAYER_TYPE.VECTOR; - +export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { protected readonly _style: VectorStyle; private readonly _joins: InnerJoin[]; @@ -265,9 +242,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } supportsFeatureEditing(): boolean { - const dataRequest = this.getDataRequest(SUPPORTS_FEATURE_EDITING_REQUEST_ID); - const data = dataRequest?.getData() as { supportsFeatureEditing: boolean } | undefined; - return data ? data.supportsFeatureEditing : false; + return false; } hasJoins() { @@ -296,38 +271,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } getCustomIconAndTooltipContent(): CustomIconAndTooltipContent { - const featureCollection = this._getSourceFeatureCollection(); - - if (!featureCollection || featureCollection.features.length === 0) { - return NO_RESULTS_ICON_AND_TOOLTIPCONTENT; - } - - if ( - this.getJoins().length && - !featureCollection.features.some( - (feature) => feature.properties?.[FEATURE_VISIBLE_PROPERTY_NAME] - ) - ) { - return { - icon: noResultsIcon, - tooltipContent: i18n.translate('xpack.maps.vectorLayer.noResultsFoundInJoinTooltip', { - defaultMessage: `No matching results found in term joins`, - }), - }; - } - - const sourceDataRequest = this.getSourceDataRequest(); - const { tooltipContent, areResultsTrimmed, isDeprecated } = - this.getSource().getSourceTooltipContent(sourceDataRequest); - return { - icon: isDeprecated ? ( - - ) : ( - this.getCurrentStyle().getIcon() - ), - tooltipContent, - areResultsTrimmed, - }; + throw new Error('Should implement AbstractVectorLayer#getCustomIconAndTooltipContent'); } getLayerTypeIconName() { @@ -343,15 +287,12 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } async getBounds(syncContext: DataRequestContext) { - const isStaticLayer = !this.getSource().isBoundsAware(); - return isStaticLayer || this.hasJoins() - ? getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins()) - : getVectorSourceBounds({ - layerId: this.getId(), - syncContext, - source: this.getSource(), - sourceQuery: this.getQuery(), - }); + return getVectorSourceBounds({ + layerId: this.getId(), + syncContext, + source: this.getSource(), + sourceQuery: this.getQuery(), + }); } async getLeftJoinFields() { @@ -409,79 +350,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { }); } - async _syncJoin({ - join, - startLoading, - stopLoading, - onLoadError, - registerCancelCallback, - dataFilters, - isForceRefresh, - }: { join: InnerJoin } & DataRequestContext): Promise { - const joinSource = join.getRightJoinSource(); - const sourceDataId = join.getSourceDataRequestId(); - const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`); - - const joinRequestMeta: VectorJoinSourceRequestMeta = buildVectorRequestMeta( - joinSource, - joinSource.getFieldNames(), - dataFilters, - joinSource.getWhereQuery(), - isForceRefresh - ) as VectorJoinSourceRequestMeta; - - const prevDataRequest = this.getDataRequest(sourceDataId); - const canSkipFetch = await canSkipSourceUpdate({ - source: joinSource, - prevDataRequest, - nextRequestMeta: joinRequestMeta, - extentAware: false, // join-sources are term-aggs that are spatially unaware (e.g. ESTermSource/TableSource). - getUpdateDueToTimeslice: () => { - return true; - }, - }); - - if (canSkipFetch) { - return { - dataHasChanged: false, - join, - propertiesMap: prevDataRequest?.getData() as PropertiesMap, - }; - } - - try { - startLoading(sourceDataId, requestToken, joinRequestMeta); - const leftSourceName = await this._source.getDisplayName(); - const propertiesMap = await joinSource.getPropertiesMap( - joinRequestMeta, - leftSourceName, - join.getLeftField().getName(), - registerCancelCallback.bind(null, requestToken) - ); - stopLoading(sourceDataId, requestToken, propertiesMap); - return { - dataHasChanged: true, - join, - propertiesMap, - }; - } catch (error) { - if (!(error instanceof DataRequestAbortError)) { - onLoadError(sourceDataId, requestToken, `Join error: ${error.message}`); - } - throw error; - } - } - - async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) { - const joinSyncs = this.getValidJoins().map(async (join) => { - await this._syncJoinStyleMeta(syncContext, join, style); - await this._syncJoinFormatters(syncContext, join, style); - return this._syncJoin({ join, ...syncContext }); - }); - - return await Promise.all(joinSyncs); - } - async _getVectorSourceRequestMeta( isForceRefresh: boolean, dataFilters: DataFilters, @@ -522,27 +390,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { }); } - async _syncJoinStyleMeta(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) { - const joinSource = join.getRightJoinSource(); - return this._syncStyleMeta({ - source: joinSource, - style, - sourceQuery: joinSource.getWhereQuery(), - dataRequestId: join.getSourceMetaDataRequestId(), - dynamicStyleProps: this.getCurrentStyle() - .getDynamicPropertiesArray() - .filter((dynamicStyleProp) => { - const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName()); - return ( - dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && - !!matchingField && - dynamicStyleProp.isFieldMetaEnabled() - ); - }), - ...syncContext, - }); - } - async _syncStyleMeta({ source, style, @@ -626,24 +473,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { }); } - async _syncJoinFormatters(syncContext: DataRequestContext, join: InnerJoin, style: IVectorStyle) { - const joinSource = join.getRightJoinSource(); - return this._syncFormatters({ - source: joinSource, - dataRequestId: join.getSourceFormattersDataRequestId(), - fields: style - .getDynamicPropertiesArray() - .filter((dynamicStyleProp) => { - const matchingField = joinSource.getFieldByName(dynamicStyleProp.getFieldName()); - return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField; - }) - .map((dynamicStyleProp) => { - return dynamicStyleProp.getField()!; - }), - ...syncContext, - }); - } - async _syncFormatters({ source, dataRequestId, @@ -693,128 +522,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } } - async syncData(syncContext: DataRequestContext) { - await this._syncData(syncContext, this.getSource(), this.getCurrentStyle()); - } - - // TLDR: Do not call getSource or getCurrentStyle in syncData flow. Use 'source' and 'style' arguments instead. - // - // 1) State is contained in the redux store. Layer instance state is readonly. - // 2) Even though data request descriptor updates trigger new instances for rendering, - // syncing data executes on a single object instance. Syncing data can not use updated redux store state. - // - // Blended layer data syncing branches on the source/style depending on whether clustering is used or not. - // Given 1 above, which source/style to use can not be stored in Layer instance state. - // Given 2 above, which source/style to use can not be pulled from data request state. - // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. - async _syncData(syncContext: DataRequestContext, source: IVectorSource, style: IVectorStyle) { - if (this.isLoadingBounds()) { - return; - } - - try { - await this._syncSourceStyleMeta(syncContext, source, style); - await this._syncSourceFormatters(syncContext, source, style); - const sourceResult = await syncVectorSource({ - layerId: this.getId(), - layerName: await this.getDisplayName(source), - prevDataRequest: this.getSourceDataRequest(), - requestMeta: await this._getVectorSourceRequestMeta( - syncContext.isForceRefresh, - syncContext.dataFilters, - source, - style - ), - syncContext, - source, - getUpdateDueToTimeslice: (timeslice?: Timeslice) => { - return this._getUpdateDueToTimesliceFromSourceRequestMeta(source, timeslice); - }, - }); - await this._syncSupportsFeatureEditing({ syncContext, source }); - if ( - !sourceResult.featureCollection || - !sourceResult.featureCollection.features.length || - !this.hasJoins() - ) { - return; - } - - const joinStates = await this._syncJoins(syncContext, style); - performInnerJoins( - sourceResult, - joinStates, - syncContext.updateSourceData, - syncContext.onJoinError - ); - } catch (error) { - if (!(error instanceof DataRequestAbortError)) { - throw error; - } - } - } - - async _syncSupportsFeatureEditing({ - syncContext, - source, - }: { - syncContext: DataRequestContext; - source: IVectorSource; - }) { - if (syncContext.dataFilters.isReadOnly) { - return; - } - const { startLoading, stopLoading, onLoadError } = syncContext; - const dataRequestId = SUPPORTS_FEATURE_EDITING_REQUEST_ID; - const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); - const prevDataRequest = this.getDataRequest(dataRequestId); - if (prevDataRequest) { - return; - } - try { - startLoading(dataRequestId, requestToken); - const supportsFeatureEditing = await source.supportsFeatureEditing(); - stopLoading(dataRequestId, requestToken, { supportsFeatureEditing }); - } catch (error) { - onLoadError(dataRequestId, requestToken, error.message); - throw error; - } - } - - _getSourceFeatureCollection() { - if (this.getSource().isMvt()) { - return null; - } - const sourceDataRequest = this.getSourceDataRequest(); - return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null; - } - - _syncFeatureCollectionWithMb(mbMap: MbMap) { - const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource; - const featureCollection = this._getSourceFeatureCollection(); - const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId()); - - if (!featureCollection) { - if (featureCollectionOnMap) { - this.getCurrentStyle().clearFeatureState(featureCollectionOnMap, mbMap, this.getId()); - } - mbGeoJSONSource.setData(EMPTY_FEATURE_COLLECTION); - return; - } - - // "feature-state" data expressions are not supported with layout properties. - // To work around this limitation, - // scaled layout properties (like icon-size) must fall back to geojson property values :( - const hasGeoJsonProperties = this.getCurrentStyle().setFeatureStateAndStyleProps( - featureCollection, - mbMap, - this.getId() - ); - if (featureCollection !== featureCollectionOnMap || hasGeoJsonProperties) { - mbGeoJSONSource.setData(featureCollection); - } - } - _setMbPointsProperties( mbMap: MbMap, mvtSourceLayer?: string, @@ -989,33 +696,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { mbMap.setLayerZoomRange(labelLayerId, this.getMinZoom(), this.getMaxZoom()); } - _syncStylePropertiesWithMb(mbMap: MbMap, timeslice?: Timeslice) { - const timesliceMaskConfig = this._getTimesliceMaskConfig(timeslice); - this._setMbLabelProperties(mbMap, undefined, timesliceMaskConfig); - this._setMbPointsProperties(mbMap, undefined, timesliceMaskConfig); - this._setMbLinePolygonProperties(mbMap, undefined, timesliceMaskConfig); - } - - _getTimesliceMaskConfig(timeslice?: Timeslice): TimesliceMaskConfig | undefined { - if (!timeslice || this.hasJoins()) { - return; - } - - const prevMeta = this.getSourceDataRequest()?.getMeta(); - return prevMeta !== undefined && prevMeta.timesliceMaskField !== undefined - ? { - timesliceMaskField: prevMeta.timesliceMaskField, - timeslice, - } - : undefined; - } - - syncLayerWithMB(mbMap: MbMap, timeslice?: Timeslice) { - addGeoJsonMbSource(this._getMbSourceId(), this.getMbLayerIds(), mbMap); - this._syncFeatureCollectionWithMb(mbMap); - this._syncStylePropertiesWithMb(mbMap, timeslice); - } - _getMbPointLayerId() { return this.makeMbLayerId('circle'); } @@ -1090,34 +770,17 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } getFeatureId(feature: Feature): string | number | undefined { - return feature.properties?.[GEOJSON_FEATURE_ID_PROPERTY_NAME]; + throw new Error('Should implement AbstractVectorLayer#getFeatureId'); } - getFeatureById(id: string | number) { - const featureCollection = this._getSourceFeatureCollection(); - if (!featureCollection) { - return null; - } - - const targetFeature = featureCollection.features.find((feature) => { - return this.getFeatureId(feature) === id; - }); - return targetFeature ? targetFeature : null; + getFeatureById(id: string | number): Feature | null { + throw new Error('Should implement AbstractVectorLayer#getFeatureById'); } async getLicensedFeatures() { return await this._source.getLicensedFeatures(); } - _getUpdateDueToTimesliceFromSourceRequestMeta(source: ISource, timeslice?: Timeslice) { - const prevDataRequest = this.getSourceDataRequest(); - const prevMeta = prevDataRequest?.getMeta(); - if (!prevMeta) { - return true; - } - return source.getUpdateDueToTimeslice(prevMeta, timeslice); - } - async addFeature(geometry: Geometry | Position[]) { const layerSource = this.getSource(); const defaultFields = await layerSource.getDefaultFields(); @@ -1130,11 +793,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer { } async getStyleMetaDescriptorFromLocalFeatures(): Promise { - const sourceDataRequest = this.getSourceDataRequest(); - const style = this.getCurrentStyle(); - if (!style || !sourceDataRequest) { - return null; - } - return await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest); + throw new Error('Should implement AbstractVectorLayer#getStyleMetaDescriptorFromLocalFeatures'); } } diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx index d4cf4dbee79430..dd2317506e5f9e 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { EMSFileCreateSourceEditor } from './create_source_editor'; import { EMSFileSource, getSourceTitle } from './ems_file_source'; @@ -46,7 +46,7 @@ export const emsBoundariesLayerWizardConfig: LayerWizard = { renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: Partial) => { const sourceDescriptor = EMSFileSource.createDescriptor(sourceConfig); - const layerDescriptor = VectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); previewLayers([layerDescriptor]); }; return ; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx index 36dd28cb5bbf11..ad046eeb02d47f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { CreateSourceEditor } from './create_source_editor'; import { ESGeoGridSource, clustersTitle } from './es_geo_grid_source'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; import { ESGeoGridSourceDescriptor, ColorDynamicOptions, @@ -45,7 +45,7 @@ export const clustersLayerWizardConfig: LayerWizard = { } const defaultDynamicProperties = getDefaultDynamicProperties(); - const layerDescriptor = VectorLayer.createDescriptor({ + const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor: ESGeoGridSource.createDescriptor(sourceConfig), style: VectorStyle.createDescriptor({ // @ts-ignore diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/layer_wizard.tsx index 8da7037a5a34cd..ba1c3c4eece4b2 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/layer_wizard.tsx @@ -12,7 +12,7 @@ import { ESGeoLineSource, geoLineTitle, REQUIRES_GOLD_LICENSE_MSG } from './es_g import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { LAYER_WIZARD_CATEGORY, STYLE_TYPE, VECTOR_STYLES } from '../../../../common/constants'; import { VectorStyle } from '../../styles/vector/vector_style'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; import { getIsGoldPlus } from '../../../licensed_features'; import { TracksLayerIcon } from '../../layers/icons/tracks_layer_icon'; @@ -40,7 +40,7 @@ export const geoLineLayerWizardConfig: LayerWizard = { return; } - const layerDescriptor = VectorLayer.createDescriptor({ + const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor: ESGeoLineSource.createDescriptor(sourceConfig), style: VectorStyle.createDescriptor({ [VECTOR_STYLES.LINE_WIDTH]: { diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx index c94c7859a85e79..84dea15daf48f1 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { getDefaultDynamicProperties } from '../../styles/vector/vector_style_defaults'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; // @ts-ignore import { ESPewPewSource, sourceTitle } from './es_pew_pew_source'; import { VectorStyle } from '../../styles/vector/vector_style'; @@ -40,7 +40,7 @@ export const point2PointLayerWizardConfig: LayerWizard = { } const defaultDynamicProperties = getDefaultDynamicProperties(); - const layerDescriptor = VectorLayer.createDescriptor({ + const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor: ESPewPewSource.createDescriptor(sourceConfig), style: VectorStyle.createDescriptor({ [VECTOR_STYLES.LINE_COLOR]: { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_layer_descriptor.ts index 41b4e8d7a318a4..5553e925258e9a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_layer_descriptor.ts @@ -9,7 +9,7 @@ import { Query } from 'src/plugins/data/public'; import { LayerDescriptor } from '../../../../common/descriptor_types'; import { ES_GEO_FIELD_TYPE, SCALING_TYPES } from '../../../../common/constants'; import { ESSearchSource } from './es_search_source'; -import { VectorLayer } from '../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../layers/vector_layer'; import { getIsGoldPlus } from '../../../licensed_features'; export interface CreateLayerDescriptorParams { @@ -37,5 +37,5 @@ export function createLayerDescriptor({ scalingType, }); - return VectorLayer.createDescriptor({ sourceDescriptor, query }); + return GeoJsonVectorLayer.createDescriptor({ sourceDescriptor, query }); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx index 26771c1bed0231..601fcee50ab2ae 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx @@ -11,10 +11,8 @@ import React from 'react'; import { CreateSourceEditor } from './create_source_editor'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { ESSearchSource, sourceTitle } from './es_search_source'; -import { BlendedVectorLayer } from '../../layers/blended_vector_layer/blended_vector_layer'; -import { VectorLayer } from '../../layers/vector_layer'; +import { BlendedVectorLayer, GeoJsonVectorLayer, MvtVectorLayer } from '../../layers/vector_layer'; import { LAYER_WIZARD_CATEGORY, SCALING_TYPES } from '../../../../common/constants'; -import { TiledVectorLayer } from '../../layers/tiled_vector_layer/tiled_vector_layer'; import { DocumentsLayerIcon } from '../../layers/icons/documents_layer_icon'; import { ESSearchSourceDescriptor, @@ -30,9 +28,9 @@ export function createDefaultLayerDescriptor( if (sourceDescriptor.scalingType === SCALING_TYPES.CLUSTERS) { return BlendedVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); } else if (sourceDescriptor.scalingType === SCALING_TYPES.MVT) { - return TiledVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + return MvtVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); } else { - return VectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + return GeoJsonVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx index e02ada305ecff3..b4339eb20b1fd8 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/top_hits/wizard.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { CreateSourceEditor } from './create_source_editor'; import { LayerWizard, RenderWizardArguments } from '../../../layers/layer_wizard_registry'; -import { VectorLayer } from '../../../layers/vector_layer'; +import { GeoJsonVectorLayer } from '../../../layers/vector_layer'; import { LAYER_WIZARD_CATEGORY } from '../../../../../common/constants'; import { TopHitsLayerIcon } from '../../../layers/icons/top_hits_layer_icon'; import { ESSearchSourceDescriptor } from '../../../../../common/descriptor_types'; @@ -30,7 +30,7 @@ export const esTopHitsLayerWizardConfig: LayerWizard = { } const sourceDescriptor = ESSearchSource.createDescriptor(sourceConfig); - const layerDescriptor = VectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + const layerDescriptor = GeoJsonVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); previewLayers([layerDescriptor]); }; return ; diff --git a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/layer_wizard.tsx index e5e1877c1ccd1c..a3f7ceafd54efb 100644 --- a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/layer_wizard.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { MVTSingleLayerVectorSourceEditor } from './mvt_single_layer_vector_source_editor'; import { MVTSingleLayerVectorSource, sourceTitle } from './mvt_single_layer_vector_source'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; -import { TiledVectorLayer } from '../../layers/tiled_vector_layer/tiled_vector_layer'; +import { MvtVectorLayer } from '../../layers/vector_layer'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; import { TiledSingleLayerVectorSourceSettings } from '../../../../common/descriptor_types'; import { VectorTileLayerIcon } from '../../layers/icons/vector_tile_layer_icon'; @@ -24,7 +24,7 @@ export const mvtVectorSourceWizardConfig: LayerWizard = { renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: TiledSingleLayerVectorSourceSettings) => { const sourceDescriptor = MVTSingleLayerVectorSource.createDescriptor(sourceConfig); - const layerDescriptor = TiledVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); + const layerDescriptor = MvtVectorLayer.createDescriptor({ sourceDescriptor }, mapColors); previewLayers([layerDescriptor]); }; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index 93dfebecd1c34b..eb0196ea156aa8 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -50,7 +50,7 @@ import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public import { RenderToolTipContent } from '../../classes/tooltips/tooltip_property'; import { TileStatusTracker } from './tile_status_tracker'; import { DrawFeatureControl } from './draw_control/draw_feature_control'; -import { TiledVectorLayer } from '../../classes/layers/tiled_vector_layer/tiled_vector_layer'; +import { MvtVectorLayer } from '../../classes/layers/vector_layer'; import type { MapExtentState } from '../../reducers/map/types'; export interface Props { @@ -127,7 +127,7 @@ export class MbMap extends Component { // This keeps track of the latest update calls, per layerId _queryForMeta = (layer: ILayer) => { if (this.state.mbMap && layer.isVisible() && layer.getType() === LAYER_TYPE.TILED_VECTOR) { - const mbFeatures = (layer as TiledVectorLayer).queryTileMetaFeatures(this.state.mbMap); + const mbFeatures = (layer as MvtVectorLayer).queryTileMetaFeatures(this.state.mbMap); if (mbFeatures !== null) { this.props.updateMetaFromTiles(layer.getId(), mbFeatures); } diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index 51acab6453921f..db903c6a025937 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -18,7 +18,7 @@ import { getVisibilityToggleLabel, } from '../action_labels'; import { ESSearchSource } from '../../../../../../classes/sources/es_search_source'; -import { VectorLayer } from '../../../../../../classes/layers/vector_layer'; +import { isVectorLayer, IVectorLayer } from '../../../../../../classes/layers/vector_layer'; import { SCALING_TYPES, VECTOR_SHAPE_TYPE } from '../../../../../../../common/constants'; export interface Props { @@ -67,10 +67,10 @@ export class TOCEntryActionsPopover extends Component { } async _loadFeatureEditing() { - if (!(this.props.layer instanceof VectorLayer)) { + if (!isVectorLayer(this.props.layer)) { return; } - const supportsFeatureEditing = this.props.layer.supportsFeatureEditing(); + const supportsFeatureEditing = (this.props.layer as IVectorLayer).supportsFeatureEditing(); const isFeatureEditingEnabled = await this._getIsFeatureEditingEnabled(); if ( !this._isMounted || @@ -83,7 +83,7 @@ export class TOCEntryActionsPopover extends Component { } async _getIsFeatureEditingEnabled(): Promise { - const vectorLayer = this.props.layer as VectorLayer; + const vectorLayer = this.props.layer as IVectorLayer; const layerSource = this.props.layer.getSource(); if (!(layerSource instanceof ESSearchSource)) { return false; diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts index dc3c6dca462372..4f336d9a8ad275 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts @@ -7,8 +7,6 @@ import { LAYER_STYLE_TYPE, LAYER_TYPE, SOURCE_TYPES } from '../../common/constants'; -jest.mock('../classes/layers/tiled_vector_layer/tiled_vector_layer', () => {}); -jest.mock('../classes/layers/blended_vector_layer/blended_vector_layer', () => {}); jest.mock('../classes/layers/heatmap_layer', () => {}); jest.mock('../classes/layers/vector_tile_layer/vector_tile_layer', () => {}); jest.mock('../classes/joins/inner_join', () => {}); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 5ca297bdff0207..f58525ea6f9741 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -13,21 +13,25 @@ import type { Query } from 'src/plugins/data/common'; import { TileLayer } from '../classes/layers/tile_layer/tile_layer'; // @ts-ignore import { VectorTileLayer } from '../classes/layers/vector_tile_layer/vector_tile_layer'; -import { IVectorLayer, VectorLayer } from '../classes/layers/vector_layer'; +import { + BlendedVectorLayer, + IVectorLayer, + MvtVectorLayer, + GeoJsonVectorLayer, +} from '../classes/layers/vector_layer'; import { VectorStyle } from '../classes/styles/vector/vector_style'; import { HeatmapLayer } from '../classes/layers/heatmap_layer'; -import { BlendedVectorLayer } from '../classes/layers/blended_vector_layer/blended_vector_layer'; import { getTimeFilter } from '../kibana_services'; import { getChartsPaletteServiceGetColor, getInspectorAdapters, } from '../reducers/non_serializable_instances'; -import { TiledVectorLayer } from '../classes/layers/tiled_vector_layer/tiled_vector_layer'; import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/copy_persistent_state'; import { InnerJoin } from '../classes/joins/inner_join'; import { getSourceByType } from '../classes/sources/source_registry'; import { GeoJsonFileSource } from '../classes/sources/geojson_file_source'; import { + LAYER_TYPE, SOURCE_DATA_REQUEST_ID, SPATIAL_FILTERS_LAYER_ID, STYLE_TYPE, @@ -66,9 +70,9 @@ export function createLayerInstance( const source: ISource = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); switch (layerDescriptor.type) { - case TileLayer.type: + case LAYER_TYPE.TILE: return new TileLayer({ layerDescriptor, source: source as ITMSSource }); - case VectorLayer.type: + case LAYER_TYPE.VECTOR: const joins: InnerJoin[] = []; const vectorLayerDescriptor = layerDescriptor as VectorLayerDescriptor; if (vectorLayerDescriptor.joins) { @@ -77,27 +81,27 @@ export function createLayerInstance( joins.push(join); }); } - return new VectorLayer({ + return new GeoJsonVectorLayer({ layerDescriptor: vectorLayerDescriptor, source: source as IVectorSource, joins, chartsPaletteServiceGetColor, }); - case VectorTileLayer.type: + case LAYER_TYPE.VECTOR_TILE: return new VectorTileLayer({ layerDescriptor, source: source as ITMSSource }); - case HeatmapLayer.type: + case LAYER_TYPE.HEATMAP: return new HeatmapLayer({ layerDescriptor: layerDescriptor as HeatmapLayerDescriptor, source: source as ESGeoGridSource, }); - case BlendedVectorLayer.type: + case LAYER_TYPE.BLENDED_VECTOR: return new BlendedVectorLayer({ layerDescriptor: layerDescriptor as VectorLayerDescriptor, source: source as IVectorSource, chartsPaletteServiceGetColor, }); - case TiledVectorLayer.type: - return new TiledVectorLayer({ + case LAYER_TYPE.TILED_VECTOR: + return new MvtVectorLayer({ layerDescriptor: layerDescriptor as VectorLayerDescriptor, source: source as IVectorSource, }); @@ -266,8 +270,8 @@ export const getSpatialFiltersLayer = createSelector( name: 'spatialFilters', }); - return new VectorLayer({ - layerDescriptor: VectorLayer.createDescriptor({ + return new GeoJsonVectorLayer({ + layerDescriptor: GeoJsonVectorLayer.createDescriptor({ id: SPATIAL_FILTERS_LAYER_ID, visible: settings.showSpatialFilters, alpha: settings.spatialFiltersAlpa,