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); |
---|