[Maps] Move field-meta implementation to style property (#52828)
* improve clarity * rename for clarity * feedback * add comment * remove debug statements
This commit is contained in:
parent
4765ed0fa1
commit
fe44595523
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <VectorStyleLegend loadRows={loadRows} />;
|
||||
}
|
||||
|
||||
_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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue