mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Maps] Add attribution handling for TMS (config & UI-entry) and WMS (#44147)
* Add attribution for tms from config * Add attribution entry fields & logic for tms from URL * Set text and url fields invalid if both aren't present * Don't return attribution unless both text and url defined * Add wms inputs & handling * Debounce input to prevent constantly generating new layers on keypress * Remove unapplicable placeholder text on WMS attribution inputs
This commit is contained in:
parent
eb9f54d216
commit
d42bc7d9ae
6 changed files with 214 additions and 36 deletions
|
@ -118,17 +118,7 @@ export class EMSTMSSource extends AbstractTMSSource {
|
|||
if (!markdown) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return markdown.split('|').map((attribution) => {
|
||||
attribution = attribution.trim();
|
||||
//this assumes attribution is plain markdown link
|
||||
const extractLink = /\[(.*)\]\((.*)\)/;
|
||||
const result = extractLink.exec(attribution);
|
||||
return {
|
||||
label: result ? result[1] : null,
|
||||
url: result ? result[2] : null
|
||||
};
|
||||
});
|
||||
return this.convertMarkdownLinkToObjectArr(markdown);
|
||||
}
|
||||
|
||||
async getUrlTemplate() {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { CreateSourceEditor } from './create_source_editor';
|
|||
import { getKibanaTileMap } from '../../../meta';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel } from '../../../../common/i18n_getters';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class KibanaTilemapSource extends AbstractTMSSource {
|
||||
|
||||
|
@ -77,6 +78,13 @@ export class KibanaTilemapSource extends AbstractTMSSource {
|
|||
return tilemap.url;
|
||||
}
|
||||
|
||||
async getAttributions() {
|
||||
const tilemap = getKibanaTileMap();
|
||||
const markdown = _.get(tilemap, 'options.attribution', '');
|
||||
const objArr = this.convertMarkdownLinkToObjectArr(markdown);
|
||||
return objArr;
|
||||
}
|
||||
|
||||
async getDisplayName() {
|
||||
try {
|
||||
return await this.getUrlTemplate();
|
||||
|
|
|
@ -11,4 +11,18 @@ export class AbstractTMSSource extends AbstractSource {
|
|||
async getUrlTemplate() {
|
||||
throw new Error('Should implement TMSSource#getUrlTemplate');
|
||||
}
|
||||
|
||||
convertMarkdownLinkToObjectArr(markdown) {
|
||||
return markdown.split('|').map((attribution) => {
|
||||
attribution = attribution.trim();
|
||||
//this assumes attribution is plain markdown link
|
||||
const extractLink = /\[(.*)\]\((.*)\)/;
|
||||
const result = extractLink.exec(attribution);
|
||||
return {
|
||||
label: result ? result[1] : null,
|
||||
url: result ? result[2] : null
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { WmsClient } from './wms_client';
|
||||
import _ from 'lodash';
|
||||
|
||||
const LAYERS_LABEL = i18n.translate('xpack.maps.source.wms.layersLabel', {
|
||||
defaultMessage: 'Layers'
|
||||
|
@ -38,8 +39,9 @@ export class WMSCreateSourceEditor extends Component {
|
|||
styleOptions: [],
|
||||
selectedLayerOptions: [],
|
||||
selectedStyleOptions: [],
|
||||
attributionText: '',
|
||||
attributionUrl: '',
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
}
|
||||
|
@ -48,18 +50,26 @@ export class WMSCreateSourceEditor extends Component {
|
|||
this._isMounted = false;
|
||||
}
|
||||
|
||||
_previewIfPossible() {
|
||||
_previewIfPossible = _.debounce(() => {
|
||||
const {
|
||||
serviceUrl,
|
||||
layers,
|
||||
styles
|
||||
styles,
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
} = this.state;
|
||||
|
||||
const sourceConfig = (serviceUrl && layers)
|
||||
? { serviceUrl, layers, styles }
|
||||
? {
|
||||
serviceUrl,
|
||||
layers,
|
||||
styles,
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
}
|
||||
: null;
|
||||
this.props.onSourceConfigChange(sourceConfig);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
_loadCapabilities = async () => {
|
||||
if (!this.state.serviceUrl) {
|
||||
|
@ -137,6 +147,18 @@ export class WMSCreateSourceEditor extends Component {
|
|||
}, this._previewIfPossible);
|
||||
}
|
||||
|
||||
_handleWMSAttributionChange(attributionUpdate) {
|
||||
const {
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
} = this.state;
|
||||
this.setState(attributionUpdate, () => {
|
||||
if (attributionText && attributionUrl) {
|
||||
this._previewIfPossible();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_renderLayerAndStyleInputs() {
|
||||
if (!this.state.hasAttemptedToLoadCapabilities || this.state.isLoadingCapabilities) {
|
||||
return null;
|
||||
|
@ -226,6 +248,54 @@ export class WMSCreateSourceEditor extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
_renderAttributionInputs() {
|
||||
if (!this.state.layers) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label="Attribution text"
|
||||
isInvalid={attributionUrl !== '' && attributionText === ''}
|
||||
error={[
|
||||
i18n.translate('xpack.maps.source.wms.attributionText', {
|
||||
defaultMessage:
|
||||
'Attribution url must have accompanying text',
|
||||
})
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
onChange={({ target }) =>
|
||||
this._handleWMSAttributionChange({ attributionText: target.value })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Attribution link"
|
||||
isInvalid={attributionText !== '' && attributionUrl === ''}
|
||||
error={[
|
||||
i18n.translate('xpack.maps.source.wms.attributionLink', {
|
||||
defaultMessage:
|
||||
'Attribution text must have an accompanying link',
|
||||
})
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
onChange={({ target }) =>
|
||||
this._handleWMSAttributionChange({ attributionUrl: target.value })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<EuiForm>
|
||||
|
@ -244,6 +314,8 @@ export class WMSCreateSourceEditor extends Component {
|
|||
|
||||
{this._renderLayerAndStyleInputs()}
|
||||
|
||||
{this._renderAttributionInputs()}
|
||||
|
||||
</EuiForm>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,12 +24,14 @@ export class WMSSource extends AbstractTMSSource {
|
|||
});
|
||||
static icon = 'grid';
|
||||
|
||||
static createDescriptor({ serviceUrl, layers, styles }) {
|
||||
static createDescriptor({ serviceUrl, layers, styles, attributionText, attributionUrl }) {
|
||||
return {
|
||||
type: WMSSource.type,
|
||||
serviceUrl: serviceUrl,
|
||||
layers: layers,
|
||||
styles: styles
|
||||
serviceUrl,
|
||||
layers,
|
||||
styles,
|
||||
attributionText,
|
||||
attributionUrl
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,18 @@ export class WMSSource extends AbstractTMSSource {
|
|||
return this._descriptor.serviceUrl;
|
||||
}
|
||||
|
||||
getAttributions() {
|
||||
const { attributionText, attributionUrl } = this._descriptor;
|
||||
const attributionComplete = !!attributionText && !!attributionUrl;
|
||||
|
||||
return attributionComplete
|
||||
? [{
|
||||
url: attributionUrl,
|
||||
label: attributionText
|
||||
}]
|
||||
: [];
|
||||
}
|
||||
|
||||
getUrlTemplate() {
|
||||
const client = new WmsClient({ serviceUrl: this._descriptor.serviceUrl });
|
||||
return client.getUrlTemplate(this._descriptor.layers, this._descriptor.styles || '');
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import {
|
||||
EuiFieldText,
|
||||
EuiFormRow,
|
||||
|
@ -15,6 +14,7 @@ import { AbstractTMSSource } from './tms_source';
|
|||
import { TileLayer } from '../tile_layer';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel, getUrlLabel } from '../../../common/i18n_getters';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class XYZTMSSource extends AbstractTMSSource {
|
||||
|
||||
|
@ -27,10 +27,12 @@ export class XYZTMSSource extends AbstractTMSSource {
|
|||
});
|
||||
static icon = 'grid';
|
||||
|
||||
static createDescriptor({ urlTemplate }) {
|
||||
static createDescriptor({ urlTemplate, attributionText, attributionUrl }) {
|
||||
return {
|
||||
type: XYZTMSSource.type,
|
||||
urlTemplate
|
||||
urlTemplate,
|
||||
attributionText,
|
||||
attributionUrl
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -68,40 +70,118 @@ export class XYZTMSSource extends AbstractTMSSource {
|
|||
return this._descriptor.urlTemplate;
|
||||
}
|
||||
|
||||
getAttributions() {
|
||||
const { attributionText, attributionUrl } = this._descriptor;
|
||||
const attributionComplete = !!attributionText && !!attributionUrl;
|
||||
|
||||
return attributionComplete
|
||||
? [{
|
||||
url: attributionUrl,
|
||||
label: attributionText
|
||||
}]
|
||||
: [];
|
||||
}
|
||||
|
||||
getUrlTemplate() {
|
||||
return this._descriptor.urlTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class XYZTMSEditor extends React.Component {
|
||||
class XYZTMSEditor extends React.Component {
|
||||
|
||||
state = {
|
||||
tmsInput: '',
|
||||
tmsCanPreview: false
|
||||
tmsCanPreview: false,
|
||||
attributionText: '',
|
||||
attributionUrl: '',
|
||||
}
|
||||
|
||||
_sourceConfigChange = _.debounce(updatedSourceConfig => {
|
||||
if (this.state.tmsCanPreview) {
|
||||
this.props.onSourceConfigChange(updatedSourceConfig);
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
_handleTMSInputChange(e) {
|
||||
const url = e.target.value;
|
||||
|
||||
const canPreview = (url.indexOf('{x}') >= 0 && url.indexOf('{y}') >= 0 && url.indexOf('{z}') >= 0);
|
||||
this.setState({
|
||||
tmsInput: url,
|
||||
tmsCanPreview: canPreview
|
||||
});
|
||||
}, () => this._sourceConfigChange({ urlTemplate: url }));
|
||||
}
|
||||
|
||||
if (canPreview) {
|
||||
this.props.onSourceConfigChange({ urlTemplate: url });
|
||||
}
|
||||
_handleTMSAttributionChange(attributionUpdate) {
|
||||
this.setState(attributionUpdate, () => {
|
||||
const {
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
tmsInput,
|
||||
} = this.state;
|
||||
|
||||
if (tmsInput && attributionText && attributionUrl) {
|
||||
this._sourceConfigChange({
|
||||
urlTemplate: tmsInput,
|
||||
attributionText,
|
||||
attributionUrl
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
attributionText,
|
||||
attributionUrl,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<EuiFormRow label="Url">
|
||||
<EuiFieldText
|
||||
placeholder={'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'}
|
||||
onChange={(e) => this._handleTMSInputChange(e)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
label="Url"
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder={'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'}
|
||||
onChange={e => this._handleTMSInputChange(e)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Attribution text"
|
||||
isInvalid={attributionUrl !== '' && attributionText === ''}
|
||||
error={[
|
||||
i18n.translate('xpack.maps.xyztmssource.attributionText', {
|
||||
defaultMessage:
|
||||
'Attribution url must have accompanying text',
|
||||
})
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder={'© OpenStreetMap contributors'}
|
||||
onChange={({ target }) =>
|
||||
this._handleTMSAttributionChange({ attributionText: target.value })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow
|
||||
label="Attribution link"
|
||||
isInvalid={attributionText !== '' && attributionUrl === ''}
|
||||
error={[
|
||||
i18n.translate('xpack.maps.xyztmssource.attributionLink', {
|
||||
defaultMessage:
|
||||
'Attribution text must have an accompanying link',
|
||||
})
|
||||
]}
|
||||
>
|
||||
<EuiFieldText
|
||||
placeholder={'https://www.openstreetmap.org/copyright'}
|
||||
onChange={({ target }) =>
|
||||
this._handleTMSAttributionChange({ attributionUrl: target.value })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue