R code that evaluates line-of-sight (LOS) between two (lat, lon) points
Asked Answered
S

1

7

I'm having trouble figuring out how to calculate line-of-sight (LOS) between two (lat, lon) points, within R code. Any advice on how to approach this problem would be appreciated. I would like to use the R package - raster - for reading in the terrain elevation data. It seems the spgrass package could be leveraged (based on http://grass.osgeo.org/grass70/manuals/r.viewshed.html) but I wanted to avoid loading up a GIS. Thanks.

Scarborough answered 17/2, 2014 at 23:28 Comment(0)
D
11

If you just want to know if point A can see point B then sample a large number of elevations from the line joining A to B to form a terrain profile and then see if the straight line from A to B intersects the polygon formed by that profile. If it doesn't, then A can see B. Coding that is fairly trivial. Conversely you could sample a number of points along the straight line from A to B and see if any of them have an elevation below the terrain elevation.

If you have a large number of points to compute, or if your raster is very detailed, or if you want to compute the entire area visible from a point, then that might take a while to run.

Also, unless your data is over a large part of the earth, convert to a regular metric grid (eg a UTM zone) and assume a flat earth.

I don't know of any existing package having this functionality, but using GRASS really isn't that much of a hassle.

Here's some code that uses raster and plyr:

cansee <- function(r, xy1, xy2, h1=0, h2=0){
### can xy1 see xy2 on DEM r?
### r is a DEM in same x,y, z units
### xy1 and xy2 are 2-length vectors of x,y coords
### h1 and h2 are extra height offsets
###  (eg top of mast, observer on a ladder etc)
    xyz = rasterprofile(r, xy1, xy2)
    np = nrow(xyz)-1
    h1 = xyz$z[1] + h1
    h2 = xyz$z[np] + h2
    hpath = h1 + (0:np)*(h2-h1)/np
    return(!any(hpath < xyz$z))
}

viewTo <- function(r, xy, xy2, h1=0, h2=0, progress="none"){
    ## xy2 is a matrix of x,y coords (not a data frame)
    require(plyr)
    aaply(xy2, 1, function(d){cansee(r,xy,d,h1,h2)}, .progress=progress)
}

rasterprofile <- function(r, xy1, xy2){
### sample a raster along a straight line between two points
### try to match the sampling size to the raster resolution
    dx = sqrt( (xy1[1]-xy2[1])^2 + (xy1[2]-xy2[2])^2 )
    nsteps = 1 + round(dx/ min(res(r)))
    xc = xy1[1] + (0:nsteps) * (xy2[1]-xy1[1])/nsteps
    yc = xy1[2] + (0:nsteps) * (xy2[2]-xy1[2])/nsteps
    data.frame(x=xc, y=yc, z=r[cellFromXY(r,cbind(xc,yc))])
}

Hopefully fairly self-explanatory but maybe needs some real documentation. I produced this with it:

visibility

which is a map of the points where a 50m high person can see a 2m high tower at the red dot. Yes, I got those numbers wrong when I ran it. It took about 20 mins to run on my 4 year old PC. I suspect GRASS could do this almost instantaneously and more correctly too.

Diwan answered 17/2, 2014 at 23:44 Comment(3)
Spacedman, Thank you, but I think my lack of experience with the raster package is causing me problems. I used fra<-getData('alt', country="FRA", mask=T) and passed it into cansee(fra,c(43.96757, 4.534960),c(44.99035, 5.253814), 5,10) and got an NA. I'm guessing the getData was wrong from the start. How do I go about getting a small dem, like in your example? Thank you again.Scarborough
I get a 404 not found from getData, but I downloaded the DEM from the divagis web site. Then I noticed your x and y coords are the wrong way round, unless France is now 4 degrees north of the equator... Also, you will have to convert degrees in lat-long to metres otherwise the equations aren't correct.Diwan
I am commenting on this old thread since I am looking for a way to calculate viewshed in R without the use of additional tools (GRASS, ArcGIS) (as previously asked here: gis.stackexchange.com/questions/272122/…). With reference to the image posted by @Spacedman, I am wondering which part of his code he actually used to get that output. I guess it should be the viewTo() function, right? Also, did he use a matrix of all the xy coordinates of the DTM as xy2 parameter?Calamondin

© 2022 - 2024 — McMap. All rights reserved.