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

如何利用OpenStreetMap实现GPS点与陆地多边形的相交检测?

解决OSM陆地多边形拓扑错误导致的点位海陆识别失效问题

问题

手上有数千个GPS点位,部分在陆地、部分在海上,想用OpenStreetMap(OSM)的高分辨率陆地数据区分两类点位,但OSM数据存在几何方向倒置/拓扑孔洞问题,导致所有点位都被误判为海上。

复现代码

library(data.table)
library(leaflet)
library(sf)

pts  <- st_as_sf(data,
                 coords = c("longitude", "latitude"),
                 crs    = 4326)

sf_use_s2(FALSE)
# 下载land-polygons-split-4326.zip文件
land <- st_read("land_polygons.shp") 

land_poly <- land[st_geometry_type(land) %in% c("POLYGON", "MULTIPOLYGON"), ]
land_poly <- st_cast(land_poly, "MULTIPOLYGON") 
landm <- st_make_valid(land_poly)
st_crs(landm) 
bbox <- st_as_sfc(st_bbox(c(xmin = -2.5, xmax = 0, 
                            ymin = 36, ymax = 38)), crs = 4326)
st_crs(bbox) <- st_crs(landm)
land_bbox <- st_crop(landm, bbox)

on_land <- st_intersects(pts, land_bbox, sparse = FALSE)[, 1]
pts_os <- pts[!on_land, ]

测试数据

data <- structure(list(latitude = c(37.74492, 37.72914, 37.72934, 37.72892, 
37.72896, 37.72916, 37.72876, 37.73037, 37.73095, 37.73687, 37.74537, 
37.75453, 37.7285, 37.72851, 37.72719, 37.72886, 37.72716, 37.72805, 
37.7273, 37.72729, 37.72831, 37.72916, 37.7691, 37.72895, 37.72849, 
37.72842, 37.74227, 37.72788, 37.74085, 37.72928, 37.72702, 37.75486, 
37.72932, 37.72941, 37.72931, 37.7349, 37.7293, 37.72928, 37.72933, 
37.73162, 37.72934, 37.73019, 37.72742, 37.73006, 37.72956, 37.729, 
37.7293, 37.72905, 37.7293, 37.72611, 37.72924, 37.72927, 37.72613, 
37.72934, 37.72621, 37.7263, 37.7292, 37.72923, 37.72924, 37.72929, 
37.72931, 37.72929, 37.78725, 37.73048, 37.85666, 37.72901, 37.73468, 
37.77225, 37.73039, 37.72892, 37.72933, 37.72932, 37.72909, 37.72937, 
37.72919, 37.72943, 37.72953, 37.76244, 37.74427, 37.72896, 37.72899, 
37.72889, 37.72923, 37.7377, 37.72998, 37.72184, 37.72933, 37.74585, 
37.75051, 37.71287, 37.72912, 37.72924, 37.72942, 37.72931, 37.72922, 
37.72158, 37.72931, 37.76327, 37.77759, 37.72931), longitude = c(-0.71772, 
-0.7059, -0.70633, -0.70623, -0.70872, -0.70676, -0.70895, -0.70347, 
-0.70258, -0.70034, -0.69904, -0.70021, -0.70486, -0.70495, -0.70606, 
-0.70636, -0.70615, -0.70418, -0.70588, -0.70322, -0.70501, -0.70643, 
-0.71751, -0.70493, -0.7049, -0.70471, -0.71993, -0.70301, -0.70871, 
-0.70643, -0.70493, -0.72041, -0.7063, -0.70635, -0.70619, -0.70494, 
-0.70627, -0.70627, -0.70626, -0.70992, -0.70627, -0.70703, -0.7049, 
-0.70925, -0.70639, -0.70672, -0.70638, -0.70725, -0.70666, -0.70652, 
-0.70681, -0.70675, -0.70661, -0.70673, -0.70669, -0.70677, -0.70648, 
-0.70656, -0.70648, -0.70655, -0.70674, -0.7066, -0.71919, -0.70615, 
-0.73799, -0.70658, -0.71353, -0.71072, -0.70673, -0.70643, -0.70628, 
-0.70622, -0.70639, -0.70627, -0.70635, -0.70643, -0.70646, -0.72753, 
-0.71354, -0.7062, -0.7063, -0.70623, -0.70643, -0.71149, -0.70928, 
-0.72652, -0.70652, -0.71616, -0.71856, -0.73371, -0.70643, -0.70613, 
-0.70634, -0.70617, -0.70624, -0.71741, -0.70626, -0.73031, -0.73075, 
-0.70641)), row.names = c(NA, -100L), class = c("data.table", 
"data.frame"))

问题根源

OSM陆地多边形的拓扑错误本质是多边形环的方向不符合GIS规范:默认外多边形应为顺时针方向(平面坐标系),孔洞为逆时针方向。如果方向倒置,会导致GIS将整个地球识别为“孔洞”,陆地反而成为外围的负空间,最终所有点都被判定在“海上”。

修复方案

方案1:修正多边形环方向

转换到平面坐标系(如Web墨卡托3857),检查并反转不符合方向的多边形环,再转回WGS84:

library(data.table)
library(sf)

# 加载点位数据
pts  <- st_as_sf(data, coords = c("longitude", "latitude"), crs = 4326)

# 加载并处理OSM陆地数据
sf_use_s2(TRUE) # 启用S2球面几何,提升WGS84下的拓扑处理能力
land <- st_read("land_polygons.shp") 

# 过滤有效多边形并修复基础拓扑
land_poly <- land[st_geometry_type(land) %in% c("POLYGON", "MULTIPOLYGON"), ]
land_poly <- st_cast(land_poly, "MULTIPOLYGON") 
landm <- st_make_valid(land_poly)

# 裁剪到目标区域
bbox <- st_bbox(c(xmin = -2.5, xmax = 0, ymin = 36, ymax = 38), crs = 4326)
land_bbox <- st_crop(landm, bbox)

# 修正多边形环方向
land_bbox <- st_transform(land_bbox, 3857) # 转平面坐标系
land_bbox_fixed <- st_geometry(land_bbox) %>%
  lapply(function(multipoly) {
    lapply(multipoly, function(poly) {
      # 检查外环方向:顺时针为正,逆时针则反转
      if (st_is_closed(poly) && st_area(poly) > 0) {
        if (st_orientation(poly) == 1) {
          poly <- st_reverse(poly)
        }
      }
      poly
    }) %>%
      st_multipolygon()
  }) %>%
  st_sfc(crs = 3857) %>%
  st_transform(4326) %>%
  st_sf()

# 用st_within判断点是否在陆地内部(比st_intersects更严格)
on_land <- st_within(pts, land_bbox_fixed, sparse = FALSE)[, 1]
pts_os <- pts[!on_land, ]

# 验证结果
table(on_land) # 应同时存在TRUE(陆地)和FALSE(海上)的点位

方案2:使用osmdata包获取预处理的OSM数据

osmdata包会自动处理部分OSM拓扑问题,无需手动修复方向:

library(osmdata)
library(sf)

# 获取目标区域的陆地数据
land <- opq(bbox = c(-2.5, 36, 0, 38)) %>%
  add_osm_feature(key = "natural", value = "land") %>%
  osmdata_sf() %>%
  .$osm_multipolygons

# 后续点位判断逻辑同方案1
on_land <- st_within(pts, land, sparse = FALSE)[, 1]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:07:02