Ticket #2: LegendPanel.js

File LegendPanel.js, 11.2 kB (added by bartvde, 2 years ago)

LegendPanel widget (not in patch form yet)

Line 
1 /*
2  * Copyright (C) 2008 Camptocamp, OpenGeo
3  *
4  * This file is part of GeoExt
5  *
6  * GeoExt is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GeoExt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GeoExt.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 Ext.namespace('GeoExt', 'GeoExt.widgets');
21
22 /**
23  * Class: GeoExt.widgets.LegendPanel
24  *      LegendPanel is an Ext.Panel that displays a legend for all layers in a
25  *      map. Currently the following layers are supported:
26  *      - {OpenLayers.Layer.WMS}
27  *      In the future other layer types can be added.
28  *
29  * Inherits from:
30  *  - {Ext.Panel}
31  */
32
33 /**
34  * Constructor: GeoExt.widgets.LegendPanel
35  * Create an instance of GeoExt.widgets.LegendPanel
36  *
37  * Parameters:
38  * config - {Object} A config object used to set the legend
39  *     panel's properties.
40  *
41  * Returns:
42  * {<GeoExt.widgets.LegendPanel>}
43  */
44 GeoExt.widgets.LegendPanel = function(config){
45     Ext.apply(this, config);
46     GeoExt.widgets.LegendPanel.superclass.constructor.call(this);
47 }
48
49 GeoExt.widgets.LegendPanel.LEGENDURL = 1;
50 GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC = 0;
51
52 Ext.extend(GeoExt.widgets.LegendPanel, Ext.Panel, {
53
54     /**
55      * APIProperty: wmsMode
56      * {Integer} should the legend component use SLD WMS GetLegendGraphic
57      *     requests or LegendURLs from the GetCapabilities reponse?
58      *     One of: GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC or
59      *     GeoExt.widgets.LegendPanel.LEGENDURL. The LEGENDURL option requires
60      *     the OpenLayers.Format.WMSCapabilities parser. GETLEGENDGRAPHIC is
61      *     the default.
62      */
63     wmsMode: GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC,
64
65     /**
66      * APIProperty: map
67      * {OpenLayers.Map}
68      */
69     map: null,
70
71     /**
72      * APIProperty: labelCls
73      * {String} Additional css class to put on the legend labels
74      */
75     labelCls: null,
76
77     /**
78      * APIProperty: wmsLegendFormat
79      * {String} the format to use in the GetLegendGraphic requests, defaults
80      *     to image/png. The value should be a valid mime-type.
81      */
82     wmsLegendFormat: "image/png",
83
84     /**
85      * APIProperty: ascending
86      * {Boolean} if true the layers in the tree will show the
87      *     bottom OpenLayer layer on top and the top OpenLayer layer on the
88      *     bottom. If false, this is inverted.
89      */
90     ascending: true,
91
92     /**
93      * APIProperty: static
94      * {Boolean} if true the LegendPanel will not listen to the addlayer,
95      *     changelayer and removelayer events. So it will load with the initial
96      *     state of the map object and not change anymore.
97      */
98     static: false,
99
100     /**
101      * Property: idPrefix
102      * {String} the prefix to use for the id attribute value of the sub panels
103      *    (gxt for GeoExt and lp for LegendPanel)
104     */
105     idPrefix: 'gxt-lp-',
106
107     /**
108      * Method: initComponent
109      *      Initialize this component. We register for the events here.
110      */
111     initComponent: function() {
112         if (!this.static) {
113             this.map.events.register("addlayer", this, this.addLayer);
114             this.map.events.register("changelayer", this, this.changeLayer);
115             this.map.events.register("removelayer", this, this.removeLayer);
116         }
117         GeoExt.widgets.LegendPanel.superclass.initComponent.call(this);
118     },
119
120     /**
121      * Method: onRender
122      *      This function is called when the component renders.
123      */
124     onRender: function(ct, position) {
125         GeoExt.widgets.LegendPanel.superclass.onRender.call(this, ct, position);
126         var layers = this.map.layers.slice();
127         if (!this.ascending) {
128             layers.reverse();
129         }
130         for (var i=0, len=layers.length; i<len; i++) {
131             var layer = layers[i];
132             this.createLegend(layer);
133         }
134     },
135
136     /**
137      * Method: onDestroy
138      *      This function is called when the component destroys. We deregister
139      *      the events here.
140      */
141     onDestroy: function() {
142         if (!this.static) {
143             this.map.events.unregister("addlayer", this, this.addLayer);
144             this.map.events.unregister("changelayer", this, this.changeLayer);
145             this.map.events.unregister("removelayer", this, this.removeLayer);
146         }
147         GeoExt.widgets.LegendPanel.superclass.onDestroy.call(this);
148     },
149
150     /**
151      * Method: addLayer
152      *      Internal function called on the addlayer event
153      *
154      * Parameters:
155      * evt - {Object} The event object sent by OpenLayers
156      */
157     addLayer: function(evt) {
158         this.createLegend(evt.layer);
159     },
160
161     /**
162      * Method: removeLayer
163      *      Internal function called on the removelayer event
164      *
165      * Parameters:
166      * evt - {Object} The event object sent by OpenLayers
167      */
168     removeLayer: function(evt) {
169         if (evt.layer && evt.layer instanceof OpenLayers.Layer.WMS) {
170             this.remove(Ext.getCmp(this.generatePanelId(evt.layer)));
171         }
172     },
173
174     /**
175      * Method: changeLayer
176      *      Internal function called on the changelayer event
177      *
178      * Parameters:
179      * evt - {Object} The event object sent by OpenLayers
180      */
181     changeLayer: function(evt) {
182         // TODO deal with property order if we want to reflect the order
183         // in the legend
184         if (evt && evt.layer && evt.property) {
185             var panel = Ext.getCmp(this.generatePanelId(evt.layer));
186             if (!panel) {
187                 panel = this.createLegend(evt.layer);
188             }
189             if (evt.property == 'visibility') {
190                 if (panel) {
191                     panel.setVisible(evt.layer.getVisibility());
192                 }
193             }
194             // it is possible for an application to hide layers from the legend
195             // by setting the hideInLegend property on the layer
196             // when the hideInLegend property changes, the application is
197             // responsible for triggering a changelayer event with a property
198             // named legendvisibility.
199             else if (evt.property == 'legendvisibility') {
200                 if (panel) {
201                     panel.setVisible(!evt.layer.hideInLegend);
202                 }
203             }
204         }
205     },
206
207     /**
208      * Method: generatePanelId
209      *     Generate an id attribute value for the panel.
210      *     It is assumed that the combination of layer.params.LAYER and
211      *     layer.mame is unique.
212      *
213      * Parameters:
214      * layer - {<OpenLayers.Layer.WMS>} the layer object
215      *
216      * Returns:
217      * {String}
218      */
219     generatePanelId: function(layer) {
220         return this.idPrefix + layer.params.LAYERS + layer.name;
221     },
222
223     /**
224      * Method: onImageLoadError
225      *     When the image fails loading (e.g. when the server returns an XML
226      *     exception) we need to set the src to a blank image otherwise IE
227      *     will show the infamous red cross.
228      */
229     onImageLoadError: function() {
230         this.src = Ext.BLANK_IMAGE_URL;
231     },
232
233     /**
234      * Method: createLegendPanel
235      *     Create a panel for every layer, it will contain a Label with the
236      *     layer's name and for every possible sub layer an image
237      *
238      * Parameters:
239      * id - {String} the unique id to use for the panel
240      * title - {String} the title of the layer
241      * legImg - {Object} the legend image object
242      *
243      * Returns:
244      * {<Ext.Panel>}
245      */
246     createLegendPanel: function(id, title, legImg) {
247         // TODO: we probably need the ability to change the css class
248         // of the label
249         var panel = new Ext.Panel({
250             id: id,
251             bodyStyle: this.bodyStyle,
252             items: [
253                 new Ext.form.Label({
254                     text: title,
255                     cls: 'x-form-item x-form-item-label' +
256                         (this.labelCls ? ' ' + this.labelCls : '')
257                 })
258             ]
259         });
260         for (var i=0, len=legImg.length; i<len; i++) {
261             panel.add(new Ext.BoxComponent({el: legImg[i]}));
262         }
263         return panel;
264     },
265
266     /**
267      * Method: getLegendUrl
268      *     Get the URL from which the legend image can be retrieved
269      *
270      * Parameters:
271      * layer - {<OpenLayers.Layer.WMS>} the WMS layer
272      * layerName - {String} one of the layers from the LAYERS parameter
273      *
274      * Returns:
275      * {String}
276      */
277     getLegendUrl: function(layer, layerName) {
278         if (this.wmsMode == GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC) {
279            return layer.getFullRequestString({
280                 REQUEST: "GetLegendGraphic",
281                 WIDTH: null,
282                 HEIGHT: null,
283                 EXCEPTIONS: "application/vnd.ogc.se_xml",
284                 LAYER: layerName,
285                 LAYERS: null,
286                 SRS: null,
287                 FORMAT: this.wmsLegendFormat
288             });
289         }
290     },
291
292     /**
293      * Method: createImage
294      *     Create an image object for the legend image
295      *
296      * Parameters:
297      * src - {String} the source of the image (url)
298      * id - {String} the id (prefix) for the image object
299      *
300      * Returns:
301      * {DOMElement}
302      */
303     createImage: function(src, id) {
304         var legendImage = document.createElement("img");
305         Ext.EventManager.addListener(legendImage, 'error',
306             this.onImageLoadError, legendImage);
307         legendImage.src = src;
308         legendImage.id = id+'_img';
309         return legendImage;
310     },
311
312     /**
313      * Method: createLegend
314      *     Create the legend panel for a layer
315      *
316      * Parameters:
317      * layer - {<OpenLayers.Layer>} the layer object
318      */
319     createLegend: function(layer) {
320         // currently only OpenLayers.Layer.WMS is supported and
321         // only for visible layers a legend is created.
322         // If an application does not want a layer in the legend
323         // they can set hideInLegend to true on the layer.
324         var panel;
325         if (layer instanceof OpenLayers.Layer.WMS &&
326             layer.getVisibility() && !layer.hideInLegend) {
327                 // if LAYERS param is in the form of LAYERS=A,B,C we need to
328                 //split them up, and show an image per layer
329                 var layers = layer.params.LAYERS.split(",");
330                 var legImg = [];
331                 for (var i=0, len=layers.length; i<len; i++) {
332                     var layerName = layers[i];
333                     legImg.push(this.createImage(
334                         this.getLegendUrl(layer, layerName),
335                         this.generatePanelId(layer)+i));
336                 }
337                 panel = this.createLegendPanel(this.generatePanelId(layer),
338                     layer.name, legImg);
339                 if (this.ascending) {
340                     this.add(panel);
341                 } else {
342                     var idx = (this.map.layers.length-1)-this.map.getLayerIndex(layer);
343                     this.insert(idx, panel);
344                 }
345                 this.doLayout();
346         }
347         return panel;
348     }
349
350 });
351
352 Ext.reg('gx_legend', GeoExt.widgets.LegendPanel);