diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.js
index 3b2f887e13c87..77b57e3fe4965 100644
--- a/x-pack/legacy/plugins/maps/common/constants.js
+++ b/x-pack/legacy/plugins/maps/common/constants.js
@@ -57,6 +57,8 @@ export const FIELD_ORIGIN = {
};
export const SOURCE_DATA_ID_ORIGIN = 'source';
+export const META_ID_ORIGIN_SUFFIX = 'meta';
+export const SOURCE_META_ID_ORIGIN = `${SOURCE_DATA_ID_ORIGIN}_${META_ID_ORIGIN_SUFFIX}`;
export const GEOJSON_FILE = 'GEOJSON_FILE';
@@ -124,6 +126,11 @@ export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLab
export const COUNT_PROP_NAME = 'doc_count';
export const STYLE_TYPE = {
- 'STATIC': 'STATIC',
- 'DYNAMIC': 'DYNAMIC'
+ STATIC: 'STATIC',
+ DYNAMIC: 'DYNAMIC'
+};
+
+export const LAYER_STYLE_TYPE = {
+ VECTOR: 'VECTOR',
+ HEATMAP: 'HEATMAP'
};
diff --git a/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.js b/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.js
new file mode 100644
index 0000000000000..ed585e013d06f
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import _ from 'lodash';
+import { LAYER_TYPE, STYLE_TYPE } from '../constants';
+
+function isVectorLayer(layerDescriptor) {
+ const layerType = _.get(layerDescriptor, 'type');
+ return layerType === LAYER_TYPE.VECTOR;
+}
+
+export function addFieldMetaOptions({ attributes }) {
+ if (!attributes.layerListJSON) {
+ return attributes;
+ }
+
+ const layerList = JSON.parse(attributes.layerListJSON);
+ layerList.forEach((layerDescriptor) => {
+ if (isVectorLayer(layerDescriptor) && _.has(layerDescriptor, 'style.properties')) {
+ Object.values(layerDescriptor.style.properties).forEach(stylePropertyDescriptor => {
+ if (stylePropertyDescriptor.type === STYLE_TYPE.DYNAMIC) {
+ stylePropertyDescriptor.options.fieldMetaOptions = {
+ isEnabled: false, // turn off field metadata to avoid changing behavior of existing saved objects
+ sigma: 3,
+ };
+ }
+ });
+ }
+ });
+
+ return {
+ ...attributes,
+ layerListJSON: JSON.stringify(layerList),
+ };
+}
diff --git a/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.test.js b/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.test.js
new file mode 100644
index 0000000000000..905f77223b3bc
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.test.js
@@ -0,0 +1,121 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { addFieldMetaOptions } from './add_field_meta_options';
+import { LAYER_TYPE, STYLE_TYPE } from '../constants';
+
+describe('addFieldMetaOptions', () => {
+
+ test('Should handle missing layerListJSON attribute', () => {
+ const attributes = {
+ title: 'my map',
+ };
+ expect(addFieldMetaOptions({ attributes })).toEqual({
+ title: 'my map',
+ });
+ });
+
+ test('Should ignore non-vector layers', () => {
+ const layerListJSON = JSON.stringify([
+ {
+ type: LAYER_TYPE.HEATMAP,
+ style: {
+ type: 'HEATMAP',
+ colorRampName: 'Greens'
+ }
+ }
+ ]);
+ const attributes = {
+ title: 'my map',
+ layerListJSON
+ };
+ expect(addFieldMetaOptions({ attributes })).toEqual({
+ title: 'my map',
+ layerListJSON
+ });
+ });
+
+ test('Should ignore static style properties', () => {
+ const layerListJSON = JSON.stringify([
+ {
+ type: LAYER_TYPE.VECTOR,
+ style: {
+ type: 'VECTOR',
+ properties: {
+ lineColor: {
+ type: STYLE_TYPE.STATIC,
+ options: {
+ color: '#FFFFFF'
+ }
+ }
+ }
+ }
+ }
+ ]);
+ const attributes = {
+ title: 'my map',
+ layerListJSON
+ };
+ expect(addFieldMetaOptions({ attributes })).toEqual({
+ title: 'my map',
+ layerListJSON
+ });
+ });
+
+ test('Should add field meta options to dynamic style properties', () => {
+ const layerListJSON = JSON.stringify([
+ {
+ type: LAYER_TYPE.VECTOR,
+ style: {
+ type: 'VECTOR',
+ properties: {
+ fillColor: {
+ type: STYLE_TYPE.DYNAMIC,
+ options: {
+ field: {
+ name: 'my_field',
+ origin: 'source'
+ },
+ color: 'Greys'
+ }
+ }
+ }
+ }
+ }
+ ]);
+ const attributes = {
+ title: 'my map',
+ layerListJSON
+ };
+ expect(addFieldMetaOptions({ attributes })).toEqual({
+ title: 'my map',
+ layerListJSON: JSON.stringify([
+ {
+ type: LAYER_TYPE.VECTOR,
+ style: {
+ type: 'VECTOR',
+ properties: {
+ fillColor: {
+ type: STYLE_TYPE.DYNAMIC,
+ options: {
+ field: {
+ name: 'my_field',
+ origin: 'source'
+ },
+ color: 'Greys',
+ fieldMetaOptions: {
+ isEnabled: false,
+ sigma: 3,
+ }
+ }
+ }
+ }
+ }
+ }
+ ])
+ });
+ });
+});
diff --git a/x-pack/legacy/plugins/maps/migrations.js b/x-pack/legacy/plugins/maps/migrations.js
index 39dc58f259961..df19c8425199a 100644
--- a/x-pack/legacy/plugins/maps/migrations.js
+++ b/x-pack/legacy/plugins/maps/migrations.js
@@ -8,6 +8,7 @@ import { extractReferences } from './common/migrations/references';
import { emsRasterTileToEmsVectorTile } from './common/migrations/ems_raster_tile_to_ems_vector_tile';
import { topHitsTimeToSort } from './common/migrations/top_hits_time_to_sort';
import { moveApplyGlobalQueryToSources } from './common/migrations/move_apply_global_query';
+import { addFieldMetaOptions } from './common/migrations/add_field_meta_options';
export const migrations = {
'map': {
@@ -37,11 +38,12 @@ export const migrations = {
};
},
'7.6.0': (doc) => {
- const attributes = moveApplyGlobalQueryToSources(doc);
+ const attributesPhase1 = moveApplyGlobalQueryToSources(doc);
+ const attributesPhase2 = addFieldMetaOptions({ attributes: attributesPhase1 });
return {
...doc,
- attributes,
+ attributes: attributesPhase2,
};
}
},
diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js
index eb80169e94eab..af78e3a871802 100644
--- a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js
+++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js
@@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
-
import { AbstractField } from './field';
import { COUNT_AGG_TYPE } from '../../../common/constants';
+import { isMetricCountable } from '../util/is_metric_countable';
import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property';
export class ESAggMetricField extends AbstractField {
@@ -36,6 +36,11 @@ export class ESAggMetricField extends AbstractField {
return (this.getAggType() === COUNT_AGG_TYPE) ? true : !!this._esDocField;
}
+ async getDataType() {
+ // aggregations only provide numerical data
+ return 'number';
+ }
+
getESDocFieldName() {
return this._esDocField ? this._esDocField.getName() : '';
}
@@ -55,7 +60,6 @@ export class ESAggMetricField extends AbstractField {
);
}
-
makeMetricAggConfig() {
const metricAggConfig = {
id: this.getName(),
@@ -69,4 +73,13 @@ export class ESAggMetricField extends AbstractField {
}
return metricAggConfig;
}
+
+ supportsFieldMeta() {
+ // count and sum aggregations are not within field bounds so they do not support field meta.
+ return !isMetricCountable(this.getAggType());
+ }
+
+ async getFieldMetaRequest(config) {
+ return this._esDocField.getFieldMetaRequest(config);
+ }
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.test.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.test.js
new file mode 100644
index 0000000000000..65b8c518fa895
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.test.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ESAggMetricField } from './es_agg_field';
+import { METRIC_TYPE } from '../../../common/constants';
+
+describe('supportsFieldMeta', () => {
+
+ test('Non-counting aggregations should support field meta', () => {
+ const avgMetric = new ESAggMetricField({ aggType: METRIC_TYPE.AVG });
+ expect(avgMetric.supportsFieldMeta()).toBe(true);
+ const maxMetric = new ESAggMetricField({ aggType: METRIC_TYPE.MAX });
+ expect(maxMetric.supportsFieldMeta()).toBe(true);
+ const minMetric = new ESAggMetricField({ aggType: METRIC_TYPE.MIN });
+ expect(minMetric.supportsFieldMeta()).toBe(true);
+ });
+
+ test('Counting aggregations should not support field meta', () => {
+ const countMetric = new ESAggMetricField({ aggType: METRIC_TYPE.COUNT });
+ expect(countMetric.supportsFieldMeta()).toBe(false);
+ const sumMetric = new ESAggMetricField({ aggType: METRIC_TYPE.SUM });
+ expect(sumMetric.supportsFieldMeta()).toBe(false);
+ const uniqueCountMetric = new ESAggMetricField({ aggType: METRIC_TYPE.UNIQUE_COUNT });
+ expect(uniqueCountMetric.supportsFieldMeta()).toBe(false);
+ });
+});
diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js
index 5cc0c9a29ce02..ad15c6249e554 100644
--- a/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js
+++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js
@@ -27,4 +27,31 @@ export class ESDocField extends AbstractField {
return field.type;
}
+ supportsFieldMeta() {
+ return true;
+ }
+
+ async getFieldMetaRequest(/* config */) {
+ const field = await this._getField();
+
+ if (field.type !== 'number' && field.type !== 'date') {
+ return null;
+ }
+
+ const extendedStats = {};
+ if (field.scripted) {
+ extendedStats.script = {
+ source: field.script,
+ lang: field.lang
+ };
+ } else {
+ extendedStats.field = this._fieldName;
+ }
+ return {
+ [this._fieldName]: {
+ extended_stats: extendedStats
+ }
+ };
+ }
+
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/field.js b/x-pack/legacy/plugins/maps/public/layers/fields/field.js
index b53c6991c6ebe..f1bb116d29c8b 100644
--- a/x-pack/legacy/plugins/maps/public/layers/fields/field.js
+++ b/x-pack/legacy/plugins/maps/public/layers/fields/field.js
@@ -42,4 +42,12 @@ export class AbstractField {
getOrigin() {
return this._origin;
}
+
+ supportsFieldMeta() {
+ return false;
+ }
+
+ async getFieldMetaRequest(/* config */) {
+ return null;
+ }
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js
index 184fdc0663bd7..432492973cce0 100644
--- a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js
+++ b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js
@@ -7,6 +7,7 @@
import { ESTermSource } from '../sources/es_term_source';
import { getComputedFieldNamePrefix } from '../styles/vector/style_util';
+import { META_ID_ORIGIN_SUFFIX } from '../../../common/constants';
export class InnerJoin {
@@ -36,10 +37,14 @@ export class InnerJoin {
// Source request id must be static and unique because the re-fetch logic uses the id to locate the previous request.
// Elasticsearch sources have a static and unique id so that requests can be modified in the inspector.
// Using the right source id as the source request id because it meets the above criteria.
- getSourceId() {
+ getSourceDataRequestId() {
return `join_source_${this._rightSource.getId()}`;
}
+ getSourceMetaDataRequestId() {
+ return `${this.getSourceDataRequestId()}_${META_ID_ORIGIN_SUFFIX}`;
+ }
+
getLeftField() {
return this._leftField;
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js
index 1c2f33df66bf8..b1f3c32f267b9 100644
--- a/x-pack/legacy/plugins/maps/public/layers/layer.js
+++ b/x-pack/legacy/plugins/maps/public/layers/layer.js
@@ -80,7 +80,7 @@ export class AbstractLayer {
}
supportsElasticsearchFilters() {
- return this._source.supportsElasticsearchFilters();
+ return this._source.isESSource();
}
async supportsFitToBounds() {
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
index 413f99480a8c2..f4cb43ad90146 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
@@ -15,7 +15,7 @@ import { AggConfigs } from 'ui/agg_types';
import { tabifyAggResponse } from 'ui/agg_response/tabify';
import { convertToGeoJson } from './convert_to_geojson';
import { VectorStyle } from '../../styles/vector/vector_style';
-import { vectorStyles } from '../../styles/vector/vector_style_defaults';
+import { getDefaultDynamicProperties, VECTOR_STYLES } from '../../styles/vector/vector_style_defaults';
import { RENDER_AS } from './render_as';
import { CreateSourceEditor } from './create_source_editor';
import { UpdateSourceEditor } from './update_source_editor';
@@ -170,13 +170,15 @@ export class ESGeoGridSource extends AbstractESAggSource {
const searchSource = await this._makeSearchSource(searchFilters, 0);
const aggConfigs = new AggConfigs(indexPattern, this._makeAggConfigs(searchFilters.geogridPrecision), aggSchemas.all);
searchSource.setField('aggs', aggConfigs.toDsl());
- const esResponse = await this._runEsQuery(
- layerName,
+ const esResponse = await this._runEsQuery({
+ requestId: this.getId(),
+ requestName: layerName,
searchSource,
registerCancelCallback,
- i18n.translate('xpack.maps.source.esGrid.inspectorDescription', {
+ requestDescription: i18n.translate('xpack.maps.source.esGrid.inspectorDescription', {
defaultMessage: 'Elasticsearch geo grid aggregation request'
- }));
+ }),
+ });
const tabifiedResp = tabifyAggResponse(aggConfigs, esResponse);
const { featureCollection } = convertToGeoJson({
@@ -226,10 +228,14 @@ export class ESGeoGridSource extends AbstractESAggSource {
sourceDescriptor: this._descriptor,
...options
});
+
+ const defaultDynamicProperties = getDefaultDynamicProperties();
+
descriptor.style = VectorStyle.createDescriptor({
- [vectorStyles.FILL_COLOR]: {
+ [VECTOR_STYLES.FILL_COLOR]: {
type: DynamicStyleProperty.type,
options: {
+ ...defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options,
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
@@ -238,9 +244,10 @@ export class ESGeoGridSource extends AbstractESAggSource {
color: 'Blues'
}
},
- [vectorStyles.ICON_SIZE]: {
+ [VECTOR_STYLES.ICON_SIZE]: {
type: DynamicStyleProperty.type,
options: {
+ ...defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options,
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js
index 1b446e1f2159a..cc1e53dc5cb3f 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js
@@ -8,13 +8,13 @@ import React, { Fragment, Component } from 'react';
import { RENDER_AS } from './render_as';
import { MetricsEditor } from '../../../components/metrics_editor';
-import { METRIC_TYPE } from '../../../../common/constants';
import { indexPatternService } from '../../../kibana_services';
import { ResolutionEditor } from './resolution_editor';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSpacer, EuiTitle } from '@elastic/eui';
import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox';
+import { isMetricCountable } from '../../util/is_metric_countable';
export class UpdateSourceEditor extends Component {
state = {
@@ -72,7 +72,7 @@ export class UpdateSourceEditor extends Component {
this.props.renderAs === RENDER_AS.HEATMAP
? metric => {
//these are countable metrics, where blending heatmap color blobs make sense
- return [METRIC_TYPE.COUNT, METRIC_TYPE.SUM, METRIC_TYPE.UNIQUE_COUNT].includes(metric.value);
+ return isMetricCountable(metric.value);
}
: null;
const allowMultipleMetrics = this.props.renderAs !== RENDER_AS.HEATMAP;
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js
index 01220136b14f3..4eb0a952defba 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js
@@ -12,7 +12,7 @@ import { VectorLayer } from '../../vector_layer';
import { CreateSourceEditor } from './create_source_editor';
import { UpdateSourceEditor } from './update_source_editor';
import { VectorStyle } from '../../styles/vector/vector_style';
-import { vectorStyles } from '../../styles/vector/vector_style_defaults';
+import { getDefaultDynamicProperties, VECTOR_STYLES } from '../../styles/vector/vector_style_defaults';
import { i18n } from '@kbn/i18n';
import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW, COUNT_PROP_NAME, COUNT_PROP_LABEL } from '../../../../common/constants';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
@@ -123,10 +123,12 @@ export class ESPewPewSource extends AbstractESAggSource {
}
createDefaultLayer(options) {
+ const defaultDynamicProperties = getDefaultDynamicProperties();
const styleDescriptor = VectorStyle.createDescriptor({
- [vectorStyles.LINE_COLOR]: {
+ [VECTOR_STYLES.LINE_COLOR]: {
type: DynamicStyleProperty.type,
options: {
+ ...defaultDynamicProperties[VECTOR_STYLES.LINE_COLOR].options,
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
@@ -135,9 +137,10 @@ export class ESPewPewSource extends AbstractESAggSource {
color: 'Blues'
}
},
- [vectorStyles.LINE_WIDTH]: {
+ [VECTOR_STYLES.LINE_WIDTH]: {
type: DynamicStyleProperty.type,
options: {
+ ...defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH].options,
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
@@ -203,13 +206,15 @@ export class ESPewPewSource extends AbstractESAggSource {
}
});
- const esResponse = await this._runEsQuery(
- layerName,
+ const esResponse = await this._runEsQuery({
+ requestId: this.getId(),
+ requestName: layerName,
searchSource,
registerCancelCallback,
- i18n.translate('xpack.maps.source.pewPew.inspectorDescription', {
+ requestDescription: i18n.translate('xpack.maps.source.pewPew.inspectorDescription', {
defaultMessage: 'Source-destination connections request'
- }));
+ }),
+ });
const { featureCollection } = convertToLines(esResponse);
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
index 57a43f924b7e6..453a1851e47aa 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
@@ -261,7 +261,13 @@ export class ESSearchSource extends AbstractESSource {
}
});
- const resp = await this._runEsQuery(layerName, searchSource, registerCancelCallback, 'Elasticsearch document top hits request');
+ const resp = await this._runEsQuery({
+ requestId: this.getId(),
+ requestName: layerName,
+ searchSource,
+ registerCancelCallback,
+ requestDescription: 'Elasticsearch document top hits request',
+ });
const allHits = [];
const entityBuckets = _.get(resp, 'aggregations.entitySplit.buckets', []);
@@ -322,7 +328,13 @@ export class ESSearchSource extends AbstractESSource {
searchSource.setField('sort', this._buildEsSort());
}
- const resp = await this._runEsQuery(layerName, searchSource, registerCancelCallback, 'Elasticsearch document request');
+ const resp = await this._runEsQuery({
+ requestId: this.getId(),
+ requestName: layerName,
+ searchSource,
+ registerCancelCallback,
+ requestDescription: 'Elasticsearch document request',
+ });
return {
hits: resp.hits.hits.reverse(), // Reverse hits so top documents by sort are drawn on top
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js
index c2f4f7e755288..b5d7f7a6f606a 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js
@@ -54,7 +54,7 @@ export class AbstractESSource extends AbstractVectorSource {
return [];
}
- supportsElasticsearchFilters() {
+ isESSource() {
return true;
}
@@ -73,7 +73,7 @@ export class AbstractESSource extends AbstractVectorSource {
return [];
}
- async _runEsQuery(requestName, searchSource, registerCancelCallback, requestDescription) {
+ async _runEsQuery({ requestId, requestName, requestDescription, searchSource, registerCancelCallback }) {
const abortController = new AbortController();
registerCancelCallback(() => abortController.abort());
@@ -82,7 +82,7 @@ export class AbstractESSource extends AbstractVectorSource {
inspectorAdapters: this._inspectorAdapters,
searchSource,
requestName,
- requestId: this.getId(),
+ requestId,
requestDesc: requestDescription,
abortSignal: abortController.signal,
});
@@ -271,4 +271,42 @@ export class AbstractESSource extends AbstractVectorSource {
return fieldFromIndexPattern.format.getConverterFor('text');
}
+ async loadStylePropsMeta(layerName, style, dynamicStyleProps, registerCancelCallback, searchFilters) {
+ const promises = dynamicStyleProps.map(dynamicStyleProp => {
+ return dynamicStyleProp.getFieldMetaRequest();
+ });
+
+ const fieldAggRequests = await Promise.all(promises);
+ const aggs = fieldAggRequests.reduce((aggs, fieldAggRequest) => {
+ return fieldAggRequest ? { ...aggs, ...fieldAggRequest } : aggs;
+ }, {});
+
+ const indexPattern = await this.getIndexPattern();
+ const searchSource = new SearchSource();
+ searchSource.setField('index', indexPattern);
+ searchSource.setField('size', 0);
+ searchSource.setField('aggs', aggs);
+ if (searchFilters.sourceQuery) {
+ searchSource.setField('query', searchFilters.sourceQuery);
+ }
+ if (style.isTimeAware() && await this.isTimeAware()) {
+ searchSource.setField('filter', [timefilter.createFilter(indexPattern, searchFilters.timeFilters)]);
+ }
+
+ const resp = await this._runEsQuery({
+ requestId: `${this.getId()}_styleMeta`,
+ requestName: i18n.translate('xpack.maps.source.esSource.stylePropsMetaRequestName', {
+ defaultMessage: '{layerName} - metadata',
+ values: { layerName }
+ }),
+ searchSource,
+ registerCancelCallback,
+ requestDescription: i18n.translate('xpack.maps.source.esSource.stylePropsMetaRequestDescription', {
+ defaultMessage: 'Elasticsearch request retrieving field metadata used for calculating symbolization bands.',
+ }),
+ });
+
+ return resp.aggregations;
+ }
+
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js
index afc402fa81bcb..57366e502d581 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js
@@ -103,9 +103,13 @@ export class ESTermSource extends AbstractESAggSource {
const aggConfigs = new AggConfigs(indexPattern, configStates, aggSchemas.all);
searchSource.setField('aggs', aggConfigs.toDsl());
- const requestName = `${this._descriptor.indexPatternTitle}.${this._termField.getName()}`;
- const requestDesc = this._getRequestDescription(leftSourceName, leftFieldName);
- const rawEsData = await this._runEsQuery(requestName, searchSource, registerCancelCallback, requestDesc);
+ const rawEsData = await this._runEsQuery({
+ requestId: this.getId(),
+ requestName: `${this._descriptor.indexPatternTitle}.${this._termField.getName()}`,
+ searchSource,
+ registerCancelCallback,
+ requestDescription: this._getRequestDescription(leftSourceName, leftFieldName),
+ });
const metricPropertyNames = configStates
.filter(configState => {
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.js b/x-pack/legacy/plugins/maps/public/layers/sources/source.js
index 78e57f79bbe56..d3b2971dbbb0c 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.js
@@ -123,7 +123,7 @@ export class AbstractSource {
return AbstractSource.isIndexingSource;
}
- supportsElasticsearchFilters() {
+ isESSource() {
return false;
}
@@ -136,6 +136,10 @@ export class AbstractSource {
async getFieldFormatter(/* fieldName */) {
return null;
}
+
+ async loadStylePropsMeta() {
+ throw new Error(`Source#loadStylePropsMeta not implemented`);
+ }
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js
index e4982c86b53bb..ed64f408b2585 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js
@@ -10,13 +10,14 @@ import { AbstractStyle } from '../abstract_style';
import { HeatmapStyleEditor } from './components/heatmap_style_editor';
import { HeatmapLegend } from './components/legend/heatmap_legend';
import { DEFAULT_HEATMAP_COLOR_RAMP_NAME } from './components/heatmap_constants';
+import { LAYER_STYLE_TYPE } from '../../../../common/constants';
import { getColorRampStops } from '../color_utils';
import { i18n } from '@kbn/i18n';
import { EuiIcon } from '@elastic/eui';
export class HeatmapStyle extends AbstractStyle {
- static type = 'HEATMAP';
+ static type = LAYER_STYLE_TYPE.HEATMAP;
constructor(descriptor = {}) {
super();
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js
new file mode 100644
index 0000000000000..095740abe3dda
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js
@@ -0,0 +1,139 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { Component, Fragment } from 'react';
+import {
+ EuiButtonIcon,
+ EuiFormRow,
+ EuiPopover,
+ EuiRange,
+ EuiSwitch,
+} from '@elastic/eui';
+import { VECTOR_STYLES } from '../vector_style_defaults';
+import { i18n } from '@kbn/i18n';
+
+function getIsEnableToggleLabel(styleName) {
+ switch (styleName) {
+ case VECTOR_STYLES.FILL_COLOR:
+ case VECTOR_STYLES.LINE_COLOR:
+ return i18n.translate('xpack.maps.styles.fieldMetaOptions.isEnabled.colorLabel', {
+ defaultMessage: 'Calculate color ramp range from indices'
+ });
+ case VECTOR_STYLES.LINE_WIDTH:
+ return i18n.translate('xpack.maps.styles.fieldMetaOptions.isEnabled.widthLabel', {
+ defaultMessage: 'Calculate border width range from indices'
+ });
+ case VECTOR_STYLES.ICON_SIZE:
+ return i18n.translate('xpack.maps.styles.fieldMetaOptions.isEnabled.sizeLabel', {
+ defaultMessage: 'Calculate symbol size range from indices'
+ });
+ default:
+ return i18n.translate('xpack.maps.styles.fieldMetaOptions.isEnabled.defaultLabel', {
+ defaultMessage: 'Calculate symbolization range from indices'
+ });
+ }
+}
+
+export class FieldMetaOptionsPopover extends Component {
+
+ state = {
+ isPopoverOpen: false,
+ };
+
+ _togglePopover = () => {
+ this.setState({
+ isPopoverOpen: !this.state.isPopoverOpen,
+ });
+ }
+
+ _closePopover = () => {
+ this.setState({
+ isPopoverOpen: false,
+ });
+ }
+
+ _onIsEnabledChange = event => {
+ this.props.onChange({
+ ...this.props.styleProperty.getFieldMetaOptions(),
+ isEnabled: event.target.checked,
+ });
+ };
+
+ _onSigmaChange = event => {
+ this.props.onChange({
+ ...this.props.styleProperty.getFieldMetaOptions(),
+ sigma: event.target.value,
+ });
+ }
+
+ _renderButton() {
+ return (
+
+ );
+ }
+
+ _renderContent() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ render() {
+ if (!this.props.styleProperty.supportsFieldMeta()) {
+ return null;
+ }
+
+ return (
+
+ {this._renderContent()}
+
+ );
+ }
+}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js
index 0984b0189558d..b21577d214bb5 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js
@@ -6,27 +6,27 @@
import { i18n } from '@kbn/i18n';
-import { vectorStyles } from '../vector_style_defaults';
+import { VECTOR_STYLES } from '../vector_style_defaults';
export function getVectorStyleLabel(styleName) {
switch (styleName) {
- case vectorStyles.FILL_COLOR:
+ case VECTOR_STYLES.FILL_COLOR:
return i18n.translate('xpack.maps.styles.vector.fillColorLabel', {
defaultMessage: 'Fill color'
});
- case vectorStyles.LINE_COLOR:
+ case VECTOR_STYLES.LINE_COLOR:
return i18n.translate('xpack.maps.styles.vector.borderColorLabel', {
defaultMessage: 'Border color'
});
- case vectorStyles.LINE_WIDTH:
+ case VECTOR_STYLES.LINE_WIDTH:
return i18n.translate('xpack.maps.styles.vector.borderWidthLabel', {
defaultMessage: 'Border width'
});
- case vectorStyles.ICON_SIZE:
+ case VECTOR_STYLES.ICON_SIZE:
return i18n.translate('xpack.maps.styles.vector.symbolSizeLabel', {
defaultMessage: 'Symbol size'
});
- case vectorStyles.ICON_ORIENTATION:
+ case VECTOR_STYLES.ICON_ORIENTATION:
return i18n.translate('xpack.maps.styles.vector.orientationLabel', {
defaultMessage: 'Symbol orientation'
});
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 35c7066b7fd0f..dc5098c4d6d4d 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
@@ -81,18 +81,24 @@ export class StylePropertyLegendRow extends Component {
}
render() {
-
const { range, style } = this.props;
if (this._excludeFromHeader()) {
return null;
}
const header = style.renderHeader();
+
+ const min = this._formatValue(_.get(range, 'min', EMPTY_VALUE));
+ const minLabel = this.props.style.isFieldMetaEnabled() && range && range.isMinOutsideStdRange ? `< ${min}` : min;
+
+ const max = this._formatValue(_.get(range, 'max', EMPTY_VALUE));
+ const maxLabel = this.props.style.isFieldMetaEnabled() && range && range.isMaxOutsideStdRange ? `> ${max}` : max;
+
return (
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js
index d1de8e0fe6b4a..9686214fec9fe 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js
@@ -4,14 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { Component, Fragment } from 'react';
import { VectorStyle } from '../vector_style';
import { i18n } from '@kbn/i18n';
+import { FieldMetaOptionsPopover } from './field_meta_options_popover';
import { getVectorStyleLabel } from './get_vector_style_label';
import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiFormRow, EuiButtonToggle } from '@elastic/eui';
-export class StaticDynamicStyleRow extends React.Component {
+export class StaticDynamicStyleRow extends Component {
// Store previous options locally so when type is toggled,
// previous style options can be used.
prevStaticStyleOptions = this.props.defaultStaticStyleOptions;
@@ -29,6 +30,17 @@ export class StaticDynamicStyleRow extends React.Component {
return this.props.styleProperty.getOptions();
}
+ _onFieldMetaOptionsChange = fieldMetaOptions => {
+ const styleDescriptor = {
+ type: VectorStyle.STYLE_TYPE.DYNAMIC,
+ options: {
+ ...this._getStyleOptions(),
+ fieldMetaOptions
+ }
+ };
+ this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor);
+ }
+
_onStaticStyleChange = options => {
const styleDescriptor = {
type: VectorStyle.STYLE_TYPE.STATIC,
@@ -64,11 +76,17 @@ export class StaticDynamicStyleRow extends React.Component {
if (this._isDynamic()) {
const DynamicSelector = this.props.DynamicSelector;
return (
-
+
+
+
+
);
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js
index 3043d57c04037..d848b9274d071 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js
@@ -22,7 +22,7 @@ import { SYMBOLIZE_AS_ICON } from '../vector_constants';
import { i18n } from '@kbn/i18n';
import { SYMBOL_OPTIONS } from '../symbol_utils';
-import { EuiSpacer, EuiButtonGroup } from '@elastic/eui';
+import { EuiSpacer, EuiButtonGroup, EuiFormRow, EuiSwitch } from '@elastic/eui';
export class VectorStyleEditor extends Component {
state = {
@@ -117,6 +117,14 @@ export class VectorStyleEditor extends Component {
return [...this.state.dateFields, ...this.state.numberFields];
}
+ _handleSelectedFeatureChange = selectedFeature => {
+ this.setState({ selectedFeature });
+ };
+
+ _onIsTimeAwareChange = event => {
+ this.props.onIsTimeAwareChange(event.target.checked);
+ };
+
_renderFillColor() {
return (
{
- this.setState({ selectedFeature });
- };
-
- render() {
+ _renderProperties() {
const { supportedFeatures, selectedFeature } = this.state;
if (!supportedFeatures) {
@@ -302,4 +306,34 @@ export class VectorStyleEditor extends Component {
);
}
+
+ _renderIsTimeAwareSwitch() {
+ if (!this.props.showIsTimeAware) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+ }
+
+ render() {
+ return (
+
+ {this._renderProperties()}
+ {this._renderIsTimeAwareSwitch()}
+
+ );
+ }
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js
index 4b4b853c274cb..d56db31d17067 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js
@@ -50,7 +50,7 @@ export class DynamicColorProperty extends DynamicStyleProperty {
}
isCustomColorRamp() {
- return !!this._options.customColorRamp;
+ return this._options.useCustomColorRamp;
}
supportsFeatureState() {
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js
index fb4ffd8cce4b4..afbe924e1afb8 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js
@@ -7,14 +7,14 @@
import { DynamicStyleProperty } from './dynamic_style_property';
import { getComputedFieldName } from '../style_util';
-import { vectorStyles } from '../vector_style_defaults';
+import { VECTOR_STYLES } from '../vector_style_defaults';
export class DynamicOrientationProperty extends DynamicStyleProperty {
syncIconRotationWithMb(symbolLayerId, mbMap) {
if (this._options.field && this._options.field.name) {
- const targetName = getComputedFieldName(vectorStyles.ICON_ORIENTATION, this._options.field.name);
+ const targetName = getComputedFieldName(VECTOR_STYLES.ICON_ORIENTATION, this._options.field.name);
// Using property state instead of feature-state because layout properties do not support feature-state
mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', ['coalesce', ['get', targetName], 0]);
} else {
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js
index bd011b27d81c8..b4e6cf7be1701 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js
@@ -8,7 +8,7 @@
import { DynamicStyleProperty } from './dynamic_style_property';
import { getComputedFieldName } from '../style_util';
import { HALF_LARGE_MAKI_ICON_SIZE, LARGE_MAKI_ICON_SIZE, SMALL_MAKI_ICON_SIZE } from '../symbol_utils';
-import { vectorStyles } from '../vector_style_defaults';
+import { VECTOR_STYLES } from '../vector_style_defaults';
import _ from 'lodash';
import { CircleIcon } from '../components/legend/circle_icon';
import React, { Fragment } from 'react';
@@ -55,7 +55,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty {
mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`);
const halfIconPixels = iconPixels / 2;
- const targetName = getComputedFieldName(vectorStyles.ICON_SIZE, this._options.field.name);
+ const targetName = getComputedFieldName(VECTOR_STYLES.ICON_SIZE, this._options.field.name);
// Using property state instead of feature-state because layout properties do not support feature-state
mbMap.setLayoutProperty(symbolLayerId, 'icon-size', [
'interpolate',
@@ -112,9 +112,9 @@ export class DynamicSizeProperty extends DynamicStyleProperty {
renderHeader() {
let icons;
- if (this.getStyleName() === vectorStyles.LINE_WIDTH) {
+ if (this.getStyleName() === VECTOR_STYLES.LINE_WIDTH) {
icons = getLineWidthIcons();
- } else if (this.getStyleName() === vectorStyles.ICON_SIZE) {
+ } else if (this.getStyleName() === VECTOR_STYLES.ICON_SIZE) {
icons = getSymbolSizeIcons();
} else {
return null;
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 e87bcc12c99be..a72502f9f17fb 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
@@ -4,8 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
-
+import _ from 'lodash';
import { AbstractStyleProperty } from './style_property';
+import { DEFAULT_SIGMA } from '../vector_style_defaults';
import { STYLE_TYPE } from '../../../../../common/constants';
export class DynamicStyleProperty extends AbstractStyleProperty {
@@ -32,6 +33,22 @@ export class DynamicStyleProperty extends AbstractStyleProperty {
return this._field.getOrigin();
}
+ isFieldMetaEnabled() {
+ const fieldMetaOptions = this.getFieldMetaOptions();
+ return this.supportsFieldMeta() && _.get(fieldMetaOptions, 'isEnabled', true);
+ }
+
+ supportsFieldMeta() {
+ return this.isComplete() && this.isScaled() && this._field.supportsFieldMeta();
+ }
+
+ async getFieldMetaRequest() {
+ const fieldMetaOptions = this.getFieldMetaOptions();
+ return this._field.getFieldMetaRequest({
+ sigma: _.get(fieldMetaOptions, 'sigma', DEFAULT_SIGMA),
+ });
+ }
+
supportsFeatureState() {
return true;
}
@@ -39,4 +56,8 @@ export class DynamicStyleProperty extends AbstractStyleProperty {
isScaled() {
return true;
}
+
+ getFieldMetaOptions() {
+ return _.get(this.getOptions(), 'fieldMetaOptions', {});
+ }
}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js
index 69caaca080138..699955fe6542a 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-
export function getComputedFieldName(styleName, fieldName) {
return `${getComputedFieldNamePrefix(fieldName)}__${styleName}`;
}
@@ -12,3 +11,19 @@ export function getComputedFieldName(styleName, fieldName) {
export function getComputedFieldNamePrefix(fieldName) {
return `__kbn__dynamic__${fieldName}`;
}
+
+export function scaleValue(value, range) {
+ if (isNaN(value) || !range) {
+ return -1; //Nothing to scale, put outside scaled range
+ }
+
+ if (range.delta === 0 || value >= range.max) {
+ return 1; //snap to end of scaled range
+ }
+
+ if (value <= range.min) {
+ return 0; //snap to beginning of scaled range
+ }
+
+ return (value - range.min) / range.delta;
+}
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.test.js
new file mode 100644
index 0000000000000..a25e3bf8684c9
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.test.js
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { scaleValue } from './style_util';
+
+describe('scaleValue', () => {
+ test('Should scale value between 0 and 1', () => {
+ expect(scaleValue(5, { min: 0, max: 10, delta: 10 })).toBe(0.5);
+ });
+
+ test('Should snap value less then range min to 0', () => {
+ expect(scaleValue(-1, { min: 0, max: 10, delta: 10 })).toBe(0);
+ });
+
+ test('Should snap value greater then range max to 1', () => {
+ expect(scaleValue(11, { min: 0, max: 10, delta: 10 })).toBe(1);
+ });
+
+ test('Should snap value to 1 when tere is not range delta', () => {
+ expect(scaleValue(10, { min: 10, max: 10, delta: 0 })).toBe(1);
+ });
+
+ test('Should put value as -1 when value is not provided', () => {
+ expect(scaleValue(undefined, { min: 0, max: 10, delta: 10 })).toBe(-1);
+ });
+
+ test('Should put value as -1 when range is not provided', () => {
+ expect(scaleValue(5, undefined)).toBe(-1);
+ });
+});
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 45a1636e5c033..53794f2043aad 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
@@ -7,15 +7,21 @@
import _ from 'lodash';
import React from 'react';
import { VectorStyleEditor } from './components/vector_style_editor';
-import { getDefaultProperties, vectorStyles } from './vector_style_defaults';
+import { getDefaultProperties, VECTOR_STYLES } from './vector_style_defaults';
import { AbstractStyle } from '../abstract_style';
-import { GEO_JSON_TYPE, FIELD_ORIGIN, STYLE_TYPE } from '../../../../common/constants';
+import {
+ GEO_JSON_TYPE,
+ FIELD_ORIGIN,
+ STYLE_TYPE,
+ SOURCE_META_ID_ORIGIN,
+ LAYER_STYLE_TYPE,
+} from '../../../../common/constants';
import { VectorIcon } from './components/legend/vector_icon';
import { VectorStyleLegend } from './components/legend/vector_style_legend';
import { VECTOR_SHAPE_TYPES } from '../../sources/vector_feature_types';
import { SYMBOLIZE_AS_CIRCLE, SYMBOLIZE_AS_ICON } from './vector_constants';
import { getMakiSymbolAnchor } from './symbol_utils';
-import { getComputedFieldName } from './style_util';
+import { getComputedFieldName, scaleValue } from './style_util';
import { StaticStyleProperty } from './properties/static_style_property';
import { DynamicStyleProperty } from './properties/dynamic_style_property';
import { DynamicSizeProperty } from './properties/dynamic_size_property';
@@ -31,12 +37,13 @@ const POLYGONS = [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON];
export class VectorStyle extends AbstractStyle {
- static type = 'VECTOR';
+ static type = LAYER_STYLE_TYPE.VECTOR;
static STYLE_TYPE = STYLE_TYPE;
- static createDescriptor(properties = {}) {
+ static createDescriptor(properties = {}, isTimeAware = true) {
return {
type: VectorStyle.type,
- properties: { ...getDefaultProperties(), ...properties }
+ properties: { ...getDefaultProperties(), ...properties },
+ isTimeAware,
};
}
@@ -50,15 +57,15 @@ export class VectorStyle extends AbstractStyle {
this._layer = layer;
this._descriptor = {
...descriptor,
- ...VectorStyle.createDescriptor(descriptor.properties),
+ ...VectorStyle.createDescriptor(descriptor.properties, descriptor.isTimeAware),
};
- this._lineColorStyleProperty = this._makeColorProperty(this._descriptor.properties[vectorStyles.LINE_COLOR], vectorStyles.LINE_COLOR);
- this._fillColorStyleProperty = this._makeColorProperty(this._descriptor.properties[vectorStyles.FILL_COLOR], vectorStyles.FILL_COLOR);
- this._lineWidthStyleProperty = this._makeSizeProperty(this._descriptor.properties[vectorStyles.LINE_WIDTH], vectorStyles.LINE_WIDTH);
- this._iconSizeStyleProperty = this._makeSizeProperty(this._descriptor.properties[vectorStyles.ICON_SIZE], vectorStyles.ICON_SIZE);
+ this._lineColorStyleProperty = this._makeColorProperty(this._descriptor.properties[VECTOR_STYLES.LINE_COLOR], VECTOR_STYLES.LINE_COLOR);
+ this._fillColorStyleProperty = this._makeColorProperty(this._descriptor.properties[VECTOR_STYLES.FILL_COLOR], VECTOR_STYLES.FILL_COLOR);
+ this._lineWidthStyleProperty = this._makeSizeProperty(this._descriptor.properties[VECTOR_STYLES.LINE_WIDTH], VECTOR_STYLES.LINE_WIDTH);
+ this._iconSizeStyleProperty = this._makeSizeProperty(this._descriptor.properties[VECTOR_STYLES.ICON_SIZE], VECTOR_STYLES.ICON_SIZE);
// eslint-disable-next-line max-len
- this._iconOrientationProperty = this._makeOrientationProperty(this._descriptor.properties[vectorStyles.ICON_ORIENTATION], vectorStyles.ICON_ORIENTATION);
+ this._iconOrientationProperty = this._makeOrientationProperty(this._descriptor.properties[VECTOR_STYLES.ICON_ORIENTATION], VECTOR_STYLES.ICON_ORIENTATION);
}
_getAllStyleProperties() {
@@ -72,13 +79,22 @@ export class VectorStyle extends AbstractStyle {
}
renderEditor({ layer, onStyleDescriptorChange }) {
- const styleProperties = { ...this.getRawProperties() };
+ const rawProperties = this.getRawProperties();
const handlePropertyChange = (propertyName, settings) => {
- styleProperties[propertyName] = settings;//override single property, but preserve the rest
- const vectorStyleDescriptor = VectorStyle.createDescriptor(styleProperties);
+ rawProperties[propertyName] = settings;//override single property, but preserve the rest
+ const vectorStyleDescriptor = VectorStyle.createDescriptor(rawProperties, this.isTimeAware());
onStyleDescriptorChange(vectorStyleDescriptor);
};
+ const onIsTimeAwareChange = isTimeAware => {
+ const vectorStyleDescriptor = VectorStyle.createDescriptor(rawProperties, isTimeAware);
+ onStyleDescriptorChange(vectorStyleDescriptor);
+ };
+
+ const propertiesWithFieldMeta = this.getDynamicPropertiesArray().filter(dynamicStyleProp => {
+ return dynamicStyleProp.isFieldMetaEnabled();
+ });
+
return (
0}
/>
);
}
@@ -156,7 +175,7 @@ export class VectorStyle extends AbstractStyle {
nextStyleDescriptor: VectorStyle.createDescriptor({
...originalProperties,
...updatedProperties,
- })
+ }, this.isTimeAware())
};
}
@@ -239,6 +258,10 @@ export class VectorStyle extends AbstractStyle {
return fieldNames;
}
+ isTimeAware() {
+ return this._descriptor.isTimeAware;
+ }
+
getRawProperties() {
return this._descriptor.properties || {};
}
@@ -277,7 +300,56 @@ export class VectorStyle extends AbstractStyle {
}
_getFieldRange = (fieldName) => {
- return _.get(this._descriptor, ['__styleMeta', fieldName]);
+ const fieldRangeFromLocalFeatures = _.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;
+ }
+
+ let dataRequestId;
+ if (dynamicProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE) {
+ dataRequestId = SOURCE_META_ID_ORIGIN;
+ } else {
+ const join = this._layer.getValidJoins().find(join => {
+ const matchingField = join.getRightJoinSource().getMetricFieldForName(fieldName);
+ return !!matchingField;
+ });
+ if (join) {
+ dataRequestId = join.getSourceMetaDataRequestId();
+ }
+ }
+
+ if (!dataRequestId) {
+ return fieldRangeFromLocalFeatures;
+ }
+
+ const styleMetaDataRequest = this._layer._findDataRequestForSource(dataRequestId);
+ if (!styleMetaDataRequest || !styleMetaDataRequest.hasData()) {
+ return fieldRangeFromLocalFeatures;
+ }
+
+ 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 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,
+ };
}
getIcon = () => {
@@ -289,8 +361,8 @@ export class VectorStyle extends AbstractStyle {
);
@@ -321,7 +393,7 @@ export class VectorStyle extends AbstractStyle {
// To work around this limitation, some styling values must fall back to geojson property values.
let supportsFeatureState;
let isScaled;
- if (styleProperty.getStyleName() === vectorStyles.ICON_SIZE
+ if (styleProperty.getStyleName() === VECTOR_STYLES.ICON_SIZE
&& this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_ICON) {
supportsFeatureState = false;
isScaled = true;
@@ -380,13 +452,7 @@ export class VectorStyle extends AbstractStyle {
const value = parseFloat(feature.properties[name]);
let styleValue;
if (isScaled) {
- if (isNaN(value) || !range) {//cannot scale
- styleValue = -1;//put outside range
- } else if (range.delta === 0) {//values are identical
- styleValue = 1;//snap to end of color range
- } else {
- styleValue = (value - range.min) / range.delta;
- }
+ styleValue = scaleValue(value, range);
} else {
if (isNaN(value)) {
styleValue = 0;
@@ -450,7 +516,6 @@ export class VectorStyle extends AbstractStyle {
}
_makeField(fieldDescriptor) {
-
if (!fieldDescriptor || !fieldDescriptor.name) {
return null;
}
@@ -473,8 +538,6 @@ export class VectorStyle extends AbstractStyle {
} else {
throw new Error(`Unknown origin-type ${fieldDescriptor.origin}`);
}
-
-
}
_makeSizeProperty(descriptor, styleName) {
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js
index ea4228430d13d..b834fb842389e 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js
@@ -16,8 +16,9 @@ const DEFAULT_ICON = 'airfield';
export const DEFAULT_MIN_SIZE = 1;
export const DEFAULT_MAX_SIZE = 64;
+export const DEFAULT_SIGMA = 3;
-export const vectorStyles = {
+export const VECTOR_STYLES = {
SYMBOL: 'symbol',
FILL_COLOR: 'fillColor',
LINE_COLOR: 'lineColor',
@@ -29,7 +30,7 @@ export const vectorStyles = {
export function getDefaultProperties(mapColors = []) {
return {
...getDefaultStaticProperties(mapColors),
- [vectorStyles.SYMBOL]: {
+ [VECTOR_STYLES.SYMBOL]: {
options: {
symbolizeAs: SYMBOLIZE_AS_CIRCLE,
symbolId: DEFAULT_ICON,
@@ -48,31 +49,31 @@ export function getDefaultStaticProperties(mapColors = []) {
return {
- [vectorStyles.FILL_COLOR]: {
+ [VECTOR_STYLES.FILL_COLOR]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
color: nextFillColor,
}
},
- [vectorStyles.LINE_COLOR]: {
+ [VECTOR_STYLES.LINE_COLOR]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
color: nextLineColor
}
},
- [vectorStyles.LINE_WIDTH]: {
+ [VECTOR_STYLES.LINE_WIDTH]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
size: 1
}
},
- [vectorStyles.ICON_SIZE]: {
+ [VECTOR_STYLES.ICON_SIZE]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
size: DEFAULT_ICON_SIZE
}
},
- [vectorStyles.ICON_ORIENTATION]: {
+ [VECTOR_STYLES.ICON_ORIENTATION]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
orientation: 0
@@ -83,40 +84,60 @@ export function getDefaultStaticProperties(mapColors = []) {
export function getDefaultDynamicProperties() {
return {
- [vectorStyles.FILL_COLOR]: {
+ [VECTOR_STYLES.FILL_COLOR]: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
color: COLOR_GRADIENTS[0].value,
field: undefined,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: DEFAULT_SIGMA,
+ }
}
},
- [vectorStyles.LINE_COLOR]: {
+ [VECTOR_STYLES.LINE_COLOR]: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
color: COLOR_GRADIENTS[0].value,
field: undefined,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: DEFAULT_SIGMA,
+ }
}
},
- [vectorStyles.LINE_WIDTH]: {
+ [VECTOR_STYLES.LINE_WIDTH]: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
minSize: DEFAULT_MIN_SIZE,
maxSize: DEFAULT_MAX_SIZE,
field: undefined,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: DEFAULT_SIGMA,
+ }
}
},
- [vectorStyles.ICON_SIZE]: {
+ [VECTOR_STYLES.ICON_SIZE]: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
minSize: DEFAULT_MIN_SIZE,
maxSize: DEFAULT_MAX_SIZE,
field: undefined,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: DEFAULT_SIGMA,
+ }
}
},
- [vectorStyles.ICON_ORIENTATION]: {
+ [VECTOR_STYLES.ICON_ORIENTATION]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
field: undefined,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: DEFAULT_SIGMA,
+ }
}
},
};
diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js
index 610c704b34ec6..557a2bf869987 100644
--- a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js
+++ b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js
@@ -128,3 +128,22 @@ export async function canSkipSourceUpdate({ source, prevDataRequest, nextMeta })
&& !updateDueToPrecisionChange
&& !updateDueToSourceMetaChange;
}
+
+export function canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }) {
+ if (!prevDataRequest) {
+ return false;
+ }
+ const prevMeta = prevDataRequest.getMeta();
+ if (!prevMeta) {
+ return false;
+ }
+
+ const updateDueToFields = !_.isEqual(prevMeta.dynamicStyleFields, nextMeta.dynamicStyleFields);
+
+ const updateDueToSourceQuery = !_.isEqual(prevMeta.sourceQuery, nextMeta.sourceQuery);
+
+ const updateDueToIsTimeAware = nextMeta.isTimeAware !== prevMeta.isTimeAware;
+ const updateDueToTime = nextMeta.isTimeAware ? !_.isEqual(prevMeta.timeFilters, nextMeta.timeFilters) : false;
+
+ return !updateDueToFields && !updateDueToSourceQuery && !updateDueToIsTimeAware && !updateDueToTime;
+}
diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js
index 77359a6def48f..24728f2ac95fd 100644
--- a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js
+++ b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js
@@ -126,7 +126,8 @@ describe('canSkipSourceUpdate', () => {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
- }
+ },
+ data: {}
});
it('can skip update when filter changes', async () => {
@@ -210,7 +211,8 @@ describe('canSkipSourceUpdate', () => {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
- }
+ },
+ data: {}
});
it('can not skip update when filter changes', async () => {
diff --git a/x-pack/legacy/plugins/maps/public/layers/util/data_request.js b/x-pack/legacy/plugins/maps/public/layers/util/data_request.js
index 95b82aa292884..12d57afbe1c87 100644
--- a/x-pack/legacy/plugins/maps/public/layers/util/data_request.js
+++ b/x-pack/legacy/plugins/maps/public/layers/util/data_request.js
@@ -22,7 +22,7 @@ export class DataRequest {
}
getMeta() {
- return _.get(this._descriptor, 'dataMeta', {});
+ return this.hasData() ? _.get(this._descriptor, 'dataMeta', {}) : _.get(this._descriptor, 'dataMetaAtStart', {});
}
hasData() {
diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_metric_countable.js b/x-pack/legacy/plugins/maps/public/layers/util/is_metric_countable.js
new file mode 100644
index 0000000000000..54d8794b1e3cf
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/public/layers/util/is_metric_countable.js
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { METRIC_TYPE } from '../../../common/constants';
+
+export function isMetricCountable(aggType) {
+ return [METRIC_TYPE.COUNT, METRIC_TYPE.SUM, METRIC_TYPE.UNIQUE_COUNT].includes(aggType);
+}
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 57126bb7681b8..7e831115e6dba 100644
--- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js
+++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js
@@ -12,16 +12,19 @@ import { InnerJoin } from './joins/inner_join';
import {
FEATURE_ID_PROPERTY_NAME,
SOURCE_DATA_ID_ORIGIN,
+ SOURCE_META_ID_ORIGIN,
FEATURE_VISIBLE_PROPERTY_NAME,
EMPTY_FEATURE_COLLECTION,
- LAYER_TYPE
+ LAYER_TYPE,
+ FIELD_ORIGIN,
+ LAYER_STYLE_TYPE,
} from '../../common/constants';
import _ from 'lodash';
import { JoinTooltipProperty } from './tooltips/join_tooltip_property';
import { EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DataRequestAbortError } from './util/data_request';
-import { canSkipSourceUpdate } from './util/can_skip_fetch';
+import { canSkipSourceUpdate, canSkipStyleMetaUpdate } from './util/can_skip_fetch';
import { assignFeatureIds } from './util/assign_feature_ids';
import {
getFillFilterExpression,
@@ -88,7 +91,7 @@ export class VectorLayer extends AbstractLayer {
const joins = this.getValidJoins();
for (let i = 0; i < joins.length; i++) {
- const joinDataRequest = this.getDataRequest(joins[i].getSourceId());
+ const joinDataRequest = this.getDataRequest(joins[i].getSourceDataRequestId());
if (!joinDataRequest || !joinDataRequest.hasData()) {
return false;
}
@@ -229,12 +232,10 @@ export class VectorLayer extends AbstractLayer {
return this._dataRequests.find(dataRequest => dataRequest.getDataId() === sourceDataId);
}
-
-
async _syncJoin({ join, startLoading, stopLoading, onLoadError, registerCancelCallback, dataFilters }) {
const joinSource = join.getRightJoinSource();
- const sourceDataId = join.getSourceId();
+ const sourceDataId = join.getSourceDataRequestId();
const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`);
const searchFilters = {
...dataFilters,
@@ -287,6 +288,7 @@ export class VectorLayer extends AbstractLayer {
async _syncJoins(syncContext) {
const joinSyncs = this.getValidJoins().map(async join => {
+ await this._syncJoinStyleMeta(syncContext, join);
return this._syncJoin({ join, ...syncContext });
});
@@ -350,7 +352,7 @@ export class VectorLayer extends AbstractLayer {
startLoading, stopLoading, onLoadError, registerCancelCallback, dataFilters
}) {
- const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`);
+ const requestToken = Symbol(`layer-source-data:${this.getId()}`);
const searchFilters = this._getSearchFilters(dataFilters);
const prevDataRequest = this.getSourceDataRequest();
@@ -389,11 +391,89 @@ export class VectorLayer extends AbstractLayer {
}
}
+ async _syncSourceStyleMeta(syncContext) {
+ if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) {
+ return;
+ }
+
+ return this._syncStyleMeta({
+ source: this._source,
+ sourceQuery: this.getQuery(),
+ dataRequestId: SOURCE_META_ID_ORIGIN,
+ dynamicStyleProps: this._style.getDynamicPropertiesArray().filter(dynamicStyleProp => {
+ return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE && dynamicStyleProp.isFieldMetaEnabled();
+ }),
+ ...syncContext
+ });
+ }
+
+ async _syncJoinStyleMeta(syncContext, join) {
+ const joinSource = join.getRightJoinSource();
+ return this._syncStyleMeta({
+ source: joinSource,
+ sourceQuery: joinSource.getWhereQuery(),
+ dataRequestId: join.getSourceMetaDataRequestId(),
+ dynamicStyleProps: this._style.getDynamicPropertiesArray().filter(dynamicStyleProp => {
+ const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getField().getName());
+ return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN
+ && !!matchingField
+ && dynamicStyleProp.isFieldMetaEnabled();
+ }),
+ ...syncContext
+ });
+ }
+
+ async _syncStyleMeta({
+ source,
+ sourceQuery,
+ dataRequestId,
+ dynamicStyleProps,
+ dataFilters,
+ startLoading,
+ stopLoading,
+ onLoadError,
+ registerCancelCallback
+ }) {
+
+ if (!source.isESSource() || dynamicStyleProps.length === 0) {
+ return;
+ }
+
+ const dynamicStyleFields = dynamicStyleProps.map(dynamicStyleProp => {
+ return dynamicStyleProp.getField().getName();
+ });
+
+ const nextMeta = {
+ dynamicStyleFields: _.uniq(dynamicStyleFields).sort(),
+ sourceQuery,
+ isTimeAware: this._style.isTimeAware() && await source.isTimeAware(),
+ timeFilters: dataFilters.timeFilters,
+ };
+ const prevDataRequest = this._findDataRequestForSource(dataRequestId);
+ const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta });
+ if (canSkipFetch) {
+ return;
+ }
+
+ const requestToken = Symbol(`layer-${this.getId()}-style-meta`);
+ try {
+ startLoading(dataRequestId, requestToken, nextMeta);
+ const layerName = await this.getDisplayName();
+ const styleMeta = await source.loadStylePropsMeta(layerName, this._style, dynamicStyleProps, registerCancelCallback, nextMeta);
+ stopLoading(dataRequestId, requestToken, styleMeta, nextMeta);
+ } catch (error) {
+ if (!(error instanceof DataRequestAbortError)) {
+ onLoadError(dataRequestId, requestToken, error.message);
+ }
+ }
+ }
+
async syncData(syncContext) {
if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) {
return;
}
+ await this._syncSourceStyleMeta(syncContext);
const sourceResult = await this._syncSource(syncContext);
if (
!sourceResult.featureCollection ||
diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts
index d7da585966758..ede0d3f394789 100644
--- a/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts
+++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts
@@ -147,6 +147,10 @@ export const mockLineLayer = {
},
minSize: 1,
maxSize: 8,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
},
},
iconSize: { type: 'STATIC', options: { size: 10 } },
diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts
index fd17e6eaeac64..637251eb64f70 100644
--- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts
+++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts
@@ -210,6 +210,10 @@ export const getLineLayer = (indexPatternTitle: string, indexPatternId: string)
},
minSize: 1,
maxSize: 8,
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
},
},
iconSize: { type: 'STATIC', options: { size: 10 } },
diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json
index 1291e3dd10cff..a9d2601442aaa 100644
--- a/x-pack/test/functional/es_archives/maps/kibana/data.json
+++ b/x-pack/test/functional/es_archives/maps/kibana/data.json
@@ -411,7 +411,7 @@
"type": "envelope"
},
"description": "",
- "layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]",
+ "layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[\"name\"],\"applyGlobalQuery\":false,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3},\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_join_0_index_pattern\"}}]}]",
"mapStateJSON": "{\"zoom\":3.02,\"center\":{\"lon\":77.33426,\"lat\":-0.04647},\"timeFilters\":{\"from\":\"now-17m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"title": "join example",
"uiStateJSON": "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[\"n1t6f\"]}"