]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Layer/Vector.js
web interface to add co-administrators
[syp.git] / openlayers / lib / OpenLayers / Layer / Vector.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Layer.js
7  * @requires OpenLayers/Renderer.js
8  * @requires OpenLayers/StyleMap.js
9  * @requires OpenLayers/Feature/Vector.js
10  * @requires OpenLayers/Console.js
11  */
12
13 /**
14  * Class: OpenLayers.Layer.Vector
15  * Instances of OpenLayers.Layer.Vector are used to render vector data from
16  *     a variety of sources. Create a new vector layer with the
17  *     <OpenLayers.Layer.Vector> constructor.
18  *
19  * Inherits from:
20  *  - <OpenLayers.Layer>
21  */
22 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
23
24     /**
25      * Constant: EVENT_TYPES
26      * {Array(String)} Supported application event types.  Register a listener
27      *     for a particular event with the following syntax:
28      * (code)
29      * layer.events.register(type, obj, listener);
30      * (end)
31      *
32      * Listeners will be called with a reference to an event object.  The
33      *     properties of this event depends on exactly what happened.
34      *
35      * All event objects have at least the following properties:
36      * object - {Object} A reference to layer.events.object.
37      * element - {DOMElement} A reference to layer.events.element.
38      *
39      * Supported map event types (in addition to those from <OpenLayers.Layer>):
40      * beforefeatureadded - Triggered before a feature is added.  Listeners
41      *      will receive an object with a *feature* property referencing the
42      *      feature to be added.  To stop the feature from being added, a
43      *      listener should return false.
44      * beforefeaturesadded - Triggered before an array of features is added.
45      *      Listeners will receive an object with a *features* property
46      *      referencing the feature to be added. To stop the features from
47      *      being added, a listener should return false.
48      * featureadded - Triggered after a feature is added.  The event
49      *      object passed to listeners will have a *feature* property with a
50      *      reference to the added feature.
51      * featuresadded - Triggered after features are added.  The event
52      *      object passed to listeners will have a *features* property with a
53      *      reference to an array of added features.
54      * beforefeatureremoved - Triggered before a feature is removed. Listeners
55      *      will receive an object with a *feature* property referencing the
56      *      feature to be removed.
57      * featureremoved - Triggerd after a feature is removed. The event
58      *      object passed to listeners will have a *feature* property with a
59      *      reference to the removed feature.
60      * featuresremoved - Triggered after features are removed. The event
61      *      object passed to listeners will have a *features* property with a
62      *      reference to an array of removed features.
63      * featureselected - Triggered after a feature is selected.  Listeners
64      *      will receive an object with a *feature* property referencing the
65      *      selected feature.
66      * featureunselected - Triggered after a feature is unselected.
67      *      Listeners will receive an object with a *feature* property
68      *      referencing the unselected feature.
69      * beforefeaturemodified - Triggered when a feature is selected to 
70      *      be modified.  Listeners will receive an object with a *feature* 
71      *      property referencing the selected feature.
72      * featuremodified - Triggered when a feature has been modified.
73      *      Listeners will receive an object with a *feature* property referencing 
74      *      the modified feature.
75      * afterfeaturemodified - Triggered when a feature is finished being modified.
76      *      Listeners will receive an object with a *feature* property referencing 
77      *      the modified feature.
78      * vertexmodified - Triggered when a vertex within any feature geometry
79      *      has been modified.  Listeners will receive an object with a
80      *      *feature* property referencing the modified feature, a *vertex*
81      *      property referencing the vertex modified (always a point geometry),
82      *      and a *pixel* property referencing the pixel location of the
83      *      modification.
84      * sketchstarted - Triggered when a feature sketch bound for this layer
85      *      is started.  Listeners will receive an object with a *feature*
86      *      property referencing the new sketch feature and a *vertex* property
87      *      referencing the creation point.
88      * sketchmodified - Triggered when a feature sketch bound for this layer
89      *      is modified.  Listeners will receive an object with a *vertex*
90      *      property referencing the modified vertex and a *feature* property
91      *      referencing the sketch feature.
92      * sketchcomplete - Triggered when a feature sketch bound for this layer
93      *      is complete.  Listeners will receive an object with a *feature*
94      *      property referencing the sketch feature.  By returning false, a
95      *      listener can stop the sketch feature from being added to the layer.
96      * refresh - Triggered when something wants a strategy to ask the protocol
97      *      for a new set of features.
98      */
99     EVENT_TYPES: ["beforefeatureadded", "beforefeaturesadded",
100                   "featureadded", "featuresadded",
101                   "beforefeatureremoved", "featureremoved", "featuresremoved",
102                   "beforefeatureselected", "featureselected", "featureunselected", 
103                   "beforefeaturemodified", "featuremodified", "afterfeaturemodified",
104                   "vertexmodified", "sketchstarted", "sketchmodified",
105                   "sketchcomplete", "refresh"],
106
107     /**
108      * APIProperty: isBaseLayer
109      * {Boolean} The layer is a base layer.  Default is true.  Set this property
110      * in the layer options
111      */
112     isBaseLayer: false,
113
114     /** 
115      * APIProperty: isFixed
116      * {Boolean} Whether the layer remains in one place while dragging the
117      * map.
118      */
119     isFixed: false,
120
121     /** 
122      * APIProperty: isVector
123      * {Boolean} Whether the layer is a vector layer.
124      */
125     isVector: true,
126     
127     /** 
128      * APIProperty: features
129      * {Array(<OpenLayers.Feature.Vector>)} 
130      */
131     features: null,
132     
133     /** 
134      * Property: selectedFeatures
135      * {Array(<OpenLayers.Feature.Vector>)} 
136      */
137     selectedFeatures: null,
138     
139     /**
140      * Property: unrenderedFeatures
141      * {Object} hash of features, keyed by feature.id, that the renderer
142      *     failed to draw
143      */
144     unrenderedFeatures: null,
145
146     /**
147      * APIProperty: reportError
148      * {Boolean} report friendly error message when loading of renderer
149      * fails.
150      */
151     reportError: true, 
152
153     /** 
154      * APIProperty: style
155      * {Object} Default style for the layer
156      */
157     style: null,
158     
159     /**
160      * Property: styleMap
161      * {<OpenLayers.StyleMap>}
162      */
163     styleMap: null,
164     
165     /**
166      * Property: strategies
167      * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
168      */
169     strategies: null,
170     
171     /**
172      * Property: protocol
173      * {<OpenLayers.Protocol>} Optional protocol for the layer.
174      */
175     protocol: null,
176     
177     /**
178      * Property: renderers
179      * {Array(String)} List of supported Renderer classes. Add to this list to
180      * add support for additional renderers. This list is ordered:
181      * the first renderer which returns true for the  'supported()'
182      * method will be used, if not defined in the 'renderer' option.
183      */
184     renderers: ['SVG', 'VML', 'Canvas'],
185     
186     /** 
187      * Property: renderer
188      * {<OpenLayers.Renderer>}
189      */
190     renderer: null,
191     
192     /**
193      * APIProperty: rendererOptions
194      * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
195      *     supported options.
196      */
197     rendererOptions: null,
198     
199     /** 
200      * APIProperty: geometryType
201      * {String} geometryType allows you to limit the types of geometries this
202      * layer supports. This should be set to something like
203      * "OpenLayers.Geometry.Point" to limit types.
204      */
205     geometryType: null,
206
207     /** 
208      * Property: drawn
209      * {Boolean} Whether the Vector Layer features have been drawn yet.
210      */
211     drawn: false,
212
213     /**
214      * Constructor: OpenLayers.Layer.Vector
215      * Create a new vector layer
216      *
217      * Parameters:
218      * name - {String} A name for the layer
219      * options - {Object} Optional object with non-default properties to set on
220      *           the layer.
221      *
222      * Returns:
223      * {<OpenLayers.Layer.Vector>} A new vector layer
224      */
225     initialize: function(name, options) {
226         
227         // concatenate events specific to vector with those from the base
228         this.EVENT_TYPES =
229             OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(
230             OpenLayers.Layer.prototype.EVENT_TYPES
231         );
232
233         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
234
235         // allow user-set renderer, otherwise assign one
236         if (!this.renderer || !this.renderer.supported()) {  
237             this.assignRenderer();
238         }
239
240         // if no valid renderer found, display error
241         if (!this.renderer || !this.renderer.supported()) {
242             this.renderer = null;
243             this.displayError();
244         } 
245
246         if (!this.styleMap) {
247             this.styleMap = new OpenLayers.StyleMap();
248         }
249
250         this.features = [];
251         this.selectedFeatures = [];
252         this.unrenderedFeatures = {};
253         
254         // Allow for custom layer behavior
255         if(this.strategies){
256             for(var i=0, len=this.strategies.length; i<len; i++) {
257                 this.strategies[i].setLayer(this);
258             }
259         }
260
261     },
262
263     /**
264      * APIMethod: destroy
265      * Destroy this layer
266      */
267     destroy: function() {
268         if (this.strategies) {
269             var strategy, i, len;
270             for(i=0, len=this.strategies.length; i<len; i++) {
271                 strategy = this.strategies[i];
272                 if(strategy.autoDestroy) {
273                     strategy.destroy();
274                 }
275             }
276             this.strategies = null;
277         }
278         if (this.protocol) {
279             if(this.protocol.autoDestroy) {
280                 this.protocol.destroy();
281             }
282             this.protocol = null;
283         }
284         this.destroyFeatures();
285         this.features = null;
286         this.selectedFeatures = null;
287         this.unrenderedFeatures = null;
288         if (this.renderer) {
289             this.renderer.destroy();
290         }
291         this.renderer = null;
292         this.geometryType = null;
293         this.drawn = null;
294         OpenLayers.Layer.prototype.destroy.apply(this, arguments);  
295     },
296
297     /**
298      * Method: refresh
299      * Ask the layer to request features again and redraw them.  Triggers
300      *     the refresh event if the layer is in range and visible.
301      *
302      * Parameters:
303      * obj - {Object} Optional object with properties for any listener of
304      *     the refresh event.
305      */
306     refresh: function(obj) {
307         if(this.calculateInRange() && this.visibility) {
308             this.events.triggerEvent("refresh", obj);
309         }
310     },
311
312     /** 
313      * Method: assignRenderer
314      * Iterates through the available renderer implementations and selects 
315      * and assigns the first one whose "supported()" function returns true.
316      */    
317     assignRenderer: function()  {
318         for (var i=0, len=this.renderers.length; i<len; i++) {
319             var rendererClass = OpenLayers.Renderer[this.renderers[i]];
320             if (rendererClass && rendererClass.prototype.supported()) {
321                 this.renderer = new rendererClass(this.div,
322                     this.rendererOptions);
323                 break;
324             }  
325         }  
326     },
327
328     /** 
329      * Method: displayError 
330      * Let the user know their browser isn't supported.
331      */
332     displayError: function() {
333         if (this.reportError) {
334             OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", 
335                                      {'renderers':this.renderers.join("\n")}));
336         }    
337     },
338
339     /** 
340      * Method: setMap
341      * The layer has been added to the map. 
342      * 
343      * If there is no renderer set, the layer can't be used. Remove it.
344      * Otherwise, give the renderer a reference to the map and set its size.
345      * 
346      * Parameters:
347      * map - {<OpenLayers.Map>} 
348      */
349     setMap: function(map) {        
350         OpenLayers.Layer.prototype.setMap.apply(this, arguments);
351
352         if (!this.renderer) {
353             this.map.removeLayer(this);
354         } else {
355             this.renderer.map = this.map;
356             this.renderer.setSize(this.map.getSize());
357         }
358     },
359
360     /**
361      * Method: afterAdd
362      * Called at the end of the map.addLayer sequence.  At this point, the map
363      *     will have a base layer.  Any autoActivate strategies will be
364      *     activated here.
365      */
366     afterAdd: function() {
367         if(this.strategies) {
368             var strategy, i, len;
369             for(i=0, len=this.strategies.length; i<len; i++) {
370                 strategy = this.strategies[i];
371                 if(strategy.autoActivate) {
372                     strategy.activate();
373                 }
374             }
375         }
376     },
377
378     /**
379      * Method: removeMap
380      * The layer has been removed from the map.
381      *
382      * Parameters:
383      * map - {<OpenLayers.Map>}
384      */
385     removeMap: function(map) {
386         if(this.strategies) {
387             var strategy, i, len;
388             for(i=0, len=this.strategies.length; i<len; i++) {
389                 strategy = this.strategies[i];
390                 if(strategy.autoActivate) {
391                     strategy.deactivate();
392                 }
393             }
394         }
395     },
396     
397     /**
398      * Method: onMapResize
399      * Notify the renderer of the change in size. 
400      * 
401      */
402     onMapResize: function() {
403         OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
404         this.renderer.setSize(this.map.getSize());
405     },
406
407     /**
408      * Method: moveTo
409      *  Reset the vector layer's div so that it once again is lined up with 
410      *   the map. Notify the renderer of the change of extent, and in the
411      *   case of a change of zoom level (resolution), have the 
412      *   renderer redraw features.
413      * 
414      *  If the layer has not yet been drawn, cycle through the layer's 
415      *   features and draw each one.
416      * 
417      * Parameters:
418      * bounds - {<OpenLayers.Bounds>} 
419      * zoomChanged - {Boolean} 
420      * dragging - {Boolean} 
421      */
422     moveTo: function(bounds, zoomChanged, dragging) {
423         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
424         
425         var coordSysUnchanged = true;
426
427         if (!dragging) {
428             this.renderer.root.style.visibility = "hidden";
429             
430             this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
431             this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
432             var extent = this.map.getExtent();
433             coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
434             
435             this.renderer.root.style.visibility = "visible";
436
437             // Force a reflow on gecko based browsers to prevent jump/flicker.
438             // This seems to happen on only certain configurations; it was originally
439             // noticed in FF 2.0 and Linux.
440             if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {
441                 this.div.scrollLeft = this.div.scrollLeft;
442             }
443             
444             if(!zoomChanged && coordSysUnchanged) {
445                 for(var i in this.unrenderedFeatures) {
446                     var feature = this.unrenderedFeatures[i];
447                     this.drawFeature(feature);
448                 }
449             }
450         }
451         
452         if (!this.drawn || zoomChanged || !coordSysUnchanged) {
453             this.drawn = true;
454             var feature;
455             for(var i=0, len=this.features.length; i<len; i++) {
456                 this.renderer.locked = (i !== (len - 1));
457                 feature = this.features[i];
458                 this.drawFeature(feature);
459             }
460         }    
461     },
462     
463     /** 
464      * APIMethod: display
465      * Hide or show the Layer
466      * 
467      * Parameters:
468      * display - {Boolean}
469      */
470     display: function(display) {
471         OpenLayers.Layer.prototype.display.apply(this, arguments);
472         // we need to set the display style of the root in case it is attached
473         // to a foreign layer
474         var currentDisplay = this.div.style.display;
475         if(currentDisplay != this.renderer.root.style.display) {
476             this.renderer.root.style.display = currentDisplay;
477         }
478     },
479
480     /**
481      * APIMethod: addFeatures
482      * Add Features to the layer.
483      *
484      * Parameters:
485      * features - {Array(<OpenLayers.Feature.Vector>)} 
486      * options - {Object}
487      */
488     addFeatures: function(features, options) {
489         if (!(features instanceof Array)) {
490             features = [features];
491         }
492         
493         var notify = !options || !options.silent;
494         if(notify) {
495             var event = {features: features};
496             var ret = this.events.triggerEvent("beforefeaturesadded", event);
497             if(ret === false) {
498                 return;
499             }
500             features = event.features;
501         }
502         
503
504         for (var i=0, len=features.length; i<len; i++) {
505             if (i != (features.length - 1)) {
506                 this.renderer.locked = true;
507             } else {
508                 this.renderer.locked = false;
509             }    
510             var feature = features[i];
511             
512             if (this.geometryType &&
513               !(feature.geometry instanceof this.geometryType)) {
514                 var throwStr = OpenLayers.i18n('componentShouldBe',
515                           {'geomType':this.geometryType.prototype.CLASS_NAME});
516                 throw throwStr;
517               }
518
519             this.features.push(feature);
520             
521             //give feature reference to its layer
522             feature.layer = this;
523
524             if (!feature.style && this.style) {
525                 feature.style = OpenLayers.Util.extend({}, this.style);
526             }
527
528             if (notify) {
529                 if(this.events.triggerEvent("beforefeatureadded",
530                                             {feature: feature}) === false) {
531                     continue;
532                 };
533                 this.preFeatureInsert(feature);
534             }
535
536             this.drawFeature(feature);
537             
538             if (notify) {
539                 this.events.triggerEvent("featureadded", {
540                     feature: feature
541                 });
542                 this.onFeatureInsert(feature);
543             }
544         }
545         
546         if(notify) {
547             this.events.triggerEvent("featuresadded", {features: features});
548         }
549     },
550
551
552     /**
553      * APIMethod: removeFeatures
554      * Remove features from the layer.  This erases any drawn features and
555      *     removes them from the layer's control.  The beforefeatureremoved
556      *     and featureremoved events will be triggered for each feature.  The
557      *     featuresremoved event will be triggered after all features have
558      *     been removed.  To supress event triggering, use the silent option.
559      * 
560      * Parameters:
561      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
562      *     removed.
563      * options - {Object} Optional properties for changing behavior of the
564      *     removal.
565      *
566      * Valid options:
567      * silent - {Boolean} Supress event triggering.  Default is false.
568      */
569     removeFeatures: function(features, options) {
570         if(!features || features.length === 0) {
571             return;
572         }
573         if (!(features instanceof Array)) {
574             features = [features];
575         }
576         if (features === this.features) {
577             features = features.slice();
578         }
579
580         var notify = !options || !options.silent;
581
582         for (var i = features.length - 1; i >= 0; i--) {
583             // We remain locked so long as we're not at 0
584             // and the 'next' feature has a geometry. We do the geometry check
585             // because if all the features after the current one are 'null', we
586             // won't call eraseGeometry, so we break the 'renderer functions
587             // will always be called with locked=false *last*' rule. The end result
588             // is a possible gratiutious unlocking to save a loop through the rest 
589             // of the list checking the remaining features every time. So long as
590             // null geoms are rare, this is probably okay.    
591             if (i != 0 && features[i-1].geometry) {
592                 this.renderer.locked = true;
593             } else {
594                 this.renderer.locked = false;
595             }
596     
597             var feature = features[i];
598             delete this.unrenderedFeatures[feature.id];
599
600             if (notify) {
601                 this.events.triggerEvent("beforefeatureremoved", {
602                     feature: feature
603                 });
604             }
605
606             this.features = OpenLayers.Util.removeItem(this.features, feature);
607             // feature has no layer at this point
608             feature.layer = null;
609
610             if (feature.geometry) {
611                 this.renderer.eraseFeatures(feature);
612             }
613                     
614             //in the case that this feature is one of the selected features, 
615             // remove it from that array as well.
616             if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
617                 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
618             }
619
620             if (notify) {
621                 this.events.triggerEvent("featureremoved", {
622                     feature: feature
623                 });
624             }
625         }
626
627         if (notify) {
628             this.events.triggerEvent("featuresremoved", {features: features});
629         }
630     },
631
632     /**
633      * APIMethod: destroyFeatures
634      * Erase and destroy features on the layer.
635      *
636      * Parameters:
637      * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
638      *     features to destroy.  If not supplied, all features on the layer
639      *     will be destroyed.
640      * options - {Object}
641      */
642     destroyFeatures: function(features, options) {
643         var all = (features == undefined); // evaluates to true if
644                                            // features is null
645         if(all) {
646             features = this.features;
647         }
648         if(features) {
649             this.removeFeatures(features, options);
650             for(var i=features.length-1; i>=0; i--) {
651                 features[i].destroy();
652             }
653         }
654     },
655
656     /**
657      * APIMethod: drawFeature
658      * Draw (or redraw) a feature on the layer.  If the optional style argument
659      * is included, this style will be used.  If no style is included, the
660      * feature's style will be used.  If the feature doesn't have a style,
661      * the layer's style will be used.
662      * 
663      * This function is not designed to be used when adding features to 
664      * the layer (use addFeatures instead). It is meant to be used when
665      * the style of a feature has changed, or in some other way needs to 
666      * visually updated *after* it has already been added to a layer. You
667      * must add the feature to the layer for most layer-related events to 
668      * happen.
669      *
670      * Parameters: 
671      * feature - {<OpenLayers.Feature.Vector>} 
672      * style - {Object} Symbolizer hash or {String} renderIntent
673      */
674     drawFeature: function(feature, style) {
675         // don't try to draw the feature with the renderer if the layer is not 
676         // drawn itself
677         if (!this.drawn) {
678             return
679         }
680         if (typeof style != "object") {
681             if(!style && feature.state === OpenLayers.State.DELETE) {
682                 style = "delete";
683             }
684             var renderIntent = style || feature.renderIntent;
685             style = feature.style || this.style;
686             if (!style) {
687                 style = this.styleMap.createSymbolizer(feature, renderIntent);
688             }
689         }
690         
691         if (!this.renderer.drawFeature(feature, style)) {
692             this.unrenderedFeatures[feature.id] = feature;
693         } else {
694             delete this.unrenderedFeatures[feature.id];
695         };
696     },
697     
698     /**
699      * Method: eraseFeatures
700      * Erase features from the layer.
701      *
702      * Parameters:
703      * features - {Array(<OpenLayers.Feature.Vector>)} 
704      */
705     eraseFeatures: function(features) {
706         this.renderer.eraseFeatures(features);
707     },
708
709     /**
710      * Method: getFeatureFromEvent
711      * Given an event, return a feature if the event occurred over one.
712      * Otherwise, return null.
713      *
714      * Parameters:
715      * evt - {Event} 
716      *
717      * Returns:
718      * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
719      */
720     getFeatureFromEvent: function(evt) {
721         if (!this.renderer) {
722             OpenLayers.Console.error(OpenLayers.i18n("getFeatureError")); 
723             return null;
724         }    
725         var featureId = this.renderer.getFeatureIdFromEvent(evt);
726         return this.getFeatureById(featureId);
727     },
728     
729     /**
730      * APIMethod: getFeatureById
731      * Given a feature id, return the feature if it exists in the features array
732      *
733      * Parameters:
734      * featureId - {String} 
735      *
736      * Returns:
737      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
738      * featureId
739      */
740     getFeatureById: function(featureId) {
741         //TBD - would it be more efficient to use a hash for this.features?
742         var feature = null;
743         for(var i=0, len=this.features.length; i<len; ++i) {
744             if(this.features[i].id == featureId) {
745                 feature = this.features[i];
746                 break;
747             }
748         }
749         return feature;
750     },
751     
752     /**
753      * Unselect the selected features
754      * i.e. clears the featureSelection array
755      * change the style back
756     clearSelection: function() {
757
758        var vectorLayer = this.map.vectorLayer;
759         for (var i = 0; i < this.map.featureSelection.length; i++) {
760             var featureSelection = this.map.featureSelection[i];
761             vectorLayer.drawFeature(featureSelection, vectorLayer.style);
762         }
763         this.map.featureSelection = [];
764     },
765      */
766
767
768     /**
769      * APIMethod: onFeatureInsert
770      * method called after a feature is inserted.
771      * Does nothing by default. Override this if you
772      * need to do something on feature updates.
773      *
774      * Paarameters: 
775      * feature - {<OpenLayers.Feature.Vector>} 
776      */
777     onFeatureInsert: function(feature) {
778     },
779     
780     /**
781      * APIMethod: preFeatureInsert
782      * method called before a feature is inserted.
783      * Does nothing by default. Override this if you
784      * need to do something when features are first added to the
785      * layer, but before they are drawn, such as adjust the style.
786      *
787      * Parameters:
788      * feature - {<OpenLayers.Feature.Vector>} 
789      */
790     preFeatureInsert: function(feature) {
791     },
792
793     /** 
794      * APIMethod: getDataExtent
795      * Calculates the max extent which includes all of the features.
796      * 
797      * Returns:
798      * {<OpenLayers.Bounds>}
799      */
800     getDataExtent: function () {
801         var maxExtent = null;
802
803         if(this.features && (this.features.length > 0)) {
804             maxExtent = new OpenLayers.Bounds();
805             for(var i=0, len=this.features.length; i<len; i++) {
806                 maxExtent.extend(this.features[i].geometry.getBounds());
807             }
808         }
809
810         return maxExtent;
811     },
812
813     CLASS_NAME: "OpenLayers.Layer.Vector"
814 });