You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QGIS2WEB-OpenLayers弹窗优化:添加查询要素选择性高亮功能

为QGIS2WEB导出的OpenLayers地图弹窗添加要素选择性高亮功能

需求概述

我用QGIS2WEB导出了支持点击查询多图层多要素的OpenLayers地图,点击后所有查询结果会展示在弹窗中(示例包含6个不同图层的结果)。现在需要给弹窗添加选择性高亮查询要素的功能,计划为每个要素添加高亮按钮(已完成效果图),并且希望复用QGIS2WEB中已有的pointermove触发的要素高亮逻辑来实现这个功能。

现有要素高亮逻辑(pointermove触发)

以下是QGIS2WEB中已有的高亮代码,需要复用这个逻辑:

if (doHighlight) {
    if (currentFeature !== highlight) {
        if (highlight) {
            featureOverlay.getSource().removeFeature(highlight);
        }
        if (currentFeature) {
            var styleDefinition = currentLayer.getStyle().toString();

            if (currentFeature.getGeometry().getType() == 'Point') {
                var radius = styleDefinition.split('radius')[1].split(' ')[1];

                highlightStyle = new ol.style.Style({
                    image: new ol.style.Circle({
                        fill: new ol.style.Fill({
                            color: 'rgba(255,255,0,1)'
                        }),
                        radius: radius,
                        stroke: new ol.style.Stroke({
                          color: 'rgba(255, 255, 0, 1)',
                          width: 3
                        }),
                    })
                })
            } else if (currentFeature.getGeometry().getType() == 'LineString') {

                var featureWidth = styleDefinition.split('width')[1].split(' ')[1].replace('})','');

                highlightStyle = new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: 'rgba(255,255,0,1)',
                        lineDash: null,
                        width: featureWidth
                    })
                });

            } else {
                highlightStyle = new ol.style.Style({
                        
                        fill: new ol.style.Fill({
                          color: 'rgba(255, 255, 0, 0)'
                        }),
                        stroke: new ol.style.Stroke({
                          color: 'rgba(255, 255, 0, 1)',
                          lineDash: [10, 10],
                          width: 3
                        }),                         
                })
            }
            featureOverlay.getSource().addFeature(currentFeature);
            featureOverlay.setStyle(highlightStyle);
        }
        highlight = currentFeature;
    }
}

当前弹窗实现代码

需要修改以下弹窗代码来添加高亮按钮功能:

var onSingleClick = function(evt) {
    if (doHover) {
        return;
    }
    if (sketch) {
        return;
    }
    var pixel = map.getEventPixel(evt.originalEvent);
    var coord = evt.coordinate;
    var popupField;
    var currentFeature;
    var currentFeatureKeys;
    var clusteredFeatures;
    var popupText = '<ul>';
    map.forEachFeatureAtPixel(pixel, function(feature, layer) {
        if (feature instanceof ol.Feature && (layer.get("interactive") || layer.get("interactive") == undefined)) {
            var doPopup = false;
            for (k in layer.get('fieldImages')) {
                if (layer.get('fieldImages')[k] != "Hidden") {
                    doPopup = true;
                }
            }
            currentFeature = feature;
            clusteredFeatures = feature.get("features");
            var clusterFeature;
            if (typeof clusteredFeatures !== "undefined") {
                if (doPopup) {
                    for(var n=0; n<clusteredFeatures.length; n++) {
                        clusterFeature = clusteredFeatures[n];
                        currentFeatureKeys = clusterFeature.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('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                    popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                                }
                                if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(clusterFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                                } else {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim()  + '" /></td>' : '');
                                }
                                popupText += '<tr>' + popupField + '</tr>';
                            }
                        } 
                        popupText += '</table></li>';    
                    }
                }
            } else {
                currentFeatureKeys = currentFeature.getKeys();
                if (doPopup) {
                    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('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                            }
                            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>';
                }
            }
        }
    });
    if (popupText == '<ul>') {
        popupText = '';
    } else {
        popupText += '</ul>';
    }
    
    var viewProjection = map.getView().getProjection();
    var viewResolution = map.getView().getResolution();
    for (i = 0; i < wms_layers.length; i++) {
        if (wms_layers[i][1]) {
            var url = wms_layers[i][0].getSource().getGetFeatureInfoUrl(
                evt.coordinate, viewResolution, viewProjection,
                {
                    'INFO_FORMAT': 'text/html',
                });
            if (url) {
                popupText = popupText + '<iframe style="width:100%;height:110px;border:0px;" id="iframe" seamless src="' + url + '"></iframe>';
            }
        }
    }

    if (popupText) {
        overlayPopup.setPosition(coord);
        content.innerHTML = popupText;
        container.style.display = 'block';        
    } else {
        container.style.display = 'none';
        closer.blur();
    }
};

修改方案

1. 提取高亮逻辑为独立函数

将现有高亮代码封装成可复用的函数,方便弹窗按钮调用:

function highlightFeature(feature, layer) {
    if (doHighlight) {
        if (feature !== highlight) {
            if (highlight) {
                featureOverlay.getSource().removeFeature(highlight);
            }
            if (feature) {
                var styleDefinition = layer.getStyle().toString();
                let highlightStyle;

                switch(feature.getGeometry().getType()) {
                    case 'Point':
                        var radius = styleDefinition.split('radius')[1].split(' ')[1];
                        highlightStyle = new ol.style.Style({
                            image: new ol.style.Circle({
                                fill: new ol.style.Fill({
                                    color: 'rgba(255,255,0,1)'
                                }),
                                radius: radius,
                                stroke: new ol.style.Stroke({
                                  color: 'rgba(255, 255, 0, 1)',
                                  width: 3
                                }),
                            })
                        });
                        break;
                    case 'LineString':
                        var featureWidth = styleDefinition.split('width')[1].split(' ')[1].replace('})','');
                        highlightStyle = new ol.style.Style({
                            stroke: new ol.style.Stroke({
                                color: 'rgba(255,255,0,1)',
                                lineDash: null,
                                width: featureWidth
                            })
                        });
                        break;
                    default: // Polygon等其他类型
                        highlightStyle = new ol.style.Style({
                            fill: new ol.style.Fill({
                              color: 'rgba(255, 255, 0, 0)'
                            }),
                            stroke: new ol.style.Stroke({
                              color: 'rgba(255, 255, 0, 1)',
                              lineDash: [10, 10],
                              width: 3
                            }),                         
                        });
                }
                featureOverlay.getSource().addFeature(feature);
                featureOverlay.setStyle(highlightStyle);
            }
            highlight = feature;
        }
    }
}

2. 给弹窗中的每个要素添加高亮按钮

修改onSingleClick函数中的弹窗生成逻辑,在每个要素的表格后添加高亮按钮:

  • 聚合要素场景:在popupText += '</table></li>';前添加:
    popupText += '<tr><td colspan="2"><button class="highlight-btn" data-feature-id="' + clusterFeature.getId() + '" data-layer-id="' + layer.get('name') + '" style="margin-top:8px;">高亮该要素</button></td></tr>';
    
  • 非聚合要素场景:在popupText += '</table>';前添加:
    popupText += '<tr><td colspan="2"><button class="highlight-btn" data-feature-id="' + currentFeature.getId() + '" data-layer-id="' + layer.get('name') + '" style="margin-top:8px;">高亮该要素</button></td></tr>';
    

3. 绑定按钮点击事件

在onSingleClick函数设置完content.innerHTML = popupText;后,添加按钮事件绑定逻辑:

// 绑定高亮按钮事件
document.querySelectorAll('.highlight-btn').forEach(btn => {
    btn.addEventListener('click', function() {
        const featureId = this.getAttribute('data-feature-id');
        const layerName = this.getAttribute('data-layer-id');
        // 查找对应的图层
        let targetLayer;
        map.getLayers().forEach(layer => {
            if (layer.get('name') === layerName) {
                targetLayer = layer;
            }
        });
        if (targetLayer) {
            // 查找对应的要素
            const targetFeature = targetLayer.getSource().getFeatureById(featureId);
            if (targetFeature) {
                highlightFeature(targetFeature, targetLayer);
            }
        }
    });
});

4. 完整修改后的onSingleClick函数

var onSingleClick = function(evt) {
    if (doHover) {
        return;
    }
    if (sketch) {
        return;
    }
    var pixel = map.getEventPixel(evt.originalEvent);
    var coord = evt.coordinate;
    var popupField;
    var currentFeature;
    var currentFeatureKeys;
    var clusteredFeatures;
    var popupText = '<ul>';
    map.forEachFeatureAtPixel(pixel, function(feature, layer) {
        if (feature instanceof ol.Feature && (layer.get("interactive") || layer.get("interactive") == undefined)) {
            var doPopup = false;
            for (k in layer.get('fieldImages')) {
                if (layer.get('fieldImages')[k] != "Hidden") {
                    doPopup = true;
                }
            }
            currentFeature = feature;
            clusteredFeatures = feature.get("features");
            var clusterFeature;
            if (typeof clusteredFeatures !== "undefined") {
                if (doPopup) {
                    for(var n=0; n<clusteredFeatures.length; n++) {
                        clusterFeature = clusteredFeatures[n];
                        currentFeatureKeys = clusterFeature.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('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                    popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                                }
                                if (layer.get('fieldImages')[currentFeatureKeys[i]] != "ExternalResource") {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? autolinker.link(clusterFeature.get(currentFeatureKeys[i]).toLocaleString()) + '</td>' : '');
                                } else {
                                    popupField += (clusterFeature.get(currentFeatureKeys[i]) != null ? '<img src="images/' + clusterFeature.get(currentFeatureKeys[i]).replace(/[\\\/:]/g, '_').trim()  + '" /></td>' : '');
                                }
                                popupText += '<tr>' + popupField + '</tr>';
                            }
                        } 
                        // 添加聚合要素的高亮按钮
                        popupText += '<tr><td colspan="2"><button class="highlight-btn" data-feature-id="' + clusterFeature.getId() + '" data-layer-id="' + layer.get('name') + '" style="margin-top:8px;">高亮该要素</button></td></tr>';
                        popupText += '</table></li>';    
                    }
                }
            } else {
                currentFeatureKeys = currentFeature.getKeys();
                if (doPopup) {
                    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('fieldLabels')[currentFeatureKeys[i]] == "header label") {
                                popupField += '<strong>' + layer.get('fieldAliases')[currentFeatureKeys[i]] + ':</strong><br />';
                            }
                            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 += '<tr><td colspan="2"><button class="highlight-btn" data-feature-id="' + currentFeature.getId() + '" data-layer-id="' + layer.get('name') + '" style="margin-top:8px;">高亮该要素</button></td></tr>';
                    popupText += '</table></li>';
                }
            }
        }
    });
    if (popupText == '<ul>') {
        popupText = '';
    } else {
        popupText += '</ul>';
    }
    
    var viewProjection = map.getView().getProjection();
    var viewResolution = map.getView().getResolution();
    for (i = 0; i < wms_layers.length; i++) {
        if (wms_layers[i][1]) {
            var url = wms_layers[i][0].getSource().getGetFeatureInfoUrl(
                evt.coordinate, viewResolution, viewProjection,
                {
                    'INFO_FORMAT': 'text/html',
                });
            if (url) {
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:40:19