Я делаю ГИС-карты в R, используя sf
пакет (и связанные с ним пакеты) для чтения в шейп-файлах и ggplot2
(и друзей) для построения графиков. Это работает нормально, но я не могу найти способ (автоматически / программно) создать места размещения меток для таких объектов, как реки и дороги. Эти особенности обычно представляют собой линейные линии неправильной формы. Смотрите изображение, прикрепленное, например, из Викимедиа.
ggrepel
Пакет хорошо работает для маркировки точек в автоматическом режиме, но это не имеет особого смысла для других географических объектов, которые не являются дискретными Lat / Long точки.
Я мог бы представить себе, делая отдельные текстовые метки для каждой функции индивидуально, но я ищу что-то более автоматизированное, если это возможно. Я понимаю, что такая автоматизация не является тривиальной проблемой, но она была решена раньше (очевидно, у 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")
ggrepel
, в основном переделать работу, которую вы уже сделали. Это значительно снижает вероятность того, что вы получите полезный ответ.Ответы:
Я думаю, у меня есть кое-что, что может сработать для вас. Я позволил себе сменить ваш пример на что-то более реалистичное: пару случайных «рек», сделанных сглаженными случайными прогулками, каждая длиной в 100 пунктов:
Мы можем построить их согласно вашему примеру:
Мое решение в основном состоит в том, чтобы извлечь точки из линейных линий и пометить их. Как и на картинке в верхней части вашего вопроса, вам может потребоваться несколько копий каждой метки по всей длине строки, поэтому, если вы хотите n меток, вы просто извлекаете n одинаково разнесенных точек.
Конечно, вы хотите иметь возможность пометить обе реки одновременно, без надписей, поэтому вам нужно будет передать несколько географических объектов в виде именованного списка.
Вот функция, которая делает все это:
Поэтому, если мы поместим объекты, которые мы хотим пометить в именованном списке, вот так:
Тогда мы можем сделать это:
источник
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))
облегчит частьsf
генерирующего кода.