[76] | 1 | ============================ |
---|
| 2 | Layer Tree Tutorial |
---|
| 3 | ============================ |
---|
| 4 | |
---|
| 5 | Often when presenting users with an interactive map, it is useful to allow them |
---|
| 6 | to control the visible layers. In this tutorial, we examine the use of |
---|
| 7 | :class:`GeoExt.tree.LayerContainer` with the stock ``Ext.tree.TreePanel`` class |
---|
| 8 | to accommodate toggling visibility of layers and rearranging their drawing |
---|
| 9 | order. |
---|
| 10 | |
---|
| 11 | .. note:: Before starting this tutorial, you should have a working |
---|
| 12 | :class:`GeoExt.MapPanel` in your page. The :doc:`mappanel-tutorial` will |
---|
| 13 | help you set one up if you don't already have one. |
---|
| 14 | |
---|
| 15 | Start With a Map |
---|
| 16 | ================ |
---|
| 17 | |
---|
| 18 | Let's assume you already have a :class:`GeoExt.MapPanel` on your page with some |
---|
| 19 | layers. In the :doc:`mappanel-tutorial`\ , we discussed how you can use the |
---|
| 20 | ``layers`` property of the ``MapPanel`` to add, remove, and modify the layers of |
---|
| 21 | the map as well as monitor the layer list for changes. This is more than |
---|
| 22 | sufficient to display a 'live' list of layers in an ``Ext.grid.GridPanel``\ . |
---|
| 23 | The :class:`GeoExt.tree.LayerContainer` is another component that can listen to |
---|
| 24 | changes to the map's layer list. However, rather than an independent panel, the |
---|
| 25 | ``LayerContainer`` is a node that must be contained in an ``Ext.tree.TreePanel`` |
---|
| 26 | to be displayed. Here's an example rendering a layer tree to a ``div``: |
---|
| 27 | |
---|
| 28 | .. code-block:: javascript |
---|
| 29 | |
---|
| 30 | var mapPanel = new GeoExt.MapPanel({ |
---|
| 31 | /* Your configuration here */ |
---|
| 32 | }); |
---|
| 33 | |
---|
| 34 | var layerList = new GeoExt.tree.LayerContainer({ |
---|
| 35 | text: 'All Layers', |
---|
| 36 | layerStore: mapPanel.layers, |
---|
| 37 | leaf: false, |
---|
| 38 | expanded: true |
---|
| 39 | }); |
---|
| 40 | |
---|
| 41 | var layerTree = new Ext.tree.TreePanel({ |
---|
| 42 | title: 'Map Layers', |
---|
| 43 | renderTo: 'layerTree', |
---|
| 44 | root: layerList |
---|
| 45 | }); |
---|
| 46 | |
---|
| 47 | ``LayerContainer``\ s automatically add checkboxes (radio buttons for base |
---|
| 48 | layers) that can be used to toggle the visibility of layers. You can also enable |
---|
| 49 | drag-n-drop layer reordering by simply setting the ``enableDD`` property of the |
---|
| 50 | ``TreePanel``. |
---|
| 51 | |
---|
| 52 | Filtering |
---|
| 53 | ========= |
---|
| 54 | By default, the ``LayerContainer``'s ``LayerLoader`` automatically pulls in all layers from the store and displays those with the ``displayInLayerSwitcher`` |
---|
| 55 | property set to true. You can provide your own filter function to the loader: |
---|
| 56 | |
---|
| 57 | .. code-block:: javascript |
---|
| 58 | |
---|
| 59 | var layerList = new GeoExt.tree.LayerContainer({ |
---|
| 60 | text: 'Tasmania Layers', |
---|
| 61 | layerStore: mapPanel.layers, |
---|
| 62 | leaf: false, |
---|
| 63 | expanded: true, |
---|
| 64 | loader: { |
---|
| 65 | filter: function(record) { |
---|
| 66 | return record.get("layer").name.indexOf("Tasmania") !== -1 |
---|
| 67 | } |
---|
| 68 | } |
---|
| 69 | }); |
---|
| 70 | |
---|
| 71 | The above will only load layers with "Tasmania" in their name. By adding |
---|
| 72 | multiple named and filtered ``LayerContainer``\ s to a ``TreePanel`` you are |
---|
| 73 | able to provide logical organization to your layer trees. When ``enableDD`` is |
---|
| 74 | set to true on the tree, drag-n-drop will also work between filtered layer |
---|
| 75 | containers, as long as they have the same parent node. You can also directly |
---|
| 76 | instantiate :class:`GeoExt.tree.LayerNode` to create tree nodes that can be |
---|
| 77 | added anywhere in a tree. Keep in mind, however, that this approach does not |
---|
| 78 | allow for automatic drag-n-drop support. |
---|
| 79 | |
---|
| 80 | .. note:: |
---|
| 81 | |
---|
| 82 | There are two LayerContainer types with a preconfigured filter: |
---|
| 83 | |
---|
| 84 | * :class:`GeoExt.tree.BaseLayerContainer` will be populated only with layers |
---|
| 85 | that have isBaseLayer set to true, |
---|
| 86 | * :class:`GeoExt.tree.OverlayLayerContainer` will be populated only with |
---|
| 87 | layers that have isBaseLayer set to false. |
---|
| 88 | |
---|
| 89 | Visibility Grouping |
---|
| 90 | =================== |
---|
| 91 | |
---|
| 92 | The concept of a base layer in OpenLayers is just a gruop of layers that are on |
---|
| 93 | the bottom of the layer stack, and only one can be visible at a time. In maps |
---|
| 94 | without base layers (when ``allOverlays`` is set to true, the latter can be |
---|
| 95 | enforced by configuring a ``checkedGroup`` on a LayerNode. Such a layer node |
---|
| 96 | will be rendered with a radio button instead of a check box. Of all layers |
---|
| 97 | configured with the same ``checkedGroup``, only one will be visible at a time: |
---|
| 98 | |
---|
| 99 | .. code-block:: javascript |
---|
| 100 | |
---|
| 101 | var layerList = new GeoExt.tree.LayerContainer({ |
---|
| 102 | text: 'Tasmania Layers', |
---|
| 103 | layerStore: mapPanel.layers, |
---|
| 104 | leaf: false, |
---|
| 105 | expanded: true, |
---|
| 106 | loader: { |
---|
| 107 | filter: function(record) { |
---|
| 108 | return record.get("layer").name.indexOf("Tasmania") !== -1 |
---|
| 109 | }, |
---|
| 110 | baseAttrs: { |
---|
| 111 | checkedGroup: "tasmania" |
---|
| 112 | } |
---|
| 113 | } |
---|
| 114 | }); |
---|
| 115 | |
---|
| 116 | Layer Nodes with Additional Radio Buttons |
---|
| 117 | ========================================= |
---|
| 118 | |
---|
| 119 | It is possible to render layer nodes with an additional radio button. This can |
---|
| 120 | be useful if an application uses the concept of an "active layer". The active |
---|
| 121 | layer can then be set by clicking its radio button: |
---|
| 122 | |
---|
| 123 | .. code-block:: javascript |
---|
| 124 | |
---|
| 125 | var layerList = new GeoExt.tree.LayerContainer({ |
---|
| 126 | text: 'All Layers', |
---|
| 127 | layerStore: mapPanel.layers, |
---|
| 128 | leaf: false, |
---|
| 129 | expanded: true, |
---|
| 130 | loader: { |
---|
| 131 | baseAttrs: { |
---|
| 132 | radioGroup: "active" |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | }); |
---|
| 136 | var registerRadio = function(node) |
---|
| 137 | if(!node.hasListener("radiochange")) { |
---|
| 138 | node.on("radiochange", function(node){ |
---|
| 139 | /* set your active layer here */ |
---|
| 140 | }); |
---|
| 141 | } |
---|
| 142 | } |
---|
| 143 | var layerTree = new Ext.tree.TreePanel({ |
---|
| 144 | title: 'Map Layers', |
---|
| 145 | renderTo: 'layerTree', |
---|
| 146 | root: layerList, |
---|
| 147 | listeners: { |
---|
| 148 | append: registerRadio, |
---|
| 149 | insert: registerRadio |
---|
| 150 | } |
---|
| 151 | }); |
---|
| 152 | |
---|
| 153 | The layer node fires the "radiochange" event when the radio button is clicked. |
---|
| 154 | The above snippet configures a listener for this event when a node is added to |
---|
| 155 | or inserted in the tree. |
---|
| 156 | |
---|
| 157 | Sub-Layers |
---|
| 158 | ========== |
---|
| 159 | |
---|
| 160 | Layers that have a ``params`` property (like ``OpenLayers.Layer.WMS``) can be |
---|
| 161 | used to create sub-layers based on one of the ``params`` properties. This is |
---|
| 162 | useful to e.g. create sub-nodes from the layer object's "LAYERS" or "CQL_FILTER" |
---|
| 163 | param: |
---|
| 164 | |
---|
| 165 | .. code-block:: javascript |
---|
| 166 | |
---|
| 167 | var groupLayer = new OpenLayers.Layer.WMS("Tasmania (Group Layer)", |
---|
| 168 | "http://demo.opengeo.org/geoserver/wms", { |
---|
| 169 | layers: [ |
---|
| 170 | "topp:tasmania_state_boundaries", |
---|
| 171 | "topp:tasmania_water_bodies", |
---|
| 172 | "topp:tasmania_cities", |
---|
| 173 | "topp:tasmania_roads" |
---|
| 174 | ], |
---|
| 175 | transparent: true, |
---|
| 176 | format: "image/gif" |
---|
| 177 | } |
---|
| 178 | ); |
---|
| 179 | var groupLayerNode = new GeoExt.tree.LayerNode({ |
---|
| 180 | layer: groupLayer, |
---|
| 181 | leaf: false, |
---|
| 182 | expanded: true, |
---|
| 183 | loader: { |
---|
| 184 | param: "LAYERS" |
---|
| 185 | } |
---|
| 186 | }); |
---|
| 187 | |
---|
| 188 | .. note:: |
---|
| 189 | The :class:`GeoExt.tree.LayerParamLoader` does not add drag-n-drop support |
---|
| 190 | to the sub-nodes it creates, so ``allowDrag`` and ``allowDrag`` should be |
---|
| 191 | set to false for a :class:`GeoExt.tree.LayerNode` configured with a |
---|
| 192 | :class:`GeoExt.class.LayerParamLoader`, unless you provide custom "move" |
---|
| 193 | handlers. |
---|
| 194 | |
---|
| 195 | .. seealso:: The ExtJS TreePanel `documentation |
---|
| 196 | <http://dev.sencha.com/deploy/dev/docs/?class=Ext.tree.TreePanel>`_ and `examples |
---|
| 197 | <http://dev.sencha.com/deploy/dev/examples/#sample-7>`_ for more |
---|
| 198 | information about customizing tree panels. |
---|