首页 > 解决方案 > OSM API 方式节点排序不正确

问题描述

所以我的目标是能够从 OSM 中提取给定的 Way 以显示在 Leaflet 地图上。但是,当我尝试使用给定的方式时,响应中的节点似乎没有正确排序。

import axios from 'axios'
import xml2js from 'xml2js'

let parser = new xml2js.Parser()

export default {
  async getStpPolygon () {
    let xml = await axios.get('https://www.openstreetmap.org/api/0.6/way/39394541/full')

    return parseNodes(xml)
  },

  async getMplsPolygon () {
    let xml = await axios.get('https://www.openstreetmap.org/api/0.6/way/93481561/full')

    return parseNodes(xml)
  }
}

async function parseNodes (xml) {
  return new Promise((resolve, reject) => {
    parser.parseString(xml.data, (err, data) => {
      if (err) reject(err)

      let output = data.osm.node.map((node) => {
        return [
          parseFloat(node.$.lat),
          parseFloat(node.$.lon)
        ]
      })

      resolve(output)
    })
  })
}

这是官方 OSM 地图上显示方式 的示例...官方地图

这是我的传单地图上显示的方式。 我的地图

提前感谢您的帮助!

标签: javascriptleafletopenstreetmap

解决方案


下面是 XML 的样子:

<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.6.1 (18903 thorn-01.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
 <node id="1083701880" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:40Z" user="neuhausr" uid="16591" lat="44.9751170" lon="-93.2758411"/>
 <node id="1083701882" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:40Z" user="neuhausr" uid="16591" lat="44.9746502" lon="-93.2772842"/>
 <node id="1083701938" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:41Z" user="neuhausr" uid="16591" lat="44.9727679" lon="-93.2778367"/>
 <node id="1083701987" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:42Z" user="neuhausr" uid="16591" lat="44.9730222" lon="-93.2787594"/>
 <node id="1083701993" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:42Z" user="neuhausr" uid="16591" lat="44.9737736" lon="-93.2793709"/>
 <node id="1083702026" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:43Z" user="neuhausr" uid="16591" lat="44.9754130" lon="-93.2765707"/>
 <way id="93481561" visible="true" version="1" changeset="6873749" timestamp="2011-01-05T16:51:43Z" user="neuhausr" uid="16591">
  <nd ref="1083701993"/>
  <nd ref="1083701987"/>
  <nd ref="1083701938"/>
  <nd ref="1083701880"/>
  <nd ref="1083702026"/>
  <nd ref="1083701882"/>
  <nd ref="1083701993"/>
  <tag k="amenity" v="university"/>
  <tag k="name" v="University of St. Thomas"/>
 </way>
</osm>

请注意, OSM XML 格式中描述了两种类型的信息:

节点是 OpenStreetMap 数据模型中的核心元素之一。它由空间中的单个点组成,由其纬度、经度和节点 ID 定义。

  • 一种公开引用节点的有序nd标签列表的方法。

一种方式是节点的有序列表[...]

因此,要以正确的顺序获取坐标,您必须将解析函数修改为:

async function parseNodes (xml) {
    return new Promise((resolve, reject) => {
        parser.parseString(xml.data, (err, data) => {
            if (err) reject(err)

            //map node ids to their coordinates
            const refs = {};
            data.osm.node.forEach((node) => {
                const attrs = node.$;
                refs[attrs.id] = [+attrs.lat, +attrs.lon];
            });

            // return the coordinates in the correct order
            const output = data.osm.way.nd.map((nd) => {
                const id = nd.$.ref;
                return refs[id];
            });

            resolve(output)
        })
    })
}

和一个演示

async function  getStpPolygon () {
    const resp = await axios.get('https://www.openstreetmap.org/api/0.6/way/93481561/full')

    const json = xml2js(resp.data, {compact: true});
    const refs = {};
    json.osm.node.forEach((node) => {
        const attrs = node._attributes;
        refs[attrs.id] = [+attrs.lat, +attrs.lon];
    });
    
    return json.osm.way.nd.map((nd) => {
        const id = nd._attributes.ref;
        return refs[id];
    });
};


var map = L.map('map').setView([44.97386, -93.27569], 15);
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);


getStpPolygon().then((points) => {
    L.polyline(points).addTo(map);
});
#map { width: 100%; height:200px }
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xml-js@1.6.11/dist/xml-js.min.js"></script>
<script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>

<div id="map"></div>

小提琴可用:https ://jsfiddle.net/mnf9eL3o/


推荐阅读