Skip to content

Commit

Permalink
[Maps] Move field-meta implementation to style property (#52828)
Browse files Browse the repository at this point in the history
* improve clarity

* rename for clarity

* feedback

* add comment

* remove debug statements
  • Loading branch information
thomasneirynck authored and nreese committed Dec 12, 2019
1 parent 4765ed0 commit fe44595
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;
Expand All @@ -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
};
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
}
Expand All @@ -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 = {
Expand All @@ -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;
}
});

Expand Down Expand Up @@ -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;
Expand All @@ -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 = () => {
Expand Down Expand Up @@ -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,
};
});
Expand All @@ -402,7 +371,7 @@ export class VectorStyle extends AbstractStyle {
return <VectorStyleLegend loadRows={loadRows} />;
}

_getStyleFields() {
_getFeatureStyleParams() {
return this.getDynamicPropertiesArray()
.map(styleProperty => {

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

Expand All @@ -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) {
Expand All @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/maps/public/layers/vector_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit fe44595

Please sign in to comment.