Skip to content

Commit

Permalink
[Maps] Add styling and tooltip support to mapbox mvt vector tile sour…
Browse files Browse the repository at this point in the history
…ces (#64488)

* tmp commit

* rename

* more boilerpalte

* more boiler

* more boilerpalte

* typing

* fix import

* boilerplate

* more boiler

* enable custom palettes

* fix label text and orientation

* fix merge errors

* remove dupe import

* stash commit

* tmp commit

* debounce settings

* return null

* slight rearrangement

* tooltip guard

* minor tweaks

* feedback

* ts fixes

* ts fixes

* more ts fixes

* ts fixes

* jest test

* fix typo

* spacing

* fix typing

* add unit test

* add more tests

* add snapshot test

* add snapshot

* add field editor snapshot test

* fix snapshot

* add snapshot

* remove unused import

* test stub for mvt layer

fix optional param

more checks

* add snapshot test

more unit tests

more unit tests

ts fixes

* add data syncing unit test

* fix autorefactor

* fix merge and replace snapshots

* field editor changes

* field editor changes

* ts fixes

* update snapshots

* fix things

* fix names

* fix tooltip

* add more error handling

* improve copy

* styling changes

* style option box a little better

* ts fixes

* fix console error

* remove mbProperties from interface

* remove unused method

* remove cruft

* rename for consistency

* remove unused param

* feedback

* feedback

* ensure properties are always present

* handle possible null values

* feedback

* typo

* update SIEM

* feedback

* remove cruft

* remove unused translations

* feedback

* improve readability

* fix brittle test

* fix snapshot after master merge

* remove unused method

* feedback

* revert some feedback

* remove micro-optimization

* initialize in constructor

* simplify wording

* add snapshot

* naming

* add clarifying comment

* remove unused import

* sanitize tooltips

* remove cruft

* feedback

* fix typo

* remove export

* Design fixes

* clean up supportsAutoDomain

* remove patch.txt

* cleanup

* clean-up

* Merge in styling changes

* Tweak message format

* fix broken import

Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: miukimiu <[email protected]>
Co-authored-by: Nathan Reese <[email protected]>
  • Loading branch information
4 people authored Jul 2, 2020
1 parent 3f80868 commit 9c76f19
Show file tree
Hide file tree
Showing 62 changed files with 2,405 additions and 378 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
import React, { Component } from 'react';
import React, { Component, ReactNode } from 'react';
import { EuiFormRow, EuiDualRange } from '@elastic/eui';
import { EuiFormRowDisplayKeys } from '@elastic/eui/src/components/form/form_row/form_row';
import { EuiDualRangeProps } from '@elastic/eui/src/components/form/range/dual_range';
Expand All @@ -32,7 +32,7 @@ export type ValueMember = EuiDualRangeProps['value'][0];
interface Props extends Omit<EuiDualRangeProps, 'value' | 'onChange' | 'min' | 'max'> {
value?: Value;
allowEmptyRange?: boolean;
label?: string;
label?: string | ReactNode;
formRowDisplay?: EuiFormRowDisplayKeys;
onChange?: (val: [string, string]) => void;
min?: number;
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ export enum SCALING_TYPES {

export const RGBA_0000 = 'rgba(0,0,0,0)';

export enum MVT_FIELD_TYPE {
STRING = 'String',
NUMBER = 'Number',
}

export const SPATIAL_FILTERS_LAYER_ID = 'SPATIAL_FILTERS_LAYER_ID';

export enum INITIAL_LOCATION {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/common/descriptor_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
*/

export * from './data_request_descriptor_types';
export * from './descriptor_types';
export * from './sources';
export * from './map_descriptor';
export * from './style_property_descriptor_types';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */

import { GeoJsonProperties } from 'geojson';
import { Query } from '../../../../../src/plugins/data/common';
import { DRAW_TYPE, ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../constants';

Expand Down Expand Up @@ -39,8 +40,9 @@ export type Goto = {
};

export type TooltipFeature = {
id: number;
id?: number | string;
layerId: string;
mbProperties: GeoJsonProperties;
};

export type TooltipState = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@

import { FeatureCollection } from 'geojson';
import { Query } from 'src/plugins/data/public';
import { AGG_TYPE, GRID_RESOLUTION, RENDER_AS, SORT_ORDER, SCALING_TYPES } from '../constants';
import {
AGG_TYPE,
GRID_RESOLUTION,
RENDER_AS,
SORT_ORDER,
SCALING_TYPES,
MVT_FIELD_TYPE,
} from '../constants';
import { StyleDescriptor, VectorStyleDescriptor } from './style_property_descriptor_types';
import { DataRequestDescriptor } from './data_request_descriptor_types';

Expand Down Expand Up @@ -96,18 +103,34 @@ export type XYZTMSSourceDescriptor = AbstractSourceDescriptor &
urlTemplate: string;
};

export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor & {
export type MVTFieldDescriptor = {
name: string;
type: MVT_FIELD_TYPE;
};

export type TiledSingleLayerVectorSourceSettings = {
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
// These are important so mapbox does not issue invalid requests based on the zoom level.

// Tiled layer data cannot be displayed at lower levels of zoom than that they are stored in the tileset.
// e.g. building footprints at level 14 cannot be displayed at level 0.
minSourceZoom: number;
// Tiled layer 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
maxSourceZoom: number;

fields: MVTFieldDescriptor[];
};

export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor &
TiledSingleLayerVectorSourceSettings & {
tooltipProperties: string[];
};

export type GeojsonFileSourceDescriptor = {
__featureCollection: FeatureCollection;
name: string;
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/es_agg_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ export class ESAggField implements IESAggField {
async getCategoricalFieldMetaRequest(size: number): Promise<unknown> {
return this._esDocField ? this._esDocField.getCategoricalFieldMetaRequest(size) : null;
}

supportsAutoDomain(): boolean {
return true;
}
}

export function esAggFieldsFactory(
Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export interface IField {
isValid(): boolean;
getOrdinalFieldMetaRequest(): Promise<unknown>;
getCategoricalFieldMetaRequest(size: number): Promise<unknown>;

// Determines whether Maps-app can automatically determine the domain of the field-values
// if this is not the case (e.g. for .mvt tiled data),
// then styling properties that require the domain to be known cannot use this property.
supportsAutoDomain(): boolean;

supportsFieldMeta(): boolean;
}

Expand Down Expand Up @@ -80,4 +86,8 @@ export class AbstractField implements IField {
async getCategoricalFieldMetaRequest(size: number): Promise<unknown> {
return null;
}

supportsAutoDomain(): boolean {
return true;
}
}
59 changes: 59 additions & 0 deletions x-pack/plugins/maps/public/classes/fields/mvt_field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { AbstractField, IField } from './field';
import { FIELD_ORIGIN, MVT_FIELD_TYPE } from '../../../common/constants';
import { ITiledSingleLayerVectorSource, IVectorSource } from '../sources/vector_source';
import { MVTFieldDescriptor } from '../../../common/descriptor_types';

export class MVTField extends AbstractField implements IField {
private readonly _source: ITiledSingleLayerVectorSource;
private readonly _type: MVT_FIELD_TYPE;
constructor({
fieldName,
type,
source,
origin,
}: {
fieldName: string;
source: ITiledSingleLayerVectorSource;
origin: FIELD_ORIGIN;
type: MVT_FIELD_TYPE;
}) {
super({ fieldName, origin });
this._source = source;
this._type = type;
}

getMVTFieldDescriptor(): MVTFieldDescriptor {
return {
type: this._type,
name: this.getName(),
};
}

getSource(): IVectorSource {
return this._source;
}

async getDataType(): Promise<string> {
if (this._type === MVT_FIELD_TYPE.STRING) {
return 'string';
} else if (this._type === MVT_FIELD_TYPE.NUMBER) {
return 'number';
} else {
throw new Error(`Unrecognized MVT field-type ${this._type}`);
}
}

async getLabel(): Promise<string> {
return this.getName();
}

supportsAutoDomain() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export class TopTermPercentageField implements IESAggField {
return 0;
}

supportsAutoDomain(): boolean {
return true;
}

supportsFieldMeta(): boolean {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 sinon from 'sinon';
import { DataRequestContext } from '../../../actions';
import { DataMeta, MapFilters } from '../../../../common/descriptor_types';

export class MockSyncContext implements DataRequestContext {
dataFilters: MapFilters;
isRequestStillActive: (dataId: string, requestToken: symbol) => boolean;
onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) => void;
registerCancelCallback: (requestToken: symbol, callback: () => void) => void;
startLoading: (dataId: string, requestToken: symbol, meta: DataMeta) => void;
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataMeta) => void;
updateSourceData: (newData: unknown) => void;

constructor({ dataFilters }: { dataFilters: Partial<MapFilters> }) {
const mapFilters: MapFilters = {
filters: [],
timeFilters: {
from: 'now',
to: '15m',
mode: 'relative',
},
zoom: 0,
...dataFilters,
};

this.dataFilters = mapFilters;
this.isRequestStillActive = sinon.spy();
this.onLoadError = sinon.spy();
this.registerCancelCallback = sinon.spy();
this.startLoading = sinon.spy();
this.stopLoading = sinon.spy();
this.updateSourceData = sinon.spy();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { getFileUploadComponent } from '../../../kibana_services';
import { GeojsonFileSource } from '../../sources/geojson_file_source';
import { VectorLayer } from '../../layers/vector_layer/vector_layer';
// @ts-ignore
// @ts-expect-error
import { createDefaultLayerDescriptor } from '../../sources/es_search_source';
import { RenderWizardArguments } from '../../layers/layer_wizard_registry';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class HeatmapLayer extends VectorLayer {
resolution: this.getSource().getGridResolution(),
});
mbMap.setPaintProperty(heatmapLayerId, 'heatmap-opacity', this.getAlpha());
mbMap.setLayerZoomRange(heatmapLayerId, this._descriptor.minZoom, this._descriptor.maxZoom);
mbMap.setLayerZoomRange(heatmapLayerId, this.getMinZoom(), this.getMaxZoom());
}

getLayerTypeIconName() {
Expand Down
17 changes: 9 additions & 8 deletions x-pack/plugins/maps/public/classes/layers/layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -325,27 +325,28 @@ export class AbstractLayer implements ILayer {
return this._source.getMinZoom();
}

_getMbSourceId() {
return this.getId();
}

_requiresPrevSourceCleanup(mbMap: unknown) {
return false;
}

_removeStaleMbSourcesAndLayers(mbMap: unknown) {
if (this._requiresPrevSourceCleanup(mbMap)) {
// @ts-ignore
// @ts-expect-error
const mbStyle = mbMap.getStyle();
// @ts-ignore
// @ts-expect-error
mbStyle.layers.forEach((mbLayer) => {
// @ts-ignore
if (this.ownsMbLayerId(mbLayer.id)) {
// @ts-ignore
// @ts-expect-error
mbMap.removeLayer(mbLayer.id);
}
});
// @ts-ignore
Object.keys(mbStyle.sources).some((mbSourceId) => {
// @ts-ignore
if (this.ownsMbSourceId(mbSourceId)) {
// @ts-ignore
// @ts-expect-error
mbMap.removeSource(mbSourceId);
}
});
Expand Down Expand Up @@ -429,7 +430,7 @@ export class AbstractLayer implements ILayer {
throw new Error('Should implement AbstractLayer#ownsMbLayerId');
}

ownsMbSourceId(sourceId: string): boolean {
ownsMbSourceId(mbSourceId: string): boolean {
throw new Error('Should implement AbstractLayer#ownsMbSourceId');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ export class TileLayer extends AbstractLayer {
return;
}

const sourceId = this.getId();
mbMap.addSource(sourceId, {
const mbSourceId = this._getMbSourceId();
mbMap.addSource(mbSourceId, {
type: 'raster',
tiles: [tmsSourceData.url],
tileSize: 256,
Expand All @@ -85,7 +85,7 @@ export class TileLayer extends AbstractLayer {
mbMap.addLayer({
id: mbLayerId,
type: 'raster',
source: sourceId,
source: mbSourceId,
minzoom: this._descriptor.minZoom,
maxzoom: this._descriptor.maxZoom,
});
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9c76f19

Please sign in to comment.