Estou fazendo mapas GIS no R usando o sf
pacote (e pacotes relacionados) para ler em shapefiles e ggplot2
(e amigos) para plotagem. Isso funciona bem, mas não consigo encontrar (automaticamente / programaticamente) canais de etiqueta para recursos como rios e estradas. Esses recursos geralmente são cadeias de linhas, com formas irregulares. Veja a imagem em anexo, por exemplo, da wikimedia.
O ggrepel
pacote funciona bem para rotular pontos de maneira automatizada, mas isso não faz muito sentido para outros recursos geográficos que não são pontos Lat / Long distintos.
Eu poderia imaginar isso colocando rótulos de texto individuais em cada recurso individualmente, mas estou procurando algo mais automatizado, se possível. Sei que essa automação não é um problema trivial, mas já foi resolvido antes (o ArcGIS aparentemente tem uma maneira de fazer isso com uma extensão chamada maplex, mas eu não tenho acesso ao software e gostaria de ficar em R se possível).
Alguém sabe de uma maneira de fazer isso?
MWE aqui:
#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")
ggrepel
, basicamente refazer o trabalho que você já fez. Isso torna muito menos provável que você tenha uma resposta útil.Respostas:
Eu acho que tenho algo que pode funcionar para você. Tomei a liberdade de mudar seu exemplo para algo um pouco mais realista: alguns "rios" aleatórios feitos com passeios aleatórios suaves, cada um com 100 pontos:
Podemos plotá-los conforme seu exemplo:
Minha solução é basicamente extrair pontos das cadeias de linhas e rotulá-los. Como na figura na parte superior da sua pergunta, você pode querer várias cópias de cada rótulo ao longo do comprimento da cadeia de linhas; portanto, se você quiser n rótulos, extraia n pontos igualmente espaçados.
É claro que você deseja rotular os dois rios de uma só vez sem que os rótulos colidam; portanto, você deve passar vários recursos geográficos como uma lista nomeada.
Aqui está uma função que faz tudo isso:
Portanto, se colocarmos os objetos que queremos rotular em uma lista nomeada como esta:
Então podemos fazer isso:
fonte
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))
facilitará parte dosf
código de geração.