Fullstack Portal Created by the HCMR for the Marine Strategy Framework Directive Program in order to cover demands and aspects considering extendability and maintainability
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

496 lines
14 KiB

2 years ago
/*!
* leaflet.wms.js
* A collection of Leaflet utilities for working with Web Mapping services.
* (c) 2014-2016, Houston Engineering, Inc.
* MIT License
*/
(function (factory) {
// Module systems magic dance, Leaflet edition
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
module.exports = factory(require('leaflet'));
} else {
// Browser globals
if (typeof this.L === 'undefined')
throw 'Leaflet must be loaded first!';
// Namespace
this.L.WMS = this.L.wms = factory(this.L);
}
}(function (L) {
// Module object
var wms = {};
// Quick shim for Object.keys()
if (!('keys' in Object)) {
Object.keys = function(obj) {
var result = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
result.push(i);
}
}
return result;
};
}
/*
* wms.Source
* The Source object manages a single WMS connection. Multiple "layers" can be
* created with the getLayer function, but a single request will be sent for
* each image update. Can be used in non-tiled "overlay" mode (default), or
* tiled mode, via an internal wms.Overlay or wms.TileLayer, respectively.
*/
wms.Source = L.Layer.extend({
'options': {
'untiled': true,
'identify': true
},
'initialize': function(url, options) {
L.setOptions(this, options);
if (this.options.tiled) {
this.options.untiled = false;
}
this._url = url;
this._subLayers = {};
this._overlay = this.createOverlay(this.options.untiled);
},
'createOverlay': function(untiled) {
// Create overlay with all options other than untiled & identify
var overlayOptions = {};
for (var opt in this.options) {
if (opt != 'untiled' && opt != 'identify') {
overlayOptions[opt] = this.options[opt];
}
}
if (untiled) {
return wms.overlay(this._url, overlayOptions);
} else {
return wms.tileLayer(this._url, overlayOptions);
}
},
'onAdd': function() {
this.refreshOverlay();
},
'getEvents': function() {
if (this.options.identify) {
return {'click': this.identify};
} else {
return {};
}
},
'setOpacity': function(opacity) {
this.options.opacity = opacity;
if (this._overlay) {
this._overlay.setOpacity(opacity);
}
},
'bringToBack': function() {
this.options.isBack = true;
if (this._overlay) {
this._overlay.bringToBack();
}
},
'bringToFront': function() {
this.options.isBack = false;
if (this._overlay) {
this._overlay.bringToFront();
}
},
'getLayer': function(name) {
return wms.layer(this, name);
},
'addSubLayer': function(name) {
this._subLayers[name] = true;
this.refreshOverlay();
},
'removeSubLayer': function(name) {
delete this._subLayers[name];
this.refreshOverlay();
},
'refreshOverlay': function() {
var subLayers = Object.keys(this._subLayers).join(",");
if (!this._map) {
return;
}
if (!subLayers) {
this._overlay.remove();
} else {
this._overlay.setParams({'layers': subLayers});
this._overlay.addTo(this._map);
}
},
'identify': function(evt) {
// Identify map features in response to map clicks. To customize this
// behavior, create a class extending wms.Source and override one or
// more of the following hook functions.
var layers = this.getIdentifyLayers();
if (!layers.length) {
return;
}
this.getFeatureInfo(
evt.containerPoint, evt.latlng, layers,
this.showFeatureInfo
);
},
'getFeatureInfo': function(point, latlng, layers, callback) {
// Request WMS GetFeatureInfo and call callback with results
// (split from identify() to faciliate use outside of map events)
var params = this.getFeatureInfoParams(point, layers),
url = this._url + L.Util.getParamString(params, this._url);
this.showWaiting();
this.ajax(url, done);
function done(result) {
this.hideWaiting();
var text = this.parseFeatureInfo(result, url);
callback.call(this, latlng, text);
}
},
'ajax': function(url, callback) {
ajax.call(this, url, callback);
},
'getIdentifyLayers': function() {
// Hook to determine which layers to identify
if (this.options.identifyLayers)
return this.options.identifyLayers;
return Object.keys(this._subLayers);
},
'getFeatureInfoParams': function(point, layers) {
// Hook to generate parameters for WMS service GetFeatureInfo request
var wmsParams, overlay;
if (this.options.untiled) {
// Use existing overlay
wmsParams = this._overlay.wmsParams;
} else {
// Create overlay instance to leverage updateWmsParams
overlay = this.createOverlay(true);
overlay.updateWmsParams(this._map);
wmsParams = overlay.wmsParams;
wmsParams.layers = layers.join(',');
}
var infoParams = {
'request': 'GetFeatureInfo',
'query_layers': layers.join(','),
'X': Math.round(point.x),
'Y': Math.round(point.y)
};
return L.extend({}, wmsParams, infoParams);
},
'parseFeatureInfo': function(result, url) {
// Hook to handle parsing AJAX response
if (result == "error") {
// AJAX failed, possibly due to CORS issues.
// Try loading content in <iframe>.
result = "<iframe src='" + url + "' style='border:none'>";
}
return result;
},
'showFeatureInfo': function(latlng, info) {
// Hook to handle displaying parsed AJAX response to the user
if (!this._map) {
return;
}
this._map.openPopup(info, latlng);
},
'showWaiting': function() {
// Hook to customize AJAX wait animation
if (!this._map)
return;
this._map._container.style.cursor = "progress";
},
'hideWaiting': function() {
// Hook to remove AJAX wait animation
if (!this._map)
return;
this._map._container.style.cursor = "default";
}
});
wms.source = function(url, options) {
return new wms.Source(url, options);
};
/*
* Layer
* Leaflet "layer" with all actual rendering handled via an underlying Source
* object. Can be called directly with a URL to automatically create or reuse
* an existing Source. Note that the auto-source feature doesn't work well in
* multi-map environments; so for best results, create a Source first and use
* getLayer() to retrieve wms.Layer instances.
*/
wms.Layer = L.Layer.extend({
'initialize': function(source, layerName, options) {
L.setOptions(this, options);
if (!source.addSubLayer) {
// Assume source is a URL
source = wms.getSourceForUrl(source, options);
}
this._source = source;
this._name = layerName;
},
'onAdd': function() {
if (!this._source._map)
this._source.addTo(this._map);
this._source.addSubLayer(this._name);
},
'onRemove': function() {
this._source.removeSubLayer(this._name);
},
'setOpacity': function(opacity) {
this._source.setOpacity(opacity);
},
'bringToBack': function() {
this._source.bringToBack();
},
'bringToFront': function() {
this._source.bringToFront();
}
});
wms.layer = function(source, options) {
return new wms.Layer(source, options);
};
// Cache of sources for use with wms.Layer auto-source option
var sources = {};
wms.getSourceForUrl = function(url, options) {
if (!sources[url]) {
sources[url] = wms.source(url, options);
}
return sources[url];
};
// Copy tiled WMS layer from leaflet core, in case we need to subclass it later
wms.TileLayer = L.TileLayer.WMS;
wms.tileLayer = L.tileLayer.wms;
/*
* wms.Overlay:
* "Single Tile" WMS image overlay that updates with map changes.
* Portions of wms.Overlay are directly extracted from L.TileLayer.WMS.
* See Leaflet license.
*/
wms.Overlay = L.Layer.extend({
'defaultWmsParams': {
'service': 'WMS',
'request': 'GetMap',
'version': '1.1.1',
'layers': '',
'styles': '',
'format': 'image/jpeg',
'transparent': false
},
'options': {
'crs': null,
'uppercase': false,
'attribution': '',
'opacity': 1,
'isBack': false,
'minZoom': 0,
'maxZoom': 18
},
'initialize': function(url, options) {
this._url = url;
// Move WMS parameters to params object
var params = {}, opts = {};
for (var opt in options) {
if (opt in this.options) {
opts[opt] = options[opt];
} else {
params[opt] = options[opt];
}
}
L.setOptions(this, opts);
this.wmsParams = L.extend({}, this.defaultWmsParams, params);
},
'setParams': function(params) {
L.extend(this.wmsParams, params);
this.update();
},
'getAttribution': function() {
return this.options.attribution;
},
'onAdd': function() {
this.update();
},
'onRemove': function(map) {
if (this._currentOverlay) {
map.removeLayer(this._currentOverlay);
delete this._currentOverlay;
}
if (this._currentUrl) {
delete this._currentUrl;
}
},
'getEvents': function() {
return {
'moveend': this.update
};
},
'update': function() {
if (!this._map) {
return;
}
// Determine image URL and whether it has changed since last update
this.updateWmsParams();
var url = this.getImageUrl();
if (this._currentUrl == url) {
return;
}
this._currentUrl = url;
// Keep current image overlay in place until new one loads
// (inspired by esri.leaflet)
var bounds = this._map.getBounds();
var overlay = L.imageOverlay(url, bounds, {'opacity': 0});
overlay.addTo(this._map);
overlay.once('load', _swap, this);
function _swap() {
if (!this._map) {
return;
}
if (overlay._url != this._currentUrl) {
this._map.removeLayer(overlay);
return;
} else if (this._currentOverlay) {
this._map.removeLayer(this._currentOverlay);
}
this._currentOverlay = overlay;
overlay.setOpacity(
this.options.opacity ? this.options.opacity : 1
);
if (this.options.isBack === true) {
overlay.bringToBack();
}
if (this.options.isBack === false) {
overlay.bringToFront();
}
}
if ((this._map.getZoom() < this.options.minZoom) ||
(this._map.getZoom() > this.options.maxZoom)){
this._map.removeLayer(overlay);
}
},
'setOpacity': function(opacity) {
this.options.opacity = opacity;
if (this._currentOverlay) {
this._currentOverlay.setOpacity(opacity);
}
},
'bringToBack': function() {
this.options.isBack = true;
if (this._currentOverlay) {
this._currentOverlay.bringToBack();
}
},
'bringToFront': function() {
this.options.isBack = false;
if (this._currentOverlay) {
this._currentOverlay.bringToFront();
}
},
// See L.TileLayer.WMS: onAdd() & getTileUrl()
'updateWmsParams': function(map) {
if (!map) {
map = this._map;
}
// Compute WMS options
var bounds = map.getBounds();
var size = map.getSize();
var wmsVersion = parseFloat(this.wmsParams.version);
var crs = this.options.crs || map.options.crs;
var projectionKey = wmsVersion >= 1.3 ? 'crs' : 'srs';
var nw = crs.project(bounds.getNorthWest());
var se = crs.project(bounds.getSouthEast());
// Assemble WMS parameter string
var params = {
'width': size.x,
'height': size.y
};
params[projectionKey] = crs.code;
params.bbox = (
wmsVersion >= 1.3 && crs === L.CRS.EPSG4326 ?
[se.y, nw.x, nw.y, se.x] :
[nw.x, se.y, se.x, nw.y]
).join(',');
L.extend(this.wmsParams, params);
},
'getImageUrl': function() {
var uppercase = this.options.uppercase || false;
var pstr = L.Util.getParamString(this.wmsParams, this._url, uppercase);
return this._url + pstr;
}
});
wms.overlay = function(url, options) {
return new wms.Overlay(url, options);
};
// Simple AJAX helper (since we can't assume jQuery etc. are present)
function ajax(url, callback) {
var context = this,
request = new XMLHttpRequest();
request.onreadystatechange = change;
request.open('GET', url);
request.send();
function change() {
if (request.readyState === 4) {
if (request.status === 200) {
callback.call(context, request.responseText);
} else {
callback.call(context, "error");
}
}
}
}
return wms;
}));