diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/ems_region_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/ems_region_field.js new file mode 100644 index 0000000000000..a078fc6cd3f35 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/fields/ems_region_field.js @@ -0,0 +1,27 @@ +/* + * 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 { AbstractField } from './field'; +import { TooltipProperty } from '../tooltips/tooltip_property'; + +//todo: rename to ESMFileField +export class EMSRegionLayerField extends AbstractField { + static type = 'EMS_REGION_LAYER'; + + async getLabel() { + const emsFileLayer = await this._source.getEMSFileLayer(); + const emsFields = emsFileLayer.getFieldsInLanguage(); + // Map EMS field name to language specific label + const emsField = emsFields.find(field => field.name === this.getName()); + return emsField ? emsField.description : this.getName(); + } + + async createTooltipProperty(value) { + const label = await this.getLabel(); + return new TooltipProperty(this.getName(), label, value); + } +} 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 new file mode 100644 index 0000000000000..a92027ae7f2d1 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js @@ -0,0 +1,64 @@ +/* + * 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 { AbstractField } from './field'; +import { METRIC_TYPE } from '../../../common/constants'; + +export class ESAggMetricField extends AbstractField { + + static type = 'ES_AGG'; + + constructor({ label, source, aggType, esDocField }) { + super({ source }); + this._label = label; + this._aggType = aggType; + this._esDocField = esDocField; + } + + getName() { + return this._source.formatMetricKey(this.getAggType(), this.getESDocFieldName()); + } + + async getLabel() { + return this.getPropertyLabel(); + } + + getAggType() { + return this._aggType; + } + + isValid() { + return (this.getAggType() === METRIC_TYPE.COUNT) ? true : !!this._esDocField; + } + + getPropertyLabel() { + return this._label ? this._label : this._source.formatMetricLabel(this.getAggType(), this.getESDocFieldName()); + } + + getESDocFieldName() { + return this._esDocField ? this._esDocField.getName() : ''; + } + + getRequestDescription() { + return this.getAggType() !== METRIC_TYPE.COUNT ? `${this.getAggType()} ${this.getESDocFieldName()}` : METRIC_TYPE.COUNT; + } + + makeMetricAggConfig() { + const metricAggConfig = { + id: this.getName(), + enabled: true, + type: this.getAggType(), + schema: 'metric', + params: {} + }; + if (this.getAggType() !== METRIC_TYPE.COUNT) { + metricAggConfig.params = { field: this.getESDocFieldName() }; + } + return metricAggConfig; + } + +} 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 new file mode 100644 index 0000000000000..187e046a2eb6c --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js @@ -0,0 +1,30 @@ +/* + * 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 { AbstractField } from './field'; +import { ESTooltipProperty } from '../tooltips/es_tooltip_property'; + +export class ESDocField extends AbstractField { + + static type = 'ES_DOC'; + + async _getField() { + const indexPattern = await this._source.getIndexPattern(); + return indexPattern.fields.find((field) => field.name === this._fieldName); + } + + async createTooltipProperty(value) { + const indexPattern = await this._source.getIndexPattern(); + return new ESTooltipProperty(this.getName(), this.getName(), value, indexPattern); + } + + async getType() { + const field = await this._getField(); + return field.type; + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/field.js b/x-pack/legacy/plugins/maps/public/layers/fields/field.js new file mode 100644 index 0000000000000..49a24c398213f --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/fields/field.js @@ -0,0 +1,52 @@ +/* + * 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. + */ + + +export class AbstractField { + + //todo consider removing + //double check if we're actually using this consistently + static FIELD_TYPE = { + STRING: 'string', + NUMBER: 'number', + DATE: 'date' + }; + + constructor({ fieldName, source }) { + this._fieldName = fieldName; + this._source = source; + } + + getName() { + return this._fieldName; + } + + isValid() { + return !!this._fieldName; + } + + async getType() { + return AbstractField.FIELD_TYPE.STRING; + } + + async getLabel() { + return this._fieldName; + } + + async createTooltipProperty() { + throw new Error('must implement Field#createTooltipProperty'); + } + +} + + + + + + + + + diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/kibana_region_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/kibana_region_field.js new file mode 100644 index 0000000000000..f341d05db9a3b --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/fields/kibana_region_field.js @@ -0,0 +1,19 @@ +/* + * 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 { AbstractField } from './field'; + +//todo: need to be implemented +export class KibanaRegionField extends AbstractField { + + static type = 'KIBANA_REGION'; + + async getType() { + return AbstractField.FIELD_TYPE.STRING; + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js index 629fb3284ff11..157d45fbbd275 100644 --- a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; import { AbstractLayer } from './layer'; import { VectorLayer } from './vector_layer'; -import { HeatmapStyle } from './styles/heatmap_style'; +import { HeatmapStyle } from './styles/heatmap/heatmap_style'; import { EMPTY_FEATURE_COLLECTION, LAYER_TYPE } from '../../common/constants'; const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__';//unique name to store scaled value for weighting @@ -28,12 +27,14 @@ export class HeatmapLayer extends VectorLayer { if (!style) { const defaultStyle = HeatmapStyle.createDescriptor(); this._style = new HeatmapStyle(defaultStyle); + } else { + this._style = style; } } _getPropKeyOfSelectedMetric() { const metricfields = this._source.getMetricFields(); - return metricfields[0].propertyKey; + return metricfields[0].getName(); } _getHeatmapLayerId() { @@ -102,7 +103,8 @@ export class HeatmapLayer extends VectorLayer { } getLegendDetails() { - const label = _.get(this._source.getMetricFields(), '[0].propertyLabel', ''); + const metricFields = this._source.getMetricFields(); + const label = metricFields[0].getPropertyLabel(); return this._style.getLegendDetails(label); } } 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 8d1d1439a967e..781c02c3cffc2 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 @@ -6,13 +6,15 @@ import { ESTermSource } from '../sources/es_term_source'; -import { VectorStyle } from '../styles/vector_style'; +import { getComputedFieldNamePrefix } from '../styles/vector/style_util'; export class InnerJoin { - constructor(joinDescriptor, inspectorAdapters) { + constructor(joinDescriptor, leftSource) { this._descriptor = joinDescriptor; + const inspectorAdapters = leftSource.getInspectorAdapters(); this._rightSource = new ESTermSource(joinDescriptor.right, inspectorAdapters); + this._leftField = this._descriptor.leftField ? leftSource.createField({ fieldName: joinDescriptor.leftField }) : null; } destroy() { @@ -20,20 +22,16 @@ export class InnerJoin { } hasCompleteConfig() { - if (this._descriptor.leftField && this._rightSource) { + if (this._leftField && this._rightSource) { return this._rightSource.hasCompleteConfig(); } return false; } - getRightMetricFields() { - return this._rightSource.getMetricFields(); - } - getJoinFields() { - return this.getRightMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { - return { label, name }; + return this._rightSource.getMetricFields().map(esAggMetricField => { + return { label: esAggMetricField.getPropertyLabel(), name: esAggMetricField.getName() }; }); } @@ -44,18 +42,19 @@ export class InnerJoin { return `join_source_${this._rightSource.getId()}`; } - getLeftFieldName() { - return this._descriptor.leftField; + getLeftField() { + return this._leftField; } - joinPropertiesToFeature(feature, propertiesMap, rightMetricFields) { + joinPropertiesToFeature(feature, propertiesMap) { + const rightMetricFields = this._rightSource.getMetricFields(); // delete feature properties added by previous join for (let j = 0; j < rightMetricFields.length; j++) { - const { propertyKey: metricPropertyKey } = rightMetricFields[j]; + const metricPropertyKey = rightMetricFields[j].getName(); delete feature.properties[metricPropertyKey]; // delete all dynamic properties for metric field - const stylePropertyPrefix = VectorStyle.getComputedFieldNamePrefix(metricPropertyKey); + const stylePropertyPrefix = getComputedFieldNamePrefix(metricPropertyKey); Object.keys(feature.properties).forEach(featurePropertyKey => { if (featurePropertyKey.length >= stylePropertyPrefix.length && featurePropertyKey.substring(0, stylePropertyPrefix.length) === stylePropertyPrefix) { @@ -64,7 +63,7 @@ export class InnerJoin { }); } - const joinKey = feature.properties[this._descriptor.leftField]; + const joinKey = feature.properties[this._leftField.getName()]; const coercedKey = typeof joinKey === 'undefined' || joinKey === null ? null : joinKey.toString(); if (propertiesMap && coercedKey !== null && propertiesMap.has(coercedKey)) { Object.assign(feature.properties, propertiesMap.get(coercedKey)); diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js index c493062723470..12790cae42ba2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js @@ -23,12 +23,25 @@ const rightSource = { indexPatternId: '90943e30-9a47-11e8-b64d-95841ca0b247', indexPatternTitle: 'kibana_sample_data_logs', term: 'geo.dest', + metrics: [{ type: 'count' }] +}; + +const mockSource = { + getInspectorAdapters() { + }, + createField(name) { + return { + getName() { + return name; + } + }; + } }; const leftJoin = new InnerJoin({ leftField: 'iso2', right: rightSource -}); +}, mockSource); const COUNT_PROPERTY_NAME = '__kbnjoin__count_groupby_kibana_sample_data_logs.geo.dest'; describe('joinPropertiesToFeature', () => { @@ -76,7 +89,7 @@ describe('joinPropertiesToFeature', () => { const leftJoin = new InnerJoin({ leftField: 'zipcode', right: rightSource - }); + }, mockSource); const feature = { properties: { @@ -118,7 +131,7 @@ describe('joinPropertiesToFeature', () => { const leftJoin = new InnerJoin({ leftField: 'code', right: rightSource - }); + }, mockSource); const feature = { properties: { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js index 3af1378e8e016..9c1bd57873a2e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js @@ -13,7 +13,7 @@ import { EMSFileCreateSourceEditor } from './create_source_editor'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { UpdateSourceEditor } from './update_source_editor'; -import { TooltipProperty } from '../../tooltips/tooltip_property'; +import { EMSRegionLayerField } from '../../fields/ems_region_field'; export class EMSFileSource extends AbstractVectorSource { @@ -45,19 +45,28 @@ export class EMSFileSource extends AbstractVectorSource { constructor(descriptor, inspectorAdapters) { super(EMSFileSource.createDescriptor(descriptor), inspectorAdapters); + this._tooltipFields = this._descriptor.tooltipProperties.map(propertyKey => this.createField({ fieldName: propertyKey })); + } + + createField({ fieldName }) { + return new EMSRegionLayerField({ + fieldName, + source: this + }); } renderSourceSettingsEditor({ onChange }) { return ( ); } - async _getEMSFileLayer() { + async getEMSFileLayer() { const emsClient = getEMSClient(); const emsFileLayers = await emsClient.getFileLayers(); const emsFileLayer = emsFileLayers.find((fileLayer => fileLayer.getId() === this._descriptor.id)); @@ -73,7 +82,7 @@ export class EMSFileSource extends AbstractVectorSource { } async getGeoJsonWithMeta() { - const emsFileLayer = await this._getEMSFileLayer(); + const emsFileLayer = await this.getEMSFileLayer(); const featureCollection = await AbstractVectorSource.getGeoJson({ format: emsFileLayer.getDefaultFormatType(), featureCollectionPath: 'data', @@ -98,7 +107,7 @@ export class EMSFileSource extends AbstractVectorSource { async getImmutableProperties() { let emsLink; try { - const emsFileLayer = await this._getEMSFileLayer(); + const emsFileLayer = await this.getEMSFileLayer(); emsLink = emsFileLayer.getEMSHotLink(); } catch(error) { // ignore error if EMS layer id could not be found @@ -121,7 +130,7 @@ export class EMSFileSource extends AbstractVectorSource { async getDisplayName() { try { - const emsFileLayer = await this._getEMSFileLayer(); + const emsFileLayer = await this.getEMSFileLayer(); return emsFileLayer.getDisplayName(); } catch (error) { return this._descriptor.id; @@ -129,13 +138,13 @@ export class EMSFileSource extends AbstractVectorSource { } async getAttributions() { - const emsFileLayer = await this._getEMSFileLayer(); + const emsFileLayer = await this.getEMSFileLayer(); return emsFileLayer.getAttributions(); } async getLeftJoinFields() { - const emsFileLayer = await this._getEMSFileLayer(); + const emsFileLayer = await this.getEMSFileLayer(); const fields = emsFileLayer.getFieldsInLanguage(); return fields.map(f => { return { name: f.name, label: f.description }; @@ -143,22 +152,16 @@ export class EMSFileSource extends AbstractVectorSource { } canFormatFeatureProperties() { - return this._descriptor.tooltipProperties.length; + return this._tooltipFields.length > 0; } async filterAndFormatPropertiesToHtml(properties) { - const emsFileLayer = await this._getEMSFileLayer(); - const emsFields = emsFileLayer.getFieldsInLanguage(); - - return this._descriptor.tooltipProperties.map(propertyName => { - // Map EMS field name to language specific label - const emsField = emsFields.find(field => { - return field.name === propertyName; - }); - const label = emsField ? emsField.description : propertyName; - - return new TooltipProperty(propertyName, label, properties[propertyName]); + const tooltipProperties = this._tooltipFields.map(field => { + const value = properties[field.getName()]; + return field.createTooltipProperty(value); }); + + return Promise.all(tooltipProperties); } async getSupportedShapeTypes() { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js index d9f759bdcc2cd..15581f1cfbacb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js @@ -13,7 +13,7 @@ function makeEMSFileSource(tooltipProperties) { const emsFileSource = new EMSFileSource({ tooltipProperties: tooltipProperties }); - emsFileSource._getEMSFileLayer = () => { + emsFileSource.getEMSFileLayer = () => { return { getFieldsInLanguage() { return [{ diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js index ccd7592649e21..705f45a9d69b6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js @@ -8,12 +8,14 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { TooltipSelector } from '../../../components/tooltip_selector'; import { getEMSClient } from '../../../meta'; +import { EMSRegionLayerField } from '../../fields/ems_region_field'; export class UpdateSourceEditor extends Component { static propTypes = { onChange: PropTypes.func.isRequired, - tooltipProperties: PropTypes.arrayOf(PropTypes.string).isRequired + tooltipFields: PropTypes.arrayOf(PropTypes.object).isRequired, + source: PropTypes.object }; state = { @@ -36,16 +38,15 @@ export class UpdateSourceEditor extends Component { const emsFiles = await emsClient.getFileLayers(); const emsFile = emsFiles.find((emsFile => emsFile.getId() === this.props.layerId)); const emsFields = emsFile.getFieldsInLanguage(); - fields = emsFields.map(field => { - return { - name: field.name, - label: field.description - }; - }); + fields = emsFields.map(field => new EMSRegionLayerField({ + fieldName: field.name, + source: this.props.source + })); } catch(e) { //swallow this error. when a matching EMS-config cannot be found, the source already will have thrown errors during the data request. This will propagate to the vector-layer and be displayed in the UX fields = []; } + if (this._isMounted) { this.setState({ fields: fields }); } @@ -56,9 +57,14 @@ export class UpdateSourceEditor extends Component { }; render() { + + if (!this.state.fields) { + return null; + } + return ( diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js new file mode 100644 index 0000000000000..5b8e6e4574cd8 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js @@ -0,0 +1,128 @@ +/* + * 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 { AbstractESSource } from './es_source'; +import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property'; +import { ESAggMetricField } from '../fields/es_agg_field'; +import { ESDocField } from '../fields/es_doc_field'; +import { METRIC_TYPE } from '../../../common/constants'; + +const COUNT_PROP_LABEL = METRIC_TYPE.COUNT; +const COUNT_PROP_NAME = 'doc_count'; + +const AGG_DELIMITER = '_of_'; + +//todo: extract in separate PR +export class AbstractESAggSource extends AbstractESSource { + + static COUNT_PROP_LABEL = COUNT_PROP_LABEL; + static COUNT_PROP_NANE = COUNT_PROP_NAME; + + constructor(descriptor, inspectorAdapters) { + super(descriptor, inspectorAdapters); + this._metricFields = this._descriptor.metrics ? this._descriptor.metrics.map(metric => { + const esDocField = metric.field ? new ESDocField({ fieldName: metric.field, source: this }) : null; + return new ESAggMetricField({ + label: metric.label, + esDocField: esDocField, + aggType: metric.type, + source: this + }); + }) : []; + } + + createField({ fieldName, label }) { + if (fieldName === COUNT_PROP_NAME) { + return new ESAggMetricField({ + aggType: METRIC_TYPE.COUNT, + label: label, + source: this + }); + } else { + //this only works because aggType is a fixed set and does not include the `_of_` string + const [aggType, docField] = fieldName.split(AGG_DELIMITER); + const esDocField = new ESDocField({ fieldName: docField, source: this }); + return new ESAggMetricField({ + label: label, + esDocField, + aggType, + source: this + }); + } + } + + getMetricFieldForName(fieldName) { + return this._metricFields.find(metricField => { + return metricField.getName() === fieldName; + }); + } + + getMetricFields() { + const metrics = this._metricFields.filter(esAggField => esAggField.isValid()); + if (metrics.length === 0) { + metrics.push(new ESAggMetricField({ + aggType: METRIC_TYPE.COUNT, + source: this + })); + } + return metrics; + } + + formatMetricKey(aggType, fieldName) { + return aggType !== METRIC_TYPE.COUNT ? `${aggType}${AGG_DELIMITER}${fieldName}` : METRIC_TYPE.COUNT; + } + + formatMetricLabel(aggType, fieldName) { + return aggType !== METRIC_TYPE.COUNT ? `${aggType} of ${fieldName}` : METRIC_TYPE.COUNT; + } + + createMetricAggConfigs() { + return this.getMetricFields().map(esAggMetric => esAggMetric.makeMetricAggConfig()); + } + + + async getNumberFields() { + return this.getMetricFields().map(esAggMetricField => { + return { label: esAggMetricField.getPropertyLabel(), name: esAggMetricField.getName() }; + }); + } + + async filterAndFormatPropertiesToHtmlForMetricFields(properties) { + let indexPattern; + try { + indexPattern = await this.getIndexPattern(); + } catch(error) { + console.warn(`Unable to find Index pattern ${this._descriptor.indexPatternId}, values are not formatted`); + return properties; + } + + + const metricFields = this.getMetricFields(); + const tooltipProperties = []; + metricFields.forEach((metricField) => { + let value; + for (const key in properties) { + if (properties.hasOwnProperty(key) && metricField.getName() === key) { + value = properties[key]; + break; + } + } + + const tooltipProperty = new ESAggMetricTooltipProperty( + metricField.getName(), + metricField.getPropertyLabel(), + value, + indexPattern, + metricField + ); + tooltipProperties.push(tooltipProperty); + }); + + return tooltipProperties; + + } +} 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 776980e17bb13..12028a17ad334 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 @@ -8,15 +8,14 @@ import React from 'react'; import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; -import { AbstractESSource } from '../es_source'; import { HeatmapLayer } from '../../heatmap_layer'; import { VectorLayer } from '../../vector_layer'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; import { tabifyAggResponse } from 'ui/agg_response/tabify'; import { convertToGeoJson } from './convert_to_geojson'; -import { VectorStyle } from '../../styles/vector_style'; -import { vectorStyles } from '../../styles/vector_style_defaults'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { vectorStyles } from '../../styles/vector/vector_style_defaults'; import { RENDER_AS } from './render_as'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; @@ -24,9 +23,9 @@ import { GRID_RESOLUTION } from '../../grid_resolution'; import { SOURCE_DATA_ID_ORIGIN, ES_GEO_GRID, METRIC_TYPE } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; +import { AbstractESAggSource } from '../es_agg_source'; +import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; -const COUNT_PROP_LABEL = 'count'; -const COUNT_PROP_NAME = 'doc_count'; const MAX_GEOTILE_LEVEL = 29; const aggSchemas = new Schemas([ @@ -58,7 +57,7 @@ const aggSchemas = new Schemas([ } ]); -export class ESGeoGridSource extends AbstractESSource { +export class ESGeoGridSource extends AbstractESAggSource { static type = ES_GEO_GRID; static title = i18n.translate('xpack.maps.source.esGridTitle', { @@ -109,7 +108,7 @@ export class ESGeoGridSource extends AbstractESSource { async getImmutableProperties() { let indexPatternTitle = this._descriptor.indexPatternId; try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); indexPatternTitle = indexPattern.title; } catch (error) { // ignore error, title will just default to id @@ -141,9 +140,7 @@ export class ESGeoGridSource extends AbstractESSource { } getFieldNames() { - return this.getMetricFields().map(({ propertyKey }) => { - return propertyKey; - }); + return this.getMetricFields().map((esAggMetricField => esAggMetricField.getName())); } isGeoGridPrecisionAware() { @@ -184,14 +181,8 @@ export class ESGeoGridSource extends AbstractESSource { })); } - async getNumberFields() { - return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { - return { label, name }; - }); - } - async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const searchSource = await this._makeSearchSource(searchFilters, 0); const aggConfigs = new AggConfigs(indexPattern, this._makeAggConfigs(searchFilters.geogridPrecision), aggSchemas.all); searchSource.setField('aggs', aggConfigs.toDsl()); @@ -221,29 +212,8 @@ export class ESGeoGridSource extends AbstractESSource { return true; } - _formatMetricKey(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type}_of_${metric.field}` : COUNT_PROP_NAME; - } - - _formatMetricLabel(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type} of ${metric.field}` : COUNT_PROP_LABEL; - } - _makeAggConfigs(precision) { - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); - + const metricAggConfigs = this.createMetricAggConfigs(); return [ ...metricAggConfigs, { @@ -274,22 +244,22 @@ export class ESGeoGridSource extends AbstractESSource { }); descriptor.style = VectorStyle.createDescriptor({ [vectorStyles.FILL_COLOR]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { - label: COUNT_PROP_LABEL, - name: COUNT_PROP_NAME, + label: AbstractESAggSource.COUNT_PROP_LABEL, + name: AbstractESAggSource.COUNT_PROP_NANE, origin: SOURCE_DATA_ID_ORIGIN }, color: 'Blues' } }, [vectorStyles.ICON_SIZE]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { - label: COUNT_PROP_LABEL, - name: COUNT_PROP_NAME, + label: AbstractESAggSource.COUNT_PROP_LABEL, + name: AbstractESAggSource.COUNT_PROP_NANE, origin: SOURCE_DATA_ID_ORIGIN }, minSize: 4, 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 3debfdf1541f7..29be8eb4ba0c2 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 @@ -8,21 +8,20 @@ import React from 'react'; import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; -import { AbstractESSource } from '../es_source'; import { VectorLayer } from '../../vector_layer'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; -import { VectorStyle } from '../../styles/vector_style'; -import { vectorStyles } from '../../styles/vector_style_defaults'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { vectorStyles } from '../../styles/vector/vector_style_defaults'; import { i18n } from '@kbn/i18n'; import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW, METRIC_TYPE } from '../../../../common/constants'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { convertToLines } from './convert_to_lines'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; +import { AbstractESAggSource } from '../es_agg_source'; +import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; -const COUNT_PROP_LABEL = 'count'; -const COUNT_PROP_NAME = 'doc_count'; const MAX_GEOTILE_LEVEL = 29; const aggSchemas = new Schemas([ @@ -46,7 +45,7 @@ const aggSchemas = new Schemas([ } ]); -export class ESPewPewSource extends AbstractESSource { +export class ESPewPewSource extends AbstractESAggSource { static type = ES_PEW_PEW; static title = i18n.translate('xpack.maps.source.pewPewTitle', { @@ -103,12 +102,6 @@ export class ESPewPewSource extends AbstractESSource { return true; } - async getNumberFields() { - return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => { - return { label, name }; - }); - } - async getSupportedShapeTypes() { return [VECTOR_SHAPE_TYPES.LINE]; } @@ -116,7 +109,7 @@ export class ESPewPewSource extends AbstractESSource { async getImmutableProperties() { let indexPatternTitle = this._descriptor.indexPatternId; try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); indexPatternTitle = indexPattern.title; } catch (error) { // ignore error, title will just default to id @@ -150,22 +143,22 @@ export class ESPewPewSource extends AbstractESSource { createDefaultLayer(options) { const styleDescriptor = VectorStyle.createDescriptor({ [vectorStyles.LINE_COLOR]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { - label: COUNT_PROP_LABEL, - name: COUNT_PROP_NAME, + label: AbstractESAggSource.COUNT_PROP_LABEL, + name: AbstractESAggSource.COUNT_PROP_NANE, origin: SOURCE_DATA_ID_ORIGIN }, color: 'Blues' } }, [vectorStyles.LINE_WIDTH]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { - label: COUNT_PROP_LABEL, - name: COUNT_PROP_NAME, + label: AbstractESAggSource.COUNT_PROP_LABEL, + name: AbstractESAggSource.COUNT_PROP_NANE, origin: SOURCE_DATA_ID_ORIGIN }, minSize: 4, @@ -191,20 +184,9 @@ export class ESPewPewSource extends AbstractESSource { } async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) { - const indexPattern = await this._getIndexPattern(); - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); + + const metricAggConfigs = this.createMetricAggConfigs(); + const indexPattern = await this.getIndexPattern(); const aggConfigs = new AggConfigs(indexPattern, metricAggConfigs, aggSchemas.all); const searchSource = await this._makeSearchSource(searchFilters, 0); @@ -258,16 +240,8 @@ export class ESPewPewSource extends AbstractESSource { }; } - _formatMetricKey(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type}_of_${metric.field}` : COUNT_PROP_NAME; - } - - _formatMetricLabel(metric) { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type} of ${metric.field}` : COUNT_PROP_LABEL; - } - async _getGeoField() { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const geoField = indexPattern.fields.getByName(this._descriptor.destGeoField); if (!geoField) { throw new Error(i18n.translate('xpack.maps.source.esSource.noGeoFieldErrorMessage', { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap index c0e443758ada6..4bfa1ef79cc99 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap @@ -13,7 +13,7 @@ exports[`should enable sort order select when sort field provided 1`] = ` this.createField(property)); + } + + createField({ fieldName }) { + return new ESDocField({ + fieldName, + source: this + }); } renderSourceSettingsEditor({ onChange }) { return ( { return { name: field.name, label: field.name }; }); @@ -98,7 +108,7 @@ export class ESSearchSource extends AbstractESSource { async getDateFields() { try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); return indexPattern.fields.getByType('date').map(field => { return { name: field.name, label: field.name }; }); @@ -107,10 +117,6 @@ export class ESSearchSource extends AbstractESSource { } } - getMetricFields() { - return []; - } - getFieldNames() { return [this._descriptor.geoField]; } @@ -119,7 +125,7 @@ export class ESSearchSource extends AbstractESSource { let indexPatternTitle = this._descriptor.indexPatternId; let geoFieldType = ''; try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); indexPatternTitle = indexPattern.title; const geoField = await this._getGeoField(); geoFieldType = geoField.type; @@ -196,7 +202,7 @@ export class ESSearchSource extends AbstractESSource { topHitsSize, } = this._descriptor; - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const geoField = await this._getGeoField(); const scriptFields = {}; @@ -327,7 +333,7 @@ export class ESSearchSource extends AbstractESSource { ? await this._getTopHits(layerName, searchFilters, registerCancelCallback) : await this._getSearchHits(layerName, searchFilters, registerCancelCallback); - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const unusedMetaFields = indexPattern.metaFields.filter(metaField => { return !['_id', '_index'].includes(metaField); }); @@ -360,11 +366,11 @@ export class ESSearchSource extends AbstractESSource { } canFormatFeatureProperties() { - return this._descriptor.tooltipProperties.length > 0; + return this._tooltipFields.length > 0; } async _loadTooltipProperties(docId, index, indexPattern) { - if (this._descriptor.tooltipProperties.length === 0) { + if (this._tooltipFields.length === 0) { return {}; } @@ -376,7 +382,7 @@ export class ESSearchSource extends AbstractESSource { query: `_id:"${docId}" and _index:${index}` }; searchSource.setField('query', query); - searchSource.setField('fields', this._descriptor.tooltipProperties); + searchSource.setField('fields', this._getTooltipPropertyNames()); const resp = await searchSource.fetch(); @@ -392,7 +398,7 @@ export class ESSearchSource extends AbstractESSource { const properties = indexPattern.flattenHit(hit); indexPattern.metaFields.forEach(metaField => { - if (!this._descriptor.tooltipProperties.includes(metaField)) { + if (!this._getTooltipPropertyNames().includes(metaField)) { delete properties[metaField]; } }); @@ -400,12 +406,13 @@ export class ESSearchSource extends AbstractESSource { } async filterAndFormatPropertiesToHtml(properties) { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const propertyValues = await this._loadTooltipProperties(properties._id, properties._index, indexPattern); - - return this._descriptor.tooltipProperties.map(propertyName => { - return new ESTooltipProperty(propertyName, propertyName, propertyValues[propertyName], indexPattern); + const tooltipProperties = this._tooltipFields.map(field => { + const value = propertyValues[field.getName()]; + return field.createTooltipProperty(value); }); + return Promise.all(tooltipProperties); } isFilterByMapBounds() { @@ -413,7 +420,7 @@ export class ESSearchSource extends AbstractESSource { } async getLeftJoinFields() { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); // Left fields are retrieved from _source. return getSourceFields(indexPattern.fields) .map(field => { @@ -505,7 +512,6 @@ export class ESSearchSource extends AbstractESSource { async getPreIndexedShape(properties) { const geoField = await this._getGeoField(); - return { index: properties._index, // Can not use index pattern title because it may reference many indices id: properties._id, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js index 3678af4d54940..05bdfe701e356 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js @@ -21,18 +21,20 @@ import { i18n } from '@kbn/i18n'; import { getTermsFields, getSourceFields } from '../../../index_pattern_util'; import { ValidatedRange } from '../../../components/validated_range'; import { SORT_ORDER } from '../../../../common/constants'; +import { ESDocField } from '../../fields/es_doc_field'; export class UpdateSourceEditor extends Component { static propTypes = { indexPatternId: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, filterByMapBounds: PropTypes.bool.isRequired, - tooltipProperties: PropTypes.arrayOf(PropTypes.string).isRequired, + tooltipFields: PropTypes.arrayOf(PropTypes.object).isRequired, sortField: PropTypes.string, sortOrder: PropTypes.string.isRequired, useTopHits: PropTypes.bool.isRequired, topHitsSplitField: PropTypes.string, topHitsSize: PropTypes.number.isRequired, + source: PropTypes.object }; state = { @@ -72,10 +74,19 @@ export class UpdateSourceEditor extends Component { return; } + //todo move this all to the source + const rawTooltipFields = getSourceFields(indexPattern.fields); + const tooltipFields = rawTooltipFields.map(field => { + return new ESDocField({ + fieldName: field.name, + source: this.props.source + }); + }); + this.setState({ - tooltipFields: getSourceFields(indexPattern.fields), - termFields: getTermsFields(indexPattern.fields), - sortFields: indexPattern.fields.filter(field => field.sortable), + tooltipFields: tooltipFields, + termFields: getTermsFields(indexPattern.fields), //todo change term fields to use fields + sortFields: indexPattern.fields.filter(field => field.sortable), //todo change sort fields to use fields }); } _onTooltipPropertiesChange = propertyNames => { @@ -168,7 +179,7 @@ export class UpdateSourceEditor extends Component { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js index 9a3a74e0ed680..5a1b83589a1ee 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js @@ -15,7 +15,7 @@ const defaultProps = { indexPatternId: 'indexPattern1', onChange: () => {}, filterByMapBounds: true, - tooltipProperties: [], + tooltipFields: [], sortOrder: 'DESC', useTopHits: false, topHitsSplitField: 'trackId', 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 85c866479a6ba..5f72727f71897 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 @@ -15,11 +15,10 @@ import { timefilter } from 'ui/timefilter'; import _ from 'lodash'; import { AggConfigs } from 'ui/agg_types'; import { i18n } from '@kbn/i18n'; -import { ESAggMetricTooltipProperty } from '../tooltips/es_aggmetric_tooltip_property'; import uuid from 'uuid/v4'; import { copyPersistentState } from '../../reducers/util'; -import { ES_GEO_FIELD_TYPE, METRIC_TYPE } from '../../../common/constants'; +import { ES_GEO_FIELD_TYPE } from '../../../common/constants'; import { DataRequestAbortError } from '../util/data_request'; export class AbstractESSource extends AbstractVectorSource { @@ -57,81 +56,6 @@ export class AbstractESSource extends AbstractVectorSource { return clonedDescriptor; } - _getValidMetrics() { - const metrics = _.get(this._descriptor, 'metrics', []).filter(({ type, field }) => { - if (type === METRIC_TYPE.COUNT) { - return true; - } - - if (field) { - return true; - } - return false; - }); - if (metrics.length === 0) { - metrics.push({ type: METRIC_TYPE.COUNT }); - } - return metrics; - } - - _formatMetricKey() { - throw new Error('should implement'); - } - - _formatMetricLabel() { - throw new Error('should implement'); - } - - getMetricFields() { - return this._getValidMetrics().map(metric => { - const metricKey = this._formatMetricKey(metric); - const metricLabel = metric.label ? metric.label : this._formatMetricLabel(metric); - const metricCopy = { ...metric }; - delete metricCopy.label; - return { - ...metricCopy, - propertyKey: metricKey, - propertyLabel: metricLabel - }; - }); - } - - async filterAndFormatPropertiesToHtmlForMetricFields(properties) { - let indexPattern; - try { - indexPattern = await this._getIndexPattern(); - } catch(error) { - console.warn(`Unable to find Index pattern ${this._descriptor.indexPatternId}, values are not formatted`); - return properties; - } - - - const metricFields = this.getMetricFields(); - const tooltipProperties = []; - metricFields.forEach((metricField) => { - let value; - for (const key in properties) { - if (properties.hasOwnProperty(key) && metricField.propertyKey === key) { - value = properties[key]; - break; - } - } - - const tooltipProperty = new ESAggMetricTooltipProperty( - metricField.propertyKey, - metricField.propertyLabel, - value, - indexPattern, - metricField - ); - tooltipProperties.push(tooltipProperty); - }); - - return tooltipProperties; - - } - - async _runEsQuery(requestName, searchSource, registerCancelCallback, requestDescription) { const abortController = new AbortController(); registerCancelCallback(() => abortController.abort()); @@ -158,7 +82,7 @@ export class AbstractESSource extends AbstractVectorSource { } async _makeSearchSource(searchFilters, limit, initialSearchContext) { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const isTimeAware = await this.isTimeAware(); const applyGlobalQuery = _.get(searchFilters, 'applyGlobalQuery', true); const globalFilters = applyGlobalQuery ? searchFilters.filters : []; @@ -193,7 +117,7 @@ export class AbstractESSource extends AbstractVectorSource { const searchSource = await this._makeSearchSource({ sourceQuery, query, timeFilters, filters, applyGlobalQuery }, 0); const geoField = await this._getGeoField(); - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const geoBoundsAgg = [{ type: 'geo_bounds', @@ -234,7 +158,7 @@ export class AbstractESSource extends AbstractVectorSource { async isTimeAware() { try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const timeField = indexPattern.timeFieldName; return !!timeField; } catch (error) { @@ -242,7 +166,7 @@ export class AbstractESSource extends AbstractVectorSource { } } - async _getIndexPattern() { + async getIndexPattern() { if (this.indexPattern) { return this.indexPattern; } @@ -271,7 +195,7 @@ export class AbstractESSource extends AbstractVectorSource { async _getGeoField() { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const geoField = indexPattern.fields.getByName(this._descriptor.geoField); if (!geoField) { throw new Error(i18n.translate('xpack.maps.source.esSource.noGeoFieldErrorMessage', { @@ -284,7 +208,7 @@ export class AbstractESSource extends AbstractVectorSource { async getDisplayName() { try { - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); return indexPattern.title; } catch (error) { // Unable to load index pattern, just return id as display name @@ -300,31 +224,31 @@ export class AbstractESSource extends AbstractVectorSource { return this._descriptor.id; } - async getFieldFormatter(fieldName) { - const metricField = this.getMetricFields().find(({ propertyKey }) => { - return propertyKey === fieldName; - }); + _getRawFieldName(fieldName) { + const metricField = this.getMetricFields().find((esAggMetricField) => esAggMetricField.getName() === fieldName); + return metricField ? metricField.getESDocFieldName() : null; + } - // Do not use field formatters for counting metrics - if (metricField && metricField.type === METRIC_TYPE.COUNT || metricField.type === METRIC_TYPE.UNIQUE_COUNT) { + async getFieldFormatter(fieldName) { + // fieldName could be an aggregation so it needs to be unpacked to expose raw field. + const rawFieldName = this._getRawFieldName(fieldName); + if (!rawFieldName) { return null; } let indexPattern; try { - indexPattern = await this._getIndexPattern(); + indexPattern = await this.getIndexPattern(); } catch(error) { return null; } - const realFieldName = metricField - ? metricField.field - : fieldName; - const fieldFromIndexPattern = indexPattern.fields.getByName(realFieldName); + const fieldFromIndexPattern = indexPattern.fields.getByName(rawFieldName); if (!fieldFromIndexPattern) { return null; } return fieldFromIndexPattern.format.getConverterFor('text'); } + } 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 1f5adc00cca6f..37af2ea4a97d2 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 @@ -6,15 +6,18 @@ import _ from 'lodash'; -import { AbstractESSource } from './es_source'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; import { i18n } from '@kbn/i18n'; -import { ESTooltipProperty } from '../tooltips/es_tooltip_property'; import { ES_SIZE_LIMIT, METRIC_TYPE } from '../../../common/constants'; +import { ESDocField } from '../fields/es_doc_field'; +import { AbstractESAggSource } from './es_agg_source'; const TERMS_AGG_NAME = 'join'; +const FIELD_NAME_PREFIX = '__kbnjoin__'; +const GROUP_BY_DELIMITER = '_groupby_'; + const aggSchemas = new Schemas([ { group: 'metrics', @@ -61,16 +64,24 @@ export function extractPropertiesMap(rawEsData, propertyNames, countPropertyName return propertiesMap; } -export class ESTermSource extends AbstractESSource { +export class ESTermSource extends AbstractESAggSource { static type = 'ES_TERM_SOURCE'; + constructor(descriptor, inspectorAdapters) { + super(descriptor, inspectorAdapters); + this._termField = new ESDocField({ fieldName: descriptor.term, source: this }); + } static renderEditor({}) { //no need to localize. this editor is never rendered. return `
editor details
`; } + createField() { + throw new Error('Not implemented. Should retrieve corresponding field from the inner_join.metrics config.'); + } + hasCompleteConfig() { return (_.has(this._descriptor, 'indexPatternId') && _.has(this._descriptor, 'term')); } @@ -79,22 +90,22 @@ export class ESTermSource extends AbstractESSource { return [this._descriptor.indexPatternId]; } - getTerm() { - return this._descriptor.term; + getTermField() { + return this._termField; } getWhereQuery() { return this._descriptor.whereQuery; } - _formatMetricKey(metric) { - const metricKey = metric.type !== METRIC_TYPE.COUNT ? `${metric.type}_of_${metric.field}` : metric.type; - return `__kbnjoin__${metricKey}_groupby_${this._descriptor.indexPatternTitle}.${this._descriptor.term}`; + formatMetricKey(aggType, fieldName) { + const metricKey = aggType !== METRIC_TYPE.COUNT ? `${aggType}_of_${fieldName}` : aggType; + return `${FIELD_NAME_PREFIX}${metricKey}${GROUP_BY_DELIMITER}${this._descriptor.indexPatternTitle}.${this._termField.getName()}`; } - _formatMetricLabel(metric) { - const metricLabel = metric.type !== METRIC_TYPE.COUNT ? `${metric.type} ${metric.field}` : 'count'; - return `${metricLabel} of ${this._descriptor.indexPatternTitle}:${this._descriptor.term}`; + formatMetricLabel(type, fieldName) { + const metricLabel = type !== METRIC_TYPE.COUNT ? `${type} ${fieldName}` : 'count'; + return `${metricLabel} of ${this._descriptor.indexPatternTitle}:${this._termField.getName()}`; } async getPropertiesMap(searchFilters, leftSourceName, leftFieldName, registerCancelCallback) { @@ -103,13 +114,13 @@ export class ESTermSource extends AbstractESSource { return []; } - const indexPattern = await this._getIndexPattern(); + const indexPattern = await this.getIndexPattern(); const searchSource = await this._makeSearchSource(searchFilters, 0); const configStates = this._makeAggConfigs(); const aggConfigs = new AggConfigs(indexPattern, configStates, aggSchemas.all); searchSource.setField('aggs', aggConfigs.toDsl()); - const requestName = `${this._descriptor.indexPatternTitle}.${this._descriptor.term}`; + const requestName = `${this._descriptor.indexPatternTitle}.${this._termField.getName()}`; const requestDesc = this._getRequestDescription(leftSourceName, leftFieldName); const rawEsData = await this._runEsQuery(requestName, searchSource, registerCancelCallback, requestDesc); @@ -134,15 +145,13 @@ export class ESTermSource extends AbstractESSource { } _getRequestDescription(leftSourceName, leftFieldName) { - const metrics = this._getValidMetrics().map(metric => { - return metric.type !== METRIC_TYPE.COUNT ? `${metric.type} ${metric.field}` : 'count'; - }); + const metrics = this.getMetricFields().map(esAggMetric => esAggMetric.getRequestDescription()); const joinStatement = []; joinStatement.push(i18n.translate('xpack.maps.source.esJoin.joinLeftDescription', { defaultMessage: `Join {leftSourceName}:{leftFieldName} with`, values: { leftSourceName, leftFieldName } })); - joinStatement.push(`${this._descriptor.indexPatternTitle}:${this._descriptor.term}`); + joinStatement.push(`${this._descriptor.indexPatternTitle}:${this._termField.getName()}`); joinStatement.push(i18n.translate('xpack.maps.source.esJoin.joinMetricsDescription', { defaultMessage: `for metrics {metrics}`, values: { metrics: metrics.join(',') } @@ -156,20 +165,7 @@ export class ESTermSource extends AbstractESSource { } _makeAggConfigs() { - const metricAggConfigs = this.getMetricFields().map(metric => { - const metricAggConfig = { - id: metric.propertyKey, - enabled: true, - type: metric.type, - schema: 'metric', - params: {} - }; - if (metric.type !== METRIC_TYPE.COUNT) { - metricAggConfig.params = { field: metric.field }; - } - return metricAggConfig; - }); - + const metricAggConfigs = this.createMetricAggConfigs(); return [ ...metricAggConfigs, { @@ -178,7 +174,7 @@ export class ESTermSource extends AbstractESSource { type: 'terms', schema: 'segment', params: { - field: this._descriptor.term, + field: this._termField.getName(), size: ES_SIZE_LIMIT } } @@ -194,21 +190,7 @@ export class ESTermSource extends AbstractESSource { return await this.filterAndFormatPropertiesToHtmlForMetricFields(properties); } - async createESTooltipProperty(propertyName, rawValue) { - try { - const indexPattern = await this._getIndexPattern(); - if (!indexPattern) { - return null; - } - return new ESTooltipProperty(propertyName, propertyName, rawValue, indexPattern); - } catch (e) { - return null; - } - } - getFieldNames() { - return this.getMetricFields().map(({ propertyKey }) => { - return propertyKey; - }); + return this.getMetricFields().map(esAggMetricField => esAggMetricField.getName()); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index b3e8c1b019f28..e8f0829cfd8b8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -7,7 +7,7 @@ import { VectorLayer } from '../vector_layer'; import { TooltipProperty } from '../tooltips/tooltip_property'; -import { VectorStyle } from '../styles/vector_style'; +import { VectorStyle } from '../styles/vector/vector_style'; import { AbstractSource } from './source'; import * as topojson from 'topojson-client'; import _ from 'lodash'; @@ -48,6 +48,10 @@ export class AbstractVectorSource extends AbstractSource { })); } + createField() { + throw new Error(`Should implemement ${this.constructor.type} ${this}`); + } + _createDefaultLayerDescriptor(options, mapColors) { return VectorLayer.createDescriptor( { @@ -57,6 +61,10 @@ export class AbstractVectorSource extends AbstractSource { mapColors); } + _getTooltipPropertyNames() { + return this._tooltipFields.map(field => field.getName()); + } + createDefaultLayer(options, mapColors) { const layerDescriptor = this._createDefaultLayerDescriptor(options, mapColors); const style = new VectorStyle(layerDescriptor.style, this); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/_color_gradient.scss b/x-pack/legacy/plugins/maps/public/layers/styles/_color_gradient.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/_color_gradient.scss rename to x-pack/legacy/plugins/maps/public/layers/styles/_color_gradient.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss index 87c7bd1face64..971f92fe76152 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss +++ b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss @@ -1,3 +1,3 @@ -@import './components/color_gradient'; -@import './components/static_dynamic_style_row'; -@import './components/vector/color/color_stops'; +@import './color_gradient'; +@import 'vector/components/static_dynamic_style_row'; +@import 'vector/components/vector/color/color_stops'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/color_gradient.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_gradient.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/color_gradient.js rename to x-pack/legacy/plugins/maps/public/layers/styles/color_gradient.js index b416d869e592b..d5a75586eb80a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/color_gradient.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_gradient.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { COLOR_RAMP_NAMES, getRGBColorRangeStrings, getLinearGradient } from '../color_utils'; +import { COLOR_RAMP_NAMES, getRGBColorRangeStrings, getLinearGradient } from './color_utils'; import classNames from 'classnames'; export const ColorGradient = ({ colorRamp, colorRampName, className }) => { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js index 78a7c9d51739a..9e96b25645ada 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js @@ -8,7 +8,7 @@ import React from 'react'; import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; import { getLegendColors, getColor } from 'ui/vis/map/color_util'; -import { ColorGradient } from './components/color_gradient'; +import { ColorGradient } from './color_gradient'; import { palettes } from '@elastic/eui/lib/services'; import tinycolor from 'tinycolor2'; import chroma from 'chroma-js'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/__snapshots__/heatmap_style_editor.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/__snapshots__/heatmap_style_editor.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_constants.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_constants.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js index 0aa20f29c341b..f461bb580b281 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js @@ -8,7 +8,7 @@ import React from 'react'; import { EuiFormRow, EuiSuperSelect } from '@elastic/eui'; import { COLOR_GRADIENTS } from '../../color_utils'; -import { ColorGradient } from '../color_gradient'; +import { ColorGradient } from '../../color_gradient'; import { DEFAULT_RGB_HEATMAP_COLOR_RAMP, DEFAULT_HEATMAP_COLOR_RAMP_NAME, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/heatmap_style_editor.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js similarity index 88% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js index 74fce11abf0a6..dddc4f88e9bee 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/heatmap/legend/heatmap_legend.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js @@ -7,8 +7,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ColorGradient } from '../../color_gradient'; -import { StyleLegendRow } from '../../style_legend_row'; +import { ColorGradient } from '../../../color_gradient'; +import { StyleLegendRow } from '../../../vector/components/style_legend_row'; import { DEFAULT_RGB_HEATMAP_COLOR_RAMP, DEFAULT_HEATMAP_COLOR_RAMP_NAME, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js similarity index 90% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js rename to x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js index 5ccabe610a120..e537da8a3e2e4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js @@ -5,12 +5,12 @@ */ import React from 'react'; -import { GRID_RESOLUTION } from '../grid_resolution'; -import { AbstractStyle } from './abstract_style'; -import { HeatmapStyleEditor } from './components/heatmap/heatmap_style_editor'; -import { HeatmapLegend } from './components/heatmap/legend/heatmap_legend'; -import { DEFAULT_HEATMAP_COLOR_RAMP_NAME } from './components/heatmap/heatmap_constants'; -import { getColorRampStops } from './color_utils'; +import { GRID_RESOLUTION } from '../../grid_resolution'; +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 { getColorRampStops } from '../color_utils'; import { i18n } from '@kbn/i18n'; import { EuiIcon } from '@elastic/eui'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/_static_dynamic_style_row.scss b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_static_dynamic_style_row.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/_static_dynamic_style_row.scss rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_static_dynamic_style_row.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js index cfe7a0741a194..6eb6b0ab23a3c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/static_dynamic_style_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js @@ -5,7 +5,8 @@ */ import React from 'react'; -import { VectorStyle } from '../vector_style'; +import { DynamicStyleProperty } from '../properties/dynamic_style_property'; +import { StaticStyleProperty } from '../properties/static_style_property'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -25,7 +26,7 @@ export class StaticDynamicStyleRow extends React.Component { if (!this.props.styleDescriptor) { return false; } - return this.props.styleDescriptor.type === VectorStyle.STYLE_TYPE.DYNAMIC; + return this.props.styleDescriptor.type === DynamicStyleProperty.type; } _getStyleOptions() { @@ -34,7 +35,7 @@ export class StaticDynamicStyleRow extends React.Component { _onStaticStyleChange = options => { const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options, }; this.props.handlePropertyChange(this.props.property, styleDescriptor); @@ -42,7 +43,7 @@ export class StaticDynamicStyleRow extends React.Component { _onDynamicStyleChange = options => { const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options, }; this.props.handlePropertyChange(this.props.property, styleDescriptor); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/style_legend_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_legend_row.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/style_legend_row.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_legend_row.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/__snapshots__/vector_style_symbol_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/_color_stops.scss b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/_color_stops.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/_color_stops.scss rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/_color_stops.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_ramp_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_ramp_select.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_ramp_select.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_ramp_select.js index c2dd51a0182e3..5fc535b764d51 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_ramp_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_ramp_select.js @@ -8,7 +8,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiSuperSelect, EuiSpacer } from '@elastic/eui'; -import { COLOR_GRADIENTS } from '../../../color_utils'; +import { COLOR_GRADIENTS } from '../../../../color_utils'; import { FormattedMessage } from '@kbn/i18n/react'; import { ColorStops } from './color_stops'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_stops.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_stops.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_stops_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/color_stops_utils.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/color_stops_utils.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/dynamic_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/dynamic_color_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/dynamic_color_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/dynamic_color_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/static_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/static_color_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/static_color_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/static_color_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/vector_style_color_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/vector_style_color_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/color/vector_style_color_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/color/vector_style_color_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/field_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/field_select.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/field_select.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/field_select.js index 1d8f4e13fdd1a..ce50978a9dc5c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/field_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/field_select.js @@ -8,7 +8,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { EuiComboBox } from '@elastic/eui'; -import { SOURCE_DATA_ID_ORIGIN } from '../../../../../common/constants'; +import { SOURCE_DATA_ID_ORIGIN } from '../../../../../../common/constants'; import { i18n } from '@kbn/i18n'; export function FieldSelect({ fields, selectedFieldName, onChange, ...rest }) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/get_vector_style_label.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/get_vector_style_label.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/get_vector_style_label.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/get_vector_style_label.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/__snapshots__/vector_icon.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/__snapshots__/vector_icon.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/__snapshots__/vector_icon.test.js.snap rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/__snapshots__/vector_icon.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/circle_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/circle_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/circle_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/circle_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/line_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/line_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/line_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/line_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/polygon_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/polygon_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/polygon_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/polygon_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/style_property_legend_row.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/style_property_legend_row.js index 2f8a603c290ab..ad2156bd20edc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/style_property_legend_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/style_property_legend_row.js @@ -9,8 +9,8 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { styleOptionShapes, rangeShape } from '../style_option_shapes'; -import { VectorStyle } from '../../../vector_style'; -import { ColorGradient } from '../../color_gradient'; +import { StaticStyleProperty } from '../../../properties/static_style_property'; +import { ColorGradient } from '../../../../color_gradient'; import { CircleIcon } from './circle_icon'; import { getVectorStyleLabel } from '../get_vector_style_label'; import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; @@ -121,7 +121,7 @@ export class StylePropertyLegendRow extends Component { } _isStatic() { - return this.props.type === VectorStyle.STYLE_TYPE.STATIC || + return this.props.type === StaticStyleProperty.type || !this.props.options.field || !this.props.options.field.name; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/symbol_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/symbol_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/symbol_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/symbol_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.js similarity index 93% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.js index 0f9e76c3e74d9..bd9f8356c0ef9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.js @@ -12,8 +12,8 @@ import { CircleIcon } from './circle_icon'; import { LineIcon } from './line_icon'; import { PolygonIcon } from './polygon_icon'; import { SymbolIcon } from './symbol_icon'; -import { VectorStyle } from '../../../vector_style'; -import { getColorRampCenterColor } from '../../../color_utils'; +import { StaticStyleProperty } from '../../../properties/static_style_property'; +import { getColorRampCenterColor } from '../../../../color_utils'; export class VectorIcon extends Component { @@ -87,7 +87,7 @@ function extractColorFromStyleProperty(colorStyleProperty, defaultColor) { return defaultColor; } - if (colorStyleProperty.type === VectorStyle.STYLE_TYPE.STATIC) { + if (colorStyleProperty.type === StaticStyleProperty.type) { return colorStyleProperty.options.color; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.test.js similarity index 91% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.test.js index 221b6268c55df..4c773b35cced3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_icon.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_icon.test.js @@ -8,7 +8,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { VectorIcon } from './vector_icon'; -import { VectorStyle } from '../../../vector_style'; +import { StaticStyleProperty } from '../../../properties/static_style_property'; +import { DynamicStyleProperty } from '../../../properties/dynamic_style_property'; let isPointsOnly = false; let isLinesOnly = false; @@ -16,13 +17,13 @@ const defaultProps = { loadIsPointsOnly: () => { return isPointsOnly; }, loadIsLinesOnly: () => { return isLinesOnly; }, fillColor: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { color: '#ff0000', } }, lineColor: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { color: 'Blues', field: { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_style_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_style_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/legend/vector_style_legend.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/legend/vector_style_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/dynamic_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/dynamic_orientation_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/dynamic_orientation_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/dynamic_orientation_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/orientation_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/orientation_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/orientation_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/orientation_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/static_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/static_orientation_selection.js similarity index 91% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/static_orientation_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/static_orientation_selection.js index b5529c6987459..5e32ed966d360 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/orientation/static_orientation_selection.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/orientation/static_orientation_selection.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { staticOrientationShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; +import { ValidatedRange } from '../../../../../../components/validated_range'; export function StaticOrientationSelection({ onChange, styleOptions }) { const onOrientationChange = orientation => { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/dynamic_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/dynamic_size_selection.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/dynamic_size_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/dynamic_size_selection.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/size_range_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/size_range_selector.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/size_range_selector.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/size_range_selector.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/static_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/static_size_selection.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/static_size_selection.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/static_size_selection.js index 38f8fe53d1748..0168a3ef68d9d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/static_size_selection.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/static_size_selection.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { staticSizeShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; +import { ValidatedRange } from '../../../../../../components/validated_range'; import { i18n } from '@kbn/i18n'; export function StaticSizeSelection({ onChange, styleOptions }) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/vector_style_size_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/vector_style_size_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/size/vector_style_size_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/size/vector_style_size_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/style_option_shapes.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/style_option_shapes.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/style_option_shapes.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/style_option_shapes.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_editor.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_editor.js index 83d4ff7c11d66..48404f6fbeafb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_editor.js @@ -17,8 +17,8 @@ import { getDefaultStaticProperties, vectorStyles, } from '../../vector_style_defaults'; -import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; -import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types'; +import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../../color_utils'; +import { VECTOR_SHAPE_TYPES } from '../../../../sources/vector_feature_types'; import { SYMBOLIZE_AS_ICON } from '../../vector_constants'; import { i18n } from '@kbn/i18n'; import { SYMBOL_OPTIONS } from '../../symbol_utils'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_symbol_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_symbol_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_symbol_editor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/vector/vector_style_symbol_editor.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector/vector_style_symbol_editor.test.js 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 new file mode 100644 index 0000000000000..eda9242fe53dd --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -0,0 +1,117 @@ +/* + * 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 { DynamicStyleProperty } from './dynamic_style_property'; +import _ from 'lodash'; +import { getComputedFieldName } from '../style_util'; +import { getColorRampStops } from '../../color_utils'; + + +export class DynamicColorProperty extends DynamicStyleProperty { + + + syncCircleColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'circle-color', color); + mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); + } + + syncIconColorWithMb(mbLayerId, mbMap) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'icon-color', color); + } + + syncHaloBorderColorWithMb(mbLayerId, mbMap) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', color); + } + + syncCircleStrokeWithMb(pointLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', color); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); + } + + syncFillColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'fill-color', color); + mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); + } + + syncLineColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'line-color', color); + mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); + } + + isCustomColorRamp() { + return !!this._options.customColorRamp; + } + + supportsFeatureState() { + return true; + } + + isScaled() { + return !this.isCustomColorRamp(); + } + + _getMbColor() { + const isDynamicConfigComplete = _.has(this._options, 'field.name') && _.has(this._options, 'color'); + if (!isDynamicConfigComplete) { + return null; + } + + if (this._options.useCustomColorRamp && (!this._options.customColorRamp || !this._options.customColorRamp.length)) { + return null; + } + + return this._getMBDataDrivenColor({ + targetName: getComputedFieldName(this._styleName, this._options.field.name), + colorStops: this._getMBColorStops(), + isSteps: this._options.useCustomColorRamp, + }); + } + + _getMBDataDrivenColor({ targetName, colorStops, isSteps }) { + if (isSteps) { + const firstStopValue = colorStops[0]; + const lessThenFirstStopValue = firstStopValue - 1; + return [ + 'step', + ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], + 'rgba(0,0,0,0)', // MB will assign the base value to any features that is below the first stop value + ...colorStops + ]; + } + + return [ + 'interpolate', + ['linear'], + ['coalesce', ['feature-state', targetName], -1], + -1, 'rgba(0,0,0,0)', + ...colorStops + ]; + } + + + _getMBColorStops() { + + if (this._options.useCustomColorRamp) { + return this._options.customColorRamp.reduce((accumulatedStops, nextStop) => { + return [...accumulatedStops, nextStop.stop, nextStop.color]; + }, []); + } + + return getColorRampStops(this._options.color); + } + +} + + + + 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 new file mode 100644 index 0000000000000..fb4ffd8cce4b4 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js @@ -0,0 +1,37 @@ +/* + * 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 { DynamicStyleProperty } from './dynamic_style_property'; +import { getComputedFieldName } from '../style_util'; +import { vectorStyles } 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); + // 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 { + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', 0); + } + } + + supportsFeatureState() { + return false; + } + + isScaled() { + return false; + } + +} + + + + 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 new file mode 100644 index 0000000000000..cfd46ed20823a --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js @@ -0,0 +1,84 @@ +/* + * 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 { 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 _ from 'lodash'; + +export class DynamicSizeProperty extends DynamicStyleProperty { + + syncHaloWidthWithMb(mbLayerId, mbMap) { + const haloWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', haloWidth); + } + + + syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId) { + if (this._isSizeDynamicConfigComplete(this._options)) { + const iconPixels = this._options.maxSize >= HALF_LARGE_MAKI_ICON_SIZE + ? LARGE_MAKI_ICON_SIZE + : SMALL_MAKI_ICON_SIZE; + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); + + const halfIconPixels = iconPixels / 2; + const targetName = getComputedFieldName(vectorStyles.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', + ['linear'], + ['coalesce', ['get', targetName], 0], + 0, this._options.minSize / halfIconPixels, + 1, this._options.maxSize / halfIconPixels + ]); + } else { + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', null); + mbMap.setLayoutProperty(symbolLayerId, 'icon-size', null); + } + } + + syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { + const lineWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', lineWidth); + } + + syncCircleRadiusWithMb(mbLayerId, mbMap) { + const circleRadius = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'circle-radius', circleRadius); + } + + syncLineWidthWithMb(mbLayerId, mbMap) { + const lineWidth = this._getMbSize(); + mbMap.setPaintProperty(mbLayerId, 'line-width', lineWidth); + } + + _getMbSize() { + if (this._isSizeDynamicConfigComplete(this._options)) { + return this._getMbDataDrivenSize({ + targetName: getComputedFieldName(this._styleName, this._options.field.name), + minSize: this._options.minSize, + maxSize: this._options.maxSize, + }); + } + return null; + } + + _getMbDataDrivenSize({ targetName, minSize, maxSize }) { + return [ + 'interpolate', + ['linear'], + ['coalesce', ['feature-state', targetName], 0], + 0, minSize, + 1, maxSize + ]; + } + + _isSizeDynamicConfigComplete() { + return this._field.isValid() && _.has(this._options, 'minSize') && _.has(this._options, 'maxSize'); + } +} 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 new file mode 100644 index 0000000000000..2110d9a1e1bb4 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -0,0 +1,37 @@ +/* + * 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 { AbstractStyleProperty } from './style_property'; + +export class DynamicStyleProperty extends AbstractStyleProperty { + static type = 'DYNAMIC'; + + constructor(options, styleName, field) { + super(options, styleName); + this._field = field; + } + + getField() { + return this._field; + } + + isDynamic() { + return !!this._field; + } + + getFieldOrigin() { + return this._options.field.origin; + } + + supportsFeatureState() { + return true; + } + + isScaled() { + return true; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js new file mode 100644 index 0000000000000..d4c44fca1bd08 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js @@ -0,0 +1,41 @@ +/* + * 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 { StaticStyleProperty } from './static_style_property'; + + +export class StaticColorProperty extends StaticStyleProperty { + + syncCircleColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'circle-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); + } + + syncFillColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'fill-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); + } + + syncIconColorWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-color', this._options.color); + } + + syncHaloBorderColorWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', this._options.color); + } + + syncLineColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'line-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); + } + + syncCircleStrokeWithMb(pointLayerId, mbMap, alpha) { + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', this._options.color); + mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js new file mode 100644 index 0000000000000..48a7bbd3f79df --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js @@ -0,0 +1,26 @@ +/* + * 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 { StaticStyleProperty } from './static_style_property'; + + +export class StaticOrientationProperty extends StaticStyleProperty { + + constructor(options, styleName) { + if (typeof options.orientation !== 'number') { + super({ orientation: 0 }, styleName); + } else { + super(options, styleName); + } + } + + syncIconRotationWithMb(symbolLayerId, mbMap) { + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', this._options.orientation); + } + + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js new file mode 100644 index 0000000000000..37162d8cb0a3c --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js @@ -0,0 +1,46 @@ +/* + * 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 { StaticStyleProperty } from './static_style_property'; +import { HALF_LARGE_MAKI_ICON_SIZE, LARGE_MAKI_ICON_SIZE, SMALL_MAKI_ICON_SIZE } from '../symbol_utils'; + + +export class StaticSizeProperty extends StaticStyleProperty { + + constructor(options, styleName) { + if (typeof options.size !== 'number') { + super({ size: 1 }, styleName); + } else { + super(options, styleName); + } + } + + syncHaloWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', this._options.size); + } + + syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId) { + const iconPixels = this._size >= HALF_LARGE_MAKI_ICON_SIZE ? LARGE_MAKI_ICON_SIZE : SMALL_MAKI_ICON_SIZE; + mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); + const halfIconPixels = iconPixels / 2; + mbMap.setLayoutProperty(symbolLayerId, 'icon-size', this._options.size / halfIconPixels); + } + + syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', this._options.size); + } + + syncCircleRadiusWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'circle-radius', this._options.size); + } + + syncLineWidthWithMb(mbLayerId, mbMap) { + mbMap.setPaintProperty(mbLayerId, 'line-width', this._options.size); + } + + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js new file mode 100644 index 0000000000000..448efc06899e5 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js @@ -0,0 +1,13 @@ +/* + * 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 { AbstractStyleProperty } from './style_property'; + +export class StaticStyleProperty extends AbstractStyleProperty { + static type = 'STATIC'; + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js new file mode 100644 index 0000000000000..302fbb10d00b2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js @@ -0,0 +1,28 @@ +/* + * 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. + */ + + + + + +export class AbstractStyleProperty { + + constructor(options, styleName) { + this._options = options; + this._styleName = styleName; + } + + isDynamic() { + return false; + } + + getStyleName() { + return this._styleName; + } + getOptions() { + return this._options || {}; + } +} 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 new file mode 100644 index 0000000000000..69caaca080138 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js @@ -0,0 +1,14 @@ +/* + * 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. + */ + + +export function getComputedFieldName(styleName, fieldName) { + return `${getComputedFieldNamePrefix(fieldName)}__${styleName}`; +} + +export function getComputedFieldNamePrefix(fieldName) { + return `__kbn__dynamic__${fieldName}`; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js index 22e1a6aea6a72..967d0e0bbd096 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js @@ -6,7 +6,7 @@ import maki from '@elastic/maki'; import xml2js from 'xml2js'; -import { parseXmlString } from '../../../common/parse_xml_string'; +import { parseXmlString } from '../../../../common/parse_xml_string'; export const LARGE_MAKI_ICON_SIZE = 15; const LARGE_MAKI_ICON_SIZE_AS_STRING = LARGE_MAKI_ICON_SIZE.toString(); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/symbol_utils.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_constants.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_constants.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_constants.js 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 new file mode 100644 index 0000000000000..55a52213aacbc --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -0,0 +1,534 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { VectorStyleEditor } from './components/vector/vector_style_editor'; +import { getDefaultProperties, vectorStyles } from './vector_style_defaults'; +import { AbstractStyle } from '../abstract_style'; +import { SOURCE_DATA_ID_ORIGIN, GEO_JSON_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; +import { VectorIcon } from './components/vector/legend/vector_icon'; +import { VectorStyleLegend } from './components/vector/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 { DynamicStyleProperty } from './properties/dynamic_style_property'; +import { StaticStyleProperty } from './properties/static_style_property'; +import { DynamicSizeProperty } from './properties/dynamic_size_property'; +import { StaticSizeProperty } from './properties/static_size_property'; +import { getComputedFieldName } from './style_util'; +import { StaticColorProperty } from './properties/static_color_property'; +import { DynamicColorProperty } from './properties/dynamic_color_property'; +import { StaticOrientationProperty } from './properties/static_orientation_property'; +import { DynamicOrientationProperty } from './properties/dynamic_orientation_property'; + +const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT]; +const LINES = [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING]; +const POLYGONS = [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON]; + +export class VectorStyle extends AbstractStyle { + + static type = 'VECTOR'; + + static createDescriptor(properties = {}) { + return { + type: VectorStyle.type, + properties: { ...getDefaultProperties(), ...properties } + }; + } + + static createDefaultStyleProperties(mapColors) { + return getDefaultProperties(mapColors); + } + + static getDisplayName() { + return i18n.translate('xpack.maps.style.vector.displayNameLabel', { + defaultMessage: 'Vector style' + }); + } + + static description = ''; + + + constructor(descriptor = {}, source, layer) { + super(); + this._source = source; + this._layer = layer; + this._descriptor = { + ...descriptor, + ...VectorStyle.createDescriptor(descriptor.properties), + }; + + this._lineColorStyleProperty = this._makeStyleProperty(vectorStyles.LINE_COLOR, this._descriptor.properties[vectorStyles.LINE_COLOR]); + this._fillColorStyleProperty = this._makeStyleProperty(vectorStyles.FILL_COLOR, this._descriptor.properties[vectorStyles.FILL_COLOR]); + this._lineWidthStyleProperty = this._makeStyleProperty(vectorStyles.LINE_WIDTH, this._descriptor.properties[vectorStyles.LINE_WIDTH]); + this._iconSizeStyleProperty = this._makeStyleProperty(vectorStyles.ICON_SIZE, this._descriptor.properties[vectorStyles.ICON_SIZE]); + // eslint-disable-next-line max-len + this._iconOrientationProperty = this._makeStyleProperty(vectorStyles.ICON_ORIENTATION, this._descriptor.properties[vectorStyles.ICON_ORIENTATION]); + } + + _getAllStyleProperties() { + return [ + this._lineColorStyleProperty, + this._fillColorStyleProperty, + this._lineWidthStyleProperty, + this._iconSizeStyleProperty, + this._iconOrientationProperty + ]; + } + + renderEditor({ layer, onStyleDescriptorChange }) { + const styleProperties = { ...this.getRawProperties() }; + const handlePropertyChange = (propertyName, settings) => { + styleProperties[propertyName] = settings;//override single property, but preserve the rest + const vectorStyleDescriptor = VectorStyle.createDescriptor(styleProperties); + onStyleDescriptorChange(vectorStyleDescriptor); + }; + + return ( + + ); + } + + /* + * Changes to source descriptor and join descriptor will impact style properties. + * For instance, a style property may be dynamically tied to the value of an ordinal field defined + * by a join or a metric aggregation. The metric aggregation or join may be edited or removed. + * When this happens, the style will be linked to a no-longer-existing ordinal field. + * This method provides a way for a style to clean itself and return a descriptor that unsets any dynamic + * properties that are tied to missing oridinal fields + * + * This method does not update its descriptor. It just returns a new descriptor that the caller + * can then use to update store state via dispatch. + */ + getDescriptorWithMissingStylePropsRemoved(nextOrdinalFields) { + const originalProperties = this.getRawProperties(); + const updatedProperties = {}; + + + this.getDynamicPropertiesArray().forEach(dynamicProperty =>{ + + const field = dynamicProperty.getField(); + if (field || !field.isValid()) { + return; + } + + const fieldName = field.getName(); + const matchingOrdinalField = nextOrdinalFields.find(oridinalField => { + return fieldName === oridinalField.name; + }); + + if (matchingOrdinalField) { + return; + } + + updatedProperties[dynamicProperty.getStyleName()] = { + type: DynamicStyleProperty.type, + options: { + ...originalProperties[dynamicProperty.getStyleName()].options + } + }; + delete updatedProperties[dynamicProperty.getStyleName()].options.field; + + }); + + if (Object.keys(updatedProperties).length === 0) { + return { + hasChanges: false, + nextStyleDescriptor: { ...this._descriptor }, + }; + } + + return { + hasChanges: true, + nextStyleDescriptor: VectorStyle.createDescriptor({ + ...originalProperties, + ...updatedProperties, + }) + }; + + } + + 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 supportedFeatures = await this._source.getSupportedShapeTypes(); + const isSingleFeatureType = supportedFeatures.length === 1; + + if (scaledFields.length === 0 && isSingleFeatureType) { + // no meta data to pull from source data request. + return {}; + } + + let hasPoints = false; + let hasLines = false; + let hasPolygons = false; + for (let i = 0; i < features.length; i++) { + const feature = features[i]; + if (!hasPoints && POINTS.includes(feature.geometry.type)) { + hasPoints = true; + } + if (!hasLines && LINES.includes(feature.geometry.type)) { + hasLines = true; + } + 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 = { + hasFeatureType: { + [VECTOR_SHAPE_TYPES.POINT]: hasPoints, + [VECTOR_SHAPE_TYPES.LINE]: hasLines, + [VECTOR_SHAPE_TYPES.POLYGON]: hasPolygons + } + }; + + scaledFields.forEach(({ min, max, name }) => { + if (min !== Infinity && max !== -Infinity) { + featuresMeta[name] = { + min, + max, + delta: max - min, + }; + } + }); + + return featuresMeta; + } + + getSourceFieldNames() { + const fieldNames = []; + this.getDynamicPropertiesArray().forEach(styleProperty => { + if (styleProperty.getFieldOrigin() === SOURCE_DATA_ID_ORIGIN) { + fieldNames.push(styleProperty.getField().getName()); + } + }); + return fieldNames; + } + + getRawProperties() { + return this._descriptor.properties || {}; + } + + getDynamicPropertiesArray() { + const styleProperties = this._getAllStyleProperties(); + return styleProperties.filter(styleProperty => styleProperty.isDynamic()); + } + + _checkIfOnlyFeatureType = async (featureType) => { + const supportedFeatures = await this._source.getSupportedShapeTypes(); + + if (supportedFeatures.length === 1) { + return supportedFeatures[0] === featureType; + } + + if (!this._descriptor.__styleMeta || !this._descriptor.__styleMeta.hasFeatureType) { + return false; + } + + const featureTypes = Object.keys(this._descriptor.__styleMeta.hasFeatureType); + return featureTypes.reduce((isOnlySingleFeatureType, featureTypeKey) => { + const hasFeature = this._descriptor.__styleMeta.hasFeatureType[featureTypeKey]; + return featureTypeKey === featureType + ? isOnlySingleFeatureType && hasFeature + : isOnlySingleFeatureType && !hasFeature; + }, true); + } + + _getIsPointsOnly = async () => { + return this._checkIfOnlyFeatureType(VECTOR_SHAPE_TYPES.POINT); + } + + _getIsLinesOnly = async () => { + return this._checkIfOnlyFeatureType(VECTOR_SHAPE_TYPES.LINE); + } + + _getFieldRange = (fieldName) => { + return _.get(this._descriptor, ['__styleMeta', fieldName]); + } + + getIcon = () => { + const styles = this.getRawProperties(); + const symbolId = this.arePointsSymbolizedAsCircles() + ? undefined + : this._descriptor.properties.symbol.options.symbolId; + return ( + + ); + } + + renderLegendDetails(getFieldLabel, getFieldFormatter) { + const styles = this.getRawProperties(); + const styleProperties = Object.keys(styles).map(styleName => { + const { type, options } = styles[styleName]; + return { + name: styleName, + type, + options, + range: options && options.field && options.field.name ? this._getFieldRange(options.field.name) : null, + }; + }); + + return ( + + ); + } + + _getStyleFields() { + return this.getDynamicPropertiesArray() + .map(styleProperty => { + + // "feature-state" data expressions are not supported with layout properties. + // 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 + && this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_ICON) { + supportsFeatureState = false; + isScaled = true; + } else { + supportsFeatureState = styleProperty.supportsFeatureState(); + isScaled = styleProperty.isScaled(); + } + + const field = styleProperty.getField(); + return { + supportsFeatureState, + isScaled, + name: field.getName(), + range: this._getFieldRange(field.getName()), + computedName: getComputedFieldName(styleProperty.getStyleName(), field.getName()), + }; + }); + } + + clearFeatureState(featureCollection, mbMap, sourceId) { + const tmpFeatureIdentifier = { + source: null, + id: null + }; + for (let i = 0; i < featureCollection.features.length; i++) { + const feature = featureCollection.features[i]; + tmpFeatureIdentifier.source = sourceId; + tmpFeatureIdentifier.id = feature.id; + mbMap.removeFeatureState(tmpFeatureIdentifier); + } + } + + setFeatureState(featureCollection, mbMap, sourceId) { + + if (!featureCollection) { + return; + } + + const styleFields = this._getStyleFields(); + if (styleFields.length === 0) { + return; + } + + const tmpFeatureIdentifier = { + source: null, + id: null + }; + const tmpFeatureState = {}; + + //scale to [0,1] domain + 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]; + 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; + } + } else { + if (isNaN(value)) { + styleValue = 0; + } else { + styleValue = value; + } + } + + if (supportsFeatureState) { + tmpFeatureState[computedName] = styleValue; + } else { + feature.properties[computedName] = styleValue; + } + } + tmpFeatureIdentifier.source = sourceId; + tmpFeatureIdentifier.id = feature.id; + mbMap.setFeatureState(tmpFeatureIdentifier, tmpFeatureState); + } + + const hasGeoJsonProperties = styleFields.some(({ supportsFeatureState }) => { + return !supportsFeatureState; + }); + return hasGeoJsonProperties; + } + + setMBPaintProperties({ alpha, mbMap, fillLayerId, lineLayerId }) { + this._fillColorStyleProperty.syncFillColorWithMb(fillLayerId, mbMap, alpha); + this._lineColorStyleProperty.syncLineColorWithMb(lineLayerId, mbMap, alpha); + this._lineWidthStyleProperty.syncLineWidthWithMb(lineLayerId, mbMap); + } + + setMBPaintPropertiesForPoints({ alpha, mbMap, pointLayerId }) { + this._fillColorStyleProperty.syncCircleColorWithMb(pointLayerId, mbMap, alpha); + this._lineColorStyleProperty.syncCircleStrokeWithMb(pointLayerId, mbMap, alpha); + this._lineWidthStyleProperty.syncCircleStrokeWidthWithMb(pointLayerId, mbMap); + this._iconSizeStyleProperty.syncCircleRadiusWithMb(pointLayerId, mbMap); + } + + setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) { + + const symbolId = this._descriptor.properties.symbol.options.symbolId; + mbMap.setLayoutProperty(symbolLayerId, 'icon-ignore-placement', true); + mbMap.setLayoutProperty(symbolLayerId, 'icon-anchor', getMakiSymbolAnchor(symbolId)); + mbMap.setPaintProperty(symbolLayerId, 'icon-opacity', alpha); + + // icon-color is only supported on SDF icons. + this._fillColorStyleProperty.syncIconColorWithMb(symbolLayerId, mbMap); + this._lineColorStyleProperty.syncHaloBorderColorWithMb(symbolLayerId, mbMap); + this._lineWidthStyleProperty.syncHaloWidthWithMb(symbolLayerId, mbMap); + this._iconSizeStyleProperty.syncIconImageAndSizeWithMb(symbolLayerId, mbMap, symbolId); + this._iconOrientationProperty.syncIconRotationWithMb(symbolLayerId, mbMap); + + } + + arePointsSymbolizedAsCircles() { + return this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_CIRCLE; + } + + _makeField(fieldDescriptor) { + + if (!fieldDescriptor || !fieldDescriptor.name) { + return null; + } + + if (fieldDescriptor.origin === FIELD_ORIGIN.SOURCE) { + return this._source.createField({ + fieldName: fieldDescriptor.name, + label: fieldDescriptor.label + }); + } else if (fieldDescriptor.origin === FIELD_ORIGIN.JOIN) { + let matchingField = null; + const joins = this._layer.getValidJoins(); + joins.find(join => { + const aggSource = join.getRightJoinSource(); + matchingField = aggSource.getMetricFieldForName(fieldDescriptor.name); + return !!matchingField; + }); + return matchingField; + } else { + throw new Error(`Unknown origin-type ${fieldDescriptor.origin}`); + } + + + } + + _makeSizeProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticSizeProperty({ size: 0 }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticSizeProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + const field = this._makeField(descriptor.options.field); + return new DynamicSizeProperty(descriptor.options, styleName, field); + } else { + throw new Error(`${descriptor} not implemented`); + } + } + + _makeColorProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticColorProperty({ color: null }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticColorProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + const field = this._makeField(descriptor.options.field); + return new DynamicColorProperty(descriptor.options, styleName, field); + } else { + throw new Error(`${descriptor} not implemented`); + } + } + + _makeOrientationProperty(descriptor, styleName) { + if (!descriptor || !descriptor.options) { + return new StaticOrientationProperty({ orientation: 0 }, styleName); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticOrientationProperty(descriptor.options, styleName); + } else if (descriptor.type === DynamicStyleProperty.type) { + const field = this._makeField(descriptor.options.field); + return new DynamicOrientationProperty(descriptor.options, styleName, field); + } else { + throw new Error(`${descriptor} not implemented`); + } + } + + _makeStyleProperty(propertyName, descriptor) { + + if (propertyName === vectorStyles.LINE_WIDTH) { + return this._makeSizeProperty(descriptor, vectorStyles.LINE_WIDTH); + } else if (propertyName === vectorStyles.ICON_SIZE) { + return this._makeSizeProperty(descriptor, vectorStyles.ICON_SIZE); + } else if (propertyName === vectorStyles.LINE_COLOR) { + return this._makeColorProperty(descriptor, vectorStyles.LINE_COLOR); + } else if (propertyName === vectorStyles.FILL_COLOR) { + return this._makeColorProperty(descriptor, vectorStyles.FILL_COLOR); + } else if (propertyName === vectorStyles.ICON_ORIENTATION) { + return this._makeOrientationProperty(descriptor, vectorStyles.ICON_ORIENTATION); + } + + throw new Error(`${propertyName} not implemented`); + } + +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js index 7c993564018aa..2b1f7ce4e5468 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js @@ -5,22 +5,25 @@ */ import { VectorStyle } from './vector_style'; -import { DataRequest } from '../util/data_request'; -import { VECTOR_SHAPE_TYPES } from '../sources/vector_feature_types'; +import { DataRequest } from '../../util/data_request'; +import { VECTOR_SHAPE_TYPES } from '../../sources/vector_feature_types'; +import { DynamicStyleProperty } from './properties/dynamic_style_property'; +import { StaticStyleProperty } from './properties/static_style_property'; + describe('getDescriptorWithMissingStylePropsRemoved', () => { const fieldName = 'doIStillExist'; const properties = { fillColor: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: {} }, lineColor: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: {} }, iconSize: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { color: 'a color', field: { name: fieldName } @@ -180,7 +183,7 @@ describe('pluckStyleMetaFromSourceDataRequest', () => { const vectorStyle = new VectorStyle({ properties: { fillColor: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { name: 'myDynamicFieldWithNoValues' @@ -202,7 +205,7 @@ describe('pluckStyleMetaFromSourceDataRequest', () => { const vectorStyle = new VectorStyle({ properties: { fillColor: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { field: { name: 'myDynamicField' diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js similarity index 82% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js index dfd0c1ca1b86b..1e215b27fa145 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style_defaults.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { VectorStyle } from './vector_style'; +import { DynamicStyleProperty } from './properties/dynamic_style_property'; +import { StaticStyleProperty } from './properties/static_style_property'; import { SYMBOLIZE_AS_CIRCLE, DEFAULT_ICON_SIZE } from './vector_constants'; import { COLOR_GRADIENTS, DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS -} from './color_utils'; +} from '../color_utils'; const DEFAULT_ICON = 'airfield'; @@ -49,31 +50,31 @@ export function getDefaultStaticProperties(mapColors = []) { return { [vectorStyles.FILL_COLOR]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { color: nextFillColor, } }, [vectorStyles.LINE_COLOR]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { color: nextLineColor } }, [vectorStyles.LINE_WIDTH]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { size: 1 } }, [vectorStyles.ICON_SIZE]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { size: DEFAULT_ICON_SIZE } }, [vectorStyles.ICON_ORIENTATION]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { orientation: 0 } @@ -84,21 +85,21 @@ export function getDefaultStaticProperties(mapColors = []) { export function getDefaultDynamicProperties() { return { [vectorStyles.FILL_COLOR]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { color: COLOR_GRADIENTS[0].value, field: undefined, } }, [vectorStyles.LINE_COLOR]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { color: COLOR_GRADIENTS[0].value, field: undefined, } }, [vectorStyles.LINE_WIDTH]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { minSize: DEFAULT_MIN_SIZE, maxSize: DEFAULT_MAX_SIZE, @@ -106,7 +107,7 @@ export function getDefaultDynamicProperties() { } }, [vectorStyles.ICON_SIZE]: { - type: VectorStyle.STYLE_TYPE.DYNAMIC, + type: DynamicStyleProperty.type, options: { minSize: DEFAULT_MIN_SIZE, maxSize: DEFAULT_MAX_SIZE, @@ -114,7 +115,7 @@ export function getDefaultDynamicProperties() { } }, [vectorStyles.ICON_ORIENTATION]: { - type: VectorStyle.STYLE_TYPE.STATIC, + type: StaticStyleProperty.type, options: { field: undefined, } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js deleted file mode 100644 index f26f4df0b1753..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector_style.js +++ /dev/null @@ -1,639 +0,0 @@ -/* - * 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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { getColorRampStops } from './color_utils'; -import { VectorStyleEditor } from './components/vector/vector_style_editor'; -import { getDefaultProperties, vectorStyles } from './vector_style_defaults'; -import { AbstractStyle } from './abstract_style'; -import { SOURCE_DATA_ID_ORIGIN, GEO_JSON_TYPE } from '../../../common/constants'; -import { VectorIcon } from './components/vector/legend/vector_icon'; -import { VectorStyleLegend } from './components/vector/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, - LARGE_MAKI_ICON_SIZE, - SMALL_MAKI_ICON_SIZE, - HALF_LARGE_MAKI_ICON_SIZE -} from './symbol_utils'; - -export class VectorStyle extends AbstractStyle { - - static type = 'VECTOR'; - static STYLE_TYPE = { 'DYNAMIC': 'DYNAMIC', 'STATIC': 'STATIC' }; - - static getComputedFieldName(styleName, fieldName) { - return `${VectorStyle.getComputedFieldNamePrefix(fieldName)}__${styleName}`; - } - - static getComputedFieldNamePrefix(fieldName) { - return `__kbn__dynamic__${fieldName}`; - } - - constructor(descriptor = {}, source) { - super(); - this._source = source; - this._descriptor = { - ...descriptor, - ...VectorStyle.createDescriptor(descriptor.properties), - }; - } - - static createDescriptor(properties = {}) { - return { - type: VectorStyle.type, - properties: { ...getDefaultProperties(), ...properties } - }; - } - - static createDefaultStyleProperties(mapColors) { - return getDefaultProperties(mapColors); - } - - static getDisplayName() { - return i18n.translate('xpack.maps.style.vector.displayNameLabel', { - defaultMessage: 'Vector style' - }); - } - - static description = ''; - - renderEditor({ layer, onStyleDescriptorChange }) { - const styleProperties = { ...this.getProperties() }; - const handlePropertyChange = (propertyName, settings) => { - styleProperties[propertyName] = settings;//override single property, but preserve the rest - const vectorStyleDescriptor = VectorStyle.createDescriptor(styleProperties); - onStyleDescriptorChange(vectorStyleDescriptor); - }; - - return ( - - ); - } - - /* - * Changes to source descriptor and join descriptor will impact style properties. - * For instance, a style property may be dynamically tied to the value of an ordinal field defined - * by a join or a metric aggregation. The metric aggregation or join may be edited or removed. - * When this happens, the style will be linked to a no-longer-existing ordinal field. - * This method provides a way for a style to clean itself and return a descriptor that unsets any dynamic - * properties that are tied to missing oridinal fields - * - * This method does not update its descriptor. It just returns a new descriptor that the caller - * can then use to update store state via dispatch. - */ - getDescriptorWithMissingStylePropsRemoved(nextOrdinalFields) { - const originalProperties = this.getProperties(); - const updatedProperties = {}; - Object.keys(originalProperties).forEach(propertyName => { - if (!this._isPropertyDynamic(propertyName)) { - return; - } - - const fieldName = _.get(originalProperties[propertyName], 'options.field.name'); - if (!fieldName) { - return; - } - - const matchingOrdinalField = nextOrdinalFields.find(oridinalField => { - return fieldName === oridinalField.name; - }); - - if (matchingOrdinalField) { - return; - } - - updatedProperties[propertyName] = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, - options: { - ...originalProperties[propertyName].options - } - }; - delete updatedProperties[propertyName].options.field; - }); - - if (Object.keys(updatedProperties).length === 0) { - return { - hasChanges: false, - nextStyleDescriptor: { ...this._descriptor }, - }; - } - - return { - hasChanges: true, - nextStyleDescriptor: VectorStyle.createDescriptor({ - ...originalProperties, - ...updatedProperties, - }) - }; - } - - async pluckStyleMetaFromSourceDataRequest(sourceDataRequest) { - const features = _.get(sourceDataRequest.getData(), 'features', []); - if (features.length === 0) { - return {}; - } - - const scaledFields = this.getDynamicPropertiesArray() - .map(({ options }) => { - return { - name: options.field.name, - min: Infinity, - max: -Infinity - }; - }); - - const supportedFeatures = await this._source.getSupportedShapeTypes(); - const isSingleFeatureType = supportedFeatures.length === 1; - - if (scaledFields.length === 0 && isSingleFeatureType) { - // no meta data to pull from source data request. - return {}; - } - - let hasPoints = false; - let hasLines = false; - let hasPolygons = false; - for (let i = 0; i < features.length; i++) { - const feature = features[i]; - if (!hasPoints && [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT].includes(feature.geometry.type)) { - hasPoints = true; - } - if (!hasLines && [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING].includes(feature.geometry.type)) { - hasLines = true; - } - if (!hasPolygons && [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON].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 = { - hasFeatureType: { - [VECTOR_SHAPE_TYPES.POINT]: hasPoints, - [VECTOR_SHAPE_TYPES.LINE]: hasLines, - [VECTOR_SHAPE_TYPES.POLYGON]: hasPolygons - } - }; - - scaledFields.forEach(({ min, max, name }) => { - if (min !== Infinity && max !== -Infinity) { - featuresMeta[name] = { - min, - max, - delta: max - min, - }; - } - }); - - return featuresMeta; - } - - getSourceFieldNames() { - const properties = this.getProperties(); - const fieldNames = []; - Object.keys(properties).forEach(propertyName => { - if (!this._isPropertyDynamic(propertyName)) { - return; - } - - const field = _.get(properties[propertyName], 'options.field', {}); - if (field.origin === SOURCE_DATA_ID_ORIGIN && field.name) { - fieldNames.push(field.name); - } - }); - - return fieldNames; - } - - getProperties() { - return this._descriptor.properties || {}; - } - - getDynamicPropertiesArray() { - const styles = this.getProperties(); - return Object.keys(styles) - .map(styleName => { - const { type, options } = styles[styleName]; - return { - styleName, - type, - options - }; - }) - .filter(({ styleName }) => { - return this._isPropertyDynamic(styleName); - }); - } - - _isPropertyDynamic(propertyName) { - const { type, options } = _.get(this._descriptor, ['properties', propertyName], {}); - return type === VectorStyle.STYLE_TYPE.DYNAMIC && options.field && options.field.name; - } - - _checkIfOnlyFeatureType = async (featureType) => { - const supportedFeatures = await this._source.getSupportedShapeTypes(); - - if (supportedFeatures.length === 1) { - return supportedFeatures[0] === featureType; - } - - if (!this._descriptor.__styleMeta || !this._descriptor.__styleMeta.hasFeatureType) { - return false; - } - - const featureTypes = Object.keys(this._descriptor.__styleMeta.hasFeatureType); - return featureTypes.reduce((isOnlySingleFeatureType, featureTypeKey) => { - const hasFeature = this._descriptor.__styleMeta.hasFeatureType[featureTypeKey]; - return featureTypeKey === featureType - ? isOnlySingleFeatureType && hasFeature - : isOnlySingleFeatureType && !hasFeature; - }, true); - } - - _getIsPointsOnly = async () => { - return this._checkIfOnlyFeatureType(VECTOR_SHAPE_TYPES.POINT); - } - - _getIsLinesOnly = async () => { - return this._checkIfOnlyFeatureType(VECTOR_SHAPE_TYPES.LINE); - } - - _getIsPolygonsOnly = async () => { - return this._checkIfOnlyFeatureType(VECTOR_SHAPE_TYPES.POLYGON); - } - - _getFieldRange = (fieldName) => { - return _.get(this._descriptor, ['__styleMeta', fieldName]); - } - - getIcon = () => { - const styles = this.getProperties(); - const symbolId = this.arePointsSymbolizedAsCircles() - ? undefined - : this._descriptor.properties.symbol.options.symbolId; - return ( - - ); - } - - getLegendDetails(getFieldLabel, getFieldFormatter) { - const styles = this.getProperties(); - const styleProperties = Object.keys(styles).map(styleName => { - const { type, options } = styles[styleName]; - return { - name: styleName, - type, - options, - range: options && options.field && options.field.name ? this._getFieldRange(options.field.name) : null, - }; - }); - - return ( - - ); - } - - _getStyleFields() { - return this.getDynamicPropertiesArray() - .map(({ styleName, options }) => { - const name = options.field.name; - - // "feature-state" data expressions are not supported with layout properties. - // To work around this limitation, some styling values must fall back to geojson property values. - let supportsFeatureState; - let isScaled; - if (styleName === 'iconSize' - && this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_ICON) { - supportsFeatureState = false; - isScaled = true; - } else if (styleName === 'iconOrientation') { - supportsFeatureState = false; - isScaled = false; - } else if ((styleName === vectorStyles.FILL_COLOR || styleName === vectorStyles.LINE_COLOR) - && options.useCustomColorRamp) { - supportsFeatureState = true; - isScaled = false; - } else { - supportsFeatureState = true; - isScaled = true; - } - - return { - supportsFeatureState, - isScaled, - name, - range: this._getFieldRange(name), - computedName: VectorStyle.getComputedFieldName(styleName, name), - }; - }); - } - - clearFeatureState(featureCollection, mbMap, sourceId) { - const tmpFeatureIdentifier = { - source: null, - id: null - }; - for (let i = 0; i < featureCollection.features.length; i++) { - const feature = featureCollection.features[i]; - tmpFeatureIdentifier.source = sourceId; - tmpFeatureIdentifier.id = feature.id; - mbMap.removeFeatureState(tmpFeatureIdentifier); - } - } - - setFeatureState(featureCollection, mbMap, sourceId) { - - if (!featureCollection) { - return; - } - - const styleFields = this._getStyleFields(); - if (styleFields.length === 0) { - return; - } - - const tmpFeatureIdentifier = { - source: null, - id: null - }; - const tmpFeatureState = {}; - - //scale to [0,1] domain - 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]; - 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; - } - } else { - if (isNaN(value)) { - styleValue = 0; - } else { - styleValue = value; - } - } - - if (supportsFeatureState) { - tmpFeatureState[computedName] = styleValue; - } else { - feature.properties[computedName] = styleValue; - } - } - tmpFeatureIdentifier.source = sourceId; - tmpFeatureIdentifier.id = feature.id; - mbMap.setFeatureState(tmpFeatureIdentifier, tmpFeatureState); - } - - const hasGeoJsonProperties = styleFields.some(({ supportsFeatureState }) => { - return !supportsFeatureState; - }); - return hasGeoJsonProperties; - } - - _getMBDataDrivenColor({ targetName, colorStops, isSteps }) { - if (isSteps) { - const firstStopValue = colorStops[0]; - const lessThenFirstStopValue = firstStopValue - 1; - return [ - 'step', - ['coalesce', ['feature-state', targetName], lessThenFirstStopValue], - 'rgba(0,0,0,0)', // MB will assign the base value to any features that is below the first stop value - ...colorStops - ]; - } - - return [ - 'interpolate', - ['linear'], - ['coalesce', ['feature-state', targetName], -1], - -1, 'rgba(0,0,0,0)', - ...colorStops - ]; - } - - _getMbDataDrivenSize({ targetName, minSize, maxSize }) { - return [ - 'interpolate', - ['linear'], - ['coalesce', ['feature-state', targetName], 0], - 0, minSize, - 1, maxSize - ]; - } - - _getMBColor(styleName, styleDescriptor) { - const isStatic = styleDescriptor.type === VectorStyle.STYLE_TYPE.STATIC; - if (isStatic) { - return _.get(styleDescriptor, 'options.color', null); - } - - const isDynamicConfigComplete = _.has(styleDescriptor, 'options.field.name') - && _.has(styleDescriptor, 'options.color'); - if (!isDynamicConfigComplete) { - return null; - } - - if (styleDescriptor.options.useCustomColorRamp && - (!styleDescriptor.options.customColorRamp || - !styleDescriptor.options.customColorRamp.length)) { - return null; - } - - return this._getMBDataDrivenColor({ - targetName: VectorStyle.getComputedFieldName(styleName, styleDescriptor.options.field.name), - colorStops: this._getMBColorStops(styleDescriptor), - isSteps: styleDescriptor.options.useCustomColorRamp, - }); - } - - _getMBColorStops(styleDescriptor) { - if (styleDescriptor.options.useCustomColorRamp) { - return styleDescriptor.options.customColorRamp.reduce((accumulatedStops, nextStop) => { - return [...accumulatedStops, nextStop.stop, nextStop.color]; - }, []); - } - - return getColorRampStops(styleDescriptor.options.color); - } - - _isSizeDynamicConfigComplete(styleDescriptor) { - return _.has(styleDescriptor, 'options.field.name') - && _.has(styleDescriptor, 'options.minSize') - && _.has(styleDescriptor, 'options.maxSize'); - } - - _getMbSize(styleName, styleDescriptor) { - if (styleDescriptor.type === VectorStyle.STYLE_TYPE.STATIC) { - return styleDescriptor.options.size; - } - - if (this._isSizeDynamicConfigComplete(styleDescriptor)) { - return this._getMbDataDrivenSize({ - targetName: VectorStyle.getComputedFieldName(styleName, styleDescriptor.options.field.name), - minSize: styleDescriptor.options.minSize, - maxSize: styleDescriptor.options.maxSize, - }); - } - - return null; - } - - setMBPaintProperties({ alpha, mbMap, fillLayerId, lineLayerId }) { - if (this._descriptor.properties.fillColor) { - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - mbMap.setPaintProperty(fillLayerId, 'fill-color', color); - mbMap.setPaintProperty(fillLayerId, 'fill-opacity', alpha); - } else { - mbMap.setPaintProperty(fillLayerId, 'fill-color', null); - mbMap.setPaintProperty(fillLayerId, 'fill-opacity', 0); - } - - if (this._descriptor.properties.lineColor) { - const color = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - mbMap.setPaintProperty(lineLayerId, 'line-color', color); - mbMap.setPaintProperty(lineLayerId, 'line-opacity', alpha); - - } else { - mbMap.setPaintProperty(lineLayerId, 'line-color', null); - mbMap.setPaintProperty(lineLayerId, 'line-opacity', 0); - } - - if (this._descriptor.properties.lineWidth) { - const lineWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - mbMap.setPaintProperty(lineLayerId, 'line-width', lineWidth); - } else { - mbMap.setPaintProperty(lineLayerId, 'line-width', 0); - } - } - - setMBPaintPropertiesForPoints({ alpha, mbMap, pointLayerId }) { - if (this._descriptor.properties.fillColor) { - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - mbMap.setPaintProperty(pointLayerId, 'circle-color', color); - mbMap.setPaintProperty(pointLayerId, 'circle-opacity', alpha); - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-color', null); - mbMap.setPaintProperty(pointLayerId, 'circle-opacity', 0); - } - if (this._descriptor.properties.lineColor) { - const color = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', color); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', alpha); - - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-color', null); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-opacity', 0); - } - if (this._descriptor.properties.lineWidth) { - const lineWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-width', lineWidth); - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-stroke-width', 0); - } - if (this._descriptor.properties.iconSize) { - const iconSize = this._getMbSize(vectorStyles.ICON_SIZE, this._descriptor.properties.iconSize); - mbMap.setPaintProperty(pointLayerId, 'circle-radius', iconSize); - } else { - mbMap.setPaintProperty(pointLayerId, 'circle-radius', 0); - } - } - - async setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) { - mbMap.setLayoutProperty(symbolLayerId, 'icon-ignore-placement', true); - - const symbolId = this._descriptor.properties.symbol.options.symbolId; - mbMap.setLayoutProperty(symbolLayerId, 'icon-anchor', getMakiSymbolAnchor(symbolId)); - const color = this._getMBColor(vectorStyles.FILL_COLOR, this._descriptor.properties.fillColor); - const haloColor = this._getMBColor(vectorStyles.LINE_COLOR, this._descriptor.properties.lineColor); - const haloWidth = this._getMbSize(vectorStyles.LINE_WIDTH, this._descriptor.properties.lineWidth); - // icon-color is only supported on SDF icons. - mbMap.setPaintProperty(symbolLayerId, 'icon-color', color); - mbMap.setPaintProperty(symbolLayerId, 'icon-halo-color', haloColor); - mbMap.setPaintProperty(symbolLayerId, 'icon-halo-width', haloWidth); - mbMap.setPaintProperty(symbolLayerId, 'icon-opacity', alpha); - - // circle sizing is by radius - // to make icons be similiar in size to circles then have to deal with icon in half width measurements - const iconSize = this._descriptor.properties.iconSize; - if (iconSize.type === VectorStyle.STYLE_TYPE.STATIC) { - const iconPixels = iconSize.options.size >= HALF_LARGE_MAKI_ICON_SIZE - ? LARGE_MAKI_ICON_SIZE - : SMALL_MAKI_ICON_SIZE; - mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); - - const halfIconPixels = iconPixels / 2; - mbMap.setLayoutProperty(symbolLayerId, 'icon-size', iconSize.options.size / halfIconPixels); - } else if (this._isSizeDynamicConfigComplete(iconSize)) { - const iconPixels = iconSize.options.maxSize >= HALF_LARGE_MAKI_ICON_SIZE - ? LARGE_MAKI_ICON_SIZE - : SMALL_MAKI_ICON_SIZE; - mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`); - - const halfIconPixels = iconPixels / 2; - const targetName = VectorStyle.getComputedFieldName(vectorStyles.ICON_SIZE, iconSize.options.field.name); - // Using property state instead of feature-state because layout properties do not support feature-state - mbMap.setLayoutProperty(symbolLayerId, 'icon-size', [ - 'interpolate', - ['linear'], - ['coalesce', ['get', targetName], 0], - 0, iconSize.options.minSize / halfIconPixels, - 1, iconSize.options.maxSize / halfIconPixels - ]); - } - - const iconOrientation = this._descriptor.properties.iconOrientation; - if (iconOrientation.type === VectorStyle.STYLE_TYPE.STATIC) { - mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', iconOrientation.options.orientation); - } else if (_.has(iconOrientation, 'options.field.name')) { - const targetName = VectorStyle.getComputedFieldName(vectorStyles.ICON_ORIENTATION, iconOrientation.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 - ]); - } - } - - arePointsSymbolizedAsCircles() { - return this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_CIRCLE; - } -} diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_aggmetric_tooltip_property.js b/x-pack/legacy/plugins/maps/public/layers/tooltips/es_aggmetric_tooltip_property.js index 42629e192c27d..48fa4327a5217 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_aggmetric_tooltip_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/tooltips/es_aggmetric_tooltip_property.js @@ -14,6 +14,7 @@ export class ESAggMetricTooltipProperty extends ESTooltipProperty { super(propertyKey, propertyName, rawValue, indexPattern); this._metricField = metricField; } + isFilterable() { return false; } diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.js b/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.js index cc19521063f36..ed9b284c12826 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.js @@ -38,12 +38,14 @@ export class JoinTooltipProperty extends TooltipProperty { for (let i = 0; i < this._leftInnerJoins.length; i++) { const rightSource = this._leftInnerJoins[i].getRightJoinSource(); - const esTooltipProperty = await rightSource.createESTooltipProperty( - rightSource.getTerm(), - this._tooltipProperty.getRawValue() - ); - if (esTooltipProperty) { - esFilters.push(...(await esTooltipProperty.getESFilters())); + const termField = rightSource.getTermField(); + try { + const esTooltipProperty = await termField.createTooltipProperty(this._tooltipProperty.getRawValue()); + if (esTooltipProperty) { + esFilters.push(...(await esTooltipProperty.getESFilters())); + } + } catch(e) { + console.error('Cannot create joined filter', e); } } 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 7372b549f6423..caa01a052b1c4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -7,7 +7,7 @@ import turf from 'turf'; import React from 'react'; import { AbstractLayer } from './layer'; -import { VectorStyle } from './styles/vector_style'; +import { VectorStyle } from './styles/vector/vector_style'; import { InnerJoin } from './joins/inner_join'; import { GEO_JSON_TYPE, @@ -91,9 +91,11 @@ export class VectorLayer extends AbstractLayer { this._joins = []; if (options.layerDescriptor.joins) { options.layerDescriptor.joins.forEach((joinDescriptor) => { - this._joins.push(new InnerJoin(joinDescriptor, this._source.getInspectorAdapters())); + const join = new InnerJoin(joinDescriptor, this._source); + this._joins.push(join); }); } + this._style = new VectorStyle(this._descriptor.style, this._source, this); } destroy() { @@ -200,7 +202,7 @@ export class VectorLayer extends AbstractLayer { return await source.getFieldFormatter(field.name); }; - return this._style.getLegendDetails(getFieldLabel, getFieldFormatter); + return this._style.renderLegendDetails(getFieldLabel, getFieldFormatter); } _getBoundsBasedOnData() { @@ -241,7 +243,6 @@ export class VectorLayer extends AbstractLayer { return this._source.getDisplayName(); } - async getDateFields() { const timeFields = await this._source.getDateFields(); return timeFields.map(({ label, name }) => { @@ -253,7 +254,6 @@ export class VectorLayer extends AbstractLayer { }); } - async getNumberFields() { const numberFields = await this._source.getNumberFields(); const numberFieldOptions = numberFields.map(({ label, name }) => { @@ -410,7 +410,7 @@ export class VectorLayer extends AbstractLayer { } = await joinSource.getPropertiesMap( searchFilters, leftSourceName, - join.getLeftFieldName(), + join.getLeftField().getName(), registerCancelCallback.bind(null, requestToken)); stopLoading(sourceDataId, requestToken, propertiesMap); return { @@ -442,9 +442,7 @@ export class VectorLayer extends AbstractLayer { const fieldNames = [ ...this._source.getFieldNames(), ...this._style.getSourceFieldNames(), - ...this.getValidJoins().map(join => { - return join.getLeftFieldName(); - }) + ...this.getValidJoins().map(join => join.getLeftField().getName()) ]; return { @@ -476,9 +474,8 @@ export class VectorLayer extends AbstractLayer { let isFeatureVisible = true; for (let j = 0; j < joinStates.length; j++) { const joinState = joinStates[j]; - const InnerJoin = joinState.join; - const rightMetricFields = InnerJoin.getRightMetricFields(); - const canJoinOnCurrent = InnerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap, rightMetricFields); + const innerJoin = joinState.join; + const canJoinOnCurrent = innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap); isFeatureVisible = isFeatureVisible && canJoinOnCurrent; } @@ -762,7 +759,7 @@ export class VectorLayer extends AbstractLayer { const tooltipProperty = tooltipsFromSource[i]; const matchingJoins = []; for (let j = 0; j < this._joins.length; j++) { - if (this._joins[j].getLeftFieldName() === tooltipProperty.getPropertyKey()) { + if (this._joins[j].getLeftField().getName() === tooltipProperty.getPropertyKey()) { matchingJoins.push(this._joins[j]); } }