R'deki çokgenlerin çizilmesi nasıl hızlandırılır?


24

Kuzey Amerika'nın ülke sınırlarını, bazı değişkenleri gösteren bir raster resmin üzerine çizmek ve ardından R kullanarak arsa üstüne konturları yerleştirmek istiyorum. Bunu, temel grafikleri ve kafes kullanarak yapmakta başarılı oldum. çok yavaş! Bunu henüz ggplot2'de yapmadım, ancak hız açısından daha iyi olacağından şüpheliyim.

Bir grib dosyasından oluşturulan bir netcdf dosyasında veri var . Şimdilik, R'yi SpatialPolygonsDataFrame nesneleri olarak okuyan GADM'den RData dosyalarında bulunan Kanada, ABD ve Meksika için ülke sınırlarını indirdim.

İşte bazı kod:

# Load packages
library(raster)
#library(ncdf) # If you cannot install ncdf4
library(ncdf4)

# Read in the file, get the 13th layer
# fn <- 'path_to_file'
r <- raster(fn, band=13)

# Set the projection and extent
p4 <- "+proj=lcc +lat_1=50.0 +lat_2=50.0 +units=km +x_0=32.46341 +y_0=32.46341 +lon_0=-107 +lat_0=1.0"
projection(r) <- CRS(p4)
extent(r) <- c(-5648.71, 5680.72, 1481.40, 10430.62)

# Get the country borders
# This will download the RData files to your working directory
can<-getData('GADM', country="CAN", level=1)
usa<-getData('GADM', country="USA", level=1)
mex<-getData('GADM', country="MEX", level=1)

# Project to model grid
can_p <- spTransform(can, CRS(p4))
usa_p <- spTransform(usa, CRS(p4))
mex_p <- spTransform(mex, CRS(p4))

### USING BASE GRAPHICS
par(mar=c(0,0,0,0))
# Plot the raster
bins <- 100
plot(r, axes=FALSE, box=FALSE, legend=FALSE,
     col=rev( rainbow(bins,start=0,end=1) ),
     breaks=seq(4500,6000,length.out=bins))
plot(r, legend.only=TRUE, col=rev( rainbow(bins,start=0,end=1)),
     legend.width=0.5, legend.shrink=0.75, 
     breaks=seq(4500,6000,length.out=bins),
     axis.args=list(at=seq(4500,6000,length.out=11),
                labels=seq(4500,6000,length.out=11),
                cex.axis=0.5),
     legend.args=list(text='Height (m)', side=4, font=2, 
                      line=2, cex=0.8))
# Plot the borders
# These are so slow!!
plot(can_p, add=TRUE, border='white', lwd=2)
plot(usa_p, add=TRUE, border='white', lwd=2)
plot(mex_p, add=TRUE, border='white', lwd=2)
# Add the contours
contour(r, add=TRUE, nlevel=5)

### USING LATTICE
library(rasterVis)

# Some settings for our themes
myTheme <- RdBuTheme()
myTheme$axis.line$col<-"transparent"
myTheme$add.line$alpha <- 1
myTheme2 <- myTheme
myTheme2$regions$col <- 'transparent'
myTheme2$add.text$cex <- 0.7
myTheme2$add.line$lwd <- 1
myTheme2$add.line$alpha <- 0.8

# Get JUST the contour lines
contours <- contourplot(r, margin=FALSE, scales=list(draw=FALSE),
                        par.settings=myTheme2, pretty=TRUE, key=NULL, cuts=5,
                        labels=TRUE)

# Plot the colour
levels <- levelplot(r, contour=FALSE, margin=FALSE, scales=list(draw=FALSE),
                    par.settings = myTheme, cuts=100)

# Plot!
levels +  
  layer(sp.polygons(can_p, col='green', lwd=2)) +
  layer(sp.polygons(usa_p, col='green', lwd=2)) +
  layer(sp.polygons(mex_p, col='green', lwd=2)) +
  contours

Çokgenlerin komplolarını hızlandırmanın bir yolu var mı? Üzerinde çalıştığım sistemde, çizim birkaç dakika sürüyor. Sonunda kolayca incelenmek üzere bu arazilerin bir kısmını oluşturacak bir işlev yapmak istiyorum ve sanıyorum ki bu haritaların çoğunu çizeceğim, bu yüzden arazilerin hızını artırmak istiyorum!

Teşekkürler!


Bunun gibi bir fikir, çokgen geometrisi alanında dizinler oluşturabilir misiniz?
Radar'ın Altında

@ Burton449 Üzgünüm, çokgenler, projeksiyonlar vb. Dahil olmak üzere R'deki haritalama ile ilgili şeylerde
yeniyim

2
Çizim penceresinden başka bir cihaza komplo kurmayı deneyebilirsiniz. Çizim işlevlerini pdf veya jpeg (ilişkili argümanlarla) içine sarın ve bu formatlardan birini alın. Bunun çok daha hızlı olduğunu buldum.
Jeffrey Evans

@JeffreyEvans Vay, evet. Bunu düşünmedim. Üç biçim dosyasını çizim penceresine çizmek yaklaşık 60 saniye sürdü, ancak bir dosyayı çizmek sadece 14 saniye sürdü. Eldeki görev için hala çok yavaş, ancak aşağıdaki yanıttaki bazı yöntemlerle birleştirildiğinde yararlı olabilir. Teşekkürler!
ialm

Yanıtlar:


30

Ülke sınırlarını R için şekil dosyalarından çizme hızını arttırmanın 3 yolunu buldum . Buradan ve buradan bir miktar ilham ve kod buldum .

(1) Çokgenlerin boylam ve enlemlerini almak için koordinatları şekil dosyalarından çıkarabiliriz. Sonra bunları, ilk boylamları içeren ilk sütun ve enlemleri içeren ikinci sütun içeren bir veri çerçevesine koyabiliriz. Farklı şekiller NA'larla ayrılmıştır.

(2) Bazı çokgenleri şekil dosyamızdan kaldırabiliriz. Şekil dosyası çok, çok ayrıntılı, ancak bazı şekiller önemsiz olan küçük adalar (arazilerim için, yine de). Daha büyük çokgenleri korumak için minimum bir çokgen alanı eşiği ayarlayabiliriz.

(3) Douglas-Peuker algoritmasını kullanarak şekillerimizin geometrisini basitleştirebiliriz . Çokgen şekillerimizin kenarları, orijinal dosyada çok karmaşık oldukları için basitleştirilebilir. Neyse ki, rgeosbunu uygulayan bir paket var .

Kurmak:

# Load packages
library(rgdal)
library(raster)
library(sp)
library(rgeos)

# Load the shape files
can<-getData('GADM', country="CAN", level=0)
usa<-getData('GADM', country="USA", level=0)
mex<-getData('GADM', country="MEX", level=0)

Yöntem 1: Şekil dosyalarından koordinatları bir veri çerçevesine çıkartın ve çizgileri çizin

En büyük dezavantajı, nesneyi projeksiyon gibi SpatialPolygonsDataFrame nesnesi olarak tutmaya kıyasla burada bazı bilgileri kaybetmemizdir. Bununla birlikte, onu bir sp nesnesine dönüştürebilir ve projeksiyon bilgisini geri ekleyebiliriz ve yine de orijinal verileri çizmekten daha hızlıdır.

Bu kodun orijinal dosya üzerinde çok yavaş çalıştığını unutmayın, çünkü çok fazla şekil vardır ve elde edilen veri çerçevesi ~ 2 milyon satır uzunluğundadır.

Kod:

# Convert the polygons into data frames so we can make lines
poly2df <- function(poly) {
  # Convert the polygons into data frames so we can make lines
  # Number of regions
  n_regions <- length(poly@polygons)

  # Get the coords into a data frame
  poly_df <- c()
  for(i in 1:n_regions) {
    # Number of polygons for first region
    n_poly <- length(poly@polygons[[i]]@Polygons)
    print(paste("There are",n_poly,"polygons"))
    # Create progress bar
    pb <- txtProgressBar(min = 0, max = n_poly, style = 3)
    for(j in 1:n_poly) {
      poly_df <- rbind(poly_df, NA, 
                       poly@polygons[[i]]@Polygons[[j]]@coords)
      # Update progress bar
      setTxtProgressBar(pb, j)
    }
    close(pb)
    print(paste("Finished region",i,"of",n_regions))
  }
  poly_df <- data.frame(poly_df)
  names(poly_df) <- c('lon','lat')
  return(poly_df)
}

Yöntem 2: Küçük çokgenleri kaldırın

Çok önemli olmayan birçok küçük ada var. Çokgen alanlarının bazılarını kontrol ederseniz, çoğunun küçük olduğunu görürüz. Kanada arsası için binden fazla poligondan sadece yüzlerce poligonun üzerine inmiştim.

Kanada için çokgen boyutu için miktarlar:

          0%          25%          50%          75%         100% 
4.335000e-10 8.780845e-06 2.666822e-05 1.800103e-04 2.104909e+02 

Kod:

# Get the main polygons, will determine by area.
getSmallPolys <- function(poly, minarea=0.01) {
  # Get the areas
  areas <- lapply(poly@polygons, 
                  function(x) sapply(x@Polygons, function(y) y@area))

  # Quick summary of the areas
  print(quantile(unlist(areas)))

  # Which are the big polygons?
  bigpolys <- lapply(areas, function(x) which(x > minarea))
  length(unlist(bigpolys))

  # Get only the big polygons and extract them
  for(i in 1:length(bigpolys)){
    if(length(bigpolys[[i]]) >= 1 && bigpolys[[i]] >= 1){
      poly@polygons[[i]]@Polygons <- poly@polygons[[i]]@Polygons[bigpolys[[i]]]
      poly@polygons[[i]]@plotOrder <- 1:length(poly@polygons[[i]]@Polygons)
    }
  }
  return(poly)
}

Yöntem 3: Çokgen şekillerin geometrisini basitleştirin

Çokgen şekillerimizdeki köşelerin sayısını paketin gSimplifyişlevini kullanarak azaltabilirizrgeos

Kod:

can <- getData('GADM', country="CAN", level=0)
can <- gSimplify(can, tol=0.01, topologyPreserve=TRUE)

Bazı kriterler:

system.timeGeçmiş zamanlarımı karşılaştırmak için geçenleri kullandım . Bunların sadece kontur çizgileri ve diğer ekstra şeyler olmadan ülkeleri çizme zamanları olduğunu unutmayın. Sp nesneleri için sadece plotişlevi kullandım . Veri çerçevesi nesneleri için, plotişlevi type='l've linesişlevini kullandım.

Orijinal Kanada, ABD ve Meksika poligonlarının çizilmesi:

73.009 saniye

Yöntem 1 kullanarak:

2.449 saniye

Yöntem 2'yi kullanarak:

17.660 saniye

Yöntem 3'ü kullanarak:

16.695 saniye

Yöntem 2 + 1'i Kullanarak:

1.729 saniye

Yöntem 2 + 3'ü Kullanma:

0,445 saniye

Yöntem 2 + 3 + 1'i kullanarak:

0.172 saniye

Diğer açıklamalar:

Metotların 2 + 3 kombinasyonunun poligonların çizilmesine yeterli hız kazandırdığı görülüyor. 2 + 3 + 1 yöntemlerini kullanmak, spnesnelerin hoş özelliklerini kaybetme sorununu da ekler ve temel zorluğum projeksiyon uygulamaktır. Veri çerçevesi nesnesini yansıtmak için birlikte bir şey hackledim, ancak oldukça yavaş çalışıyor. Bence 2 + 3 yöntemini kullanmak, benim için 2 + 3 + 1 yöntemini kullanmaya başladığım kadarıyla benim için yeterli hız artışı sağlıyor.


3
Yazma için +1, ki bu gelecekteki okuyucular yararlı bulacaktır.
SlowLearner

3

Herkes sp yerine sf (mekansal özellikler) paketine geçmeye çalışmalıdır. Bu önemli ölçüde daha hızlı (bu durumda 1/60) ve kullanımı daha kolaydır. İşte bir shp içinde okuma ve ggplot2 üzerinden çizim örneği.

Not: ggplot2'yi github'daki en son sürümden yeniden yüklemeniz gerekir (aşağıya bakın)

library(rgdal)
library(sp)
library(sf)
library(plyr)
devtools::install_github("tidyverse/ggplot2")
library(ggplot2)

# Load the shape files
can<-getData('GADM', country="CAN", level=0)
td <- file.path(tempdir(), "rgdal_examples"); dir.create(td)
st_write(st_as_sf(can),file.path(td,'can.shp'))


ptm <- proc.time()
  can = readOGR(dsn=td, layer="can")
  can@data$id = rownames(can@data)
  can.points = fortify(can, region="id")
  can.df = join(can.points, can@data, by="id")
  ggplot(can.df) +  geom_polygon(aes(long,lat,group=group,fill='NAME_ENGLISH'))
proc.time() - ptm

user  system elapsed 
683.344   0.980 684.51 

ptm <- proc.time()
  can2 = st_read(file.path(td,'can.shp'))  
  ggplot(can2)+geom_sf( aes(fill = 'NAME_ENGLISH' )) 
proc.time() - ptm

user  system elapsed 
11.340   0.096  11.433 

0

GADM verileri sahil şeridinin mekansal çözünürlüğüne çok yüksektir. İhtiyacınız yoksa daha genel veri seti kullanabilirsiniz. ialm'ın yaklaşımları çok ilginç, ama basit bir alternatif 'maptools' ile gelen 'wrld_simpl' verisini kullanmak.

library(maptools)
data(wrld_simpl)
plot(wrld_simpl)

Veri setimdeki şekilleri korumak istedim çünkü bölgenin ülkenin içindeki sınırları (örneğin iller ve eyaletler) içermekteydi. Aksi takdirde, haritaları harita veri paketinde kullanırdım!
ialm
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.