Определить линейный объект на растровой карте и вернуть объект линейной формы, используя R - PullRequest
13 голосов
/ 07 марта 2012

Я хотел бы определить линейные объекты, такие как дороги и реки, на растровых картах и ​​преобразовать их в линейный пространственный объект (класс SpatialLines), используя R.

raster и sp пакеты могут использоваться для преобразования объектов из растров в векторные объекты полигонов (класс SpatialPolygons).rasterToPolygons() извлечет ячейки определенного значения из растра и вернет объект многоугольника.Продукт можно упростить, используя опцию dissolve=TRUE, которая вызывает для этого подпрограммы в пакете rgeos.

Все это прекрасно работает, но я бы предпочел, чтобы это был объект SpatialLines.Как я могу это сделать?

Рассмотрим этот пример:

## Produce a sinuous linear feature on a raster as an example
library(raster)
r <- raster(nrow=400, ncol=400, xmn=0, ymn=0, xmx=400, ymx=400)
r[] <- NA
x <-seq(1, 100, by=0.01)
r[cellFromRowCol(r, round((sin(0.2*x) + cos(0.06*x)+2)*100), round(x*4))] <- 1

## Quick trick to make it three cells wide
r[edge(r, type="outer")] <- 1

## Plot
plot(r, legend=FALSE, axes=FALSE)

Raster representation of linear feature as an example

## Convert linear feature to a SpatialPolygons object
library(rgeos)
rPoly <- rasterToPolygons(r, fun=function(x) x==1, dissolve=TRUE)
plot(rPoly)

SpatialPolygons representation of the linear feature

Будет ли лучший подходбыть, чтобы найти центральную линию через многоугольник?
Или есть существующий код, доступный для этого?

РЕДАКТИРОВАТЬ: Спасибо @mdsumner за указание, что это называется скелетонизацией.

Ответы [ 4 ]

16 голосов
/ 10 марта 2012

Вот мои усилия. План:

  • уплотнение линий
  • вычислить триангуляцию Делоне
  • возьмите средние точки и те точки, которые находятся в многоугольнике
  • построение взвешенного на расстоянии минимального остовного дерева
  • найди свой график диаметра пути

Уплотняющий код для стартеров:

densify <- function(xy,n=5){
  ## densify a 2-col matrix
  cbind(dens(xy[,1],n=n),dens(xy[,2],n=n))
}

dens <- function(x,n=5){
  ## densify a vector
  out = rep(NA,1+(length(x)-1)*(n+1))
  ss = seq(1,length(out),by=(n+1))
  out[ss]=x
  for(s in 1:(length(x)-1)){
    out[(1+ss[s]):(ss[s+1]-1)]=seq(x[s],x[s+1],len=(n+2))[-c(1,n+2)]
  }
  out
}

А теперь основное блюдо:

simplecentre <- function(xyP,dense){
require(deldir)
require(splancs)
require(igraph)
require(rgeos)

### optionally add extra points
if(!missing(dense)){
  xy = densify(xyP,dense)
} else {
  xy = xyP
}

### compute triangulation
d=deldir(xy[,1],xy[,2])

### find midpoints of triangle sides
mids=cbind((d$delsgs[,'x1']+d$delsgs[,'x2'])/2,
  (d$delsgs[,'y1']+d$delsgs[,'y2'])/2)

### get points that are inside the polygon 
sr = SpatialPolygons(list(Polygons(list(Polygon(xyP)),ID=1)))
ins = over(SpatialPoints(mids),sr)

### select the points
pts = mids[!is.na(ins),]

dPoly = gDistance(as(sr,"SpatialLines"),SpatialPoints(pts),byid=TRUE)
pts = pts[dPoly > max(dPoly/1.5),]

### now build a minimum spanning tree weighted on the distance
G = graph.adjacency(as.matrix(dist(pts)),weighted=TRUE,mode="upper")
T = minimum.spanning.tree(G,weighted=TRUE)

### get a diameter
path = get.diameter(T)

if(length(path)!=vcount(T)){
  stop("Path not linear - try increasing dens parameter")
}

### path should be the sequence of points in order
list(pts=pts[path+1,],tree=T)

}

Вместо буферизации в более ранней версии я вычисляю расстояние от каждой средней точки до линии многоугольника и беру только точки, которые а) находятся внутри и б) дальше от края, чем 1,5 от расстояния изнутри точка, наиболее удаленная от края.

Проблемы могут возникнуть, если многоугольник изгибается сам по себе, с длинными сегментами и без уплотнения. В этом случае граф является деревом и код сообщает об этом.

В качестве теста я оцифровал линию (s, объект SpatialLines), буферизовал ее (p), затем вычислил осевую линию и наложил их:

 s = capture()
 p = gBuffer(s,width=0.2)
 plot(p,col="#cdeaff")
 plot(s,add=TRUE,lwd=3,col="red")
 scp = simplecentre(onering(p))
 lines(scp$pts,col="white")

source line (red), polygon (blue) and recovered centreline (white)

Функция 'onering' просто получает координаты одного кольца от объекта SpatialPolygons, который должен быть только одним кольцом:

onering=function(p){p@polygons[[1]]@Polygons[[1]]@coords}

Захват пространственных линий с помощью функции «захват»:

capture = function(){p=locator(type="l")
            SpatialLines(list(Lines(list(Line(cbind(p$x,p$y))),ID=1)))}
3 голосов
/ 07 марта 2012

Вы можете получить границу этого многоугольника в виде SpatialLines путем прямого принуждения:

rLines <- as(rPoly, "SpatialLinesDataFrame")

Суммирование координат до единой "центральной линии" было бы возможно, но ничего непосредственного, о котором я знаю. Я думаю, что этот процесс обычно называется "скелетонизация":

http://en.wikipedia.org/wiki/Topological_skeleton

2 голосов
/ 09 марта 2012

Спасибо @klewis на gis.stackexchange.com за ссылку на этот элегантный алгоритм для поиска центральной линии (в ответ на связанный с этим вопрос, который я задал там).

Процесс требует нахождения координат на краю многоугольника, описывающего линейный объект, и выполнения тесселяции Вороного этих точек.Координаты плиток Вороного, которые попадают в многоугольник линейного объекта, попадают на центральную линию.Превратите эти точки в линию.

Тесселяция Вороного действительно эффективно выполняется в R с использованием пакета deldir и пересечений многоугольников и точек с пакетом rgeos.

## Find points on boundary of rPoly (see question)
rPolyPts <-  coordinates(as(as(rPoly, "SpatialLinesDataFrame"),
                "SpatialPointsDataFrame"))

## Perform Voronoi tessellation of those points and extract coordinates of tiles
library(deldir)
rVoronoi <- tile.list(deldir(rPolyPts[, 1], rPolyPts[,2]))
rVoronoiPts <- SpatialPoints(do.call(rbind, 
                 lapply(rVoronoi, function(x) cbind(x$x, x$y))))

## Find the points on the Voronoi tiles that fall inside 
## the linear feature polygon
## N.B. That the width parameter may need to be adjusted if coordinate
## system is fractional (i.e. if longlat), but must be negative, and less
## than the dimension of a cell on the original raster.
library(rgeos)
rLinePts <- gIntersection(gBuffer(rPoly, width=-1), rVoronoiPts)

## Create SpatialLines object
rLine <- SpatialLines(list(Lines(Line(rLinePts), ID="1")))

Полученный объект SpatialLines: SpatialLines object describing linear feature from a raster

0 голосов
/ 19 июля 2019

Я думаю, что идеальным решением было бы создать такой отрицательный буфер, который динамически достигает минимальной ширины и не ломается, когда значение слишком велико;сохраняет продолжение объекта и, в конце концов, рисует линию, если значение достигнуто.Но, к сожалению, это может быть очень требовательным к вычислениям, потому что это будет сделано, вероятно, поэтапно и проверяет, достаточно ли значения для конкретной точки, чтобы иметь точку (нашей средней линии).Возможно, вам не нужно иметь бесконечное количество шагов или, по крайней мере, какое-то параметризованное значение.

Я пока не знаю, как это реализовать.

...