Convert Begin and End Coordinates into Spatial Lines in R

Convert Begin and End Coordinates into Spatial Lines in R

Here's a way:

## raw list to store Lines objects
l <- vector("list", nrow(begin.coord))
library(sp)
for (i in seq_along(l)) {
l[[i]] <- Lines(list(Line(rbind(as.matrix(begin.coord[i, ], end.coord[i,])))), as.character(i))
}

SpatialLines(l)

This makes a separate Lines object (each with a unique ID) for each pair, you otherwise might want a single object?

And just for fun, build as a spatstat psp object first and then coerce with methods in maptools:

library(spatstat)
p <- psp(begin.coord[,1], begin.coord[,2], end.coord[,1], end.coord[,2], owin(range(c(begin.coord[,1], end.coord[,1])), range(c(begin.coord[,2], end.coord[,2]))))

library(maptools)
as(p, "SpatialLines")

Retain start and end IDs when creating spatial lines from X Y points

In short:

newds2 <- SpatialLinesDataFrame(l.spatial, newds1, match.ID = FALSE)  
## or you can use the rownames of newds1 in the lines loop)

There are 42 distinct lines, made by matching every coordinate to each other coordinate once.

FWIW, you don't need to convert from factor for numeric:

 x <- c(2,4,6,3,7,9,1)
y <- c(6,4,8,2,9,6,1)
id <- c("a","b","c","d","e","f","g")
## don't coerce to character in the
##first place cbind(x, y, id) *must* be
## character and then data.frame
## converts characters to factors
dataset <- data.frame(x = x, y = y, id = id)

There are other ways to simplify your task, but here's a reasonably straightforward way (I think this is what you are after):

x <- c(2,4,6,3,7,9,1)
y <- c(6,4,8,2,9,6,1)
id<-c("a","b","c","d","e","f","g")
## don't coerce to character in the first place cbind(x, y, id) must be character
## and then data.frame converts characters to factors by default
dataset<-data.frame(x = x, y = y, id = id)

l <- vector("list", nrow(dataset) * (nrow(dataset) - 1)) 
origID <- destID <- character(length(l))
##xy <- as.matrix(dataset[, c("x", "y")])
cnt <- 0
for (i in seq(nrow(dataset))) {
pt0 <- as.matrix(dataset[i, c("x", "y") ])
pts <- dataset[-i, ]
for (j in seq(nrow(pts))) {
cnt <- cnt + 1
l[[cnt]] <- Lines(list(Line(rbind(pt0, as.matrix(pts[j, c("x", "y")])))), as.character(cnt))
destID[cnt] <- pts$id[j]
origID[cnt] <- dataset$id[i]
}
}

x <- SpatialLinesDataFrame(SpatialLines(l), data.frame(dest = destID, orig = origID, row.names = as.character(1:cnt)))

Pick out one line and investigate:

itest <- 10
## so for example
as.data.frame(x[itest, ])

index <- c(x$orig[itest], x$dest[itest])
plot(x)
plot(x[itest, ], lwd = 4, add = TRUE)
lines(dataset[index, c("x", "y")], col = "firebrick", lwd = 2)
text(dataset[index, c("x", "y")], label = dataset$id[index], col = "dodgerblue", cex = 4)

Spatial line start and end point in R

Since you tagged question with sf as well, I'll provide a solution in sf. Note you can transform your sp object to sf using

library(sf)
st_as_sf(sp_obj)

Create linestring

line <- st_as_sfc(c("LINESTRING(0 0 , 0.5 1 , 1 1 , 1 0.3)")) %>% 
st_sf(ID = "poly1")

Convert each vertex to point

pt <- st_cast(line, "POINT")

Start and end are simply the first and last row of the data.frame

start <- pt[1,]
end <- pt[nrow(pt),]

plot - green is start point, red is end point

library(ggplot2)
ggplot() +
geom_sf(data = line) +
geom_sf(data = start, color = 'green') +
geom_sf(data = end, color = 'red') +
coord_sf(datum = NULL)

Sample Image

Connecting two sets of coordinates to create lines using sf/mapview

The tricky thing is to create a valid LINESTRING object from the coordinate pairs in wide format. sf expects linestring coordinates in rows of a matrix. Here's a way that works. The sfc column of a sf object is a list so here we use lapply to loop over the rows of the data you provided.

library(sf)
library(mapview)

b = dat[, c("Blong", "Blat")]
names(b) = c("long", "lat")
e = dat[, c("Elong", "Elat")]
names(e) = c("long", "lat")

dat$geometry = do.call(
"c",
lapply(seq(nrow(b)), function(i) {
st_sfc(
st_linestring(
as.matrix(
rbind(b[i, ], e[i, ])
)
),
crs = 4326
)
}))

dat_sf = st_as_sf(dat)

mapview(dat_sf, zcol = "Flyway")

Connect xy points with spatial lines

I'm speculating a bit here as to what exactly you wanted, but I think you want to visualize the connections from any point to the others. If that's the case, then this might work.
But first, some assumptions:

  • Your x and y coordinates are starting points. Consequently, id are thus id.origin
  • All other points will need to become "destinations", and then their own coordinates will become x_destination and so on.

< disclaimer> There should be a better, more elegant way to do this. I'd appreciate if someone more experienced can jump in and show me any of the *ply ways to do it. < /disclaimer>

Replicate the dataframe to cover for all possible combinations

dataset<-do.call(rbind, replicate(7, dataset, simplify=FALSE))

Now, create a matrix with all the same destination points, mixed:

nm=matrix(ncol=3)
for (i in 1:7){
nm<-rbind(nm,do.call(rbind,replicate(7,as.matrix(dataset[i,]),simplify=FALSE)))
}
nm<-nm[-1,]

Rename the columns of matrix, so they make sense, and bind the existing data frame with the new matrix

colnames(nm)<-c("x2","y2","id.dest")
newds<-cbind(dataset,as.data.frame(nm))

Remove duplicated trajectories:

newds<-newds[-which(newds$id.origin==newds$id.dest),]

and plot the result using geom_segment:

p<-ggplot(newds,aes(x=x,y=y))+geom_segment(aes(xend=x2,yend=y2))

There is a way to name the segments, but from observing the plot I would't suggest doing it. Instead you might consider naming the points using geom_text (other options are available, see ?annotate for one).

p<-p + geom_text(aes(x=1.8,y=6.1,label="a"))

That will produce a plot like the one here:
Quiver-like plot of distances

How to Convert data frame to spatial coordinates

First, you take the columns of lon and lat and create an object for coord. Then, you subtract them from the original data frame and create a new object. You finally use SpatialPointsDataFrame() to create a SpatialPointsDataFrame. When you create a SpatialPointsDataFrame, you need to assign proj4string. Choose an appropriate one for you.

In your case, you do not have any other columns but lon and lat, the method won't work. I purposely left lon and lat @data.

DATA

mydf <- structure(list(longitude = c(128.6979, 153.0046, 104.3261, 124.9019, 
126.7328, 153.2439, 142.8673, 152.689), latitude = c(-7.4197,
-4.7089, -6.7541, 4.7817, 2.1643, -5.65, 23.3882, -5.571)), .Names = c("longitude",
"latitude"), class = "data.frame", row.names = c(NA, -8L))

### Get long and lat from your data.frame. Make sure that the order is in lon/lat.

xy <- mydf[,c(1,2)]

spdf <- SpatialPointsDataFrame(coords = xy, data = mydf,
proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))

#> str(spdf)
#Formal class 'SpatialPointsDataFrame' [package "sp"] with 5 slots
#..@ data :'data.frame': 8 obs. of 2 variables:
#.. ..$ longitude: num [1:8] 129 153 104 125 127 ...
#.. ..$ latitude : num [1:8] -7.42 -4.71 -6.75 4.78 2.16 ...
#..@ coords.nrs : num(0)
#..@ coords : num [1:8, 1:2] 129 153 104 125 127 ...
#.. ..- attr(*, "dimnames")=List of 2
#.. .. ..$ : NULL
#.. .. ..$ : chr [1:2] "longitude" "latitude"
#..@ bbox : num [1:2, 1:2] 104.33 -7.42 153.24 23.39
#.. ..- attr(*, "dimnames")=List of 2
#.. .. ..$ : chr [1:2] "longitude" "latitude"
#.. .. ..$ : chr [1:2] "min" "max"
#..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slot
#.. .. ..@ projargs: chr "+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"

R from SpatialPointsDataFrame to SpatialLines

I have looked at the shapefile. There is an id column, but if you plot the data, it seems that the id is not ordered north-south or something. The extra lines are created because the point order is not perfect, connecting points that are next to each other in the table, but far from each other in terms of space. You could try to figure out the correct ordering of the data by calculating distances between points and then ordering on distance.

A workaround is to remove those lines that are longer than a certain distance, e.g. 500 m.. First, find out where distance between consecutive coordinates is larger than this distance: the breaks. Then take a subset of coordinates between two breaks and lastly create Lines for that subset. You end up with a coastline consisting of several (breaks-1) segments and without the erroneous ones.

# read data
library(rgdal)
pst<-readOGR("/data_spatial/coast/","points_coast")
coord<-as.data.frame(coordinates(pst))
colnames(coord) <- c('X','Y')

# determine distance between consective coordinates
linelength = LineLength(as.matrix(coord),sum=F)
# 'id' of long lines, plus first and last item of dataset
breaks = c(1,which(linelength>500),nrow(coord))

# check position of breaks
breaks = c(1,which(linelength>500),nrow(coord))

# plot extent of coords and check breaks
plot(coord,type='n')
points(coord[breaks,], pch=16,cex=1)

# create vector to be filled with lines of each subset
ll <- vector("list", length(breaks)-1)
for (i in 1: (length(breaks)-1)){
subcoord = coord[(breaks[i]+1):(breaks[i+1]),]

# check if subset contains more than 2 coordinates
if (nrow(subcoord) >= 2){
Slo1<-Line(subcoord)
Sli1<-Lines(list(Slo1),ID=paste0('section',i))
ll[[i]] = Sli1
}

}

# remove any invalid lines
nulls = which(unlist(lapply(ll,is.null)))
ll = ll[-nulls]
lin = SpatialLines(ll)
# add result to plot
lines(lin,col=2)

# write shapefile
df = data.frame(row.names=names(lin),id=1:length(names(lin)))
lin2 = SpatialLinesDataFrame(sl=lin, data=df)
proj4string(lin2) <- proj4string(pst)
writeOGR(obj=lin2, layer='coastline', dsn='/data_spatial/coast', driver='ESRI Shapefile')

coastline

Convert data frame to spatial lines data frame in R with x,y x,y coordintates

I believe what you want to end up with is a column in your data frame that for each row is a list (or data frame) with x.coord and y.coord columns. To achieve that, we can use unnest and nest from tidyr with dplyr:

library(dplyr)
library(tidyr)
result <- finalsub %>% mutate(coordinates = strsplit(coordinates,split=" ",fixed=TRUE)) %>%
unnest(coordinates) %>%
mutate(coordinates = strsplit(coordinates,split=",",fixed=TRUE),
x.coord = as.numeric(unlist(coordinates)[c(TRUE,FALSE)]),
y.coord = as.numeric(unlist(coordinates)[c(FALSE,TRUE)])) %>%
select(-coordinates) %>%
nest(x.coord,y.coord,.key=coordinates)

Notes:

  1. The first mutate splits the character vector in your coordinates column by " " to separate each coordinate x,y resulting in a list of these.
  2. unnest separates this list into rows.
  3. In the second mutate, we first split each coordinate x,y, this time by "," to separate each coordinate into x and y. Then we create separate x.coord and y.coord columns to hold these. Note the conversion to numeric here.
  4. Finally, we use nest to collect the x.coord and y.coord columns as a list under the column named coordinates. Note that we first have to remove the original coordinates column.

The result using your dput data, printing only the coordinates column:

print(result$coordinates)
##[[1]]
### A tibble: 284 x 2
## x.coord y.coord
## <dbl> <dbl>
##1 -1.294832 54.61024
##2 -1.294883 54.61008
##3 -1.294262 54.61002
##4 -1.294141 54.61001
##5 -1.293710 54.61004
##6 -1.293726 54.61014
##7 -1.293742 54.61025
##8 -1.293510 54.61026
##9 -1.293368 54.61026
##10 -1.292816 54.61019
### ... with 274 more rows
##
##[[2]]
### A tibble: 322 x 2
## x.coord y.coord
## <dbl> <dbl>
##1 -1.294832 54.61024
##2 -1.294883 54.61008
##3 -1.294262 54.61002
##4 -1.294141 54.61001
##5 -1.293710 54.61004
##6 -1.293726 54.61014
##7 -1.293742 54.61025
##8 -1.293510 54.61026
##9 -1.293368 54.61026
##10 -1.292816 54.61019
### ... with 312 more rows


Related Topics



Leave a reply



Submit