[Commits] r1999 - in core/trunk/geoext: examples lib lib/GeoExt/widgets tests tests/lib/GeoExt/widgets

commits at geoext.org commits at geoext.org
Wed Mar 17 17:02:00 CET 2010


Author: tschaub
Date: 2010-03-17 17:02:00 +0100 (Wed, 17 Mar 2010)
New Revision: 1999

Added:
   core/trunk/geoext/examples/vector-legend.html
   core/trunk/geoext/examples/vector-legend.js
   core/trunk/geoext/lib/GeoExt/widgets/VectorLegend.js
   core/trunk/geoext/tests/lib/GeoExt/widgets/VectorLegend.html
Modified:
   core/trunk/geoext/lib/GeoExt.js
   core/trunk/geoext/lib/GeoExt/widgets/LayerLegend.js
   core/trunk/geoext/tests/lib/GeoExt/widgets/LayerLegend.html
   core/trunk/geoext/tests/lib/GeoExt/widgets/LegendPanel.html
   core/trunk/geoext/tests/list-tests.html
Log:
Adding a legend component for vector layers.  This can be constructed with a vector layer or a list of rules and a symbolizer type.  To use a legend panel without entries for your vector layers, set layer.displayInLayerSwitcher to false.  r=elemoine closes #174

Added: core/trunk/geoext/examples/vector-legend.html
===================================================================
--- core/trunk/geoext/examples/vector-legend.html	                        (rev 0)
+++ core/trunk/geoext/examples/vector-legend.html	2010-03-17 16:02:00 UTC (rev 1999)
@@ -0,0 +1,42 @@
+<html>
+    <head>
+        <title>GeoExt Vector Legend</title>
+
+        <script type="text/javascript" src="http://extjs.cachefly.net/ext-2.2.1/adapter/ext/ext-base.js"></script>
+        <script type="text/javascript" src="http://extjs.cachefly.net/ext-2.2.1/ext-all.js"></script>
+        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/ext-all.css" />
+        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/examples/shared/examples.css" />
+        <script src="http://openlayers.org/api/2.8/OpenLayers.js"></script>
+        <script type="text/javascript" src="../lib/GeoExt.js"></script>
+
+        <script type="text/javascript" src="vector-legend.js"></script>
+        <style>
+            #wrapper {
+                position: relative;
+            }
+            #mappanel {
+                position: absolute;
+                top: 0px;
+                left: 0px;
+                width: 512px;
+                height: 256px;
+            }
+            #legend {
+                position: absolute;
+                top: 0px;
+                left: 550px;
+                height: 256px;
+                width: 150px;
+            }
+        </style>
+    </head>
+    <body>
+        <h1>GeoExt.LegendPanel with Vector Layers</h1>
+        <p>This example shows the how to create a legend for vector layers.  Zoom out to see how the legend is updated with changes in scale.<p>
+        <p>The js is not minified so it is readable. See <a href="vector-legend.js">vector-legend.js</a>.</p>
+        <div id="wrapper">
+            <div id="mappanel"></div>
+            <div id="legend"></div>
+        </div>
+    </body>
+</html>

Added: core/trunk/geoext/examples/vector-legend.js
===================================================================
--- core/trunk/geoext/examples/vector-legend.js	                        (rev 0)
+++ core/trunk/geoext/examples/vector-legend.js	2010-03-17 16:02:00 UTC (rev 1999)
@@ -0,0 +1,99 @@
+var mapPanel, legendPanel;
+
+Ext.onReady(function() {
+
+    var rules = [
+        new OpenLayers.Rule({
+            title: "> 2000m",
+            maxScaleDenominator: 3000000,
+            filter: new OpenLayers.Filter.Comparison({
+                type: OpenLayers.Filter.Comparison.GREATER_THAN,
+                property: "elevation",
+                value: 2000
+            }),
+            symbolizer: {
+                graphicName: "star",
+                pointRadius: 8,
+                fillColor: "#99ccff",
+                strokeColor: "#666666",
+                strokeWidth: 1
+            }
+        }),
+        new OpenLayers.Rule({
+            title: "1500 - 2000m",
+            maxScaleDenominator: 3000000,
+            filter: new OpenLayers.Filter.Comparison({
+                type: OpenLayers.Filter.Comparison.BETWEEN,
+                property: "elevation",
+                upperBoundary: 2000,
+                lowerBoundary: 1500
+            }),
+            symbolizer: {
+                graphicName: "star",
+                pointRadius: 6,
+                fillColor: "#6699cc",
+                strokeColor: "#666666",
+                strokeWidth: 1
+            }
+        }),
+        new OpenLayers.Rule({
+            title: "< 1500m",
+            maxScaleDenominator: 3000000,
+            filter: new OpenLayers.Filter.Comparison({
+                type: OpenLayers.Filter.Comparison.LESS_THAN,
+                property: "elevation",
+                value: 1500
+            }),
+            symbolizer: {
+                graphicName: "star",
+                pointRadius: 4,
+                fillColor: "#0033cc",
+                strokeColor: "#666666",
+                strokeWidth: 1
+            }
+        }),
+        new OpenLayers.Rule({
+            title: "All",
+            minScaleDenominator: 3000000,
+            symbolizer: {
+                graphicName: "star",
+                pointRadius: 5,
+                fillColor: "#99ccff",
+                strokeColor: "#666666",
+                strokeWidth: 1
+            }
+        })
+    ];
+
+    var imagery = new OpenLayers.Layer.WMS(
+        "Imagery",
+        "http://maps.opengeo.org/geowebcache/service/wms",
+        {layers: "bluemarble"},
+        {displayInLayerSwitcher: false}
+    );
+
+    var summits = new OpenLayers.Layer.Vector("Summits", {
+        strategies: [new OpenLayers.Strategy.Fixed()],
+        protocol: new OpenLayers.Protocol.HTTP({
+            url: "data/summits.json",
+            format: new OpenLayers.Format.GeoJSON()
+        }),
+        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({}, {rules: rules}))
+    });
+    
+    mapPanel = new GeoExt.MapPanel({
+        renderTo: "mappanel",
+        border: false,
+        layers: [imagery, summits],
+        center: [6.3, 45.6],
+        height: 256, // IE6 wants this
+        zoom: 8
+    });
+    
+    legendPanel = new GeoExt.LegendPanel({
+        map: mapPanel.map,
+        renderTo: "legend",
+        border: false
+    });
+
+});

Modified: core/trunk/geoext/lib/GeoExt/widgets/LayerLegend.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/widgets/LayerLegend.js	2010-03-17 15:13:09 UTC (rev 1998)
+++ core/trunk/geoext/lib/GeoExt/widgets/LayerLegend.js	2010-03-17 16:02:00 UTC (rev 1999)
@@ -32,6 +32,13 @@
      *  on the LayerStore record using the hideTitle property.
      */
     showTitle: true,
+    
+    /** api: config[legendTitle]
+     *  ``String``
+     *  Optional title to be displayed instead of the layer title.  If this is
+     *  set, the value of ``showTitle`` will be ignored (assumed to be true).
+     */
+    legendTitle: null,
 
     /** api: config[labelCls]
      *  ``String``
@@ -71,9 +78,13 @@
      *  name.
      */
     getLayerTitle: function(record) {
-        var title = "";
-        if (this.showTitle && !record.get("hideTitle")) {
-            title = record.get("title") || record.get("name") || record.get("layer").name || "";
+        var title = this.legendTitle || "";
+        if (this.showTitle && !title) {
+            if (record && !record.get("hideTitle")) {
+                title = record.get("title") || 
+                    record.get("name") || 
+                    record.get("layer").name || "";
+            }
         }
         return title;
     }

Added: core/trunk/geoext/lib/GeoExt/widgets/VectorLegend.js
===================================================================
--- core/trunk/geoext/lib/GeoExt/widgets/VectorLegend.js	                        (rev 0)
+++ core/trunk/geoext/lib/GeoExt/widgets/VectorLegend.js	2010-03-17 16:02:00 UTC (rev 1999)
@@ -0,0 +1,606 @@
+/**
+ * Copyright (c) 2008-2010 The Open Source Geospatial Foundation
+ * 
+ * Published under the BSD license.
+ * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text
+ * of the license.
+ */
+
+/**
+ * @requires GeoExt/widgets/LayerLegend.js
+ */
+
+/** api: (define)
+ *  module = GeoExt
+ *  class = VectorLegend
+ *  base_link = `Ext.Panel <http://extjs.com/deploy/dev/docs/?class=Ext.Panel>`_
+ */
+
+Ext.namespace('GeoExt');
+
+/** api: constructor
+ *  .. class:: VectorLegend(config)
+ *
+ *      Create a vector legend.
+ */
+GeoExt.VectorLegend = Ext.extend(GeoExt.LayerLegend, {
+
+    /** api: config[layerRecord]
+     *  :class:`GeoExt.data.LayerRecord`
+     *  The record containing a vector layer that this legend will be based on.  
+     *  One of ``layerRecord``, ``layer``,  or ``rules`` must be specified in 
+     *  the config.
+     */
+    layerRecord: null,
+    
+    /** api: config[layer]
+     *  ``OpenLayers.Layer.Vector``
+     *  The layer that this legend will be based on.  One of ``layer``, 
+     *  ``rules``, or ``layerRecord`` must be specified in the config.
+     */
+    layer: null,
+
+    /** api: config[rules]
+     * ``Array(OpenLayers.Rule)``
+     *  List of rules.  One of ``rules``, ``layer``, or ``layerRecord`` must be 
+     *  specified in the config.  The ``symbolType`` property must also be
+     *  provided if only ``rules`` are given in the config.
+     */
+    rules: null,
+    
+    /** api: config[symbolType]
+     *  ``String``
+     *  The symbol type for legend swatches.  Must be one of ``"Point"``, 
+     *  ``"Line"``, or ``"Polygon"``.  If not provided, the ``layer`` or
+     *  ``layerRecord`` config property must be specified, and the geometry type
+     *  of the first feature found on the layer will be used.
+     */
+    symbolType: null,
+
+    /** api: config[untitledPrefix]
+     *  ``String``
+     *  The prefix to use as a title for rules with no title or
+     *  name.  Default is ``"Untitled "``.  Prefix will be appended with a
+     *  number.
+     */
+    untitledPrefix: "Untitled ",
+    
+    /** api: config[clickableSymbol]
+     *  ``Boolean``
+     *  Set cursor style to "pointer" for symbolizers.  Register for
+     *  the ``symbolclick`` event to handle clicks.  Note that click events
+     *  are fired regardless of this value.  If ``false``, no cursor style will
+     *  be set.  Default is ``false``.
+     */
+    clickableSymbol: false,
+    
+    /** api: config[clickableTitle]
+     *  ``Boolean``
+     *  Set cursor style to "pointer" for rule titles.  Register for
+     *  the ``titleclick`` event to handle clicks.  Note that click events
+     *  are fired regardless of this value.  If ``false``, no cursor style will
+     *  be set.  Default is ``false``.
+     */
+    clickableTitle: false,
+    
+    /** api: config[selectOnClick]
+     *  ``Boolean``
+     *  Set to true if a rule should be selected by clicking on the
+     *  symbol or title. Selection will trigger the ruleselected event, and
+     *  a click on a selected rule will unselect it and trigger the
+     *  ``ruleunselected`` event. Default is ``false``.
+     */
+    selectOnClick: false,
+    
+    /** api: config[enableDD]
+     *  ``Boolean``
+     *  Allow drag and drop of rules. Default is ``false``.
+     */
+    enableDD: false,
+    
+    /** api: config[bodyBorder]
+     *  ``Boolean``
+     *  Show a border around the legend panel. Default is ``false``.
+     */
+    bodyBorder: false,
+
+    /** private: property[feature]
+     *  ``OpenLayers.Feature.Vector``
+     *  Cached feature for rendering.
+     */
+    feature: null,
+    
+    /** private: property[selectedRule]
+     *  ``OpenLayers.Rule``
+     *  The rule that is currently selected.
+     */
+    selectedRule: null,
+
+    /** private: property[currentScaleDenominator]
+     *  ``Number`` 
+     *  The current scale denominator of any map associated with this
+     *  legend.  Use :meth`setCurrentScaleDenominator` to change this.  If not
+     *  set an entry for each rule will be rendered.  If set, only rules that
+     *  apply for the given scale will be rendered.
+     */
+    currentScaleDenominator: null,
+    
+    /** private: property[untitledCount]
+     *  ``Number``
+     *  Last number used for untitled rule.
+     */
+    untitledCount: 0,
+    
+    /** private: method[initComponent]
+     *  Initializes the Vector legend.
+     */
+    initComponent: function() {
+        GeoExt.VectorLegend.superclass.initComponent.call(this);
+        if (this.layerRecord) {
+            this.layer = this.layerRecord.get("layer");
+            if (this.layer.map) {
+                this.currentScaleDenominator = this.layer.map.getScale();
+                this.layer.map.events.on({
+                    "zoomend": this.onMapZoom,
+                    scope: this
+                });
+            }
+        }
+        
+        // determine symbol type
+        if (!this.symbolType) {
+            if (this.feature) {
+                this.symbolType = this.symbolTypeFromFeature(this.feature);
+            } else if (this.layer) {
+                if (this.layer.features.length > 0) {
+                    var feature = this.layer.features[0].clone();
+                    feature.attributes = {};
+                    this.feature = feature;
+                    this.symbolType = this.symbolTypeFromFeature(this.feature);
+                } else {
+                    this.layer.events.on({
+                        featuresadded: this.onFeaturesAdded,
+                        scope: this
+                    });
+                }
+            }
+        }
+        
+        // set rules if not provided
+        if (this.layer && this.feature && !this.rules) {
+            this.setRules();
+        }
+
+        this.rulesContainer = new Ext.Container({
+            autoEl: {}
+        });
+        
+        this.add(this.rulesContainer);
+        
+        this.addEvents(
+            /** api: event[titleclick]
+             *  Fires when a rule title is clicked.
+             *
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule whose title was clicked.
+             */
+            "titleclick", 
+
+            /** api: event[symbolclick]
+             *  Fires when a rule symbolizer is clicked.
+             *
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule whose symbol was clicked.
+             */
+            "symbolclick",
+
+            /** api: event[ruleclick]
+             *  Fires when a rule entry is clicked (fired with symbolizer or
+             *  title click).
+             *
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule that was clicked.
+             */
+            "ruleclick",
+            
+            /** api: event[ruleselected]
+             *  Fires when a rule is clicked and ``selectOnClick`` is set to 
+             *  ``true``.
+             * 
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule that was selected.
+             */
+            "ruleselected",
+            
+            /** api: event[ruleunselected]
+             *  Fires when the selected rule is clicked and ``selectOnClick`` 
+             *  is set to ``true``, or when a rule is unselected by selecting a
+             *  different one.
+             * 
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule that was unselected.
+             */
+            "ruleunselected",
+            
+            /** api: event[rulemoved]
+             *  Fires when a rule is moved.
+             * 
+             *  Listener arguments:
+             *  * comp - :class:`GeoExt.VectorLegend`` This component.
+             *  * rule - ``OpenLayers.Rule`` The rule that was moved.
+             */
+            "rulemoved"
+        ); 
+        
+        this.update();
+    },
+    
+    /** private: method[onMapZoom]
+     *  Listener for map zoomend.
+     */
+    onMapZoom: function() {
+        this.setCurrentScaleDenominator(
+            this.layer.map.getScale()
+        );
+    },
+    
+    /** private: method[symbolTypeFromFeature]
+     *  :arg feature:  ``OpenLayers.Feature.Vector``
+     *
+     *  Determine the symbol type given a feature.
+     */
+    symbolTypeFromFeature: function(feature) {
+        var match = feature.geometry.CLASS_NAME.match(/Point|Line|Polygon/);
+        return (match && match[0]) || "Point";
+    },
+    
+    /** private: method[onFeaturesAdded]
+     *  Set as a one time listener for the ``featuresadded`` event on the layer
+     *  if it was provided with no features originally.
+     */
+    onFeaturesAdded: function() {
+        this.layer.events.un({
+            featuresadded: this.onFeaturesAdded,
+            scope: this
+        });
+        var feature = this.layer.features[0].clone();
+        feature.attributes = {};
+        this.feature = feature;
+        this.symbolType = this.symbolTypeFromFeature(this.feature);
+        if (!this.rules) {
+            this.setRules();
+        }
+        this.update();
+    },
+    
+    /** private: method[setRules]
+     *  Sets the ``rules`` property for this.  This is called when the component
+     *  is constructed without rules.  Rules will be derived from the layer's 
+     *  style map if it has one.
+     */
+    setRules: function() {
+        var style = this.layer.styleMap && this.layer.styleMap.styles["default"];
+        if (!style) {
+            style = new OpenLayers.Style();
+        }
+        if (style.rules.length === 0) {
+            this.rules = [
+                new OpenLayers.Rule({
+                    symbolizer: style.createSymbolizer(this.feature)
+                })
+            ];
+        } else {
+            this.rules = style.rules;                
+        }
+    },
+    
+    /** api: method[setCurrentScaleDenominator]
+     *  :arg scale: ``Number`` The scale denominator.
+     *
+     *  Set the current scale denominator.  This will hide entries for any
+     *  rules that don't apply at the current scale.
+     */
+    setCurrentScaleDenominator: function(scale) {
+        if (scale !== this.currentScaleDenominator) {
+            this.currentScaleDenominator = scale;
+            this.update();
+        }
+    },
+
+    /** private: method[getRuleEntry]
+     *  :arg rule: ``OpenLayers.Rule``
+     *  :returns: ``Ext.Container``
+     *
+     *  Get the item corresponding to the rule.
+     */
+    getRuleEntry: function(rule) {
+        return this.rulesContainer.items.get(this.rules.indexOf(rule));
+    },
+
+    /** private: method[addRuleEntry]
+     *  :arg rule: ``OpenLayers.Rule``
+     *  :arg noDoLayout: ``Boolean``  Don't call doLayout after adding rule.
+     *      Default is ``false``.
+     *
+     *  Add a new rule entry in the rules container. This
+     *  method does not add the rule to the rules array.
+     */
+    addRuleEntry: function(rule, noDoLayout) {
+        this.rulesContainer.add(this.createRuleEntry(rule));
+        if (!noDoLayout) {
+            this.doLayout();
+        }
+    },
+
+    /** private: method[removeRuleEntry]
+     *  :arg rule: ``OpenLayers.Rule``
+     *  :arg noDoLayout: ``Boolean``  Don't call doLayout after removing rule.
+     *      Default is ``false``.
+     *
+     *  Remove a rule entry from the rules container, this
+     *  method assumes the rule is in the rules array, and
+     *  it does not remove the rule from the rules array.
+     */
+    removeRuleEntry: function(rule, noDoLayout) {
+        var ruleEntry = this.getRuleEntry(rule);
+        if (ruleEntry) {
+            this.rulesContainer.remove(ruleEntry);
+            if (!noDoLayout) {
+                this.doLayout();
+            }
+        }
+    },
+    
+    /** private: method[selectRuleEntry]
+     */
+    selectRuleEntry: function(rule) {
+        var newSelection = rule != this.selectedRule;
+        if (this.selectedRule) {
+            this.unselect();
+        }
+        if (newSelection) {
+            var ruleEntry = this.getRuleEntry(rule);
+            ruleEntry.body.addClass("x-grid3-row-selected");
+            this.selectedRule = rule;
+            this.fireEvent("ruleselected", this, rule);
+        }
+    },
+    
+    /** private: method[unselect]
+     */
+    unselect: function() {
+        this.rulesContainer.items.each(function(item, i) {
+            if (this.rules[i] == this.selectedRule) {
+                item.body.removeClass("x-grid3-row-selected");
+                this.selectedRule = null;
+                this.fireEvent("ruleunselected", this, this.rules[i]);
+            }
+        }, this);
+    },
+
+    /** private: method[createRuleEntry]
+     */
+    createRuleEntry: function(rule) {
+        var applies = true;
+        if (this.currentScaleDenominator != null) {
+            if (rule.minScaleDenominator) {
+                applies = applies && (this.currentScaleDenominator >= rule.minScaleDenominator);
+            }
+            if (rule.maxScaleDenominator) {
+                applies = applies && (this.currentScaleDenominator < rule.maxScaleDenominator);
+            }
+        }
+        return {
+            xtype: "panel",
+            layout: "column",
+            border: false,
+            hidden: !applies,
+            bodyStyle: this.selectOnClick ? {cursor: "pointer"} : undefined,
+            defaults: {
+                border: false
+            },
+            items: [
+                this.createRuleRenderer(rule),
+                this.createRuleTitle(rule)
+            ],
+            listeners: {
+                render: function(comp){
+                    this.selectOnClick && comp.getEl().on({
+                        click: function(comp){
+                            this.selectRuleEntry(rule);
+                        },
+                        scope: this
+                    });
+                    if (this.enableDD == true) {
+                        this.addDD(comp);
+                    }
+                },
+                scope: this
+            }
+        };
+    },
+
+    /** private: method[createRuleRenderer]
+     *  :arg rule: ``OpenLayers.Rule``
+     *  :returns: ``GeoExt.FeatureRenderer``
+     *
+     *  Create a renderer for the rule.
+     */
+    createRuleRenderer: function(rule) {
+        var symbolizer = rule.symbolizer;
+        if (symbolizer[this.symbolType]) {
+            symbolizer = symbolizer[this.symbolType];
+        }
+        return {
+            xtype: "gx_renderer",
+            symbolType: this.symbolType,
+            symbolizers: [symbolizer],
+            style: this.clickableSymbol ? {cursor: "pointer"} : undefined,
+            listeners: {
+                click: function() {
+                    if (this.clickableSymbol) {
+                        this.fireEvent("symbolclick", this, rule);
+                        this.fireEvent("ruleclick", this, rule);
+                    }
+                },
+                scope: this
+            }
+        };
+    },
+
+    /** private: method[createRuleTitle]
+     *  :arg rule: ``OpenLayers.Rule``
+     *  :returns: ``Ext.Component``
+     *
+     *  Create a title component for the rule.
+     */
+    createRuleTitle: function(rule) {
+        return {
+            cls: "x-form-item",
+            style: "padding: 0.2em 0.5em 0;", // TODO: css
+            bodyStyle: Ext.applyIf({background: "transparent"}, 
+                this.clickableTitle ? {cursor: "pointer"} : undefined),
+            html: this.getRuleTitle(rule),
+            listeners: {
+                render: function(comp) {
+                    this.clickableTitle && comp.getEl().on({
+                        click: function() {
+                            this.fireEvent("titleclick", this, rule);
+                            this.fireEvent("ruleclick", this, rule);
+                        },
+                        scope: this
+                    });
+                },
+                scope: this
+            }
+        };
+    },
+    
+    /** private: method[addDD]
+     *  :arg component: ``Ext.Component``
+     *
+     *  Adds drag & drop functionality to a rule entry.
+     */
+    addDD: function(component) {
+        var cursor = component.body.getStyle("cursor");
+        var dd = new Ext.Panel.DD(component);
+        // restore previous curser (if set). because Panel.DD always
+        // sets a move cursor
+        component.body.setStyle("cursor", cursor || "move");
+        var panel = this;
+        var dropZone = new Ext.dd.DropTarget(component.getEl(), {
+            notifyDrop: function(ddSource) {
+                var source = Ext.getCmp(ddSource.getEl().id);
+                var target = Ext.getCmp(this.getEl().id);
+                // sometimes, for whatever reason, Ext forgets who the source
+                // was, so we make sure that we have one before moving on
+                if (source && target && source != target) {
+                    var sourceCt = source.ownerCt;
+                    var targetCt = target.ownerCt;
+                    // only move rules around inside the same container
+                    if (sourceCt == targetCt) {
+                        panel.moveRule(
+                            sourceCt.items.indexOf(source),
+                            targetCt.items.indexOf(target)
+                        );
+                    }
+                }
+            }
+        });
+    },
+    
+    /** api: method[update]
+     *  Update rule titles and symbolizers.
+     */
+    update: function() {
+        if (this.symbolType && this.rules) {
+            if (this.rulesContainer.items) {
+                var comp;
+                for (var i=this.rulesContainer.items.length-1; i>=0; --i) {
+                    comp = this.rulesContainer.getComponent(i);
+                    this.rulesContainer.remove(comp, true);
+                }
+            }
+            for (var i=0, ii=this.rules.length; i<ii; ++i) {
+                this.addRuleEntry(this.rules[i], true);
+            }
+            this.doLayout();
+        }
+    },
+
+    /** private: method[updateRuleEntry]
+     *  :arg rule: ``OpenLayers.Rule``
+     *
+     *  Update the renderer and the title of a rule.
+     */
+    updateRuleEntry: function(rule) {
+        var ruleEntry = this.getRuleEntry(rule);
+        if (ruleEntry) {
+            ruleEntry.removeAll();
+            ruleEntry.add(this.createRuleRenderer(rule));
+            ruleEntry.add(this.createRuleTitle(rule));
+            ruleEntry.doLayout();
+        }
+    },
+    
+    /** private: method[moveRule]
+     */
+    moveRule: function(sourcePos, targetPos) {
+        var srcRule = this.rules[sourcePos];
+        this.rules.splice(sourcePos, 1);
+        this.rules.splice(targetPos, 0, srcRule);
+        this.update();
+        this.fireEvent("rulemoved", this, srcRule);
+    },
+    
+    /** private: method[getRuleTitle]
+     *  :returns: ``String``
+     *
+     *  Get a rule title given a rule.
+     */
+    getRuleTitle: function(rule) {
+        return rule.title || rule.name || (this.untitledPrefix + (++this.untitledCount));
+    },
+
+    /** private: method[beforeDestroy]
+     *  Override.
+     */
+    beforeDestroy: function() {
+        if (this.layer) {
+            if (this.layer.events) {
+                this.layer.events.un({
+                    featuresadded: this.onFeaturesAdded,
+                    scope: this
+                });
+            }
+            if (this.layer.map && this.layer.map.events) {
+                this.layer.map.events.un({
+                    "zoomend": this.onMapZoom,
+                    scope: this
+                });
+            }
+        }
+        delete this.layer;
+        delete this.rules;
+        GeoExt.VectorLegend.superclass.beforeDestroy.apply(this, arguments);
+    }
+
+});
+
+/** private: method[supports]
+ *  Private override
+ */
+GeoExt.VectorLegend.supports = function(layerRecord) {
+    return layerRecord.get("layer") instanceof OpenLayers.Layer.Vector;
+};
+
+/** api: legendtype = gx_vectorlegend */
+GeoExt.LayerLegend.types["gx_vectorlegend"] = GeoExt.VectorLegend;
+
+/** api: xtype = gx_vectorlegend */
+Ext.reg("gx_vectorlegend", GeoExt.VectorLegend); 

Modified: core/trunk/geoext/lib/GeoExt.js
===================================================================
--- core/trunk/geoext/lib/GeoExt.js	2010-03-17 15:13:09 UTC (rev 1998)
+++ core/trunk/geoext/lib/GeoExt.js	2010-03-17 16:02:00 UTC (rev 1999)
@@ -105,6 +105,7 @@
             "GeoExt/widgets/LegendImage.js",
             "GeoExt/widgets/UrlLegend.js",
             "GeoExt/widgets/WMSLegend.js",
+            "GeoExt/widgets/VectorLegend.js",
             "GeoExt/widgets/LegendPanel.js",
             "GeoExt/widgets/ZoomSlider.js",
             "GeoExt/widgets/grid/FeatureSelectionModel.js",

Modified: core/trunk/geoext/tests/lib/GeoExt/widgets/LayerLegend.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/widgets/LayerLegend.html	2010-03-17 15:13:09 UTC (rev 1998)
+++ core/trunk/geoext/tests/lib/GeoExt/widgets/LayerLegend.html	2010-03-17 16:02:00 UTC (rev 1999)
@@ -36,7 +36,7 @@
         }
         
         function test_update(t) {
-            t.plan(2);
+            t.plan(3);
             
             var Rec = GeoExt.data.LayerRecord.create([{
                 name: "hideTitle", type: "boolean"
@@ -66,10 +66,22 @@
             t.eq(legend.items.get(0).text, "", "Layer text for record with hideTitle set to true still empty after update.")
             legend.destroy();
 
+            rec = new Rec({
+                layer: new OpenLayers.Layer("foo")
+            });
+            legend = new GeoExt.LayerLegend({
+                layerRecord: rec,
+                legendTitle: "bar",
+                renderTo: "legend"
+            });
+            legend.update();
+            t.eq(legend.items.get(0).text, "bar", "legendTitle can be used to override layer name")
+            legend.destroy();
+
         }
 
         function test_getTypes(t) {
-            t.plan(4);
+            t.plan(5);
             var Rec = GeoExt.data.LayerRecord.create([{
                 name: "legendURL"
             }]);
@@ -88,6 +100,12 @@
                 layer: new OpenLayers.Layer.WMS()
             });
             t.eq(GeoExt.LayerLegend.getTypes(rec), ["gx_wmslegend"], "a layer record with wms layer is supported by gx_wmslegend only.");
+            
+            rec = new Rec({
+                layer: new OpenLayers.Layer.Vector()
+            });
+            t.eq(GeoExt.LayerLegend.getTypes(rec), ["gx_vectorlegend"], "a layer record with vector layer is supported by gx_vectorlegend only.");
+            
         }
 
     </script>

Modified: core/trunk/geoext/tests/lib/GeoExt/widgets/LegendPanel.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/widgets/LegendPanel.html	2010-03-17 15:13:09 UTC (rev 1998)
+++ core/trunk/geoext/tests/lib/GeoExt/widgets/LegendPanel.html	2010-03-17 16:02:00 UTC (rev 1999)
@@ -49,12 +49,12 @@
 
             vectorLayer.setVisibility(false);
 
-            t.eq(lp.items.length, 1, "Currently there are no legends for non WMS layers");
+            t.eq(lp.items.length, 2, "legend for vector layer");
 
             var wms = new OpenLayers.Layer.WMS("testArray", '/ows', {layers: ['a', 'b', 'c']});
             mapPanel.map.addLayer(wms);
 
-            t.eq(lp.items.length, 2, "The legend panel can deal with WMS layers which have a LAYERS params which is an array");
+            t.eq(lp.items.length, 3, "The legend panel can deal with WMS layers which have a LAYERS params which is an array");
 
             lp.destroy();
             mapPanel.destroy();

Added: core/trunk/geoext/tests/lib/GeoExt/widgets/VectorLegend.html
===================================================================
--- core/trunk/geoext/tests/lib/GeoExt/widgets/VectorLegend.html	                        (rev 0)
+++ core/trunk/geoext/tests/lib/GeoExt/widgets/VectorLegend.html	2010-03-17 16:02:00 UTC (rev 1999)
@@ -0,0 +1,89 @@
+<html><head>
+    <script src="../../../../../openlayers/lib/OpenLayers.js"></script>
+    <script src="../../../../../ext/adapter/ext/ext-base.js"></script>
+    <script src="../../../../../ext/ext-all.js"></script>
+    <script src="../../../../lib/GeoExt.js"></script>
+
+    <script>
+
+    function test_constructor(t) {
+        t.plan(5);
+        
+        var legend;
+
+        // no config (won't render)
+        legend = new GeoExt.VectorLegend();
+        t.ok(legend instanceof GeoExt.VectorLegend, "instance of VectorLegend");
+        t.ok(legend instanceof GeoExt.LayerLegend, "instance of LayerLegend");
+        legend.destroy();
+        
+        // rules & symbolType
+        legend = new GeoExt.VectorLegend({
+            legendTitle: "my title",
+            renderTo: "legendpanel",
+            rules: [
+                new OpenLayers.Rule({title: "first rule"}),
+                new OpenLayers.Rule({title: "second rule"})
+            ],
+            symbolType: "Point"
+        });
+        
+        // check title
+        var titleCmp = legend.getComponent(0);
+        t.eq(titleCmp.getEl().dom.innerHTML, "my title", "correct legend title");
+        
+        // check rule components    
+        t.eq(legend.rulesContainer.items.length, 2, "two items in the rules container");
+        var ruleCmp = legend.rulesContainer.getComponent(0);
+        var ruleTitleCmp = ruleCmp.getComponent(1);
+        // this should eventually become a container instead of a panel
+        t.ok(ruleTitleCmp.getEl().dom.innerHTML.indexOf("first rule") > -1, "correct title for first rule");
+
+        legend.destroy();
+    }
+    
+    function test_setCurrentScaleDenominator(t) {
+        t.plan(12);
+        
+        var legend = new GeoExt.VectorLegend({
+            legendTitle: "my title",
+            renderTo: "legendpanel",
+            rules: [
+                new OpenLayers.Rule({title: "first rule", maxScaleDenominator: 100}),
+                new OpenLayers.Rule({title: "second rule", minScaleDenominator: 100})
+            ],
+            symbolType: "Point"
+        });
+        
+        // all rules shown if no scale denominator is set
+        t.eq(legend.rulesContainer.items.length, 2, "two items in the rules container");
+        t.eq(legend.rulesContainer.getComponent(0).hidden, false, "first rule shown");
+        t.eq(legend.rulesContainer.getComponent(1).hidden, false, "second rule shown");
+        
+        // show first rule at 1:50
+        legend.setCurrentScaleDenominator(50)
+        t.eq(legend.rulesContainer.items.length, 2, "two items in the rules container at 1:50");
+        t.eq(legend.rulesContainer.getComponent(0).hidden, false, "first rule shown at 1:50");
+        t.eq(legend.rulesContainer.getComponent(1).hidden, true, "second rule hidden at 1:50");
+
+        // show second rule at 1:150
+        legend.setCurrentScaleDenominator(150)
+        t.eq(legend.rulesContainer.items.length, 2, "two items in the rules container at 1:150");
+        t.eq(legend.rulesContainer.getComponent(0).hidden, true, "first rule hidden at 1:150");
+        t.eq(legend.rulesContainer.getComponent(1).hidden, false, "second rule shown at 1:150");
+        
+        // show second rule at 1:100 (min is inclusive)
+        legend.setCurrentScaleDenominator(100)
+        t.eq(legend.rulesContainer.items.length, 2, "two items in the rules container at 1:100");
+        t.eq(legend.rulesContainer.getComponent(0).hidden, true, "first rule hidden at 1:100");
+        t.eq(legend.rulesContainer.getComponent(1).hidden, false, "second rule shown at 1:100");
+        
+        legend.destroy();
+        
+    }
+    
+
+    </script>
+</head><body>
+    <div id="legendpanel"></div>
+</body></html>

Modified: core/trunk/geoext/tests/list-tests.html
===================================================================
--- core/trunk/geoext/tests/list-tests.html	2010-03-17 15:13:09 UTC (rev 1998)
+++ core/trunk/geoext/tests/list-tests.html	2010-03-17 16:02:00 UTC (rev 1999)
@@ -39,6 +39,7 @@
   <li>lib/GeoExt/widgets/LegendPanel.html</li>
   <li>lib/GeoExt/widgets/LayerLegend.html</li>
   <li>lib/GeoExt/widgets/UrlLegend.html</li>
+  <li>lib/GeoExt/widgets/VectorLegend.html</li>
   <li>lib/GeoExt/widgets/WMSLegend.html</li>
   <li>lib/GeoExt/widgets/ZoomSlider.html</li>
   <li>lib/GeoExt/widgets/grid/FeatureSelectionModel.html</li>



More information about the Commits mailing list