基于Overpass API实现建筑多选保存及屋顶真实面积计算问询
针对React OSM建筑可视化项目的修改方案
一、实现多建筑点击选择与数据保存
添加选中状态管理
在组件中新增状态变量存储选中的建筑数据:const [selectedBuildings, setSelectedBuildings] = useState([]);修改建筑交互逻辑
给每个建筑图层绑定点击事件,实现选中/取消选中的切换,并添加视觉反馈:// 以Leaflet的GeoJSON组件为例,渲染建筑时绑定交互逻辑 <GeoJSON data={processedBuildings} onEachFeature={(feature, layer) => { // 点击切换选中状态 layer.on('click', () => { const targetId = feature.id; setSelectedBuildings(prev => { const isSelected = prev.some(item => item.id === targetId); return isSelected ? prev.filter(item => item.id !== targetId) : [...prev, feature]; }); }); // 根据选中状态切换样式 const isSelected = selectedBuildings.some(item => item.id === feature.id); layer.setStyle({ fillColor: isSelected ? '#e74c3c' : '#3498db', weight: isSelected ? 2 : 1, fillOpacity: isSelected ? 0.7 : 0.5 }); }} />数据保存实现
添加保存/导出功能,可将选中数据存储到本地或导出为文件:const saveSelectedData = () => { // 存储到localStorage localStorage.setItem('selectedOSMBuildings', JSON.stringify(selectedBuildings)); // 可选:导出为JSON文件 const blob = new Blob([JSON.stringify(selectedBuildings, null, 2)], {type: 'application/json'}); const downloadUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = 'selected-buildings.json'; a.click(); URL.revokeObjectURL(downloadUrl); }; // 在组件中添加保存按钮 <button onClick={saveSelectedData} style={{margin: '10px'}}>保存选中建筑数据</button>
二、获取屋顶角度并计算真实表面积
修改Overpass API查询语句
在原有查询中加入roof:angle标签,获取屋顶角度数据:const overpassQuery = ` [out:json][timeout:25]; ( way["building"]({{bbox}}); relation["building"]({{bbox}}); ); out body; >; out skel qt; // 补充请求屋顶角度标签 out tags; `;添加表面积计算工具函数
基于2D占地面积和屋顶角度计算真实屋顶表面积(公式:真实面积 = 2D面积 / cos(屋顶角度弧度)):const calculateRoofArea = (footprintArea, roofAngle) => { if (!roofAngle || roofAngle === 0) return footprintArea; // 角度转弧度 const angleRad = (roofAngle * Math.PI) / 180; // 边界处理:避免角度接近90度时出现异常值 if (angleRad > Math.PI / 2 - 0.01) return footprintArea * 10; return footprintArea / Math.cos(angleRad); };整合计算逻辑并嵌入数据
在处理Overpass返回数据时,计算真实表面积并添加到建筑属性中:const processBuildingsData = (rawData) => { return rawData.features.map(feature => { const footprintArea = feature.properties.area; // 原有2D面积字段 const roofAngle = parseFloat(feature.properties['roof:angle']); const roofArea = calculateRoofArea(footprintArea, roofAngle); return { ...feature, properties: { ...feature.properties, roofArea: roofArea.toFixed(2), roofAngle: roofAngle || '未记录' } }; }); };在UI中展示计算结果
可在悬停弹窗或选中详情中展示真实表面积:// 悬停弹窗示例 layer.bindPopup(` <div style={{padding: '8px'}}> <p>2D占地面积: ${feature.properties.area} ㎡</p> <p>屋顶角度: ${feature.properties.roofAngle}</p> <p>屋顶真实面积: ${feature.properties.roofArea} ㎡</p> </div> `);
内容的提问来源于stack exchange,提问作者David Guri
相关产品推荐
相关产品推荐

