diff --git a/README.md b/README.md index 6ac6c16..85d2f9d 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,14 @@ tiles.addTo(map); This class provides a "single-tile"/untiled/non-tiled WMS layer. Every time the map is panned or zoomed, a new WMS image will be requested for the entire display. To make transitions smoother, the existing image will be kept visible until the new one is loaded. An internal [L.ImageOverlay] instance is created for this purpose. (This technique was inspired by the [esri-leaflet] plugin.) -The API is nearly identical to `L.WMS.TileLayer`, except that the tile options do not apply. +#### Events +**`loading`** - fired before requesting external service for map layer content; + +**`load`** - fired after data from external service has been received; + +**`error`** - forwarded from internal `L.ImageOverlay` instance. + +The API is nearly identical to `L.WMS.TileLayer`, except that the tile options do not apply. ```javascript var overlay = L.WMS.overlay("http://example.com/mapserv", { @@ -102,9 +109,30 @@ overlay.addTo(map); ### L.WMS.Source -`L.WMS.Source` is a virtual Leaflet "layer" that manages multiple WMS layers coming from a single WMS source. By using the same source for multiple layers, you can have the WMS service composite the image, and avoid overloading the client with multiple large images. `L.WMS.Source` is a virtual layer, as it does not load the WMS image directly. Instead, it creates an internal `L.WMS.Overlay` or `L.WMS.TileLayer` to handle the actual loading. +`L.WMS.Source` is a virtual Leaflet "layer" that manages multiple WMS layers coming from a single WMS source. +By using the same source for multiple layers, you can have the WMS service composite the image, +and avoid overloading the client with multiple large images. +`L.WMS.Source` is a virtual layer, as it does not load the WMS image directly. +Instead, it creates an internal `L.WMS.Overlay` or `L.WMS.TileLayer` to handle the actual loading. + +Like the other WMS layers, `L.WMS.Source` takes a URL and an options object as initialization parameters. +The options are passed on to the underlying `Overlay` or `TileLayer`. +An additional option, `untiled`, toggles whether to use `Overlay` or `TileLayer`. +The default is `true`, which uses the non-tiled `Overlay`. +Unless your WMS service is optimized for tiling, the default should provide the best performance. +To use the `TileLayer`, set `untiled` to `false`. +You can also set `tiled` to `true`, which will both use the `TileLayer` backend and +set `tiled=true` in the WMS request (see [#16](https://github.com/heigeo/leaflet.wms/issues/16). + +#### Events +**`loading`** - fired before requesting external service for map layer content; + +**`load`** - fired after data from external service has been received; + +**`error`** - forwarded from internal `L.ImageOverlay` instance. -Like the other WMS layers, `L.WMS.Source` takes a URL and an options object as initialization parameters. The options are passed on to the underlying `Overlay` or `TileLayer`. An additional option, `untiled`, toggles whether to use `Overlay` or `TileLayer`. The default is `true`, which uses the non-tiled `Overlay`. Unless your WMS service is optimized for tiling, the default should provide the best performance. To use the `TileLayer`, set `untiled` to `false`. You can also set `tiled` to `true`, which will both use the `TileLayer` backend and set `tiled=true` in the WMS request (see [#16](https://github.com/heigeo/leaflet.wms/issues/16). +**NB:** `loading` and `load` events are related to layer visual content loading, +_not_ to information queries like `showWaiting()` and `hideWaiting()` hooks described below. `L.WMS.Source` provides two functions for toggling on and off individual WMS layers (`addSubLayer` and `removeSubLayer`, respectively). That said, it is usually more convenient to use `L.WMS.Layer` instances (described next). @@ -165,7 +193,7 @@ var MySource = L.WMS.Source.extend({ }); ``` -The following hooks are available: +The following `WMS.Source` hooks are available: Name | Description -----|------------- @@ -174,8 +202,8 @@ Name | Description `ajax(url, callback)` | Actual AJAX call. The default implementation is a rudimentary `XMLHttpRequest` wrapper. Override this if you want to use jQuery or something with more robust support for older browsers. If you override this, be sure to preserve the value of `this` when calling the callback function (e.g. `callback.call(this, result)`). `parseFeatureInfo(result, url)` | Parse the AJAX response into HTML `showFeatureInfo(latlng, info)` | Display parsed AJAX response to the user (e.g in a popup) -`showWaiting()` | Start AJAX wait animation (spinner, etc.) -`hideWaiting()` | Stop AJAX wait animation +`showWaiting()` | Start AJAX wait animation (spinner, etc.) during layer info request +`hideWaiting()` | Stop AJAX wait animation after layer info request is completed [Leaflet]: http://leafletjs.com [esri-leaflet]: https://github.com/Esri/esri-leaflet diff --git a/dist/leaflet.wms.js b/dist/leaflet.wms.js index 83c680a..4d6ee17 100644 --- a/dist/leaflet.wms.js +++ b/dist/leaflet.wms.js @@ -1 +1 @@ -(function(factory){if(typeof define==="function"&&define.amd){define(["leaflet"],factory)}else if(typeof module!=="undefined"){module.exports=factory(require("leaflet"))}else{if(typeof this.L==="undefined")throw"Leaflet must be loaded first!";this.L.WMS=this.L.wms=factory(this.L)}})(function(L){var wms={};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=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){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){var layers=this.getIdentifyLayers();if(!layers.length){return}this.getFeatureInfo(evt.containerPoint,evt.latlng,layers,this.showFeatureInfo)},getFeatureInfo:function(point,latlng,layers,callback){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(){if(this.options.identifyLayers)return this.options.identifyLayers;return Object.keys(this._subLayers)},getFeatureInfoParams:function(point,layers){var wmsParams,overlay;if(this.options.untiled){wmsParams=this._overlay.wmsParams}else{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){if(result=="error"){result=" - +

Overlay ("Single-Tile"/Untiled Mode)

Click the map to get information about the underlying data.

+ LOADING...

Tiled Layer

@@ -36,6 +37,7 @@

Tiled Layer

+ LOADING...
diff --git a/src/leaflet.wms.js b/src/leaflet.wms.js index d676c0d..2e8a871 100644 --- a/src/leaflet.wms.js +++ b/src/leaflet.wms.js @@ -58,7 +58,28 @@ wms.Source = L.Layer.extend({ } this._url = url; this._subLayers = {}; - this._overlay = this.createOverlay(this.options.untiled); + var overlay = this._overlay = this.createOverlay(this.options.untiled); + + // TileLayer may emit multiple load events, we need to pass only one. + var counter = 0; + overlay.on('error', L.bind(this.fire, this, 'error')); + overlay.on('loading', L.bind(this.fire, this, 'loading')); + overlay.on('load', _onLoad, this); + overlay.on('tileloadstart', _count.bind(this, 1)); + overlay.on('tileload', _count.bind(this, -1)); + overlay.on('tileerror', _count.bind(this, -1)); + + function _count(delta) { + if ((counter += delta) <0) { + counter = 0; + } + } + + function _onLoad(ev) { + if (counter === 0) { + this.fire('load', ev) + } + } }, 'createOverlay': function(untiled) { @@ -94,7 +115,7 @@ wms.Source = L.Layer.extend({ this._overlay.setOpacity(opacity); } }, - + 'bringToBack': function() { this.options.isBack = true; if (this._overlay) { @@ -381,8 +402,12 @@ wms.Overlay = L.Layer.extend({ var bounds = this._map.getBounds(); var overlay = L.imageOverlay(url, bounds, {'opacity': 0}); overlay.addTo(this._map); + // + this.fire('loading'); + overlay.on('error', L.bind(this.fire, this, 'error')); overlay.once('load', _swap, this); function _swap() { + this.fire('load'); if (!this._map) { return; }