From fe445955232662fbcf7ac747f07e9093b1ec2460 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Thu, 12 Dec 2019 18:19:06 -0500 Subject: [PATCH] [Maps] Move field-meta implementation to style property (#52828) * improve clarity * rename for clarity * feedback * add comment * remove debug statements --- .../legend/style_property_legend_row.js | 5 +- .../components/legend/vector_style_legend.js | 2 +- .../vector/components/style_option_shapes.js | 5 - .../properties/dynamic_style_property.js | 47 ++++++++++ .../layers/styles/vector/vector_style.js | 94 +++++++------------ .../maps/public/layers/vector_layer.js | 2 +- 6 files changed, 83 insertions(+), 72 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js index 6bd8b64ddf83..b8e4ea0cca4d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/style_property_legend_row.js @@ -8,7 +8,6 @@ import _ from 'lodash'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { rangeShape } from '../style_option_shapes'; import { getVectorStyleLabel } from '../get_vector_style_label'; import { StyleLegendRow } from '../../../components/style_legend_row'; @@ -25,7 +24,7 @@ export class StylePropertyLegendRow extends Component { } render() { - const { range, style } = this.props; + const { meta: range, style } = this.props; const min = this._formatValue(_.get(range, 'min', EMPTY_VALUE)); const minLabel = this.props.style.isFieldMetaEnabled() && range && range.isMinOutsideStdRange ? `< ${min}` : min; @@ -48,6 +47,6 @@ export class StylePropertyLegendRow extends Component { StylePropertyLegendRow.propTypes = { label: PropTypes.string, fieldFormatter: PropTypes.func, - range: rangeShape, + meta: PropTypes.object, style: PropTypes.object }; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js index 5f4139432a91..a1c82b29a159 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js @@ -35,7 +35,7 @@ export class VectorStyleLegend extends Component { const rowDescriptors = rows.map(row => { return { label: row.label, - range: row.range, + range: row.meta, styleOptions: row.style.getOptions(), }; }); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js index a2edc8cb4f68..d2b5178174e1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js @@ -35,8 +35,3 @@ export const dynamicSizeShape = PropTypes.shape({ maxSize: PropTypes.number.isRequired, field: fieldShape, }); - -export const rangeShape = PropTypes.shape({ - min: PropTypes.number.isRequired, - max: PropTypes.number.isRequired, -}); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index a72502f9f17f..1ce94f796573 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -60,4 +60,51 @@ export class DynamicStyleProperty extends AbstractStyleProperty { getFieldMetaOptions() { return _.get(this.getOptions(), 'fieldMetaOptions', {}); } + + + pluckStyleMetaFromFeatures(features) { + + const name = this.getField().getName(); + let min = Infinity; + let max = -Infinity; + for (let i = 0; i < features.length; i++) { + const feature = features[i]; + const newValue = parseFloat(feature.properties[name]); + if (!isNaN(newValue)) { + min = Math.min(min, newValue); + max = Math.max(max, newValue); + } + } + + return (min === Infinity || max === -Infinity) ? null : ({ + min: min, + max: max, + delta: max - min + }); + + } + + pluckStyleMetaFromFieldMetaData(fieldMetaData) { + + const realFieldName = this._field.getESDocFieldName ? this._field.getESDocFieldName() : this._field.getName(); + const stats = fieldMetaData[realFieldName]; + if (!stats) { + return null; + } + + const sigma = _.get(this.getFieldMetaOptions(), 'sigma', DEFAULT_SIGMA); + const stdLowerBounds = stats.avg - (stats.std_deviation * sigma); + const stdUpperBounds = stats.avg + (stats.std_deviation * sigma); + const min = Math.max(stats.min, stdLowerBounds); + const max = Math.min(stats.max, stdUpperBounds); + return { + min, + max, + delta: max - min, + isMinOutsideStdRange: stats.min < stdLowerBounds, + isMaxOutsideStdRange: stats.max > stdUpperBounds, + }; + + } + } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 426af96b63ba..3b7857180cca 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -180,24 +180,17 @@ export class VectorStyle extends AbstractStyle { } async pluckStyleMetaFromSourceDataRequest(sourceDataRequest) { + const features = _.get(sourceDataRequest.getData(), 'features', []); if (features.length === 0) { return {}; } - const scaledFields = this.getDynamicPropertiesArray() - .map(styleProperty => { - return { - name: styleProperty.getField().getName(), - min: Infinity, - max: -Infinity - }; - }); + const dynamicProperties = this.getDynamicPropertiesArray(); const supportedFeatures = await this._source.getSupportedShapeTypes(); const isSingleFeatureType = supportedFeatures.length === 1; - - if (scaledFields.length === 0 && isSingleFeatureType) { + if (dynamicProperties.length === 0 && isSingleFeatureType) { // no meta data to pull from source data request. return {}; } @@ -216,15 +209,6 @@ export class VectorStyle extends AbstractStyle { if (!hasPolygons && POLYGONS.includes(feature.geometry.type)) { hasPolygons = true; } - - for (let j = 0; j < scaledFields.length; j++) { - const scaledField = scaledFields[j]; - const newValue = parseFloat(feature.properties[scaledField.name]); - if (!isNaN(newValue)) { - scaledField.min = Math.min(scaledField.min, newValue); - scaledField.max = Math.max(scaledField.max, newValue); - } - } } const featuresMeta = { @@ -235,13 +219,11 @@ export class VectorStyle extends AbstractStyle { } }; - scaledFields.forEach(({ min, max, name }) => { - if (min !== Infinity && max !== -Infinity) { - featuresMeta[name] = { - min, - max, - delta: max - min, - }; + dynamicProperties.forEach(dynamicProperty => { + const styleMeta = dynamicProperty.pluckStyleMetaFromFeatures(features); + if (styleMeta) { + const name = dynamicProperty.getField().getName(); + featuresMeta[name] = styleMeta; } }); @@ -290,13 +272,15 @@ export class VectorStyle extends AbstractStyle { return this._isOnlySingleFeatureType(VECTOR_SHAPE_TYPES.POLYGON); } - _getFieldRange = (fieldName) => { - const fieldRangeFromLocalFeatures = _.get(this._descriptor, ['__styleMeta', fieldName]); + _getFieldMeta = (fieldName) => { + + const fieldMetaFromLocalFeatures = _.get(this._descriptor, ['__styleMeta', fieldName]); + const dynamicProps = this.getDynamicPropertiesArray(); const dynamicProp = dynamicProps.find(dynamicProp => { return fieldName === dynamicProp.getField().getName(); }); if (!dynamicProp || !dynamicProp.isFieldMetaEnabled()) { - return fieldRangeFromLocalFeatures; + return fieldMetaFromLocalFeatures; } let dataRequestId; @@ -313,34 +297,19 @@ export class VectorStyle extends AbstractStyle { } if (!dataRequestId) { - return fieldRangeFromLocalFeatures; + return fieldMetaFromLocalFeatures; } const styleMetaDataRequest = this._layer._findDataRequestForSource(dataRequestId); if (!styleMetaDataRequest || !styleMetaDataRequest.hasData()) { - return fieldRangeFromLocalFeatures; + return fieldMetaFromLocalFeatures; } const data = styleMetaDataRequest.getData(); - const field = dynamicProp.getField(); - const realFieldName = field.getESDocFieldName ? field.getESDocFieldName() : field.getName(); - const stats = data[realFieldName]; - if (!stats) { - return fieldRangeFromLocalFeatures; - } + const fieldMeta = dynamicProp.pluckStyleMetaFromFieldMetaData(data); + + return fieldMeta ? fieldMeta : fieldMetaFromLocalFeatures; - const sigma = _.get(dynamicProp.getFieldMetaOptions(), 'sigma', 3); - const stdLowerBounds = stats.avg - (stats.std_deviation * sigma); - const stdUpperBounds = stats.avg + (stats.std_deviation * sigma); - const min = Math.max(stats.min, stdLowerBounds); - const max = Math.min(stats.max, stdUpperBounds); - return { - min, - max, - delta: max - min, - isMinOutsideStdRange: stats.min < stdLowerBounds, - isMaxOutsideStdRange: stats.max > stdUpperBounds, - }; } _getStyleMeta = () => { @@ -392,7 +361,7 @@ export class VectorStyle extends AbstractStyle { return { label: await style.getField().getLabel(), fieldFormatter: await this._source.getFieldFormatter(style.getField().getName()), - range: this._getFieldRange(style.getField().getName()), + meta: this._getFieldMeta(style.getField().getName()), style, }; }); @@ -402,7 +371,7 @@ export class VectorStyle extends AbstractStyle { return ; } - _getStyleFields() { + _getFeatureStyleParams() { return this.getDynamicPropertiesArray() .map(styleProperty => { @@ -424,7 +393,7 @@ export class VectorStyle extends AbstractStyle { supportsFeatureState, isScaled, name: field.getName(), - range: this._getFieldRange(field.getName()), + meta: this._getFieldMeta(field.getName()), computedName: getComputedFieldName(styleProperty.getStyleName(), field.getName()), }; }); @@ -443,14 +412,14 @@ export class VectorStyle extends AbstractStyle { } } - setFeatureState(featureCollection, mbMap, sourceId) { + setFeatureStateAndStyleProps(featureCollection, mbMap, mbSourceId) { if (!featureCollection) { return; } - const styleFields = this._getStyleFields(); - if (styleFields.length === 0) { + const featureStateParams = this._getFeatureStyleParams(); + if (featureStateParams.length === 0) { return; } @@ -464,8 +433,8 @@ export class VectorStyle extends AbstractStyle { for (let i = 0; i < featureCollection.features.length; i++) { const feature = featureCollection.features[i]; - for (let j = 0; j < styleFields.length; j++) { - const { supportsFeatureState, isScaled, name, range, computedName } = styleFields[j]; + for (let j = 0; j < featureStateParams.length; j++) { + const { supportsFeatureState, isScaled, name, meta: range, computedName } = featureStateParams[j]; const value = parseFloat(feature.properties[name]); let styleValue; if (isScaled) { @@ -484,15 +453,16 @@ export class VectorStyle extends AbstractStyle { feature.properties[computedName] = styleValue; } } - tmpFeatureIdentifier.source = sourceId; + tmpFeatureIdentifier.source = mbSourceId; tmpFeatureIdentifier.id = feature.id; mbMap.setFeatureState(tmpFeatureIdentifier, tmpFeatureState); } - const hasGeoJsonProperties = styleFields.some(({ supportsFeatureState }) => { - return !supportsFeatureState; - }); - return hasGeoJsonProperties; + //returns boolean indicating if styles do not support feature-state and some values are stored in geojson properties + //this return-value is used in an optimization for style-updates with mapbox-gl. + //`true` indicates the entire data needs to reset on the source (otherwise the style-rules will not be reapplied) + //`false` indicates the data does not need to be reset on the store, because styles are re-evaluated if they use featureState + return featureStateParams.some(({ supportsFeatureState }) => !supportsFeatureState); } arePointsSymbolizedAsCircles() { diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index 60a6201ac872..f7db17d06ee2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -509,7 +509,7 @@ export class VectorLayer extends AbstractLayer { // "feature-state" data expressions are not supported with layout properties. // To work around this limitation, // scaled layout properties (like icon-size) must fall back to geojson property values :( - const hasGeoJsonProperties = this._style.setFeatureState(featureCollection, mbMap, this.getId()); + const hasGeoJsonProperties = this._style.setFeatureStateAndStyleProps(featureCollection, mbMap, this.getId()); if (featureCollection !== featureCollectionOnMap || hasGeoJsonProperties) { mbGeoJSONSource.setData(featureCollection); }