You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从存储国家OpenStreetMap数据的DataFrame中提取特定城市的边界坐标(Polygon)

解决OpenStreetMap DataFrame获取城市边界Polygon的问题

首先,咱们得先搞清楚OSM里城市边界的存储逻辑,这大概率是你没拿到预期结果的核心原因:

1. OSM边界数据的结构特点

城市这类行政边界在OSM中一般是以relation类型存储的,而非单个的way或node。这类relation的标签通常包含:

  • name: 城市名称(比如你的"Baarle-Nassau")
  • type: "boundary"
  • admin_level: 行政层级(不同国家有差异,比如荷兰的城市一般是admin_level=8,你可以根据目标区域调整)

而且,relation本身并不直接存储坐标,它是由多个way类型的成员组成的,这些way才包含构成边界的具体坐标点。

2. 修正你的过滤逻辑

你当前的过滤只针对tags,但没限定元素类型,还可能漏掉了admin_level的筛选(避免拿到其他同名的非城市级边界)。先调整过滤条件,定位到正确的relation:

// 筛选目标城市的边界relation
val cityBoundaryRel = df.filter(
  df("type") === "relation" && 
  df("tags").getItem("name") === "Baarle-Nassau" && 
  df("tags").getItem("type") === "boundary" && 
  df("tags").getItem("admin_level") === "8" // 根据实际区域调整层级
)

3. 提取边界的坐标集合

拿到relation后,需要找到它关联的所有way,再从这些way中提取坐标:

步骤3.1 提取relation的关联way

OSM的relation字段里有members数组,每个元素包含该relation引用的way的id和类型。我们先展开这个数组:

import org.apache.spark.sql.functions.explode

// 展开members数组,筛选出way类型的成员并提取id
val relMembers = cityBoundaryRel.select(explode(df("members")).alias("member"))
  .select("member.ref", "member.type", "member.role")
  .filter($"member.type" === "way")
  .filter($"member.role" === "outer") // 优先取外边界,Baarle-Nassau有飞地,inner是内飞地边界
  .select($"member.ref".alias("way_id"))

步骤3.2 匹配way数据并提取坐标

从原DataFrame中找到这些way,然后提取它们的nodes坐标集合:

// 获取所有关联的way数据
val boundaryWays = df.filter(df("type") === "way")
  .join(relMembers, df("id") === relMembers("way_id"))
  .select("nodes")

// 展开way的nodes数组,拿到所有经纬度坐标
val boundaryCoordinates = boundaryWays.select(explode(df("nodes")).alias("coord"))
  .select($"coord.lon", $"coord.lat")

步骤3.3 组装成Polygon格式

最后把这些坐标整理成标准的Polygon格式(比如WKT格式):

import org.apache.spark.sql.functions.{collect_list, concat_ws, lit}

val polygonWkt = boundaryCoordinates.groupBy()
  .agg(collect_list(concat_ws(" ", $"lon", $"lat")).alias("coords"))
  .select(concat(lit("POLYGON(("), concat_ws(", ", $"coords"), lit("))")).alias("city_boundary_polygon"))

polygonWkt.show(false)

额外注意事项

  • 不同地区的admin_level可能不同,如果你不确定,可以先去掉这个条件,看看返回的relation再调整。
  • Baarle-Nassau本身存在大量飞地,如果你需要完整的边界,可能还要处理role=inner的way,最终生成带内环的Polygon。

内容的提问来源于stack exchange,提问作者gilianzz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 12:37:31