mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* [Maps] Show joined term metrics in tooltip (#31826) * fix merge
This commit is contained in:
parent
8125f044c6
commit
e12716f7d9
13 changed files with 85 additions and 33 deletions
|
@ -13,3 +13,5 @@ export const DECIMAL_DEGREES_PRECISION = 5; // meters precision
|
|||
export const ZOOM_PRECISION = 2;
|
||||
|
||||
export const DEFAULT_EMS_TILE_LAYER = 'road_map';
|
||||
|
||||
export const SOURCE_DATA_ID_ORIGIN = 'source';
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
getTransientLayerId,
|
||||
} from '../selectors/map_selectors';
|
||||
import { updateFlyout, FLYOUT_STATE } from '../store/ui';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../common/constants';
|
||||
|
||||
export const SET_SELECTED_LAYER = 'SET_SELECTED_LAYER';
|
||||
export const SET_TRANSIENT_LAYER = 'SET_TRANSIENT_LAYER';
|
||||
|
@ -32,6 +33,7 @@ export const MAP_DESTROYED = 'MAP_DESTROYED';
|
|||
export const LAYER_DATA_LOAD_STARTED = 'LAYER_DATA_LOAD_STARTED';
|
||||
export const LAYER_DATA_LOAD_ENDED = 'LAYER_DATA_LOAD_ENDED';
|
||||
export const LAYER_DATA_LOAD_ERROR = 'LAYER_DATA_LOAD_ERROR';
|
||||
export const UPDATE_SOURCE_DATA_REQUEST = 'UPDATE_SOURCE_DATA_REQUEST';
|
||||
export const SET_JOINS = 'SET_JOINS';
|
||||
export const SET_QUERY = 'SET_QUERY';
|
||||
export const TRIGGER_REFRESH_TIMER = 'TRIGGER_REFRESH_TIMER';
|
||||
|
@ -58,6 +60,9 @@ function getLayerLoadingCallbacks(dispatch, layerId) {
|
|||
type: TOUCH_LAYER,
|
||||
layerId: layerId
|
||||
});
|
||||
},
|
||||
updateSourceData: (newData) => {
|
||||
dispatch(updateSourceDataRequest(layerId, newData));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -351,6 +356,15 @@ export function startDataLoad(layerId, dataId, requestToken, meta = {}) {
|
|||
});
|
||||
}
|
||||
|
||||
export function updateSourceDataRequest(layerId, newData) {
|
||||
return ({
|
||||
type: UPDATE_SOURCE_DATA_REQUEST,
|
||||
dataId: SOURCE_DATA_ID_ORIGIN,
|
||||
layerId,
|
||||
newData
|
||||
});
|
||||
}
|
||||
|
||||
export function endDataLoad(layerId, dataId, requestToken, data, meta) {
|
||||
return ({
|
||||
type: LAYER_DATA_LOAD_ENDED,
|
||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import { AbstractLayer } from './layer';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { HeatmapStyle } from './styles/heatmap_style';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
|
||||
|
||||
const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__';//unique name to store scaled value for weighting
|
||||
|
||||
|
@ -150,7 +151,7 @@ export class HeatmapLayer extends AbstractLayer {
|
|||
async _fetchNewData({ startLoading, stopLoading, onLoadError, dataMeta }) {
|
||||
const { geogridPrecision, timeFilters, buffer, query } = dataMeta;
|
||||
const requestToken = Symbol(`layer-source-refresh: this.getId()`);
|
||||
startLoading('source', requestToken, dataMeta);
|
||||
startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataMeta);
|
||||
try {
|
||||
const layerName = await this.getDisplayName();
|
||||
const data = await this._source.getGeoJsonPoints({ layerName }, {
|
||||
|
@ -159,9 +160,9 @@ export class HeatmapLayer extends AbstractLayer {
|
|||
timeFilters,
|
||||
query,
|
||||
});
|
||||
stopLoading('source', requestToken, data);
|
||||
stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, data);
|
||||
} catch (error) {
|
||||
onLoadError('source', requestToken, error.message);
|
||||
onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,12 +58,14 @@ export class LeftInnerJoin {
|
|||
|
||||
const joinKey = feature.properties[this._descriptor.leftField];
|
||||
if (propertiesMap.has(joinKey)) {
|
||||
feature.properties = {
|
||||
...feature.properties,
|
||||
...propertiesMap.get(joinKey),
|
||||
};
|
||||
Object.assign(feature.properties, propertiesMap.get(joinKey));
|
||||
}
|
||||
});
|
||||
|
||||
//Create a new instance.
|
||||
//We use a reference check to determine whether the feature collection has changed and needs to be updated on the mapbox-gl source.
|
||||
//We need to update because mapbox creates copies of the property object, that it then dispatches on tooltip-events.
|
||||
return { ...featureCollection };
|
||||
}
|
||||
|
||||
getJoinSource() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import _ from 'lodash';
|
|||
import turf from 'turf';
|
||||
import turfBooleanContains from '@turf/boolean-contains';
|
||||
import { DataRequest } from './util/data_request';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
|
||||
|
||||
const SOURCE_UPDATE_REQUIRED = true;
|
||||
const NO_SOURCE_UPDATE_REQUIRED = false;
|
||||
|
@ -141,7 +142,7 @@ export class AbstractLayer {
|
|||
};
|
||||
|
||||
getSourceDataRequest() {
|
||||
return this._dataRequests.find(dataRequest => dataRequest.getDataId() === 'source');
|
||||
return this._dataRequests.find(dataRequest => dataRequest.getDataId() === SOURCE_DATA_ID_ORIGIN);
|
||||
}
|
||||
|
||||
isLayerLoading() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import { CreateSourceEditor } from './create_source_editor';
|
|||
import { UpdateSourceEditor } from './update_source_editor';
|
||||
import { GRID_RESOLUTION } from '../../grid_resolution';
|
||||
import { getGeohashPrecisionForZoom } from './zoom_to_precision';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../../../common/constants';
|
||||
import { filterPropertiesForTooltip } from '../../util';
|
||||
|
||||
const COUNT_PROP_LABEL = 'count';
|
||||
|
@ -257,7 +258,7 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
field: {
|
||||
label: COUNT_PROP_LABEL,
|
||||
name: COUNT_PROP_NAME,
|
||||
origin: 'source'
|
||||
origin: SOURCE_DATA_ID_ORIGIN
|
||||
},
|
||||
color: 'Blues'
|
||||
}
|
||||
|
@ -268,7 +269,7 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
field: {
|
||||
label: COUNT_PROP_LABEL,
|
||||
name: COUNT_PROP_NAME,
|
||||
origin: 'source'
|
||||
origin: SOURCE_DATA_ID_ORIGIN
|
||||
},
|
||||
minSize: 4,
|
||||
maxSize: 32,
|
||||
|
|
|
@ -8,6 +8,7 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
|
||||
import { EuiComboBox } from '@elastic/eui';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../../../../common/constants';
|
||||
|
||||
export function FieldSelect({ fields, selectedField, onChange }) {
|
||||
|
||||
|
@ -71,7 +72,7 @@ export function FieldSelect({ fields, selectedField, onChange }) {
|
|||
export const fieldShape = PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
origin: PropTypes.oneOf(['join', 'source']).isRequired
|
||||
origin: PropTypes.oneOf(['join', SOURCE_DATA_ID_ORIGIN]).isRequired
|
||||
});
|
||||
|
||||
FieldSelect.propTypes = {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { getHexColorRangeStrings } from '../../utils/color_utils';
|
|||
import { VectorStyleEditor } from './components/vector/vector_style_editor';
|
||||
import { getDefaultStaticProperties } from './vector_style_defaults';
|
||||
import { AbstractStyle } from './abstract_style';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../../common/constants';
|
||||
|
||||
export class VectorStyle extends AbstractStyle {
|
||||
|
||||
|
@ -133,7 +134,7 @@ export class VectorStyle extends AbstractStyle {
|
|||
}
|
||||
|
||||
const field = _.get(properties[propertyName], 'options.field', {});
|
||||
if (field.origin === 'source' && field.name) {
|
||||
if (field.origin === SOURCE_DATA_ID_ORIGIN && field.name) {
|
||||
fieldNames.push(field.name);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import _ from 'lodash';
|
|||
import React from 'react';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { TileStyle } from '../layers/styles/tile_style';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
|
||||
|
||||
export class TileLayer extends AbstractLayer {
|
||||
|
||||
|
@ -38,14 +39,13 @@ export class TileLayer extends AbstractLayer {
|
|||
if (sourceDataRequest) {//data is immmutable
|
||||
return;
|
||||
}
|
||||
const sourceDataId = 'source';
|
||||
const requestToken = Symbol(`layer-source-refresh:${ this.getId()} - source`);
|
||||
startLoading(sourceDataId, requestToken, dataFilters);
|
||||
startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataFilters);
|
||||
try {
|
||||
const url = await this._source.getUrlTemplate();
|
||||
stopLoading(sourceDataId, requestToken, url, {});
|
||||
stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, url, {});
|
||||
} catch(error) {
|
||||
onLoadError(sourceDataId, requestToken, error.message);
|
||||
onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,13 +7,19 @@
|
|||
export function filterPropertiesForTooltip(metricFields, properties) {
|
||||
const tooltipProps = {};
|
||||
metricFields.forEach((field) => {
|
||||
let value;
|
||||
for (const key in properties) {
|
||||
if (properties.hasOwnProperty(key)) {
|
||||
if (field.propertyKey === key) {
|
||||
tooltipProps[field.propertyLabel] = properties[key];
|
||||
value = properties[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!value) {
|
||||
value = '-';
|
||||
}
|
||||
tooltipProps[field.propertyLabel] = value;
|
||||
});
|
||||
return tooltipProps;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ export class DataRequest {
|
|||
constructor(descriptor) {
|
||||
this._descriptor = descriptor;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this._descriptor.data;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import ReactDOM from 'react-dom';
|
|||
import { AbstractLayer } from './layer';
|
||||
import { VectorStyle } from './styles/vector_style';
|
||||
import { LeftInnerJoin } from './joins/left_inner_join';
|
||||
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
|
||||
import { FeatureTooltip } from '../../components/map/feature_tooltip';
|
||||
import { getStore } from '../../store/store';
|
||||
import { getMapColors } from '../../selectors/map_selectors';
|
||||
|
@ -128,7 +128,7 @@ export class VectorLayer extends AbstractLayer {
|
|||
return {
|
||||
label,
|
||||
name,
|
||||
origin: 'source'
|
||||
origin: SOURCE_DATA_ID_ORIGIN
|
||||
};
|
||||
});
|
||||
const joinFields = [];
|
||||
|
@ -285,11 +285,10 @@ export class VectorLayer extends AbstractLayer {
|
|||
|
||||
async _syncSource({ startLoading, stopLoading, onLoadError, dataFilters }) {
|
||||
|
||||
const sourceDataId = 'source';
|
||||
const requestToken = Symbol(`layer-source-refresh:${ this.getId()} - source`);
|
||||
|
||||
const searchFilters = this._getSearchFilters(dataFilters);
|
||||
const canSkip = await this._canSkipSourceUpdate(this._source, sourceDataId, searchFilters);
|
||||
const canSkip = await this._canSkipSourceUpdate(this._source, SOURCE_DATA_ID_ORIGIN, searchFilters);
|
||||
if (canSkip) {
|
||||
const sourceDataRequest = this.getSourceDataRequest();
|
||||
return {
|
||||
|
@ -299,25 +298,25 @@ export class VectorLayer extends AbstractLayer {
|
|||
}
|
||||
|
||||
try {
|
||||
startLoading(sourceDataId, requestToken, searchFilters);
|
||||
startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, searchFilters);
|
||||
const layerName = await this.getDisplayName();
|
||||
const { data, meta } = await this._source.getGeoJsonWithMeta({
|
||||
layerName,
|
||||
}, searchFilters);
|
||||
stopLoading(sourceDataId, requestToken, data, meta);
|
||||
stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, data, meta);
|
||||
return {
|
||||
refreshed: true,
|
||||
featureCollection: data
|
||||
};
|
||||
} catch (error) {
|
||||
onLoadError(sourceDataId, requestToken, error.message);
|
||||
onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message);
|
||||
return {
|
||||
refreshed: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_joinToFeatureCollection(sourceResult, joinState) {
|
||||
_joinToFeatureCollection(sourceResult, joinState, updateSourceData) {
|
||||
if (!sourceResult.refreshed && !joinState.shouldJoin) {
|
||||
return false;
|
||||
}
|
||||
|
@ -325,27 +324,30 @@ export class VectorLayer extends AbstractLayer {
|
|||
return false;
|
||||
}
|
||||
|
||||
joinState.join.joinPropertiesToFeatureCollection(
|
||||
const updatedFeatureCollection = joinState.join.joinPropertiesToFeatureCollection(
|
||||
sourceResult.featureCollection,
|
||||
joinState.propertiesMap);
|
||||
|
||||
updateSourceData(updatedFeatureCollection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async _performJoins(sourceResult, joinStates) {
|
||||
async _performJoins(sourceResult, joinStates, updateSourceData) {
|
||||
const hasJoined = joinStates.map(joinState => {
|
||||
return this._joinToFeatureCollection(sourceResult, joinState);
|
||||
return this._joinToFeatureCollection(sourceResult, joinState, updateSourceData);
|
||||
});
|
||||
|
||||
return hasJoined.some(shouldRefresh => shouldRefresh === true);
|
||||
}
|
||||
|
||||
async syncData({ startLoading, stopLoading, onLoadError, onRefreshStyle, dataFilters }) {
|
||||
async syncData({ startLoading, stopLoading, onLoadError, onRefreshStyle, dataFilters, updateSourceData }) {
|
||||
if (!this.isVisible() || !this.showAtZoomLevel(dataFilters.zoom)) {
|
||||
return;
|
||||
}
|
||||
const sourceResult = await this._syncSource({ startLoading, stopLoading, onLoadError, dataFilters });
|
||||
const joinResults = await this._syncJoins({ startLoading, stopLoading, onLoadError, dataFilters });
|
||||
const shouldRefresh = await this._performJoins(sourceResult, joinResults);
|
||||
const shouldRefresh = await this._performJoins(sourceResult, joinResults, updateSourceData);
|
||||
if (shouldRefresh) {
|
||||
onRefreshStyle();
|
||||
}
|
||||
|
|
|
@ -34,10 +34,12 @@ import {
|
|||
CLEAR_GOTO,
|
||||
TRACK_CURRENT_LAYER_STATE,
|
||||
ROLLBACK_TO_TRACKED_LAYER_STATE,
|
||||
REMOVE_TRACKED_LAYER_STATE
|
||||
} from "../actions/store_actions";
|
||||
REMOVE_TRACKED_LAYER_STATE,
|
||||
UPDATE_SOURCE_DATA_REQUEST
|
||||
} from '../actions/store_actions';
|
||||
|
||||
import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from './util';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../common/constants';
|
||||
|
||||
const getLayerIndex = (list, layerId) => list.findIndex(({ id }) => layerId === id);
|
||||
|
||||
|
@ -162,6 +164,8 @@ export function map(state = INITIAL_STATE, action) {
|
|||
...layerList.slice(layerIdx + 1)
|
||||
]
|
||||
};
|
||||
case UPDATE_SOURCE_DATA_REQUEST:
|
||||
return updateSourceDataRequest(state, action);
|
||||
case LAYER_DATA_LOAD_STARTED:
|
||||
return updateWithDataRequest(state, action);
|
||||
case LAYER_DATA_LOAD_ERROR:
|
||||
|
@ -310,6 +314,24 @@ function updateWithDataRequest(state, action) {
|
|||
return { ...state, layerList };
|
||||
}
|
||||
|
||||
|
||||
function updateSourceDataRequest(state, action) {
|
||||
const layerDescriptor = findLayerById(state, action.layerId);
|
||||
if (!layerDescriptor) {
|
||||
return state;
|
||||
}
|
||||
const dataRequest = layerDescriptor.__dataRequests.find(dataRequest => {
|
||||
return dataRequest.dataId === SOURCE_DATA_ID_ORIGIN;
|
||||
});
|
||||
if (!dataRequest) {
|
||||
return state;
|
||||
}
|
||||
|
||||
dataRequest.data = action.newData;
|
||||
return resetDataRequest(state, action, dataRequest);
|
||||
}
|
||||
|
||||
|
||||
function updateWithDataResponse(state, action) {
|
||||
const dataRequest = getValidDataRequest(state, action);
|
||||
if (!dataRequest) { return state; }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue