Converti lo shapefile della linea in raster, valore = lunghezza totale delle linee all'interno della cella


14

Ho uno shapefile di linea che rappresenta una rete stradale. Desidero rasterizzare questi dati, con i valori risultanti nel raster che mostrano la lunghezza totale delle linee che rientrano nella cella raster.

I dati si trovano nella proiezione della British National Grid, quindi le unità saranno metri.

Idealmente, vorrei eseguire questa operazione usando R, e suppongo che la rasterizefunzione del rasterpacchetto avrebbe un ruolo nel raggiungere questo obiettivo, non riesco proprio a capire quale dovrebbe essere la funzione applicata.


Forse vignette('over', package = 'sp')potrebbe aiutare.

Risposte:


12

A seguito di una domanda recente , potresti voler utilizzare le funzionalità offerte dal pacchetto rgeos per risolvere il tuo problema. Per motivi di riproducibilità, ho scaricato un file di forma delle strade della Tanzania da DIVA-GIS e l'ho inserito nella mia directory di lavoro corrente. Per le attività imminenti, avrai bisogno di tre pacchetti:

  • rgdal per la gestione generale dei dati spaziali
  • raster per la rasterizzazione dei dati dello shapefile
  • rgeos per controllare l'intersezione delle strade con il modello raster e calcolare le lunghezze delle strade

Di conseguenza, le tue prime righe potrebbero apparire così:

library(rgdal)
library(raster)
library(rgeos)

Successivamente, è necessario importare i dati dello shapefile. Si noti che gli shapefile DIVA-GIS sono distribuiti in EPSG: 4326, quindi proietterò lo shapefile su EPSG: 21037 (UTM 37S) per gestire i contatori anziché i gradi.

roads <- readOGR(dsn = ".", layer = "TZA_roads")
roads_utm <- spTransform(roads, CRS("+init=epsg:21037"))

Per la successiva rasterizzazione, sarà necessario un modello raster che copra l'estensione spaziale del file di forma. Il modello raster è composto da 10 righe e 10 colonne per impostazione predefinita, evitando così tempi di calcolo troppo lunghi.

roads_utm_rst <- raster(extent(roads_utm), crs = projection(roads_utm))

Ora che il modello è impostato, passa in rassegna tutte le celle del raster (che attualmente consiste solo di valori NA). Assegnando un valore di '1' alla cella corrente e successivamente eseguendo rasterToPolygons, il file di forma risultante 'tmp_shp' mantiene automaticamente l'estensione del pixel attualmente elaborato. gIntersectsrileva se questa estensione si sovrappone alle strade. In caso contrario, la funzione restituirà un valore di '0'. In caso contrario, il file di forma della strada viene ritagliato dalla cella corrente e la lunghezza totale di "SpatialLines" all'interno di quella cella viene calcolata utilizzando gLength.

lengths <- sapply(1:ncell(roads_utm_rst), function(i) {
  tmp_rst <- roads_utm_rst
  tmp_rst[i] <- 1
  tmp_shp <- rasterToPolygons(tmp_rst)

  if (gIntersects(roads_utm, tmp_shp)) {
    roads_utm_crp <- crop(roads_utm, tmp_shp)
    roads_utm_crp_length <- gLength(roads_utm_crp)
    return(roads_utm_crp_length)
  } else {
    return(0)
  }
})

Infine, puoi inserire le lunghezze calcolate (che vengono convertite in chilometri) nel modello raster e verificare visivamente i risultati.

roads_utm_rst[] <- lengths / 1000

library(RColorBrewer)
spplot(roads_utm_rst, scales = list(draw = TRUE), xlab = "x", ylab = "y", 
       col.regions = colorRampPalette(brewer.pal(9, "YlOrRd")), 
       sp.layout = list("sp.lines", roads_utm), 
       par.settings = list(fontsize = list(text = 15)), at = seq(0, 1800, 200))

lines2raster


Questo e spettacolare! Ho cambiato sapply()per pbsapply()e utilizzato l'argomento del cluster cl = detectCores()-1. Ora sono in grado di eseguire questo esempio in parallelo!
philiporlando,

11

Il seguito è modificato dalla soluzione di Jeffrey Evans. Questa soluzione è molto più veloce in quanto non utilizza la rasterizzazione

library(raster)
library(rgdal)
library(rgeos)

roads <- shapefile("TZA_roads.shp")
roads <- spTransform(roads, CRS("+proj=utm +zone=37 +south +datum=WGS84"))
rs <- raster(extent(roads), crs=projection(roads))
rs[] <- 1:ncell(rs)

# Intersect lines with raster "polygons" and add length to new lines segments
rsp <- rasterToPolygons(rs)
rp <- intersect(roads, rsp)
rp$length <- gLength(rp, byid=TRUE) / 1000
x <- tapply(rp$length, rp$layer, sum)
r <- raster(rs)
r[as.integer(names(x))] <- x

Sembra il metodo più efficiente ed elegante di quelli elencati. Inoltre, non avevo mai visto raster::intersect() prima, mi piace che combini gli attributi delle caratteristiche intersecate, a differenza rgeos::gIntersection().
Matt SM

+1 sempre bello vedere soluzioni più efficienti!
Jeffrey Evans,

@RobertH, ho provato a utilizzare questa soluzione per un altro problema, in cui voglio fare lo stesso di quello richiesto in questo thread, ma con un file di forme molto ampio di strade (per un intero continente). Sembra aver funzionato, ma quando provo a fare la figura come fatto da @ fdetsch, non ottengo una griglia contigua ma solo alcuni quadrati colorati nell'immagine (vedi in tinypic.com/r/20hu87k/9 ).
Doon_Bogan,

E con la massima efficienza ... con il mio set di dati di esempio: tempo di esecuzione 0,6 secondi per questa soluzione rispetto a 8,25 secondi per la soluzione più votata.
user3386170

1

Non è necessario un ciclo for. Basta intersecare tutto in una volta e quindi aggiungere lunghezze di linea ai nuovi segmenti di linea utilizzando la funzione "SpatialLinesLengths" in sp. Quindi, usando la funzione rasterize del pacchetto raster con l'argomento fun = sum è possibile creare un raster con la somma della lunghezza (e) della linea che interseca ciascuna cella. L'uso della risposta sopra e dei dati associati qui è un codice che genererà gli stessi risultati.

require(rgdal)
require(raster)
require(sp)
require(rgeos)

setwd("D:/TEST/RDSUM")
roads <- readOGR(getwd(), "TZA_roads")
  roads <- spTransform(roads, CRS("+init=epsg:21037"))
    rrst <- raster(extent(roads), crs=projection(roads))

# Intersect lines with raster "polygons" and add length to new lines segments
rrst.poly <- rasterToPolygons(rrst)
  rp <- gIntersection(roads, rrst.poly, byid=TRUE)
    rp <- SpatialLinesDataFrame(rp, data.frame(row.names=sapply(slot(rp, "lines"), 
                               function(x) slot(x, "ID")), ID=1:length(rp), 
                               length=SpatialLinesLengths(rp)/1000) ) 

# Rasterize using sum of intersected lines                            
rd.rst <- rasterize(rp, rrst, field="length", fun="sum")

# Plot results
require(RColorBrewer)
spplot(rd.rst, scales = list(draw=TRUE), xlab="x", ylab="y", 
       col.regions=colorRampPalette(brewer.pal(9, "YlOrRd")), 
       sp.layout=list("sp.lines", rp), 
       par.settings=list(fontsize=list(text=15)), at=seq(0, 1800, 200))

La prima volta che vedo SpatialLinesLengths. Immagino che non sia mai troppo tardi per imparare, grazie (: rasterizeimpiega abbastanza tempo, però (7 volte più lungo dell'approccio superiore sulla mia macchina).
fdetsch,

Ho notato che la rasterizzazione era lenta. Tuttavia, per grandi problemi un ciclo for rallenterà davvero le cose e penso che vedrai una soluzione molto più ottimizzata con rasterizzazione. Inoltre lo sviluppatore del pacchetto raster si sta impegnando per rendere ogni versione più ottimizzata e più veloce.
Jeffrey Evans,

Un potenziale problema che ho riscontrato con questa tecnica è che la rasterize()funzione include tutte le linee che toccano una determinata cella. Ciò comporta che in alcuni casi le lunghezze dei segmenti di linea vengono contate due volte: una volta nella cella dovrebbero e una volta nella cella adiacente che l'endpoint della linea tocca appena.
Matt SM

Sì, ma "rp" è l'oggetto che viene rasterizzato, ovvero l'intersezione di poligoni e punti.
Jeffrey Evans,

1

Ecco ancora un altro approccio. Si discosta da quelli già forniti utilizzando il spatstatpacchetto. Per quanto posso dire, questo pacchetto ha la sua versione di oggetti spaziali (ad es. imVs. rasteroggetti), ma il maptoolspacchetto consente la conversione avanti e indietro traspatstat oggetti e oggetti spaziali standard.

Questo approccio è preso da questo post di R-sig-Geo .

require(sp)
require(raster)
require(rgdal)
require(spatstat)
require(maptools)
require(RColorBrewer)

# Load data and transform to UTM
roads <- shapefile('data/TZA_roads.shp')
roadsUTM <- spTransform(roads, CRS("+init=epsg:21037"))

# Need to convert to a line segment pattern object with maptools
roadsPSP <- as.psp(as(roadsUTM, 'SpatialLines'))

# Calculate lengths per cell
roadLengthIM <- pixellate.psp(roadsUTM, dimyx=10)

# Convert pixel image to raster in km
roadLength <- raster(dtanz / 1000, crs=projection(roadsUTM))

# Plot
spplot(rtanz, scales = list(draw=TRUE), xlab="x", ylab="y", 
       col.regions=colorRampPalette(brewer.pal(9, "YlOrRd")), 
       sp.layout=list("sp.lines", roadsUTM), 
       par.settings=list(fontsize=list(text=15)), at=seq(0, 1800, 200))

Imgur

Il bit più lento sta convertendo le strade da SpatialLinesin un modello di segmento di linea (cioè spatstat::psp). Una volta fatto, le parti di calcolo della lunghezza effettiva sono piuttosto rapide, anche per risoluzioni molto più elevate. Ad esempio, sul mio vecchio MacBook 2009:

system.time(pixellate(tanzpsp, dimyx=10))
#    user  system elapsed 
#   0.007   0.001   0.007

system.time(pixellate(tanzpsp, dimyx=1000))
#    user  system elapsed 
#   0.146   0.032   0.178 

Hmmmm ... Vorrei che quegli assi non fossero in notazione scientifica. Qualcuno ha idea di come risolverlo?
Matt SM

È possibile modificare le impostazioni R globali e disattivare la notazione utilizzando: options (scipen = 999)), tuttavia, non so se il reticolo rispetterà le impostazioni globali dell'ambiente.
Jeffrey Evans

0

Lascia che ti presenti la vena del pacchetto con diverse funzioni per lavorare le linee spaziali e importare sf e data.table

library(vein)
library(sf)
library(cptcity)
data(net)
netsf <- st_as_sf(net) #Convert Spatial to sf
netsf <- st_transform(netsf, 31983) # Project data
netsf$length_m  <- st_length(netsf)
netsf <- netsf[, "length_m"]
g <- make_grid(netsf, width = 1000) #Creat grid of 1000m spacing with columns id for each feature
# Number of lon points: 12
# Number of lat points: 11

gnet <- emis_grid(netsf, g)
plot(gnet["length_m"])

sf_to_raster <- function(x, column, ncol, nrow){
  x <- sf::as_Spatial(x)
  r <- raster::raster(ncol = ncol, nrow = nrow)
  raster::extent(r) <- raster::extent(x)
  r <- raster::rasterize(x, r, column)
  return(r)
}

rr <- sf_to_raster(gnet, "length_m", 12, 11)
spplot(rr, sp.layout = list("sp.lines", as_Spatial(netsf)),
       col.regions = cpt(), scales = list(draw = T))
spplot(rr, sp.layout = list("sp.lines", as_Spatial(netsf)),
       col.regions = cpt(pal = 5176), scales = list(draw = T))
spplot(rr, sp.layout = list("sp.lines", as_Spatial(netsf)),
       col.regions = lucky(), scales = list(draw = T))
# Colour gradient: neota_flor_apple_green, number: 6165

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


-1

Questo può sembrare un po 'ingenuo, ma se si tratta di un sistema stradale, seleziona le strade e salva su una lavagna per appunti, quindi cerca uno strumento che ti consenta di aggiungere un buffer agli appunti impostandolo sulla larghezza legale della strada, ovvero 3 metri +/- ricorda che il buffer è dalla linea centrale al bordo * 2 i per ciascun lato, quindi un buffer di 3 metri è in realtà una strada di 6 metri da un lato all'altro.


Cosa ha a che fare la larghezza della strada con la lunghezza della strada. Questa risposta non affronta la domanda.
alphabetasoup
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.