mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Maps] Only show vector style properties used by source in editor and show line icon in legend (#36280)
* supported features POC * render point, line and polygon properties * cleanup from merge with master * line icon * display button group for feature type styling * fix vector_style tests * remove unused function * rename getSupportedFeatures to getSupportedShapeTypes and VECTOR_FEATURE_TYPE to VECTOR_SHAPE_TYPES * review feedback * display feature styles when user selects feature type * create constants for geo_json types * change line icon to line instead of rect * add help function to avoid duplicate logic for isPointsOnly, isLineOnly, and isPolygonOnly * add unit tests for _checkIfOnlyFeatureType
This commit is contained in:
parent
2347106fe6
commit
8202a1da61
16 changed files with 529 additions and 116 deletions
|
@ -33,3 +33,13 @@ export const ES_GEO_FIELD_TYPE = {
|
|||
GEO_POINT: 'geo_point',
|
||||
GEO_SHAPE: 'geo_shape'
|
||||
};
|
||||
|
||||
export const GEO_JSON_TYPE = {
|
||||
POINT: 'Point',
|
||||
MULTI_POINT: 'MultiPoint',
|
||||
LINE_STRING: 'LineString',
|
||||
MULTI_LINE_STRING: 'MultiLineString',
|
||||
POLYGON: 'Polygon',
|
||||
MULTI_POLYGON: 'MultiPolygon',
|
||||
GEOMETRY_COLLECTION: 'GeometryCollection',
|
||||
};
|
||||
|
|
|
@ -658,7 +658,7 @@ export function updateLayerStyle(layerId, styleDescriptor) {
|
|||
}
|
||||
|
||||
export function updateStyleMeta(layerId) {
|
||||
return (dispatch, getState) => {
|
||||
return async (dispatch, getState) => {
|
||||
const layer = getLayerById(layerId, getState());
|
||||
if (!layer) {
|
||||
return;
|
||||
|
@ -668,10 +668,11 @@ export function updateStyleMeta(layerId) {
|
|||
if (!style || !sourceDataRequest) {
|
||||
return;
|
||||
}
|
||||
const styleMeta = await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
dispatch({
|
||||
type: SET_LAYER_STYLE_META,
|
||||
layerId,
|
||||
styleMeta: style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest),
|
||||
styleMeta,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import _ from 'lodash';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { parse } from 'wellknown';
|
||||
import { decodeGeoHash } from 'ui/utils/decode_geo_hash';
|
||||
import { DECIMAL_DEGREES_PRECISION, ES_GEO_FIELD_TYPE } from '../common/constants';
|
||||
import { DECIMAL_DEGREES_PRECISION, ES_GEO_FIELD_TYPE, GEO_JSON_TYPE } from '../common/constants';
|
||||
|
||||
/**
|
||||
* Converts Elasticsearch search results into GeoJson FeatureCollection
|
||||
|
@ -60,7 +60,7 @@ export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType) {
|
|||
|
||||
function pointGeometryFactory(lat, lon) {
|
||||
return {
|
||||
type: 'Point',
|
||||
type: GEO_JSON_TYPE.POINT,
|
||||
coordinates: [lon, lat]
|
||||
};
|
||||
}
|
||||
|
@ -131,25 +131,25 @@ export function convertESShapeToGeojsonGeometry(value) {
|
|||
// The below is the correction in-place.
|
||||
switch (value.type) {
|
||||
case 'point':
|
||||
geoJson.type = 'Point';
|
||||
geoJson.type = GEO_JSON_TYPE.POINT;
|
||||
break;
|
||||
case 'linestring':
|
||||
geoJson.type = 'LineString';
|
||||
geoJson.type = GEO_JSON_TYPE.LINE_STRING;
|
||||
break;
|
||||
case 'polygon':
|
||||
geoJson.type = 'Polygon';
|
||||
geoJson.type = GEO_JSON_TYPE.POLYGON;
|
||||
break;
|
||||
case 'multipoint':
|
||||
geoJson.type = 'MultiPoint';
|
||||
geoJson.type = GEO_JSON_TYPE.MULTI_POINT;
|
||||
break;
|
||||
case 'multilinestring':
|
||||
geoJson.type = 'MultiLineString';
|
||||
geoJson.type = GEO_JSON_TYPE.MULTI_LINE_STRING;
|
||||
break;
|
||||
case 'multipolygon':
|
||||
geoJson.type = 'MultiPolygon';
|
||||
geoJson.type = GEO_JSON_TYPE.MULTI_POLYGON;
|
||||
break;
|
||||
case 'geometrycollection':
|
||||
geoJson.type = 'GeometryCollection';
|
||||
geoJson.type = GEO_JSON_TYPE.GEOMETRY_COLLECTION;
|
||||
break;
|
||||
case 'envelope':
|
||||
case 'circle':
|
||||
|
|
|
@ -19,7 +19,7 @@ import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../store/util';
|
|||
|
||||
function createLayerInstance(layerDescriptor, inspectorAdapters) {
|
||||
const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters);
|
||||
const style = createStyleInstance(layerDescriptor.style);
|
||||
const style = createStyleInstance(layerDescriptor.style, source);
|
||||
switch (layerDescriptor.type) {
|
||||
case TileLayer.type:
|
||||
return new TileLayer({ layerDescriptor, source, style });
|
||||
|
@ -43,7 +43,7 @@ function createSourceInstance(sourceDescriptor, inspectorAdapters) {
|
|||
}
|
||||
|
||||
|
||||
function createStyleInstance(styleDescriptor) {
|
||||
function createStyleInstance(styleDescriptor, source) {
|
||||
|
||||
if (!styleDescriptor || !styleDescriptor.type) {
|
||||
return null;
|
||||
|
@ -51,7 +51,7 @@ function createStyleInstance(styleDescriptor) {
|
|||
|
||||
switch (styleDescriptor.type) {
|
||||
case VectorStyle.type:
|
||||
return new VectorStyle(styleDescriptor);
|
||||
return new VectorStyle(styleDescriptor, source);
|
||||
case TileStyle.type:
|
||||
return new TileStyle(styleDescriptor);
|
||||
case HeatmapStyle.type:
|
||||
|
|
|
@ -54,3 +54,9 @@ export const FillableRectangle = ({ style }) => (
|
|||
<rect width="15" height="15" x=".5" y=".5" style={style} rx="4"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ColorableLine = ({ style }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<line x1="0" y1="6" x2="16" y2="6" style={style} />
|
||||
</svg>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { AbstractVectorSource } from '../vector_source';
|
||||
import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
|
||||
import React from 'react';
|
||||
import { GIS_API_PATH, EMS_FILE } from '../../../../../common/constants';
|
||||
import { getEmsVectorFilesMeta } from '../../../../meta';
|
||||
|
@ -116,4 +117,8 @@ export class EMSFileSource extends AbstractVectorSource {
|
|||
return true;
|
||||
}
|
||||
|
||||
async getSupportedShapeTypes() {
|
||||
return [VECTOR_SHAPE_TYPES.POLYGON];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
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';
|
||||
|
@ -302,11 +303,11 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
}
|
||||
|
||||
const layerDescriptor = this._createDefaultLayerDescriptor(options);
|
||||
const style = new VectorStyle(layerDescriptor.style);
|
||||
const style = new VectorStyle(layerDescriptor.style, this);
|
||||
return new VectorLayer({
|
||||
layerDescriptor: layerDescriptor,
|
||||
source: this,
|
||||
style: style
|
||||
style
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -317,4 +318,12 @@ export class ESGeoGridSource extends AbstractESSource {
|
|||
async filterAndFormatPropertiesToHtml(properties) {
|
||||
return await this.filterAndFormatPropertiesToHtmlForMetricFields(properties);
|
||||
}
|
||||
|
||||
async getSupportedShapeTypes() {
|
||||
if (this._descriptor.requestType === RENDER_AS.GRID) {
|
||||
return [VECTOR_SHAPE_TYPES.POLYGON];
|
||||
}
|
||||
|
||||
return [VECTOR_SHAPE_TYPES.POINT];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@ import _ from 'lodash';
|
|||
import React from 'react';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
|
||||
import { AbstractESSource } from '../es_source';
|
||||
import { hitsToGeoJson } from '../../../../elasticsearch_geo_utils';
|
||||
import { CreateSourceEditor } from './create_source_editor';
|
||||
import { UpdateSourceEditor } from './update_source_editor';
|
||||
import { ES_SEARCH } from '../../../../../common/constants';
|
||||
import { ES_SEARCH, ES_GEO_FIELD_TYPE } from '../../../../../common/constants';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel } from '../../../../../common/i18n_getters';
|
||||
import { ESTooltipProperty } from '../../tooltips/es_tooltip_property';
|
||||
|
@ -197,6 +198,26 @@ export class ESSearchSource extends AbstractESSource {
|
|||
});
|
||||
}
|
||||
|
||||
async getSupportedShapeTypes() {
|
||||
let geoFieldType;
|
||||
try {
|
||||
const geoField = this._getGeoField();
|
||||
geoFieldType = geoField.type;
|
||||
} catch(error) {
|
||||
// ignore exeception
|
||||
}
|
||||
|
||||
if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT) {
|
||||
return [VECTOR_SHAPE_TYPES.POINT];
|
||||
}
|
||||
|
||||
return [
|
||||
VECTOR_SHAPE_TYPES.POINT,
|
||||
VECTOR_SHAPE_TYPES.LINE,
|
||||
VECTOR_SHAPE_TYPES.POLYGON
|
||||
];
|
||||
}
|
||||
|
||||
getSourceTooltipContent(sourceDataRequest) {
|
||||
const featureCollection = sourceDataRequest ? sourceDataRequest.getData() : null;
|
||||
const meta = sourceDataRequest ? sourceDataRequest.getMeta() : {};
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const VECTOR_SHAPE_TYPES = {
|
||||
POINT: 'POINT',
|
||||
LINE: 'LINE',
|
||||
POLYGON: 'POLYGON'
|
||||
};
|
|
@ -12,6 +12,7 @@ import { AbstractSource } from './source';
|
|||
import * as topojson from 'topojson-client';
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VECTOR_SHAPE_TYPES } from './vector_feature_types';
|
||||
|
||||
export class AbstractVectorSource extends AbstractSource {
|
||||
|
||||
|
@ -58,11 +59,11 @@ export class AbstractVectorSource extends AbstractSource {
|
|||
|
||||
createDefaultLayer(options, mapColors) {
|
||||
const layerDescriptor = this._createDefaultLayerDescriptor(options, mapColors);
|
||||
const style = new VectorStyle(layerDescriptor.style);
|
||||
const style = new VectorStyle(layerDescriptor.style, this);
|
||||
return new VectorLayer({
|
||||
layerDescriptor: layerDescriptor,
|
||||
source: this,
|
||||
style: style
|
||||
style
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -115,6 +116,14 @@ export class AbstractVectorSource extends AbstractSource {
|
|||
return true;
|
||||
}
|
||||
|
||||
async getSupportedShapeTypes() {
|
||||
return [
|
||||
VECTOR_SHAPE_TYPES.POINT,
|
||||
VECTOR_SHAPE_TYPES.LINE,
|
||||
VECTOR_SHAPE_TYPES.POLYGON
|
||||
];
|
||||
}
|
||||
|
||||
getSourceTooltipContent(/* sourceDataRequest */) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export class AbstractStyle {
|
|||
};
|
||||
}
|
||||
|
||||
pluckStyleMetaFromSourceDataRequest(/* sourceDataRequest */) {
|
||||
async pluckStyleMetaFromSourceDataRequest(/* sourceDataRequest */) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,24 +4,66 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { dynamicColorShape, staticColorShape } from '../style_option_shapes';
|
||||
import { FillableCircle, FillableRectangle } from '../../../../../icons/additional_layer_icons';
|
||||
import { ColorableLine, FillableCircle, FillableRectangle } from '../../../../../icons/additional_layer_icons';
|
||||
import { VectorStyle } from '../../../vector_style';
|
||||
import { getColorRampCenterColor } from '../../../../../utils/color_utils';
|
||||
|
||||
export function VectorIcon({ fillColor, lineColor, isPointsOnly }) {
|
||||
const style = {
|
||||
stroke: extractColorFromStyleProperty(lineColor, 'none'),
|
||||
strokeWidth: '1px',
|
||||
fill: extractColorFromStyleProperty(fillColor, 'grey'),
|
||||
};
|
||||
export class VectorIcon extends Component {
|
||||
|
||||
return isPointsOnly
|
||||
? <FillableCircle style={style}/>
|
||||
: <FillableRectangle style={style}/>;
|
||||
state = {
|
||||
isInitialized: false
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this._init();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
async _init() {
|
||||
const isPointsOnly = await this.props.loadIsPointsOnly();
|
||||
const isLinesOnly = await this.props.loadIsLinesOnly();
|
||||
if (this._isMounted) {
|
||||
this.setState({
|
||||
isInitialized: true,
|
||||
isPointsOnly,
|
||||
isLinesOnly,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.isInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.isLinesOnly) {
|
||||
const style = {
|
||||
stroke: extractColorFromStyleProperty(this.props.lineColor, 'grey'),
|
||||
strokeWidth: '4px',
|
||||
};
|
||||
return (
|
||||
<ColorableLine style={style}/>
|
||||
);
|
||||
}
|
||||
|
||||
const style = {
|
||||
stroke: extractColorFromStyleProperty(this.props.lineColor, 'none'),
|
||||
strokeWidth: '1px',
|
||||
fill: extractColorFromStyleProperty(this.props.fillColor, 'grey'),
|
||||
};
|
||||
|
||||
return this.state.isPointsOnly
|
||||
? <FillableCircle style={style}/>
|
||||
: <FillableRectangle style={style}/>;
|
||||
}
|
||||
}
|
||||
|
||||
function extractColorFromStyleProperty(colorStyleProperty, defaultColor) {
|
||||
|
@ -48,5 +90,6 @@ const colorStylePropertyShape = PropTypes.shape({
|
|||
VectorIcon.propTypes = {
|
||||
fillColor: colorStylePropertyShape,
|
||||
lineColor: colorStylePropertyShape,
|
||||
isPointsOnly: PropTypes.bool,
|
||||
loadIsPointsOnly: PropTypes.func.isRequired,
|
||||
loadIsLinesOnly: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -10,14 +10,18 @@ import React, { Component, Fragment } from 'react';
|
|||
import { VectorStyleColorEditor } from './color/vector_style_color_editor';
|
||||
import { VectorStyleSizeEditor } from './size/vector_style_size_editor';
|
||||
import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../../vector_style_defaults';
|
||||
import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { EuiSpacer, EuiButtonGroup } from '@elastic/eui';
|
||||
|
||||
export class VectorStyleEditor extends Component {
|
||||
state = {
|
||||
ordinalFields: [],
|
||||
defaultDynamicProperties: getDefaultDynamicProperties(),
|
||||
defaultStaticProperties: getDefaultStaticProperties()
|
||||
defaultStaticProperties: getDefaultStaticProperties(),
|
||||
supportedFeatures: undefined,
|
||||
selectedFeatureType: undefined,
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -27,10 +31,12 @@ export class VectorStyleEditor extends Component {
|
|||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this._loadOrdinalFields();
|
||||
this._loadSupportedFeatures();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._loadOrdinalFields();
|
||||
this._loadSupportedFeatures();
|
||||
}
|
||||
|
||||
async _loadOrdinalFields() {
|
||||
|
@ -43,52 +49,200 @@ export class VectorStyleEditor extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
async _loadSupportedFeatures() {
|
||||
const supportedFeatures = await this.props.layer.getSource().getSupportedShapeTypes();
|
||||
const isPointsOnly = await this.props.loadIsPointsOnly();
|
||||
const isLinesOnly = await this.props.loadIsLinesOnly();
|
||||
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isEqual(supportedFeatures, this.state.supportedFeatures)
|
||||
&& isPointsOnly === this.state.isPointsOnly
|
||||
&& isLinesOnly === this.state.isLinesOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
let selectedFeature = VECTOR_SHAPE_TYPES.POLYGON;
|
||||
if (isPointsOnly) {
|
||||
selectedFeature = VECTOR_SHAPE_TYPES.POINT;
|
||||
} else if (isLinesOnly) {
|
||||
selectedFeature = VECTOR_SHAPE_TYPES.LINE;
|
||||
}
|
||||
|
||||
if (!_.isEqual(supportedFeatures, this.state.supportedFeatures) ||
|
||||
selectedFeature !== this.state.selectedFeature) {
|
||||
this.setState({
|
||||
supportedFeatures,
|
||||
selectedFeature,
|
||||
isPointsOnly,
|
||||
isLinesOnly,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_renderFillColor() {
|
||||
return (
|
||||
<VectorStyleColorEditor
|
||||
styleProperty="fillColor"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.fillColor}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.fillColor.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.fillColor.options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderLineColor() {
|
||||
return (
|
||||
<VectorStyleColorEditor
|
||||
styleProperty="lineColor"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.lineColor}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.lineColor.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.lineColor.options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderLineWidth() {
|
||||
return (
|
||||
<VectorStyleSizeEditor
|
||||
styleProperty="lineWidth"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.lineWidth}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.lineWidth.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.lineWidth.options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSymbolSize() {
|
||||
return (
|
||||
<VectorStyleSizeEditor
|
||||
styleProperty="iconSize"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.iconSize}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.iconSize.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.iconSize.options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPointProperties() {
|
||||
return (
|
||||
<Fragment>
|
||||
{this._renderFillColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<VectorStyleColorEditor
|
||||
styleProperty="fillColor"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.fillColor}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.fillColor.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.fillColor.options}
|
||||
{this._renderLineColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{this._renderLineWidth()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{this._renderSymbolSize()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
_renderLineProperties() {
|
||||
return (
|
||||
<Fragment>
|
||||
{this._renderLineColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{this._renderLineWidth()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPolygonProperties() {
|
||||
return (
|
||||
<Fragment>
|
||||
{this._renderFillColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{this._renderLineColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{this._renderLineWidth()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
_handleSelectedFeatureChange = selectedFeature => {
|
||||
this.setState({ selectedFeature });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
supportedFeatures,
|
||||
selectedFeature,
|
||||
} = this.state;
|
||||
|
||||
if (!supportedFeatures) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (supportedFeatures.length === 1) {
|
||||
switch (supportedFeatures[0]) {
|
||||
case VECTOR_SHAPE_TYPES.POINT:
|
||||
return this._renderPointProperties();
|
||||
case VECTOR_SHAPE_TYPES.LINE:
|
||||
return this._renderLineProperties();
|
||||
case VECTOR_SHAPE_TYPES.POLYGON:
|
||||
return this._renderPolygonProperties();
|
||||
}
|
||||
}
|
||||
|
||||
const featureButtons = [
|
||||
{
|
||||
id: VECTOR_SHAPE_TYPES.POINT,
|
||||
label: i18n.translate('xpack.maps.vectorStyleEditor.pointLabel', {
|
||||
defaultMessage: 'Points'
|
||||
})
|
||||
},
|
||||
{
|
||||
id: VECTOR_SHAPE_TYPES.LINE,
|
||||
label: i18n.translate('xpack.maps.vectorStyleEditor.lineLabel', {
|
||||
defaultMessage: 'Lines'
|
||||
})
|
||||
},
|
||||
{
|
||||
id: VECTOR_SHAPE_TYPES.POLYGON,
|
||||
label: i18n.translate('xpack.maps.vectorStyleEditor.polygonLabel', {
|
||||
defaultMessage: 'Polygons'
|
||||
})
|
||||
}
|
||||
];
|
||||
|
||||
let styleProperties = this._renderPolygonProperties();
|
||||
if (selectedFeature === VECTOR_SHAPE_TYPES.LINE) {
|
||||
styleProperties = this._renderLineProperties();
|
||||
} else if (selectedFeature === VECTOR_SHAPE_TYPES.POINT) {
|
||||
styleProperties = this._renderPointProperties();
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiButtonGroup
|
||||
legend={i18n.translate('xpack.maps.vectorStyleEditor.featureTypeButtonGroupLegend', {
|
||||
defaultMessage: 'vector feature button group'
|
||||
})}
|
||||
options={featureButtons}
|
||||
idSelected={selectedFeature}
|
||||
onChange={this._handleSelectedFeatureChange}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<VectorStyleColorEditor
|
||||
styleProperty="lineColor"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.lineColor}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.lineColor.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.lineColor.options}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<VectorStyleSizeEditor
|
||||
styleProperty="lineWidth"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.lineWidth}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.lineWidth.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.lineWidth.options}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<VectorStyleSizeEditor
|
||||
styleProperty="iconSize"
|
||||
handlePropertyChange={this.props.handlePropertyChange}
|
||||
styleDescriptor={this.props.styleProperties.iconSize}
|
||||
ordinalFields={this.state.ordinalFields}
|
||||
defaultStaticStyleOptions={this.state.defaultStaticProperties.iconSize.options}
|
||||
defaultDynamicStyleOptions={this.state.defaultDynamicProperties.iconSize.options}
|
||||
/>
|
||||
|
||||
{styleProperties}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,10 @@ import { getHexColorRangeStrings } from '../../utils/color_utils';
|
|||
import { VectorStyleEditor } from './components/vector/vector_style_editor';
|
||||
import { getDefaultStaticProperties } from './vector_style_defaults';
|
||||
import { AbstractStyle } from './abstract_style';
|
||||
import { SOURCE_DATA_ID_ORIGIN } from '../../../../common/constants';
|
||||
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';
|
||||
|
||||
export class VectorStyle extends AbstractStyle {
|
||||
|
||||
|
@ -24,8 +25,9 @@ export class VectorStyle extends AbstractStyle {
|
|||
return `__kbn__scaled(${fieldName})`;
|
||||
}
|
||||
|
||||
constructor(descriptor = {}) {
|
||||
constructor(descriptor = {}, source) {
|
||||
super();
|
||||
this._source = source;
|
||||
this._descriptor = {
|
||||
...descriptor,
|
||||
...VectorStyle.createDescriptor(descriptor.properties),
|
||||
|
@ -64,6 +66,8 @@ export class VectorStyle extends AbstractStyle {
|
|||
handlePropertyChange={handlePropertyChange}
|
||||
styleProperties={styleProperties}
|
||||
layer={layer}
|
||||
loadIsPointsOnly={this._getIsPointsOnly}
|
||||
loadIsLinesOnly={this._getIsLinesOnly}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -125,7 +129,7 @@ export class VectorStyle extends AbstractStyle {
|
|||
};
|
||||
}
|
||||
|
||||
pluckStyleMetaFromSourceDataRequest(sourceDataRequest) {
|
||||
async pluckStyleMetaFromSourceDataRequest(sourceDataRequest) {
|
||||
const features = _.get(sourceDataRequest.getData(), 'features', []);
|
||||
if (features.length === 0) {
|
||||
return {};
|
||||
|
@ -140,12 +144,29 @@ export class VectorStyle extends AbstractStyle {
|
|||
};
|
||||
});
|
||||
|
||||
let isPointsOnly = true;
|
||||
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 (isPointsOnly && feature.geometry.type !== 'Point') {
|
||||
isPointsOnly = false;
|
||||
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]);
|
||||
|
@ -157,7 +178,11 @@ export class VectorStyle extends AbstractStyle {
|
|||
}
|
||||
|
||||
const featuresMeta = {
|
||||
isPointsOnly
|
||||
hasFeatureType: {
|
||||
[VECTOR_SHAPE_TYPES.POINT]: hasPoints,
|
||||
[VECTOR_SHAPE_TYPES.LINE]: hasLines,
|
||||
[VECTOR_SHAPE_TYPES.POLYGON]: hasPolygons
|
||||
}
|
||||
};
|
||||
|
||||
scaledFields.forEach(({ min, max, name }) => {
|
||||
|
@ -215,8 +240,36 @@ export class VectorStyle extends AbstractStyle {
|
|||
return type === VectorStyle.STYLE_TYPE.DYNAMIC && options.field && options.field.name;
|
||||
}
|
||||
|
||||
_getIsPointsOnly = () => {
|
||||
return _.get(this._descriptor, '__styleMeta.isPointsOnly', false);
|
||||
_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) => {
|
||||
|
@ -227,7 +280,8 @@ export class VectorStyle extends AbstractStyle {
|
|||
const styles = this.getProperties();
|
||||
return (
|
||||
<VectorIcon
|
||||
isPointsOnly={this._getIsPointsOnly()}
|
||||
loadIsPointsOnly={this._getIsPointsOnly}
|
||||
loadIsLinesOnly={this._getIsLinesOnly}
|
||||
fillColor={styles.fillColor}
|
||||
lineColor={styles.lineColor}
|
||||
/>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { VectorStyle } from './vector_style';
|
||||
import { DataRequest } from '../util/data_request';
|
||||
import { VECTOR_SHAPE_TYPES } from '../sources/vector_feature_types';
|
||||
|
||||
describe('getDescriptorWithMissingStylePropsRemoved', () => {
|
||||
const fieldName = 'doIStillExist';
|
||||
|
@ -70,8 +71,14 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => {
|
|||
|
||||
describe('pluckStyleMetaFromSourceDataRequest', () => {
|
||||
|
||||
describe('isPointsOnly', () => {
|
||||
it('Should identify when feature collection only contains points', () => {
|
||||
const sourceMock = {
|
||||
getSupportedShapeTypes: () => {
|
||||
return Object.values(VECTOR_SHAPE_TYPES);
|
||||
}
|
||||
};
|
||||
|
||||
describe('has features', () => {
|
||||
it('Should identify when feature collection only contains points', async () => {
|
||||
const sourceDataRequest = new DataRequest({
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
|
@ -84,43 +91,51 @@ describe('pluckStyleMetaFromSourceDataRequest', () => {
|
|||
},
|
||||
{
|
||||
geometry: {
|
||||
type: 'Point'
|
||||
type: 'MultiPoint'
|
||||
},
|
||||
properties: {}
|
||||
}
|
||||
],
|
||||
}
|
||||
});
|
||||
const vectorStyle = new VectorStyle({});
|
||||
const vectorStyle = new VectorStyle({}, sourceMock);
|
||||
|
||||
const featuresMeta = vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta).toEqual({ isPointsOnly: true });
|
||||
const featuresMeta = await vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta.hasFeatureType).toEqual({
|
||||
LINE: false,
|
||||
POINT: true,
|
||||
POLYGON: false
|
||||
});
|
||||
});
|
||||
|
||||
it('Should identify when feature collection contains features other than points', () => {
|
||||
it('Should identify when feature collection only contains lines', async () => {
|
||||
const sourceDataRequest = new DataRequest({
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
geometry: {
|
||||
type: 'Point'
|
||||
type: 'LineString'
|
||||
},
|
||||
properties: {}
|
||||
},
|
||||
{
|
||||
geometry: {
|
||||
type: 'Polygon'
|
||||
type: 'MultiLineString'
|
||||
},
|
||||
properties: {}
|
||||
}
|
||||
],
|
||||
}
|
||||
});
|
||||
const vectorStyle = new VectorStyle({});
|
||||
const vectorStyle = new VectorStyle({}, sourceMock);
|
||||
|
||||
const featuresMeta = vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta).toEqual({ isPointsOnly: false });
|
||||
const featuresMeta = await vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta.hasFeatureType).toEqual({
|
||||
LINE: true,
|
||||
POINT: false,
|
||||
POLYGON: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -149,7 +164,7 @@ describe('pluckStyleMetaFromSourceDataRequest', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('Should not extract scaled field range when scaled field has not values', () => {
|
||||
it('Should not extract scaled field range when scaled field has no values', async () => {
|
||||
const vectorStyle = new VectorStyle({
|
||||
properties: {
|
||||
fillColor: {
|
||||
|
@ -161,13 +176,17 @@ describe('pluckStyleMetaFromSourceDataRequest', () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, sourceMock);
|
||||
|
||||
const featuresMeta = vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta).toEqual({ isPointsOnly: true });
|
||||
const featuresMeta = await vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta.hasFeatureType).toEqual({
|
||||
LINE: false,
|
||||
POINT: true,
|
||||
POLYGON: false
|
||||
});
|
||||
});
|
||||
|
||||
it('Should extract scaled field range', () => {
|
||||
it('Should extract scaled field range', async () => {
|
||||
const vectorStyle = new VectorStyle({
|
||||
properties: {
|
||||
fillColor: {
|
||||
|
@ -179,18 +198,89 @@ describe('pluckStyleMetaFromSourceDataRequest', () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, sourceMock);
|
||||
|
||||
const featuresMeta = vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta).toEqual({
|
||||
isPointsOnly: true,
|
||||
myDynamicField: {
|
||||
delta: 9,
|
||||
max: 10,
|
||||
min: 1
|
||||
}
|
||||
const featuresMeta = await vectorStyle.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
|
||||
expect(featuresMeta.myDynamicField).toEqual({
|
||||
delta: 9,
|
||||
max: 10,
|
||||
min: 1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('checkIfOnlyFeatureType', () => {
|
||||
|
||||
describe('source supports single feature type', () => {
|
||||
const sourceMock = {
|
||||
getSupportedShapeTypes: () => {
|
||||
return [VECTOR_SHAPE_TYPES.POINT];
|
||||
}
|
||||
};
|
||||
|
||||
it('isPointsOnly should be true when source feature type only supports points', async () => {
|
||||
const vectorStyle = new VectorStyle({}, sourceMock);
|
||||
const isPointsOnly = await vectorStyle._getIsPointsOnly();
|
||||
expect(isPointsOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('isLineOnly should be false when source feature type only supports points', async () => {
|
||||
const vectorStyle = new VectorStyle({}, sourceMock);
|
||||
const isLineOnly = await vectorStyle._getIsLinesOnly();
|
||||
expect(isLineOnly).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('source supports multiple feature types', () => {
|
||||
const sourceMock = {
|
||||
getSupportedShapeTypes: () => {
|
||||
return Object.values(VECTOR_SHAPE_TYPES);
|
||||
}
|
||||
};
|
||||
|
||||
it('isPointsOnly should be true when data contains just points', async () => {
|
||||
const vectorStyle = new VectorStyle({
|
||||
__styleMeta: {
|
||||
hasFeatureType: {
|
||||
POINT: true,
|
||||
LINE: false,
|
||||
POLYGON: false
|
||||
}
|
||||
}
|
||||
}, sourceMock);
|
||||
const isPointsOnly = await vectorStyle._getIsPointsOnly();
|
||||
expect(isPointsOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('isPointsOnly should be false when data contains just lines', async () => {
|
||||
const vectorStyle = new VectorStyle({
|
||||
__styleMeta: {
|
||||
hasFeatureType: {
|
||||
POINT: false,
|
||||
LINE: true,
|
||||
POLYGON: false
|
||||
}
|
||||
}
|
||||
}, sourceMock);
|
||||
const isPointsOnly = await vectorStyle._getIsPointsOnly();
|
||||
expect(isPointsOnly).toBe(false);
|
||||
});
|
||||
|
||||
it('isPointsOnly should be false when data contains points, lines, and polygons', async () => {
|
||||
const vectorStyle = new VectorStyle({
|
||||
__styleMeta: {
|
||||
hasFeatureType: {
|
||||
POINT: true,
|
||||
LINE: true,
|
||||
POLYGON: true
|
||||
}
|
||||
}
|
||||
}, sourceMock);
|
||||
const isPointsOnly = await vectorStyle._getIsPointsOnly();
|
||||
expect(isPointsOnly).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { AbstractLayer } from './layer';
|
||||
import { VectorStyle } from './styles/vector_style';
|
||||
import { LeftInnerJoin } from './joins/left_inner_join';
|
||||
import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
|
||||
import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN, GEO_JSON_TYPE } from '../../../common/constants';
|
||||
import _ from 'lodash';
|
||||
import { JoinTooltipProperty } from './tooltips/join_tooltip_property';
|
||||
import { isRefreshOnlyQuery } from './util/is_refresh_only_query';
|
||||
|
@ -24,16 +24,16 @@ const EMPTY_FEATURE_COLLECTION = {
|
|||
|
||||
const CLOSED_SHAPE_MB_FILTER = [
|
||||
'any',
|
||||
['==', ['geometry-type'], 'Polygon'],
|
||||
['==', ['geometry-type'], 'MultiPolygon']
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON],
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON]
|
||||
];
|
||||
|
||||
const ALL_SHAPE_MB_FILTER = [
|
||||
'any',
|
||||
['==', ['geometry-type'], 'Polygon'],
|
||||
['==', ['geometry-type'], 'MultiPolygon'],
|
||||
['==', ['geometry-type'], 'LineString'],
|
||||
['==', ['geometry-type'], 'MultiLineString']
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.POLYGON],
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_POLYGON],
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.LINE_STRING],
|
||||
['==', ['geometry-type'], GEO_JSON_TYPE.MULTI_LINE_STRING]
|
||||
];
|
||||
|
||||
export class VectorLayer extends AbstractLayer {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue