AWa*_*ton 6 r openstreetmap r-sf
我正在使用 OSM 数据来创建矢量街道地图。对于道路,我使用 OSM 提供的线几何图形,并添加缓冲区以将线转换为看起来像道路的几何图形。
我的问题与几何相关,而不是 OSM,因此为了简单起见,我将使用基本线条。
library(dplyr)
library(ggplot2)
library(sf)
# two lines representing an intersection of roads
l1 <- st_as_sfc(c("LINESTRING(0 0,0 5)","LINESTRING(-5 3,5 3)"))
# union the two lines (so they "intersect" instead of overlap once buffered
l2 <- l1 %>% st_union()
ggplot()+
geom_sf(data = st_buffer(l2, dist = 0.75))
Run Code Online (Sandbox Code Playgroud)
这将产生如下结果:
最终,我尝试以类似于 Google、OSM、Bing 等地图 API 的方式渲染道路。这些内角圆角如下所示:
我已经搜索了 sf 包中的 st_ 系列方法,但没有找到解决方案。我发现的最接近的是st_buffer 的一些参数,它们控制端盖的形状和用于外角(但不是内角)的角度数量。
有没有一个简单/实用的解决方案,或者我最好习惯非圆形交叉路口?
谢谢
Lovalery 和 mrhellmann 提出了以下两种解决方案。
最终,我选择了 Mrhellmann 的回答,因为它符合我的特殊需求;然而,我确实比较并探索了下面分享的每一种的优点。我将这个讨论留在这里,因为其他人的用例可能略有不同,并且这些差异对于理解很有用。
让我们定义使用的不同方法:
l1 <- st_as_sfc(c("LINESTRING(0 0,0 5)","LINESTRING(-5 3,5 3)"))
l2 <- l1 %>% st_union()
l2.base <- l2 %>% st_buffer(dist = 0.75, endCapStyle = "ROUND") # OP
l2.neg <- l2.base %>% st_buffer(dist = -0.25) # mrhellmann (st_buffer)
l2.smth <- l2.base %>% smooth(method = "ksmooth", smoothness = 3, n= 50L)# Lovalery (smoothr)
Run Code Online (Sandbox Code Playgroud)
Lovalery 提出了一个很好的观点,负缓冲方法也使末端/四肢变得平滑。
比较这三种方法表明,负缓冲区方法影响的不仅仅是端盖,它通过 st_buffer 在所有边上使用的距离缩小几何形状。
l2.neg 问题可以通过将“平滑”值(负缓冲区中使用的值)添加到原始缓冲距离 (0.75 + 0.25) 来解决。
l3 <- l1 %>% st_union %>% st_buffer(dist = 0.75+0.25, endCapStyle = "ROUND")
l3.neg <- st_buffer(l3, dist = -0.25)
Run Code Online (Sandbox Code Playgroud)
通过这种调整,这些方法具有相似的结果。事实上,l3 可能比 l2.smth 更类似于 l2.base。此时,我可能会出于以下原因表达对 l3.neg 方法的偏好:
smoothr(顶点之间的平均距离的因子 - 下面详细介绍)更容易让用户理解。然而,Lovalery 关于线端精度的观点提出了另一个好观点。st_buffer 使用的默认 (ROUND) endCapStyle 通过缓冲区距离设置来扩展行的长度。
如果该地块限制所有街道末端的农作物,则这是无关紧要的。如果要在图中渲染街道末端,它们的长度应该是正确的。
更合适的缓冲区设置是endCapStyle = FLAT。请注意,当不安装圆形端盖时,l2.smth 的平滑度设置会从 变为smoothness = 3。smoothness = 0.2
线条长度按照 l2 和 l2.smth 方法的预期呈现。l2.neg 和 l3.neg 保留 90 度。末端的角度(“很好有”,但不是关键功能),但会通过平滑缓冲区中使用的负距离缩短线长度。点为smoothr.
最后,要挑剔的是, 提供的曲线smoothr不是对称的。平滑度值越大,顶点长度差异越大,不对称性越明显。这是smoothr使用smoothness = 1.
注意较长顶点上曲线的伸长。这在大多数尺度上可能并不明显。
正如 Lovalery 所提到的,这取决于什么最适合个人用例。另外,普通观众会注意到街道是否比 OSM 所说的短一米,或者曲线是否稍微不对称?精度是一件有趣的事情,就像图形一样……完美的精度可能并不重要,但它是数学和数字,所以感觉应该如此。
上面的所有代码:
library(dplyr)
library(ggplot2)
library(sf)
library(smoothr)
l1 <- st_as_sfc(c("LINESTRING(0 0,0 5)","LINESTRING(-5 3,5 3)"))#,"LINESTRING(-5 7,5 7)"))
l2 <- l1 %>% st_union()
#### endCapStyle = ROUND ####
l2.base <- l2 %>% st_buffer(dist = 0.75, endCapStyle = "ROUND")
l2.neg <- l2 %>% st_buffer(dist = -0.25)
l2.smth <- l2 %>% smooth(method = "ksmooth", smoothness = 3, n= 50L)
l3 <- l1 %>% st_union %>% st_buffer(dist = 0.75+0.25, endCapStyle = "ROUND")
l3.neg <- st_buffer(l3, dist = -0.25)
ggplot()+
geom_sf(data = l2.base, colour = "darkgreen", fill = "palegreen")+
geom_sf(data = l2.neg, colour = "blue", fill = NA)+
geom_sf(data = l2.smth, colour = "red", fill = NA)+
geom_sf(data = l3.neg, colour = "cyan", fill = NA)+
ggtitle("Method Comparison",
subtitle = "EndCapStyle ROUND: l2 (green) v l2.smth (red), l2.neg (blue), l3.neg (cyan)") +
scale_y_continuous(breaks = c(-1:6), limits = c(-1,6))+
scale_x_continuous(breaks = c(-6:6), limits = c(-6,6))
#### endCapStyle = FLAT ####
l2.base <- l1 %>% st_union() %>% st_buffer(dist = 0.75, endCapStyle = "FLAT")
l2.neg <- l2.base %>% st_buffer(dist = -0.25)
l2.smth <- l2.base %>% smooth(method = "ksmooth", smoothness = 0.2, n= 50L)
l3 <- l1 %>% st_union %>% st_buffer(dist = 0.75+0.25, endCapStyle = "FLAT")
l3.neg <- st_buffer(l3, dist = -0.25)
ggplot()+
geom_sf(data = l2.base, colour = "darkgreen", fill = "palegreen")+
geom_sf(data = l2.neg, colour = "blue", fill = NA)+
geom_sf(data = l2.smth, colour = "red", fill = NA)+
geom_sf(data = l3.neg, colour = "cyan", fill = NA)+
ggtitle("Method Comparison",
subtitle = "EndCapStyle FLAT: l2 (green) v l2.smth (red), l2.neg (blue), l3.neg (cyan)") +
scale_y_continuous(breaks = c(-1:6), limits = c(-1,6))+
scale_x_continuous(breaks = c(-6:6), limits = c(-6,6))
#### illustrate asymmetry of smoothr method as l4.smth ####
l4.smth <- l2.base %>% smooth(method = "ksmooth", smoothness = 1, n= 50L)
ggplot()+
geom_sf(data = l2.base, colour = "darkgreen", fill = "palegreen")+
geom_sf(data = l4.smth, colour = "red", fill = NA)+
geom_sf(data = l3.neg, colour = "blue", fill = NA)+
geom_abline(slope = 1, intercept = 3, colour = "gray", linetype = "dashed")+
ggtitle("Method Comparison",
subtitle = "EndCapStyle FLAT: l3.neg (blue), smoothr smoothness = 1 (red)") +
coord_sf(xlim = c(0,5), ylim = c(3,5))
Run Code Online (Sandbox Code Playgroud)
您可以缓冲这些行,然后对结果进行负缓冲:
ggplot()+
geom_sf(data = st_buffer(st_buffer(l2, dist = 1), dist = -0.5))
Run Code Online (Sandbox Code Playgroud)
更详细,更少嵌套:
library(dplyr)
library(ggplot2)
library(sf)
# two lines representing an intersection of roads
l1 <- st_as_sfc(c("LINESTRING(0 0,0 5)","LINESTRING(-5 3,5 3)"))
# union the two lines (so they "intersect" instead of overlap once buffered
l2 <- l1 %>% st_union()
# buffer the lines with a positive dist argument
l2_buffered <- st_buffer(l2, dist = 1)
# un-buffer (negative buffer?) the lines for rounded inner corners
# with a negative dist argument
l2_unbuffered <- st_buffer(l2_buffered, dist = -0.5)
Run Code Online (Sandbox Code Playgroud)
两者一起,红色的去缓冲多边形被黑色覆盖,除了弯曲的内角:
ggplot()+
geom_sf(data = st_buffer(st_buffer(l2, dist = 1.5), dist = -0.75), fill = NA, col = 'red') +
geom_sf(data = st_buffer(l2, dist = .75), fill = NA, col = 'black')
Run Code Online (Sandbox Code Playgroud)
放大一个角:
ggplot()+
geom_sf(data = st_buffer(st_buffer(l2, dist = 1.5), dist = -0.75), fill = NA, col = 'red') +
geom_sf(data = st_buffer(l2, dist = .75), fill = NA, col = 'black') +
coord_sf(xlim = c(-2,0), ylim = c(1,3))
Run Code Online (Sandbox Code Playgroud)