mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[maps] convert TileStatusTracker to redux connected react component (#135943)
* [maps] convert TileStatusTracker to redux connected react component * eslint * Update x-pack/plugins/maps/public/connected_components/mb_map/tile_status_tracker/tile_status_tracker.tsx Co-authored-by: Nick Peihl <nickpeihl@gmail.com> Co-authored-by: Nick Peihl <nickpeihl@gmail.com>
This commit is contained in:
parent
3891aeb95f
commit
a349512b1e
6 changed files with 191 additions and 169 deletions
|
@ -15,11 +15,8 @@ import {
|
|||
mapDestroyed,
|
||||
mapExtentChanged,
|
||||
mapReady,
|
||||
setAreTilesLoaded,
|
||||
setLayerDataLoadErrorStatus,
|
||||
setMapInitError,
|
||||
setMouseCoordinates,
|
||||
updateMetaFromTiles,
|
||||
} from '../../actions';
|
||||
import {
|
||||
getCustomIcons,
|
||||
|
@ -34,7 +31,6 @@ import { getDrawMode, getIsFullScreen } from '../../selectors/ui_selectors';
|
|||
import { getInspectorAdapters, getOnMapMove } from '../../reducers/non_serializable_instances';
|
||||
import { MapStoreState } from '../../reducers/store';
|
||||
import { DRAW_MODE } from '../../../common/constants';
|
||||
import { TileMetaFeature } from '../../../common/descriptor_types';
|
||||
import type { MapExtentState } from '../../reducers/map/types';
|
||||
|
||||
function mapStateToProps(state: MapStoreState) {
|
||||
|
@ -80,18 +76,6 @@ function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyActi
|
|||
setMapInitError(errorMessage: string) {
|
||||
dispatch(setMapInitError(errorMessage));
|
||||
},
|
||||
setAreTilesLoaded(layerId: string, areTilesLoaded: boolean) {
|
||||
dispatch(setAreTilesLoaded(layerId, areTilesLoaded));
|
||||
},
|
||||
updateMetaFromTiles(layerId: string, features: TileMetaFeature[]) {
|
||||
dispatch(updateMetaFromTiles(layerId, features));
|
||||
},
|
||||
clearTileLoadError(layerId: string) {
|
||||
dispatch(setLayerDataLoadErrorStatus(layerId, null));
|
||||
},
|
||||
setTileLoadError(layerId: string, errorMessage: string) {
|
||||
dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,15 +20,8 @@ import { clampToLatBounds, clampToLonBounds } from '../../../common/elasticsearc
|
|||
import { getInitialView } from './get_initial_view';
|
||||
import { getPreserveDrawingBuffer, isScreenshotMode } from '../../kibana_services';
|
||||
import { ILayer } from '../../classes/layers/layer';
|
||||
import { IVectorSource } from '../../classes/sources/vector_source';
|
||||
import { MapSettings } from '../../reducers/map';
|
||||
import {
|
||||
CustomIcon,
|
||||
Goto,
|
||||
MapCenterAndZoom,
|
||||
TileMetaFeature,
|
||||
Timeslice,
|
||||
} from '../../../common/descriptor_types';
|
||||
import { CustomIcon, Goto, MapCenterAndZoom, Timeslice } from '../../../common/descriptor_types';
|
||||
import {
|
||||
CUSTOM_ICON_SIZE,
|
||||
DECIMAL_DEGREES_PRECISION,
|
||||
|
@ -39,7 +32,7 @@ import {
|
|||
import { getGlyphUrl } from '../../util';
|
||||
import { syncLayerOrder } from './sort_layers';
|
||||
|
||||
import { getTileMetaFeatures, removeOrphanedSourcesAndLayers } from './utils';
|
||||
import { removeOrphanedSourcesAndLayers } from './utils';
|
||||
import { RenderToolTipContent } from '../../classes/tooltips/tooltip_property';
|
||||
import { TileStatusTracker } from './tile_status_tracker';
|
||||
import { DrawFeatureControl } from './draw_control/draw_feature_control';
|
||||
|
@ -70,13 +63,9 @@ export interface Props {
|
|||
getActionContext?: () => ActionExecutionContext;
|
||||
onSingleValueTrigger?: (actionId: string, key: string, value: RawValue) => void;
|
||||
renderTooltipContent?: RenderToolTipContent;
|
||||
setAreTilesLoaded: (layerId: string, areTilesLoaded: boolean) => void;
|
||||
timeslice?: Timeslice;
|
||||
updateMetaFromTiles: (layerId: string, features: TileMetaFeature[]) => void;
|
||||
featureModeActive: boolean;
|
||||
filterModeActive: boolean;
|
||||
setTileLoadError(layerId: string, errorMessage: string): void;
|
||||
clearTileLoadError(layerId: string): void;
|
||||
onMapMove?: (lat: number, lon: number, zoom: number) => void;
|
||||
}
|
||||
|
||||
|
@ -93,7 +82,6 @@ export class MbMap extends Component<Props, State> {
|
|||
private _prevLayerList?: ILayer[];
|
||||
private _prevTimeslice?: Timeslice;
|
||||
private _navigationControl = new maplibregl.NavigationControl({ showCompass: false });
|
||||
private _tileStatusTracker?: TileStatusTracker;
|
||||
|
||||
state: State = {
|
||||
mbMap: undefined,
|
||||
|
@ -114,9 +102,6 @@ export class MbMap extends Component<Props, State> {
|
|||
if (this._checker) {
|
||||
this._checker.destroy();
|
||||
}
|
||||
if (this._tileStatusTracker) {
|
||||
this._tileStatusTracker.destroy();
|
||||
}
|
||||
if (this.state.mbMap) {
|
||||
this.state.mbMap.remove();
|
||||
this.state.mbMap = undefined;
|
||||
|
@ -124,21 +109,6 @@ export class MbMap extends Component<Props, State> {
|
|||
this.props.onMapDestroyed();
|
||||
}
|
||||
|
||||
// This keeps track of the latest update calls, per layerId
|
||||
_queryForMeta = (layer: ILayer) => {
|
||||
const source = layer.getSource();
|
||||
if (
|
||||
this.state.mbMap &&
|
||||
layer.isVisible() &&
|
||||
source.isESSource() &&
|
||||
typeof (source as IVectorSource).isMvt === 'function' &&
|
||||
(source as IVectorSource).isMvt()
|
||||
) {
|
||||
const features = getTileMetaFeatures(this.state.mbMap, layer.getMbSourceId());
|
||||
this.props.updateMetaFromTiles(layer.getId(), features);
|
||||
}
|
||||
};
|
||||
|
||||
_debouncedSync = _.debounce(() => {
|
||||
if (this._isMounted && this.props.isMapReady && this.state.mbMap) {
|
||||
const hasLayerListChanged = this._prevLayerList !== this.props.layerList; // Comparing re-select memoized instance so no deep equals needed
|
||||
|
@ -203,22 +173,6 @@ export class MbMap extends Component<Props, State> {
|
|||
mbMap.dragRotate.disable();
|
||||
mbMap.touchZoomRotate.disableRotation();
|
||||
|
||||
this._tileStatusTracker = new TileStatusTracker({
|
||||
mbMap,
|
||||
getCurrentLayerList: () => this.props.layerList,
|
||||
updateTileStatus: (layer: ILayer, areTilesLoaded: boolean, errorMessage?: string) => {
|
||||
this.props.setAreTilesLoaded(layer.getId(), areTilesLoaded);
|
||||
|
||||
if (errorMessage) {
|
||||
this.props.setTileLoadError(layer.getId(), errorMessage);
|
||||
} else {
|
||||
this.props.clearTileLoadError(layer.getId());
|
||||
}
|
||||
|
||||
this._queryForMeta(layer);
|
||||
},
|
||||
});
|
||||
|
||||
let emptyImage: HTMLImageElement;
|
||||
mbMap.on('styleimagemissing', (e: unknown) => {
|
||||
if (emptyImage) {
|
||||
|
@ -472,6 +426,7 @@ export class MbMap extends Component<Props, State> {
|
|||
let tooltipControl;
|
||||
let scaleControl;
|
||||
let keydownScrollZoomControl;
|
||||
let tileStatusTrackerControl;
|
||||
if (this.state.mbMap) {
|
||||
drawFilterControl =
|
||||
this.props.addFilters && this.props.filterModeActive ? (
|
||||
|
@ -496,6 +451,7 @@ export class MbMap extends Component<Props, State> {
|
|||
keydownScrollZoomControl = this.props.settings.keydownScrollZoom ? (
|
||||
<KeydownScrollZoom mbMap={this.state.mbMap} />
|
||||
) : null;
|
||||
tileStatusTrackerControl = <TileStatusTracker mbMap={this.state.mbMap} />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
|
@ -509,6 +465,7 @@ export class MbMap extends Component<Props, State> {
|
|||
{keydownScrollZoomControl}
|
||||
{scaleControl}
|
||||
{tooltipControl}
|
||||
{tileStatusTrackerControl}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 { AnyAction } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { connect } from 'react-redux';
|
||||
import { TileMetaFeature } from '../../../../common/descriptor_types';
|
||||
import {
|
||||
setAreTilesLoaded,
|
||||
setLayerDataLoadErrorStatus,
|
||||
updateMetaFromTiles,
|
||||
} from '../../../actions';
|
||||
import { getLayerList } from '../../../selectors/map_selectors';
|
||||
import { MapStoreState } from '../../../reducers/store';
|
||||
import { TileStatusTracker } from './tile_status_tracker';
|
||||
|
||||
function mapStateToProps(state: MapStoreState) {
|
||||
return {
|
||||
layerList: getLayerList(state),
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) {
|
||||
return {
|
||||
setAreTilesLoaded(layerId: string, areTilesLoaded: boolean) {
|
||||
dispatch(setAreTilesLoaded(layerId, areTilesLoaded));
|
||||
},
|
||||
updateMetaFromTiles(layerId: string, features: TileMetaFeature[]) {
|
||||
dispatch(updateMetaFromTiles(layerId, features));
|
||||
},
|
||||
clearTileLoadError(layerId: string) {
|
||||
dispatch(setLayerDataLoadErrorStatus(layerId, null));
|
||||
},
|
||||
setTileLoadError(layerId: string, errorMessage: string) {
|
||||
dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const connected = connect(mapStateToProps, mapDispatchToProps)(TileStatusTracker);
|
||||
export { connected as TileStatusTracker };
|
|
@ -6,9 +6,11 @@
|
|||
*/
|
||||
|
||||
// eslint-disable-next-line max-classes-per-file
|
||||
import { TileStatusTracker } from './tile_status_tracker';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import type { Map as MbMap } from '@kbn/mapbox-gl';
|
||||
import { ILayer } from '../../classes/layers/layer';
|
||||
import { TileStatusTracker } from './tile_status_tracker';
|
||||
import { ILayer } from '../../../classes/layers/layer';
|
||||
|
||||
class MockMbMap {
|
||||
public listeners: Array<{ type: string; callback: (e: unknown) => void }> = [];
|
||||
|
@ -49,6 +51,18 @@ class MockLayer {
|
|||
ownsMbSourceId(mbSourceId: string) {
|
||||
return this._mbSourceId === mbSourceId;
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getSource() {
|
||||
return {
|
||||
isESSource() {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createMockLayer(id: string, mbSourceId: string): ILayer {
|
||||
|
@ -83,23 +97,35 @@ async function sleep(timeout: number) {
|
|||
});
|
||||
}
|
||||
|
||||
const mockMbMap = new MockMbMap();
|
||||
const defaultProps = {
|
||||
mbMap: mockMbMap as unknown as MbMap,
|
||||
layerList: [],
|
||||
setAreTilesLoaded: () => {},
|
||||
updateMetaFromTiles: () => {},
|
||||
clearTileLoadError: () => {},
|
||||
setTileLoadError: () => {},
|
||||
};
|
||||
|
||||
describe('TileStatusTracker', () => {
|
||||
test('should add and remove tiles', async () => {
|
||||
const mockMbMap = new MockMbMap();
|
||||
test('should set tile load status', async () => {
|
||||
const layerList = [
|
||||
createMockLayer('foo', 'foosource'),
|
||||
createMockLayer('bar', 'barsource'),
|
||||
createMockLayer('foobar', 'foobarsource'),
|
||||
];
|
||||
const loadedMap: Map<string, boolean> = new Map<string, boolean>();
|
||||
new TileStatusTracker({
|
||||
mbMap: mockMbMap as unknown as MbMap,
|
||||
updateTileStatus: (layer, areTilesLoaded) => {
|
||||
loadedMap.set(layer.getId(), areTilesLoaded);
|
||||
},
|
||||
getCurrentLayerList: () => {
|
||||
return [
|
||||
createMockLayer('foo', 'foosource'),
|
||||
createMockLayer('bar', 'barsource'),
|
||||
createMockLayer('foobar', 'foobarsource'),
|
||||
];
|
||||
},
|
||||
});
|
||||
const setAreTilesLoaded = (layerId: string, areTilesLoaded: boolean) => {
|
||||
loadedMap.set(layerId, areTilesLoaded);
|
||||
};
|
||||
|
||||
const component = mount(
|
||||
<TileStatusTracker
|
||||
{...defaultProps}
|
||||
layerList={layerList}
|
||||
setAreTilesLoaded={setAreTilesLoaded}
|
||||
/>
|
||||
);
|
||||
|
||||
mockMbMap.emit('sourcedataloading', createMockMbDataEvent('foosource', 'aa11'));
|
||||
|
||||
|
@ -126,20 +152,9 @@ describe('TileStatusTracker', () => {
|
|||
expect(loadedMap.get('foo')).toBe(false); // still outstanding tile requests
|
||||
expect(loadedMap.get('bar')).toBe(true); // tiles were aborted or errored
|
||||
expect(loadedMap.has('foobar')).toBe(false); // never received tile requests, status should not have been reported for layer
|
||||
});
|
||||
|
||||
test('should cleanup listeners on destroy', async () => {
|
||||
const mockMbMap = new MockMbMap();
|
||||
const tileStatusTracker = new TileStatusTracker({
|
||||
mbMap: mockMbMap as unknown as MbMap,
|
||||
updateTileStatus: () => {},
|
||||
getCurrentLayerList: () => {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
component.unmount();
|
||||
|
||||
expect(mockMbMap.listeners.length).toBe(4);
|
||||
tileStatusTracker.destroy();
|
||||
expect(mockMbMap.listeners.length).toBe(0);
|
||||
});
|
||||
});
|
|
@ -5,12 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Map as MapboxMap, MapSourceDataEvent } from '@kbn/mapbox-gl';
|
||||
import _ from 'lodash';
|
||||
import { Component } from 'react';
|
||||
import type { Map as MbMap, MapSourceDataEvent } from '@kbn/mapbox-gl';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ILayer } from '../../classes/layers/layer';
|
||||
import { SPATIAL_FILTERS_LAYER_ID } from '../../../common/constants';
|
||||
import { getTileKey } from '../../classes/util/geo_tile_utils';
|
||||
import { TileMetaFeature } from '../../../../common/descriptor_types';
|
||||
import { SPATIAL_FILTERS_LAYER_ID } from '../../../../common/constants';
|
||||
import { ILayer } from '../../../classes/layers/layer';
|
||||
import { IVectorSource } from '../../../classes/sources/vector_source';
|
||||
import { getTileKey } from '../../../classes/util/geo_tile_utils';
|
||||
import { ES_MVT_META_LAYER_NAME } from '../../../classes/util/tile_meta_feature_utils';
|
||||
|
||||
interface MbTile {
|
||||
// references internal object from mapbox
|
||||
|
@ -28,32 +32,49 @@ interface Tile {
|
|||
mbTile: MbTile;
|
||||
}
|
||||
|
||||
export class TileStatusTracker {
|
||||
export interface Props {
|
||||
mbMap: MbMap;
|
||||
layerList: ILayer[];
|
||||
setAreTilesLoaded: (layerId: string, areTilesLoaded: boolean) => void;
|
||||
updateMetaFromTiles: (layerId: string, features: TileMetaFeature[]) => void;
|
||||
clearTileLoadError: (layerId: string) => void;
|
||||
setTileLoadError: (layerId: string, errorMessage: string) => void;
|
||||
}
|
||||
|
||||
export class TileStatusTracker extends Component<Props> {
|
||||
private _isMounted = false;
|
||||
// Tile cache tracks active tile requests
|
||||
// 'sourcedataloading' event adds tile request to cache
|
||||
// 'sourcedata' and 'error' events remove tile request from cache
|
||||
// Tile requests with 'aborted' status are removed from cache when reporting 'areTilesLoaded' status
|
||||
private _tileCache: Tile[] = [];
|
||||
|
||||
// Tile error cache tracks tile request errors per layer
|
||||
// Error cache is cleared when map center tile changes
|
||||
private _tileErrorCache: Record<string, TileError[]> = {};
|
||||
|
||||
// Layer cache tracks layers that have requested one or more tiles
|
||||
// Layer cache is used so that only a layer that has requested one or more tiles reports 'areTilesLoaded' status
|
||||
// layer cache is never cleared
|
||||
private _layerCache: Map<string, boolean> = new Map<string, boolean>();
|
||||
|
||||
private _prevCenterTileKey?: string;
|
||||
private readonly _mbMap: MapboxMap;
|
||||
private readonly _updateTileStatus: (
|
||||
layer: ILayer,
|
||||
areTilesLoaded: boolean,
|
||||
errorMessage?: string
|
||||
) => void;
|
||||
private readonly _getCurrentLayerList: () => ILayer[];
|
||||
|
||||
private readonly _onSourceDataLoading = (e: MapSourceDataEvent) => {
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this.props.mbMap.on('sourcedataloading', this._onSourceDataLoading);
|
||||
this.props.mbMap.on('error', this._onError);
|
||||
this.props.mbMap.on('sourcedata', this._onSourceData);
|
||||
this.props.mbMap.on('move', this._onMove);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
this.props.mbMap.off('error', this._onError);
|
||||
this.props.mbMap.off('sourcedata', this._onSourceData);
|
||||
this.props.mbMap.off('sourcedataloading', this._onSourceDataLoading);
|
||||
this.props.mbMap.off('move', this._onMove);
|
||||
this._tileCache.length = 0;
|
||||
}
|
||||
|
||||
_onSourceDataLoading = (e: MapSourceDataEvent) => {
|
||||
if (
|
||||
e.sourceId &&
|
||||
e.sourceId !== SPATIAL_FILTERS_LAYER_ID &&
|
||||
|
@ -61,7 +82,7 @@ export class TileStatusTracker {
|
|||
e.tile &&
|
||||
(e.source.type === 'vector' || e.source.type === 'raster')
|
||||
) {
|
||||
const targetLayer = this._getCurrentLayerList().find((layer) => {
|
||||
const targetLayer = this.props.layerList.find((layer) => {
|
||||
return layer.ownsMbSourceId(e.sourceId);
|
||||
});
|
||||
const layerId = targetLayer ? targetLayer.getId() : undefined;
|
||||
|
@ -86,14 +107,14 @@ export class TileStatusTracker {
|
|||
}
|
||||
};
|
||||
|
||||
private readonly _onError = (e: MapSourceDataEvent & { error: Error & { status: number } }) => {
|
||||
_onError = (e: MapSourceDataEvent & { error: Error & { status: number } }) => {
|
||||
if (
|
||||
e.sourceId &&
|
||||
e.sourceId !== SPATIAL_FILTERS_LAYER_ID &&
|
||||
e.tile &&
|
||||
(e.source.type === 'vector' || e.source.type === 'raster')
|
||||
) {
|
||||
const targetLayer = this._getCurrentLayerList().find((layer) => {
|
||||
const targetLayer = this.props.layerList.find((layer) => {
|
||||
return layer.ownsMbSourceId(e.sourceId);
|
||||
});
|
||||
const layerId = targetLayer ? targetLayer.getId() : undefined;
|
||||
|
@ -109,7 +130,7 @@ export class TileStatusTracker {
|
|||
}
|
||||
};
|
||||
|
||||
private readonly _onSourceData = (e: MapSourceDataEvent) => {
|
||||
_onSourceData = (e: MapSourceDataEvent) => {
|
||||
if (
|
||||
e.sourceId &&
|
||||
e.sourceId !== SPATIAL_FILTERS_LAYER_ID &&
|
||||
|
@ -125,44 +146,33 @@ export class TileStatusTracker {
|
|||
* Clear errors when center tile changes.
|
||||
* Tracking center tile provides the cleanest way to know when a new data fetching cycle is beginning
|
||||
*/
|
||||
private readonly _onMove = () => {
|
||||
const center = this._mbMap.getCenter();
|
||||
_onMove = () => {
|
||||
const center = this.props.mbMap.getCenter();
|
||||
// Maplibre rounds zoom when 'source.roundZoom' is true and floors zoom when 'source.roundZoom' is false
|
||||
// 'source.roundZoom' is true for raster and video layers
|
||||
// 'source.roundZoom' is false for vector layers
|
||||
// Always floor zoom to keep logic as simple as possible and not have to track center tile by source.
|
||||
// We are mainly concerned with showing errors from Elasticsearch vector tile requests (which are vector sources)
|
||||
const centerTileKey = getTileKey(center.lat, center.lng, Math.floor(this._mbMap.getZoom()));
|
||||
const centerTileKey = getTileKey(
|
||||
center.lat,
|
||||
center.lng,
|
||||
Math.floor(this.props.mbMap.getZoom())
|
||||
);
|
||||
if (this._prevCenterTileKey !== centerTileKey) {
|
||||
this._prevCenterTileKey = centerTileKey;
|
||||
this._tileErrorCache = {};
|
||||
}
|
||||
};
|
||||
|
||||
constructor({
|
||||
mbMap,
|
||||
updateTileStatus,
|
||||
getCurrentLayerList,
|
||||
}: {
|
||||
mbMap: MapboxMap;
|
||||
updateTileStatus: (layer: ILayer, areTilesLoaded: boolean, errorMessage?: string) => void;
|
||||
getCurrentLayerList: () => ILayer[];
|
||||
}) {
|
||||
this._updateTileStatus = updateTileStatus;
|
||||
this._getCurrentLayerList = getCurrentLayerList;
|
||||
|
||||
this._mbMap = mbMap;
|
||||
this._mbMap.on('sourcedataloading', this._onSourceDataLoading);
|
||||
this._mbMap.on('error', this._onError);
|
||||
this._mbMap.on('sourcedata', this._onSourceData);
|
||||
this._mbMap.on('move', this._onMove);
|
||||
}
|
||||
|
||||
_updateTileStatusForAllLayers = _.debounce(() => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._tileCache = this._tileCache.filter((tile) => {
|
||||
return typeof tile.mbTile.aborted === 'boolean' ? !tile.mbTile.aborted : true;
|
||||
});
|
||||
const layerList = this._getCurrentLayerList();
|
||||
const layerList = this.props.layerList;
|
||||
for (let i = 0; i < layerList.length; i++) {
|
||||
const layer: ILayer = layerList[i];
|
||||
|
||||
|
@ -191,7 +201,7 @@ export class TileStatusTracker {
|
|||
});
|
||||
})
|
||||
: [];
|
||||
this._updateTileStatus(
|
||||
this._updateTileStatusForLayer(
|
||||
layer,
|
||||
!atLeastOnePendingTile,
|
||||
tileErrorMessages.length
|
||||
|
@ -207,6 +217,47 @@ export class TileStatusTracker {
|
|||
}
|
||||
}, 100);
|
||||
|
||||
_updateTileStatusForLayer = (layer: ILayer, areTilesLoaded: boolean, errorMessage?: string) => {
|
||||
this.props.setAreTilesLoaded(layer.getId(), areTilesLoaded);
|
||||
|
||||
if (errorMessage) {
|
||||
this.props.setTileLoadError(layer.getId(), errorMessage);
|
||||
} else {
|
||||
this.props.clearTileLoadError(layer.getId());
|
||||
}
|
||||
|
||||
const source = layer.getSource();
|
||||
if (
|
||||
layer.isVisible() &&
|
||||
source.isESSource() &&
|
||||
typeof (source as IVectorSource).isMvt === 'function' &&
|
||||
(source as IVectorSource).isMvt()
|
||||
) {
|
||||
// querySourceFeatures can return duplicated features when features cross tile boundaries.
|
||||
// Tile meta will never have duplicated features since by their nature, tile meta is a feature contained within a single tile
|
||||
const mbFeatures = this.props.mbMap.querySourceFeatures(layer.getMbSourceId(), {
|
||||
sourceLayer: ES_MVT_META_LAYER_NAME,
|
||||
filter: [],
|
||||
});
|
||||
|
||||
const features = mbFeatures
|
||||
.map((mbFeature) => {
|
||||
try {
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: mbFeature?.id,
|
||||
geometry: mbFeature?.geometry, // this getter might throw with non-conforming geometries
|
||||
properties: mbFeature?.properties,
|
||||
} as TileMetaFeature;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((mbFeature: TileMetaFeature | null) => mbFeature !== null) as TileMetaFeature[];
|
||||
this.props.updateMetaFromTiles(layer.getId(), features);
|
||||
}
|
||||
};
|
||||
|
||||
_removeTileFromCache = (mbSourceId: string, mbKey: string) => {
|
||||
const trackedIndex = this._tileCache.findIndex((tile) => {
|
||||
return tile.mbKey === (mbKey as unknown as string) && tile.mbSourceId === mbSourceId;
|
||||
|
@ -218,11 +269,7 @@ export class TileStatusTracker {
|
|||
}
|
||||
};
|
||||
|
||||
destroy() {
|
||||
this._mbMap.off('error', this._onError);
|
||||
this._mbMap.off('sourcedata', this._onSourceData);
|
||||
this._mbMap.off('sourcedataloading', this._onSourceDataLoading);
|
||||
this._mbMap.off('move', this._onMove);
|
||||
this._tileCache.length = 0;
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { Map as MbMap } from '@kbn/mapbox-gl';
|
||||
import { TileMetaFeature } from '../../../common/descriptor_types';
|
||||
import { isGlDrawLayer } from './sort_layers';
|
||||
import { ILayer } from '../../classes/layers/layer';
|
||||
import { ES_MVT_META_LAYER_NAME } from '../../classes/util/tile_meta_feature_utils';
|
||||
|
||||
export function removeOrphanedSourcesAndLayers(
|
||||
mbMap: MbMap,
|
||||
|
@ -60,27 +58,3 @@ export function removeOrphanedSourcesAndLayers(
|
|||
}
|
||||
mbSourcesToRemove.forEach((mbSourceId) => mbMap.removeSource(mbSourceId));
|
||||
}
|
||||
|
||||
export function getTileMetaFeatures(mbMap: MbMap, mbSourceId: string): TileMetaFeature[] {
|
||||
// querySourceFeatures can return duplicated features when features cross tile boundaries.
|
||||
// Tile meta will never have duplicated features since by there nature, tile meta is a feature contained within a single tile
|
||||
const mbFeatures = mbMap.querySourceFeatures(mbSourceId, {
|
||||
sourceLayer: ES_MVT_META_LAYER_NAME,
|
||||
filter: [],
|
||||
});
|
||||
|
||||
return mbFeatures
|
||||
.map((mbFeature) => {
|
||||
try {
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: mbFeature?.id,
|
||||
geometry: mbFeature?.geometry, // this getter might throw with non-conforming geometries
|
||||
properties: mbFeature?.properties,
|
||||
} as TileMetaFeature;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((mbFeature) => mbFeature !== null) as TileMetaFeature[];
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue