Vị trí nhãn tự động cho bản đồ GIS trong R


9

Tôi đang tạo bản đồ GIS trong R bằng cách sử dụng sfgói (và các gói có liên quan) để đọc trong shapefiles và ggplot2(và bạn bè) để vẽ đồ thị. Điều này hoạt động tốt, nhưng tôi không thể tìm cách (tự động / lập trình) tạo các vị trí nhãn cho các tính năng như sông và đường. Các tính năng này thường là linestrings, với hình dạng không đều. Xem hình ảnh đính kèm ví dụ từ wikidia.

nhập mô tả hình ảnh ở đây

Các ggrepelgói hoạt động tốt cho ghi nhãn điểm một cách tự động, nhưng điều này không có ý nghĩa nhiều đối với đặc điểm địa lý khác mà không phải là rời rạc điểm Lat / Long.

Tôi có thể tưởng tượng làm điều này bằng cách đặt các nhãn văn bản riêng lẻ trên từng tính năng, nhưng tôi đang tìm kiếm thứ gì đó tự động hơn, nếu có thể. Tôi nhận ra tự động hóa như vậy không phải là một vấn đề nhỏ, nhưng nó đã được giải quyết trước đây (ArcGIS rõ ràng có cách thực hiện điều này với một phần mở rộng gọi là maplex, nhưng tôi không có quyền truy cập vào phần mềm và tôi muốn ở lại R nếu có thể).

Có ai biết một cách làm điều này?

MWE ở đây:

#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")

nhập mô tả hình ảnh ở đây


8
Rất tiếc. Không, không chỉ ngoài nguyên tắc. Tôi không biết bạn đang âm mưu như thế nào hoặc bạn đã nhận được bao xa, hoặc những gì bạn đề cập đã hoạt động trong ggrepel với dữ liệu phi địa lý. Bạn nói "cái này hoạt động tốt" nhưng không cho thấy "cái này" là gì, sẽ hữu ích khi thấy và xây dựng. Có thể bao gồm một ví dụ về sf và các gói không gian khác như dữ liệu mẫu của tàu spData hoặc bạn có thể tạo một đối tượng linestring giả nhỏ nhưng ngay bây giờ chúng ta chỉ có thể đoán những thứ nào sẽ giúp ích cho tình huống của bạn, và đó chỉ là không hữu ích lâu dài
camille

8
Nếu bạn không cung cấp một ví dụ có thể lặp lại tối thiểu, về cơ bản, bạn đang yêu cầu người khác tạo một ví dụ cho bạn. Nếu không họ thường không thể đưa ra một câu trả lời rất tốt. Trong trường hợp này có nghĩa là họ sẽ cần tìm một shapefile, tìm hiểu cách bạn đang sử dụng ggrepel, về cơ bản làm lại công việc mà bạn đã thực hiện. Điều này làm cho nó ít có khả năng bạn sẽ có một câu trả lời hữu ích.
Axeman

3
MWE hiện bao gồm trong câu hỏi. Xin lỗi về phản ứng; Tôi không muốn tỏ ra thô lỗ và tôi đã suy nghĩ kỹ về cách không lãng phí thời gian của mọi người trước khi đăng. Dường như với tôi, tôi đã yêu cầu một câu trả lời theo khái niệm - tức là, một công cụ như vậy có tồn tại không? - thay vì một câu trả lời cụ thể cho dự án cụ thể của tôi.
invertdna

4
Thật tuyệt, đây là một ví dụ điển hình và không phải là ví dụ mà tôi sẽ nghĩ ra nếu bạn để chúng tôi đoán. Tìm kiếm một cái gì đó mang tính khái niệm như liệu một công cụ có tồn tại được coi là lạc đề đối với SO hay không; câu hỏi sẽ tốt hơn nhiều khi chúng được gắn với một vấn đề hoặc dự án cụ thể. Để làm rõ, có phải các nhãn được đặt dọc theo phần linestring của mục tiêu, hoặc chỉ để đặt chúng gần các tính năng?
camille

8
@camille Đầu tiên: Tôi thực sự xin lỗi vì đã trả lời đầu tiên. Tôi ngần ngại đăng lên SO vì nó đầy ý nghĩa, và trong khi chuẩn bị tinh thần cho điều đó, tôi đã trở thành người có ý nghĩa. Tôi cảm thấy khủng khiếp về điều đó, và tôi thực sự xin lỗi. Đối với câu hỏi trong tầm tay: các nhãn không cần phải được đặt trong góc; trong bối cảnh rộng hơn (đường và sông, chủ yếu), các đường nối không đều, và do đó, có lẽ nhãn chỉ cần ở đâu đó dọc theo đường, nhưng (quan trọng) song song với đường.
invertdna

Câu trả lời:


8

Tôi nghĩ rằng tôi có một cái gì đó có thể làm việc cho bạn. Tôi đã tự do thay đổi ví dụ của bạn thành một cái gì đó thực tế hơn một chút: một vài "dòng sông" ngẫu nhiên được thực hiện với các bước đi ngẫu nhiên được làm mịn, mỗi chiều dài 100 điểm:

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") 

Chúng tôi có thể vẽ chúng theo ví dụ của bạ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

nhập mô tả hình ảnh ở đây

Giải pháp của tôi về cơ bản là trích xuất các điểm từ linestrings và dán nhãn cho chúng. Giống như hình ảnh ở đầu câu hỏi của bạn, bạn có thể muốn có nhiều bản sao của mỗi nhãn dọc theo chiều dài của dòng, vì vậy nếu bạn muốn n nhãn, bạn chỉ cần trích xuất n điểm cách đều nhau.

Tất nhiên, bạn muốn có thể gắn nhãn cả hai con sông cùng một lúc mà không bị xung đột nhãn, vì vậy bạn sẽ cần có thể vượt qua nhiều tính năng địa lý dưới dạng danh sách được đặt tên.

Đây là một chức năng thực hiện tất cả điều đó:

linestring_labels <- function(linestrings, n)
{
  do.call(rbind, mapply(function(linestring, label)
  {
  n_points <- length(linestring)/2
  distance <- round(n_points / (n + 1))
  data.frame(x = linestring[1:n * distance],
             y = linestring[1:n * distance + n_points],
             label = rep(label, n))
  }, linestrings, names(linestrings), SIMPLIFY = FALSE)) %>%
  st_as_sf(coords = c("x","y"))
}

Vì vậy, nếu chúng ta đặt các đối tượng chúng ta muốn gắn nhãn vào một danh sách có tên như thế này:

river_list <- list("River 1" = river_1, "River 2" = river_2)

Sau đó chúng ta có thể làm điều này:

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")

nhập mô tả hình ảnh ở đây


2
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))sẽ giảm bớt một số sfmã tạo.
SymbolixAU
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.