[76] | 1 | /** |
---|
| 2 | * Copyright (c) 2008-2010 The Open Source Geospatial Foundation |
---|
| 3 | * |
---|
| 4 | * Published under the BSD license. |
---|
| 5 | * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text |
---|
| 6 | * of the license. |
---|
| 7 | */ |
---|
| 8 | |
---|
| 9 | /** api: (define) |
---|
| 10 | * module = GeoExt |
---|
| 11 | * class = FeatureRenderer |
---|
| 12 | * base_link = `Ext.BoxComponent <http://dev.sencha.com/deploy/dev/docs/?class=Ext.BoxComponent>`_ |
---|
| 13 | */ |
---|
| 14 | Ext.namespace('GeoExt'); |
---|
| 15 | |
---|
| 16 | /** api: constructor |
---|
| 17 | * .. class:: FeatureRenderer(config) |
---|
| 18 | * |
---|
| 19 | * Create a box component for rendering a vector feature. |
---|
| 20 | */ |
---|
| 21 | GeoExt.FeatureRenderer = Ext.extend(Ext.BoxComponent, { |
---|
| 22 | |
---|
| 23 | /** api: config[feature] |
---|
| 24 | * ``OpenLayers.Feature.Vector`` |
---|
| 25 | * Optional vector to be drawn. If not provided, and if ``symbolizers`` |
---|
| 26 | * is configured with an array of plain symbolizer objects, ``symbolType`` |
---|
| 27 | * should be configured. |
---|
| 28 | */ |
---|
| 29 | feature: undefined, |
---|
| 30 | |
---|
| 31 | /** api: config[symbolizers] |
---|
| 32 | * ``Array(Object)`` |
---|
| 33 | * An array of ``OpenLayers.Symbolizer`` instances or plain symbolizer |
---|
| 34 | * objects (in painters order) for rendering a feature. If no |
---|
| 35 | * symbolizers are provided, the OpenLayers default will be used. If a |
---|
| 36 | * symbolizer is an instance of ``OpenLayers.Symbolizer``, its type will |
---|
| 37 | * override the symbolType for rendering. |
---|
| 38 | */ |
---|
| 39 | symbolizers: [OpenLayers.Feature.Vector.style["default"]], |
---|
| 40 | |
---|
| 41 | /** api: config[symbolType] |
---|
| 42 | * ``String`` |
---|
| 43 | * One of ``"Point"``, ``"Line"``, or ``"Polygon"``. Only pertinent if |
---|
| 44 | * OpenLayers.Symbolizer objects are not used. If ``feature`` |
---|
| 45 | * is provided, it will be preferred. The default is "Polygon". |
---|
| 46 | */ |
---|
| 47 | symbolType: "Polygon", |
---|
| 48 | |
---|
| 49 | /** private: property[resolution] |
---|
| 50 | * ``Number`` |
---|
| 51 | * The resolution for the renderer. |
---|
| 52 | */ |
---|
| 53 | resolution: 1, |
---|
| 54 | |
---|
| 55 | /** private: property[minWidth] |
---|
| 56 | * ``Number`` |
---|
| 57 | */ |
---|
| 58 | minWidth: 20, |
---|
| 59 | |
---|
| 60 | /** private: property[minHeight] |
---|
| 61 | * ``Number`` |
---|
| 62 | */ |
---|
| 63 | minHeight: 20, |
---|
| 64 | |
---|
| 65 | /** private: property[renderers] |
---|
| 66 | * ``Array(String)`` |
---|
| 67 | * List of supported Renderer classes. Add to this list to add support for |
---|
| 68 | * additional renderers. The first renderer in the list that returns |
---|
| 69 | * ``true`` for the ``supported`` method will be used, if not defined in |
---|
| 70 | * the ``renderer`` config property. |
---|
| 71 | */ |
---|
| 72 | renderers: ["SVG", "VML", "Canvas"], |
---|
| 73 | |
---|
| 74 | /** private: property[rendererOptions] |
---|
| 75 | * ``Object`` |
---|
| 76 | * Options for the renderer. See ``OpenLayers.Renderer`` for supported |
---|
| 77 | * options. |
---|
| 78 | */ |
---|
| 79 | rendererOptions: null, |
---|
| 80 | |
---|
| 81 | /** private: property[pointFeature] |
---|
| 82 | * ``OpenLayers.Feature.Vector`` |
---|
| 83 | * Feature with point geometry. |
---|
| 84 | */ |
---|
| 85 | pointFeature: undefined, |
---|
| 86 | |
---|
| 87 | /** private: property[lineFeature] |
---|
| 88 | * ``OpenLayers.Feature.Vector`` |
---|
| 89 | * Feature with LineString geometry. Default zig-zag is provided. |
---|
| 90 | */ |
---|
| 91 | lineFeature: undefined, |
---|
| 92 | |
---|
| 93 | /** private: property[polygonFeature] |
---|
| 94 | * ``OpenLayers.Feature.Vector`` |
---|
| 95 | * Feature with Polygon geometry. Default is a soft cornered rectangle. |
---|
| 96 | */ |
---|
| 97 | polygonFeature: undefined, |
---|
| 98 | |
---|
| 99 | /** private: property[renderer] |
---|
| 100 | * ``OpenLayers.Renderer`` |
---|
| 101 | */ |
---|
| 102 | renderer: null, |
---|
| 103 | |
---|
| 104 | /** private: method[initComponent] |
---|
| 105 | */ |
---|
| 106 | initComponent: function() { |
---|
| 107 | GeoExt.FeatureRenderer.superclass.initComponent.apply(this, arguments); |
---|
| 108 | Ext.applyIf(this, { |
---|
| 109 | pointFeature: new OpenLayers.Feature.Vector( |
---|
| 110 | new OpenLayers.Geometry.Point(0, 0) |
---|
| 111 | ), |
---|
| 112 | lineFeature: new OpenLayers.Feature.Vector( |
---|
| 113 | new OpenLayers.Geometry.LineString([ |
---|
| 114 | new OpenLayers.Geometry.Point(-8, -3), |
---|
| 115 | new OpenLayers.Geometry.Point(-3, 3), |
---|
| 116 | new OpenLayers.Geometry.Point(3, -3), |
---|
| 117 | new OpenLayers.Geometry.Point(8, 3) |
---|
| 118 | ]) |
---|
| 119 | ), |
---|
| 120 | polygonFeature: new OpenLayers.Feature.Vector( |
---|
| 121 | new OpenLayers.Geometry.Polygon([ |
---|
| 122 | new OpenLayers.Geometry.LinearRing([ |
---|
| 123 | new OpenLayers.Geometry.Point(-8, -4), |
---|
| 124 | new OpenLayers.Geometry.Point(-6, -6), |
---|
| 125 | new OpenLayers.Geometry.Point(6, -6), |
---|
| 126 | new OpenLayers.Geometry.Point(8, -4), |
---|
| 127 | new OpenLayers.Geometry.Point(8, 4), |
---|
| 128 | new OpenLayers.Geometry.Point(6, 6), |
---|
| 129 | new OpenLayers.Geometry.Point(-6, 6), |
---|
| 130 | new OpenLayers.Geometry.Point(-8, 4) |
---|
| 131 | ]) |
---|
| 132 | ]) |
---|
| 133 | ) |
---|
| 134 | }); |
---|
| 135 | if(!this.feature) { |
---|
| 136 | this.setFeature(null, {draw: false}); |
---|
| 137 | } |
---|
| 138 | this.addEvents( |
---|
| 139 | /** api: event[click] |
---|
| 140 | * Fires when the feature is clicked on. |
---|
| 141 | * |
---|
| 142 | * Listener arguments: |
---|
| 143 | * |
---|
| 144 | * * renderer - :class:`GeoExt.FeatureRenderer` This feature renderer. |
---|
| 145 | */ |
---|
| 146 | "click" |
---|
| 147 | ); |
---|
| 148 | }, |
---|
| 149 | |
---|
| 150 | /** private: method[initCustomEvents] |
---|
| 151 | */ |
---|
| 152 | initCustomEvents: function() { |
---|
| 153 | this.clearCustomEvents(); |
---|
| 154 | this.el.on("click", this.onClick, this); |
---|
| 155 | }, |
---|
| 156 | |
---|
| 157 | /** private: method[clearCustomEvents] |
---|
| 158 | */ |
---|
| 159 | clearCustomEvents: function() { |
---|
| 160 | if (this.el && this.el.removeAllListeners) { |
---|
| 161 | this.el.removeAllListeners(); |
---|
| 162 | } |
---|
| 163 | }, |
---|
| 164 | |
---|
| 165 | /** private: method[onClick] |
---|
| 166 | */ |
---|
| 167 | onClick: function() { |
---|
| 168 | this.fireEvent("click", this); |
---|
| 169 | }, |
---|
| 170 | |
---|
| 171 | /** private: method[onRender] |
---|
| 172 | */ |
---|
| 173 | onRender: function(ct, position) { |
---|
| 174 | if(!this.el) { |
---|
| 175 | this.el = document.createElement("div"); |
---|
| 176 | this.el.id = this.getId(); |
---|
| 177 | } |
---|
| 178 | if(!this.renderer || !this.renderer.supported()) { |
---|
| 179 | this.assignRenderer(); |
---|
| 180 | } |
---|
| 181 | // monkey-patch renderer so we always get a resolution |
---|
| 182 | this.renderer.map = { |
---|
| 183 | getResolution: (function() { |
---|
| 184 | return this.resolution; |
---|
| 185 | }).createDelegate(this) |
---|
| 186 | }; |
---|
| 187 | |
---|
| 188 | GeoExt.FeatureRenderer.superclass.onRender.apply(this, arguments); |
---|
| 189 | |
---|
| 190 | this.drawFeature(); |
---|
| 191 | }, |
---|
| 192 | |
---|
| 193 | /** private: method[afterRender] |
---|
| 194 | */ |
---|
| 195 | afterRender: function() { |
---|
| 196 | GeoExt.FeatureRenderer.superclass.afterRender.apply(this, arguments); |
---|
| 197 | this.initCustomEvents(); |
---|
| 198 | }, |
---|
| 199 | |
---|
| 200 | /** private: method[onResize] |
---|
| 201 | */ |
---|
| 202 | onResize: function(w, h) { |
---|
| 203 | this.setRendererDimensions(); |
---|
| 204 | GeoExt.FeatureRenderer.superclass.onResize.apply(this, arguments); |
---|
| 205 | }, |
---|
| 206 | |
---|
| 207 | /** private: method[setRendererDimensions] |
---|
| 208 | */ |
---|
| 209 | setRendererDimensions: function() { |
---|
| 210 | var gb = this.feature.geometry.getBounds(); |
---|
| 211 | var gw = gb.getWidth(); |
---|
| 212 | var gh = gb.getHeight(); |
---|
| 213 | /** |
---|
| 214 | * Determine resolution based on the following rules: |
---|
| 215 | * 1) always use value specified in config |
---|
| 216 | * 2) if not specified, use max res based on width or height of element |
---|
| 217 | * 3) if no width or height, assume a resolution of 1 |
---|
| 218 | */ |
---|
| 219 | var resolution = this.initialConfig.resolution; |
---|
| 220 | if(!resolution) { |
---|
| 221 | resolution = Math.max(gw / this.width || 0, gh / this.height || 0) || 1; |
---|
| 222 | } |
---|
| 223 | this.resolution = resolution; |
---|
| 224 | // determine height and width of element |
---|
| 225 | var width = Math.max(this.width || this.minWidth, gw / resolution); |
---|
| 226 | var height = Math.max(this.height || this.minHeight, gh / resolution); |
---|
| 227 | // determine bounds of renderer |
---|
| 228 | var center = gb.getCenterPixel(); |
---|
| 229 | var bhalfw = width * resolution / 2; |
---|
| 230 | var bhalfh = height * resolution / 2; |
---|
| 231 | var bounds = new OpenLayers.Bounds( |
---|
| 232 | center.x - bhalfw, center.y - bhalfh, |
---|
| 233 | center.x + bhalfw, center.y + bhalfh |
---|
| 234 | ); |
---|
| 235 | this.renderer.setSize(new OpenLayers.Size(Math.round(width), Math.round(height))); |
---|
| 236 | this.renderer.setExtent(bounds, true); |
---|
| 237 | }, |
---|
| 238 | |
---|
| 239 | /** private: method[assignRenderer] |
---|
| 240 | * Iterate through the available renderer implementations and selects |
---|
| 241 | * and assign the first one whose ``supported`` method returns ``true``. |
---|
| 242 | */ |
---|
| 243 | assignRenderer: function() { |
---|
| 244 | for(var i=0, len=this.renderers.length; i<len; ++i) { |
---|
| 245 | var Renderer = OpenLayers.Renderer[this.renderers[i]]; |
---|
| 246 | if(Renderer && Renderer.prototype.supported()) { |
---|
| 247 | this.renderer = new Renderer( |
---|
| 248 | this.el, this.rendererOptions |
---|
| 249 | ); |
---|
| 250 | break; |
---|
| 251 | } |
---|
| 252 | } |
---|
| 253 | }, |
---|
| 254 | |
---|
| 255 | /** api: method[setSymbolizers] |
---|
| 256 | * :arg symbolizers: ``Array(Object)`` An array of symbolizers |
---|
| 257 | * :arg options: ``Object`` |
---|
| 258 | * |
---|
| 259 | * Update the symbolizers used to render the feature. |
---|
| 260 | * |
---|
| 261 | * Valid options: |
---|
| 262 | * |
---|
| 263 | * * draw - ``Boolean`` Draw the feature after setting it. Default is ``true``. |
---|
| 264 | */ |
---|
| 265 | setSymbolizers: function(symbolizers, options) { |
---|
| 266 | this.symbolizers = symbolizers; |
---|
| 267 | if(!options || options.draw) { |
---|
| 268 | this.drawFeature(); |
---|
| 269 | } |
---|
| 270 | }, |
---|
| 271 | |
---|
| 272 | /** api: method[setSymbolType] |
---|
| 273 | * :arg type: ``String`` One of the ``symbolType`` strings. |
---|
| 274 | * :arg options: ``Object`` |
---|
| 275 | * |
---|
| 276 | * Create a new feature based on the geometry type and render it. |
---|
| 277 | * |
---|
| 278 | * Valid options: |
---|
| 279 | * |
---|
| 280 | * * draw - ``Boolean`` Draw the feature after setting it. Default is ``true``. |
---|
| 281 | */ |
---|
| 282 | setSymbolType: function(type, options) { |
---|
| 283 | this.symbolType = type; |
---|
| 284 | this.setFeature(null, options); |
---|
| 285 | }, |
---|
| 286 | |
---|
| 287 | /** api: method[setFeature] |
---|
| 288 | * :arg feature: ``OpenLayers.Feature.Vector`` The feature to be rendered. |
---|
| 289 | * If none is provided, one will be created based on ``symbolType``. |
---|
| 290 | * :arg options: ``Object`` |
---|
| 291 | * |
---|
| 292 | * Update the feature and redraw. |
---|
| 293 | * |
---|
| 294 | * Valid options: |
---|
| 295 | * |
---|
| 296 | * * draw - ``Boolean`` Draw the feature after setting it. Default is ``true``. |
---|
| 297 | */ |
---|
| 298 | setFeature: function(feature, options) { |
---|
| 299 | this.feature = feature || this[this.symbolType.toLowerCase() + "Feature"]; |
---|
| 300 | if(!options || options.draw) { |
---|
| 301 | this.drawFeature(); |
---|
| 302 | } |
---|
| 303 | }, |
---|
| 304 | |
---|
| 305 | /** private: method[drawFeature] |
---|
| 306 | * Render the feature with the symbolizers. |
---|
| 307 | */ |
---|
| 308 | drawFeature: function() { |
---|
| 309 | this.renderer.clear(); |
---|
| 310 | this.setRendererDimensions(); |
---|
| 311 | // TODO: remove this when OpenLayers.Symbolizer is required |
---|
| 312 | var Symbolizer = OpenLayers.Symbolizer; |
---|
| 313 | var Text = Symbolizer && Symbolizer.Text; |
---|
| 314 | var symbolizer, feature, geomType; |
---|
| 315 | for (var i=0, len=this.symbolizers.length; i<len; ++i) { |
---|
| 316 | symbolizer = this.symbolizers[i]; |
---|
| 317 | feature = this.feature; |
---|
| 318 | // don't render text symbolizers |
---|
| 319 | if (!Text || !(symbolizer instanceof Text)) { |
---|
| 320 | if (Symbolizer && (symbolizer instanceof Symbolizer)) { |
---|
| 321 | symbolizer = symbolizer.clone(); |
---|
| 322 | if (!this.initialConfig.feature) { |
---|
| 323 | geomType = symbolizer.CLASS_NAME.split(".").pop().toLowerCase(); |
---|
| 324 | feature = this[geomType + "Feature"]; |
---|
| 325 | } |
---|
| 326 | } else { |
---|
| 327 | // TODO: remove this when OpenLayers.Symbolizer is used everywhere |
---|
| 328 | symbolizer = Ext.apply({}, symbolizer); |
---|
| 329 | } |
---|
| 330 | this.renderer.drawFeature( |
---|
| 331 | feature.clone(), |
---|
| 332 | symbolizer |
---|
| 333 | ); |
---|
| 334 | } |
---|
| 335 | } |
---|
| 336 | }, |
---|
| 337 | |
---|
| 338 | /** api: method[update] |
---|
| 339 | * :arg options: ``Object`` Object with properties to be updated. |
---|
| 340 | * |
---|
| 341 | * Update the ``symbolType`` or ``feature`` and ``symbolizer`` and redraw |
---|
| 342 | * the feature. |
---|
| 343 | * |
---|
| 344 | * Valid options: |
---|
| 345 | * |
---|
| 346 | * * feature - ``OpenLayers.Feature.Vector`` The new or updated feature. |
---|
| 347 | * If provided, the feature gets precedence over ``symbolType``. |
---|
| 348 | * * symbolType - ``String`` One of the allowed ``symbolType`` values. |
---|
| 349 | * * symbolizers - ``Array(Object)`` An array of symbolizer objects. |
---|
| 350 | */ |
---|
| 351 | update: function(options) { |
---|
| 352 | options = options || {}; |
---|
| 353 | if(options.feature) { |
---|
| 354 | this.setFeature(options.feature, {draw: false}); |
---|
| 355 | } else if(options.symbolType) { |
---|
| 356 | this.setSymbolType(options.symbolType, {draw: false}); |
---|
| 357 | } |
---|
| 358 | if(options.symbolizers) { |
---|
| 359 | this.setSymbolizers(options.symbolizers, {draw: false}); |
---|
| 360 | } |
---|
| 361 | this.drawFeature(); |
---|
| 362 | }, |
---|
| 363 | |
---|
| 364 | /** private: method[beforeDestroy] |
---|
| 365 | * Private method called during the destroy sequence. |
---|
| 366 | */ |
---|
| 367 | beforeDestroy: function() { |
---|
| 368 | this.clearCustomEvents(); |
---|
| 369 | if (this.renderer) { |
---|
| 370 | this.renderer.destroy(); |
---|
| 371 | } |
---|
| 372 | } |
---|
| 373 | |
---|
| 374 | }); |
---|
| 375 | |
---|
| 376 | /** api: xtype = gx_renderer */ |
---|
| 377 | Ext.reg('gx_renderer', GeoExt.FeatureRenderer); |
---|