0

I'm using OpenLayers 2.13 for an application that create several maps. When the user click on a map a popup appears where the user clicked on the map. Sometime the popup is offset either east or west of where it should be. I used console.log to get the values from map.getLongLatFromViewPort(event.xy) and the event.xy that place the popup. The event.xy, when popup appears in the wrong location, has a value for event.xy.x that is not correct. Below is the code for the popup:

innovate.popupbox[mapID] = new OpenLayers.Popup.FramedCloud(
                        "innovate_info_popup_" + mapID,
                        map.getLonLatFromViewPortPx(event.xy),
                        boxSize,
                        content,
                        null,
                        true,
                        function() {
                            this.div.style.display = 'none';
                            map.panTo(innovate.lastPopupCenter[mapID]);
                            map.removePopup(innovate.popupbox[mapID]);
                            }
                        );

The following is how the map is built:

innovate.Map = function(mapID){
var currentObject = this;

this.map = null;
this.config = innovate.getUserConfigForMap(mapID);
this.innovateLayerObj = new innovate.Layers(this);
this.innovateControlsObj = new innovate.Controls(this);
this.clickControls = {};
this.mapClickControlIds = {};

//build map object
this.setMap = function(){
    var baseLayerType = this.innovateLayerObj.getBaseLayerType();
    this.map = new OpenLayers.Map('container-map'+mapID, { 
        allOverlays: false,
        fallThrough : true,
        projection: currentObject.config.projection,
        controls: [
        new OpenLayers.Control.PanZoomBar()
        ]
    //,eventListeners: {"mouseout": innovate.handleOnMouseOutOnMap,"mouseover":innovate.handleOnMouseOverOnMap}
    });

    if(baseLayerType==="arcgiscache"){
        this.map.setOptions({ 
            maxExtent: new OpenLayers.Bounds(currentObject.config.maxExtent[0],currentObject.config.maxExtent[1],currentObject.config.maxExtent[2],currentObject.config.maxExtent[3])
        });
    }else if (baseLayerType==="bing"){
        this.map.setOptions({ 
            maxExtent: new OpenLayers.Bounds(currentObject.config.maxExtent[0],currentObject.config.maxExtent[1],currentObject.config.maxExtent[2],currentObject.config.maxExtent[3])
        });
    }
}

//set all the base layer as well as overlays
this.setLayers = function(){
    var layer = null;
    for (key in this.config['baseMap']) {
        layer = this.innovateLayerObj.getLayer(this.config['baseMap'][key]);
        this.map.addLayer(layer);
    }

    for (key in this.config['layers']) {
        layer = this.innovateLayerObj.getLayer(this.config['layers'][key]);

        if(this.config['initialDisplayLayer']){
            if(key!=parseInt(this.config['initialDisplayLayer'])){
                layer.setVisibility(false);
            }
        }
        this.map.addLayer(layer);
    }

    if(this.config['layers'].length>1 && this.config['initialDisplayLayer']){
        var layers = this.map.layers.slice(),count=0;
        for(var i=0;i<layers.length;i++){
            if(!(layers[i].isBaseLayer)){
                if(count!=this.config['initialDisplayLayer']){
                    layers[i].setVisibility(false);
                }
                count++;
            }
        }

    }
}

this.reactivateClickEvents = function(){
    //first remove the click control from the map
    //for some reason in dev, the click control was added twice!
    //same issue did not arrise in local
    //but adding code to delete the control and then add it back did the trick
    for(var keys in this.map.controls){
        if(this.mapClickControlIds[this.map.controls[keys].id]){
            //alert("removed"+this.map.controls[keys].id);
            this.map.controls[keys].deactivate();
            this.map.removeControl(this.map.controls[keys]);
        }
    }
    this.mapClickControlIds=[];

    var control = null,layerName;
    for (key in this.config['controls']) {

        var controlName = this.config['controls'][key];
        if(controlName.substr(0,6)=='click_'){
            for(var layerIndx in this.config["layers"]){

                layerName = this.innovateLayerObj.layerDefinitions[this.config["layers"][layerIndx]]["name"];
                control = (this.innovateControlsObj[controlName]).call(this.innovateControlsObj,layerName);
                if(control!=null){
                    this.clickControls[layerName] = control;
                    this.map.addControl(control);
                    control.activate();
                    this.mapClickControlIds[control.id]=1;
                }

            }
        }
    }

}

//set all the controls
this.setControls = function(){
    var control = null,layerName;
    for (key in this.config['controls']) {
        var controlName = this.config['controls'][key];
        if(controlName.substr(0,6)==='click_'){
            for(var layerIndx in this.config["layers"]){

                layerName = this.innovateLayerObj.layerDefinitions[this.config["layers"][layerIndx]]["name"];
                control = (this.innovateControlsObj[controlName]).call(this.innovateControlsObj,layerName);
                if(control!=null){
                    this.clickControls[layerName] = control;
                    this.map.addControl(control);
                    control.activate();
                    this.mapClickControlIds[control.id]=1;
                }

            }
        }
        else{
            control = this.innovateControlsObj.getControl(controlName);
            if(control!=null){
                this.map.addControl(control);
            }
        }
    }
}

/*
 * Thus function builds map based on map id set to the current innovate.Map function
 */
this.buildMap=function(){
    this.setMap();
    this.setLayers();
    this.setControls();

    var baseLayerType = this.innovateLayerObj.getBaseLayerType();

    if(currentObject.config.zoomToExtent){
        this.map.zoomToExtent(new OpenLayers.Bounds(currentObject.config.zoomToExtent[0],currentObject.config.zoomToExtent[1],currentObject.config.zoomToExtent[2],currentObject.config.zoomToExtent[3]));
    }else if(this.config.center){
        var zoomLevel = this.config.defaultZoomLevel?this.config.defaultZoomLevel-this.config.startResolution:0;
        var geographic = new OpenLayers.Projection("EPSG:4326");
        var mercator = new OpenLayers.Projection(this.config["projection"]);
        this.map.setCenter(new OpenLayers.LonLat(this.config.center[0], this.config.center[1]).transform(geographic,mercator),zoomLevel);

    }else if(baseLayerType==="arcgiscache"){
        this.map.zoomTo(this.config.startResolution);
    }
}

}

Below is the event handler for the map:

this.click_radons = function(layerName) {
if (layerName == "State Boundaries") {
    return null;
}
var map = innovateMapObj.map,
        layer = null,
        serviceURL = null,
        layers = map.layers.slice(),
        content = "",
        desc = null,
        img = null,
        priority = null;

//because we don't want to query base layer
for (var i = 0; i < layers.length; i++) {
    if (!(layers[i].isBaseLayer)) {
        layer = layers[i];
        serviceURL = layers[i].url;
        break;
    }
}


OpenLayers.ProxyHost = innovate.proxyURL;
return new OpenLayers.Control.WMSGetFeatureInfo({
    layers: [layer],
    url: serviceURL,
    title: 'Identify features by clicking',
    queryVisible: true,
    eventListeners: {
        getfeatureinfo: function(event) {
            var thisObject = new innovate.Controls(innovateMapObj)
            obj = thisObject.makeObjectOutOfResponse(event.text);

            var boxSize = null;
            var mapID = innovateMapObj.config.id;
            //hide any open popup on click
            if (innovate.popupbox[mapID]) {
                map.removePopup(innovate.popupbox[mapID]);
                innovate.popupbox[mapID].destroy();
                //$("#innovate_info_popup_" + mapID).hide();
            }
            if ($.isEmptyObject(obj) || obj === false) {
                return;
            }
            innovate.lastPopupCenter[mapID] = map.getCenter();

            if (obj['radonzone'] == 1) {
                desc = "<b>Zone 1 </b>counties have a predicted average indoor radon screening level greater than 4 pCi/L (picocuries per liter) <b>(red zones)</b>";
                priority = "<b>Highest Potential</b>";
                img = "3b6a386668a6d07b8d8e029744820282";
            } else if (obj['radonzone'] == 2) {
                desc = "<b>Zone 2 </b>counties have a predicted average indoor radon screening level between 2 and 4 pCi/L <b>(orange zones)</b>";
                priority = "<b>Moderate Potential</b>";
                img = "52edb92fef3b3447d910ea3f4e3bad06";
            } else if (obj['radonzone'] == 3) {
                desc = "<b>Zone 3 </b>counties have a predicted average indoor radon screening level less than 2 pCi/L <b>(yellow zones)</b>";
                priority = "<b>Low Potential</b>";
                img = "d1573b09a0505bef245b2a380a18e380";
            }
            var stateName = obj['statename'], initCapedStateName = "";
            stateName = stateName.split(" ");
            for (var keys in stateName) {
                initCapedStateName += stateName[keys].substr(0, 1) + stateName[keys].substr(1).toLowerCase() + " ";
            }

            var imgURL = innovate.mapServiceBaseURL["legend"] + innovateMapObj.config.legend["url"] + "/../0/images/" + img;
            content = "<div>" + obj['namelsad'] + ", " + initCapedStateName + "</div>" +
                    "<div>Radon risk zone: " + obj['radonzone'] + "</div>" +
                    "<table style=\"width:420px\height:120px\"><tbody><tr style=\"background-color: #EEEEEE; border-color:#DDDDDD;border-style: solid; border-width: 1px 0 1px 0px;padding: 5px;\"><td valign=\"top\" align=\"center\"><div><img alt=\"\" src=\"" + imgURL + "\"></div></td>" +
                    "<td valign=\"top\" align=\"left\">" + desc + "</td>" +
                    "<td valign=\"top\" align=\"left\">" + priority + "</td></tr></tbody></table>";


                innovate.popupbox[mapID] = new OpenLayers.Popup.FramedCloud(
                        "innovate_info_popup_" + mapID,
                        map.getLonLatFromViewPortPx(event.xy),
                        boxSize,
                        content,
                        null,
                        true,
                        function() {
                            this.div.style.display = 'none';
                            map.panTo(innovate.lastPopupCenter[mapID]);
                            map.removePopup(innovate.popupbox[mapID]);
                            }
                        );

                map.addPopup(innovate.popupbox[mapID]);

        }
    }
});

}

It seem as though something is causing the screen coordinates to sometime be shifted to the left or right of where the user click. Any advice would helpful.

  • It would be of more help if you can also put the event handler function from which you get the click event object. – mfdev Dec 7 '13 at 21:06
  • I have added the event handler function for the click event – dyarnell Dec 10 '13 at 16:24
1

Maybe the container div of map has resized after the map widget has been placed over it. You could make a call to

(OpenLayer.Map).updateSize();

so the mouse position is accurate with the container(map) size. Also the click is considered only if there is no mouse move between the start of click and end of click.

You also added some controls that could be causing race conditions if they are activated at same time. Disable any unnecessary controls that listen to click events.

I could not figure your custom click listeners but you can attach a listener to the map widget by listening to the click event on map.

(OpenLayers.Map).events.register( 'click', null, function( evt ){
  window.alert( 'You got a click on map: ' + evt.xy + '!' );
}
);

The following example should register a click event listener before other registered click listeners.

(OpenLayers.Map).events.registerPriority( 'click', null, function( evt ){
  window.alert( 'You got a click on map before any other listeners: ' + evt.xy + '!' );
  window.alert( 'You can also consume the event and avoid others to see it with evt.stopPropagation()' );
  evt.stopPropagation();
}
);
  • I have disabled the Navigation control and the issue does not happen. Would this mean that it is a race condition? and if so do I then need to add the controls in a different way? – dyarnell Dec 10 '13 at 18:07
  • By some reason, maybe that the navigation control is consuming the click event or preventing him from firing . You could try add your listener on top( before ) other click listeners. I will update the response with an example to add the click before. – cavila Dec 11 '13 at 11:52
  • I added the listener before other click listeners and it fires for the navigation control but evt.xy returns undefined? I'm not sure what this means? – dyarnell Dec 16 '13 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.