kmap-ui
Version:
A components of zmap base on vue2.X
650 lines (611 loc) • 20.9 kB
JavaScript
/*
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_ext_inherits from '../util/ext'
import ol_source_TileWMS from 'ol/source/TileWMS'
import ol_layer_Tile from 'ol/layer/Tile'
import {transformExtent as ol_proj_transformExtent} from 'ol/proj'
import ol_format_WMSCapabilities from 'ol/format/WMSCapabilities'
import ol_ext_element from '../util/element'
import ol_ext_Ajax from '../util/Ajax'
import '../layer/GetPreview';
import ol_control_Button from './Button'
/** WMSCapabilities
* @constructor
* @param {*} options
* @param {string} options.proxy proxy to use when requesting Getcapabilites, default none (suppose the service use CORS)
* @param {string} options.placeholder input placeholder, default 'service url...'
* @param {string} options.title dialog title, default 'WMS'
* @param {string} options.searchLabel Label for search button, default 'search'
* @param {string} options.loadLabel Label for load button, default 'load'
* @param {*} options.services a key/url object of services for quick access in a menu
* @param {Array<string>} options.srs an array of supported srs, default map projection code or 'EPSG:3857'
* @param {number} options.timeout Timeout for getCapabilities request, default 1000
* @param {boolean} options.cors Use CORS, default false
* @param {boolean} options.trace Log layer info, default false
*/
var ol_control_WMSCapabilities = function (options) {
options = Object.assign({}, options || {});
this._proxy = options.proxy;
this.createDialog(options);
if (options.target===document.body) delete options.target;
if (options.target) {
options.className = ((options.className||'') + ' ol-wmscapabilities ol-hidden').trim();
delete options.target;
} else {
options.className = ((options.className||'') + ' ol-wmscapabilities').trim();
options.handleClick = function () {
this.showDialog();
}.bind(this)
}
ol_control_Button.call(this, options);
// 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);
// Ajax request
var parser = new ol_format_WMSCapabilities();
this._ajax = new ol_ext_Ajax({ dataType:'text', auth: options.authentication });
this._ajax.on('success', function (e) {
try {
var caps = parser.read(e.response);
this.showCapabilitis(caps)
} catch (e) {
this.showError({ type: 'load', error: e });
}
}.bind(this));
this._ajax.on('error', function(e) {
this.showError({ type: 'load', error: e });
}.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));
};
ol_ext_inherits(ol_control_WMSCapabilities, ol_control_Button);
/** Error list */
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...',
srs: 'The service projection looks different from that of your map, it may not display correctly...'
};
/** Form labels */
ol_control_WMSCapabilities.prototype.labels = {
formTitle: 'Title:',
formLayer: 'Layers:',
formMap: 'Map:',
formFormat: 'Format:',
formMinZoom: 'Min zoom level:',
formMaxZoom: 'Max zoom level:',
formExtent: 'Extent:',
formProjection: 'Projection:',
formCrossOrigin: 'CrossOrigin:',
formVersion: 'Version:'
};
/** Create dialog
* @private
*/
ol_control_WMSCapabilities.prototype.createDialog = function (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: 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',
placeholder: options.placeholder || 'service url...',
parent: inputdiv
});
if (options.services) {
var qaccess = ol_ext_element.create('SELECT', {
className: 'url',
on: {
change: function(e) {
console.log(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';
}.bind(this));
this._img.addEventListener('load', function() {
preview.className = 'ol-preview ok';
}.bind(this));
// Select list
var select = this._elements.select = ol_ext_element.create('SELECT', {
className: 'ol-select-list',
size: 10,
on: {
change: function (e) {
select.options[select.selectedIndex].click();
}.bind(this)
},
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),
parent: li
});
}
}.bind(this);
addLine('formTitle');
addLine('formLayer', '', 'layer1,layer2,...');
addLine('formMap');
addLine('formFormat', ['image/png', 'image/jpeg']);
addLine('formMinZoom', 0);
addLine('formMaxZoom', 20);
addLine('formExtent', '', 'xmin,ymin,xmax,ymax');
addLine('formProjection', '');
addLine('formCrossOrigin', false);
addLine('formVersion', '1.3.0');
ol_ext_element.create('BUTTON', {
html: this.get('loadLabel') || 'Load',
click: function() {
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 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,
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
}
}
if (this._elements.formMap.value) options.source.param.MAP = this._elements.formMap.value;
var layer = this.getLayerFromOptions(options);
this.dispatchEvent({ type: 'load', layer: layer, options: options });
}.bind(this),
parent: form
});
return element;
};
/** Create a new layer using options received by getOptionsFromCap method
* @param {*} options
*/
ol_control_WMSCapabilities.prototype.getLayerFromOptions = function (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.
*/
ol_control_WMSCapabilities.prototype.setMap = function (map) {
ol_control_Button.prototype.setMap.call(this, map);
if (this._dialog) this._dialog.setMap(map);
};
/** Show dialog for url */
ol_control_WMSCapabilities.prototype.showDialog = function(url, options) {
if (url) this.showError();
if (!this._elements.formProjection.value) {
this._elements.formProjection.value = this.getMap().getView().getProjection().getCode();
}
this._dialog.show({
title: this.get('title')===undefined ? 'WMS' : this.get('title'),
content: this._elements.element
});
this.getCapabilities(url, options);
};
/** Get WMS capabilities for a server
* @param {string} url service url
* @param {*} options
* @param {string} options.version WMS version, default 1.3.0
* @param {Number} options.timeout
* @param {string} options.map WMS map or get map in url?map=xxx
* @param {string} options.version WMS version
*/
ol_control_WMSCapabilities.prototype.getCapabilities = function(url, options) {
if (!url) return;
if (!/(https?:\/\/)?([\da-z\.-]+)\.([a-z]{2,6})([\/\w\.-]*)*\/?/g.test(url)) {
this.showError({
type: 'badUrl'
})
return;
}
options = options || {};
// Extract map attributes
url = url.split('?');
var search = url[1];
url = url[0];
var map = options.map || '';
if (search) {
search = search.replace(/^\?/,'').split('&');
search.forEach(function(s) {
console.log(s)
s = s.split('=');
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];
}
}.bind(this))
}
// Fill form
this._elements.input.value = (url || '') + (map ? '?map='+map : '');
this.clearForm();
var request = {
SERVICE: 'WMS',
REQUEST: 'GetCapabilities',
VERSION: options.version || '1.3.0'
}
if (map) request.MAP = map;
if (this._proxy) {
var q = '';
for (var r in request) q += (q?'&':'')+r+'='+request[r];
this._ajax.send(this._proxy, {
url: q
}, {
timeout: options.timeout || 10000
});
} else {
this._ajax.send(url, request, {
timeout: options.timeout || 10000
});
}
};
/** Display error
* @param {*} error event
*/
ol_control_WMSCapabilities.prototype.showError = function(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
*/
ol_control_WMSCapabilities.prototype.clearForm = function() {
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 = '';
};
/** Display capabilities in the dialog
* @param {*} caps JSON capabilities
*/
ol_control_WMSCapabilities.prototype.showCapabilitis = function(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,
click: function(e) {
if (e.isTrusted) return;
// Load layer
var options = this.getOptionsFromCap(l, caps);
var layer = this.getLayerFromOptions(options);
//
list.forEach(function(i) {
i.classList.remove('selected');
})
li.classList.add('selected');
this._elements.buttons.innerHTML = '';
ol_ext_element.create('BUTTON', {
html: this.get('loadLabel') || 'Load',
click: function() {
this.dispatchEvent({type: 'load', layer: layer, options: options });
if (this._dialog) this._dialog.hide();
}.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
this._elements.data.innerHTML = '';
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.length) {
this._elements.legend.src = options.data.legend[0];
} else {
this._elements.legend.src = '';
}
}.bind(this),
parent: this._elements.select
});
list.push(li);
if (l.Layer) {
addLayers(l, level+1);
}
}.bind(this));
}.bind(this);
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
*/
ol_control_WMSCapabilities.prototype.getLayerResolution = function(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
*/
ol_control_WMSCapabilities.prototype.getOptionsFromCap = function(caps, parent) {
var formats = parent.Capability.Request.GetMap.Format;
// 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);
bbox = ol_proj_transformExtent(bbox, 'EPSG:4326', this.getMap().getView().getProjection());
var attributions = [];
if (caps.Attribution) {
attributions.push('<a href="'+encodeURI(caps.Attribution.OnlineResource)+'">© '+caps.Attribution.Title.replace(/</g,'<')+'</a>');
}
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, //parent.Service.OnlineResource,
projection: srs,
attributions: attributions,
crossOrigin: this.get('cors') ? 'anonymous' : null,
params: {
'LAYERS': caps.Name,
'FORMAT': format,
'VERSION': parent.version || '1.3.0'
}
}
// Trace
if (this.get('trace')) {
var tso = JSON.stringify([ source_opt ], null, "\t").replace(/\\"/g,'"');
layer_opt.source = "new ol.source.TileWMS("+tso+")";
var t = "new ol.layer.Tile (" +JSON.stringify(layer_opt, null, "\t")+ ")"
t = t.replace(/\\"/g,'"')
.replace(/"new/g,'new')
.replace(/\)"/g,')')
.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
}
});
};
export default ol_control_WMSCapabilities