R中GIS地图的自动标签放置

inv*_*dna 13 gis r spatial ggrepel r-sf

我正在使用sf包(和相关包)在 R 中制作 GIS 地图以读取 shapefile 和ggplot2(和朋友)进行绘图。这工作正常,但我找不到(自动/以编程方式)为河流和道路等要素创建标签放置的方法。这些特征通常是具有不规则形状的线串。请参阅例如来自维基媒体的图片。

在此处输入图片说明

ggrepel软件包适用于以自动方式标记点,但这对于不是离散的纬度/经度点的其他地理特征没有多大意义。

我可以想象通过在每个功能上单独放置单独的文本标签来做到这一点,但如果可能的话,我正在寻找更自动化的东西。我意识到这种自动化不是一个小问题,但之前已经解决了(ArcGIS 显然有一种方法可以使用名为 Maplex 的扩展程序来做到这一点,但我无法访问该软件,我想留在R 如果可能)。

有谁知道这样做的方法?

MWE在这里:

#MWE Linestring labeling

library(tidyverse)
library(sf)
library(ggrepel)
set.seed(120)

#pick a county from the built-in North Carolina dataset
BuncombeCounty <- st_read(system.file("shapes/", package="maptools"), "sids") %>% 
  filter(NAME == "Buncombe") 

#pick 4 random points in that county
pts_sf <- data.frame(
  x = seq(-82.3, -82.7, by=-0.1) %>% 
    sample(4),
  y = seq(35.5, 35.7, by=0.05) %>% 
    sample(4),
  placenames = c("A", "B", "C", "D")
) %>% 
  st_as_sf(coords = c("x","y")) 

#link those points into a linestring
linestring_sf <- pts_sf %>% 
  st_coordinates() %>%
  st_linestring()
  st_cast("LINESTRING") 

#plot them with labels, using geom_text_repel() from the `ggrepel` package
ggplot() +
  geom_sf(data = BuncombeCounty) +
  geom_sf(data = linestring_sf) +
  geom_label_repel(data = pts_sf,
                  stat = "sf_coordinates",
                  aes(geometry = geometry,
                      label = placenames),
                  nudge_y = 0.05,
                  label.r = 0, #don't round corners of label boxes
                  min.segment.length = 0,
                  segment.size = 0.4,
                  segment.color = "dodgerblue")
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

All*_*ron 11

我想我有一些可能对你有用的东西。我冒昧地将您的示例更改为更现实的东西:几条由平滑的随机游走制成的随机“河流”,每条 100 点长:

library(tidyverse)
library(sf)
library(ggrepel)

BuncombeCounty <- st_read(system.file("shapes/", package = "maptools"), "sids") %>% 
                  filter(NAME == "Buncombe")
set.seed(120)

x1 <- seq(-82.795, -82.285, length.out = 100)
y1 <- cumsum(runif(100, -.01, .01))
y1 <- predict(loess(y1 ~ x1, span = 0.1)) + 35.6

x2 <- x1 + 0.02
y2 <- cumsum(runif(100, -.01, .01))
y2 <- predict(loess(y2 ~ x2, span = 0.1)) + 35.57

river_1 <- data.frame(x = x1, y = y1)     %>% 
           st_as_sf(coords = c("x", "y")) %>%
           st_coordinates()               %>%
           st_linestring()                %>%
           st_cast("LINESTRING") 

river_2 <- data.frame(x = x2, y = y2)     %>% 
           st_as_sf(coords = c("x", "y")) %>%
           st_coordinates()               %>%
           st_linestring()                %>%
           st_cast("LINESTRING") 
Run Code Online (Sandbox Code Playgroud)

我们可以根据您的示例绘制它们:

library(tidyverse)
library(sf)
library(ggrepel)

BuncombeCounty <- st_read(system.file("shapes/", package = "maptools"), "sids") %>% 
                  filter(NAME == "Buncombe")
set.seed(120)

x1 <- seq(-82.795, -82.285, length.out = 100)
y1 <- cumsum(runif(100, -.01, .01))
y1 <- predict(loess(y1 ~ x1, span = 0.1)) + 35.6

x2 <- x1 + 0.02
y2 <- cumsum(runif(100, -.01, .01))
y2 <- predict(loess(y2 ~ x2, span = 0.1)) + 35.57

river_1 <- data.frame(x = x1, y = y1)     %>% 
           st_as_sf(coords = c("x", "y")) %>%
           st_coordinates()               %>%
           st_linestring()                %>%
           st_cast("LINESTRING") 

river_2 <- data.frame(x = x2, y = y2)     %>% 
           st_as_sf(coords = c("x", "y")) %>%
           st_coordinates()               %>%
           st_linestring()                %>%
           st_cast("LINESTRING") 
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我的解决方案基本上是从线串中提取点并标记它们。就像问题顶部的图片一样,您可能需要沿线串长度的每个标签的多个副本,因此如果您需要n 个标签,您只需提取n个等距的点。

当然,您希望能够同时标注两条河流而不会发生标注冲突,因此您需要能够将多个地理要素作为命名列表传递。

这是一个完成所有这些工作的函数:

riverplot  <- ggplot() +
              geom_sf(data = BuncombeCounty) +
              geom_sf(data = river_1, colour = "blue", size = 2) +
              geom_sf(data = river_2, colour = "blue", size = 2)

riverplot
Run Code Online (Sandbox Code Playgroud)

因此,如果我们将要标记的对象放在一个命名列表中,如下所示:

river_list <- list("River 1" = river_1, "River 2" = river_2)
Run Code Online (Sandbox Code Playgroud)

然后我们可以这样做:

riverplot + 
   geom_label_repel(data = linestring_labels(river_list, 3),
                    stat = "sf_coordinates",
                    aes(geometry = geometry, label = label),
                    nudge_y = 0.05,
                    label.r = 0, #don't round corners of label boxes
                    min.segment.length = 0,
                    segment.size = 0.4,
                    segment.color = "dodgerblue")
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • `sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))` 将简化一些 `sf` 生成代码。 (3认同)