[6.7] [Maps] Show joined term metrics in tooltip (#31826) (#32001)

* [Maps] Show joined term metrics in tooltip (#31826)

* fix merge
This commit is contained in:
Thomas Neirynck 2019-02-26 08:54:25 -05:00 committed by GitHub
parent 8125f044c6
commit e12716f7d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 85 additions and 33 deletions

View file

@ -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';

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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() {

View file

@ -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() {

View file

@ -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,

View file

@ -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 = {

View file

@ -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);
}
});

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -8,7 +8,6 @@ export class DataRequest {
constructor(descriptor) {
this._descriptor = descriptor;
}
getData() {
return this._descriptor.data;
}

View file

@ -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();
}

View file

@ -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; }