如何从存储国家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
相关产品推荐
相关产品推荐

