[OpenLayers-Dev] A kind of MultiLayer container

RICHARD Didier didier.richard at ign.fr
Tue Aug 4 04:32:40 EDT 2009


> On Tue, Aug 4, 2009 at 6:04 AM, Eric Lemoine<eric.lemoine at camptocamp.com>
> wrote:
>> On Monday, August 3, 2009, Ivan Grcic <ivan.grcic at geofoto.hr> wrote:
>>> Hi devs,
>>>
>>> I want to ask if anyone has made something like Vector/RootContainer,
>>> a layer that would contain several layers, and every layer would
>>> activate/deactivate on different zoomLevels.
>>>
>>> Simple case: 3 (or more) layers combined in one ContainerLayer
>>> small scale: WMS raster layer
>>> larger scales: other WMS  raster layer
>>> large scale: WFS vector layer
>>>
>>>  Usually I always register zoomend event on map, and manually activate
>>> or deactivate layers...but it got little bit boring to do that on
>>> application level every time.
>>>
>>> I remember Alexandre did something like that but for Vector layer
>>>  or http://openlayers.org/pipermail/users/2009-January/009666.html
>>>
>>> Anyone did something like that? (does mapfish maybe has soemthing like
>>> that)
>>>  If not, can anyone just give few tips, so I could start developing
>>> it...
>>
>>
>> Hi Ivan
>>
>> Can't you just set different resolution ranges in the layers?
>>
>> Cheers,
>>
>>
> Tnx for replies guys,
>
>
> Yes yes, im setting ranges for my layers, but I want to have only one
> layer shown in layerSwitcher for group of layers.
>
> I guess I just have to create one dummy layer with inLayerSwitcher:
> true, and other layers to false. And then just register activate,
> deactivate events that will turn off/on all group layers. (as alex
> suggested, tnx)
>
> I was just wondering if it would have sense to have it included in
> API, not to do it manually all the time...
> Cheers
>

Hi all,

For our portal API, we have develop something really close :


/*
 * Copyright 2008-2009 Institut Geographique National France, released
under the
 * BSD license.
 */
/**
 * Class: Geoportal.Layer.Aggregate
 * A container of layers. The idea is to group layers to avoid big list to be
 * displayed in the LayerSwitcher. This aggregation also helps in grouping
 * spread OGC services into a compound one.
 *      This feature is experimental.
 *
 * Inherits from:
 * - {<OpenLayers.Layer>}
 */
Geoportal.Layer.Aggregate=
    OpenLayers.Class( Geoportal.Layer, {

    /**
     * APIProperty: layers
     * {Array(<Geoportal.Layer>)} Ordered list of layers in the aggregation.
     */
    layers: null,

    /**
     * Constructor: Geoportal.Layer.Aggregate
     * Build an aggregation of layers.
     *
     * Parameters:
     * name - {String} The aggregation name.
     * layers - {Array(<Geoportal.Layer>)} Ordered list of layers to push
into
     *      the aggregation.
     * options - {Object} Hash table of options.
     */
    initialize: function(name, layers, options) {
        Geoportal.Layer.prototype.initialize.apply(this,[name, options]);
        this.layers= [];
        this.addLayers(layers);
    },

    /**
     * APIMethod: destroy
     * Clean up the compound layer.
     *
     * Parameters:
     * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
     *     been destroyed.  Default is true.
     */
    destroy: function(setNewBaseLayer) {
        if (this.map) {
            this.map.events.unregister("removelayer",this,this.remove);
        }
        if (this.layers) {
            for (var i= 0, len= this.layers.length; i<len; i++) {
                this.layers[i].aggregate= null;
            }
            this.layers= null;
        }
        Geoportal.Layer.prototype.destroy.apply(this,arguments);
    },

    /**
     * Method: remove
     * Remove the aggregation from the map.
     *
     * Parameters:
     * evt - {Event}
     */
    remove: function(evt) {
        if (evt.layer != this) { return; }
        if (this.layers) {
            var layer;
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                layer.map.removeLayer(this.layers[i],false);
            }
        }
    },

    /**
     * Method: clone
     *
     * Returns:
     * {<Geoportal.Layer>} An exact clone of this <Geoportal.Layer>
     */
    clone: function() {
        var obj= Geoportal.Layer.prototype.clone.apply(this,arguments);
        // FIXME: a layer may belong to multiple aggregates ?
        obj.addLayers(this.layers);
    },

    /**
     * Method: setMap
     * Set the map property for the layer. This is done through an accessor
     *     so that subclasses can override this and take special action once
     *     they have their map variable set.
     *
     *     Here we take care to bring over any of the necessary default
     *     properties from the map.
     *
     * Parameters:
     * map - {<OpenLayers.Map>}
     */
    setMap: function(map) {
        if (this.map != null) { return; }
        if (!this.layers) { return; }
        var layer;
        for (var i= 0, len= this.layers.length; i<len; i++) {
            layer= this.layers[i];
            if (layer.map == null) {
                map.addLayer(layer);
            }
        }
        var me= this.maxExtent == null;
        Geoportal.Layer.prototype.setMap.apply(this,arguments);
        if (me) {
            this.maxExtent= null;
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                if (i==0) {
                    this.maxExtent= layer.maxExtent.clone();
                } else {
                    this.maxExtent.extend(layer);
                }
            }
        }
        // update layers' z-index :
        this.setZIndex(this.getZIndex());
        this.map.events.register("removelayer",this,this.remove);
    },

    /**
     * APIMethod: removeMap
     * The layer has been removed from the map.
     *
     * Parameters:
     * map - {<OpenLayers.Map>}
     */
    removeMap: function(map) {
        if (this.map == null) { return; }
        if (this.layers) {
            var layer;
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                if (layer.map != null) {
                    layer.map.removeLayer(layer);
                }
            }
        }
    },

    /**
     * APIMethod: setVisibility
     * Set the visibility flag for the layer and hide/show & redraw
     *     accordingly. Fire event unless otherwise specified
     *
     * Note that visibility is no longer simply whether or not the layer's
     *     style.display is set to "block". Now we store a 'visibility' state
     *     property on the layer class, this allows us to remember whether or
     *     not we *desire* for a layer to be visible. In the case where the
     *     map's resolution is out of the layer's range, this desire may be
     *     subverted.
     *
     * Parameters:
     * visible - {Boolean} Whether or not to display the layer (if in range)
     */
    setVisibility: function(visibility) {
        if (visibility != this.visibility) {
            this.visibility= visibility;
            Geoportal.Layer.prototype.display.apply(this,arguments);
            if (this.layers && this.map!=null) {
                var layer, lproj;
                for (var i= 0, len= this.layers.length; i<len; i++) {
                    layer= this.layers[i];
                    if (layer.isBaseLayer) { continue; }
                    if (layer.territory!==undefined &&
this.map.baseLayer.territory!==undefined) {
                        if (layer.territory==this.map.baseLayer.territory) {
                            if (visibility) {
                                layer.setVisibility(layer.calculateInRange());
                            } else {
                                layer.setVisibility(false);
                            }
                        }
                        continue;
                    }
                    lproj= layer.getNativeProjection();
                    if (!lproj) { continue; }
                    if
(!(lproj.equals(this.map.baseLayer.nativeProjection)
||
                          (lproj.proj.projName=='longlat' &&
                           lproj.isCompatibleWith(this.map.baseLayer.nativeProjection))))
{ continue; }
                    if (!(layer.restrictedExtent || layer.maxExtent) ||
                          this.map.baseLayer.maxExtent.containsBounds(
                            layer.restrictedExtent ||
layer.maxExtent,true,true)) {
                        if (visibility) {
                            layer.setVisibility(layer.calculateInRange());
                        } else {
                            layer.setVisibility(false);
                        }
                    }
                }
            }
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "visibility"
                });
            }
            this.events.triggerEvent("visibilitychanged");
        }
    },

    /**
     * APIMethod: display
     * Hide or show the Layer.
     *
     * Parameters:
     * display - {Boolean}
     */
    display: function(display) {
        var inRange = this.calculateInRange();
        if (display != (this.div.style.display != "none")) {
            this.div.style.display = (display && inRange) ? "block" : "none";
            if (this.layers) {
                var layer;
                for (var i= 0, len= this.layers.length; i<len; i++) {
                    layer= this.layers[i];
                    layer.display(display);
                }
            }
        }
    },

    /**
     * Method: initResolutions
     * This method's responsibility is to set up the 'resolutions' array
     *     for the layer -- this array is what the layer will use to
interface
     *     between the zoom levels of the map and the resolution display
     *     of the layer.
     *
     * The user has several options that determine how the array is set up.
     *
     * For a detailed explanation, see the following wiki from the
     *     openlayers.org homepage:
     *     http://trac.openlayers.org/wiki/SettingZoomLevels
     */
    initResolutions: function() {
        if (this.layers) {
            var layer;
            var mnz= typeof(this.options.minZoomLevel) == 'number';
            var mxz= typeof(this.options.maxZoomLevel) == 'number';
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                if (i==0) {
                    if (!mnz && typeof(layer.minZoomLevel) == 'number') {
                        this.options.minZoomLevel= layer.minZoomLevel;
                    }
                    if (!mxz && typeof(layer.maxZoomLevel) == 'number') {
                        this.options.maxZoomLevel= layer.maxZoomLevel;
                    }
                } else {
                    if (!mnz && typeof(layer.minZoomLevel) == 'number') {
                        this.options.minZoomLevel=
Math.min(this.options.minZoomLevel,
layer.minZoomLevel);
                    }
                    if (!mxz && typeof(layer.maxZoomLevel) == 'number') {
                        this.options.maxZoomLevel=
Math.max(this.options.maxZoomLevel,
layer.maxZoomLevel);
                    }
                }
            }
        }
        Geoportal.Layer.prototype.initResolutions.apply(this,arguments);
    },

    /**
     * Method: getDataExtent
     * Calculates the max extent which includes all of the data for the
layer.
     *     This function is to be implemented by subclasses.
     *
     * Returns:
     * {<OpenLayers.Bounds>}
     */
    getDataExtent: function () {
        var de= null;
        if (this.layers) {
            var layer;
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                if (i==0) {
                    de= layer.getDataExtent();
                } else {
                    de.extend(layer.getDataExtent());
                }
            }
        }
        return de;
    },

    /**
     * APIMethod: setOpacity
     * Sets the opacity for the entire layer (all images)
     *
     * Parameter:
     * opacity - {Float}
     */
    setOpacity: function(opacity) {
        if (opacity != this.opacity) {
            Geoportal.Layer.prototype.setOpacity.apply(this,arguments);
            if (this.layers) {
                var layer;
                for (var i= 0, len= this.layers.length; i<len; i++) {
                    layer= this.layers[i];
                    layer.setOpacity(opacity);
                }
            }
        }
    },

    /**
     * Method: setZIndex
     * Assign the aggregation z-index.
     *      Aggregated layers have their z-index numbered from the
aggregation's
     *      z-index by decrementing their rank to the base z-index.
     *
     * Parameters:
     * zIndex - {Integer}
     */
    setZIndex: function (zIndex) {
        this.div.style.zIndex = zIndex;
        if (this.layers) {
            var layer;
            for (var i= 0, len= this.layers.length; i<len; i++) {
                layer= this.layers[i];
                // sub-layers are added before the aggregation to the map
                // the first sub-layer has z-index Z_INDEX_BASE['Overlay']
+ rank * 5
                // the last sub-layer's z-index is augmented by
layers.length * 5
                // the aggregation's z-index is augmentented by
(layers.length + 1) * 5
                if (layer.div) {
                    // on destroying the map, the sub-layer are destroyed
                    // before the aggregation ... if which case layer's
div is
                    // null !
                    layer.setZIndex(zIndex-i-1);
                }
            }
        }
    },

    /**
     * APIMethod: addLayer
     * Add a layer to an aggregation.
     *
     * Parameters:
     * layer - {<OpenLayers.Layer>}
     */
    addLayer: function(layer) {
        var lyr;
        for (var i= 0, len= this.layers.length; i<len; i++) {
            lyr= this.layers[i];
            if (lyr == layer) {
                var msg= OpenLayers.i18n('layerAlreadyAdded',
{'layerName':layer.name});
                OpenLayers.Console.warn(msg);
                return;
            }
        }
        layer.displayInLayerSwitcher= false;
        layer.visibility= this.visibility;
        // FIXME: a layer may belong to multiple aggregates ?
        layer.aggregate= this;
        this.layers.push(layer);
        if (this.map) {
            if (layer.map == null) {
                this.map.addLayer(layer);
            }
            this.initResolutions();
            this.setZIndex(this.getZIndex());
        }
    },

    /**
     * APIMethod: addLayers
     * Add layers to an aggregation.
     *
     * Parameters:
     * layers - {Array(<OpenLayers.Layer>)}
     */
    addLayers: function(layers) {
        if (!layers) { return; }
        if (!(layers instanceof Array)) {
            this.addLayer(layers);
            return;
        }
        for (var i= 0, len= layers.length; i<len; i++) {
            this.addLayer(layers[i]);
        }
    },

    /**
     * Constant: CLASS_NAME
     * {String} *"Geoportal.Layer.Aggregate"*
     */
    CLASS_NAME:"Geoportal.Layer.Aggregate"
});


Note that Geoportal.Layer is just a synonym of OpenLayers.Layer

Regards,

didier
>
>>>
>>> Tnx&Cheers,
>>>
>>>
>>> --
>>> Ivan Grcic
>>> _______________________________________________
>>> Dev mailing list
>>> Dev at openlayers.org
>>> http://openlayers.org/mailman/listinfo/dev
>>>
>>
>> --
>> Eric Lemoine
>>
>> Camptocamp France SAS
>> Savoie Technolac, BP 352
>> 73377 Le Bourget du Lac, Cedex
>>
>> Tel : 00 33 4 79 44 44 96
>> Mail : eric.lemoine at camptocamp.com
>> http://www.camptocamp.com
>> _______________________________________________
>> Dev mailing list
>> Dev at openlayers.org
>> http://openlayers.org/mailman/listinfo/dev
>>
>
>
>
> --
> Ivan Grcic
> _______________________________________________
> Dev mailing list
> Dev at openlayers.org
> http://openlayers.org/mailman/listinfo/dev
>


-- 
RICHARD Didier - Chef du pôle technique du Géoportail
2/4, avenue Pasteur - 94165 Saint Mandé Cedex
Tél : +33 (0) 1 43 98 83 23



More information about the Dev mailing list