我正在try 使用天桥API、OpenStreetMap API、ReactJS来计算建筑的表面积.代码如下:

import { useState, useEffect } from 'react';
import axios from 'axios';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css'; // Import Leaflet CSS
import 'leaflet/dist/leaflet-src.esm'; // Import Leaflet as ES module
import * as turf from '@turf/turf'; // Import turf library

const MapComponent = () => {
  const [buildingData, setBuildingData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        // Make a request to the Overpass API
        const response = await axios.get('https://api.openstreetmap.org/api/interpreter');
        setBuildingData(response.data);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []); // Run once on component mount

  const calculateArea = () => {
    if (!buildingData || buildingData.features.length === 0) {
      return { areaSquareMeters: 0, areaSquareKilometers: 0 };
    }

    // Assuming buildingData is in a suitable format, adjust accordingly
    const osmCoordinates = buildingData.features[0].geometry.coordinates[0];

    // Convert OSM coordinates to Leaflet-compatible coordinates
    const leafletCoordinates = osmCoordinates.map(([lon, lat]) => [lat, lon]);

    // Create a Leaflet map with a simple base layer
    const map = L.map('map').setView([59.132659900251944, 9.727169813491393], 12);

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
    }).addTo(map);

    // Create a Leaflet polygon
    const polygon = new L.Polygon(leafletCoordinates);

    // Add the polygon to the map
    polygon.addTo(map);

    // Convert Leaflet polygon coordinates to GeoJSON format
    const geoJsonPolygon = {
      type: 'Polygon',
      coordinates: [leafletCoordinates],
    };

    // Calculate the area using the turf library
    const areaSquareMeters = turf.area(geoJsonPolygon);

    return {
      areaSquareMeters
    };
  };

  const area = calculateArea();

  return (
    <div style={{ width: "100%", height: "100vh", display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column" }}>
      <p>Area: {area.areaSquareMeters} square meters</p>
      <div id="map" style={{ height: '500px', width: '500px', border: "3.2px solid red" }}></div>
    </div>
  );
};

export default MapComponent;

我期望显示一个 map (id="map"容器),但它没有显示,并且区域输出为0.我已经发现问题出在overpass API Endpoint(https://api.openstreetmap.org/api/interpreter)上.

有什么办法可以解决这个问题吗?

推荐答案

您的组件需要在那里更改两个项目

API Endpoint:后来,您改用https://overpass-api.de/api/interpreter,这是专门为OpenStreetMap数据库上复杂而广泛的查询而设计的. 而不是GET,应该是POST呼叫.

Query Format:立交桥QL中的查询格式可能得到了增强,包括更具体的参数、过滤器或边界框坐标,以获取更有针对性的数据,如特定半径内的建筑物.

更多文档在这里

Overpass QL

Overpass API Wiki

Overpass Turbo

Wrong URL

https://api.openstreetmap.org/api/interpreter

enter image description here

Correct URL

https://overpass-api.de/api/interpreter

text格式正文

[out:json];
(
  // Fetch buildings in a specific area
  way["building"](around:3000,59.132659900251944, 9.727169813491393); 
);
out body;
>;
out skel qt;

enter image description here

Demo component

另存为‘MapComponent.js’

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 MapComponent = () => {
  const [buildingData, setBuildingData] = useState(null);
  const [area, setArea] = useState(0);
  const mapRef = useRef(null);

  // Initialize the map
  useEffect(() => {
    if (!mapRef.current) {
      mapRef.current = L.map('map').setView([59.132659900251944, 9.727169813491393], 13); // Centered on Paris
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      }).addTo(mapRef.current);
    }
  }, []);

  // Fetch building data
  useEffect(() => {
    const fetchData = async () => {
      try {

        const response = await axios.post(
          'https://overpass-api.de/api/interpreter',
          `[out:json];(way["building"](around:3000,59.132659900251944, 9.727169813491393););out body;>;out skel qt;`,
          {
            headers: { 'Content-Type': 'text/plain' }
          }
        );
  
        setBuildingData(response.data);

      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []);

  const calculateAndDisplayArea = useCallback(() => {
    if (buildingData && mapRef.current) {
      let totalArea = 0;
      const nodeMapping = {}; // Assuming you have this mapping from node ID to { lat, lon }
  
      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];
          });
  
          if (coordinates.length > 0) {
            L.polygon(coordinates).addTo(mapRef.current); // Add polygon to map
  
            const geoJsonPolygon = {
              type: 'Polygon',
              coordinates: [coordinates.map(coord => [coord[1], coord[0]])], // Convert to [lon, lat]
            };
  
            totalArea += turf.area(geoJsonPolygon);
          }
        }
      });
  
      setArea(totalArea);
    }
  }, [buildingData]);
  

  useEffect(() => {
    calculateAndDisplayArea();
  }, [calculateAndDisplayArea, buildingData]);

  return (
    <div>
      <p>Area: {area.toFixed(2)} square meters</p>
      <div id="map" style={{ height: '800px', width: '1000px', margin: '0 auto'}}></div>
    </div>
  );
};

export default MapComponent;

Result

enter image description here

文本"Area: 303102.35 square meters"表示 这意味着您查询的建筑物或要素覆盖的总面积约为303,102.35平方米.这是一个定量的测量这些建筑物占据了多少表面积.

Zoom in for seeing detail polygon enter image description here


Update Component Two different color building drawing

CulateAndDisplayArea函数判断每个功能的建筑标记,并为"办公"建筑应用蓝色,为"工业"建筑应用红色.

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 MapComponent = () => {
  const [buildingData, setBuildingData] = useState(null);
  const [area, setArea] = useState(0);
  const mapRef = useRef(null);

  // Initialize the map
  useEffect(() => {
    if (!mapRef.current) {
      mapRef.current = L.map('map').setView([59.132659900251944, 9.727169813491393], 13); // Centered on Paris
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      }).addTo(mapRef.current);
    }
  }, []);

  // Fetch building data
  useEffect(() => {
    const fetchData = async () => {
      try {

        const query = `[out:json];
          (
            way["building"="office"](around:3000,59.132659900251944, 9.727169813491393);
            way["building"="industrial"](around:3000,59.132659900251944, 9.727169813491393);
          );
          out body; >; out skel qt;`;

        const response = await axios.post(
          'https://overpass-api.de/api/interpreter', query, {
            headers: { 'Content-Type': 'text/plain' }
          }
        );
  
        setBuildingData(response.data);

      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, []);

  const calculateAndDisplayArea = useCallback(() => {
    if (buildingData && mapRef.current) {
      let totalArea = 0;
      const nodeMapping = {}; // Assuming you have this mapping from node ID to { lat, lon }
  
      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];
          });
  
          if (coordinates.length > 0) {
            // Check building type and apply color
            const buildingType = feature.tags?.building;
            const color = buildingType === 'office' ? 'blue' : buildingType === 'industrial' ? 'red' : 'grey';

            L.polygon(coordinates, { color }).addTo(mapRef.current); // Apply color to polygon

  
            const geoJsonPolygon = {
              type: 'Polygon',
              coordinates: [coordinates.map(coord => [coord[1], coord[0]])], // Convert to [lon, lat]
            };
  
            totalArea += turf.area(geoJsonPolygon);
          }
        }
      });
  
      setArea(totalArea);
    }
  }, [buildingData]);
  

  useEffect(() => {
    calculateAndDisplayArea();
  }, [calculateAndDisplayArea, buildingData]);

  return (
    <div>
      <p>Area: {area.toFixed(2)} square meters</p>
      <div id="map" style={{ height: '800px', width: '1000px', margin: '0 auto'}}></div>
    </div>
  );
};

export default MapComponent;

Result

enter image description here

Reactjs相关问答推荐

为什么Next.js 14无法解析页面路由路径?

如何访问子集合中的文档?

在新屏幕上显示照片时出现问题-react

在React中映射对象数组时,如何正确呈现JSX.Element或React.ReactNode类型?

安装使用环境的BIT组件的依赖项

shadcn输入+窗体+Zod;一个部件正在改变要被控制的不受控制的输入;

如何清除过滤器搜索的状态

Chart.js如何go 除边框

在 React 中将 MUI 样式外包到单独的文件中?

使用reactrouterdom时显示"对象无效作为React子组件"错误

无法读取未定义的属性(阅读 'editQuoteModalShown')reactredux

Zod 验证模式根据另一个数组字段使字段成为必需字段

如何在屏幕上使用相同的可重用

Select 项目后 Select HeadlessUI 组合框中的所有文本?

使用带有搜索字符串的全局过滤器时如何在 React-Table 中进行精确匹配?

ReactJs 行和列

react 测试显示错误的结果

react 页面更新

如果 URL 已随功能组件更改,则取消刷新

下拉菜单在 AppBar 中未正确对齐