diff --git a/docs/maps/vector-style-properties.asciidoc b/docs/maps/vector-style-properties.asciidoc index f51632218add1..5656a7f04d0e3 100644 --- a/docs/maps/vector-style-properties.asciidoc +++ b/docs/maps/vector-style-properties.asciidoc @@ -8,32 +8,52 @@ Point, polygon, and line features support different styling properties. [[point-style-properties]] ==== Point style properties +You can add text labels to your Point features by configuring label style properties. + +[cols="2*"] +|=== +|*Label* +|Specifies label content. +|*Label color* +|The text color. +|*Label size* +|The size of the text font, in pixels. +|=== + You can symbolize Point features as *Circle markers* or *Icons*. Use *Circle marker* to symbolize Points as circles. -*Fill color*:: The fill color of the point features. - -*Border color*:: The border color of the point features. - -*Border width*:: The border width of the point features. - -*Symbol size*:: The radius of the symbol size, in pixels. +[cols="2*"] +|=== +|*Border color* +|The border color of the point features. +|*Border width* +|The border width of the point features. +|*Fill color* +|The fill color of the point features. +|*Symbol size* +|The radius of the symbol size, in pixels. +|=== Use *Icon* to symbolize Points as icons. -*Fill color*:: The fill color of the point features. - -*Border color*:: The border color of the point features. - -*Border width*:: The border width of the point features. +[cols="2*"] +|=== +|*Border color* +|The border color of the point features. +|*Border width* +|The border width of the point features. +|*Fill color* +|The fill color of the point features. +|*Symbol orientation* +|The symbol orientation rotating the icon clockwise. +|*Symbol size* +|The radius of the symbol size, in pixels. +|=== -*Symbol orientation*:: The symbol orientation rotating the icon clockwise. - -*Symbol size*:: The radius of the symbol size, in pixels. -+ Available icons -+ + [role="screenshot"] image::maps/images/maki-icons.png[] @@ -42,17 +62,25 @@ image::maps/images/maki-icons.png[] [[polygon-style-properties]] ==== Polygon style properties -*Fill color*:: The fill color of the polygon features. - -*Border color*:: The border color of the polygon features. - -*Border width*:: The border width of the polygon features. +[cols="2*"] +|=== +|*Border color* +|The border color of the polygon features. +|*Border width* +|The border width of the polygon features. +|*Fill color* +|The fill color of the polygon features. +|=== [float] [[line-style-properties]] ==== Line style properties -*Border color*:: The color of the line features. - -*Border width*:: The width of the line features. +[cols="2*"] +|=== +|*Border color* +|The color of the line features. +|*Border width* +|The width of the line features. +|=== diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js index 2890b75172fb0..21c5f15fb6122 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/layer.js @@ -344,6 +344,10 @@ export class AbstractLayer { return []; } + async getFields() { + return []; + } + syncVisibilityWithMb(mbMap, mbLayerId) { mbMap.setLayoutProperty(mbLayerId, 'visibility', this.isVisible() ? 'visible' : 'none'); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index d8960ae1f6a04..d852332ac2f84 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -124,6 +124,24 @@ export class ESSearchSource extends AbstractESSource { } } + async getFields() { + try { + const indexPattern = await this.getIndexPattern(); + return indexPattern.fields + .filter(field => { + // Ensure fielddata is enabled for field. + // Search does not request _source + return field.aggregatable; + }) + .map(field => { + return this.createField({ fieldName: field.name }); + }); + } catch (error) { + // failed index-pattern retrieval will show up as error-message in the layer-toc-entry + return []; + } + } + getFieldNames() { return [this._descriptor.geoField]; } 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 814cbffc7bfe3..bf7267e9c5858 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 @@ -103,6 +103,10 @@ export class AbstractVectorSource extends AbstractSource { return []; } + async getFields() { + return [...(await this.getDateFields()), ...(await this.getNumberFields())]; + } + async getLeftJoinFields() { return []; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js index 2db92ba7f9337..84327635f2b65 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js @@ -12,7 +12,7 @@ import { FieldSelect, fieldShape } from '../field_select'; import { ColorRampSelect } from './color_ramp_select'; import { EuiSpacer } from '@elastic/eui'; -export function DynamicColorSelection({ ordinalFields, onChange, styleOptions }) { +export function DynamicColorSelection({ fields, onChange, styleOptions }) { const onFieldChange = ({ field }) => { onChange({ ...styleOptions, field }); }; @@ -32,7 +32,7 @@ export function DynamicColorSelection({ ordinalFields, onChange, styleOptions }) /> { + onChange({ ...styleOptions, field }); + }; + + return ( + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js new file mode 100644 index 0000000000000..ea296a3312799 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.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. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFieldText } from '@elastic/eui'; + +export function StaticLabelSelector({ onChange, styleOptions }) { + const onValueChange = event => { + onChange({ value: event.target.value }); + }; + + return ( + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js new file mode 100644 index 0000000000000..6bca56425d38d --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; +import { DynamicLabelSelector } from './dynamic_label_selector'; +import { StaticLabelSelector } from './static_label_selector'; + +export function VectorStyleLabelEditor(props) { + return ( + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js index 6df3283737c73..8ad3916ac6509 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js @@ -10,14 +10,14 @@ import PropTypes from 'prop-types'; import { dynamicOrientationShape } from '../style_option_shapes'; import { FieldSelect, fieldShape } from '../field_select'; -export function DynamicOrientationSelection({ ordinalFields, styleOptions, onChange }) { +export function DynamicOrientationSelection({ fields, styleOptions, onChange }) { const onFieldChange = ({ field }) => { onChange({ ...styleOptions, field }); }; return ( { onChange({ ...styleOptions, field }); }; @@ -32,7 +32,7 @@ export function DynamicSizeSelection({ ordinalFields, styleOptions, onChange }) /> 0; + return this.props.fields.length > 0; } _isDynamic() { @@ -78,7 +78,7 @@ export class StaticDynamicStyleRow extends Component { return ( diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 43dd7b1b2d032..44f630db9d890 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -11,6 +11,7 @@ import chrome from 'ui/chrome'; import { VectorStyleColorEditor } from './color/vector_style_color_editor'; import { VectorStyleSizeEditor } from './size/vector_style_size_editor'; import { VectorStyleSymbolEditor } from './vector_style_symbol_editor'; +import { VectorStyleLabelEditor } from './label/vector_style_label_editor'; import { OrientationEditor } from './orientation/orientation_editor'; import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; @@ -25,6 +26,7 @@ export class VectorStyleEditor extends Component { state = { dateFields: [], numberFields: [], + fields: [], defaultDynamicProperties: getDefaultDynamicProperties(), defaultStaticProperties: getDefaultStaticProperties(), supportedFeatures: undefined, @@ -37,16 +39,16 @@ export class VectorStyleEditor extends Component { componentDidMount() { this._isMounted = true; - this._loadOrdinalFields(); + this._loadFields(); this._loadSupportedFeatures(); } componentDidUpdate() { - this._loadOrdinalFields(); + this._loadFields(); this._loadSupportedFeatures(); } - async _loadOrdinalFields() { + async _loadFields() { const getFieldMeta = async field => { return { label: await field.getLabel(), @@ -54,21 +56,27 @@ export class VectorStyleEditor extends Component { origin: field.getOrigin(), }; }; + const dateFields = await this.props.layer.getDateFields(); const dateFieldPromises = dateFields.map(getFieldMeta); const dateFieldsArray = await Promise.all(dateFieldPromises); - if (this._isMounted && !_.isEqual(dateFieldsArray, this.state.dateFields)) { this.setState({ dateFields: dateFieldsArray }); } const numberFields = await this.props.layer.getNumberFields(); const numberFieldPromises = numberFields.map(getFieldMeta); - const numberFieldsArray = await Promise.all(numberFieldPromises); if (this._isMounted && !_.isEqual(numberFieldsArray, this.state.numberFields)) { this.setState({ numberFields: numberFieldsArray }); } + + const fields = await this.props.layer.getFields(); + const fieldPromises = fields.map(getFieldMeta); + const fieldsArray = await Promise.all(fieldPromises); + if (this._isMounted && !_.isEqual(fieldsArray, this.state.fields)) { + this.setState({ fields: fieldsArray }); + } } async _loadSupportedFeatures() { @@ -126,7 +134,7 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_FILL_COLORS} handlePropertyChange={this.props.handlePropertyChange} styleProperty={this.props.styleProperties.fillColor} - ordinalFields={this._getOrdinalFields()} + fields={this._getOrdinalFields()} defaultStaticStyleOptions={this.state.defaultStaticProperties.fillColor.options} defaultDynamicStyleOptions={this.state.defaultDynamicProperties.fillColor.options} /> @@ -139,7 +147,7 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_LINE_COLORS} handlePropertyChange={this.props.handlePropertyChange} styleProperty={this.props.styleProperties.lineColor} - ordinalFields={this._getOrdinalFields()} + fields={this._getOrdinalFields()} defaultStaticStyleOptions={this.state.defaultStaticProperties.lineColor.options} defaultDynamicStyleOptions={this.state.defaultDynamicProperties.lineColor.options} /> @@ -151,7 +159,7 @@ export class VectorStyleEditor extends Component { @@ -163,27 +171,58 @@ export class VectorStyleEditor extends Component { ); } + _renderLabelProperties() { + return ( + + + + + + + + + + + ); + } + _renderPointProperties() { let iconOrientation; if (this.props.symbolDescriptor.options.symbolizeAs === SYMBOLIZE_AS_ICON) { iconOrientation = ( - - - - + ); } @@ -207,8 +246,12 @@ export class VectorStyleEditor extends Component { {iconOrientation} + {this._renderSymbolSize()} + + + {this._renderLabelProperties()} ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js index fdbb19a60d2e6..0e019994f74a1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -46,6 +46,12 @@ export class DynamicColorProperty extends DynamicStyleProperty { mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); } + syncLabelColorWithMb(mbLayerId, mbMap, alpha) { + const color = this._getMbColor(); + mbMap.setPaintProperty(mbLayerId, 'text-color', color); + mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); + } + isCustomColorRamp() { return this._options.useCustomColorRamp; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js index 78409ef0d488f..d8a68701c1820 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js @@ -43,6 +43,10 @@ function getSymbolSizeIcons() { } export class DynamicSizeProperty extends DynamicStyleProperty { + supportsFeatureState() { + return this.getStyleName() !== VECTOR_STYLES.LABEL_SIZE; + } + syncHaloWidthWithMb(mbLayerId, mbMap) { const haloWidth = this._getMbSize(); mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', haloWidth); @@ -89,6 +93,11 @@ export class DynamicSizeProperty extends DynamicStyleProperty { mbMap.setPaintProperty(mbLayerId, 'line-width', lineWidth); } + syncLabelSizeWithMb(mbLayerId, mbMap) { + const lineWidth = this._getMbSize(); + mbMap.setLayoutProperty(mbLayerId, 'text-size', lineWidth); + } + _getMbSize() { if (this._isSizeDynamicConfigComplete(this._options)) { return this._getMbDataDrivenSize({ @@ -101,10 +110,11 @@ export class DynamicSizeProperty extends DynamicStyleProperty { } _getMbDataDrivenSize({ targetName, minSize, maxSize }) { + const lookup = this.supportsFeatureState() ? 'feature-state' : 'get'; return [ 'interpolate', ['linear'], - ['coalesce', ['feature-state', targetName], 0], + ['coalesce', [lookup, targetName], 0], 0, minSize, 1, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index 3b25746717088..9c55332d069b8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -25,6 +25,10 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return true; } + isOrdinal() { + return true; + } + isComplete() { return !!this._field; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js new file mode 100644 index 0000000000000..b716030d2f263 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js @@ -0,0 +1,39 @@ +/* + * 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'; + +export class DynamicTextProperty extends DynamicStyleProperty { + syncTextFieldWithMb(mbLayerId, mbMap) { + if (this._field && this._field.isValid()) { + const targetName = getComputedFieldName(this._styleName, this._options.field.name); + mbMap.setLayoutProperty(mbLayerId, 'text-field', ['coalesce', ['get', targetName], '']); + } else { + mbMap.setLayoutProperty(mbLayerId, 'text-field', null); + } + } + + isOrdinal() { + return false; + } + + supportsFieldMeta() { + return false; + } + + supportsFeatureState() { + return false; + } + + isScaled() { + return false; + } + + renderHeader() { + return null; + } +} 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 index a32b17af0be9e..658eb6a164556 100644 --- 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 @@ -30,8 +30,13 @@ export class StaticColorProperty extends StaticStyleProperty { 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); + syncCircleStrokeWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-opacity', alpha); + } + + syncLabelColorWithMb(mbLayerId, mbMap, alpha) { + mbMap.setPaintProperty(mbLayerId, 'text-color', this._options.color); + mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); } } 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 index d2682a07def62..1584dec998986 100644 --- 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 @@ -43,4 +43,8 @@ export class StaticSizeProperty extends StaticStyleProperty { syncLineWidthWithMb(mbLayerId, mbMap) { mbMap.setPaintProperty(mbLayerId, 'line-width', this._options.size); } + + syncLabelSizeWithMb(mbLayerId, mbMap) { + mbMap.setLayoutProperty(mbLayerId, 'text-size', this._options.size); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_text_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_text_property.js new file mode 100644 index 0000000000000..7a4a4672152c0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_text_property.js @@ -0,0 +1,21 @@ +/* + * 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 StaticTextProperty extends StaticStyleProperty { + isComplete() { + return this.getOptions().value.length > 0; + } + + syncTextFieldWithMb(mbLayerId, mbMap) { + if (this.getOptions().value.length) { + mbMap.setLayoutProperty(mbLayerId, 'text-field', this.getOptions().value); + } else { + mbMap.setLayoutProperty(mbLayerId, 'text-field', null); + } + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 161c0ea69e86c..8e7cd8ed9831c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -36,6 +36,8 @@ 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'; +import { StaticTextProperty } from './properties/static_text_property'; +import { DynamicTextProperty } from './properties/dynamic_text_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]; @@ -81,11 +83,21 @@ export class VectorStyle extends AbstractStyle { this._descriptor.properties[VECTOR_STYLES.ICON_SIZE], VECTOR_STYLES.ICON_SIZE ); - this._iconOrientationProperty = this._makeOrientationProperty( this._descriptor.properties[VECTOR_STYLES.ICON_ORIENTATION], VECTOR_STYLES.ICON_ORIENTATION ); + this._labelStyleProperty = this._makeLabelProperty( + this._descriptor.properties[VECTOR_STYLES.LABEL_TEXT] + ); + this._labelSizeStyleProperty = this._makeSizeProperty( + this._descriptor.properties[VECTOR_STYLES.LABEL_SIZE], + VECTOR_STYLES.LABEL_SIZE + ); + this._labelColorStyleProperty = this._makeColorProperty( + this._descriptor.properties[VECTOR_STYLES.LABEL_COLOR], + VECTOR_STYLES.LABEL_COLOR + ); } _getAllStyleProperties() { @@ -95,6 +107,9 @@ export class VectorStyle extends AbstractStyle { this._lineWidthStyleProperty, this._iconSizeStyleProperty, this._iconOrientationProperty, + this._labelStyleProperty, + this._labelSizeStyleProperty, + this._labelColorStyleProperty, ]; } @@ -115,16 +130,15 @@ export class VectorStyle extends AbstractStyle { return dynamicStyleProp.isFieldMetaEnabled(); }); + const styleProperties = {}; + this._getAllStyleProperties().forEach(styleProperty => { + styleProperties[styleProperty.getStyleName()] = styleProperty; + }); + return ( { + const styleName = styleProperty.getStyleName(); + if ([VECTOR_STYLES.ICON_ORIENTATION, VECTOR_STYLES.LABEL_TEXT].includes(styleName)) { + return false; + } + if (isLinesOnly) { - return LINE_STYLES.includes(styleProperty.getStyleName()); + return LINE_STYLES.includes(styleName); } if (isPolygonsOnly) { - return POLYGON_STYLES.includes(styleProperty.getStyleName()); + return POLYGON_STYLES.includes(styleName); } return true; @@ -433,6 +452,7 @@ export class VectorStyle extends AbstractStyle { // To work around this limitation, some styling values must fall back to geojson property values. let supportsFeatureState; let isScaled; + // TODO move first check into DynamicSizeProperty.supportsFeatureState if ( styleProperty.getStyleName() === VECTOR_STYLES.ICON_SIZE && this._descriptor.properties.symbol.options.symbolizeAs === SYMBOLIZE_AS_ICON @@ -448,8 +468,10 @@ export class VectorStyle extends AbstractStyle { return { supportsFeatureState, isScaled, + isOrdinal: styleProperty.isOrdinal(), name: field.getName(), meta: this._getFieldMeta(field.getName()), + formatter: this._getFieldFormatter(field.getName()), computedName: getComputedFieldName(styleProperty.getStyleName(), field.getName()), }; }); @@ -468,6 +490,20 @@ export class VectorStyle extends AbstractStyle { } } + _getOrdinalValue(value, isScaled, range) { + const valueAsFloat = parseFloat(value); + + if (isScaled) { + return scaleValue(valueAsFloat, range); + } + + if (isNaN(valueAsFloat)) { + return 0; + } + + return valueAsFloat; + } + setFeatureStateAndStyleProps(featureCollection, mbMap, mbSourceId) { if (!featureCollection) { return; @@ -492,20 +528,20 @@ export class VectorStyle extends AbstractStyle { const { supportsFeatureState, isScaled, + isOrdinal, name, meta: range, + formatter, computedName, } = featureStateParams[j]; - const value = parseFloat(feature.properties[name]); + let styleValue; - if (isScaled) { - styleValue = scaleValue(value, range); + if (isOrdinal) { + styleValue = this._getOrdinalValue(feature.properties[name], isScaled, range); + } else if (formatter) { + styleValue = formatter(feature.properties[name]); } else { - if (isNaN(value)) { - styleValue = 0; - } else { - styleValue = value; - } + styleValue = feature.properties[name]; } if (supportsFeatureState) { @@ -543,6 +579,14 @@ export class VectorStyle extends AbstractStyle { this._iconSizeStyleProperty.syncCircleRadiusWithMb(pointLayerId, mbMap); } + setMBPropertiesForLabelText({ alpha, mbMap, textLayerId }) { + mbMap.setLayoutProperty(textLayerId, 'icon-allow-overlap', true); + mbMap.setLayoutProperty(textLayerId, 'text-allow-overlap', true); + this._labelStyleProperty.syncTextFieldWithMb(textLayerId, mbMap); + this._labelColorStyleProperty.syncLabelColorWithMb(textLayerId, mbMap, alpha); + this._labelSizeStyleProperty.syncLabelSizeWithMb(textLayerId, mbMap); + } + setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) { const symbolId = this._descriptor.properties.symbol.options.symbolId; mbMap.setLayoutProperty(symbolLayerId, 'icon-ignore-placement', true); @@ -620,4 +664,17 @@ export class VectorStyle extends AbstractStyle { throw new Error(`${descriptor} not implemented`); } } + + _makeLabelProperty(descriptor) { + if (!descriptor || !descriptor.options) { + return new StaticTextProperty({ value: '' }, VECTOR_STYLES.LABEL_TEXT); + } else if (descriptor.type === StaticStyleProperty.type) { + return new StaticTextProperty(descriptor.options, VECTOR_STYLES.LABEL_TEXT); + } else if (descriptor.type === DynamicStyleProperty.type) { + const field = this._makeField(descriptor.options.field); + return new DynamicTextProperty(descriptor.options, VECTOR_STYLES.LABEL_TEXT, field); + } else { + throw new Error(`${descriptor} not implemented`); + } + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js index 1242d7307dc48..aa0badd5583d5 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js @@ -96,6 +96,24 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => { }, type: 'DYNAMIC', }, + labelText: { + options: { + value: '', + }, + type: 'STATIC', + }, + labelColor: { + options: { + color: '#000000', + }, + type: 'STATIC', + }, + labelSize: { + options: { + size: 14, + }, + type: 'STATIC', + }, lineColor: { options: {}, type: 'DYNAMIC', diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js index 3f2851fa092cd..4bae90c3165f2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js @@ -7,11 +7,14 @@ import { VectorStyle } from './vector_style'; import { SYMBOLIZE_AS_CIRCLE, DEFAULT_ICON_SIZE } from './vector_constants'; import { COLOR_GRADIENTS, DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../color_utils'; +import chrome from 'ui/chrome'; const DEFAULT_ICON = 'airfield'; export const MIN_SIZE = 1; export const MAX_SIZE = 64; +export const DEFAULT_MIN_SIZE = 4; +export const DEFAULT_MAX_SIZE = 32; export const DEFAULT_SIGMA = 3; export const VECTOR_STYLES = { @@ -21,6 +24,9 @@ export const VECTOR_STYLES = { LINE_WIDTH: 'lineWidth', ICON_SIZE: 'iconSize', ICON_ORIENTATION: 'iconOrientation', + LABEL_TEXT: 'labelText', + LABEL_COLOR: 'labelColor', + LABEL_SIZE: 'labelSize', }; export const LINE_STYLES = [VECTOR_STYLES.LINE_COLOR, VECTOR_STYLES.LINE_WIDTH]; @@ -49,6 +55,8 @@ export function getDefaultStaticProperties(mapColors = []) { const nextFillColor = DEFAULT_FILL_COLORS[nextColorIndex]; const nextLineColor = DEFAULT_LINE_COLORS[nextColorIndex]; + const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode', false); + return { [VECTOR_STYLES.FILL_COLOR]: { type: VectorStyle.STYLE_TYPE.STATIC, @@ -80,6 +88,24 @@ export function getDefaultStaticProperties(mapColors = []) { orientation: 0, }, }, + [VECTOR_STYLES.LABEL_TEXT]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + value: '', + }, + }, + [VECTOR_STYLES.LABEL_COLOR]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + color: isDarkMode ? '#FFFFFF' : '#000000', + }, + }, + [VECTOR_STYLES.LABEL_SIZE]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + size: 14, + }, + }, }; } @@ -122,8 +148,8 @@ export function getDefaultDynamicProperties() { [VECTOR_STYLES.ICON_SIZE]: { type: VectorStyle.STYLE_TYPE.DYNAMIC, options: { - minSize: 4, - maxSize: 32, + minSize: DEFAULT_MIN_SIZE, + maxSize: DEFAULT_MAX_SIZE, field: undefined, fieldMetaOptions: { isEnabled: true, @@ -141,5 +167,34 @@ export function getDefaultDynamicProperties() { }, }, }, + [VECTOR_STYLES.LABEL_TEXT]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + field: undefined, + }, + }, + [VECTOR_STYLES.LABEL_COLOR]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + color: COLOR_GRADIENTS[0].value, + field: undefined, + fieldMetaOptions: { + isEnabled: true, + sigma: DEFAULT_SIGMA, + }, + }, + }, + [VECTOR_STYLES.LABEL_SIZE]: { + type: VectorStyle.STYLE_TYPE.STATIC, + options: { + minSize: DEFAULT_MIN_SIZE, + maxSize: DEFAULT_MAX_SIZE, + field: undefined, + fieldMetaOptions: { + isEnabled: true, + sigma: DEFAULT_SIGMA, + }, + }, + }, }; } 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 30c47658bb327..6ebc1b3d95250 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -191,24 +191,33 @@ export class VectorLayer extends AbstractLayer { return this._source.getDisplayName(); } + _getJoinFields() { + const joinFields = []; + this.getValidJoins().forEach(join => { + const fields = join.getJoinFields(); + joinFields.push(...fields); + }); + return joinFields; + } + async getDateFields() { return await this._source.getDateFields(); } async getNumberFields() { const numberFieldOptions = await this._source.getNumberFields(); - const joinFields = []; - this.getValidJoins().forEach(join => { - const fields = join.getJoinFields(); - joinFields.push(...fields); - }); - return [...numberFieldOptions, ...joinFields]; + return [...numberFieldOptions, ...this._getJoinFields()]; } async getOrdinalFields() { return [...(await this.getDateFields()), ...(await this.getNumberFields())]; } + async getFields() { + const sourceFields = await this._source.getFields(); + return [...sourceFields, ...this._getJoinFields()]; + } + getIndexPatternIds() { const indexPatternIds = this._source.getIndexPatternIds(); this.getValidJoins().forEach(join => { @@ -621,30 +630,40 @@ export class VectorLayer extends AbstractLayer { const pointLayer = mbMap.getLayer(pointLayerId); const symbolLayer = mbMap.getLayer(symbolLayerId); - let mbLayerId; + // Point layers symbolized as circles require 2 mapbox layers because + // "circle" layers do not support "text" style properties + // Point layers symbolized as icons only contain a single mapbox layer. + let markerLayerId; + let textLayerId; if (this._style.arePointsSymbolizedAsCircles()) { - mbLayerId = pointLayerId; + markerLayerId = pointLayerId; + textLayerId = this._getMbTextLayerId(); if (symbolLayer) { mbMap.setLayoutProperty(symbolLayerId, 'visibility', 'none'); } this._setMbCircleProperties(mbMap); } else { - mbLayerId = symbolLayerId; + markerLayerId = symbolLayerId; + textLayerId = symbolLayerId; if (pointLayer) { mbMap.setLayoutProperty(pointLayerId, 'visibility', 'none'); + mbMap.setLayoutProperty(this._getMbTextLayerId(), 'visibility', 'none'); } this._setMbSymbolProperties(mbMap); } - this.syncVisibilityWithMb(mbMap, mbLayerId); - mbMap.setLayerZoomRange(mbLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); + this.syncVisibilityWithMb(mbMap, markerLayerId); + mbMap.setLayerZoomRange(markerLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); + if (markerLayerId !== textLayerId) { + this.syncVisibilityWithMb(mbMap, textLayerId); + mbMap.setLayerZoomRange(textLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); + } } _setMbCircleProperties(mbMap) { const sourceId = this.getId(); const pointLayerId = this._getMbPointLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); - if (!pointLayer) { mbMap.addLayer({ id: pointLayerId, @@ -654,15 +673,32 @@ export class VectorLayer extends AbstractLayer { }); } + const textLayerId = this._getMbTextLayerId(); + const textLayer = mbMap.getLayer(textLayerId); + if (!textLayer) { + mbMap.addLayer({ + id: textLayerId, + type: 'symbol', + source: sourceId, + }); + } + const filterExpr = getPointFilterExpression(this._hasJoins()); if (filterExpr !== mbMap.getFilter(pointLayerId)) { mbMap.setFilter(pointLayerId, filterExpr); + mbMap.setFilter(textLayerId, filterExpr); } this._style.setMBPaintPropertiesForPoints({ alpha: this.getAlpha(), mbMap, - pointLayerId: pointLayerId, + pointLayerId, + }); + + this._style.setMBPropertiesForLabelText({ + alpha: this.getAlpha(), + mbMap, + textLayerId, }); } @@ -687,7 +723,13 @@ export class VectorLayer extends AbstractLayer { this._style.setMBSymbolPropertiesForPoints({ alpha: this.getAlpha(), mbMap, - symbolLayerId: symbolLayerId, + symbolLayerId, + }); + + this._style.setMBPropertiesForLabelText({ + alpha: this.getAlpha(), + mbMap, + textLayerId: symbolLayerId, }); } @@ -759,6 +801,10 @@ export class VectorLayer extends AbstractLayer { return this.makeMbLayerId('circle'); } + _getMbTextLayerId() { + return this.makeMbLayerId('text'); + } + _getMbSymbolLayerId() { return this.makeMbLayerId('symbol'); } @@ -774,6 +820,7 @@ export class VectorLayer extends AbstractLayer { getMbLayerIds() { return [ this._getMbPointLayerId(), + this._getMbTextLayerId(), this._getMbSymbolLayerId(), this._getMbLineLayerId(), this._getMbPolygonLayerId(), @@ -781,12 +828,7 @@ export class VectorLayer extends AbstractLayer { } ownsMbLayerId(mbLayerId) { - return ( - this._getMbPointLayerId() === mbLayerId || - this._getMbLineLayerId() === mbLayerId || - this._getMbPolygonLayerId() === mbLayerId || - this._getMbSymbolLayerId() === mbLayerId - ); + return this.getMbLayerIds().includes(mbLayerId); } ownsMbSourceId(mbSourceId) { diff --git a/x-pack/test/functional/apps/maps/joins.js b/x-pack/test/functional/apps/maps/joins.js index cbe7c98cd881d..89a6c6ea82e53 100644 --- a/x-pack/test/functional/apps/maps/joins.js +++ b/x-pack/test/functional/apps/maps/joins.js @@ -18,6 +18,9 @@ const EXPECTED_JOIN_VALUES = { }; const VECTOR_SOURCE_ID = 'n1t6f'; +const CIRCLE_STYLE_LAYER_INDEX = 0; +const FILL_STYLE_LAYER_INDEX = 2; +const LINE_STYLE_LAYER_INDEX = 3; export default function({ getPageObjects, getService }) { const PageObjects = getPageObjects(['maps']); @@ -82,19 +85,21 @@ export default function({ getPageObjects, getService }) { const layersForVectorSource = mapboxStyle.layers.filter(mbLayer => { return mbLayer.id.startsWith(VECTOR_SOURCE_ID); }); + // Color is dynamically obtained from eui source lib - const dynamicColor = layersForVectorSource[0].paint['circle-stroke-color']; + const dynamicColor = + layersForVectorSource[CIRCLE_STYLE_LAYER_INDEX].paint['circle-stroke-color']; //circle layer for points - expect(layersForVectorSource[0]).to.eql( + expect(layersForVectorSource[CIRCLE_STYLE_LAYER_INDEX]).to.eql( _.set(MAPBOX_STYLES.POINT_LAYER, 'paint.circle-stroke-color', dynamicColor) ); //fill layer - expect(layersForVectorSource[1]).to.eql(MAPBOX_STYLES.FILL_LAYER); + expect(layersForVectorSource[FILL_STYLE_LAYER_INDEX]).to.eql(MAPBOX_STYLES.FILL_LAYER); //line layer for borders - expect(layersForVectorSource[2]).to.eql( + expect(layersForVectorSource[LINE_STYLE_LAYER_INDEX]).to.eql( _.set(MAPBOX_STYLES.LINE_LAYER, 'paint.line-color', dynamicColor) ); });