[Vis: Default editor] EUIficate region map options tab (#42944) (#43393)

* EUIficate region_map_options

* Reuse types

* Remove wms_options directive

* Remove style import

* Fix issue with join field default value

# Conflicts:
#	src/legacy/core_plugins/region_map/public/region_map_vis_params.html
This commit is contained in:
Daniil Suleiman 2019-08-16 10:45:35 +03:00 committed by GitHub
parent bf19b19506
commit 39ca6bdb90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 449 additions and 635 deletions

View file

@ -0,0 +1,55 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { EuiFormRow, EuiFieldNumber } from '@elastic/eui';
interface NumberInputOptionProps<ParamName extends string> {
label?: React.ReactNode;
max?: number;
min?: number;
paramName: ParamName;
value?: number | '';
setValue: (paramName: ParamName, value: number | '') => void;
}
function NumberInputOption<ParamName extends string>({
label,
max,
min,
paramName,
value = '',
setValue,
}: NumberInputOptionProps<ParamName>) {
return (
<EuiFormRow label={label} fullWidth compressed>
<EuiFieldNumber
fullWidth
max={max}
min={min}
value={value}
onChange={ev =>
setValue(paramName, isNaN(ev.target.valueAsNumber) ? '' : ev.target.valueAsNumber)
}
/>
</EuiFormRow>
);
}
export { NumberInputOption };

View file

@ -23,26 +23,29 @@ import { EuiFormRow, EuiSelect } from '@elastic/eui';
interface SelectOptionProps<ParamName extends string, ValidParamValues extends string> {
id?: string;
label: string;
labelAppend?: React.ReactNode;
options: Array<{ value: ValidParamValues; text: string }>;
paramName: ParamName;
value?: ValidParamValues;
dataTestSubj?: string;
setValue: (paramName: ParamName, value: ValidParamValues) => void;
}
const emptyValue = { text: '', value: 'EMPTY_VALUE', disabled: true, hidden: true };
function SelectOption<ParamName extends string, ValidParamValues extends string>({
id,
label,
labelAppend,
options,
paramName,
value,
setValue,
}: SelectOptionProps<ParamName, ValidParamValues>) {
return (
<EuiFormRow id={id} label={label} fullWidth={true} compressed>
<EuiFormRow id={id} label={label} fullWidth={true} compressed labelAppend={labelAppend}>
<EuiSelect
options={options}
value={value}
options={[emptyValue, ...options]}
value={value || emptyValue.value}
onChange={ev => setValue(paramName, ev.target.value as ValidParamValues)}
fullWidth={true}
/>

View file

@ -28,7 +28,6 @@ const regionMapPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyP
require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter', 'data'],
publicDir: resolve(__dirname, 'public'),
uiExports: {
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
hacks: [resolve(__dirname, 'public/legacy')],
injectDefaultVars(server) {
const { regionmap } = server.config().get('map');

View file

@ -1,4 +0,0 @@
.rgmEMSLink {
margin-top: $euiSizeXS;
font-size: $euiFontSizeXS;
}

View file

@ -0,0 +1,266 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { EuiIcon, EuiLink, EuiPanel, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { toastNotifications } from 'ui/notify';
import { FileLayerField, VectorLayer, ServiceSettings } from 'ui/vis/map/service_settings';
import { VisOptionsProps } from 'ui/vis/editors/default';
import { SelectOption } from '../../../kbn_vislib_vis_types/public/controls/select';
import { SwitchOption } from '../../../kbn_vislib_vis_types/public/controls/switch';
import { NumberInputOption } from '../../../kbn_vislib_vis_types/public/controls/number_input';
import { ORIGIN } from '../../../tile_map/common/origin';
import { WmsOptions } from '../../../tile_map/public/components/wms_options';
import { mapToLayerWithId } from '../util';
import { RegionMapVisParams } from '../types';
import { RegionMapsConfig } from '../plugin';
const mapLayerForOption = ({ layerId, name }: VectorLayer) => ({
text: name,
value: layerId,
});
const mapFieldForOption = ({ description, name }: FileLayerField) => ({
text: description,
value: name,
});
export type RegionMapOptionsProps = {
serviceSettings: ServiceSettings;
includeElasticMapsService: RegionMapsConfig['includeElasticMapsService'];
} & VisOptionsProps<RegionMapVisParams>;
function RegionMapOptions(props: RegionMapOptionsProps) {
const { includeElasticMapsService, serviceSettings, stateParams, vis, setValue } = props;
const [vectorLayers, setVectorLayers] = useState<VectorLayer[]>(
vis.type.editorConfig.collections.vectorLayers
);
const [vectorLayerOptions, setVectorLayerOptions] = useState(vectorLayers.map(mapLayerForOption));
const currentLayerId = stateParams.selectedLayer && stateParams.selectedLayer.layerId;
const fieldOptions = useMemo(
() =>
((stateParams.selectedLayer && stateParams.selectedLayer.fields) || []).map(
mapFieldForOption
),
[currentLayerId]
);
const setEmsHotLink = useCallback(
async (layer: VectorLayer) => {
const emsHotLink = await serviceSettings.getEMSHotLink(layer);
setValue('emsHotLink', emsHotLink);
},
[setValue]
);
const setLayer = useCallback(
async (paramName: 'selectedLayer', value: VectorLayer['layerId']) => {
const newLayer = vectorLayers.find(({ layerId }: VectorLayer) => layerId === value);
if (newLayer) {
setValue(paramName, newLayer);
setValue('selectedJoinField', newLayer.fields[0]);
setEmsHotLink(newLayer);
}
},
[vectorLayers, setValue]
);
const setField = useCallback(
(paramName: 'selectedJoinField', value: FileLayerField['name']) => {
if (stateParams.selectedLayer) {
setValue(paramName, stateParams.selectedLayer.fields.find(f => f.name === value));
}
},
[currentLayerId, setValue]
);
useEffect(() => {
async function setDefaultValues() {
try {
const layers = await serviceSettings.getFileLayers();
const newLayers = layers
.map(mapToLayerWithId.bind(null, ORIGIN.EMS))
.filter(
layer => !vectorLayers.some(vectorLayer => vectorLayer.layerId === layer.layerId)
);
// backfill v1 manifest for now
newLayers.forEach(layer => {
if (layer.format === 'geojson') {
layer.format = {
type: 'geojson',
};
}
});
const newVectorLayers = [...vectorLayers, ...newLayers];
setVectorLayers(newVectorLayers);
setVectorLayerOptions(newVectorLayers.map(mapLayerForOption));
const [newLayer] = newVectorLayers;
if (newLayer && !stateParams.selectedLayer) {
setValue('selectedLayer', newLayer);
setValue('selectedJoinField', newLayer.fields[0]);
if (newLayer.isEMS) {
setEmsHotLink(newLayer);
}
}
} catch (error) {
toastNotifications.addWarning(error.message);
}
}
if (includeElasticMapsService) {
setDefaultValues();
}
}, []);
return (
<>
<EuiPanel paddingSize="s">
<EuiTitle size="xs">
<h2>
<FormattedMessage
id="regionMap.visParams.layerSettingsTitle"
defaultMessage="Layer settings"
/>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<SelectOption
id="regionMapOptionsSelectLayer"
label={i18n.translate('regionMap.visParams.vectorMapLabel', {
defaultMessage: 'Vector map',
})}
labelAppend={
stateParams.emsHotLink && (
<EuiText size="xs">
<EuiLink
href={stateParams.emsHotLink}
target="_blank"
title={i18n.translate('regionMap.visParams.previewOnEMSLinkTitle', {
defaultMessage: 'Preview {selectedLayerName} on the Elastic Maps Service',
values: {
selectedLayerName:
stateParams.selectedLayer && stateParams.selectedLayer.name,
},
})}
>
<FormattedMessage
id="regionMap.visParams.previewOnEMSLinkText"
defaultMessage="Preview on EMS"
/>{' '}
<EuiIcon type="popout" size="s" />
</EuiLink>
</EuiText>
)
}
options={vectorLayerOptions}
paramName="selectedLayer"
value={stateParams.selectedLayer && stateParams.selectedLayer.layerId}
setValue={setLayer}
/>
<SelectOption
id="regionMapOptionsSelectJoinField"
label={i18n.translate('regionMap.visParams.joinFieldLabel', {
defaultMessage: 'Join field',
})}
options={fieldOptions}
paramName="selectedJoinField"
value={stateParams.selectedJoinField && stateParams.selectedJoinField.name}
setValue={setField}
/>
<SwitchOption
label={i18n.translate('regionMap.visParams.displayWarningsLabel', {
defaultMessage: 'Display warnings',
})}
tooltip={i18n.translate('regionMap.visParams.switchWarningsTipText', {
defaultMessage:
'Turns on/off warnings. When turned on, warning will be shown for each term that cannot be matched to a shape in the vector layer based on the join field. When turned off, these warnings will be turned off.',
})}
paramName="isDisplayWarning"
value={stateParams.isDisplayWarning}
setValue={setValue}
/>
<SwitchOption
label={i18n.translate('regionMap.visParams.showAllShapesLabel', {
defaultMessage: 'Show all shapes',
})}
tooltip={i18n.translate('regionMap.visParams.turnOffShowingAllShapesTipText', {
defaultMessage:
'Turning this off only shows the shapes that were matched with a corresponding term.',
})}
paramName="showAllShapes"
value={stateParams.showAllShapes}
setValue={setValue}
/>
</EuiPanel>
<EuiSpacer size="s" />
<EuiPanel paddingSize="s">
<EuiTitle size="xs">
<h2>
<FormattedMessage
id="regionMap.visParams.styleSettingsLabel"
defaultMessage="Style settings"
/>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<SelectOption
label={i18n.translate('regionMap.visParams.colorSchemaLabel', {
defaultMessage: 'Color schema',
})}
options={vis.type.editorConfig.collections.colorSchemas}
paramName="colorSchema"
value={stateParams.colorSchema}
setValue={setValue}
/>
<NumberInputOption
label={i18n.translate('regionMap.visParams.outlineWeightLabel', {
defaultMessage: 'Border thickness',
})}
paramName="outlineWeight"
value={stateParams.outlineWeight}
setValue={setValue}
/>
</EuiPanel>
<EuiSpacer size="s" />
<WmsOptions {...props} />
</>
);
}
export { RegionMapOptions };

View file

@ -1,10 +0,0 @@
@import 'src/legacy/ui/public/styles/styling_constants';
// Prefix all styles with "rgm" to avoid conflicts.
// Examples
// rgmChart
// rgmChart__legend
// rgmChart__legend--small
// rgmChart__legend-isLoading
@import './region_map';

View file

@ -16,24 +16,26 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';
import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps';
import { mapToLayerWithId } from './util';
import { createRegionMapVisualization } from './region_map_visualization';
import { Status } from 'ui/vis/update_status';
import { RegionMapOptions } from './components/region_map_options';
import { visFactory } from '../../visualizations/public';
// TODO: reference to TILE_MAP plugin should be removed
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
import { ORIGIN } from '../../tile_map/common/origin';
export function createRegionMapTypeDefinition(dependencies) {
const { uiSettings, regionmapsConfig } = dependencies;
const RegionMapsVisualization = createRegionMapVisualization(dependencies);
const { uiSettings, regionmapsConfig, serviceSettings } = dependencies;
const visualization = createRegionMapVisualization(dependencies);
const vectorLayers = regionmapsConfig.layers.map(mapToLayerWithId.bind(null, ORIGIN.KIBANA_YML));
const selectedLayer = vectorLayers[0];
const selectedJoinField = selectedLayer ? vectorLayers[0].fields[0] : null;
const selectedJoinField = selectedLayer ? selectedLayer.fields[0] : null;
return visFactory.createBaseVisualization({
name: 'region_map',
@ -46,9 +48,9 @@ provided base maps, or add your own. Darker colors represent higher values.' }),
legendPosition: 'bottomright',
addTooltip: true,
colorSchema: 'Yellow to Red',
selectedLayer: selectedLayer,
emsHotLink: '',
selectedJoinField: selectedJoinField,
selectedLayer,
selectedJoinField,
isDisplayWarning: true,
wms: uiSettings.get('visualization:tileMap:WMSdefaults'),
mapZoom: 2,
@ -58,25 +60,17 @@ provided base maps, or add your own. Darker colors represent higher values.' }),
}
},
requiresUpdateStatus: [Status.AGGS, Status.PARAMS, Status.RESIZE, Status.DATA, Status.UI_STATE],
visualization: RegionMapsVisualization,
visualization,
editorConfig: {
optionsTemplate: '<region_map-vis-params></region_map-vis-params>',
optionsTemplate: (props) =>
(<RegionMapOptions
{...props}
serviceSettings={serviceSettings}
includeElasticMapsService={regionmapsConfig.includeElasticMapsService}
/>),
collections: {
legendPositions: [{
value: 'bottomleft',
text: i18n.translate('regionMap.mapVis.regionMapEditorConfig.bottomLeftText', { defaultMessage: 'bottom left' }),
}, {
value: 'bottomright',
text: i18n.translate('regionMap.mapVis.regionMapEditorConfig.bottomRightText', { defaultMessage: 'bottom right' }),
}, {
value: 'topleft',
text: i18n.translate('regionMap.mapVis.regionMapEditorConfig.topLeftText', { defaultMessage: 'top left' }),
}, {
value: 'topright',
text: i18n.translate('regionMap.mapVis.regionMapEditorConfig.topRightText', { defaultMessage: 'top right' }),
}],
colorSchemas: Object.values(truncatedColorMaps).map(value => ({ id: value.id, label: value.label })),
vectorLayers: vectorLayers,
colorSchemas,
vectorLayers,
tmsLayers: []
},
schemas: new Schemas([

View file

@ -1,152 +0,0 @@
<div class="visEditorSidebar__section">
<div class="form-group">
<div class="visEditorSidebar__sectionTitle">
<div
i18n-id="regionMap.visParams.layerSettingsTitle"
i18n-default-message="Layer Settings"
></div>
</div>
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
for="regionMap"
i18n-id="regionMap.visParams.vectorMapLabel"
i18n-default-message="Vector map"
></label>
<div class="visEditorSidebar__formControl">
<select
id="regionMap"
class="kuiSelect visEditorSidebar__select"
ng-model="editorState.params.selectedLayer"
ng-options="layer.name for layer in collections.vectorLayers track by layer.layerId"
ng-change="onLayerChange()"
></select>
</div>
</div>
<div class="form-group clearfix" ng-hide="!editorState.params.emsHotLink">
<a
class="rgmEMSLink pull-right"
target="_blank"
rel="noopener noreferrer"
ng-href="{{editorState.params.emsHotLink}}"
target="_blank"
title="{{'regionMap.visParams.previewOnEMSLinkTitle' | i18n: {
defaultMessage: 'Preview {selectedLayerName} on the Elastic Maps Service',
values: { selectedLayerName: editorState.params.selectedLayer.name }
} }}"
>
<icon aria-hidden size="'s'" type="'link'" />
<span
i18n-id="regionMap.visParams.previewOnEMSLinkText"
i18n-default-message="Preview on EMS"
></span>
</a>
</div>
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
for="joinField"
i18n-id="regionMap.visParams.joinFieldLabel"
i18n-default-message="Join field"
></label>
<div class="visEditorSidebar__formControl">
<select id="joinField"
class="kuiSelect visEditorSidebar__select"
ng-model="editorState.params.selectedJoinField"
ng-options="field.description for field in editorState.params.selectedLayer.fields track by field.name"
>
<option
value=''
i18n-id="regionMap.visParams.selectOptionLabel"
i18n-default-message="Select"
></option>
</select>
</div>
</div>
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
for="displayWarnings"
i18n-id="regionMap.visParams.displayWarningsLabel"
i18n-default-message="Display warnings"
></label>
<div class="visEditorSidebar__formControl">
<input id="displayWarnings" type="checkbox" ng-model="editorState.params.isDisplayWarning">
&nbsp;
<icon-tip
content="::'regionMap.visParams.switchWarningsTipText' | i18n: {
defaultMessage: 'Turns on/off warnings. When turned on, warning will be shown for each term that cannot be matched to a shape in the vector layer based on the join field. When turned off, these warnings will be turned off.'
}"
position="'right'"
></icon-tip>
</div>
</div>
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
for="onlyShowMatchingShapes"
i18n-id="regionMap.visParams.showAllShapesLabel"
i18n-default-message="Show all shapes"
></label>
<div class="visEditorSidebar__formControl">
<input id="onlyShowMatchingShapes" type="checkbox" ng-model="editorState.params.showAllShapes">
&nbsp;
<icon-tip
content="::'regionMap.visParams.turnOffShowingAllShapesTipText' | i18n: {
defaultMessage: 'Turning this off only shows the shapes that were matched with a corresponding term'
}"
position="'right'"
></icon-tip>
</div>
</div>
</div>
</div>
<div class="visEditorSidebar__section">
<div class="visEditorSidebar__sectionTitle">
<div
i18n-id="regionMap.visParams.styleSettingsLabel"
i18n-default-message="Style settings"
></div>
</div>
<div class="visEditorSidebar__formRow" >
<label
class="visEditorSidebar__formLabel"
for="colorSchema"
i18n-id="regionMap.visParams.colorSchemaLabel"
i18n-default-message="Color Schema"
></label>
<div class="visEditorSidebar__formControl">
<select
id="colorSchema"
class="kuiSelect visEditorSidebar__select"
ng-model="editorState.params.colorSchema"
ng-options="mode.id as mode.label for mode in collections.colorSchemas"
></select>
</div>
</div>
<div class="visEditorSidebar__formRow" >
<label
class="visEditorSidebar__formLabel"
for="outlineWeight"
i18n-id="regionMap.visParams.outlineWeightLabel"
i18n-default-message="Outline weight"
i18n-description="Describes the width of a line surrounding a country on a map."
></label>
<div class="visEditorSidebar__formControl">
<input
id="outlineWeight"
class="kuiInput visEditorSidebar__input"
type="number"
ng-model="editorState.params.outlineWeight"
>
</div>
</div>
</div>
<wms-options options="editorState.params.wms" collections="collections"></wms-options>

View file

@ -1,94 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { toastNotifications } from 'ui/notify';
import regionMapVisParamsTemplate from './region_map_vis_params.html';
import { mapToLayerWithId } from './util';
// TODO: reference to TILE_MAP plugin should be removed
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
export function RegionMapVisParams(serviceSettings, regionmapsConfig) {
return {
restrict: 'E',
template: regionMapVisParamsTemplate,
link: function ($scope) {
$scope.collections = $scope.vis.type.editorConfig.collections;
$scope.onLayerChange = onLayerChange;
if (regionmapsConfig.includeElasticMapsService) {
serviceSettings.getFileLayers()
.then(function (layersFromService) {
layersFromService = layersFromService.map(mapToLayerWithId.bind(null, ORIGIN.EMS));
const newVectorLayers = $scope.collections.vectorLayers.slice();
for (let i = 0; i < layersFromService.length; i += 1) {
const layerFromService = layersFromService[i];
const alreadyAdded = newVectorLayers.some((layer) => layerFromService.layerId === layer.layerId);
if (!alreadyAdded) {
//backfill v1 manifest for now
if (layerFromService.format === 'geojson') {
layerFromService.format = {
type: 'geojson',
};
}
newVectorLayers.push(layerFromService);
}
}
$scope.collections.vectorLayers = newVectorLayers;
if ($scope.collections.vectorLayers[0] && !$scope.editorState.params.selectedLayer) {
$scope.editorState.params.selectedLayer = $scope.collections.vectorLayers[0];
onLayerChange();
}
//the dirty flag is set to true because the change in vector layers config causes an update of the scope.params
//temp work-around. addressing this issue with the visualize refactor for 6.0
setTimeout(function () {
$scope.dirty = false;
}, 0);
})
.catch(function (error) {
toastNotifications.addWarning(error.message);
});
}
async function onLayerChange() {
if (!$scope.editorState.params.selectedLayer) {
return;
}
$scope.editorState.params.selectedJoinField = $scope.editorState.params.selectedLayer.fields[0];
if ($scope.editorState.params.selectedLayer.isEMS) {
$scope.editorState.params.emsHotLink = null;
$scope.editorState.params.emsHotLink = await serviceSettings.getEMSHotLink($scope.editorState.params.selectedLayer);
$scope.$digest();
} else {
$scope.editorState.params.emsHotLink = null;
}
}
onLayerChange();
},
};
}

View file

@ -19,7 +19,7 @@
import chrome from 'ui/chrome';
import { CoreStart, Plugin } from 'kibana/public';
import { initTileMapLegacyModule } from './region_map_legacy_module';
import 'ui/vis/map/service_settings';
import { RegionMapsConfig } from '../plugin';
/** @internal */
@ -34,8 +34,6 @@ export class LegacyDependenciesPlugin
constructor(private readonly regionmapsConfig: RegionMapsConfig) {}
public async setup() {
initTileMapLegacyModule(this.regionmapsConfig);
const $injector = await chrome.dangerouslyGetActiveInjector();
return {

View file

@ -17,26 +17,20 @@
* under the License.
*/
import { once } from 'lodash';
// @ts-ignore
import { uiModules } from 'ui/modules';
import { VectorLayer, FileLayerField } from 'ui/vis/map/service_settings';
import { WMSOptions } from '../../tile_map/public/types';
import 'ui/vis/map/service_settings';
// @ts-ignore
import { RegionMapVisParams } from '../region_map_vis_params';
// @ts-ignore
import { WmsOptions } from '../wms_options';
import { RegionMapsConfig } from '../plugin';
/** @internal */
export const initTileMapLegacyModule = once((regionmapsConfig: RegionMapsConfig): void => {
uiModules
// TODO: Region Map Plugin uses wmsOptions directive from the kibana/tile_map module.
// in future this reference should be removed
.get('kibana/region_map', ['kibana'])
.constant('regionmapsConfig', regionmapsConfig)
.directive('regionMapVisParams', RegionMapVisParams)
.directive('wmsOptions', WmsOptions);
});
export interface RegionMapVisParams {
readonly addTooltip: true;
readonly legendPosition: 'bottomright';
colorSchema: string;
emsHotLink?: string | null;
mapCenter: [number, number];
mapZoom: number;
outlineWeight: number | '';
isDisplayWarning: boolean;
showAllShapes: boolean;
selectedLayer?: VectorLayer;
selectedJoinField?: FileLayerField;
wms: WMSOptions;
}

View file

@ -17,14 +17,12 @@
* under the License.
*/
import _ from 'lodash';
import { FileLayer, VectorLayer } from 'ui/vis/map/service_settings';
// TODO: reference to TILE_MAP plugin should be removed
import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin';
export function mapToLayerWithId(prefix, layer) {
const clonedLayer = _.cloneDeep(layer);
clonedLayer.layerId = prefix + '.' + layer.name;
clonedLayer.isEMS = ORIGIN.EMS === prefix ? true : false;
return clonedLayer;
}
export const mapToLayerWithId = (prefix: string, layer: FileLayer): VectorLayer => ({
...layer,
layerId: `${prefix}.${layer.name}`,
isEMS: ORIGIN.EMS === prefix,
});

View file

@ -1,60 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { i18n } from '@kbn/i18n';
import wmsOptionsTemplate from './wms_options.html';
export function WmsOptions(serviceSettings) {
return {
restrict: 'E',
template: wmsOptionsTemplate,
replace: true,
scope: {
options: '=',
collections: '=',
},
link: function ($scope) {
$scope.wmsLinkText = i18n.translate('regionMap.wmsOptions.wmsLinkText', { defaultMessage: 'here' });
new Promise((resolve, reject) => {
serviceSettings
.getTMSServices()
.then((allTMSServices) => {
const newBaseLayers = $scope.collections.tmsLayers.slice();
for (let i = 0; i < allTMSServices.length; i += 1) {
const layerFromService = allTMSServices[i];
const alreadyAdded = newBaseLayers.some((layer) => layerFromService.id === layer.id);
if (!alreadyAdded) {
newBaseLayers.push(layerFromService);
}
}
$scope.collections.tmsLayers = newBaseLayers;
if (!$scope.options.selectedTmsLayer && $scope.collections.tmsLayers.length) {
$scope.options.selectedTmsLayer = $scope.collections.tmsLayers[0];
}
resolve(true);
})
.catch(function (e) {
reject(e);
});
});
},
};
}

View file

@ -1,181 +0,0 @@
<div>
<div class="visEditorSidebar__section">
<div class="visEditorSidebar__sectionTitle">
<div
i18n-id="regionMap.wmsOptions.baseLayerSettingsTitle"
i18n-default-message="Base layer settings"
></div>
</div>
<div class="visEditorSidebar__formRow" ng-show="!options.enabled">
<label
class="visEditorSidebar__formLabel"
for="tmsLayers"
i18n-id="regionMap.wmsOptions.layersLabel"
i18n-default-message="Layers"
></label>
<div class="visEditorSidebar__formControl">
<select
id="tmsLayers"
class="kuiSelect visEditorSidebar__select"
ng-model="options.selectedTmsLayer"
ng-options="layer.id for layer in collections.tmsLayers track by layer.id"
></select>
</div>
</div>
<div class="visEditorSidebar__formRow">
<label
class="visEditorSidebar__formLabel"
for="wmsCompliantMapServer"
i18n-id="regionMap.wmsOptions.wmsMapServerLabel"
i18n-default-message="WMS map server"
></label>
<div class="visEditorSidebar__formControl">
<input
id="wmsCompliantMapServer"
type="checkbox"
name="wms.enabled"
ng-model="options.enabled"
>
&nbsp;
<icon-tip
content="::'regionMap.wmsOptions.useWMSCompliantMapTileServerTip' | i18n: {
defaultMessage: 'Use WMS compliant map tile server. For advanced users only.'
}"
position="'right'"
></icon-tip>
</div>
</div>
<div ng-show="options.enabled">
<div class="form-group">
<p
i18n-id="regionMap.wmsOptions.wmsDescription"
i18n-default-message="WMS is an OGC standard for map image services. For more information, go {wmsLink}."
i18n-values="{
html_wmsLink: '<a href=\'http://www.opengeospatial.org/standards/wms\'>' + wmsLinkText + '</a>'
}"
></p>
<br>
<label>
<span
i18n-id="regionMap.wmsOptions.wmsUrlLabel"
i18n-default-message="WMS url*"
></span>
<icon-tip
content="::'regionMap.wmsOptions.urlOfWMSWebServiceTip' | i18n: {
defaultMessage: 'The URL of the WMS web service'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
name="wms.url"
ng-model="options.url">
</div>
<div class="form-group">
<label>
<span
i18n-id="regionMap.wmsOptions.wmsLayersLabel"
i18n-default-message="WMS layers*"
></span>
<icon-tip
content="::'regionMap.wmsOptions.listOfLayersToUseTip' | i18n: {
defaultMessage: 'A comma separated list of layers to use'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
ng-require="options.enabled"
name="wms.options.layers"
ng-model="options.options.layers">
</div>
<div class="form-group">
<label>
<span
i18n-id="regionMap.wmsOptions.wmsVersionLabel"
i18n-default-message="WMS version*"
></span>
<icon-tip
content="::'regionMap.wmsOptions.versionOfWMSserverSupportsTip' | i18n: {
defaultMessage: 'The version of WMS the server supports'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
name="wms.options.version"
ng-model="options.options.version">
</div>
<div class="form-group">
<label>
<span
i18n-id="regionMap.wmsOptions.wmsFormatLabel"
i18n-default-message="WMS format*"
></span>
<icon-tip
content="::'regionMap.wmsOptions.imageFormatToUseTip' | i18n: {
defaultMessage: 'Usually image/png or image/jpeg. Use png if the server will return transparent layers.'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
name="wms.options.format"
ng-model="options.options.format">
</div>
<div class="form-group">
<label>
<span
i18n-id="regionMap.wmsOptions.wmsAttributionLabel"
i18n-default-message="WMS attribution"
></span>
<icon-tip
content="::'regionMap.wmsOptions.attributionStringTip' | i18n: {
defaultMessage: 'Attribution string for the lower right corner'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
name="wms.options.attribution"
ng-model="options.options.attribution">
</div>
<div class="form-group">
<label>
<span
i18n-id="regionMap.wmsOptions.wmsStylesLabel"
i18n-default-message="WMS styles*"
></span>
<icon-tip
content="::'regionMap.wmsOptions.wmsServerSupportedStylesListTip' | i18n: {
defaultMessage: 'A comma separated list of WMS server supported styles to use. Blank in most cases.'
}"
position="'right'"
></icon-tip>
</label>
<input type="text" class="form-control"
name="wms.options.styles"
ng-model="options.options.styles">
</div>
<p
i18n-id="regionMap.wmsOptions.mapLoadFailDescription"
i18n-default-message="* if this parameter is incorrect, maps will fail to load."
></p>
</div>
</div>
</div>

View file

@ -17,7 +17,7 @@
* under the License.
*/
export const ORIGIN = {
EMS: 'elastic_maps_service',
KIBANA_YML: 'self_hosted'
};
export enum ORIGIN {
EMS = 'elastic_maps_service',
KIBANA_YML = 'self_hosted',
}

View file

@ -22,14 +22,11 @@ import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { TextInputOption } from '../../../kbn_vislib_vis_types/public/controls/text_input';
import { TileMapVisParams } from '../types';
import { WMSOptions } from '../types';
interface WmsInternalOptions {
wms: TileMapVisParams['wms'];
setValue: <T extends keyof TileMapVisParams['wms']>(
paramName: T,
value: TileMapVisParams['wms'][T]
) => void;
wms: WMSOptions;
setValue: <T extends keyof WMSOptions>(paramName: T, value: WMSOptions[T]) => void;
}
function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) {
@ -53,9 +50,9 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) {
</EuiScreenReaderOnly>
);
const setOptions = <T extends keyof TileMapVisParams['wms']['options']>(
const setOptions = <T extends keyof WMSOptions['options']>(
paramName: T,
value: TileMapVisParams['wms']['options'][T]
value: WMSOptions['options'][T]
) =>
setValue('options', {
...wms.options,

View file

@ -26,17 +26,23 @@ import { toastNotifications } from 'ui/notify';
import { TmsLayer } from 'ui/vis/map/service_settings';
import { SelectOption } from '../../../kbn_vislib_vis_types/public/controls/select';
import { SwitchOption } from '../../../kbn_vislib_vis_types/public/controls/switch';
import { RegionMapOptionsProps } from '../../../region_map/public/components/region_map_options';
import { WmsInternalOptions } from './wms_internal_options';
import { TileMapOptionsProps } from './tile_map_options';
import { TileMapVisParams } from '../types';
const mapLayerForOption = ({ id }: TmsLayer) => ({ text: id, value: id });
function WmsOptions({ serviceSettings, stateParams, setValue, vis }: TileMapOptionsProps) {
function WmsOptions({
serviceSettings,
stateParams,
setValue,
vis,
}: TileMapOptionsProps | RegionMapOptionsProps) {
const { wms } = stateParams;
const { tmsLayers } = vis.type.editorConfig.collections;
const [tmsLayerOptions, setTmsLayersOptions] = useState<Array<{ text: string; value: string }>>(
vis.type.editorConfig.collections.tmsLayers.map(mapLayerForOption)
tmsLayers.map(mapLayerForOption)
);
const [layers, setLayers] = useState<TmsLayer[]>([]);
@ -60,7 +66,7 @@ function WmsOptions({ serviceSettings, stateParams, setValue, vis }: TileMapOpti
serviceSettings
.getTMSServices()
.then(services => {
const newBaseLayers = [
const newBaseLayers: TmsLayer[] = [
...tmsLayers,
...services.filter(service => !tmsLayers.some(({ id }: TmsLayer) => service.id === id)),
];
@ -78,12 +84,12 @@ function WmsOptions({ serviceSettings, stateParams, setValue, vis }: TileMapOpti
return (
<EuiPanel paddingSize="s">
<EuiTitle size="xs">
<div>
<h2>
<FormattedMessage
id="tileMap.wmsOptions.baseLayerSettingsTitle"
defaultMessage="Base layer settings"
/>
</div>
</h2>
</EuiTitle>
<EuiSpacer size="s" />
@ -103,6 +109,7 @@ function WmsOptions({ serviceSettings, stateParams, setValue, vis }: TileMapOpti
<>
<EuiSpacer size="s" />
<SelectOption
id="wmsOptionsSelectTmsLayer"
label={i18n.translate('tileMap.wmsOptions.layersLabel', {
defaultMessage: 'Layers',
})}

View file

@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
import { supports } from 'ui/utils/supports';
import { Schemas } from 'ui/vis/editors/default/schemas';
import { Status } from 'ui/vis/update_status';
import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';
import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps';
import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson';
import { createTileMapVisualization } from './tile_map_visualization';
@ -63,7 +63,7 @@ export function createTileMapTypeDefinition(dependencies) {
responseHandler: convertToGeoJson,
editorConfig: {
collections: {
colorSchemas: Object.values(truncatedColorMaps).map(({ id, label }) => ({ value: id, text: label })),
colorSchemas,
legendPositions: [{
value: 'bottomleft',
text: i18n.translate('tileMap.vis.editorConfig.legendPositions.bottomLeftText', {

View file

@ -17,6 +17,22 @@
* under the License.
*/
import { TmsLayer } from 'ui/vis/map/service_settings';
export interface WMSOptions {
selectedTmsLayer?: TmsLayer;
enabled: boolean;
url?: string;
options: {
version?: string;
layers?: string;
format: string;
transparent: boolean;
attribution?: string;
styles?: string;
};
}
export interface TileMapVisParams {
colorSchema: string;
mapType: 'Scaled Circle Markers' | 'Shaded Circle Markers' | 'Shaded geohash grid' | 'Heatmap';
@ -26,19 +42,5 @@ export interface TileMapVisParams {
legendPosition: 'bottomright' | 'bottomleft' | 'topright' | 'topleft';
mapZoom: number;
mapCenter: [number, number];
wms: {
selectedTmsLayer?: {
id: string;
};
enabled: boolean;
url?: string;
options: {
version?: string;
layers?: string;
format: string;
transparent: boolean;
attribution?: string;
styles?: string;
};
};
wms: WMSOptions;
}

View file

@ -25,6 +25,27 @@ export interface TmsLayer {
attribution: string;
}
export interface ServiceSettings {
getTMSServices(): Promise<TmsLayer[]>;
export interface FileLayer {
name: string;
origin: string;
id: string;
format: string | { type: string };
fields: FileLayerField[];
}
export interface FileLayerField {
name: string;
description: string;
type: string;
}
export interface VectorLayer extends FileLayer {
layerId: string;
isEMS: boolean;
}
export interface ServiceSettings {
getEMSHotLink(layer: FileLayer): Promise<string>;
getTMSServices(): Promise<TmsLayer[]>;
getFileLayers(): Promise<FileLayer[]>;
}

View file

@ -31,3 +31,5 @@ for (const key in colormaps) {
};
}
}
export const colorSchemas = Object.values(truncatedColorMaps).map(({ id, label }) => ({ value: id, text: label }));

View file

@ -63,16 +63,13 @@ export default function ({ getService, getPageObjects }) {
it('should change results after changing layer to world', async function () {
await PageObjects.visualize.clickOptions();
await PageObjects.visualize.selectFieldById('World Countries', 'regionMap');
await PageObjects.common.sleep(1000);//give angular time to go update UI state
await PageObjects.visualize.setSelectByOptionText('regionMapOptionsSelectLayer', 'World Countries');
//ensure all fields are there
await PageObjects.visualize.selectFieldById('ISO 3166-1 alpha-2 code', 'joinField');
await PageObjects.visualize.selectFieldById('ISO 3166-1 alpha-3 code', 'joinField');
await PageObjects.visualize.selectFieldById('name', 'joinField');
await PageObjects.visualize.selectFieldById('ISO 3166-1 alpha-2 code', 'joinField');
await PageObjects.common.sleep(2000);//need some time for the data to load
await PageObjects.visualize.setSelectByOptionText('regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-2 code');
await PageObjects.visualize.setSelectByOptionText('regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-3 code');
await PageObjects.visualize.setSelectByOptionText('regionMapOptionsSelectJoinField', 'name');
await PageObjects.visualize.setSelectByOptionText('regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-2 code');
await inspector.open();
const actualData = await inspector.getTableData();
@ -82,8 +79,11 @@ export default function ({ getService, getPageObjects }) {
it('should contain a dropdown with the default road_map base layer as an option',
async () => {
const roadMapExists = await find.existsByCssSelector('[label="road_map"]');
expect(roadMapExists).to.be(true);
const selectField = await find.byCssSelector('#wmsOptionsSelectTmsLayer');
const $ = await selectField.parseDomContent();
const optionsText = $('option').toArray().map(option => $(option).text());
expect(optionsText.includes('road_map')).to.be(true);
});
});
});

View file

@ -361,13 +361,12 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli
}
async setSelectByOptionText(selectId, optionText) {
const selectField = await find.byCssSelector(`#${selectId}`);
const options = await find.allByCssSelector(`#${selectId} > option`);
const optionsTextPromises = options.map(async (optionElement) => {
return await optionElement.getVisibleText();
});
const optionsText = await Promise.all(optionsTextPromises);
const $ = await selectField.parseDomContent();
const optionsText = $('option').toArray().map(option => $(option).text());
const optionIndex = optionsText.indexOf(optionText);
if (optionIndex === -1) {
throw new Error(`Unable to find option '${optionText}' in select ${selectId}. Available options: ${optionsText.join(',')}`);
}
@ -565,10 +564,6 @@ export function VisualizePageProvider({ getService, getPageObjects, updateBaseli
await sortMetric.click();
}
async selectFieldById(fieldValue, id) {
await find.clickByCssSelector(`#${id} > option[label="${fieldValue}"]`);
}
async getInterval() {
return await comboBox.getComboBoxSelectedOptions('visEditorInterval');
}

View file

@ -2653,21 +2653,13 @@
"regionMap.choroplethLayer.unrecognizedFormatErrorMessage": "認識されないフォーマット {formatType}",
"regionMap.function.help": "地域マップビジュアライゼーション",
"regionMap.mapVis.regionMapDescription": "マップにメトリックを表示します。提供されたベースマップを使用するか、独自のマップを追加できます。暗い色は大きな値を意味します。",
"regionMap.mapVis.regionMapEditorConfig.bottomLeftText": "左下",
"regionMap.mapVis.regionMapEditorConfig.bottomRightText": "右下",
"regionMap.mapVis.regionMapEditorConfig.schemas.metricTitle": "値",
"regionMap.mapVis.regionMapEditorConfig.schemas.segmentTitle": "フィールドのシェイプ",
"regionMap.mapVis.regionMapEditorConfig.topLeftText": "左上",
"regionMap.mapVis.regionMapEditorConfig.topRightText": "右上",
"regionMap.mapVis.regionMapTitle": "地域マップ",
"regionMap.visParams.colorSchemaLabel": "カラースキーム",
"regionMap.visParams.displayWarningsLabel": "警告を表示",
"regionMap.visParams.joinFieldLabel": "フィールドを結合",
"regionMap.visParams.layerSettingsTitle": "レイヤー設定",
"regionMap.visParams.outlineWeightLabel": "アウトライン重量",
"regionMap.visParams.previewOnEMSLinkText": "EMS でプレビュー",
"regionMap.visParams.previewOnEMSLinkTitle": "Elastic Maps Service で {selectedLayerName} をプレビュー",
"regionMap.visParams.selectOptionLabel": "選択してください",
"regionMap.visParams.showAllShapesLabel": "すべてのシェイプを表示",
"regionMap.visParams.styleSettingsLabel": "スタイル設定",
"regionMap.visParams.switchWarningsTipText": "警告のオン・オフを切り替えます。オンの場合、結合フィールドに基づきベクトルレイヤーのシェイプと一致しない用語ごとに警告が表示されます。オフにすると、これらの警告がオフになります。",
@ -10538,4 +10530,4 @@
"xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}

View file

@ -2654,21 +2654,13 @@
"regionMap.choroplethLayer.unrecognizedFormatErrorMessage": "格式 {formatType} 无法识别",
"regionMap.function.help": "地区地图可视化",
"regionMap.mapVis.regionMapDescription": "在专题地图上显示指标。使用其中提供的基本地图,或添加自己的地图。颜色越深表示值越大。",
"regionMap.mapVis.regionMapEditorConfig.bottomLeftText": "左下方",
"regionMap.mapVis.regionMapEditorConfig.bottomRightText": "右下方",
"regionMap.mapVis.regionMapEditorConfig.schemas.metricTitle": "值",
"regionMap.mapVis.regionMapEditorConfig.schemas.segmentTitle": "形状字段",
"regionMap.mapVis.regionMapEditorConfig.topLeftText": "左上方",
"regionMap.mapVis.regionMapEditorConfig.topRightText": "右上方",
"regionMap.mapVis.regionMapTitle": "区域地图",
"regionMap.visParams.colorSchemaLabel": "颜色模式",
"regionMap.visParams.displayWarningsLabel": "显示警告",
"regionMap.visParams.joinFieldLabel": "联接字段",
"regionMap.visParams.layerSettingsTitle": "图层设置",
"regionMap.visParams.outlineWeightLabel": "轮廓粗细",
"regionMap.visParams.previewOnEMSLinkText": "在 EMS 上预览",
"regionMap.visParams.previewOnEMSLinkTitle": "在 Elastic 地图服务上预览“{selectedLayerName}”",
"regionMap.visParams.selectOptionLabel": "选择",
"regionMap.visParams.showAllShapesLabel": "显示所有形状",
"regionMap.visParams.styleSettingsLabel": "样式设置",
"regionMap.visParams.switchWarningsTipText": "打开/关闭警告。打开状态下,将根据联接字段值对与矢量图层中形状不匹配的每个字词显示警告。关闭状态下,将不显示这些警告。",