Estoy haciendo mapas SIG en R usando el sf
paquete (y paquetes relacionados) para leer en shapefiles, y ggplot2
(y amigos) para trazar. Esto funciona bien, pero no puedo encontrar la manera (automática / programáticamente) de crear ubicaciones de etiquetas para características como ríos y carreteras. Estas características son típicamente cadenas lineales, con formas irregulares. Ver imagen adjunta, por ejemplo, de wikimedia.
El ggrepel
paquete funciona bien para etiquetar puntos de manera automatizada, pero esto no tiene mucho sentido para otras características geográficas que no son puntos Lat / Long discretos.
Me imagino haciendo esto colocando etiquetas de texto individuales en cada función individualmente, pero estoy buscando algo más automatizado, si es posible. Me doy cuenta de que esa automatización no es un problema trivial, pero se ha resuelto antes (ArcGIS aparentemente tiene una forma de hacerlo con una extensión llamada maplex, pero no tengo acceso al software y me gustaría quedarme en R si es posible).
¿Alguien sabe de una manera de hacer esto?
MWE aquí:
#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
, básicamente rehacer el trabajo que ya ha hecho. Esto hace que sea mucho menos probable que obtenga una respuesta útil.Respuestas:
Creo que tengo algo que podría funcionar para ti. Me he tomado la libertad de cambiar su ejemplo a algo un poco más realista: un par de "ríos" aleatorios hechos con caminatas aleatorias suavizadas, cada una de 100 puntos de largo:
Podemos trazarlos según su ejemplo:
Mi solución es básicamente extraer puntos de las cadenas lineales y etiquetarlos. Al igual que la imagen en la parte superior de su pregunta, es posible que desee varias copias de cada etiqueta a lo largo de la cadena de líneas, por lo que si desea n etiquetas, simplemente extraiga n puntos igualmente espaciados.
Por supuesto, desea poder etiquetar ambos ríos a la vez sin que las etiquetas entren en conflicto, por lo que deberá poder pasar múltiples características geográficas como una lista con nombre.
Aquí hay una función que hace todo eso:
Entonces, si ponemos los objetos que queremos etiquetar en una lista con nombre como esta:
Entonces podemos hacer esto:
fuente
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))
facilitará parte delsf
código generador.