我有下面的代码,显示悬停时 OpenStreetMap 中建筑物的表面积。这是代码:
import { useState, useEffect, useRef, useCallback } from 'react';
import axios from 'axios';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import * as turf from '@turf/turf';
const debounce = (func: any, wait: any) => {
let timeout: any;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const MapComponent = () => {
const [area, setArea] = useState(0);
const [mousePosition, setMousePosition] = useState({ lat: 0, lon: 0 });
const mapRef = useRef(null);
// Initialize the map
useEffect(() => {
if (!mapRef.current) {
const map = L.map('map').setView([59.132659900251944, 9.727169813491393], 18);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
map.on('mousemove', (e) => {
setMousePosition({ lat: e.latlng.lat, lon: e.latlng.lng });
});
mapRef.current = map;
}
}, []);
const displayBuildings = useCallback((buildingData) => {
if (buildingData && mapRef.current) {
mapRef.current.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
mapRef.current.removeLayer(layer);
}
});
let totalArea = 0;
const nodeMapping = {};
buildingData.elements.forEach(element => {
if (element.type === 'node') {
nodeMapping[element.id] = { lat: element.lat, lon: element.lon };
}
});
const features = buildingData.elements.filter(element => element.type === 'way');
features.forEach(feature => {
if (feature.nodes && feature.nodes.length > 0) {
const coordinates = feature.nodes.map(nodeId => {
const node = nodeMapping[nodeId];
return [node.lat, node.lon]; // Lon, Lat format for Leaflet
});
if (coordinates.length > 0) {
L.polygon(coordinates, { color: 'blue' }).addTo(mapRef.current);
const geoJsonPolygon = {
type: 'Polygon',
coordinates: [coordinates],
};
totalArea += turf.area(geoJsonPolygon);
}
}
});
setArea(totalArea);
}
}, []);
// Function to fetch and display building data
const fetchAndDisplayBuildingData = useCallback(async (lat, lon) => {
try {
const response = await axios.post(
'https://overpass-api.de/api/interpreter',
`[out:json];
(
is_in(${lat},${lon});
area._[building];
);
out body; >; out skel qt;`,
{
headers: { 'Content-Type': 'text/plain' }
}
);
displayBuildings(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}, [displayBuildings]);
// Debounced version of fetchAndDisplayBuildingData
const debouncedFetchAndDisplay = useCallback(debounce(fetchAndDisplayBuildingData, 100), [
fetchAndDisplayBuildingData
]);
// Handle mouse movement
useEffect(() => {
if (mapRef.current) {
mapRef.current.on('mousemove', (e) => {
setMousePosition({ lat: e.latlng.lat, lon: e.latlng.lng });
debouncedFetchAndDisplay(e.latlng.lat, e.latlng.lng);
});
}
}, [debouncedFetchAndDisplay]);
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<p style={{ fontSize: '24px', textAlign: 'center' }}>Area: {area.toFixed(2)} square meters</p>
<p style={{ fontSize: '24px', textAlign: 'center' }}>Mouse Position: Latitude: {mousePosition.lat.toFixed(5)}, Longitude: {mousePosition.lon.toFixed(5)}</p>
<div id="map" style={{ height: '420px', width: '420px' }}></div>
</div>
);
};
export default MapComponent;
我希望它可以在单击时选择,然后保存该值。需要保存多座建筑物。另外,有没有办法也获取屋顶角度数据?
我需要角度数据,因为我需要计算建筑物屋顶的真实面积,而不仅仅是它在 2D 地图上占据的空间。然后需要保存这个特定的真实面积值。
我该如何解决这个问题?
有没有办法获取屋顶角度数据?不,我找不到解决方案。我检查了立交桥文档,没有建筑物或屋顶的角度数据。
此演示可以处理多个建筑物,通过在构建区域上单击鼠标来保存数据。
保存格式示例
Area: 197.70, Latitude: 59.13330, Longitude: 9.72721, Tags: {"building":"house","ref:bygningsnr":"300001805"}
添加功能标签并通过单击显示所有已保存的构建信息。
const handleMouseClick = () => {
const newDataItem = {
area: area.toFixed(2),
geo_location: {
lat: mousePosition.lat.toFixed(5),
longitude: mousePosition.lon.toFixed(5)
},
tags: buildingTag
};
setDataToSave([...dataToSave, newDataItem]);
};
从地图呼叫
<div> tag
<div id="map" style={{ height: '800px', width: '100%', maxWidth: '1000px' }} onClick={handleMouseClick}></div>
从 Overpass API 获取标签信息
if (element.type === 'way' && element.tags) {
setBuildingTag(JSON.stringify(element.tags));
}
数据来源
Overpass
{
"type": "way",
"id": 944874632,
"nodes": [
8747572457,
8747572456,
8747572455,
8747572454,
8747572453,
8747557628,
8747557629,
8747572452,
8747572451,
8747572457
],
"tags": {
"building": "house",
"building:levels": "2",
"ref:bygningsnr": "22480235"
}
}
import React, { useState, useEffect, useRef, useCallback } from 'react';
import axios from 'axios';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import * as turf from '@turf/turf';
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const MapComponent = () => {
const [area, setArea] = useState(0);
const [mousePosition, setMousePosition] = useState({ lat: 0, lon: 0 });
const [dataToSave, setDataToSave] = useState([]);
const [buildingTag, setBuildingTag] = useState("");
const mapRef = useRef(null);
// Initialize the map
useEffect(() => {
if (!mapRef.current) {
const map = L.map('map').setView([59.132659900251944, 9.727169813491393], 18);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
map.on('mousemove', (e) => {
setMousePosition({ lat: e.latlng.lat, lon: e.latlng.lng });
});
mapRef.current = map;
}
}, []);
const displayBuildings = useCallback((buildingData) => {
if (buildingData && mapRef.current) {
mapRef.current.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
mapRef.current.removeLayer(layer);
}
});
let totalArea = 0;
const nodeMapping = {};
buildingData.elements.forEach(element => {
if (element.type === 'node') {
nodeMapping[element.id] = { lat: element.lat, lon: element.lon };
}
if (element.type === 'way' && element.tags) {
setBuildingTag(JSON.stringify(element.tags));
}
});
const features = buildingData.elements.filter(element => element.type === 'way');
features.forEach(feature => {
if (feature.nodes && feature.nodes.length > 0) {
const coordinates = feature.nodes.map(nodeId => {
const node = nodeMapping[nodeId];
return [node.lat, node.lon]; // Lon, Lat format for Leaflet
});
if (coordinates.length > 0) {
L.polygon(coordinates, { color: 'blue' }).addTo(mapRef.current);
const geoJsonPolygon = {
type: 'Polygon',
coordinates: [coordinates],
};
totalArea += turf.area(geoJsonPolygon);
}
}
});
setArea(totalArea);
}
}, []);
// Function to fetch and display building data
const fetchAndDisplayBuildingData = useCallback(async (lat, lon) => {
try {
const response = await axios.post(
'https://overpass-api.de/api/interpreter',
`[out:json];
(
is_in(${lat},${lon});
area._[building];
);
out body; >; out skel qt;`,
{
headers: { 'Content-Type': 'text/plain' }
}
);
displayBuildings(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}, [displayBuildings]);
const handleMouseClick = () => {
const newDataItem = {
area: area.toFixed(2),
geo_location: {
lat: mousePosition.lat.toFixed(5),
longitude: mousePosition.lon.toFixed(5)
},
tags: buildingTag
};
setDataToSave([...dataToSave, newDataItem]);
};
const handleClearData = () => {
setDataToSave([]);
};
// Debounced version of fetchAndDisplayBuildingData
const debouncedFetchAndDisplay = useCallback(debounce(fetchAndDisplayBuildingData, 100), [
fetchAndDisplayBuildingData
]);
// Handle mouse movement
useEffect(() => {
if (mapRef.current) {
mapRef.current.on('mousemove', (e) => {
setMousePosition({ lat: e.latlng.lat, lon: e.latlng.lng });
debouncedFetchAndDisplay(e.latlng.lat, e.latlng.lng);
});
}
}, [debouncedFetchAndDisplay]);
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<p style={{ fontSize: '24px', textAlign: 'center' }}>Area: {area.toFixed(2)} square meters</p>
<p style={{ fontSize: '24px', textAlign: 'center' }}>Mouse Position: Latitude: {mousePosition.lat.toFixed(5)}, Longitude: {mousePosition.lon.toFixed(5)}</p>
<p style={{ fontSize: '24px', textAlign: 'center' }}>Tag: {buildingTag}</p>
<div id="map" style={{ height: '800px', width: '100%', maxWidth: '1000px' }} onClick={handleMouseClick}></div>
<button onClick={handleClearData}>Clear data</button>
<div style={{ maxHeight: '400px', overflowY: 'auto' }}>
<h2>Saved Data</h2>
<ul>
{dataToSave.map((data, index) => (
<li key={index}>
Area: {data.area}, Latitude: {data.geo_location.lat}, Longitude: {data.geo_location.longitude}, Tags: {data.tags}
</li>
))}
</ul>
</div>
</div>
);
};
export default MapComponent;