ตำแหน่งฉลากอัตโนมัติสำหรับแผนที่ GIS ใน R


9

ฉันกำลังทำแผนที่ GIS ใน R โดยใช้sfแพ็คเกจ (และแพ็คเกจที่เกี่ยวข้อง) เพื่ออ่านในรูปแบบไฟล์และggplot2(และเพื่อน) สำหรับการลงจุด ใช้งานได้ดี แต่ฉันไม่สามารถหา (ป้ายกำกับอัตโนมัติ / โดยโปรแกรม) ไม่สามารถสร้างตำแหน่งป้ายกำกับสำหรับคุณสมบัติเช่นแม่น้ำและถนน คุณสมบัติเหล่านี้มักจะมีการเคลื่อนไหวโดยมีรูปร่างผิดปกติ ดูภาพที่แนบมาตัวอย่างจาก wikimedia

ป้อนคำอธิบายรูปภาพที่นี่

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

ป้อนคำอธิบายรูปภาพที่นี่


8
Yikes ไม่ใช่ไม่ใช่แค่หลักการ ฉันไม่ทราบว่าคุณวางแผนหรือใช้เวลานานแค่ไหนหรือสิ่งที่คุณพูดถึงได้ทำงานใน ggrepel ด้วยข้อมูลที่ไม่ใช่ทางภูมิศาสตร์ คุณพูดว่า "ใช้งานได้ดี" แต่อย่าแสดงว่า "นี่คืออะไร" ซึ่งจะเป็นประโยชน์ในการดูและต่อเติม อาจเป็นไปได้ที่จะรวมตัวอย่าง - เอสเอฟและแพ็คเกจอวกาศอื่น ๆ เช่นข้อมูลตัวอย่าง spData จัดส่งหรือคุณสามารถสร้างวัตถุจำลองขนาดเล็กจำลองได้ แต่ตอนนี้เราสามารถเดาได้ว่าสิ่งเหล่านั้นจะช่วยสถานการณ์ของคุณได้อย่างไร ระยะยาวไม่ได้มีประโยชน์มาก
คามิลล์

8
หากคุณไม่ให้ตัวอย่างที่ทำซ้ำได้เพียงเล็กน้อยคุณกำลังขอให้ผู้อื่นทำเพื่อคุณ มิฉะนั้นพวกเขามักจะไม่ได้รับคำตอบที่ดีมาก ในกรณีนี้หมายความว่าพวกเขาจะต้องค้นหารูปร่างไฟล์ลองคิดดูว่าคุณกำลังใช้งานggrepelอย่างไรโดยทั่วไปจะทำซ้ำงานที่คุณทำไปแล้ว สิ่งนี้ทำให้มีโอกาสน้อยที่คุณจะได้คำตอบที่มีประโยชน์
Axeman

3
ตอนนี้ MWE รวมอยู่ในคำถาม ขอโทษสำหรับปฏิกิริยา; ฉันไม่อยากหยาบคายและคิดหนักว่าจะไม่เสียเวลากับคนอื่นก่อนโพสต์อย่างไร ดูเหมือนว่าฉันกำลังขอคำตอบจากแนวคิด - เช่นมีเครื่องมือดังกล่าวอยู่หรือไม่? - แทนที่จะตอบเฉพาะโครงการของฉัน
invertdna

4
เจ๋งนี่เป็นตัวอย่างที่ดีและไม่ใช่สิ่งที่ฉันจะทำถ้าคุณปล่อยให้เราเดา กำลังมองหาบางอย่างที่เป็นแนวคิดเช่นว่ามีเครื่องมืออยู่หรือไม่ คำถามจะดีกว่ามากเมื่อเชื่อมโยงกับปัญหาหรือโครงการเฉพาะ หากต้องการชี้แจงให้ชัดเจนว่ามีป้ายกำกับทำมุมตามส่วนของเป้าหมายหรือเพียงแค่วางไว้ใกล้กับคุณสมบัติหรือไม่
คามิลล์

8
@camille First: ฉันขอโทษจริงๆสำหรับคำตอบแรกของฉัน ฉันลังเลที่จะโพสต์ไปที่ SO เพราะมันเต็มไปด้วยความถ่อมตัวและในการทำให้ตัวเองแข็งแรงสำหรับสิ่งนั้น ฉันรู้สึกแย่เกี่ยวกับเรื่องนั้นและฉันเสียใจจริงๆ สำหรับคำถามในมือ: ฉลากไม่จำเป็นต้องทำมุม ในบริบทที่กว้างขึ้น (ส่วนใหญ่ถนนและแม่น้ำ) การส่องสว่างนั้นไม่สม่ำเสมอและอาจเป็นเพราะฉลากต้องอยู่ที่ใดที่หนึ่งตามแนวเส้น (แต่สำคัญ) ขนานกับแนวเส้น
invertdna

คำตอบ:


8

ฉันคิดว่าฉันมีบางอย่างที่อาจเหมาะกับคุณ ฉันใช้เสรีภาพในการเปลี่ยนตัวอย่างของคุณเป็นอะไรที่ดูสมจริงกว่านี้สักหน่อย: "แม่น้ำ" สองสุ่มที่สร้างขึ้นด้วยการเดินแบบสุ่มที่นุ่มนวลความยาว 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") 

เราสามารถพล็อตได้ตามตัวอย่างของคุณ:

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

ป้อนคำอธิบายรูปภาพที่นี่

วิธีการแก้ปัญหาของฉันคือการแยกจุดออกจากการประกอบและติดฉลาก เหมือนภาพที่ด้านบนของคำถามของคุณที่คุณอาจต้องการหลายสำเนาของแต่ละฉลากตามความยาวของ LineString ดังนั้นถ้าคุณต้องการnป้ายคุณเพียงแค่ดึงnจุดเท่า ๆ กันระยะห่าง

แน่นอนว่าคุณต้องการติดป้ายแม่น้ำทั้งสองพร้อมกันโดยไม่ต้องติดป้ายดังนั้นคุณจะต้องสามารถผ่านคุณสมบัติทางภูมิศาสตร์หลาย ๆ รายการเป็นรายการที่มีชื่อ

นี่คือฟังก์ชั่นที่ทำทุกอย่างที่:

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

ดังนั้นถ้าเราใส่วัตถุที่เราต้องการติดป้ายกำกับไว้ในรายการที่มีชื่อดังนี้:

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

จากนั้นเราสามารถทำสิ่งนี้:

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

ป้อนคำอธิบายรูปภาพที่นี่


2
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))จะทำให้sfโค้ดที่ใช้ในการสร้างความสะดวกสบายลดลง
SymbolixAU
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.