[Maps] Add 3rd party vector tile support (#62084)
Adds support for adding an external vector tile service to Maps. This is experimental functionality. To enable, add `xpack.maps.enableVectorTiles: true` to the `kibana.yml`configuration file.
This commit is contained in:
parent
0a9e17b57f
commit
ad41eea211
|
@ -25,7 +25,7 @@ export * from './ui_settings';
|
||||||
export * from './field_icon';
|
export * from './field_icon';
|
||||||
export * from './table_list_view';
|
export * from './table_list_view';
|
||||||
export * from './split_panel';
|
export * from './split_panel';
|
||||||
export { ValidatedDualRange } from './validated_range';
|
export { ValidatedDualRange, Value } from './validated_range';
|
||||||
export * from './notifications';
|
export * from './notifications';
|
||||||
export { Markdown, MarkdownSimple } from './markdown';
|
export { Markdown, MarkdownSimple } from './markdown';
|
||||||
export { reactToUiComponent, uiToReactComponent } from './adapters';
|
export { reactToUiComponent, uiToReactComponent } from './adapters';
|
||||||
|
|
|
@ -17,4 +17,4 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { ValidatedDualRange } from './validated_dual_range';
|
export { ValidatedDualRange, Value } from './validated_dual_range';
|
||||||
|
|
|
@ -38,6 +38,7 @@ export function maps(kibana) {
|
||||||
return {
|
return {
|
||||||
showMapVisualizationTypes: serverConfig.get('xpack.maps.showMapVisualizationTypes'),
|
showMapVisualizationTypes: serverConfig.get('xpack.maps.showMapVisualizationTypes'),
|
||||||
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
|
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
|
||||||
|
enableVectorTiles: serverConfig.get('xpack.maps.enableVectorTiles'),
|
||||||
preserveDrawingBuffer: serverConfig.get('xpack.maps.preserveDrawingBuffer'),
|
preserveDrawingBuffer: serverConfig.get('xpack.maps.preserveDrawingBuffer'),
|
||||||
isEmsEnabled: mapConfig.includeElasticMapsService,
|
isEmsEnabled: mapConfig.includeElasticMapsService,
|
||||||
emsFontLibraryUrl: mapConfig.emsFontLibraryUrl,
|
emsFontLibraryUrl: mapConfig.emsFontLibraryUrl,
|
||||||
|
@ -85,6 +86,7 @@ export function maps(kibana) {
|
||||||
showMapVisualizationTypes: Joi.boolean().default(false),
|
showMapVisualizationTypes: Joi.boolean().default(false),
|
||||||
showMapsInspectorAdapter: Joi.boolean().default(false), // flag used in functional testing
|
showMapsInspectorAdapter: Joi.boolean().default(false), // flag used in functional testing
|
||||||
preserveDrawingBuffer: Joi.boolean().default(false), // flag used in functional testing
|
preserveDrawingBuffer: Joi.boolean().default(false), // flag used in functional testing
|
||||||
|
enableVectorTiles: Joi.boolean().default(false), // flag used to enable/disable vector-tiles
|
||||||
}).default();
|
}).default();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,13 @@ export class AddLayerPanel extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const style =
|
const styleDescriptor =
|
||||||
this.state.layer && this.state.layer.getCurrentStyle()
|
this.state.layer && this.state.layer.getCurrentStyle()
|
||||||
? this.state.layer.getCurrentStyle().getDescriptor()
|
? this.state.layer.getCurrentStyle().getDescriptor()
|
||||||
: null;
|
: null;
|
||||||
const layerInitProps = {
|
const layerInitProps = {
|
||||||
...options,
|
...options,
|
||||||
style: style,
|
style: styleDescriptor,
|
||||||
};
|
};
|
||||||
const newLayer = source.createDefaultLayer(layerInitProps, this.props.mapColors);
|
const newLayer = source.createDefaultLayer(layerInitProps, this.props.mapColors);
|
||||||
if (!this._isMounted) {
|
if (!this._isMounted) {
|
||||||
|
|
|
@ -13,10 +13,13 @@ import {
|
||||||
updateLayerMinZoom,
|
updateLayerMinZoom,
|
||||||
updateLayerAlpha,
|
updateLayerAlpha,
|
||||||
} from '../../../actions/map_actions';
|
} from '../../../actions/map_actions';
|
||||||
|
import { MAX_ZOOM } from '../../../../../../../plugins/maps/common/constants';
|
||||||
|
|
||||||
function mapStateToProps(state = {}) {
|
function mapStateToProps(state = {}) {
|
||||||
const selectedLayer = getSelectedLayer(state);
|
const selectedLayer = getSelectedLayer(state);
|
||||||
return {
|
return {
|
||||||
|
minVisibilityZoom: selectedLayer.getMinSourceZoom(),
|
||||||
|
maxVisibilityZoom: MAX_ZOOM,
|
||||||
alpha: selectedLayer.getAlpha(),
|
alpha: selectedLayer.getAlpha(),
|
||||||
label: selectedLayer.getLabel(),
|
label: selectedLayer.getLabel(),
|
||||||
layerId: selectedLayer.getId(),
|
layerId: selectedLayer.getId(),
|
||||||
|
|
|
@ -13,8 +13,6 @@ import { ValidatedRange } from '../../../../../../../plugins/maps/public/compone
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public';
|
import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||||
import { MAX_ZOOM, MIN_ZOOM } from '../../../../common/constants';
|
|
||||||
|
|
||||||
export function LayerSettings(props) {
|
export function LayerSettings(props) {
|
||||||
const onLabelChange = event => {
|
const onLabelChange = event => {
|
||||||
const label = event.target.value;
|
const label = event.target.value;
|
||||||
|
@ -22,8 +20,8 @@ export function LayerSettings(props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onZoomChange = ([min, max]) => {
|
const onZoomChange = ([min, max]) => {
|
||||||
props.updateMinZoom(props.layerId, Math.max(MIN_ZOOM, parseInt(min, 10)));
|
props.updateMinZoom(props.layerId, Math.max(props.minVisibilityZoom, parseInt(min, 10)));
|
||||||
props.updateMaxZoom(props.layerId, Math.min(MAX_ZOOM, parseInt(max, 10)));
|
props.updateMaxZoom(props.layerId, Math.min(props.maxVisibilityZoom, parseInt(max, 10)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAlphaChange = alpha => {
|
const onAlphaChange = alpha => {
|
||||||
|
@ -38,8 +36,8 @@ export function LayerSettings(props) {
|
||||||
defaultMessage: 'Visibility',
|
defaultMessage: 'Visibility',
|
||||||
})}
|
})}
|
||||||
formRowDisplay="columnCompressed"
|
formRowDisplay="columnCompressed"
|
||||||
min={MIN_ZOOM}
|
min={props.minVisibilityZoom}
|
||||||
max={MAX_ZOOM}
|
max={props.maxVisibilityZoom}
|
||||||
value={[props.minZoom, props.maxZoom]}
|
value={[props.minZoom, props.maxZoom]}
|
||||||
showInput="inputWithPopover"
|
showInput="inputWithPopover"
|
||||||
showRange
|
showRange
|
||||||
|
|
|
@ -14,7 +14,12 @@ import {
|
||||||
} from './utils';
|
} from './utils';
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
import { getGlyphUrl, isRetina } from '../../../../../../../plugins/maps/public/meta';
|
import { getGlyphUrl, isRetina } from '../../../../../../../plugins/maps/public/meta';
|
||||||
import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants';
|
import {
|
||||||
|
DECIMAL_DEGREES_PRECISION,
|
||||||
|
MAX_ZOOM,
|
||||||
|
MIN_ZOOM,
|
||||||
|
ZOOM_PRECISION,
|
||||||
|
} from '../../../../common/constants';
|
||||||
import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp';
|
import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp';
|
||||||
import mbWorkerUrl from '!!file-loader!mapbox-gl/dist/mapbox-gl-csp-worker';
|
import mbWorkerUrl from '!!file-loader!mapbox-gl/dist/mapbox-gl-csp-worker';
|
||||||
import mbRtlPlugin from '!!file-loader!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js';
|
import mbRtlPlugin from '!!file-loader!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js';
|
||||||
|
@ -132,6 +137,8 @@ export class MBMapContainer extends React.Component {
|
||||||
scrollZoom: this.props.scrollZoom,
|
scrollZoom: this.props.scrollZoom,
|
||||||
preserveDrawingBuffer: getInjectedVarFunc()('preserveDrawingBuffer', false),
|
preserveDrawingBuffer: getInjectedVarFunc()('preserveDrawingBuffer', false),
|
||||||
interactive: !this.props.disableInteractive,
|
interactive: !this.props.disableInteractive,
|
||||||
|
minZoom: MIN_ZOOM,
|
||||||
|
maxZoom: MAX_ZOOM,
|
||||||
};
|
};
|
||||||
const initialView = _.get(this.props.goto, 'center');
|
const initialView = _.get(this.props.goto, 'center');
|
||||||
if (initialView) {
|
if (initialView) {
|
||||||
|
|
|
@ -4,13 +4,6 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
|
||||||
import '../../../../plugins/maps/public/layers/layer_wizard_registry';
|
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
|
||||||
import '../../../../plugins/maps/public/layers/sources/source_registry';
|
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
|
||||||
import '../../../../plugins/maps/public/layers/load_layer_wizards';
|
|
||||||
|
|
||||||
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
|
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
|
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
|
||||||
|
|
|
@ -19,6 +19,8 @@ import { BlendedVectorLayer } from '../../../../../plugins/maps/public/layers/bl
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
import { getTimeFilter } from '../../../../../plugins/maps/public/kibana_services';
|
import { getTimeFilter } from '../../../../../plugins/maps/public/kibana_services';
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
|
import { TiledVectorLayer } from '../../../../../plugins/maps/public/layers/tiled_vector_layer';
|
||||||
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
|
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
|
||||||
import {
|
import {
|
||||||
copyPersistentState,
|
copyPersistentState,
|
||||||
|
@ -51,6 +53,8 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) {
|
||||||
return new HeatmapLayer({ layerDescriptor, source });
|
return new HeatmapLayer({ layerDescriptor, source });
|
||||||
case BlendedVectorLayer.type:
|
case BlendedVectorLayer.type:
|
||||||
return new BlendedVectorLayer({ layerDescriptor, source });
|
return new BlendedVectorLayer({ layerDescriptor, source });
|
||||||
|
case TiledVectorLayer.type:
|
||||||
|
return new TiledVectorLayer({ layerDescriptor, source });
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognized layerType ${layerDescriptor.type}`);
|
throw new Error(`Unrecognized layerType ${layerDescriptor.type}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
jest.mock('../../../../../plugins/maps/public/layers/vector_layer', () => {});
|
jest.mock('../../../../../plugins/maps/public/layers/vector_layer', () => {});
|
||||||
|
jest.mock('../../../../../plugins/maps/public/layers/tiled_vector_layer', () => {});
|
||||||
jest.mock('../../../../../plugins/maps/public/layers/blended_vector_layer', () => {});
|
jest.mock('../../../../../plugins/maps/public/layers/blended_vector_layer', () => {});
|
||||||
jest.mock('../../../../../plugins/maps/public/layers/heatmap_layer', () => {});
|
jest.mock('../../../../../plugins/maps/public/layers/heatmap_layer', () => {});
|
||||||
jest.mock('../../../../../plugins/maps/public/layers/vector_tile_layer', () => {});
|
jest.mock('../../../../../plugins/maps/public/layers/vector_tile_layer', () => {});
|
||||||
|
|
|
@ -46,9 +46,10 @@ export function createMapPath(id: string) {
|
||||||
export enum LAYER_TYPE {
|
export enum LAYER_TYPE {
|
||||||
TILE = 'TILE',
|
TILE = 'TILE',
|
||||||
VECTOR = 'VECTOR',
|
VECTOR = 'VECTOR',
|
||||||
VECTOR_TILE = 'VECTOR_TILE',
|
VECTOR_TILE = 'VECTOR_TILE', // for static display of mvt vector tiles with a mapbox stylesheet. Does not support any ad-hoc configurations. Used for consuming EMS vector tiles.
|
||||||
HEATMAP = 'HEATMAP',
|
HEATMAP = 'HEATMAP',
|
||||||
BLENDED_VECTOR = 'BLENDED_VECTOR',
|
BLENDED_VECTOR = 'BLENDED_VECTOR',
|
||||||
|
TILED_VECTOR = 'TILED_VECTOR', // similar to a regular vector-layer, but it consumes the data as .mvt tilea iso GeoJson. It supports similar ad-hoc configurations like a regular vector layer (E.g. using IVectorStyle), although there is some loss of functionality e.g. does not support term joining
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SORT_ORDER {
|
export enum SORT_ORDER {
|
||||||
|
@ -67,6 +68,7 @@ export enum SOURCE_TYPES {
|
||||||
KIBANA_TILEMAP = 'KIBANA_TILEMAP',
|
KIBANA_TILEMAP = 'KIBANA_TILEMAP',
|
||||||
REGIONMAP_FILE = 'REGIONMAP_FILE',
|
REGIONMAP_FILE = 'REGIONMAP_FILE',
|
||||||
GEOJSON_FILE = 'GEOJSON_FILE',
|
GEOJSON_FILE = 'GEOJSON_FILE',
|
||||||
|
MVT_SINGLE_LAYER = 'MVT_SINGLE_LAYER',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FIELD_ORIGIN {
|
export enum FIELD_ORIGIN {
|
||||||
|
|
|
@ -31,12 +31,12 @@ type ESGeoGridSourceSyncMeta = {
|
||||||
requestType: RENDER_AS;
|
requestType: RENDER_AS;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VectorSourceSyncMeta = ESSearchSourceSyncMeta | ESGeoGridSourceSyncMeta;
|
export type VectorSourceSyncMeta = ESSearchSourceSyncMeta | ESGeoGridSourceSyncMeta | null;
|
||||||
|
|
||||||
export type VectorSourceRequestMeta = MapFilters & {
|
export type VectorSourceRequestMeta = MapFilters & {
|
||||||
applyGlobalQuery: boolean;
|
applyGlobalQuery: boolean;
|
||||||
fieldNames: string[];
|
fieldNames: string[];
|
||||||
geogridPrecision: number;
|
geogridPrecision?: number;
|
||||||
sourceQuery: MapQuery;
|
sourceQuery: MapQuery;
|
||||||
sourceMeta: VectorSourceSyncMeta;
|
sourceMeta: VectorSourceSyncMeta;
|
||||||
};
|
};
|
||||||
|
|
|
@ -94,6 +94,18 @@ export type XYZTMSSourceDescriptor = AbstractSourceDescriptor &
|
||||||
urlTemplate: string;
|
urlTemplate: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor & {
|
||||||
|
urlTemplate: string;
|
||||||
|
layerName: string;
|
||||||
|
|
||||||
|
// These are the min/max zoom levels of the availability of the a particular layerName in the tileset at urlTemplate.
|
||||||
|
// These are _not_ the visible zoom-range of the data on a map.
|
||||||
|
// Tiled data can be displayed at higher levels of zoom than that they are stored in the tileset.
|
||||||
|
// e.g. EMS basemap data from level 14 is at most detailed resolution and can be displayed at higher levels
|
||||||
|
minSourceZoom: number;
|
||||||
|
maxSourceZoom: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type JoinDescriptor = {
|
export type JoinDescriptor = {
|
||||||
leftField: string;
|
leftField: string;
|
||||||
right: ESTermSourceDescriptor;
|
right: ESTermSourceDescriptor;
|
||||||
|
@ -107,7 +119,9 @@ export type SourceDescriptor =
|
||||||
| ESTermSourceDescriptor
|
| ESTermSourceDescriptor
|
||||||
| ESSearchSourceDescriptor
|
| ESSearchSourceDescriptor
|
||||||
| ESGeoGridSourceDescriptor
|
| ESGeoGridSourceDescriptor
|
||||||
| EMSFileSourceDescriptor;
|
| EMSFileSourceDescriptor
|
||||||
|
| ESPewPewSourceDescriptor
|
||||||
|
| TiledSingleLayerVectorSourceDescriptor;
|
||||||
|
|
||||||
export type LayerDescriptor = {
|
export type LayerDescriptor = {
|
||||||
__dataRequests?: DataRequestDescriptor[];
|
__dataRequests?: DataRequestDescriptor[];
|
||||||
|
|
|
@ -38,7 +38,9 @@ export const getFileUploadComponent = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
let getInjectedVar;
|
let getInjectedVar;
|
||||||
export const setInjectedVarFunc = getInjectedVarFunc => (getInjectedVar = getInjectedVarFunc);
|
export const setInjectedVarFunc = getInjectedVarFunc => {
|
||||||
|
getInjectedVar = getInjectedVarFunc;
|
||||||
|
};
|
||||||
export const getInjectedVarFunc = () => getInjectedVar;
|
export const getInjectedVarFunc = () => getInjectedVar;
|
||||||
|
|
||||||
let uiSettings;
|
let uiSettings;
|
||||||
|
|
15
x-pack/plugins/maps/public/layers/layer.d.ts
vendored
15
x-pack/plugins/maps/public/layers/layer.d.ts
vendored
|
@ -3,7 +3,7 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
import { LayerDescriptor, MapExtent, MapFilters } from '../../common/descriptor_types';
|
import { LayerDescriptor, MapExtent, MapFilters, MapQuery } from '../../common/descriptor_types';
|
||||||
import { ISource } from './sources/source';
|
import { ISource } from './sources/source';
|
||||||
import { DataRequest } from './util/data_request';
|
import { DataRequest } from './util/data_request';
|
||||||
import { SyncContext } from '../actions/map_actions';
|
import { SyncContext } from '../actions/map_actions';
|
||||||
|
@ -17,6 +17,11 @@ export interface ILayer {
|
||||||
getSource(): ISource;
|
getSource(): ISource;
|
||||||
getSourceForEditing(): ISource;
|
getSourceForEditing(): ISource;
|
||||||
syncData(syncContext: SyncContext): Promise<void>;
|
syncData(syncContext: SyncContext): Promise<void>;
|
||||||
|
isVisible(): boolean;
|
||||||
|
showAtZoomLevel(zoomLevel: number): boolean;
|
||||||
|
getMinZoom(): number;
|
||||||
|
getMaxZoom(): number;
|
||||||
|
getMinSourceZoom(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILayerArguments {
|
export interface ILayerArguments {
|
||||||
|
@ -35,4 +40,12 @@ export class AbstractLayer implements ILayer {
|
||||||
getSource(): ISource;
|
getSource(): ISource;
|
||||||
getSourceForEditing(): ISource;
|
getSourceForEditing(): ISource;
|
||||||
syncData(syncContext: SyncContext): Promise<void>;
|
syncData(syncContext: SyncContext): Promise<void>;
|
||||||
|
isVisible(): boolean;
|
||||||
|
showAtZoomLevel(zoomLevel: number): boolean;
|
||||||
|
getMinZoom(): number;
|
||||||
|
getMaxZoom(): number;
|
||||||
|
getMinSourceZoom(): number;
|
||||||
|
getQuery(): MapQuery;
|
||||||
|
_removeStaleMbSourcesAndLayers(mbMap: unknown): void;
|
||||||
|
_requiresPrevSourceCleanup(mbMap: unknown): boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,8 @@ export class AbstractLayer {
|
||||||
defaultMessage: `Layer is hidden.`,
|
defaultMessage: `Layer is hidden.`,
|
||||||
});
|
});
|
||||||
} else if (!this.showAtZoomLevel(zoomLevel)) {
|
} else if (!this.showAtZoomLevel(zoomLevel)) {
|
||||||
const { minZoom, maxZoom } = this.getZoomConfig();
|
const minZoom = this.getMinZoom();
|
||||||
|
const maxZoom = this.getMaxZoom();
|
||||||
icon = <EuiIcon size="m" type="expand" />;
|
icon = <EuiIcon size="m" type="expand" />;
|
||||||
tooltipContent = i18n.translate('xpack.maps.layer.zoomFeedbackTooltip', {
|
tooltipContent = i18n.translate('xpack.maps.layer.zoomFeedbackTooltip', {
|
||||||
defaultMessage: `Layer is visible between zoom levels {minZoom} and {maxZoom}.`,
|
defaultMessage: `Layer is visible between zoom levels {minZoom} and {maxZoom}.`,
|
||||||
|
@ -203,7 +204,7 @@ export class AbstractLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
showAtZoomLevel(zoom) {
|
showAtZoomLevel(zoom) {
|
||||||
return zoom >= this._descriptor.minZoom && zoom <= this._descriptor.maxZoom;
|
return zoom >= this.getMinZoom() && zoom <= this.getMaxZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
getMinZoom() {
|
getMinZoom() {
|
||||||
|
@ -214,6 +215,30 @@ export class AbstractLayer {
|
||||||
return this._descriptor.maxZoom;
|
return this._descriptor.maxZoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMinSourceZoom() {
|
||||||
|
return this._source.getMinZoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
_requiresPrevSourceCleanup() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeStaleMbSourcesAndLayers(mbMap) {
|
||||||
|
if (this._requiresPrevSourceCleanup(mbMap)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getAlpha() {
|
getAlpha() {
|
||||||
return this._descriptor.alpha;
|
return this._descriptor.alpha;
|
||||||
}
|
}
|
||||||
|
@ -222,13 +247,6 @@ export class AbstractLayer {
|
||||||
return this._descriptor.query;
|
return this._descriptor.query;
|
||||||
}
|
}
|
||||||
|
|
||||||
getZoomConfig() {
|
|
||||||
return {
|
|
||||||
minZoom: this._descriptor.minZoom,
|
|
||||||
maxZoom: this._descriptor.maxZoom,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentStyle() {
|
getCurrentStyle() {
|
||||||
return this._style;
|
return this._style;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 { registerLayerWizard } from './layer_wizard_registry';
|
|
||||||
import { uploadLayerWizardConfig } from './sources/client_file_source';
|
|
||||||
import { esDocumentsLayerWizardConfig } from './sources/es_search_source';
|
|
||||||
import { clustersLayerWizardConfig, heatmapLayerWizardConfig } from './sources/es_geo_grid_source';
|
|
||||||
import { point2PointLayerWizardConfig } from './sources/es_pew_pew_source/es_pew_pew_source';
|
|
||||||
import { emsBoundariesLayerWizardConfig } from './sources/ems_file_source';
|
|
||||||
import { emsBaseMapLayerWizardConfig } from './sources/ems_tms_source';
|
|
||||||
import { kibanaRegionMapLayerWizardConfig } from './sources/kibana_regionmap_source';
|
|
||||||
import { kibanaBasemapLayerWizardConfig } from './sources/kibana_tilemap_source';
|
|
||||||
import { tmsLayerWizardConfig } from './sources/xyz_tms_source';
|
|
||||||
import { wmsLayerWizardConfig } from './sources/wms_source';
|
|
||||||
|
|
||||||
// Registration order determines display order
|
|
||||||
registerLayerWizard(uploadLayerWizardConfig);
|
|
||||||
registerLayerWizard(esDocumentsLayerWizardConfig);
|
|
||||||
registerLayerWizard(clustersLayerWizardConfig);
|
|
||||||
registerLayerWizard(heatmapLayerWizardConfig);
|
|
||||||
registerLayerWizard(point2PointLayerWizardConfig);
|
|
||||||
registerLayerWizard(emsBoundariesLayerWizardConfig);
|
|
||||||
registerLayerWizard(emsBaseMapLayerWizardConfig);
|
|
||||||
registerLayerWizard(kibanaRegionMapLayerWizardConfig);
|
|
||||||
registerLayerWizard(kibanaBasemapLayerWizardConfig);
|
|
||||||
registerLayerWizard(tmsLayerWizardConfig);
|
|
||||||
registerLayerWizard(wmsLayerWizardConfig);
|
|
66
x-pack/plugins/maps/public/layers/load_layer_wizards.ts
Normal file
66
x-pack/plugins/maps/public/layers/load_layer_wizards.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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 { registerLayerWizard } from './layer_wizard_registry';
|
||||||
|
// @ts-ignore
|
||||||
|
import { uploadLayerWizardConfig } from './sources/client_file_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { esDocumentsLayerWizardConfig } from './sources/es_search_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { clustersLayerWizardConfig, heatmapLayerWizardConfig } from './sources/es_geo_grid_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { point2PointLayerWizardConfig } from './sources/es_pew_pew_source/es_pew_pew_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { emsBoundariesLayerWizardConfig } from './sources/ems_file_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { emsBaseMapLayerWizardConfig } from './sources/ems_tms_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { kibanaRegionMapLayerWizardConfig } from './sources/kibana_regionmap_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { kibanaBasemapLayerWizardConfig } from './sources/kibana_tilemap_source';
|
||||||
|
import { tmsLayerWizardConfig } from './sources/xyz_tms_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { wmsLayerWizardConfig } from './sources/wms_source';
|
||||||
|
import { mvtVectorSourceWizardConfig } from './sources/mvt_single_layer_vector_source';
|
||||||
|
// @ts-ignore
|
||||||
|
import { getInjectedVarFunc } from '../kibana_services';
|
||||||
|
|
||||||
|
// Registration order determines display order
|
||||||
|
let registered = false;
|
||||||
|
export function registerLayerWizards() {
|
||||||
|
if (registered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(uploadLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(esDocumentsLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(clustersLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(heatmapLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(point2PointLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(emsBoundariesLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(emsBaseMapLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(kibanaRegionMapLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(kibanaBasemapLayerWizardConfig);
|
||||||
|
registerLayerWizard(tmsLayerWizardConfig);
|
||||||
|
// @ts-ignore
|
||||||
|
registerLayerWizard(wmsLayerWizardConfig);
|
||||||
|
|
||||||
|
const getInjectedVar = getInjectedVarFunc();
|
||||||
|
if (getInjectedVar && getInjectedVar('enableVectorTiles', false)) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Vector tiles are an experimental feature and should not be used in production.');
|
||||||
|
registerLayerWizard(mvtVectorSourceWizardConfig);
|
||||||
|
}
|
||||||
|
registered = true;
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ export class ESGeoGridSource extends AbstractESAggSource {
|
||||||
|
|
||||||
constructor(sourceDescriptor: ESGeoGridSourceDescriptor, inspectorAdapters: unknown);
|
constructor(sourceDescriptor: ESGeoGridSourceDescriptor, inspectorAdapters: unknown);
|
||||||
|
|
||||||
|
getFieldNames(): string[];
|
||||||
getGridResolution(): GRID_RESOLUTION;
|
getGridResolution(): GRID_RESOLUTION;
|
||||||
getGeoGridPrecision(zoom: number): number;
|
getGeoGridPrecision(zoom: number): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,5 @@ import { ESSearchSourceDescriptor } from '../../../../common/descriptor_types';
|
||||||
|
|
||||||
export class ESSearchSource extends AbstractESSource {
|
export class ESSearchSource extends AbstractESSource {
|
||||||
constructor(sourceDescriptor: Partial<ESSearchSourceDescriptor>, inspectorAdapters: unknown);
|
constructor(sourceDescriptor: Partial<ESSearchSourceDescriptor>, inspectorAdapters: unknown);
|
||||||
|
getFieldNames(): string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,5 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const VECTOR_SHAPE_TYPES = {
|
export * from './mvt_single_layer_vector_source';
|
||||||
POINT: 'POINT',
|
export * from './layer_wizard';
|
||||||
LINE: 'LINE',
|
|
||||||
POLYGON: 'POLYGON',
|
|
||||||
};
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 { i18n } from '@kbn/i18n';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
MVTSingleLayerVectorSourceEditor,
|
||||||
|
MVTSingleLayerVectorSourceConfig,
|
||||||
|
} from './mvt_single_layer_vector_source_editor';
|
||||||
|
import { MVTSingleLayerVectorSource, sourceTitle } from './mvt_single_layer_vector_source';
|
||||||
|
import { LayerWizard, RenderWizardArguments } from '../../layer_wizard_registry';
|
||||||
|
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||||
|
|
||||||
|
export const mvtVectorSourceWizardConfig: LayerWizard = {
|
||||||
|
description: i18n.translate('xpack.maps.source.mvtVectorSourceWizard', {
|
||||||
|
defaultMessage: 'Vector source wizard',
|
||||||
|
}),
|
||||||
|
icon: 'grid',
|
||||||
|
renderWizard: ({ onPreviewSource, inspectorAdapters }: RenderWizardArguments) => {
|
||||||
|
const onSourceConfigChange = ({
|
||||||
|
urlTemplate,
|
||||||
|
layerName,
|
||||||
|
minSourceZoom,
|
||||||
|
maxSourceZoom,
|
||||||
|
}: MVTSingleLayerVectorSourceConfig) => {
|
||||||
|
const sourceDescriptor = MVTSingleLayerVectorSource.createDescriptor({
|
||||||
|
urlTemplate,
|
||||||
|
layerName,
|
||||||
|
minSourceZoom,
|
||||||
|
maxSourceZoom,
|
||||||
|
type: SOURCE_TYPES.MVT_SINGLE_LAYER,
|
||||||
|
});
|
||||||
|
const source = new MVTSingleLayerVectorSource(sourceDescriptor, inspectorAdapters);
|
||||||
|
onPreviewSource(source);
|
||||||
|
};
|
||||||
|
return <MVTSingleLayerVectorSourceEditor onSourceConfigChange={onSourceConfigChange} />;
|
||||||
|
},
|
||||||
|
title: sourceTitle,
|
||||||
|
};
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* 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 { i18n } from '@kbn/i18n';
|
||||||
|
import uuid from 'uuid/v4';
|
||||||
|
import { AbstractSource, ImmutableSourceProperty } from '../source';
|
||||||
|
import { TiledVectorLayer } from '../../tiled_vector_layer';
|
||||||
|
import { GeoJsonWithMeta, ITiledSingleLayerVectorSource } from '../vector_source';
|
||||||
|
import { MAX_ZOOM, MIN_ZOOM, SOURCE_TYPES } from '../../../../common/constants';
|
||||||
|
import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
|
||||||
|
import { IField } from '../../fields/field';
|
||||||
|
import { registerSource } from '../source_registry';
|
||||||
|
import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters';
|
||||||
|
import {
|
||||||
|
LayerDescriptor,
|
||||||
|
MapExtent,
|
||||||
|
TiledSingleLayerVectorSourceDescriptor,
|
||||||
|
VectorSourceRequestMeta,
|
||||||
|
VectorSourceSyncMeta,
|
||||||
|
} from '../../../../common/descriptor_types';
|
||||||
|
import { VectorLayerArguments } from '../../vector_layer';
|
||||||
|
|
||||||
|
export const sourceTitle = i18n.translate(
|
||||||
|
'xpack.maps.source.MVTSingleLayerVectorSource.sourceTitle',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Vector Tile Layer',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export class MVTSingleLayerVectorSource extends AbstractSource
|
||||||
|
implements ITiledSingleLayerVectorSource {
|
||||||
|
static createDescriptor({
|
||||||
|
urlTemplate,
|
||||||
|
layerName,
|
||||||
|
minSourceZoom,
|
||||||
|
maxSourceZoom,
|
||||||
|
}: TiledSingleLayerVectorSourceDescriptor) {
|
||||||
|
return {
|
||||||
|
type: SOURCE_TYPES.MVT_SINGLE_LAYER,
|
||||||
|
id: uuid(),
|
||||||
|
urlTemplate,
|
||||||
|
layerName,
|
||||||
|
minSourceZoom: Math.max(MIN_ZOOM, minSourceZoom),
|
||||||
|
maxSourceZoom: Math.min(MAX_ZOOM, maxSourceZoom),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _descriptor: TiledSingleLayerVectorSourceDescriptor;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
sourceDescriptor: TiledSingleLayerVectorSourceDescriptor,
|
||||||
|
inspectorAdapters?: object
|
||||||
|
) {
|
||||||
|
super(sourceDescriptor, inspectorAdapters);
|
||||||
|
this._descriptor = sourceDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSourceSettingsEditor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFieldNames(): string[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
createDefaultLayer(options: LayerDescriptor): TiledVectorLayer {
|
||||||
|
const layerDescriptor = {
|
||||||
|
sourceDescriptor: this._descriptor,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
const normalizedLayerDescriptor = TiledVectorLayer.createDescriptor(layerDescriptor, []);
|
||||||
|
const vectorLayerArguments: VectorLayerArguments = {
|
||||||
|
layerDescriptor: normalizedLayerDescriptor,
|
||||||
|
source: this,
|
||||||
|
};
|
||||||
|
return new TiledVectorLayer(vectorLayerArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGeoJsonWithMeta(
|
||||||
|
layerName: 'string',
|
||||||
|
searchFilters: unknown[],
|
||||||
|
registerCancelCallback: (callback: () => void) => void
|
||||||
|
): Promise<GeoJsonWithMeta> {
|
||||||
|
// todo: remove this method
|
||||||
|
// This is a consequence of ITiledSingleLayerVectorSource extending IVectorSource.
|
||||||
|
throw new Error('Does not implement getGeoJsonWithMeta');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFields(): Promise<IField[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getImmutableProperties(): Promise<ImmutableSourceProperty[]> {
|
||||||
|
return [
|
||||||
|
{ label: getDataSourceLabel(), value: sourceTitle },
|
||||||
|
{ label: getUrlLabel(), value: this._descriptor.urlTemplate },
|
||||||
|
{
|
||||||
|
label: i18n.translate('xpack.maps.source.MVTSingleLayerVectorSource.layerNameMessage', {
|
||||||
|
defaultMessage: 'Layer name',
|
||||||
|
}),
|
||||||
|
value: this._descriptor.layerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.translate('xpack.maps.source.MVTSingleLayerVectorSource.minZoomMessage', {
|
||||||
|
defaultMessage: 'Min zoom',
|
||||||
|
}),
|
||||||
|
value: this._descriptor.minSourceZoom.toString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.translate('xpack.maps.source.MVTSingleLayerVectorSource.maxZoomMessage', {
|
||||||
|
defaultMessage: 'Max zoom',
|
||||||
|
}),
|
||||||
|
value: this._descriptor.maxSourceZoom.toString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDisplayName(): Promise<string> {
|
||||||
|
return this._descriptor.layerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUrlTemplateWithMeta() {
|
||||||
|
return {
|
||||||
|
urlTemplate: this._descriptor.urlTemplate,
|
||||||
|
layerName: this._descriptor.layerName,
|
||||||
|
minSourceZoom: this._descriptor.minSourceZoom,
|
||||||
|
maxSourceZoom: this._descriptor.maxSourceZoom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPES[]> {
|
||||||
|
return [VECTOR_SHAPE_TYPES.POINT, VECTOR_SHAPE_TYPES.LINE, VECTOR_SHAPE_TYPES.POLYGON];
|
||||||
|
}
|
||||||
|
|
||||||
|
canFormatFeatureProperties() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMinZoom() {
|
||||||
|
return this._descriptor.minSourceZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxZoom() {
|
||||||
|
return this._descriptor.maxSourceZoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoundsForFilters(searchFilters: VectorSourceRequestMeta): MapExtent {
|
||||||
|
return {
|
||||||
|
maxLat: 90,
|
||||||
|
maxLon: 180,
|
||||||
|
minLat: -90,
|
||||||
|
minLon: -180,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getFieldByName(fieldName: string): IField | null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSyncMeta(): VectorSourceSyncMeta {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplyGlobalQuery(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSource({
|
||||||
|
ConstructorFunction: MVTSingleLayerVectorSource,
|
||||||
|
type: SOURCE_TYPES.MVT_SINGLE_LAYER,
|
||||||
|
});
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
||||||
|
|
||||||
|
import React, { Fragment, Component, ChangeEvent } from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { EuiFieldText, EuiFormRow } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { MAX_ZOOM, MIN_ZOOM } from '../../../../common/constants';
|
||||||
|
import { ValidatedDualRange, Value } from '../../../../../../../src/plugins/kibana_react/public';
|
||||||
|
|
||||||
|
export type MVTSingleLayerVectorSourceConfig = {
|
||||||
|
urlTemplate: string;
|
||||||
|
layerName: string;
|
||||||
|
minSourceZoom: number;
|
||||||
|
maxSourceZoom: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
onSourceConfigChange: (sourceConfig: MVTSingleLayerVectorSourceConfig) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
urlTemplate: string;
|
||||||
|
layerName: string;
|
||||||
|
minSourceZoom: number;
|
||||||
|
maxSourceZoom: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MVTSingleLayerVectorSourceEditor extends Component<Props, State> {
|
||||||
|
state = {
|
||||||
|
urlTemplate: '',
|
||||||
|
layerName: '',
|
||||||
|
minSourceZoom: MIN_ZOOM,
|
||||||
|
maxSourceZoom: MAX_ZOOM,
|
||||||
|
};
|
||||||
|
|
||||||
|
_sourceConfigChange = _.debounce(() => {
|
||||||
|
const canPreview =
|
||||||
|
this.state.urlTemplate.indexOf('{x}') >= 0 &&
|
||||||
|
this.state.urlTemplate.indexOf('{y}') >= 0 &&
|
||||||
|
this.state.urlTemplate.indexOf('{z}') >= 0;
|
||||||
|
|
||||||
|
if (canPreview && this.state.layerName) {
|
||||||
|
this.props.onSourceConfigChange({
|
||||||
|
urlTemplate: this.state.urlTemplate,
|
||||||
|
layerName: this.state.layerName,
|
||||||
|
minSourceZoom: this.state.minSourceZoom,
|
||||||
|
maxSourceZoom: this.state.maxSourceZoom,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
_handleUrlTemplateChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const url = e.target.value;
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
urlTemplate: url,
|
||||||
|
},
|
||||||
|
() => this._sourceConfigChange()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleLayerNameInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const layerName = e.target.value;
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
layerName,
|
||||||
|
},
|
||||||
|
() => this._sourceConfigChange()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleZoomRangeChange = (e: Value) => {
|
||||||
|
const minSourceZoom = parseInt(e[0] as string, 10);
|
||||||
|
const maxSourceZoom = parseInt(e[1] as string, 10);
|
||||||
|
|
||||||
|
if (this.state.minSourceZoom !== minSourceZoom || this.state.maxSourceZoom !== maxSourceZoom) {
|
||||||
|
this.setState({ minSourceZoom, maxSourceZoom }, () => this._sourceConfigChange());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<EuiFormRow
|
||||||
|
label={i18n.translate('xpack.maps.source.MVTSingleLayerVectorSourceEditor.urlMessage', {
|
||||||
|
defaultMessage: 'Url',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<EuiFieldText value={this.state.urlTemplate} onChange={this._handleUrlTemplateChange} />
|
||||||
|
</EuiFormRow>
|
||||||
|
<EuiFormRow
|
||||||
|
label={i18n.translate(
|
||||||
|
'xpack.maps.source.MVTSingleLayerVectorSourceEditor.layerNameMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Layer name',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<EuiFieldText value={this.state.layerName} onChange={this._handleLayerNameInputChange} />
|
||||||
|
</EuiFormRow>
|
||||||
|
<ValidatedDualRange
|
||||||
|
label=""
|
||||||
|
formRowDisplay="columnCompressed"
|
||||||
|
min={MIN_ZOOM}
|
||||||
|
max={MAX_ZOOM}
|
||||||
|
value={[this.state.minSourceZoom, this.state.maxSourceZoom]}
|
||||||
|
showInput="inputWithPopover"
|
||||||
|
showRange
|
||||||
|
showLabels
|
||||||
|
onChange={this._handleZoomRangeChange}
|
||||||
|
allowEmptyRange={false}
|
||||||
|
compressed
|
||||||
|
prepend={i18n.translate(
|
||||||
|
'xpack.maps.source.MVTSingleLayerVectorSourceEditor.dataZoomRangeMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Zoom levels',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ export type Attribution = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ISource {
|
export interface ISource {
|
||||||
createDefaultLayer(): ILayer;
|
createDefaultLayer(options?: LayerDescriptor): ILayer;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
getDisplayName(): Promise<string>;
|
getDisplayName(): Promise<string>;
|
||||||
getInspectorAdapters(): object;
|
getInspectorAdapters(): object;
|
||||||
|
@ -31,6 +31,8 @@ export interface ISource {
|
||||||
isTimeAware(): Promise<boolean>;
|
isTimeAware(): Promise<boolean>;
|
||||||
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
|
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
|
||||||
getAttributions(): Promise<Attribution[]>;
|
getAttributions(): Promise<Attribution[]>;
|
||||||
|
getMinZoom(): number;
|
||||||
|
getMaxZoom(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractSource implements ISource {
|
export class AbstractSource implements ISource {
|
||||||
|
@ -49,4 +51,6 @@ export class AbstractSource implements ISource {
|
||||||
isTimeAware(): Promise<boolean>;
|
isTimeAware(): Promise<boolean>;
|
||||||
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
|
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
|
||||||
getAttributions(): Promise<Attribution[]>;
|
getAttributions(): Promise<Attribution[]>;
|
||||||
|
getMinZoom(): number;
|
||||||
|
getMaxZoom(): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
import { copyPersistentState } from '../../reducers/util';
|
import { copyPersistentState } from '../../reducers/util';
|
||||||
|
import { MIN_ZOOM, MAX_ZOOM } from '../../../common/constants';
|
||||||
|
|
||||||
export class AbstractSource {
|
export class AbstractSource {
|
||||||
static isIndexingSource = false;
|
static isIndexingSource = false;
|
||||||
|
@ -79,7 +80,7 @@ export class AbstractSource {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isQueryAware() {
|
async isTimeAware() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +108,14 @@ export class AbstractSource {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFilterByMapBounds() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isQueryAware() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
getGeoGridPrecision() {
|
getGeoGridPrecision() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -140,4 +149,12 @@ export class AbstractSource {
|
||||||
async getValueSuggestions(/* field, query */) {
|
async getValueSuggestions(/* field, query */) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMinZoom() {
|
||||||
|
return MIN_ZOOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxZoom() {
|
||||||
|
return MAX_ZOOM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { ISource } from './source';
|
||||||
type SourceRegistryEntry = {
|
type SourceRegistryEntry = {
|
||||||
ConstructorFunction: new (
|
ConstructorFunction: new (
|
||||||
sourceDescriptor: any, // this is the source-descriptor that corresponds specifically to the particular ISource instance
|
sourceDescriptor: any, // this is the source-descriptor that corresponds specifically to the particular ISource instance
|
||||||
inspectorAdapters: unknown
|
inspectorAdapters?: object
|
||||||
) => ISource;
|
) => ISource;
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum VECTOR_SHAPE_TYPES {
|
||||||
|
POINT = 'POINT',
|
||||||
|
LINE = 'LINE',
|
||||||
|
POLYGON = 'POLYGON',
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import {
|
||||||
VectorSourceRequestMeta,
|
VectorSourceRequestMeta,
|
||||||
VectorSourceSyncMeta,
|
VectorSourceSyncMeta,
|
||||||
} from '../../../../common/descriptor_types';
|
} from '../../../../common/descriptor_types';
|
||||||
|
import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
|
||||||
|
|
||||||
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
|
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
|
||||||
|
|
||||||
|
@ -31,8 +32,10 @@ export interface IVectorSource extends ISource {
|
||||||
): Promise<GeoJsonWithMeta>;
|
): Promise<GeoJsonWithMeta>;
|
||||||
|
|
||||||
getFields(): Promise<IField[]>;
|
getFields(): Promise<IField[]>;
|
||||||
getFieldByName(fieldName: string): IField;
|
getFieldByName(fieldName: string): IField | null;
|
||||||
getSyncMeta(): VectorSourceSyncMeta;
|
getSyncMeta(): VectorSourceSyncMeta;
|
||||||
|
getFieldNames(): string[];
|
||||||
|
getApplyGlobalQuery(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractVectorSource extends AbstractSource implements IVectorSource {
|
export class AbstractVectorSource extends AbstractSource implements IVectorSource {
|
||||||
|
@ -44,6 +47,21 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc
|
||||||
): Promise<GeoJsonWithMeta>;
|
): Promise<GeoJsonWithMeta>;
|
||||||
|
|
||||||
getFields(): Promise<IField[]>;
|
getFields(): Promise<IField[]>;
|
||||||
getFieldByName(fieldName: string): IField;
|
getFieldByName(fieldName: string): IField | null;
|
||||||
getSyncMeta(): VectorSourceSyncMeta;
|
getSyncMeta(): VectorSourceSyncMeta;
|
||||||
|
getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPES[]>;
|
||||||
|
canFormatFeatureProperties(): boolean;
|
||||||
|
getApplyGlobalQuery(): boolean;
|
||||||
|
getFieldNames(): string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITiledSingleLayerVectorSource extends IVectorSource {
|
||||||
|
getUrlTemplateWithMeta(): Promise<{
|
||||||
|
layerName: string;
|
||||||
|
urlTemplate: string;
|
||||||
|
minSourceZoom: number;
|
||||||
|
maxSourceZoom: number;
|
||||||
|
}>;
|
||||||
|
getMinZoom(): number;
|
||||||
|
getMaxZoom(): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@ export class AbstractVectorSource extends AbstractSource {
|
||||||
throw new Error(`Should implemement ${this.constructor.type} ${this}`);
|
throw new Error(`Should implemement ${this.constructor.type} ${this}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFieldNames() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a field. This may be an existing instance.
|
* Retrieves a field. This may be an existing instance.
|
||||||
* @param fieldName
|
* @param fieldName
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class VectorStyleEditor extends Component {
|
||||||
defaultDynamicProperties: getDefaultDynamicProperties(),
|
defaultDynamicProperties: getDefaultDynamicProperties(),
|
||||||
defaultStaticProperties: getDefaultStaticProperties(),
|
defaultStaticProperties: getDefaultStaticProperties(),
|
||||||
supportedFeatures: undefined,
|
supportedFeatures: undefined,
|
||||||
selectedFeatureType: undefined,
|
selectedFeature: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -91,18 +91,20 @@ export class VectorStyleEditor extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedFeature = VECTOR_SHAPE_TYPES.POLYGON;
|
if (!_.isEqual(supportedFeatures, this.state.supportedFeatures)) {
|
||||||
if (this.props.isPointsOnly) {
|
this.setState({ supportedFeatures });
|
||||||
selectedFeature = VECTOR_SHAPE_TYPES.POINT;
|
|
||||||
} else if (this.props.isLinesOnly) {
|
|
||||||
selectedFeature = VECTOR_SHAPE_TYPES.LINE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (this.state.selectedFeature === null) {
|
||||||
!_.isEqual(supportedFeatures, this.state.supportedFeatures) ||
|
let selectedFeature = VECTOR_SHAPE_TYPES.POLYGON;
|
||||||
selectedFeature !== this.state.selectedFeature
|
if (this.props.isPointsOnly) {
|
||||||
) {
|
selectedFeature = VECTOR_SHAPE_TYPES.POINT;
|
||||||
this.setState({ supportedFeatures, selectedFeature });
|
} else if (this.props.isLinesOnly) {
|
||||||
|
selectedFeature = VECTOR_SHAPE_TYPES.LINE;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
selectedFeature: selectedFeature,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,23 @@ import { IStyleProperty } from './properties/style_property';
|
||||||
import { IDynamicStyleProperty } from './properties/dynamic_style_property';
|
import { IDynamicStyleProperty } from './properties/dynamic_style_property';
|
||||||
import { IVectorLayer } from '../../vector_layer';
|
import { IVectorLayer } from '../../vector_layer';
|
||||||
import { IVectorSource } from '../../sources/vector_source';
|
import { IVectorSource } from '../../sources/vector_source';
|
||||||
import { VectorStyleDescriptor } from '../../../../common/descriptor_types';
|
import {
|
||||||
|
VectorStyleDescriptor,
|
||||||
|
VectorStylePropertiesDescriptor,
|
||||||
|
} from '../../../../common/descriptor_types';
|
||||||
|
|
||||||
export interface IVectorStyle {
|
export interface IVectorStyle {
|
||||||
getAllStyleProperties(): IStyleProperty[];
|
getAllStyleProperties(): IStyleProperty[];
|
||||||
getDescriptor(): VectorStyleDescriptor;
|
getDescriptor(): VectorStyleDescriptor;
|
||||||
getDynamicPropertiesArray(): IDynamicStyleProperty[];
|
getDynamicPropertiesArray(): IDynamicStyleProperty[];
|
||||||
|
getSourceFieldNames(): string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VectorStyle implements IVectorStyle {
|
export class VectorStyle implements IVectorStyle {
|
||||||
|
static createDescriptor(properties: VectorStylePropertiesDescriptor): VectorStyleDescriptor;
|
||||||
|
static createDefaultStyleProperties(mapColors: string[]): VectorStylePropertiesDescriptor;
|
||||||
constructor(descriptor: VectorStyleDescriptor, source: IVectorSource, layer: IVectorLayer);
|
constructor(descriptor: VectorStyleDescriptor, source: IVectorSource, layer: IVectorLayer);
|
||||||
|
getSourceFieldNames(): string[];
|
||||||
getAllStyleProperties(): IStyleProperty[];
|
getAllStyleProperties(): IStyleProperty[];
|
||||||
getDescriptor(): VectorStyleDescriptor;
|
getDescriptor(): VectorStyleDescriptor;
|
||||||
getDynamicPropertiesArray(): IDynamicStyleProperty[];
|
getDynamicPropertiesArray(): IDynamicStyleProperty[];
|
||||||
|
|
170
x-pack/plugins/maps/public/layers/tiled_vector_layer.tsx
Normal file
170
x-pack/plugins/maps/public/layers/tiled_vector_layer.tsx
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiIcon } from '@elastic/eui';
|
||||||
|
import { VectorStyle } from './styles/vector/vector_style';
|
||||||
|
import { SOURCE_DATA_ID_ORIGIN, LAYER_TYPE } from '../../common/constants';
|
||||||
|
import { VectorLayer, VectorLayerArguments } from './vector_layer';
|
||||||
|
import { canSkipSourceUpdate } from './util/can_skip_fetch';
|
||||||
|
import { ITiledSingleLayerVectorSource } from './sources/vector_source';
|
||||||
|
import { SyncContext } from '../actions/map_actions';
|
||||||
|
import { ISource } from './sources/source';
|
||||||
|
import { VectorLayerDescriptor, VectorSourceRequestMeta } from '../../common/descriptor_types';
|
||||||
|
import { MVTSingleLayerVectorSourceConfig } from './sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source_editor';
|
||||||
|
|
||||||
|
export class TiledVectorLayer extends VectorLayer {
|
||||||
|
static type = LAYER_TYPE.TILED_VECTOR;
|
||||||
|
|
||||||
|
static createDescriptor(
|
||||||
|
descriptor: VectorLayerDescriptor,
|
||||||
|
mapColors: string[]
|
||||||
|
): VectorLayerDescriptor {
|
||||||
|
const layerDescriptor = super.createDescriptor(descriptor, mapColors);
|
||||||
|
layerDescriptor.type = TiledVectorLayer.type;
|
||||||
|
|
||||||
|
if (!layerDescriptor.style) {
|
||||||
|
const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors);
|
||||||
|
layerDescriptor.style = VectorStyle.createDescriptor(styleProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _source: ITiledSingleLayerVectorSource; // downcast to the more specific type
|
||||||
|
|
||||||
|
constructor({ layerDescriptor, source }: VectorLayerArguments) {
|
||||||
|
super({ layerDescriptor, source });
|
||||||
|
this._source = source as ITiledSingleLayerVectorSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomIconAndTooltipContent() {
|
||||||
|
return {
|
||||||
|
icon: <EuiIcon size="m" type={this.getLayerTypeIconName()} />,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async _syncMVTUrlTemplate({ startLoading, stopLoading, onLoadError, dataFilters }: SyncContext) {
|
||||||
|
const requestToken: symbol = Symbol(`layer-${this.getId()}-${SOURCE_DATA_ID_ORIGIN}`);
|
||||||
|
const searchFilters: VectorSourceRequestMeta = this._getSearchFilters(
|
||||||
|
dataFilters,
|
||||||
|
this.getSource(),
|
||||||
|
this._style
|
||||||
|
);
|
||||||
|
const prevDataRequest = this.getSourceDataRequest();
|
||||||
|
|
||||||
|
const canSkip = await canSkipSourceUpdate({
|
||||||
|
source: this._source as ISource,
|
||||||
|
prevDataRequest,
|
||||||
|
nextMeta: searchFilters,
|
||||||
|
});
|
||||||
|
if (canSkip) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, searchFilters);
|
||||||
|
try {
|
||||||
|
const templateWithMeta = await this._source.getUrlTemplateWithMeta();
|
||||||
|
stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, templateWithMeta, {});
|
||||||
|
} catch (error) {
|
||||||
|
onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncData(syncContext: SyncContext) {
|
||||||
|
if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this._syncSourceStyleMeta(syncContext, this._source, this._style);
|
||||||
|
await this._syncSourceFormatters(syncContext, this._source, this._style);
|
||||||
|
await this._syncMVTUrlTemplate(syncContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncSourceBindingWithMb(mbMap: unknown) {
|
||||||
|
// @ts-ignore
|
||||||
|
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 sourceMeta: MVTSingleLayerVectorSourceConfig | null = sourceDataRequest.getData() as MVTSingleLayerVectorSourceConfig;
|
||||||
|
if (!sourceMeta) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceId = this.getId();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
mbMap.addSource(sourceId, {
|
||||||
|
type: 'vector',
|
||||||
|
tiles: [sourceMeta.urlTemplate],
|
||||||
|
minzoom: sourceMeta.minSourceZoom,
|
||||||
|
maxzoom: sourceMeta.maxSourceZoom,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncStylePropertiesWithMb(mbMap: unknown) {
|
||||||
|
// @ts-ignore
|
||||||
|
const mbSource = mbMap.getSource(this.getId());
|
||||||
|
if (!mbSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceDataRequest = this.getSourceDataRequest();
|
||||||
|
if (!sourceDataRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sourceMeta: MVTSingleLayerVectorSourceConfig = sourceDataRequest.getData() as MVTSingleLayerVectorSourceConfig;
|
||||||
|
|
||||||
|
this._setMbPointsProperties(mbMap, sourceMeta.layerName);
|
||||||
|
this._setMbLinePolygonProperties(mbMap, sourceMeta.layerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
_requiresPrevSourceCleanup(mbMap: unknown): boolean {
|
||||||
|
// @ts-ignore
|
||||||
|
const mbTileSource = mbMap.getSource(this.getId());
|
||||||
|
if (!mbTileSource) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const dataRequest = this.getSourceDataRequest();
|
||||||
|
if (!dataRequest) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tiledSourceMeta: MVTSingleLayerVectorSourceConfig | null = dataRequest.getData() as MVTSingleLayerVectorSourceConfig;
|
||||||
|
if (
|
||||||
|
mbTileSource.tiles[0] === tiledSourceMeta.urlTemplate &&
|
||||||
|
mbTileSource.minzoom === tiledSourceMeta.minSourceZoom &&
|
||||||
|
mbTileSource.maxzoom === tiledSourceMeta.maxSourceZoom
|
||||||
|
) {
|
||||||
|
// TileURL and zoom-range captures all the state. If this does not change, no updates are required.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncLayerWithMB(mbMap: unknown) {
|
||||||
|
this._removeStaleMbSourcesAndLayers(mbMap);
|
||||||
|
this._syncSourceBindingWithMb(mbMap);
|
||||||
|
this._syncStylePropertiesWithMb(mbMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
getJoins() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getMinZoom() {
|
||||||
|
// higher resolution vector tiles cannot be displayed at lower-res
|
||||||
|
return Math.max(this._source.getMinZoom(), super.getMinZoom());
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { AbstractLayer } from './layer';
|
||||||
import { IVectorSource } from './sources/vector_source';
|
import { IVectorSource } from './sources/vector_source';
|
||||||
import {
|
import {
|
||||||
MapFilters,
|
MapFilters,
|
||||||
|
LayerDescriptor,
|
||||||
VectorLayerDescriptor,
|
VectorLayerDescriptor,
|
||||||
VectorSourceRequestMeta,
|
VectorSourceRequestMeta,
|
||||||
} from '../../common/descriptor_types';
|
} from '../../common/descriptor_types';
|
||||||
|
@ -20,7 +21,7 @@ import { SyncContext } from '../actions/map_actions';
|
||||||
|
|
||||||
type VectorLayerArguments = {
|
type VectorLayerArguments = {
|
||||||
source: IVectorSource;
|
source: IVectorSource;
|
||||||
joins: IJoin[];
|
joins?: IJoin[];
|
||||||
layerDescriptor: VectorLayerDescriptor;
|
layerDescriptor: VectorLayerDescriptor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,11 +29,12 @@ export interface IVectorLayer extends ILayer {
|
||||||
getFields(): Promise<IField[]>;
|
getFields(): Promise<IField[]>;
|
||||||
getStyleEditorFields(): Promise<IField[]>;
|
getStyleEditorFields(): Promise<IField[]>;
|
||||||
getValidJoins(): IJoin[];
|
getValidJoins(): IJoin[];
|
||||||
|
getSource(): IVectorSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
||||||
static createDescriptor(
|
static createDescriptor(
|
||||||
options: Partial<VectorLayerDescriptor>,
|
options: Partial<LayerDescriptor>,
|
||||||
mapColors?: string[]
|
mapColors?: string[]
|
||||||
): VectorLayerDescriptor;
|
): VectorLayerDescriptor;
|
||||||
|
|
||||||
|
@ -40,14 +42,30 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
||||||
protected readonly _style: IVectorStyle;
|
protected readonly _style: IVectorStyle;
|
||||||
|
|
||||||
constructor(options: VectorLayerArguments);
|
constructor(options: VectorLayerArguments);
|
||||||
|
getLayerTypeIconName(): string;
|
||||||
getFields(): Promise<IField[]>;
|
getFields(): Promise<IField[]>;
|
||||||
getStyleEditorFields(): Promise<IField[]>;
|
getStyleEditorFields(): Promise<IField[]>;
|
||||||
getValidJoins(): IJoin[];
|
getValidJoins(): IJoin[];
|
||||||
|
_syncSourceStyleMeta(
|
||||||
|
syncContext: SyncContext,
|
||||||
|
source: IVectorSource,
|
||||||
|
style: IVectorStyle
|
||||||
|
): Promise<void>;
|
||||||
|
_syncSourceFormatters(
|
||||||
|
syncContext: SyncContext,
|
||||||
|
source: IVectorSource,
|
||||||
|
style: IVectorStyle
|
||||||
|
): Promise<void>;
|
||||||
|
syncLayerWithMB(mbMap: unknown): void;
|
||||||
_getSearchFilters(
|
_getSearchFilters(
|
||||||
dataFilters: MapFilters,
|
dataFilters: MapFilters,
|
||||||
source: IVectorSource,
|
source: IVectorSource,
|
||||||
style: IVectorStyle
|
style: IVectorStyle
|
||||||
): VectorSourceRequestMeta;
|
): VectorSourceRequestMeta;
|
||||||
_syncData(syncContext: SyncContext, source: IVectorSource, style: IVectorStyle): Promise<void>;
|
_syncData(syncContext: SyncContext, source: IVectorSource, style: IVectorStyle): Promise<void>;
|
||||||
|
ownsMbSourceId(sourceId: string): boolean;
|
||||||
|
ownsMbLayerId(sourceId: string): boolean;
|
||||||
|
_setMbPointsProperties(mbMap: unknown, mvtSourceLayer?: string): void;
|
||||||
|
_setMbLinePolygonProperties(mbMap: unknown, mvtSourceLayer?: string): void;
|
||||||
|
getSource(): IVectorSource;
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,7 +641,7 @@ export class VectorLayer extends AbstractLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setMbPointsProperties(mbMap) {
|
_setMbPointsProperties(mbMap, mvtSourceLayer) {
|
||||||
const pointLayerId = this._getMbPointLayerId();
|
const pointLayerId = this._getMbPointLayerId();
|
||||||
const symbolLayerId = this._getMbSymbolLayerId();
|
const symbolLayerId = this._getMbSymbolLayerId();
|
||||||
const pointLayer = mbMap.getLayer(pointLayerId);
|
const pointLayer = mbMap.getLayer(pointLayerId);
|
||||||
|
@ -658,7 +658,7 @@ export class VectorLayer extends AbstractLayer {
|
||||||
if (symbolLayer) {
|
if (symbolLayer) {
|
||||||
mbMap.setLayoutProperty(symbolLayerId, 'visibility', 'none');
|
mbMap.setLayoutProperty(symbolLayerId, 'visibility', 'none');
|
||||||
}
|
}
|
||||||
this._setMbCircleProperties(mbMap);
|
this._setMbCircleProperties(mbMap, mvtSourceLayer);
|
||||||
} else {
|
} else {
|
||||||
markerLayerId = symbolLayerId;
|
markerLayerId = symbolLayerId;
|
||||||
textLayerId = symbolLayerId;
|
textLayerId = symbolLayerId;
|
||||||
|
@ -666,7 +666,7 @@ export class VectorLayer extends AbstractLayer {
|
||||||
mbMap.setLayoutProperty(pointLayerId, 'visibility', 'none');
|
mbMap.setLayoutProperty(pointLayerId, 'visibility', 'none');
|
||||||
mbMap.setLayoutProperty(this._getMbTextLayerId(), 'visibility', 'none');
|
mbMap.setLayoutProperty(this._getMbTextLayerId(), 'visibility', 'none');
|
||||||
}
|
}
|
||||||
this._setMbSymbolProperties(mbMap);
|
this._setMbSymbolProperties(mbMap, mvtSourceLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncVisibilityWithMb(mbMap, markerLayerId);
|
this.syncVisibilityWithMb(mbMap, markerLayerId);
|
||||||
|
@ -677,27 +677,36 @@ export class VectorLayer extends AbstractLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setMbCircleProperties(mbMap) {
|
_setMbCircleProperties(mbMap, mvtSourceLayer) {
|
||||||
const sourceId = this.getId();
|
const sourceId = this.getId();
|
||||||
const pointLayerId = this._getMbPointLayerId();
|
const pointLayerId = this._getMbPointLayerId();
|
||||||
const pointLayer = mbMap.getLayer(pointLayerId);
|
const pointLayer = mbMap.getLayer(pointLayerId);
|
||||||
if (!pointLayer) {
|
if (!pointLayer) {
|
||||||
mbMap.addLayer({
|
const mbLayer = {
|
||||||
id: pointLayerId,
|
id: pointLayerId,
|
||||||
type: 'circle',
|
type: 'circle',
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
paint: {},
|
paint: {},
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (mvtSourceLayer) {
|
||||||
|
mbLayer['source-layer'] = mvtSourceLayer;
|
||||||
|
}
|
||||||
|
mbMap.addLayer(mbLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const textLayerId = this._getMbTextLayerId();
|
const textLayerId = this._getMbTextLayerId();
|
||||||
const textLayer = mbMap.getLayer(textLayerId);
|
const textLayer = mbMap.getLayer(textLayerId);
|
||||||
if (!textLayer) {
|
if (!textLayer) {
|
||||||
mbMap.addLayer({
|
const mbLayer = {
|
||||||
id: textLayerId,
|
id: textLayerId,
|
||||||
type: 'symbol',
|
type: 'symbol',
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
});
|
};
|
||||||
|
if (mvtSourceLayer) {
|
||||||
|
mbLayer['source-layer'] = mvtSourceLayer;
|
||||||
|
}
|
||||||
|
mbMap.addLayer(mbLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterExpr = getPointFilterExpression(this._hasJoins());
|
const filterExpr = getPointFilterExpression(this._hasJoins());
|
||||||
|
@ -719,17 +728,21 @@ export class VectorLayer extends AbstractLayer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_setMbSymbolProperties(mbMap) {
|
_setMbSymbolProperties(mbMap, mvtSourceLayer) {
|
||||||
const sourceId = this.getId();
|
const sourceId = this.getId();
|
||||||
const symbolLayerId = this._getMbSymbolLayerId();
|
const symbolLayerId = this._getMbSymbolLayerId();
|
||||||
const symbolLayer = mbMap.getLayer(symbolLayerId);
|
const symbolLayer = mbMap.getLayer(symbolLayerId);
|
||||||
|
|
||||||
if (!symbolLayer) {
|
if (!symbolLayer) {
|
||||||
mbMap.addLayer({
|
const mbLayer = {
|
||||||
id: symbolLayerId,
|
id: symbolLayerId,
|
||||||
type: 'symbol',
|
type: 'symbol',
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
});
|
};
|
||||||
|
if (mvtSourceLayer) {
|
||||||
|
mbLayer['source-layer'] = mvtSourceLayer;
|
||||||
|
}
|
||||||
|
mbMap.addLayer(mbLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterExpr = getPointFilterExpression(this._hasJoins());
|
const filterExpr = getPointFilterExpression(this._hasJoins());
|
||||||
|
@ -750,26 +763,34 @@ export class VectorLayer extends AbstractLayer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_setMbLinePolygonProperties(mbMap) {
|
_setMbLinePolygonProperties(mbMap, mvtSourceLayer) {
|
||||||
const sourceId = this.getId();
|
const sourceId = this.getId();
|
||||||
const fillLayerId = this._getMbPolygonLayerId();
|
const fillLayerId = this._getMbPolygonLayerId();
|
||||||
const lineLayerId = this._getMbLineLayerId();
|
const lineLayerId = this._getMbLineLayerId();
|
||||||
const hasJoins = this._hasJoins();
|
const hasJoins = this._hasJoins();
|
||||||
if (!mbMap.getLayer(fillLayerId)) {
|
if (!mbMap.getLayer(fillLayerId)) {
|
||||||
mbMap.addLayer({
|
const mbLayer = {
|
||||||
id: fillLayerId,
|
id: fillLayerId,
|
||||||
type: 'fill',
|
type: 'fill',
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
paint: {},
|
paint: {},
|
||||||
});
|
};
|
||||||
|
if (mvtSourceLayer) {
|
||||||
|
mbLayer['source-layer'] = mvtSourceLayer;
|
||||||
|
}
|
||||||
|
mbMap.addLayer(mbLayer);
|
||||||
}
|
}
|
||||||
if (!mbMap.getLayer(lineLayerId)) {
|
if (!mbMap.getLayer(lineLayerId)) {
|
||||||
mbMap.addLayer({
|
const mbLayer = {
|
||||||
id: lineLayerId,
|
id: lineLayerId,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
paint: {},
|
paint: {},
|
||||||
});
|
};
|
||||||
|
if (mvtSourceLayer) {
|
||||||
|
mbLayer['source-layer'] = mvtSourceLayer;
|
||||||
|
}
|
||||||
|
mbMap.addLayer(mbLayer);
|
||||||
}
|
}
|
||||||
this.getCurrentStyle().setMBPaintProperties({
|
this.getCurrentStyle().setMBPaintProperties({
|
||||||
alpha: this.getAlpha(),
|
alpha: this.getAlpha(),
|
||||||
|
|
|
@ -161,19 +161,7 @@ export class VectorTileLayer extends TileLayer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._requiresPrevSourceCleanup(mbMap)) {
|
this._removeStaleMbSourcesAndLayers(mbMap);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let initialBootstrapCompleted = false;
|
let initialBootstrapCompleted = false;
|
||||||
const sourceIds = Object.keys(vectorStyle.sources);
|
const sourceIds = Object.keys(vectorStyle.sources);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {
|
||||||
setVisualizations,
|
setVisualizations,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} from './kibana_services';
|
} from './kibana_services';
|
||||||
|
import { registerLayerWizards } from './layers/load_layer_wizards';
|
||||||
|
|
||||||
export interface MapsPluginSetupDependencies {
|
export interface MapsPluginSetupDependencies {
|
||||||
inspector: InspectorSetupContract;
|
inspector: InspectorSetupContract;
|
||||||
|
@ -72,6 +73,7 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => {
|
||||||
setUiActions(plugins.uiActions);
|
setUiActions(plugins.uiActions);
|
||||||
setNavigation(plugins.navigation);
|
setNavigation(plugins.navigation);
|
||||||
setCoreI18n(core.i18n);
|
setCoreI18n(core.i18n);
|
||||||
|
registerLayerWizards();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue