如何修复Overpass API端点问题以实现建筑面积计算与地图展示?
修复方案
你的问题核心是没有向Overpass API发送有效的查询语句,直接请求https://api.openstreetmap.org/api/interpreter不会返回任何建筑数据,导致buildingData一直为空,进而面积计算为0,地图也无法正常初始化。以下是具体修复步骤:
1. 构造有效的Overpass API查询
Overpass API需要你提供Overpass QL查询语句来获取指定的地理数据。比如要获取你指定坐标附近的建筑,可以用如下查询:
[out:json][timeout:25]; ( way["building"](59.12,9.71,59.14,9.74); ); out body; >; out skel qt;
这个查询会返回坐标范围(59.12,9.71)到(59.14,9.74)内的所有建筑数据,格式为OSM原生结构,后续需要转成GeoJSON。
正确的API请求方式
使用GET请求时,需要将查询语句作为data参数传递:
const response = await axios.get('https://api.openstreetmap.org/api/interpreter', { params: { data: '[out:json][timeout:25];(way["building"](59.12,9.71,59.14,9.74););out body;>;out skel qt;' } });
2. 修复组件逻辑问题
- 避免重复创建地图:地图应该在数据加载完成后初始化一次,而不是每次计算面积都重新创建。
- 处理异步数据:初始状态下
buildingData为null,必须等待数据加载完成后再执行地图初始化和面积计算。 - 转换OSM数据为GeoJSON:Overpass返回的是OSM原生格式,需要转成标准GeoJSON才能被turf和Leaflet正常识别。
修改后的完整代码
import { useState, useEffect } from 'react'; import axios from 'axios'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import * as turf from '@turf/turf'; import osmtogeojson from 'osmtogeojson'; // 需要安装此包 // 修复Leaflet默认图标路径问题(可替换为本地图标路径) delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: '/assets/marker-icon-2x.png', iconUrl: '/assets/marker-icon.png', shadowUrl: '/assets/marker-shadow.png', }); const MapComponent = () => { const [buildingData, setBuildingData] = useState(null); const [area, setArea] = useState(0); const [map, setMap] = useState(null); useEffect(() => { const fetchData = async () => { try { // 构造Overpass QL查询 const overpassQuery = `[out:json][timeout:25];(way["building"](59.12,9.71,59.14,9.74););out body;>;out skel qt;`; const response = await axios.get('https://api.openstreetmap.org/api/interpreter', { params: { data: overpassQuery } }); // 将OSM数据转换为标准GeoJSON const geoJsonData = osmtogeojson(response.data); setBuildingData(geoJsonData); } catch (error) { console.error('获取数据失败:', error); } }; fetchData(); }, []); // 初始化地图并计算面积 useEffect(() => { if (!buildingData || buildingData.features.length === 0) return; // 仅初始化一次地图 if (!map) { const newMap = L.map('map').setView([59.132659900251944, 9.727169813491393], 16); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', }).addTo(newMap); setMap(newMap); } // 计算第一个建筑的表面积 const firstBuilding = buildingData.features[0]; if (firstBuilding.geometry.type === 'Polygon') { const calculatedArea = turf.area(firstBuilding); setArea(calculatedArea); // 将建筑多边形添加到地图 const leafletCoords = firstBuilding.geometry.coordinates[0].map(([lon, lat]) => [lat, lon]); L.polygon(leafletCoords, { color: 'red' }).addTo(map); } }, [buildingData, map]); return ( <div style={{ width: "100%", height: "100vh", display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column" }}> <p>表面积: {area.toFixed(2)} 平方米</p> <div id="map" style={{ height: '500px', width: '500px', border: "3.2px solid red" }}></div> </div> ); }; export default MapComponent;
额外说明
- 执行
npm install osmtogeojson安装数据转换依赖包 - 地图初始化和面积计算逻辑拆分到独立的
useEffect中,确保只在数据加载完成后执行 - 对面积结果做了保留两位小数的格式化,提升可读性
内容的提问来源于stack exchange,提问作者David Guri
相关产品推荐
相关产品推荐

