Search code examples
openlayers

Openlayers - popup for all overlapping features in GeoJSON and label visibility


Good morning, when I query overlapping features the popup shows only the data of the above feature. How can I change the popup to show data for all features queried at that point?

Example project posted here

Geojson: (The geojson contains the 3 geometries positioned in the same coordinate)

var json_overlapped_feature = {
    "type":"FeatureCollection",
    "name":"overlapped_feature",
    "crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},
    "features":[
        {"type":"Feature","properties":{"Nome":"GEOMETRIA1"},"geometry":{"type":"Point","coordinates":[13.006901604608238,43.843783667531724]}},
        {"type":"Feature","properties":{"Nome":"GEOMETRIA2"},"geometry":{"type":"Point","coordinates":[13.006901604608238,43.843783667531724]}},
        {"type":"Feature","properties":{"Nome":"GEOMETRIA3"},"geometry":{"type":"Point","coordinates":[13.006901604608238,43.843783667531724]}}
]}

Layer: (the layer is declared with .readFeatures so the features should be present in the map)

var format_overlapped_feature = new ol.format.GeoJSON();
var features_overlapped_feature = format_overlapped_feature.readFeatures(json_overlapped_feature, 
            {dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3004'});
var jsonSource_overlapped_feature = new ol.source.Vector({
        });
jsonSource_overlapped_feature.addFeatures(features_overlapped_feature);
var lyr_overlapped_feature = new ol.layer.Vector({
                declutter: true,
                source:jsonSource_overlapped_feature, 
                style: style_overlapped_feature,
    title: 'overlapped_feature<br />\
    <a class="layerlegend"<br />\
    <img src="styles/legend/overlapped_feature_0.png" /> GEOMETRIA1<br />\
    <img src="styles/legend/overlapped_feature_1.png" /> GEOMETRIA2<br />\
    <img src="styles/legend/overlapped_feature_2.png" /> GEOMETRIA3<br /></a>'
        });

Popup: (the popup is configured to query features, but it only queries the top one)

var onSingleClick = function(evt) {
    var pixel = map.getEventPixel(evt.originalEvent);
    var coord = evt.coordinate;
    var popupField;
    var popupText = '<ul>';
        map.forEachFeatureAtPixel(pixel, function(feature, layer) {
            var currentFeature = feature;
            var currentFeatureKeys = currentFeature.getKeys();
            popupText += '<li><table>';
                for (var i=0; i<currentFeatureKeys.length; i++) {
                    if (currentFeatureKeys[i] != 'geometry') {
                        popupField = '';
                        if (layer.get('fieldLabels')[currentFeatureKeys[i]] == "inline label") {
                            popupField += '<th>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</th><td>';
                        } else {
                            popupField += '<td colspan="2">';
                        }
                        if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                            popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(currentFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                        } else {
                            popupField += (currentFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + currentFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim()  + '" /></td>' : '');
                        }
                        popupText += '<tr>' + popupField + '</tr>';
                    }
                }
            popupText += '</table>';
        });
    // disable popup if no feature
    if (popupText == '<ul>') {
        popupText = '';
    } else {
        popupText += '</ul>';
    }
        // display popup
        if (popupText) {
            overlayPopup.setPosition(coord);
            content.innerHTML = popupText;
            container.style.display = 'block';        
        } else {
            container.style.display = 'none';
            closer.blur();
        }
};

Result: (only the higher geometry is queried)

enter image description here

### UPDATE ###

By eliminating declutter:true from layer, the 3 geometries become interrogable, however the labels overlap. How to display them non-overlapping? I insert the style

enter image description here

Style

var size = 0;
var placement = 'point';
function categories_overlapped_feature(feature, value, size, resolution, labelText,
                       labelFont, labelFill, bufferColor, bufferWidth,
                       placement) {
                switch(value.toString()) {case 'GEOMETRIA1':
                    return [ new ol.style.Style({
        image: new ol.style.Circle({radius: 4.0 + size,
            stroke: new ol.style.Stroke({color: 'rgba(35,35,35,1.0)', lineDash: null, lineCap: 'butt', lineJoin: 'miter', width: 0}), fill: new ol.style.Fill({color: 'rgba(135,239,166,1.0)'})}),
        text: createTextStyle(feature, resolution, labelText, labelFont,
                              labelFill, placement, bufferColor,
                              bufferWidth)
    })];
                    break;
case 'GEOMETRIA2':
                    return [ new ol.style.Style({
        image: new ol.style.Circle({radius: 4.0 + size,
            stroke: new ol.style.Stroke({color: 'rgba(35,35,35,1.0)', lineDash: null, lineCap: 'butt', lineJoin: 'miter', width: 0}), fill: new ol.style.Fill({color: 'rgba(52,87,229,1.0)'})}),
        text: createTextStyle(feature, resolution, labelText, labelFont,
                              labelFill, placement, bufferColor,
                              bufferWidth)
    })];
                    break;
case 'GEOMETRIA3':
                    return [ new ol.style.Style({
        image: new ol.style.Circle({radius: 4.0 + size,
            stroke: new ol.style.Stroke({color: 'rgba(35,35,35,1.0)', lineDash: null, lineCap: 'butt', lineJoin: 'miter', width: 0}), fill: new ol.style.Fill({color: 'rgba(216,191,91,1.0)'})}),
        text: createTextStyle(feature, resolution, labelText, labelFont,
                              labelFill, placement, bufferColor,
                              bufferWidth)
    })];
                    break;}};

var style_overlapped_feature = function(feature, resolution){
    var context = {
        feature: feature,
        variables: {}
    };
    var value = feature.get("Nome");
    var labelText = "";
    size = 0;
    var labelFont = "10px, sans-serif";
    var labelFill = "#000000";
    var bufferColor = "";
    var bufferWidth = 0;
    var textAlign = "left";
    var offsetX = 8;
    var offsetY = 3;
    var placement = 'point';
    if (feature.get("numcivico") !== null) {
        labelText = String(feature.get("Nome"));
    }
    
var style = categories_overlapped_feature(feature, value, size, resolution, labelText,
                          labelFont, labelFill, bufferColor,
                          bufferWidth, placement);

    return style;
};

Solution

  • Based on Mike's help in the comments I realized that I couldn't query all the features in the same place as the declutter: true option in the layer made them disappear from the map.

    Testing declutter: false solves this problem however all the labels appear in cascade, if configured as visible. This destroys the map and slows it down.

    A possible solution comes from a recent fix made by openlayers 6.15.1 reported here however I can't apply it to my project.

    I worked around the problem by configuring the style differently. Before it was like this:

    var style = [ new ol.style.Style({
            image: new ol.style.RegularShape({
                radius: 4.0 + size,
                points: 4,
                stroke: new ol.style.Stroke({
                   color: 'rgba(128,17,25)',
                   lineDash: null,
                   lineCap: 'butt',
                   lineJoin: 'miter',
                   width: 1}), 
                fill: new ol.style.Fill({
                   color: 'rgba(219,30,42,0.5)'})
            }),
            text: createTextStyle(feature, resolution, labelText, labelFont,
                                  labelFill, placement, bufferColor,
                                  bufferWidth)
        })];
    

    Now it's like this:

    var style = [ new ol.style.Style({
        geometry: new ol.geom.Circle(feature.getGeometry().getCoordinates(), 3*resolution),
        stroke: new ol.style.Stroke({
                color: 'rgba(35,35,35)',
                lineDash: null,
                lineCap: 'butt',
                lineJoin: 'miter',
                width: 1}),
        fill: new ol.style.Fill({color: 'rgba(227,26,28,0.5)'}),
        text: createTextStyle(feature, resolution, labelText, labelFont,
                              labelFill, placement, bufferColor,
                              bufferWidth)
    })];
    

    The result is that the declutter is deactivated for the geometries, but remains active for the labels (in the case of completely overlapping labels, only one will be visible).

    Result:

    enter image description here