[GIS] Creating buffer around WFS features to ease their selection in OpenLayers 2

buffergeoserveropenlayers-2selectwfs

I'm using a WFS SelectFeature from OpenLayers 2 to show a Popup containing the features' attributes from line layers and point layers (from GeoServer). This works fine, but I would like to create a buffer (similar to the clickTolerance in the WFS GetFeature) around the point/line, so that I wouldn't have to click exactly on the center of the feature to select it. As i've searched through the Q&A, I found related questions, but no answer worked for me so far.

My code is here:

<script defer="defer" type="text/javascript">           
    var map;

    // pink tile avoidance
    OpenLayers.IMAGE_RELOAD_ATTEMPTS = 5;
    // make OL compute scale according to WMS spec
    OpenLayers.DOTS_PER_INCH = 25.4 / 0.28;

    function init(){

        // Save-strategy
        var saveStrategy = new OpenLayers.Strategy.Save();

        // Controls
        var controls = [                
            new OpenLayers.Control.ArgParser(),
            new OpenLayers.Control.Attribution(),   
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.PanZoomBar({position: new OpenLayers.Pixel(2, 15)}),
            new OpenLayers.Control.LayerSwitcher(),
            new OpenLayers.Control.ScaleLine(),
            new OpenLayers.Control.Scale(document.getElementById('scale')),
            new OpenLayers.Control.MousePosition({element: document.getElementById('location')})
        ];              
        /////
        var options = {
            scales: [500000, 100000, 50000, 25000, 10000, 5000],
            projection: "EPSG:4326",
            displayProjection: "EPSG:4326",
            units: 'm'
        }
        /////               
        map = new OpenLayers.Map('map', /*{controls: controls}*/ options);              
        //Basemaps
        var osm = new OpenLayers.Layer.OSM("Simple OSM Map");
        var gsat = new OpenLayers.Layer.Google("Google Satellite",
            {type: google.maps.MapTypeId.SATELLITE, numZoomLevels: 22}
        );
        var gter = new OpenLayers.Layer.Google("Google Terrain",
            {type: google.maps.MapTypeId.TERRAIN, numZoomLevels: 22}
        );              
        //Add Basemaps
           map.addLayers([gter, osm, gsat]);                                                                        
        //Layer
        var test = new OpenLayers.Layer.Vector("xName", {               
            strategies: [new OpenLayers.Strategy.Fixed(), saveStrategy],    
            protocol: new OpenLayers.Protocol.WFS({
            url: SETTINGS.BASE_URL,
            featurePrefix: 'xNS',
            featureNS: "xNS",
            //extractAttributes: true,
            featureType: "xName",
            }),
                styleMap: new OpenLayers.StyleMap({
                    "default": new OpenLayers.Style({
                        "pointRadius": 2,
                        fill: true,
                        fillColor: "#8d006a",
                        strokeColor: "#000000",
                        strokeWidth: 0.2
                    })
                })
            });
        //Add Layer
        map.addLayer(test);                                             
        //add Popup
        var select = new OpenLayers.Control.SelectFeature(test, {
            hover: false,
            toggle: true,
            box: false,                 
        });
            map.addControl(select);
            select.activate();

        function onPopupClose(evt) {
            selectControl.unselect(selectedFeature);
        }

        test.events.on({
            featureselected: function(event) {
                var feature = event.feature;
                console.log(feature);
                feature.popup = new OpenLayers.Popup.FramedCloud
                ("pop",
                feature.geometry.getBounds().getCenterLonLat(),
                null, feature.attributes["xx"] + " " + feature.attributes["xx"],
                null,
                true //Boolean indicating if the close buttons must be shown 
                );
             map.addPopup(feature.popup);  
            },

        // destroy popup when feature is no longer selected. Prevents showing 2 Popups at the same time
            featureunselected: function(event) {
                var feature = event.feature;
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
                }
        });

        //Set Map Center        
        map.setCenter(
            new OpenLayers.LonLat(27.1, 47.55).transform(
                new OpenLayers.Projection("EPSG:4326"),
                map.getProjectionObject()
            ), 9
        );

    }

I have also tried WFS GetFeature with clickTolerance, but it doesn't seem to work, nothing is selected on the map.

Code:

var select = new OpenLayers.Layer.Vector("Selection", {styleMap: new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"])});

        map.addLayer(select);

        getfeature = new OpenLayers.Control.GetFeature({
            protocol: new OpenLayers.Protocol.WFS({
                url: SETTINGS.BASE_URL,
                featurePrefix: 'xNS',
                featureNS: "xNS",
                //extractAttributes: true,
                featureType: "xName"
            }),
            box: true,
            hover: true,
            multipleKey: "shiftKey",
            toggleKey: "ctrlKey",
            clickTolerance: 1000
        });

        getfeature.events.register("featureselected", this, function {
              select.addFeatures([feature]);
        });

        getfeature.events.register("featureunselected", this, function {
              select.removeFeatures([feature]);
        });         

        map.addControl(getfeature);
        getfeature.activate();

Cloning the features with bigger invisible ones wouldn't help, due to the large number of features and layers I already have.

Best Answer

quick+dirty workaround:

If you don't need a visible stroke around your symbols you could just use a thicker stroke and make it invisible:

{
strokeOpacity:0,
strokeWidth: 10,
...
}

http://jsfiddle.net/expedio/et4c43gp/