Перетворити файл форми лінійки в растровий, значення = загальна довжина рядків у комірці


14

У мене є профіль форми лінії, що представляє дорожню мережу. Я хотів би растеризувати ці дані, при цьому отримані значення в растрі показують загальну довжину ліній, що потрапляють у растрову клітинку.

Дані містяться в прогнозах британської національної сітки, тому одиниці будуть лічильниками.

В ідеалі я хотів би виконати цю операцію, використовуючи R, і я здогадуюсь, що rasterizeфункція з rasterпакету зіграє певну роль у досягненні цього, я просто не можу розробити, якою має бути застосована функція.


Можливо, vignette('over', package = 'sp')може допомогти.

Відповіді:


12

Після останнього запитання ви можете скористатися функціями, запропонованими пакетом rgeos, щоб вирішити вашу проблему. З метою відтворюваності я завантажив з DIVA-GIS форм-файл танзанійських доріг і помістив його у свій поточний робочий каталог. Для майбутніх завдань вам знадобляться три пакети:

  • rgdal для загальної обробки просторових даних
  • растр для растерізації даних форм-файлів
  • rgeos перевірити перехрестя доріг за допомогою растрового шаблону та обчислити довжину дороги

Отже, ваші перші рядки можуть виглядати так:

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

Після цього вам потрібно імпортувати дані форми файлу. Зауважте, що файли форм DIVA-GIS поширюються у форматі EPSG: 4326, тому я спроектую файл форми в EPSG: 21037 (UTM 37S) для вирішення метрів, а не градусів.

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

Для подальшої растерізації вам знадобиться шаблон растра, який охоплює просторовий обсяг вашого файлу форм. Растровий шаблон складається з 10 рядків і 10 стовпців за замовчуванням, таким чином уникаючи занадто великого часу обчислень.

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

Тепер, коли шаблон налаштований, проведіть через усі комірки растра (який наразі складається лише із значень NA). Призначаючи поточній комірці значення "1" та згодом виконуючи rasterToPolygons, отриманий формуляр 'tmp_shp' автоматично утримує ступінь оброблюваного пікселя. gIntersectsвиявляє, чи збігається ця ступінь з дорогами. Якщо ні, функція поверне значення "0". В іншому випадку файл форми дороги обрізається поточною коміркою і загальна довжина 'SpatialLines' у цій комірці обчислюється за допомогою 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)
  }
})

Нарешті, ви можете вставити обчислені довжини (які перетворюються на кілометри) у растровий шаблон і візуально перевірити свої результати.

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

лінії2raster


Це круто! Я змінив sapply()до pbsapply()і використовувати кластерний аргумент cl = detectCores()-1. Тепер я можу запустити цей приклад паралельно!
philiporlando

11

Нижче видозмінено рішення Джефрі Еванса. Це рішення відбувається набагато швидше, оскільки не використовує растерізацію

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

Здається, найефективніший та елегантний метод із перелічених. Крім того, не бачив raster::intersect() раніше, мені подобається, що він поєднує атрибути перерізаних рис, на відміну від rgeos::gIntersection().
Метт СМ

+1 завжди приємно бачити ефективніші рішення!
Джефрі Еванс

@RobertH, я спробував використати це рішення для іншої проблеми, де я хочу зробити те саме, що просили в цій темі, але з дуже великою формою доріг (для цілого континенту). Здається, це спрацювало, але коли я намагаюся зробити фігуру, як це зроблено @ fdetsch, я не отримую суцільної сітки, а лише кілька кольорових квадратів на малюнку (див. У tinypic.com/r/20hu87k/9 ).
Doon_Bogan

І найефективніше ... з моїм набором даних вибірки: час роботи 0,6 сек для цього рішення проти 8,25 сек для найбільш обґрунтованого рішення.
user3386170

1

Вам не потрібна петля. Просто пересікайте все одразу, а потім додайте довжини рядків до нових сегментів рядків, використовуючи функцію "SpatialLinesLengths" в sp. Тоді, використовуючи функцію растрового пакету растрових даних за допомогою аргументу fun = sum, ви можете створити растр із сумою довжини (рядків), що перетинає кожну комірку. Використовуючи вищевказану відповідь та пов’язані з цим дані, тут є код, який дасть однакові результати.

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

Перший раз бачу SpatialLinesLengths. Здогадайтесь, ніколи не пізно вчитися, дякую (: rasterizeзаймає досить довго, хоча (у 7 разів довше, ніж верхній підхід на моїй машині).
fdetsch

Я помітив, що растерзання відбувається повільно. Однак, для великих проблем "цикл" дійсно сповільнить справи, і я думаю, що ви побачите набагато більш оптимізоване рішення з розширенням. Крім того, розробник растрового пакету прагне зробити кожен випуск більш оптимізованим та швидшим.
Джеффрі Еванс

Одне з потенційних проблем, які я знайшов у цій техніці, полягає в тому, що rasterize()функція включає всі лінії, які стосуються певної комірки. Це призводить до того, що довжина відрізка ліній в деяких випадках підраховується двічі: один раз у комірці, якою вони належать, та один раз у сусідній комірці, до якої лише торкається кінцевої точки лінії.
Метт СМ

Так, але "rp" - це об'єкт, який розкривається, і це перетин багатокутників і точок.
Джефрі Еванс

1

Ось ще один підхід. Це відхиляється від тих, які вже були надані за допомогою spatstatпакету. Наскільки я можу сказати, у цього пакета є своя версія просторових об'єктів (наприклад, imпроти rasterоб’єктів), але maptoolsпакет дозволяє перетворювати назад і назад між spatstatоб'єктами і стандартними просторовими об'єктами.

Цей підхід взято з цього посту 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))

Імгур

Найповільніший біт - це перетворення доріг із SpatialLinesшаблону лінійного сегмента (тобто spatstat::psp). Після того, як це зроблено, фактичні частини обчислення довжини проходять досить швидко, навіть при набагато більшій роздільній здатності. Наприклад, на моєму старому 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 

Гмммм ... я, звичайно, хотів би, щоб ці оси не були в науковій нотації. Хтось має ідею, як це виправити?
Matt SM

Ви можете змінити глобальні налаштування R та відключити позначення, використовуючи: options (scipen = 999)), однак, я не знаю, чи ґрати будуть шанувати глобальні налаштування середовища.
Джефрі Еванс

0

Дозвольте представити вам пакетну вену з кількома функціями для роботи просторових ліній та імпорту sf та 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

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення


-1

Це може здатися трохи наївним, але якщо це дорожня система, тоді виберіть дороги та збережіть на дошці для скріплення, тоді подивіться, знайдіть інструмент, який дозволяє додати буфер до буфера обміну, встановивши його на законну ширину дороги, тобто 3 метри +/- пам’ятайте, що буфер знаходиться від центральної лінії до краю * 2 i для кожної сторони, тому 3-метровий буфер - це фактично 6-метрова дорога з боку в бік.


Що стосується ширини дороги з довжиною дороги. Ця відповідь не стосується питання.
alphabetasoup
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.