[Maps] pew pew source (#41504)

* [Maps][POC] pew pew source

* refetch data on zoom level change

* add metric aggs to request

* fix bug where initial draw did not have styles set up

* make tooltips work

* fix import broken with merging master

* use custom labels in tooltips

* remove duplicate keys and clean up title and desc wording

* update source description

* update references migration to extract references for pew pew source

* add percy visual test to ensure pew pew map data is fetch, processed, and visualized as expected

* update title and description

* use GEO_FIELD_TYPES in filterGeoField function

* remove unneeded Fragment wrapper

* fix typo

* update inspector description id and message
This commit is contained in:
Nathan Reese 2019-08-28 15:35:03 -06:00 committed by GitHub
parent ce2ba6d5f5
commit e42cfacb9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 832 additions and 5 deletions

View file

@ -43,6 +43,7 @@ export const EMS_TMS = 'EMS_TMS';
export const EMS_FILE = 'EMS_FILE';
export const ES_GEO_GRID = 'ES_GEO_GRID';
export const ES_SEARCH = 'ES_SEARCH';
export const ES_PEW_PEW = 'ES_PEW_PEW';
export const SOURCE_DATA_ID_ORIGIN = 'source';
export const GEOJSON_FILE = 'GEOJSON_FILE';

View file

@ -7,11 +7,11 @@
// Can not use public Layer classes to extract references since this logic must run in both client and server.
import _ from 'lodash';
import { ES_GEO_GRID, ES_SEARCH } from '../constants';
import { ES_GEO_GRID, ES_SEARCH, ES_PEW_PEW } from '../constants';
function doesSourceUseIndexPattern(layerDescriptor) {
const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type');
return sourceType === ES_GEO_GRID || sourceType === ES_SEARCH;
return sourceType === ES_GEO_GRID || sourceType === ES_SEARCH || sourceType === ES_PEW_PEW;
}
export function extractReferences({ attributes, references = [] }) {

View file

@ -7,7 +7,7 @@
/* eslint max-len: 0 */
import { extractReferences, injectReferences } from './references';
import { ES_GEO_GRID, ES_SEARCH } from '../constants';
import { ES_GEO_GRID, ES_SEARCH, ES_PEW_PEW } from '../constants';
const layerListJSON = {
esSearchSource: {
@ -21,6 +21,10 @@ const layerListJSON = {
join: {
withIndexPatternId: '[{\"joins\":[{\"right\":{\"indexPatternId\":\"e20b2a30-f735-11e8-8ce0-9723965e01e3\"}}]}]',
withIndexPatternRef: '[{\"joins\":[{\"right\":{\"indexPatternRefName\":\"layer_0_join_0_index_pattern\"}}]}]',
},
pewPewSource: {
withIndexPatternId: `[{\"sourceDescriptor\":{\"type\":\"${ES_PEW_PEW}\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\"}}]`,
withIndexPatternRef: `[{\"sourceDescriptor\":{\"type\":\"${ES_PEW_PEW}\",\"indexPatternRefName\":\"layer_0_source_index_pattern\"}}]`,
}
};
@ -78,6 +82,26 @@ describe('extractReferences', () => {
});
});
test('Should extract index-pattern reference from ES pew pew source descriptor', () => {
const attributes = {
title: 'my map',
layerListJSON: layerListJSON.pewPewSource.withIndexPatternId,
};
expect(extractReferences({ attributes })).toEqual({
attributes: {
title: 'my map',
layerListJSON: layerListJSON.pewPewSource.withIndexPatternRef,
},
references: [
{
id: 'c698b940-e149-11e8-a35a-370a8516603a',
name: 'layer_0_source_index_pattern',
type: 'index-pattern',
}
],
});
});
test('Should extract index-pattern reference from joins', () => {
const attributes = {
title: 'my map',
@ -151,6 +175,26 @@ describe('injectReferences', () => {
});
});
test('Should inject index-pattern reference into ES pew pew source descriptor', () => {
const attributes = {
title: 'my map',
layerListJSON: layerListJSON.pewPewSource.withIndexPatternRef,
};
const references = [
{
id: 'c698b940-e149-11e8-a35a-370a8516603a',
name: 'layer_0_source_index_pattern',
type: 'index-pattern',
}
];
expect(injectReferences({ attributes, references })).toEqual({
attributes: {
title: 'my map',
layerListJSON: layerListJSON.pewPewSource.withIndexPatternId,
}
});
});
test('Should inject index-pattern reference into joins', () => {
const attributes = {
title: 'my map',

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EMSFileSource } from './ems_file_source';
import { GeojsonFileSource } from './client_file_source';
import { KibanaRegionmapSource } from './kibana_regionmap_source';
@ -14,12 +13,13 @@ import { WMSSource } from './wms_source';
import { KibanaTilemapSource } from './kibana_tilemap_source';
import { ESGeoGridSource } from './es_geo_grid_source';
import { ESSearchSource } from './es_search_source';
import { ESPewPewSource } from './es_pew_pew_source/es_pew_pew_source';
export const ALL_SOURCES = [
GeojsonFileSource,
ESSearchSource,
ESGeoGridSource,
ESPewPewSource,
EMSFileSource,
EMSTMSSource,
KibanaRegionmapSource,

View file

@ -0,0 +1,61 @@
/*
* 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';
const LAT_INDEX = 0;
const LON_INDEX = 1;
function parsePointFromKey(key) {
const split = key.split(',');
const lat = parseFloat(split[LAT_INDEX]);
const lon = parseFloat(split[LON_INDEX]);
return [lon, lat];
}
export function convertToLines(esResponse) {
const lineFeatures = [];
const destBuckets = _.get(esResponse, 'aggregations.destSplit.buckets', []);
for (let i = 0; i < destBuckets.length; i++) {
const destBucket = destBuckets[i];
const dest = parsePointFromKey(destBucket.key);
const sourceBuckets = _.get(destBucket, 'sourceGrid.buckets', []);
for (let j = 0; j < sourceBuckets.length; j++) {
const {
key, // eslint-disable-line no-unused-vars
sourceCentroid,
...rest
} = sourceBuckets[j];
// flatten metrics
Object.keys(rest).forEach(key => {
if (_.has(rest[key], 'value')) {
rest[key] = rest[key].value;
}
});
lineFeatures.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [[sourceCentroid.location.lon, sourceCentroid.location.lat], dest]
},
properties: {
...rest
}
});
}
}
return {
featureCollection: {
type: 'FeatureCollection',
features: lineFeatures
}
};
}

View file

@ -0,0 +1,208 @@
/*
* 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, { Fragment, Component } from 'react';
import PropTypes from 'prop-types';
import { IndexPatternSelect } from 'ui/index_patterns';
import { SingleFieldSelect } from '../../../components/single_field_select';
import { indexPatternService } from '../../../kibana_services';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFormRow,
EuiCallOut,
} from '@elastic/eui';
import { ES_GEO_FIELD_TYPE } from '../../../../common/constants';
const GEO_FIELD_TYPES = [ES_GEO_FIELD_TYPE.GEO_POINT];
function filterGeoField({ type }) {
return GEO_FIELD_TYPES.includes(type);
}
export class CreateSourceEditor extends Component {
static propTypes = {
onSourceConfigChange: PropTypes.func.isRequired,
};
state = {
isLoadingIndexPattern: false,
indexPattern: undefined,
indexPatternId: undefined,
sourceGeoField: undefined,
destGeoField: undefined,
indexPatternHasMultipleGeoFields: false,
}
componentWillUnmount() {
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
}
onIndexPatternSelect = (indexPatternId) => {
this.setState({
indexPatternId,
}, this.loadIndexPattern.bind(null, indexPatternId));
};
loadIndexPattern = (indexPatternId) => {
this.setState({
isLoadingIndexPattern: true,
indexPattern: undefined,
sourceGeoField: undefined,
destGeoField: undefined,
indexPatternHasMultipleGeoFields: false,
}, this.debouncedLoad.bind(null, indexPatternId));
};
debouncedLoad = _.debounce(async (indexPatternId) => {
if (!indexPatternId || indexPatternId.length === 0) {
return;
}
let indexPattern;
try {
indexPattern = await indexPatternService.get(indexPatternId);
} catch (err) {
// index pattern no longer exists
return;
}
if (!this._isMounted) {
return;
}
// props.indexPatternId may be updated before getIndexPattern returns
// ignore response when fetched index pattern does not match active index pattern
if (this.state.indexPatternId !== indexPatternId) {
return;
}
const geoFields = indexPattern.fields.filter(filterGeoField);
this.setState({
isLoadingIndexPattern: false,
indexPattern: indexPattern,
indexPatternHasMultipleGeoFields: geoFields.length >= 2,
});
}, 300);
_onSourceGeoSelect = (sourceGeoField) => {
this.setState({
sourceGeoField
}, this.previewLayer);
};
_onDestGeoSelect = (destGeoField) => {
this.setState({
destGeoField
}, this.previewLayer);
};
previewLayer = () => {
const {
indexPatternId,
sourceGeoField,
destGeoField,
} = this.state;
const sourceConfig = (indexPatternId && sourceGeoField && destGeoField)
? { indexPatternId, sourceGeoField, destGeoField }
: null;
this.props.onSourceConfigChange(sourceConfig);
};
_renderGeoSelects() {
if (!this.state.indexPattern || !this.state.indexPatternHasMultipleGeoFields) {
return null;
}
return (
<Fragment>
<EuiFormRow label={i18n.translate('xpack.maps.source.pewPew.sourceGeoFieldLabel', {
defaultMessage: 'Source'
})}
>
<SingleFieldSelect
placeholder={i18n.translate('xpack.maps.source.pewPew.sourceGeoFieldPlaceholder', {
defaultMessage: 'Select source geo field'
})}
value={this.state.sourceGeoField}
onChange={this._onSourceGeoSelect}
filterField={filterGeoField}
fields={this.state.indexPattern ? this.state.indexPattern.fields : undefined}
/>
</EuiFormRow>
<EuiFormRow label={i18n.translate('xpack.maps.source.pewPew.destGeoFieldLabel', {
defaultMessage: 'Destination'
})}
>
<SingleFieldSelect
placeholder={i18n.translate('xpack.maps.source.pewPew.destGeoFieldPlaceholder', {
defaultMessage: 'Select destination geo field'
})}
value={this.state.destGeoField}
onChange={this._onDestGeoSelect}
filterField={filterGeoField}
fields={this.state.indexPattern ? this.state.indexPattern.fields : undefined}
/>
</EuiFormRow>
</Fragment>
);
}
_renderIndexPatternSelect() {
return (
<EuiFormRow label={i18n.translate('xpack.maps.source.pewPew.indexPatternLabel', {
defaultMessage: 'Index pattern'
})}
>
<IndexPatternSelect
indexPatternId={this.state.indexPatternId}
onChange={this.onIndexPatternSelect}
placeholder={i18n.translate('xpack.maps.source.pewPew.indexPatternPlaceholder', {
defaultMessage: 'Select index pattern'
})}
fieldTypes={GEO_FIELD_TYPES}
/>
</EuiFormRow>
);
}
render() {
let callout;
if (this.state.indexPattern && !this.state.indexPatternHasMultipleGeoFields) {
callout = (
<EuiCallOut
color="warning"
>
<p>
<FormattedMessage
id="xpack.maps.source.pewPew.noSourceAndDestDetails"
defaultMessage="Selected index pattern does not contain source and destination fields."
/>
</p>
</EuiCallOut>
);
}
return (
<Fragment>
{callout}
{this._renderIndexPatternSelect()}
{this._renderGeoSelects()}
</Fragment>
);
}
}

View file

@ -0,0 +1,276 @@
/*
* 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 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 { i18n } from '@kbn/i18n';
import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW } 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/vis/agg_configs';
const COUNT_PROP_LABEL = 'count';
const COUNT_PROP_NAME = 'doc_count';
const MAX_GEOTILE_LEVEL = 29;
const aggSchemas = new Schemas([
{
group: 'metrics',
name: 'metric',
title: 'Value',
min: 1,
max: Infinity,
aggFilter: ['avg', 'count', 'max', 'min', 'sum'],
defaults: [
{ schema: 'metric', type: 'count' }
]
}
]);
export class ESPewPewSource extends AbstractESSource {
static type = ES_PEW_PEW;
static title = i18n.translate('xpack.maps.source.pewPewTitle', {
defaultMessage: 'Source-destination connections'
});
static description = i18n.translate('xpack.maps.source.pewPewDescription', {
defaultMessage: 'Aggregated data paths between the origin and destinations.'
});
static createDescriptor({ indexPatternId, sourceGeoField, destGeoField }) {
return {
type: ESPewPewSource.type,
id: uuid(),
indexPatternId: indexPatternId,
sourceGeoField,
destGeoField
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = (sourceConfig) => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESPewPewSource.createDescriptor(sourceConfig);
const source = new ESPewPewSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return (<CreateSourceEditor onSourceConfigChange={onSourceConfigChange}/>);
}
renderSourceSettingsEditor({ onChange }) {
return (
<UpdateSourceEditor
indexPatternId={this._descriptor.indexPatternId}
onChange={onChange}
metrics={this._descriptor.metrics}
/>
);
}
isFilterByMapBounds() {
return true;
}
isJoinable() {
return false;
}
isGeoGridPrecisionAware() {
return true;
}
async getNumberFields() {
return this.getMetricFields().map(({ propertyKey: name, propertyLabel: label }) => {
return { label, name };
});
}
async getSupportedShapeTypes() {
return [VECTOR_SHAPE_TYPES.LINE];
}
async getImmutableProperties() {
let indexPatternTitle = this._descriptor.indexPatternId;
try {
const indexPattern = await this._getIndexPattern();
indexPatternTitle = indexPattern.title;
} catch (error) {
// ignore error, title will just default to id
}
return [
{
label: getDataSourceLabel(),
value: ESPewPewSource.title
},
{
label: i18n.translate('xpack.maps.source.pewPew.indexPatternLabel', {
defaultMessage: 'Index pattern'
}),
value: indexPatternTitle },
{
label: i18n.translate('xpack.maps.source.pewPew.sourceGeoFieldLabel', {
defaultMessage: 'Source'
}),
value: this._descriptor.sourceGeoField
},
{
label: i18n.translate('xpack.maps.source.pewPew.destGeoFieldLabel', {
defaultMessage: 'Destination'
}),
value: this._descriptor.destGeoField
},
];
}
createDefaultLayer(options) {
const styleDescriptor = VectorStyle.createDescriptor({
lineColor: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
origin: SOURCE_DATA_ID_ORIGIN
},
color: 'Blues'
}
},
lineWidth: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
field: {
label: COUNT_PROP_LABEL,
name: COUNT_PROP_NAME,
origin: SOURCE_DATA_ID_ORIGIN
},
minSize: 4,
maxSize: 32,
}
}
});
return new VectorLayer({
layerDescriptor: VectorLayer.createDescriptor({
...options,
sourceDescriptor: this._descriptor,
style: styleDescriptor
}),
source: this,
style: new VectorStyle(styleDescriptor, this)
});
}
getGeoGridPrecision(zoom) {
const targetGeotileLevel = Math.ceil(zoom) + 2;
return Math.min(targetGeotileLevel, MAX_GEOTILE_LEVEL);
}
async getGeoJsonWithMeta(layerName, searchFilters) {
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 !== 'count') {
metricAggConfig.params = { field: metric.field };
}
return metricAggConfig;
});
const aggConfigs = new AggConfigs(indexPattern, metricAggConfigs, aggSchemas.all);
const searchSource = await this._makeSearchSource(searchFilters, 0);
searchSource.setField('aggs', {
destSplit: {
terms: {
script: {
source: `doc['${this._descriptor.destGeoField}'].value.toString()`,
lang: 'painless'
},
order: {
_count: 'desc'
},
size: 100
},
aggs: {
sourceGrid: {
geotile_grid: {
field: this._descriptor.sourceGeoField,
precision: searchFilters.geogridPrecision,
size: 500,
},
aggs: {
sourceCentroid: {
geo_centroid: {
field: this._descriptor.sourceGeoField
}
},
...aggConfigs.toDsl()
}
}
}
}
});
const esResponse = await this._runEsQuery(layerName, searchSource, i18n.translate('xpack.maps.source.pewPew.inspectorDescription', {
defaultMessage: 'Source-destination connections request'
}));
const { featureCollection } = convertToLines(esResponse);
return {
data: featureCollection,
meta: {
areResultsTrimmed: false
}
};
}
_formatMetricKey(metric) {
return metric.type !== 'count' ? `${metric.type}_of_${metric.field}` : COUNT_PROP_NAME;
}
_formatMetricLabel(metric) {
return metric.type !== 'count' ? `${metric.type} of ${metric.field}` : COUNT_PROP_LABEL;
}
async _getGeoField() {
const indexPattern = await this._getIndexPattern();
const geoField = indexPattern.fields.byName[this._descriptor.destGeoField];
if (!geoField) {
throw new Error(i18n.translate('xpack.maps.source.esSource.noGeoFieldErrorMessage', {
defaultMessage: `Index pattern {indexPatternTitle} no longer contains the geo field {geoField}`,
values: { indexPatternTitle: indexPattern.title, geoField: this._descriptor.geoField }
}));
}
return geoField;
}
canFormatFeatureProperties() {
return true;
}
async filterAndFormatPropertiesToHtml(properties) {
return await this.filterAndFormatPropertiesToHtmlForMetricFields(properties);
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Component } from 'react';
import { MetricsEditor } from '../../../components/metrics_editor';
import { indexPatternService } from '../../../kibana_services';
import { i18n } from '@kbn/i18n';
import { EuiFormRow } from '@elastic/eui';
export class UpdateSourceEditor extends Component {
state = {
fields: null,
};
componentDidMount() {
this._isMounted = true;
this._loadFields();
}
componentWillUnmount() {
this._isMounted = false;
}
async _loadFields() {
let indexPattern;
try {
indexPattern = await indexPatternService.get(this.props.indexPatternId);
} catch (err) {
if (this._isMounted) {
this.setState({
loadError: i18n.translate('xpack.maps.source.pewPew.noIndexPatternErrorMessage', {
defaultMessage: `Unable to find Index pattern {id}`,
values: {
id: this.props.indexPatternId
}
})
});
}
return;
}
if (!this._isMounted) {
return;
}
this.setState({ fields: indexPattern.fields });
}
_onMetricsChange = (metrics) => {
this.props.onChange({ propName: 'metrics', value: metrics });
};
render() {
return (
<EuiFormRow
label={i18n.translate('xpack.maps.source.pewPew.metricsLabel', {
defaultMessage: 'Metrics'
})}
>
<div>
<MetricsEditor
allowMultipleMetrics={true}
fields={this.state.fields}
metrics={this.props.metrics}
onChange={this._onMetricsChange}
/>
</div>
</EuiFormRow>
);
}
}

View file

@ -388,3 +388,51 @@
}
}
}
{
"type": "doc",
"value": {
"id": "1",
"index": "connections",
"source": {
"source" : [-65, 40],
"destination" : "drm3btev3e86"
}
}
}
{
"type": "doc",
"value": {
"id": "2",
"index": "connections",
"source": {
"source" : [-75.00001, 41],
"destination" : { "lon": -71, "lat": 40 }
}
}
}
{
"type": "doc",
"value": {
"id": "3",
"index": "connections",
"source": {
"source" : [-75, 41.00003],
"destination" : [ -71.000000000000000, 40 ]
}
}
}
{
"type": "doc",
"value": {
"id": "4",
"index": "connections",
"source": {
"source" : [-75, 41.00004],
"destination" : "40.000,-71"
}
}
}

View file

@ -109,3 +109,26 @@
}
}
}
{
"type": "index",
"value": {
"index": "connections",
"mappings": {
"properties": {
"source": {
"type": "geo_point"
},
"destination": {
"type": "geo_point"
}
}
},
"settings": {
"index": {
"number_of_replicas": "0",
"number_of_shards": "1"
}
}
}
}

View file

@ -109,6 +109,26 @@
}
}
{
"type": "doc",
"value": {
"id": "index-pattern:dedd3180-c8d8-11e9-b36c-81f9f9da524f",
"index": ".kibana",
"source": {
"index-pattern" : {
"title" : "connections",
"fields" : "[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"destination\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]"
},
"type" : "index-pattern",
"references" : [ ],
"migrationVersion" : {
"index-pattern" : "6.5.0"
},
"updated_at" : "2019-08-27T14:42:20.061Z"
}
}
}
{
"type": "doc",
"value": {
@ -617,6 +637,62 @@
}
}
{
"type": "doc",
"value": {
"id": "map:3c9949f0-c8dc-11e9-9ea1-8b2710d4a86b",
"index": ".kibana",
"source": {
"map" : {
"title" : "pew pew demo",
"description" : "",
"mapStateJSON" : "{\"zoom\":5.5,\"center\":{\"lon\":-71.34293,\"lat\":40.33097},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":false,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]}",
"layerListJSON" : "[{\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#1EA593\"}},\"lineColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineWidth\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"symbol\":{\"options\":{\"symbolizeAs\":\"circle\",\"symbolId\":\"airfield\"}}}},\"sourceDescriptor\":{\"type\":\"ES_PEW_PEW\",\"id\":\"d7ca27a8-dba2-4f5b-857c-8f529511ad81\",\"sourceGeoField\":\"source\",\"destGeoField\":\"destination\",\"indexPatternRefName\":\"layer_0_source_index_pattern\"},\"id\":\"67c1de2c-2fc5-4425-8983-094b589afe61\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"applyGlobalQuery\":true,\"type\":\"VECTOR\"}]",
"uiStateJSON" : "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}",
"bounds" : {
"type" : "Polygon",
"coordinates" : [
[
[
-83.72374,
44.30172
],
[
-83.72374,
36.11211
],
[
-58.96211,
36.11211
],
[
-58.96211,
44.30172
],
[
-83.72374,
44.30172
]
]
]
}
},
"type" : "map",
"references" : [
{
"name" : "layer_0_source_index_pattern",
"type" : "index-pattern",
"id" : "dedd3180-c8d8-11e9-b36c-81f9f9da524f"
}
],
"migrationVersion" : {
"map" : "7.4.0"
},
"updated_at" : "2019-08-27T15:06:24.654Z"
}
}
}
{
"type": "doc",
"value": {

View file

@ -35,5 +35,19 @@ export default function ({ getPageObjects, getService }) {
});
});
describe('dynamic line coloring', () => {
before(async () => {
await PageObjects.maps.loadSavedMap('pew pew demo');
await PageObjects.maps.enterFullScreen();
await PageObjects.maps.closeLegend();
});
// eslint-disable-next-line max-len
it('should symbolize pew pew lines', async () => {
await visualTesting.snapshot();
});
});
});
}