OpenLayers OpenLayers

Ticket #434: mulafese001.patch

File mulafese001.patch, 11.5 kB (added by openlayers, 2 months ago)

A patch enabling selecting features on multiple layers, using geometry intersection tests. Requires the Handler/MultiLayerFeature.js file.

  • lib/OpenLayers/Control/SelectFeature.js

    old new  
    8181    geometryTypes: null, 
    8282 
    8383    /** 
    84      * Property: layer 
    85      * {<OpenLayers.Layer.Vector>
     84     * Property: layers 
     85     * {Array(<OpenLayers.Layer.Vector>)
    8686     */ 
    87     layer: null, 
     87    layers: null, 
    8888     
    8989    /** 
    9090     * APIProperty: callbacks 
     
    118118     * layer - {<OpenLayers.Layer.Vector>}  
    119119     * options - {Object}  
    120120     */ 
    121     initialize: function(layer, options) { 
     121    initialize: function(layers, options) { 
    122122        OpenLayers.Control.prototype.initialize.apply(this, [options]); 
    123         this.layer = layer
     123        this.layers = layers instanceof Array ? layers : [layers]
    124124        this.callbacks = OpenLayers.Util.extend({ 
    125125                                                  click: this.clickFeature, 
    126126                                                  clickout: this.clickoutFeature, 
     
    128128                                                  out: this.outFeature 
    129129                                                }, this.callbacks); 
    130130        var handlerOptions = { geometryTypes: this.geometryTypes}; 
    131         this.handler = new OpenLayers.Handler.Feature(this, layer, 
    132                                                       this.callbacks, 
    133                                                       handlerOptions); 
     131         
     132        // If there's only one layer, using the normal FeatureHandler. 
     133        // For many layers, using MultiLayerFeature handler. 
     134         
     135        // The normal FeatureHandler is better&faster for one layer, 
     136        // because it uses the browser events directly. The problem is that 
     137        // the browser events are triggered only for a layer whose Z value is the largest. 
     138        // The MultiLayer handler does some time consuming and akwardish 
     139        // geometry intersection tests to find the features on each layer. 
     140         
     141        // TBD: The feature handlers might be merged into one that could 
     142        // choose which way to work by itself.  
     143        // Or maybe come up with some other solution... 
     144         
     145        if(this.layers.length == 1) { 
     146            this.handler =  
     147                new OpenLayers.Handler.Feature(this, this.layers[0], 
     148                                                         this.callbacks, 
     149                                                         handlerOptions); 
     150        } 
     151        else { 
     152            this.handler =  
     153                new OpenLayers.Handler.MultiLayerFeature(this, this.layers, 
     154                                               this.callbacks, 
     155                                               handlerOptions); 
     156        } 
     157         
    134158    }, 
    135159 
    136160    /** 
     
    143167     */ 
    144168    unselectAll: function(options) { 
    145169        // we'll want an option to supress notification here 
    146         var feature; 
    147         for(var i=this.layer.selectedFeatures.length-1; i>=0; --i) { 
    148             feature = this.layer.selectedFeatures[i]; 
    149             if(!options || options.except != feature) { 
    150                 this.unselect(feature); 
    151             } 
     170        var feature, layer; 
     171        for(var la=0; la<this.layers.length; ++la) { 
     172            layer = this.layers[la]; 
     173            for(var i=layer.selectedFeatures.length-1; i>=0; --i) { 
     174                feature = layer.selectedFeatures[i]; 
     175                if(!options || options.except != feature) { 
     176                    this.unselect(feature); 
     177                } 
     178            } 
    152179        } 
    153180    }, 
    154181 
     
    162189     */ 
    163190    clickFeature: function(feature) { 
    164191        if(!this.hover) { 
    165             var selected = (OpenLayers.Util.indexOf(this.layer.selectedFeatures, 
    166                                                     feature) > -1); 
     192            var selected = this.featureIsSelected(feature); 
     193             
    167194            if(selected) { 
    168195                if(this.toggleSelect()) { 
    169196                    this.unselect(feature); 
     
    226253     * feature - {<OpenLayers.Feature.Vector>}  
    227254     */ 
    228255    overFeature: function(feature) { 
    229         if(this.hover && 
    230            (OpenLayers.Util.indexOf(this.layer.selectedFeatures, feature) == -1)) { 
     256        if(this.hover && !this.featureIsSelected(feature)) { 
    231257            this.select(feature); 
    232258        } 
    233259    }, 
     
    255281     * feature - {<OpenLayers.Feature.Vector>}  
    256282     */ 
    257283    select: function(feature) { 
    258         this.layer.selectedFeatures.push(feature); 
     284        var layer = feature.layer; 
     285        layer.selectedFeatures.push(feature); 
    259286 
    260287        var selectStyle = this.selectStyle || this.renderIntent; 
    261288         
    262         this.layer.drawFeature(feature, selectStyle); 
    263         this.layer.events.triggerEvent("featureselected", {feature: feature}); 
     289        layer.drawFeature(feature, selectStyle); 
     290        layer.events.triggerEvent("featureselected", {feature: feature}); 
    264291        this.onSelect(feature); 
    265292    }, 
    266293 
     
    274301     */ 
    275302    unselect: function(feature) { 
    276303        // Store feature style for restoration later 
    277         this.layer.drawFeature(feature, "default"); 
    278         OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); 
    279         this.layer.events.triggerEvent("featureunselected", {feature: feature}); 
     304        var layer = feature.layer; 
     305        layer.drawFeature(feature, "default"); 
     306        OpenLayers.Util.removeItem(layer.selectedFeatures, feature); 
     307        layer.events.triggerEvent("featureunselected", {feature: feature}); 
    280308        this.onUnselect(feature); 
    281309    }, 
    282310 
     
    291319        this.handler.setMap(map); 
    292320        OpenLayers.Control.prototype.setMap.apply(this, arguments); 
    293321    }, 
     322     
     323    /**  
     324     * Method: featureIsSelected 
     325     * Convenience method. 
     326     *  
     327     * Parameters: 
     328     * feature - {<OpenLayers.Feature>} 
     329     *  
     330     * Returns: 
     331     * {Boolean} True iff the feature is selected.  
     332     */ 
     333    featureIsSelected: function(feature) { 
     334        return OpenLayers.Util.indexOf( 
     335            feature.layer.selectedFeatures, feature) > -1; 
     336    }, 
    294337 
    295338    CLASS_NAME: "OpenLayers.Control.SelectFeature" 
    296339}); 
  • lib/OpenLayers/Feature/Vector.js

    old new  
    3939     * {<OpenLayers.Geometry>}  
    4040     */ 
    4141    geometry: null, 
     42     
     43    /** 
     44     * Property: viewGeometry  
     45     * {<OpenLayers.Geometry>} 
     46     * viewGeometry is the geometry representing the visual appearance  
     47     * of the feature. 
     48     * For most features, this is just their ordinary geometry. 
     49     * For Points it's a rectangle whose size is based on  
     50     * the size of its style properties. 
     51     *  
     52     * Access via getViewGeometry() 
     53     */ 
     54    viewGeometry: null, 
     55     
     56    /** 
     57     * Property: resolutionOfViewGeometry 
     58     * {Number} 
     59     * The resolution for which the viewGeometry was calculated. 
     60     * This is stored to be able to know if the viewGeometry 
     61     * should be recalculated. 
     62     */ 
     63    resolutionOfViewGeometry: null, 
    4264 
    4365    /**  
    4466     * APIProperty: attributes  
     
    260282        } 
    261283    }, 
    262284     
     285    /** 
     286     * Method: getViewGeometry 
     287     * Returns the "viewGeometry" of the feature. 
     288     * viewGeometry is the geometry representing the visual appearance  
     289     * of the feature. 
     290     * For most features, this is just their ordinary geometry. 
     291     * For Points it's a rectangle whose size is based on  
     292     * the size of its style properties. 
     293     *  
     294     * Returns: 
     295     * {<OpenLayers.Geometry>} The viewGeometry of the feature. 
     296     */ 
     297    getViewGeometry: function() { 
     298        var newRes = this.layer.getResolution(); 
     299        if( !this.viewGeometry || newRes != this.resolutionOfViewGeometry ) { 
     300            this.resolutionOfViewGeometry = newRes; 
     301            this.viewGeometry = this.calcViewGeometry(this.geometry); 
     302        } 
     303        return this.viewGeometry; 
     304    }, 
     305     
     306    /** 
     307     * Method: calcViewGeometry 
     308     * Calculates a viewGeometry corresponding to a given geometry. 
     309     *  
     310     * Used only internally (= by this class). 
     311     *  
     312     * Parameters: 
     313     * {<OpenLayers.Geometry>} The original geometry. 
     314     *  
     315     * Returns: 
     316     * {<OpenLayers.Geometry>} The viewGeometry. 
     317     */ 
     318    calcViewGeometry: function(geometry) { 
     319        var layer = this.layer; 
     320        switch(geometry.CLASS_NAME) { 
     321             
     322        case "OpenLayers.Geometry.Point": 
     323            // A point is presented as a rectangle whose size and 
     324            // position (offset) is determined by its style properties. 
     325             
     326            var style = this.style || OpenLayers.Feature.Vector.style['default']; 
     327             
     328            var res = layer.getResolution(); 
     329            var x = geometry.x; 
     330            var y = geometry.y; 
     331                 
     332            var width,height,xOffset,yOffset; 
     333             
     334            // use the graphic size if it is specified 
     335            if(style.graphicWidth && style.graphicHeight) { 
     336                width = style.graphicWidth * res; 
     337                height = style.graphicHeight * res; 
     338                xOffset = style.graphicXOffset ? style.graphicXOffset * res : 0; 
     339                yOffset = style.graphicYOffset ? style.graphicYOffset * res : 0; 
     340            } 
     341            // otherwise use the pointRadius property 
     342            // TBD: in this case, it could be possible to create  
     343            // a circle-like geometry instead of a rectangle... 
     344            else { 
     345                var rad = style.pointRadius * res; 
     346                width = height = rad*2; 
     347                xOffset = yOffset = -rad; 
     348            } 
     349             
     350            var left = x + xOffset; 
     351            var top = y + yOffset; 
     352            var right = left + width; 
     353            var bottom  = top + height; 
     354             
     355            var bounds = new OpenLayers.Bounds(left,bottom,right,top); 
     356            return bounds.toGeometry(); 
     357         
     358        case "OpenLayers.Geometry.Collection": 
     359        case "OpenLayers.Geometry.MultiPoint": 
     360            var components = Array(geometry.components.length); 
     361            for(var i=0; i<components.length; ++i) { 
     362                components[i] = this.calcViewGeometry(geometry.components[i]); 
     363            } 
     364            return new OpenLayers.Geometry.Collection(components); 
     365         
     366        default: 
     367            return geometry; 
     368        } 
     369    }, 
     370     
    263371    CLASS_NAME: "OpenLayers.Feature.Vector" 
    264372}); 
    265373 
  • lib/OpenLayers/Layer/Vector.js

    old new  
    526526    }, 
    527527     
    528528    /** 
     529     * Method: getFeaturesIntersectingGeometry 
     530     * Given a geometry, returns an array of features that intersect 
     531     * the geometry. The features' viewGeometry property is used 
     532     * in the intersection tests. 
     533     * 
     534     * Parameters: 
     535     * geometry - {<OpenLayers.Geometry>} The geometry. 
     536     * 
     537     * Returns: 
     538     * {Array(<OpenLayers.Feature.Vector>)} The intersecting features. 
     539     */ 
     540    getFeaturesIntersectingGeometry: function(geometry) { 
     541        var intersecting = []; 
     542        var feature; 
     543        for(var i=0; i<this.features.length; ++i){ 
     544            feature = this.features[i]; 
     545            if( geometry.intersects(feature.getViewGeometry()) ) { 
     546                intersecting.push(feature); 
     547            } 
     548        } 
     549        return intersecting; 
     550    }, 
     551     
     552    /** 
    529553     * Unselect the selected features 
    530554     * i.e. clears the featureSelection array 
    531555     * change the style back 
  • lib/OpenLayers.js

    old new  
    134134            "OpenLayers/Handler/Path.js", 
    135135            "OpenLayers/Handler/Polygon.js", 
    136136            "OpenLayers/Handler/Feature.js", 
     137            "OpenLayers/Handler/MultiLayerFeature.js", 
    137138            "OpenLayers/Handler/Drag.js", 
    138139            "OpenLayers/Handler/RegularPolygon.js", 
    139140            "OpenLayers/Handler/Box.js",