Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/web/OpenLayers/lib/OpenLayers/Layer.js @ 76

Revision 76, 41.8 KB checked in by djay, 12 years ago (diff)

Ajout du répertoire web

  • Property svn:executable set to *
Line 
1/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
2 * full list of contributors). Published under the Clear BSD license. 
3 * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
4 * full text of the license. */
5
6
7/**
8 * @requires OpenLayers/Map.js
9 * @requires OpenLayers/Projection.js
10 */
11
12/**
13 * Class: OpenLayers.Layer
14 */
15OpenLayers.Layer = OpenLayers.Class({
16
17    /**
18     * APIProperty: id
19     * {String}
20     */
21    id: null,
22
23    /**
24     * APIProperty: name
25     * {String}
26     */
27    name: null,
28
29    /**
30     * APIProperty: div
31     * {DOMElement}
32     */
33    div: null,
34
35    /**
36     * Property: opacity
37     * {Float} The layer's opacity. Float number between 0.0 and 1.0.
38     */
39    opacity: null,
40
41    /**
42     * APIProperty: alwaysInRange
43     * {Boolean} If a layer's display should not be scale-based, this should
44     *     be set to true. This will cause the layer, as an overlay, to always
45     *     be 'active', by always returning true from the calculateInRange()
46     *     function.
47     *
48     *     If not explicitly specified for a layer, its value will be
49     *     determined on startup in initResolutions() based on whether or not
50     *     any scale-specific properties have been set as options on the
51     *     layer. If no scale-specific options have been set on the layer, we
52     *     assume that it should always be in range.
53     *
54     *     See #987 for more info.
55     */
56    alwaysInRange: null,   
57
58    /**
59     * Constant: EVENT_TYPES
60     * {Array(String)} Supported application event types.  Register a listener
61     *     for a particular event with the following syntax:
62     * (code)
63     * layer.events.register(type, obj, listener);
64     * (end)
65     *
66     * Listeners will be called with a reference to an event object.  The
67     *     properties of this event depends on exactly what happened.
68     *
69     * All event objects have at least the following properties:
70     * object - {Object} A reference to layer.events.object.
71     * element - {DOMElement} A reference to layer.events.element.
72     *
73     * Supported map event types:
74     * loadstart - Triggered when layer loading starts.
75     * loadend - Triggered when layer loading ends.
76     * loadcancel - Triggered when layer loading is canceled.
77     * visibilitychanged - Triggered when layer visibility is changed.
78     * move - Triggered when layer moves (triggered with every mousemove
79     *     during a drag).
80     * moveend - Triggered when layer is done moving, object passed as
81     *     argument has a zoomChanged boolean property which tells that the
82     *     zoom has changed.
83     */
84    EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
85                  "move", "moveend"],
86
87    /**
88     * Constant: RESOLUTION_PROPERTIES
89     * {Array} The properties that are used for calculating resolutions
90     *     information.
91     */
92    RESOLUTION_PROPERTIES: [
93        'scales', 'resolutions',
94        'maxScale', 'minScale',
95        'maxResolution', 'minResolution',
96        'numZoomLevels', 'maxZoomLevel'
97    ],
98
99    /**
100     * APIProperty: events
101     * {<OpenLayers.Events>}
102     */
103    events: null,
104
105    /**
106     * APIProperty: map
107     * {<OpenLayers.Map>} This variable is set when the layer is added to
108     *     the map, via the accessor function setMap().
109     */
110    map: null,
111   
112    /**
113     * APIProperty: isBaseLayer
114     * {Boolean} Whether or not the layer is a base layer. This should be set
115     *     individually by all subclasses. Default is false
116     */
117    isBaseLayer: false,
118 
119    /**
120     * Property: alpha
121     * {Boolean} The layer's images have an alpha channel.  Default is false.
122     */
123    alpha: false,
124
125    /**
126     * APIProperty: displayInLayerSwitcher
127     * {Boolean} Display the layer's name in the layer switcher.  Default is
128     *     true.
129     */
130    displayInLayerSwitcher: true,
131
132    /**
133     * APIProperty: visibility
134     * {Boolean} The layer should be displayed in the map.  Default is true.
135     */
136    visibility: true,
137
138    /**
139     * APIProperty: attribution
140     * {String} Attribution string, displayed when an
141     *     <OpenLayers.Control.Attribution> has been added to the map.
142     */
143    attribution: null, 
144
145    /**
146     * Property: inRange
147     * {Boolean} The current map resolution is within the layer's min/max
148     *     range. This is set in <OpenLayers.Map.setCenter> whenever the zoom
149     *     changes.
150     */
151    inRange: false,
152   
153    /**
154     * Propery: imageSize
155     * {<OpenLayers.Size>} For layers with a gutter, the image is larger than
156     *     the tile by twice the gutter in each dimension.
157     */
158    imageSize: null,
159   
160    /**
161     * Property: imageOffset
162     * {<OpenLayers.Pixel>} For layers with a gutter, the image offset
163     *     represents displacement due to the gutter.
164     */
165    imageOffset: null,
166
167  // OPTIONS
168
169    /**
170     * Property: options
171     * {Object} An optional object whose properties will be set on the layer.
172     *     Any of the layer properties can be set as a property of the options
173     *     object and sent to the constructor when the layer is created.
174     */
175    options: null,
176
177    /**
178     * APIProperty: eventListeners
179     * {Object} If set as an option at construction, the eventListeners
180     *     object will be registered with <OpenLayers.Events.on>.  Object
181     *     structure must be a listeners object as shown in the example for
182     *     the events.on method.
183     */
184    eventListeners: null,
185
186    /**
187     * APIProperty: gutter
188     * {Integer} Determines the width (in pixels) of the gutter around image
189     *     tiles to ignore.  By setting this property to a non-zero value,
190     *     images will be requested that are wider and taller than the tile
191     *     size by a value of 2 x gutter.  This allows artifacts of rendering
192     *     at tile edges to be ignored.  Set a gutter value that is equal to
193     *     half the size of the widest symbol that needs to be displayed.
194     *     Defaults to zero.  Non-tiled layers always have zero gutter.
195     */ 
196    gutter: 0, 
197
198    /**
199     * APIProperty: projection
200     * {<OpenLayers.Projection>} or {<String>} Set in the layer options to
201     *     override the default projection string this layer - also set maxExtent,
202     *     maxResolution, and units if appropriate. Can be either a string or
203     *     an <OpenLayers.Projection> object when created -- will be converted
204     *     to an object when setMap is called if a string is passed. 
205     */
206    projection: null,   
207   
208    /**
209     * APIProperty: units
210     * {String} The layer map units.  Defaults to 'degrees'.  Possible values
211     *     are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
212     */
213    units: null,
214
215    /**
216     * APIProperty: scales
217     * {Array}  An array of map scales in descending order.  The values in the
218     *     array correspond to the map scale denominator.  Note that these
219     *     values only make sense if the display (monitor) resolution of the
220     *     client is correctly guessed by whomever is configuring the
221     *     application.  In addition, the units property must also be set.
222     *     Use <resolutions> instead wherever possible.
223     */
224    scales: null,
225
226    /**
227     * APIProperty: resolutions
228     * {Array} A list of map resolutions (map units per pixel) in descending
229     *     order.  If this is not set in the layer constructor, it will be set
230     *     based on other resolution related properties (maxExtent,
231     *     maxResolution, maxScale, etc.).
232     */
233    resolutions: null,
234   
235    /**
236     * APIProperty: maxExtent
237     * {<OpenLayers.Bounds>}  The center of these bounds will not stray outside
238     *     of the viewport extent during panning.  In addition, if
239     *     <displayOutsideMaxExtent> is set to false, data will not be
240     *     requested that falls completely outside of these bounds.
241     */
242    maxExtent: null,
243   
244    /**
245     * APIProperty: minExtent
246     * {<OpenLayers.Bounds>}
247     */
248    minExtent: null,
249   
250    /**
251     * APIProperty: maxResolution
252     * {Float} Default max is 360 deg / 256 px, which corresponds to
253     *     zoom level 0 on gmaps.  Specify a different value in the layer
254     *     options if you are not using a geographic projection and
255     *     displaying the whole world.
256     */
257    maxResolution: null,
258
259    /**
260     * APIProperty: minResolution
261     * {Float}
262     */
263    minResolution: null,
264
265    /**
266     * APIProperty: numZoomLevels
267     * {Integer}
268     */
269    numZoomLevels: null,
270   
271    /**
272     * APIProperty: minScale
273     * {Float}
274     */
275    minScale: null,
276   
277    /**
278     * APIProperty: maxScale
279     * {Float}
280     */
281    maxScale: null,
282
283    /**
284     * APIProperty: displayOutsideMaxExtent
285     * {Boolean} Request map tiles that are completely outside of the max
286     *     extent for this layer. Defaults to false.
287     */
288    displayOutsideMaxExtent: false,
289
290    /**
291     * APIProperty: wrapDateLine
292     * {Boolean} #487 for more info.   
293     */
294    wrapDateLine: false,
295   
296    /**
297     * APIProperty: transitionEffect
298     * {String} The transition effect to use when the map is panned or
299     *     zoomed. 
300     *
301     * There are currently two supported values:
302     *  - *null* No transition effect (the default).
303     *  - *resize*  Existing tiles are resized on zoom to provide a visual
304     *    effect of the zoom having taken place immediately.  As the
305     *    new tiles become available, they are drawn over top of the
306     *    resized tiles.
307     */
308    transitionEffect: null,
309   
310    /**
311     * Property: SUPPORTED_TRANSITIONS
312     * {Array} An immutable (that means don't change it!) list of supported
313     *     transitionEffect values.
314     */
315    SUPPORTED_TRANSITIONS: ['resize'],
316
317    /**
318     * Property: metadata
319     * {Object} This object can be used to store additional information on a
320     *     layer object.
321     */
322    metadata: {},
323   
324    /**
325     * Constructor: OpenLayers.Layer
326     *
327     * Parameters:
328     * name - {String} The layer name
329     * options - {Object} Hashtable of extra options to tag onto the layer
330     */
331    initialize: function(name, options) {
332
333        this.addOptions(options);
334
335        this.name = name;
336       
337        if (this.id == null) {
338
339            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
340
341            this.div = OpenLayers.Util.createDiv(this.id);
342            this.div.style.width = "100%";
343            this.div.style.height = "100%";
344            this.div.dir = "ltr";
345
346            this.events = new OpenLayers.Events(this, this.div, 
347                                                this.EVENT_TYPES);
348            if(this.eventListeners instanceof Object) {
349                this.events.on(this.eventListeners);
350            }
351
352        }
353
354        if (this.wrapDateLine) {
355            this.displayOutsideMaxExtent = true;
356        }
357    },
358   
359    /**
360     * Method: destroy
361     * Destroy is a destructor: this is to alleviate cyclic references which
362     *     the Javascript garbage cleaner can not take care of on its own.
363     *
364     * Parameters:
365     * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
366     *     been destroyed.  Default is true.
367     */
368    destroy: function(setNewBaseLayer) {
369        if (setNewBaseLayer == null) {
370            setNewBaseLayer = true;
371        }
372        if (this.map != null) {
373            this.map.removeLayer(this, setNewBaseLayer);
374        }
375        this.projection = null;
376        this.map = null;
377        this.name = null;
378        this.div = null;
379        this.options = null;
380
381        if (this.events) {
382            if(this.eventListeners) {
383                this.events.un(this.eventListeners);
384            }
385            this.events.destroy();
386        }
387        this.eventListeners = null;
388        this.events = null;
389    },
390   
391   /**
392    * Method: clone
393    *
394    * Parameters:
395    * obj - {<OpenLayers.Layer>} The layer to be cloned
396    *
397    * Returns:
398    * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
399    */
400    clone: function (obj) {
401       
402        if (obj == null) {
403            obj = new OpenLayers.Layer(this.name, this.getOptions());
404        }
405       
406        // catch any randomly tagged-on properties
407        OpenLayers.Util.applyDefaults(obj, this);
408       
409        // a cloned layer should never have its map property set
410        //  because it has not been added to a map yet.
411        obj.map = null;
412       
413        return obj;
414    },
415   
416    /**
417     * Method: getOptions
418     * Extracts an object from the layer with the properties that were set as
419     *     options, but updates them with the values currently set on the
420     *     instance.
421     *
422     * Returns:
423     * {Object} the <options> of the layer, representing the current state.
424     */
425    getOptions: function() {
426        var options = {};
427        for(var o in this.options) {
428            options[o] = this[o];
429        }
430        return options;
431    },
432   
433    /**
434     * APIMethod: setName
435     * Sets the new layer name for this layer.  Can trigger a changelayer event
436     *     on the map.
437     *
438     * Parameters:
439     * newName - {String} The new name.
440     */
441    setName: function(newName) {
442        if (newName != this.name) {
443            this.name = newName;
444            if (this.map != null) {
445                this.map.events.triggerEvent("changelayer", {
446                    layer: this,
447                    property: "name"
448                });
449            }
450        }
451    },   
452   
453   /**
454    * APIMethod: addOptions
455    *
456    * Parameters:
457    * newOptions - {Object}
458    */
459    addOptions: function (newOptions) {
460
461        if (this.options == null) {
462            this.options = {};
463        }
464
465        // update our copy for clone
466        OpenLayers.Util.extend(this.options, newOptions);
467
468        // add new options to this
469        OpenLayers.Util.extend(this, newOptions);
470
471        // make sure this.projection references a projection object
472        if(typeof this.projection == "string") {
473            this.projection = new OpenLayers.Projection(this.projection);
474        }
475
476        // get the units from the projection, if we have a projection
477        // and it it has units
478        if(this.projection && this.projection.getUnits()) {
479            this.units = this.projection.getUnits();
480        }
481
482        // re-initialize resolutions if necessary, i.e. if any of the
483        // properties of the "properties" array defined below is set
484        // in the new options
485        if(this.map) {
486            var properties = this.RESOLUTION_PROPERTIES.concat(
487                ["projection", "units", "minExtent", "maxExtent"]
488            );
489            for(var o in newOptions) {
490                if(newOptions.hasOwnProperty(o) &&
491                   OpenLayers.Util.indexOf(properties, o) >= 0) {
492
493                    this.initResolutions();
494                    break;
495                }
496            }
497        }
498    },
499
500    /**
501     * APIMethod: onMapResize
502     * This function can be implemented by subclasses
503     */
504    onMapResize: function() {
505        //this function can be implemented by subclasses 
506    },
507
508    /**
509     * APIMethod: redraw
510     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
511     *
512     * Returns:
513     * {Boolean} The layer was redrawn.
514     */
515    redraw: function() {
516        var redrawn = false;
517        if (this.map) {
518
519            // min/max Range may have changed
520            this.inRange = this.calculateInRange();
521
522            // map's center might not yet be set
523            var extent = this.getExtent();
524
525            if (extent && this.inRange && this.visibility) {
526                var zoomChanged = true;
527                this.moveTo(extent, zoomChanged, false);
528                this.events.triggerEvent("moveend",
529                    {"zoomChanged": zoomChanged});
530                redrawn = true;
531            }
532        }
533        return redrawn;
534    },
535
536    /**
537     * Method: moveTo
538     *
539     * Parameters:
540     * bound - {<OpenLayers.Bounds>}
541     * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
542     *     do some init work in that case.
543     * dragging - {Boolean}
544     */
545    moveTo:function(bounds, zoomChanged, dragging) {
546        var display = this.visibility;
547        if (!this.isBaseLayer) {
548            display = display && this.inRange;
549        }
550        this.display(display);
551    },
552
553    /**
554     * Method: setMap
555     * Set the map property for the layer. This is done through an accessor
556     *     so that subclasses can override this and take special action once
557     *     they have their map variable set.
558     *
559     *     Here we take care to bring over any of the necessary default
560     *     properties from the map.
561     *
562     * Parameters:
563     * map - {<OpenLayers.Map>}
564     */
565    setMap: function(map) {
566        if (this.map == null) {
567       
568            this.map = map;
569           
570            // grab some essential layer data from the map if it hasn't already
571            //  been set
572            this.maxExtent = this.maxExtent || this.map.maxExtent;
573            this.minExtent = this.minExtent || this.map.minExtent;
574
575            this.projection = this.projection || this.map.projection;
576            if (typeof this.projection == "string") {
577                this.projection = new OpenLayers.Projection(this.projection);
578            }
579
580            // Check the projection to see if we can get units -- if not, refer
581            // to properties.
582            this.units = this.projection.getUnits() ||
583                         this.units || this.map.units;
584           
585            this.initResolutions();
586           
587            if (!this.isBaseLayer) {
588                this.inRange = this.calculateInRange();
589                var show = ((this.visibility) && (this.inRange));
590                this.div.style.display = show ? "" : "none";
591            }
592           
593            // deal with gutters
594            this.setTileSize();
595        }
596    },
597   
598    /**
599     * Method: afterAdd
600     * Called at the end of the map.addLayer sequence.  At this point, the map
601     *     will have a base layer.  To be overridden by subclasses.
602     */
603    afterAdd: function() {
604    },
605   
606    /**
607     * APIMethod: removeMap
608     * Just as setMap() allows each layer the possibility to take a
609     *     personalized action on being added to the map, removeMap() allows
610     *     each layer to take a personalized action on being removed from it.
611     *     For now, this will be mostly unused, except for the EventPane layer,
612     *     which needs this hook so that it can remove the special invisible
613     *     pane.
614     *
615     * Parameters:
616     * map - {<OpenLayers.Map>}
617     */
618    removeMap: function(map) {
619        //to be overridden by subclasses
620    },
621   
622    /**
623     * APIMethod: getImageSize
624     *
625     * Parameters:
626     * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used
627     *     by subclasses that have to deal with different tile sizes at the
628     *     layer extent edges (e.g. Zoomify)
629     *
630     * Returns:
631     * {<OpenLayers.Size>} The size that the image should be, taking into
632     *     account gutters.
633     */ 
634    getImageSize: function(bounds) { 
635        return (this.imageSize || this.tileSize); 
636    },   
637 
638    /**
639     * APIMethod: setTileSize
640     * Set the tile size based on the map size.  This also sets layer.imageSize
641     *     and layer.imageOffset for use by Tile.Image.
642     *
643     * Parameters:
644     * size - {<OpenLayers.Size>}
645     */
646    setTileSize: function(size) {
647        var tileSize = (size) ? size :
648                                ((this.tileSize) ? this.tileSize :
649                                                   this.map.getTileSize());
650        this.tileSize = tileSize;
651        if(this.gutter) {
652          // layers with gutters need non-null tile sizes
653          //if(tileSize == null) {
654          //    OpenLayers.console.error("Error in layer.setMap() for " +
655          //                              this.name + ": layers with " +
656          //                              "gutters need non-null tile sizes");
657          //}
658            this.imageOffset = new OpenLayers.Pixel(-this.gutter, 
659                                                    -this.gutter); 
660            this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter), 
661                                                 tileSize.h + (2*this.gutter)); 
662        }
663    },
664
665    /**
666     * APIMethod: getVisibility
667     *
668     * Returns:
669     * {Boolean} The layer should be displayed (if in range).
670     */
671    getVisibility: function() {
672        return this.visibility;
673    },
674
675    /**
676     * APIMethod: setVisibility
677     * Set the visibility flag for the layer and hide/show & redraw
678     *     accordingly. Fire event unless otherwise specified
679     *
680     * Note that visibility is no longer simply whether or not the layer's
681     *     style.display is set to "block". Now we store a 'visibility' state
682     *     property on the layer class, this allows us to remember whether or
683     *     not we *desire* for a layer to be visible. In the case where the
684     *     map's resolution is out of the layer's range, this desire may be
685     *     subverted.
686     *
687     * Parameters:
688     * visible - {Boolean} Whether or not to display the layer (if in range)
689     */
690    setVisibility: function(visibility) {
691        if (visibility != this.visibility) {
692            this.visibility = visibility;
693            this.display(visibility);
694            this.redraw();
695            if (this.map != null) {
696                this.map.events.triggerEvent("changelayer", {
697                    layer: this,
698                    property: "visibility"
699                });
700            }
701            this.events.triggerEvent("visibilitychanged");
702        }
703    },
704
705    /**
706     * APIMethod: display
707     * Hide or show the Layer
708     *
709     * Parameters:
710     * display - {Boolean}
711     */
712    display: function(display) {
713        if (display != (this.div.style.display != "none")) {
714            this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
715        }
716    },
717
718    /**
719     * APIMethod: calculateInRange
720     *
721     * Returns:
722     * {Boolean} The layer is displayable at the current map's current
723     *     resolution. Note that if 'alwaysInRange' is true for the layer,
724     *     this function will always return true.
725     */
726    calculateInRange: function() {
727        var inRange = false;
728
729        if (this.alwaysInRange) {
730            inRange = true;
731        } else {
732            if (this.map) {
733                var resolution = this.map.getResolution();
734                inRange = ( (resolution >= this.minResolution) &&
735                            (resolution <= this.maxResolution) );
736            }
737        }
738        return inRange;
739    },
740
741    /**
742     * APIMethod: setIsBaseLayer
743     *
744     * Parameters:
745     * isBaseLayer - {Boolean}
746     */
747    setIsBaseLayer: function(isBaseLayer) {
748        if (isBaseLayer != this.isBaseLayer) {
749            this.isBaseLayer = isBaseLayer;
750            if (this.map != null) {
751                this.map.events.triggerEvent("changebaselayer", {
752                    layer: this
753                });
754            }
755        }
756    },
757
758  /********************************************************/
759  /*                                                      */
760  /*                 Baselayer Functions                  */
761  /*                                                      */
762  /********************************************************/
763 
764    /**
765     * Method: initResolutions
766     * This method's responsibility is to set up the 'resolutions' array
767     *     for the layer -- this array is what the layer will use to interface
768     *     between the zoom levels of the map and the resolution display
769     *     of the layer.
770     *
771     * The user has several options that determine how the array is set up.
772     * 
773     * For a detailed explanation, see the following wiki from the
774     *     openlayers.org homepage:
775     *     http://trac.openlayers.org/wiki/SettingZoomLevels
776     */
777    initResolutions: function() {
778
779        // ok we want resolutions, here's our strategy:
780        //
781        // 1. if resolutions are defined in the layer config, use them
782        // 2. else, if scales are defined in the layer config then derive
783        //    resolutions from these scales
784        // 3. else, attempt to calculate resolutions from maxResolution,
785        //    minResolution, numZoomLevels, maxZoomLevel set in the
786        //    layer config
787        // 4. if we still don't have resolutions, and if resolutions
788        //    are defined in the same, use them
789        // 5. else, if scales are defined in the map then derive
790        //    resolutions from these scales
791        // 6. else, attempt to calculate resolutions from maxResolution,
792        //    minResolution, numZoomLevels, maxZoomLevel set in the
793        //    map
794        // 7. hope for the best!
795
796        var i, len;
797        var props = {}, alwaysInRange = true;
798
799        // get resolution data from layer config
800        // (we also set alwaysInRange in the layer as appropriate)
801        for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
802            var p = this.RESOLUTION_PROPERTIES[i];
803            props[p] = this.options[p];
804            if(alwaysInRange && this.options[p]) {
805                alwaysInRange = false;
806            }
807        }
808        if(this.alwaysInRange == null) {
809            this.alwaysInRange = alwaysInRange;
810        }
811
812        // if we don't have resolutions then attempt to derive them from scales
813        if(props.resolutions == null) {
814            props.resolutions = this.resolutionsFromScales(props.scales);
815        }
816
817        // if we still don't have resolutions then attempt to calculate them
818        if(props.resolutions == null) {
819            props.resolutions = this.calculateResolutions(props);
820        }
821
822        // if we couldn't calculate resolutions then we look at we have
823        // in the map
824        if(props.resolutions == null) {
825            for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
826                var p = this.RESOLUTION_PROPERTIES[i];
827                props[p] = this.options[p] != null ?
828                    this.options[p] : this.map[p];
829            }
830            if(props.resolutions == null) {
831                props.resolutions = this.resolutionsFromScales(props.scales);
832            }
833            if(props.resolutions == null) {
834                props.resolutions = this.calculateResolutions(props);
835            }
836        }
837
838        // ok, we new need to set properties in the instance
839
840        // get maxResolution from the config if it's defined there
841        var maxResolution;
842        if(this.options.maxResolution &&
843           this.options.maxResolution !== "auto") {
844            maxResolution = this.options.maxResolution;
845        }
846        if(this.options.minScale) {
847            maxResolution = OpenLayers.Util.getResolutionFromScale(
848                this.options.minScale, this.units);
849        }
850
851        // get minResolution from the config if it's defined there
852        var minResolution;
853        if(this.options.minResolution &&
854           this.options.minResolution !== "auto") {
855            minResolution = this.options.minResolution;
856        }
857        if(this.options.maxScale) {
858            minResolution = OpenLayers.Util.getResolutionFromScale(
859                this.options.maxScale, this.units);
860        }
861
862        if(props.resolutions) {
863
864            //sort resolutions array descendingly
865            props.resolutions.sort(function(a, b) {
866                return (b - a);
867            });
868
869            // if we still don't have a maxResolution get it from the
870            // resolutions array
871            if(!maxResolution) {
872                maxResolution = props.resolutions[0];
873            }
874
875            // if we still don't have a minResolution get it from the
876            // resolutions array
877            if(!minResolution) {
878                var lastIdx = props.resolutions.length - 1;
879                minResolution = props.resolutions[lastIdx];
880            }
881        }
882
883        this.resolutions = props.resolutions;
884        if(this.resolutions) {
885            len = this.resolutions.length;
886            this.scales = new Array(len);
887            for(i=0; i<len; i++) {
888                this.scales[i] = OpenLayers.Util.getScaleFromResolution(
889                    this.resolutions[i], this.units);
890            }
891            this.numZoomLevels = len;
892        }
893        this.minResolution = minResolution;
894        if(minResolution) {
895            this.maxScale = OpenLayers.Util.getScaleFromResolution(
896                minResolution, this.units);
897        }
898        this.maxResolution = maxResolution;
899        if(maxResolution) {
900            this.minScale = OpenLayers.Util.getScaleFromResolution(
901                maxResolution, this.units);
902        }
903    },
904
905    /**
906     * Method: resolutionsFromScales
907     * Derive resolutions from scales.
908     *
909     * Parameters:
910     * scales - {Array(Number)} Scales
911     *
912     * Returns
913     * {Array(Number)} Resolutions
914     */
915    resolutionsFromScales: function(scales) {
916        if(scales == null) {
917            return;
918        }
919        var resolutions, i, len;
920        len = scales.length;
921        resolutions = new Array(len);
922        for(i=0; i<len; i++) {
923            resolutions[i] = OpenLayers.Util.getResolutionFromScale(
924                scales[i], this.units);
925        }
926        return resolutions;
927    },
928
929    /**
930     * Method: calculateResolutions
931     * Calculate resolutions based on the provided properties.
932     *
933     * Parameters:
934     * props - {Object} Properties
935     *
936     * Return:
937     * {Array({Number})} Array of resolutions.
938     */
939    calculateResolutions: function(props) {
940
941        // determine maxResolution
942        var maxResolution = props.maxResolution;
943        if(props.minScale != null) {
944            maxResolution =
945                OpenLayers.Util.getResolutionFromScale(props.minScale,
946                                                       this.units);
947        } else if(maxResolution == "auto" && this.maxExtent != null) {
948            var viewSize = this.map.getSize();
949            var wRes = this.maxExtent.getWidth() / viewSize.w;
950            var hRes = this.maxExtent.getHeight() / viewSize.h;
951            maxResolution = Math.max(wRes, hRes);
952        }
953
954        // determine minResolution
955        var minResolution = props.minResolution;
956        if(props.maxScale != null) {
957            minResolution =
958                OpenLayers.Util.getResolutionFromScale(props.maxScale,
959                                                       this.units);
960        } else if(props.minResolution == "auto" && this.minExtent != null) {
961            var viewSize = this.map.getSize();
962            var wRes = this.minExtent.getWidth() / viewSize.w;
963            var hRes = this.minExtent.getHeight()/ viewSize.h;
964            minResolution = Math.max(wRes, hRes);
965        }
966
967        // determine numZoomLevels
968        var maxZoomLevel = props.maxZoomLevel;
969        var numZoomLevels = props.numZoomLevels;
970        if(typeof minResolution === "number" &&
971           typeof maxResolution === "number" && numZoomLevels === undefined) {
972            var ratio = maxResolution / minResolution;
973            numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
974        } else if(numZoomLevels === undefined && maxZoomLevel != null) {
975            numZoomLevels = maxZoomLevel + 1;
976        }
977
978        // are we able to calculate resolutions?
979        if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 ||
980           (typeof maxResolution !== "number" &&
981                typeof minResolution !== "number")) {
982            return;
983        }
984
985        // now we have numZoomLevels and at least one of maxResolution
986        // or minResolution, we can populate the resolutions array
987
988        var resolutions = new Array(numZoomLevels);
989        var base = 2;
990        if(typeof minResolution == "number" &&
991           typeof maxResolution == "number") {
992            // if maxResolution and minResolution are set, we calculate
993            // the base for exponential scaling that starts at
994            // maxResolution and ends at minResolution in numZoomLevels
995            // steps.
996            base = Math.pow(
997                    (maxResolution / minResolution),
998                (1 / (numZoomLevels - 1))
999            );
1000        }
1001
1002        var i;
1003        if(typeof maxResolution === "number") {
1004            for(i=0; i<numZoomLevels; i++) {
1005                resolutions[i] = maxResolution / Math.pow(base, i);
1006            }
1007        } else {
1008            for(i=0; i<numZoomLevels; i++) {
1009                resolutions[numZoomLevels - 1 - i] =
1010                    minResolution * Math.pow(base, i);
1011            }
1012        }
1013
1014        return resolutions;
1015    },
1016
1017    /**
1018     * APIMethod: getResolution
1019     *
1020     * Returns:
1021     * {Float} The currently selected resolution of the map, taken from the
1022     *     resolutions array, indexed by current zoom level.
1023     */
1024    getResolution: function() {
1025        var zoom = this.map.getZoom();
1026        return this.getResolutionForZoom(zoom);
1027    },
1028
1029    /**
1030     * APIMethod: getExtent
1031     *
1032     * Returns:
1033     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
1034     *     bounds of the current viewPort.
1035     */
1036    getExtent: function() {
1037        // just use stock map calculateBounds function -- passing no arguments
1038        //  means it will user map's current center & resolution
1039        //
1040        return this.map.calculateBounds();
1041    },
1042
1043    /**
1044     * APIMethod: getZoomForExtent
1045     *
1046     * Parameters:
1047     * bounds - {<OpenLayers.Bounds>}
1048     * closest - {Boolean} Find the zoom level that most closely fits the
1049     *     specified bounds. Note that this may result in a zoom that does
1050     *     not exactly contain the entire extent.
1051     *     Default is false.
1052     *
1053     * Returns:
1054     * {Integer} The index of the zoomLevel (entry in the resolutions array)
1055     *     for the passed-in extent. We do this by calculating the ideal
1056     *     resolution for the given extent (based on the map size) and then
1057     *     calling getZoomForResolution(), passing along the 'closest'
1058     *     parameter.
1059     */
1060    getZoomForExtent: function(extent, closest) {
1061        var viewSize = this.map.getSize();
1062        var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
1063                                        extent.getHeight() / viewSize.h );
1064
1065        return this.getZoomForResolution(idealResolution, closest);
1066    },
1067   
1068    /**
1069     * Method: getDataExtent
1070     * Calculates the max extent which includes all of the data for the layer.
1071     *     This function is to be implemented by subclasses.
1072     *
1073     * Returns:
1074     * {<OpenLayers.Bounds>}
1075     */
1076    getDataExtent: function () {
1077        //to be implemented by subclasses
1078    },
1079
1080    /**
1081     * APIMethod: getResolutionForZoom
1082     *
1083     * Parameter:
1084     * zoom - {Float}
1085     *
1086     * Returns:
1087     * {Float} A suitable resolution for the specified zoom.
1088     */
1089    getResolutionForZoom: function(zoom) {
1090        zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
1091        var resolution;
1092        if(this.map.fractionalZoom) {
1093            var low = Math.floor(zoom);
1094            var high = Math.ceil(zoom);
1095            resolution = this.resolutions[low] -
1096                ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
1097        } else {
1098            resolution = this.resolutions[Math.round(zoom)];
1099        }
1100        return resolution;
1101    },
1102
1103    /**
1104     * APIMethod: getZoomForResolution
1105     *
1106     * Parameters:
1107     * resolution - {Float}
1108     * closest - {Boolean} Find the zoom level that corresponds to the absolute
1109     *     closest resolution, which may result in a zoom whose corresponding
1110     *     resolution is actually smaller than we would have desired (if this
1111     *     is being called from a getZoomForExtent() call, then this means that
1112     *     the returned zoom index might not actually contain the entire
1113     *     extent specified... but it'll be close).
1114     *     Default is false.
1115     *
1116     * Returns:
1117     * {Integer} The index of the zoomLevel (entry in the resolutions array)
1118     *     that corresponds to the best fit resolution given the passed in
1119     *     value and the 'closest' specification.
1120     */
1121    getZoomForResolution: function(resolution, closest) {
1122        var zoom;
1123        if(this.map.fractionalZoom) {
1124            var lowZoom = 0;
1125            var highZoom = this.resolutions.length - 1;
1126            var highRes = this.resolutions[lowZoom];
1127            var lowRes = this.resolutions[highZoom];
1128            var res;
1129            for(var i=0, len=this.resolutions.length; i<len; ++i) {
1130                res = this.resolutions[i];
1131                if(res >= resolution) {
1132                    highRes = res;
1133                    lowZoom = i;
1134                }
1135                if(res <= resolution) {
1136                    lowRes = res;
1137                    highZoom = i;
1138                    break;
1139                }
1140            }
1141            var dRes = highRes - lowRes;
1142            if(dRes > 0) {
1143                zoom = lowZoom + ((highRes - resolution) / dRes);
1144            } else {
1145                zoom = lowZoom;
1146            }
1147        } else {
1148            var diff;
1149            var minDiff = Number.POSITIVE_INFINITY;
1150            for(var i=0, len=this.resolutions.length; i<len; i++) {           
1151                if (closest) {
1152                    diff = Math.abs(this.resolutions[i] - resolution);
1153                    if (diff > minDiff) {
1154                        break;
1155                    }
1156                    minDiff = diff;
1157                } else {
1158                    if (this.resolutions[i] < resolution) {
1159                        break;
1160                    }
1161                }
1162            }
1163            zoom = Math.max(0, i-1);
1164        }
1165        return zoom;
1166    },
1167   
1168    /**
1169     * APIMethod: getLonLatFromViewPortPx
1170     *
1171     * Parameters:
1172     * viewPortPx - {<OpenLayers.Pixel>}
1173     *
1174     * Returns:
1175     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in
1176     *     view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
1177     */
1178    getLonLatFromViewPortPx: function (viewPortPx) {
1179        var lonlat = null;
1180        if (viewPortPx != null) {
1181            var size = this.map.getSize();
1182            var center = this.map.getCenter();
1183            if (center) {
1184                var res  = this.map.getResolution();
1185       
1186                var delta_x = viewPortPx.x - (size.w / 2);
1187                var delta_y = viewPortPx.y - (size.h / 2);
1188           
1189                lonlat = new OpenLayers.LonLat(center.lon + delta_x * res ,
1190                                             center.lat - delta_y * res); 
1191
1192                if (this.wrapDateLine) {
1193                    lonlat = lonlat.wrapDateLine(this.maxExtent);
1194                }
1195            } // else { DEBUG STATEMENT }
1196        }
1197        return lonlat;
1198    },
1199
1200    /**
1201     * APIMethod: getViewPortPxFromLonLat
1202     * Returns a pixel location given a map location.  This method will return
1203     *     fractional pixel values.
1204     *
1205     * Parameters:
1206     * lonlat - {<OpenLayers.LonLat>}
1207     *
1208     * Returns:
1209     * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
1210     *     <OpenLayers.LonLat>,translated into view port pixels.
1211     */
1212    getViewPortPxFromLonLat: function (lonlat) {
1213        var px = null; 
1214        if (lonlat != null) {
1215            var resolution = this.map.getResolution();
1216            var extent = this.map.getExtent();
1217            px = new OpenLayers.Pixel(
1218                (1/resolution * (lonlat.lon - extent.left)),
1219                (1/resolution * (extent.top - lonlat.lat))
1220            );   
1221        }
1222        return px;
1223    },
1224   
1225    /**
1226     * APIMethod: setOpacity
1227     * Sets the opacity for the entire layer (all images)
1228     *
1229     * Parameter:
1230     * opacity - {Float}
1231     */
1232    setOpacity: function(opacity) {
1233        if (opacity != this.opacity) {
1234            this.opacity = opacity;
1235            for(var i=0, len=this.div.childNodes.length; i<len; ++i) {
1236                var element = this.div.childNodes[i].firstChild;
1237                OpenLayers.Util.modifyDOMElement(element, null, null, null, 
1238                                                 null, null, null, opacity);
1239            }
1240            if (this.map != null) {
1241                this.map.events.triggerEvent("changelayer", {
1242                    layer: this,
1243                    property: "opacity"
1244                });
1245            }
1246        }
1247    },
1248
1249    /**
1250     * Method: getZIndex
1251     *
1252     * Returns:
1253     * {Integer} the z-index of this layer
1254     */   
1255    getZIndex: function () {
1256        return this.div.style.zIndex;
1257    },
1258
1259    /**
1260     * Method: setZIndex
1261     *
1262     * Parameters:
1263     * zIndex - {Integer}
1264     */   
1265    setZIndex: function (zIndex) {
1266        this.div.style.zIndex = zIndex;
1267    },
1268
1269    /**
1270     * Method: adjustBounds
1271     * This function will take a bounds, and if wrapDateLine option is set
1272     *     on the layer, it will return a bounds which is wrapped around the
1273     *     world. We do not wrap for bounds which *cross* the
1274     *     maxExtent.left/right, only bounds which are entirely to the left
1275     *     or entirely to the right.
1276     *
1277     * Parameters:
1278     * bounds - {<OpenLayers.Bounds>}
1279     */
1280    adjustBounds: function (bounds) {
1281
1282        if (this.gutter) {
1283            // Adjust the extent of a bounds in map units by the
1284            // layer's gutter in pixels.
1285            var mapGutter = this.gutter * this.map.getResolution();
1286            bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
1287                                           bounds.bottom - mapGutter,
1288                                           bounds.right + mapGutter,
1289                                           bounds.top + mapGutter);
1290        }
1291
1292        if (this.wrapDateLine) {
1293            // wrap around the date line, within the limits of rounding error
1294            var wrappingOptions = { 
1295                'rightTolerance':this.getResolution()
1296            };   
1297            bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
1298                             
1299        }
1300        return bounds;
1301    },
1302
1303    CLASS_NAME: "OpenLayers.Layer"
1304});
Note: See TracBrowser for help on using the repository browser.