/* Using WMS Layer with EPSG:4326 projection. The tiles will be reprojected to map pojection (EPSG:3857). NB: reduce tileSize to minimize deformations on small scales. */ import ol_View from 'ol/View.js' import ol_source_TileWMS from 'ol/source/TileWMS.js' import ol_layer_Tile from 'ol/layer/Tile.js' import {transformExtent as ol_proj_transformExtent} from 'ol/proj.js' import ol_format_WMSCapabilities from 'ol/format/WMSCapabilities.js' import ol_ext_element from '../util/element.js' import ol_ext_Ajax from '../util/Ajax.js' import ol_control_Button from './Button.js' import ol_control_Dialog from './Dialog.js' import '../layer/GetPreview.js'; /** WMSCapabilities * @constructor * @fires load * @fires capabilities * @extends {ol_control_Button} * @param {*} options * @param {string|Element} [options.target] the target to set the dialog, use document.body to have fullwindow dialog * @param {string} [options.proxy] proxy to use when requesting Getcapabilites, default none (suppose the service use CORS) * @param {string} [options.placeholder='service url...'] input placeholder, default 'service url...' * @param {string} [options.title=WMS] dialog title, default 'WMS' * @param {string} [options.searchLabel='search'] Label for search button, default 'search' * @param {string} [options.loadLabel='load'] Label for load button, default 'load' * @param {Array} [options.srs] an array of supported srs, default map projection code or 'EPSG:3857' * @param {number} [options.timeout=1000] Timeout for getCapabilities request, default 1000 * @param {boolean} [options.cors=false] Use CORS, default false * @param {string} [options.optional] a list of optional url properties (when set in the request url), separated with ',' * @param {boolean} [options.trace=false] Log layer info, default false * @param {*} [options.services] a key/url object of services for quick access in a menu */ var ol_control_WMSCapabilities = class olcontrolWMSCapabilities extends ol_control_Button { constructor(options) { options = options || {} var buttonOptions = Object.assign({}, options || {}) if (buttonOptions.target === document.body) delete buttonOptions.target if (buttonOptions.target) { buttonOptions.className = ((buttonOptions.className || '') + ' ol-wmscapabilities ol-hidden').trim() delete buttonOptions.target } else { buttonOptions.className = ((buttonOptions.className || '') + ' ol-wmscapabilities').trim() buttonOptions.handleClick = function () { self.showDialog() } } super(buttonOptions); var self = this; this._proxy = options.proxy // WMS options this.set('srs', options.srs || []) this.set('cors', options.cors) this.set('trace', options.trace) this.set('title', options.title) this.set('loadLabel', options.loadLabel) this.set('optional', options.optional) // Dialog this.createDialog(options) // Default version this._elements.formVersion.value = '1.0.0' // Ajax request var parser = this._getParser() this._ajax = new ol_ext_Ajax({ dataType: 'text', auth: options.authentication }) this._ajax.on('success', function (evt) { var caps try { caps = parser.read(evt.response) } catch (e) { this.showError({ type: 'load', error: e }) } if (caps) { if (!caps.Capability.Layer.Layer) { this.showError({ type: 'noLayer' }) } else { caps.url = evt.options.url this.showCapabilities(caps) } } this.dispatchEvent({ type: 'capabilities', capabilities: caps }) if (typeof (evt.options.callback) === 'function') evt.options.callback(caps) }.bind(this)) this._ajax.on('error', function (evt) { this.showError({ type: 'load', error: evt }) this.dispatchEvent({ type: 'capabilities' }) if (typeof (evt.options.callback) === 'function') false }.bind(this)) // Handle waiting this._ajax.on('loadstart', function () { this._elements.element.classList.add('ol-searching') }.bind(this)) this._ajax.on('loadend', function () { this._elements.element.classList.remove('ol-searching') }.bind(this)) // Load a layer if (options.onselect) { this.on('load', function (e) { options.onselect(e.layer, e.options) }) } } /** Get service parser */ _getParser() { return new ol_format_WMSCapabilities() } /** Create dialog * @private */ createDialog(options) { var target = options.target if (!target || target === document.body) { this._dialog = new ol_control_Dialog({ className: 'ol-wmscapabilities', closeBox: true, closeOnSubmit: false, target: options.target }) this._dialog.on('button', function (e) { if (e.button === 'submit') { this.getCapabilities(e.inputs.url.value) } }.bind(this)) target = null } var element = ol_ext_element.create('DIV', { className: ('ol-wmscapabilities ' + (options.className || '')).trim(), parent: target }) this._elements = { element: target || element } var inputdiv = ol_ext_element.create('DIV', { className: 'ol-url', parent: element }) var input = this._elements.input = ol_ext_element.create('INPUT', { className: 'url', type: 'text', tabIndex: 1, placeholder: options.placeholder || 'service url...', autocorrect: 'off', autocapitalize: 'off', parent: inputdiv }) input.addEventListener('keyup', function (e) { if (e.keyCode === 13) { this.getCapabilities(input.value, options) } }.bind(this)) if (options.services) { var qaccess = ol_ext_element.create('SELECT', { className: 'url', on: { change: function (e) { var url = e.target.options[e.target.selectedIndex].value this.getCapabilities(url, options) e.target.selectedIndex = 0 }.bind(this) }, parent: inputdiv }) ol_ext_element.create('OPTION', { html: ' ', parent: qaccess }) for (var k in options.services) { ol_ext_element.create('OPTION', { html: k, value: options.services[k], parent: qaccess }) } } ol_ext_element.create('BUTTON', { click: function () { this.getCapabilities(input.value, options) }.bind(this), html: options.searchLabel || 'search', parent: inputdiv }) // Errors this._elements.error = ol_ext_element.create('DIV', { className: 'ol-error', parent: inputdiv }) // Result div var rdiv = this._elements.result = ol_ext_element.create('DIV', { className: 'ol-result', parent: element }) // Preview var preview = ol_ext_element.create('DIV', { className: 'ol-preview', html: options.previewLabel || 'preview', parent: rdiv }) this._elements.preview = ol_ext_element.create('IMG', { parent: preview }) // Check tainted canvas this._img = new Image this._img.crossOrigin = 'Anonymous' this._img.addEventListener('error', function () { preview.className = 'ol-preview tainted' this._elements.formCrossOrigin.checked = false }.bind(this)) this._img.addEventListener('load', function () { preview.className = 'ol-preview ok' this._elements.formCrossOrigin.checked = true }.bind(this)) // Select list this._elements.select = ol_ext_element.create('SELECT', { className: 'ol-select-list', on: { keydown: function(e) { if (e.key === 'Enter' || e.key === ' ') { var index = this._elements.select.selectedIndex; if (index >= 0) { this._elements.select.querySelectorAll('OPTION')[index].click(); } } }.bind(this) }, size: 100, parent: rdiv }) // Info data this._elements.data = ol_ext_element.create('DIV', { className: 'ol-data', parent: rdiv }) this._elements.buttons = ol_ext_element.create('DIV', { className: 'ol-buttons', parent: rdiv }) this._elements.legend = ol_ext_element.create('IMG', { className: 'ol-legend', parent: rdiv }) // WMS form var form = this._elements.form = ol_ext_element.create('UL', { className: 'ol-wmsform', parent: element }) var addLine = function (label, val, pholder) { var li = ol_ext_element.create('LI', { parent: form }) ol_ext_element.create('LABEL', { html: this.labels[label], parent: li }) if (typeof (val) === 'boolean') { this._elements[label] = ol_ext_element.create('INPUT', { type: 'checkbox', checked: val, parent: li }) } else if (val instanceof Array) { var sel = this._elements[label] = ol_ext_element.create('SELECT', { parent: li }) val.forEach(function (v) { ol_ext_element.create('OPTION', { html: v, value: v, parent: sel }) }.bind(this)) } else { this._elements[label] = ol_ext_element.create('INPUT', { value: (val === undefined ? '' : val), placeholder: pholder || '', type: typeof (val) === 'number' ? 'number' : 'text', parent: li }) } return li }.bind(this) addLine('formTitle') addLine('formLayer', '', 'layer1,layer2,...') var li = addLine('formMap') li.setAttribute('data-param', 'map') li = addLine('formStyle') li.setAttribute('data-param', 'style') addLine('formFormat', ['image/png', 'image/jpeg']) addLine('formMinZoom', 0) addLine('formMaxZoom', 20) li = addLine('formExtent', '', 'xmin,ymin,xmax,ymax') li.setAttribute('data-param', 'extent') var extent = li.querySelector('input') ol_ext_element.create('BUTTON', { title: this.labels.mapExtent, click: function () { extent.value = this.getMap().getView().calculateExtent(this.getMap().getSize()).join(',') }.bind(this), parent: li }) li = addLine('formProjection', '') li.setAttribute('data-param', 'proj') addLine('formCrossOrigin', false) li = addLine('formVersion', '1.3.0') li.setAttribute('data-param', 'version') addLine('formAttribution', '') ol_ext_element.create('BUTTON', { html: this.get('loadLabel') || 'Load', click: function () { var opt = this._getFormOptions() var layer = this.getLayerFromOptions(opt) this.dispatchEvent({ type: 'load', layer: layer, options: opt }) this._dialog.hide() }.bind(this), parent: form }) return element } /** Create a new layer using options received by getOptionsFromCap method * @param {*} options */ getLayerFromOptions(options) { options.layer.source = new ol_source_TileWMS(options.source) var layer = new ol_layer_Tile(options.layer) delete options.layer.source return layer } /** * Set the map instance the control is associated with * and add its controls associated to this map. * @param {_ol_Map_} map The map instance. */ setMap(map) { super.setMap(map) if (this._dialog) this._dialog.setMap(map) } /** Get the dialog * @returns {ol_control_Dialog} */ getDialog() { return this._dialog } /** Show dialog for url * @param {string} [url] service url, default ask for an url * @param {*} options capabilities options * @param {string} options.map WMS map or get map in url?map=xxx * @param {string} options.version WMS version (yet only 1.3.0 is implemented), default 1.3.0 * @param {number} options.timeout timout to get the capabilities, default 10000 */ showDialog(url, options) { this.showError() if (!this._elements.formProjection.value) { this._elements.formProjection.value = this.getMap().getView().getProjection().getCode() } if (this._dialog) { this._dialog.show({ title: this.get('title') === undefined ? 'WMS' : this.get('title'), content: this._elements.element }) } this.getCapabilities(url, options) // Center on selection var sel = this._elements.select.querySelector('.selected') if (sel) { this._elements.select.scrollTop = sel.offsetTop - 20 } } /** Test url and return true if it is a valid url string * @param {string} url * @return {bolean} * @api */ testUrl(url) { // var pattern = /(https?:\/\/)([\da-z.-]+)\.([a-z]{2,6})([/\w.-]*)*\/?/ var pattern = new RegExp( // protocol '^(https?:\\/\\/)' + // domain name '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // OR ip (v4) address '((\\d{1,3}\\.){3}\\d{1,3}))' + // port and path '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // query string '(\\?[;&a-z\\d%_.~+=\\/-]*)?' + // fragment locator '(\\#[-a-z\\d_]*)?$', 'i') return !!pattern.test(url) } /** Get Capabilities request parameters * @param {*} options */ getRequestParam(options) { return { SERVICE: 'WMS', REQUEST: 'GetCapabilities', VERSION: options.version || '1.3.0' } } /** Get WMS capabilities for a server * @fire load * @param {string} url service url * @param {*} options * @param {string} options.map WMS map or get map in url?map=xxx * @param {string} [options.version=1.3.0] WMS version (yet only 1.3.0 is implemented), default 1.3.0 * @param {number} [options.timeout=10000] timout to get the capabilities, default 10000 * @param {function} [options.onload] callback function */ getCapabilities(url, options) { if (!url) return if (!this.testUrl(url)) { this.showError({ type: 'badUrl' }) return } options = options || {} // Extract map attributes url = url.split('?') var search = url[1] url = url[0] // reset this._elements.formMap.value = '' this._elements.formLayer.value = '' this._elements.formStyle.value = '' this._elements.formTitle.value = '' this._elements.formProjection.value = this.getMap().getView().getProjection().getCode() this._elements.formFormat.selectedIndex = 0 var map = options.map || '' var optional = {} if (search) { search = search.replace(/^\?/, '').split('&') search.forEach(function (s) { s = s.split('=') s[1] = decodeURIComponent(s[1] || '') if (/^map$/i.test(s[0])) { map = s[1] this._elements.formMap.value = map } if (/^layers$/i.test(s[0])) { this._elements.formLayer.value = s[1] this._elements.formTitle.value = s[1].split(',')[0] } if (/^style$/i.test(s[0])) { this._elements.formStyle.value = s[1] } if (/^crs$/i.test(s[0])) { this._elements.formProjection.value = s[1] } if (/^format$/i.test(s[0])) { for (var o, i = 0; o = this._elements.formFormat.options[i]; i++) { if (o.value === s[1]) { this._elements.formFormat.selectedIndex = i break } } } // Check optionals if (this.get('optional')) { this.get('optional').split(',').forEach(function (o) { if (o === s[0]) { optional[o] = s[1] } }.bind(this)) } }.bind(this)) } this._optional = optional; // Get request params var request = this.getRequestParam(options) var opt = [] if (map) { request.MAP = map opt.push('map=' + map) } for (var o in optional) { request[o] = optional[o] opt.push(o + '=' + optional[o]) } // Fill form var uri = this._elements.input.value = (url || '') + (opt ? '?' + opt.join('&') : '') this.clearForm() // Sen drequest if (this._proxy) { var q = '' for (var r in request) q += (q ? '&' : '') + r + '=' + request[r] this._ajax.send(this._proxy, { url: q }, { url: uri, timeout: options.timeout || 10000, callback: options.onload, abort: false }) } else { this._ajax.send(url, request, { url: uri, timeout: options.timeout || 10000, callback: options.onload, abort: false }) } } /** Display error * @param {*} error event */ showError(e) { if (!e) this._elements.error.innerHTML = '' else this._elements.error.innerHTML = this.error[e.type] || ('ERROR (' + e.type + ')') if (e && e.type === 'load') { this._elements.form.classList.add('visible') } else { this._elements.form.classList.remove('visible') } } /** Clear form */ clearForm() { this._elements.result.classList.remove('ol-visible') this.showError() this._elements.select.innerHTML = '' this._elements.data.innerHTML = '' this._elements.preview.src = '' this._elements.legend.src = '' this._elements.legend.classList.remove('visible') } /** Display capabilities in the dialog * @param {*} caps JSON capabilities */ showCapabilities(caps) { this._elements.result.classList.add('ol-visible') // console.log(caps) var list = [] var addLayers = function (parent, level) { level = level || 0 parent.Layer.forEach(function (l) { if (!l.Attribution) l.Attribution = parent.Attribution if (!l.EX_GeographicBoundingBox) l.EX_GeographicBoundingBox = parent.EX_GeographicBoundingBox var li = ol_ext_element.create('OPTION', { className: (l.Layer ? 'ol-title ' : '') + 'level-' + level, html: l.Name || l.Title, title: l.Name || l.Title, click: function () { // Reset this._elements.buttons.innerHTML = '' this._elements.data.innerHTML = '' this._elements.legend.src = this._elements.preview.src = '' this._elements.element.classList.remove('ol-form') this.showError() // Load layer var options = this.getOptionsFromCap(l, caps) var layer = this.getLayerFromOptions(options) this._currentOptions = options // list.forEach(function (i) { i.classList.remove('selected') }) li.classList.add('selected') // Fill form if (layer) { ol_ext_element.create('BUTTON', { html: this.get('loadLabel') || 'Load', className: 'ol-load', click: function () { this.dispatchEvent({ type: 'load', layer: layer, options: options }) if (this._dialog) this._dialog.hide() }.bind(this), parent: this._elements.buttons }) ol_ext_element.create('BUTTON', { className: 'ol-wmsform', click: function () { this._elements.element.classList.toggle('ol-form') }.bind(this), parent: this._elements.buttons }) // Show preview var reso = this.getMap().getView().getResolution() var center = this.getMap().getView().getCenter() this._elements.preview.src = layer.getPreview(center, reso, this.getMap().getView().getProjection()) this._img.src = this._elements.preview.src // ShowInfo ol_ext_element.create('p', { className: 'ol-title', html: options.data.title, parent: this._elements.data }) ol_ext_element.create('p', { html: options.data.abstract, parent: this._elements.data }) if (options.data.legend && options.data.legend.length) { this._elements.legend.src = options.data.legend[0] this._elements.legend.classList.add('visible') } else { this._elements.legend.src = '' this._elements.legend.classList.remove('visible') } } }.bind(this), parent: this._elements.select }) list.push(li) if (l.Layer) { addLayers(l, level + 1) } }.bind(this)) }.bind(this) // Show layers this._elements.select.innerHTML = '' addLayers(caps.Capability.Layer) } /** Get resolution for a layer * @param {string} 'min' or 'max' * @param {*} layer * @param {number} val * @return {number} * @private */ getLayerResolution(m, layer, val) { var att = m === 'min' ? 'MinScaleDenominator' : 'MaxScaleDenominator' if (layer[att] !== undefined) return layer[att] / (72 / 2.54 * 100) if (!layer.Layer) return (m === 'min' ? 0 : 156543.03392804097) // Get min / max of contained layers val = (m === 'min' ? 156543.03392804097 : 0) for (var i = 0; i < layer.Layer.length; i++) { var res = this.getLayerResolution(m, layer.Layer[i], val) if (res !== undefined) val = Math[m](val, res) } return val } /** Return a WMS ol.layer.Tile for the given capabilities * @param {*} caps layer capabilities (read from the capabilities) * @param {*} parent capabilities * @return {*} options */ getOptionsFromCap(caps, parent) { var formats = parent.Capability.Request.GetMap.Format var format, i // Look for prefered format first var pref = [/png/, /jpeg/, /gif/] for (i = 0; i < 3; i++) { for (var f = 0; f < formats.length; f++) { if (pref[i].test(formats[f])) { format = formats[f] break } } if (format) break } if (!format) format = formats[0] // Check srs var srs = this.getMap().getView().getProjection().getCode() this.showError() var crs = false if (!caps.CRS) { crs = false } else if (caps.CRS.indexOf(srs) >= 0) { crs = true } else if (caps.CRS.indexOf('EPSG:4326') >= 0) { // try to set EPSG:4326 instead srs = 'EPSG:4326' crs = true } else { this.get('srs').forEach(function (s) { if (caps.CRS.indexOf(s) >= 0) { srs = s crs = true } }) } if (!crs) { this.showError({ type: 'srs' }) if (this.get('trace')) console.log('BAD srs: ', caps.CRS) } var bbox = caps.EX_GeographicBoundingBox //bbox = ol_proj_transformExtent(bbox, 'EPSG:4326', srs); if (bbox) bbox = ol_proj_transformExtent(bbox, 'EPSG:4326', this.getMap().getView().getProjection()) var attributions = [] if (caps.Attribution && caps.Attribution.Title) { attributions.push('© ' + caps.Attribution.Title.replace(/') } var layer_opt = { title: caps.Title, extent: bbox, queryable: caps.queryable, abstract: caps.Abstract, minResolution: this.getLayerResolution('min', caps), maxResolution: this.getLayerResolution('max', caps) || 156543.03392804097 } var source_opt = { url: (parent.Capability.Request.GetMap.DCPType[0].HTTP.Get.OnlineResource || '').replace(/service=wms&?/i,''), projection: srs, attributions: attributions, crossOrigin: this.get('cors') ? 'anonymous' : null, params: { 'LAYERS': caps.Name, 'FORMAT': format, 'VERSION': parent.version || '1.3.0' } } Object.keys(this._optional).forEach(o => source_opt.params[o] = this._optional[o]) // Resolution to zoom var view = new ol_View({ projection: this.getMap().getView().getProjection() }) view.setResolution(layer_opt.minResolution) var maxZoom = Math.round(view.getZoom()) view.setResolution(layer_opt.maxResolution) var minZoom = Math.round(view.getZoom()) // Fill form this._fillForm({ title: layer_opt.title, layers: source_opt.params.LAYERS, format: source_opt.params.FORMAT, minZoom: minZoom, maxZoom: maxZoom, extent: bbox ? bbox.join(',') : '', projection: source_opt.projection, attribution: source_opt.attributions[0] || '', version: source_opt.params.VERSION }) // Trace if (this.get('trace')) { var tso = JSON.stringify([source_opt], null, "\t").replace(/\\"/g, '"') layer_opt.source = "SOURCE" var t = "new ol.layer.Tile (" + JSON.stringify(layer_opt, null, "\t") + ")" t = t.replace(/\\"/g, '"') .replace('"SOURCE"', "new ol.source.TileWMS(" + tso + ")") .replace(/\\t/g, "\t").replace(/\\n/g, "\n") .replace("([\n\t", "(") .replace("}\n])", "})") console.log(t) delete layer_opt.source } // Legend ? var legend = [] if (caps.Style) { caps.Style.forEach(function (s) { if (s.LegendURL) { legend.push(s.LegendURL[0].OnlineResource) } }) } return ({ layer: layer_opt, source: source_opt, data: { title: caps.Title, abstract: caps.Abstract, logo: caps.Attribution && caps.Attribution.LogoURL ? caps.Attribution.LogoURL.OnlineResource : undefined, keyword: caps.KeywordList, legend: legend, opaque: caps.opaque, queryable: caps.queryable } }) } /** Get WMS options from control form * @return {*} options * @private */ _getFormOptions() { var minZoom = parseInt(this._elements.formMinZoom.value) var maxZoom = parseInt(this._elements.formMaxZoom.value) var view = new ol_View({ projection: this.getMap().getView().getProjection() }) view.setZoom(minZoom) var maxResolution = view.getResolution() view.setZoom(maxZoom) var minResolution = view.getResolution() var ext = [] if (this._elements.formExtent.value) { this._elements.formExtent.value.split(',').forEach(function (b) { ext.push(parseFloat(b)) }) } if (ext.length !== 4) ext = undefined var attributions = [] if (this._elements.formAttribution.value) attributions.push(this._elements.formAttribution.value) var options = { layer: { title: this._elements.formTitle.value, extent: ext, maxResolution: maxResolution, minResolution: minResolution }, source: { url: this._elements.input.value, crossOrigin: this._elements.formCrossOrigin.checked ? 'anonymous' : null, projection: this._elements.formProjection.value, attributions: attributions, params: { FORMAT: this._elements.formFormat.options[this._elements.formFormat.selectedIndex].value, LAYERS: this._elements.formLayer.value, VERSION: this._elements.formVersion.value } }, data: { title: this._elements.formTitle.value } } Object.keys(this._optional).forEach(o => options.source.params[o] = this._optional[o]) if (this._elements.formMap.value) { options.source.params.MAP = this._elements.formMap.value } return options } /** Fill dialog form * @private */ _fillForm(opt) { this._elements.formTitle.value = opt.title this._elements.formLayer.value = opt.layers this._elements.formStyle.value = opt.style var o, i for (i = 0; o = this._elements.formFormat.options[i]; i++) { if (o.value === opt.format) { this._elements.formFormat.selectedIndex = i break } } this._elements.formExtent.value = opt.extent || '' this._elements.formMaxZoom.value = opt.maxZoom this._elements.formMinZoom.value = opt.minZoom this._elements.formProjection.value = opt.projection this._elements.formAttribution.value = opt.attribution this._elements.formVersion.value = opt.version } /** Load a layer using service * @param {string} url service url * @param {string} layername * @param {function} [onload] callback function (or listen to 'load' event) */ loadLayer(url, layerName, onload) { this.getCapabilities(url, { onload: function (cap) { if (cap) { cap.Capability.Layer.Layer.forEach(function (l) { if (l.Name === layerName || l.Identifier === layerName) { var options = this.getOptionsFromCap(l, cap) var layer = this.getLayerFromOptions(options) this.dispatchEvent({ type: 'load', layer: layer, options: options }) if (typeof (onload) === 'function') onload({ layer: layer, options: options }) } }.bind(this)) } else { this.dispatchEvent({ type: 'load', error: true }) } }.bind(this) }) } } /** Error list: a key/value list of error to display in the dialog * Overwrite it to handle internationalization */ ol_control_WMSCapabilities.prototype.error = { load: 'Can\'t retrieve service capabilities, try to add it manually...', badUrl: 'The input value is not a valid url...', TileMatrix: 'No TileMatrixSet supported...', noLayer: 'No layer available for this service...', srs: 'The service projection looks different from that of your map, it may not display correctly...' }; /** Form labels: a key/value list of form labels to display in the dialog * Overwrite it to handle internationalization */ ol_control_WMSCapabilities.prototype.labels = { formTitle: 'Title:', formLayer: 'Layers:', formMap: 'Map:', formStyle: 'Style:', formFormat: 'Format:', formMinZoom: 'Min zoom level:', formMaxZoom: 'Max zoom level:', formExtent: 'Extent:', mapExtent: 'use map extent...', formProjection: 'Projection:', formCrossOrigin: 'CrossOrigin:', formVersion: 'Version:', formAttribution: 'Attribution:', }; export default ol_control_WMSCapabilities