Reputation: 5656
Another day working with openlayers and another problem.
Namely - i have multiple vector layers on top of each other for different types of stuff (cars, trips from history and areas). They all have events that im trying to catch... But as Niklas found out, when you activate events on one layer, it gets moved on top and events on layers below wont fire.
Is there a way to bypass this? Because when i move over area polygon i want event firing and displaying its name and when i move mouse over to car marker i want the event fire too. And no - i dont want to put them on same layer, cause i want it to be possible to turn them off or on fast and without looping through all the features and disabling them each.
Alan
Edit1: I did some searching, and found out, that you could use same control on multiple layers. that could probably fix this problem for me. im checking it out atm and testing if adding more layers to single control is solution to my problem or not.
Upvotes: 10
Views: 18129
Reputation: 176
I found this when I had the same issue, trying to get multiple layers to react to mouse events.
The solution, just in case anyone else finds this thread is much simpler.
The SelectFeature control takes an array of Vector layers and if all the laters you need to react to mouse events (hover and click) are in that array, they ALL work, not just the one that was moved to the top.
So, in the approved solution to this thread, it can be much simplified by doing this:
this.carSelect = new OpenLayers.Control.SelectFeature(
[this.vectorsLayer, this.carsLayer],
{
'hover':true,
'callbacks': {
blah blah blah
}
});
This will register the appropriate events on both layers and make them both live.
I hope this helps anyone else stumbling on this issue.
As said elsewhere, using OpenLayers is not hard, finding the correct way to do things with it is.
Upvotes: 10
Reputation: 159
this solve my problem :
before :
map.addLayer(layer);
layer.events.register("loadend", layer, function(){
$("#progress").hide();
});
after :
layer.events.register("loadend", layer, function(){
$("#progress").hide();
});
map.addLayer(layer);
Hope it helps
Upvotes: 3
Reputation: 5656
Well heres the solution:
/*
* This method will be called each time you add more vector layers to your map.
* i get more data with ajax calls and some of this data will go to existing layers,
* some of it will go to new layers... Some layers will be added and some removed.
*/
OpenMap.prototype.bindFeatureEvents = function (arr){
var that = this;
if ( this.initialized == true ){
/* if map is already initialized we deactivate and remove control -
* we will re add it later. I havent tested if its really necessary...
*/
this.carSelect.deactivate();
this.mymap.removeControl(this.carSelect);
} else {
this.carSelect = new OpenLayers.Control.SelectFeature([], {
'hover':true,
'callbacks':{
'click':function(f){
if ( typeof f.attributes.data.id != 'undefined'){
that.selectCar(f.attributes.data.id);
}
}
}});
this.vectorsLayer.events.on({
'featureselected': this.onFeatureSelect.bind(this),// these methods open and close popups.
'featureunselected': this.onFeatureUnselect.bind(this)
});
this.carsLayer.events.on({
'featureselected': this.onFeatureSelect.bind(this),
'featureunselected': this.onFeatureUnselect.bind(this),
'moveend': function(e) {
if (e.zoomChanged) {
if (this.watchCar == true){
this.holdZoom = true;
}
}
}.bind(this)//without this the "this" in moveend callback is openlayers.layer.vector
});
/*
* I save existing layers in two arrays... It seemed simpler to use two arrays..
* or you could of course use one Object instead of two Arrays...
* and you really need to bind events to each layer only once... otherwise each rebinds
* makes events firing more and more.
* each time bindFeatureEvents is called.. new events would be added.
*/
var name = this.vectorsLayer.name;
this.boundLayers.push(name)
this.allLayers.push(this.vectorsLayer);
var name = this.carsLayer.name;
this.boundLayers.push(name)
this.allLayers.push(this.carsLayer);
this.initialized = true;
}
/*
* We need to check if any arr was provided at bindFeatureEvents call.
* if not.. then ohwell. pass :P
*/
if ( arr != null && typeof(arr)=='object' && arr instanceof Array && arr.length > 0 ){
for ( i = 0 ; i < arr.length; i++){
var name = arr[i].name;
if ( name in oc(this.boundLayers) ){
// Tell me how to skip this part...
} else {
//we add new layer to both arrays.
this.boundLayers.push(name);
this.allLayers.push(arr[i]);
}
}
}
/*
* this next line is what made it sensible to use two arrays... you can
* feed all layers easyer to setLayer method
* We could also use bit of code to check if some layers were removed...
*/
this.carSelect.setLayer(this.allLayers);
this.mymap.addControl(this.carSelect);
this.carSelect.activate();
/*
* Yeah the control is acitvated again and the events are firing on all layers...
*/
};
//taken from http://snook.ca/archives/javascript/testing_for_a_v
function oc(array){
var o = {};
for(var i=0;i<array.length;i++){
o[array[i]]='';
}
return o;
};
Upvotes: 1
Reputation: 3524
I'm honored to be mentioned like that! :-)
I ended up overriding the activate function in the Feature Handler in an additional js file: (The commented row is the only difference.)
OpenLayers.Handler.Feature.prototype.activate = function() {
var activated = false;
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
//this.moveLayerToTop();
this.map.events.on({
"removelayer": this.handleMapEvents,
"changelayer": this.handleMapEvents,
scope: this
});
activated = true;
}
return activated;
};
I also found that the select controls worked on layers not being on top, so I think your approach using multiple layers in the control looks good.
Upvotes: 0