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) {
相关产品推荐
相关产品推荐

