R'deki GIS haritaları için Otomatik Etiket Yerleşimi


9

Şekil dosyalarında sfokumak için paketi (ve ilgili paketleri) ggplot2ve çizim için (ve arkadaşları) kullanarak R'de GIS haritaları yapıyorum . Bu iyi çalışıyor, ancak nehirler ve yollar gibi özellikler için (otomatik / programlı) etiket yerleşimleri oluşturmanın bir yolunu bulamıyorum. Bu özellikler tipik olarak düzensiz şekillere sahip linestringlerdir. Örneğin wikimedia'dan eklenen resme bakın.

resim açıklamasını buraya girin

ggrepelPaket otomatik bir şekilde noktalarını etiketlemek için iyi çalışır, ancak bu ayrık Lat / Long noktaları olmayan diğer coğrafi özellikler için çok mantıklı değil.

Bunu her özelliğe ayrı ayrı metin etiketleri yerleştirerek hayal edebiliyorum, ancak mümkünse daha otomatik bir şey arıyorum. Böyle bir otomasyonun önemsiz bir sorun olmadığını anlıyorum, ancak daha önce çözüldü (ArcGIS'in görünüşte bunu maplex adlı bir uzantıyla yapmanın bir yolu var, ancak yazılıma erişimim yok ve kalmak istiyorum Mümkünse R).

Bunu yapmanın bir yolunu bilen var mı?

MWE burada:

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

resim açıklamasını buraya girin


8
Amanın. Hayır, sadece prensip dışında değil. Nasıl planladığınızı veya ne kadar ilerlediğinizi veya bahsettiğiniz şeylerin coğrafi olmayan verilerle glifpel'de çalıştığını bilmiyorum. "Bu iyi çalışıyor" diyorsunuz ama "bu" nun ne olduğunu göstermeyin, bu da görmek ve üzerine inşa etmekte fayda sağlayacaktır. Bir örnek —sf ve spData gemisi örnek verileri gibi diğer uzamsal paketleri dahil etmek mümkün olabilirdi veya küçük bir kukla linestleme nesnesi yapabilirsiniz - ancak şu anda sadece bunlardan hangisinin durumunuza yardımcı olacağını tahmin edebiliriz ve bu sadece çok yararlı uzun vadeli
camille

8
Minimal tekrarlanabilir bir örnek sunmazsanız, temelde başkalarından sizin için bir tane yapmasını istersiniz. Aksi takdirde genellikle çok iyi bir cevap veremezler. Bu durumda, bir şekil dosyası bulmaları, nasıl kullandığınızı anlamaları, ggrepeldaha önce yapmış olduğunuz işleri yeniden yapmaları gerektiği anlamına gelir . Bu, yararlı bir cevap vermeniz olasılığını azaltır.
Axeman

3
MWE artık soruya dahil edildi. Reaksiyon için özür dilerim; Kaba olmak istemiyorum ve göndermeden önce insanların zamanını nasıl harcamamak gerektiğini çok düşündüm. Bana kavramsal bir cevap soruyordum - yani böyle bir araç var mı? - benim özel projeme özgü bir cevaptan ziyade.
invertdna

4
Harika, bu şimdi iyi bir örnek ve tahminimizi bize bıraksaydınız bu soruya cevap vermezdim. Bir aracın var olup olmadığı gibi kavramsal bir şey aramak, SO için konu dışı sayılır; sorular belirli bir soruna veya projeye bağlı olduklarında çok daha iyidir. Açıklığa kavuşturmak için, etiketlerin hedefin linestring kısmı boyunca açılı mı olması yoksa sadece özelliklerin yanına yerleştirilmesi mi?
camille

8
@camille İlk: İlk cevabım için gerçekten özür dilerim. SO'ya mesaj atmakta tereddüt ettim, çünkü anlam dolu ve bunun için kendimi desteklerken, kendim için ortalama biri oldum. Bu konuda kendimi çok kötü hissediyorum ve gerçekten üzgünüm. Eldeki soruya gelince: etiketlerin açılı olması gerekmez; daha geniş bağlamda (esas olarak yollar ve nehirler), linestringler düzensizdir ve bu nedenle muhtemelen etiketin çizgi boyunca bir yerde olması gerekir, ancak (önemli olarak) çizgiye paralel olması gerekir.
invertdna

Yanıtlar:


8

Sanırım senin için işe yarayabilecek bir şeyim var. Örneğinizi biraz daha gerçekçi bir şeye değiştirme özgürlüğünü aldım: her biri 100 puan uzunluğunda, düzgünleştirilmiş rastgele yürüyüşlerle yapılan birkaç rastgele "nehir":

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

Bunları örneğinize göre çizebiliriz:

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

resim açıklamasını buraya girin

Benim çözümüm temelde linestringlerden nokta çıkarmak ve bunları etiketlemek. Sorunuzun üst kısmındaki resim gibi, linestring boyunca her bir etiketin birden fazla kopyasını isteyebilirsiniz, bu nedenle n etiket istiyorsanız, n eşit aralıklı noktaları çıkarırsınız .

Tabii ki, her iki ırmağı etiket çakışması olmadan aynı anda etiketlemek isteyebilirsiniz, bu nedenle adlandırılmış bir liste olarak birden fazla coğrafi özelliği geçirebilmeniz gerekir.

İşte tüm bunları yapan bir işlev:

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

Bu nedenle, etiketlemek istediğimiz nesneleri şu şekilde adlandırılmış bir listeye koyarsak:

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

Sonra bunu yapabiliriz:

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

resim açıklamasını buraya girin


2
sfheaders::sf_linestring(obj = data.frame(x = x1, y = y1))sfüreten kodun bir kısmını kolaylaştıracaktır .
SymbolixAU
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.