5

In R I have a SpatialLinesDataFrame (for example from a coastline shapefile) and I want to convert it to a SpatialPointsDataFrame. I want to keep all the vertices from the line shapefile to become point shapes in the point shapefile. I also need each point to have the attributes from the original line. For example if the SpatialLinesDataFrame has a line with 5 points and a "name" attribute, then all 5 new points should retain the attribute value.

I figured out some R code to convert the geometry part:

library(rworldmap)
library(sp)
data(coastsCoarse)
lineshape = coastsCoarse

point_coordinates = c()
i = 1
nLines = nrow(lineshape)
for (i in 1: nLines) {
  line1 <- lineshape[i,]@lines[[1]]@Lines[[1]]
  line1coords <- line1@coords
  point_coordinates = rbind(point_coordinates, line1coords)
}

pointshape <- data.frame(x=point_coordinates[,1], y=point_coordinates[,2])
coordinates(pointshape) <- ~x+y

#test result
plot(lineshape)
points(pointshape)

#now how do I transfer the attributes from the lineshape to the pointshape?

I'm stuck on how to transfer the attributes from the SpatialLinesDataFrame to the SpatialPointsDataFrame. I want my script to be reusable for different kinds of SpatialLinesDataFrame variables and so I don't know beforehand the number, names and data types of the attributes.

How can I transfer the attributes?

10

What's wrong with the one-liner:

> ptsCoarse = as(coastsCoarse, "SpatialPointsDataFrame")

Converts the geometry and preserves the attributes, and gives you some more attributes so you can reconstruct the lines back if you so desire:

> head(ptsCoarse@data)
    ScaleRank FeatureCla Lines.NR Lines.ID Line.NR
0           1  Coastline        1        0       1
0.1         1  Coastline        1        0       1
0.2         1  Coastline        1        0       1
0.3         1  Coastline        1        0       1
0.4         1  Coastline        1        0       1
0.5         1  Coastline        1        0       1
  • Allways these one-liners ;-), brilliant, what's behind the as (...) process – huckfinn Mar 12 '16 at 8:14
  • Its an S4 "as" method, defined here: github.com/cran/sp/blob/master/R/… - it uses a couple of other as methods to convert the geom and then does a lookup into the original @data to set the attributes. – Spacedman Mar 12 '16 at 8:20
  • Thanks, github.com/cran is a very good advise, where to read and learn something from programmes in a fast way. – huckfinn Mar 12 '16 at 8:53
  • 1
    Now, that is an "oh duh" moment, thanks! Embarrassingly enough, I just covered coercion in one of my classes and discussed it in depth with the students. I should just stay off line when I am sick. – Jeffrey Evans Mar 12 '16 at 18:16
  • Can this be adapted to create the points at equidistant locations? otherwise you are left with points close together where lines are tortuous, but far apart when lines are straight. – ecologist1234 Nov 21 '17 at 15:43
1

You can simplify this a bit using a list to store the vertices, coerce to a SpatialPointsDataFrame object and add line attributes in the loop.

Add sp some example data from rworldmap.

library(sp)
library(rworldmap)
lineshape <- coastsCoarse  

Create an empty list to store results, loop through lines and pull vertices and associated attributes for the associated line. R will throw an error (thus the options(warn=-1)) when the coordinates matrix is combined with a one line data.frame but, it works just fine. The object is then coerced into a SpatialPointsDataFrame and stored in the pts list. The do.call function is used to combine into a single SpatialPointsDataFrame object.

pts <- list()
options(warn=-1)
  for (i in 1:length(lineshape)) {
    l <- data.frame(lineshape[i,]@lines[[1]]@Lines[[1]]@coords,
                    lineshape[i,]@data)
    coordinates(l) <- ~X1+X2                
    pts[[i]] <- l
  }
options(warn=0)
pts <- do.call("rbind", pts)

Plot results by color coding the points using the "FeatureCla" attribute.

pcol <- ifelse( pts@data[,"ScaleRank"] == 0, "blue",
          ifelse( pts@data[,"ScaleRank"] == 1, "red", NA))                
plot(lineshape)
  plot(pts, col = pcol, pch = 20, cex = 0.5, add = TRUE)
  legend("bottomleft", legend=c("Coastline","Country"), pch=c(20,20),
         col=c("blue","red"))
1

I'm not sure why you want duplicate all the attribut data. I take your script and made some adoptions and comments. At least I duplicate all the attribute data at the right place in the loop...

  # !! Duplicate for each point the data column assumng that we have only on row per line
  for (c in 1: num.dcols) {
    # [[names.dcols[c]]] will copy the column names
    temp.tab[[names.dcols[c]]] <- rep(attr[1,c],num.coord)
  } # eof for duplicate

. Here is the script:

# Load libraries
library(rworldmap)
library(sp)
# Load data
data(coastsCoarse)
# Get coast lines
coast.lines <- coastsCoarse
# Names of the data columns to copy
names.dcols <- names(coast.lines@data)
# Number of data columns
num.dcols <- length(names.dcols) 
# The resulting point data frame
coast.points <- NA
# Number of coast lines
num.lines <- nrow(coast.lines)
# Loop over the datasets 
for (i in 1: num.lines) {
  # Get one line .. sure not more elements? ..lines[[1]]@Lines[[1]] 
  line <- coast.lines[i,]@lines[[1]]@Lines[[1]]
  # Get the attributes
  attr <- coast.lines[i,]@data
  # Create a temporary table with the coords
  temp.tab <- as.data.frame(line@coords)
  # Set the column names for the coords
  names(temp.tab) <- c('x','y');
  # Get the number of coordinates in the temporary table
  num.coord <- nrow(temp.tab)
  # !! Duplicate for each point the data column assumng that we have only on row per line
  for (c in 1: num.dcols) {
    # [[names.dcols[c]]] will copy the column names
    temp.tab[[names.dcols[c]]] <- rep(attr[1,c],num.coord)
  }  # eof for duplicate
  # Assign the temporary tab as result data table in the first loop 
  if ( i == 1 ) {
    coast.points <- temp.tab
  } 
  # Or append the temporary tab to the result
  else {
    coast.points <- rbind(coast.points, temp.tab)
  } # eof if else first loop
} # eof for all lines
# Assign coordinates
coordinates(coast.points)<-~x+y
# Test the result
plot(coast.lines)
points(coast.points)
# EOF

Very interesting.. I've learned from the post of @Jeffrey Evans to handle and run the attribute duplication process in two steps. You can loop over the datasets collect all temporay tables in a list and use do.call on the rbind function to build the coast.points table later. But I build the coordiantes later to prevent the usage of option(warn=XX).

# Load libraries
library(rworldmap)
library(sp)
# Load data
data(coastsCoarse)
# Get coast lines
coast.lines = coastsCoarse
# Point object at a collection list ..later the spatial table
coast.points <- list()
# Number of coast lines
num.lines = nrow(coast.lines)
# Loop over the datasets with the shortcut of @Jeffrey Evans 
# to build the point table via a `list` and a `do.call` of the
# rbind process and build the coordiantes later to prevent
# the usage of `option(warn=XX)`
for (i in 1: num.lines) {
  # Get one line and the attribues ..sure not more elements? ..lines[[1]]@Lines[[1]] 
  coast.points[[i]] <- data.frame(
                  coast.lines[i,]@lines[[1]]@Lines[[1]]@coords,
                  coast.lines[i,]@data)
}  
# Build the spatial table from the collection list 
coast.points <- do.call("rbind", coast.points)
# Assign coordinates from the default Varables
coordinates(coast.points) <- ~X1+X2
# Test the result
plot(coast.lines)
points(coast.points)
# EOF 
  • It is not a good practice to name objects the same as R functions as it can cause quite unexpected results. The line 'data <- coast.lines[i,]@data' is the one that could cause problems because of utils::data in base. – Jeffrey Evans Mar 12 '16 at 3:44
  • @Jeffrey, ok to be exact I change data <- coast.lines[i,]@data to attr <- coast.lines[i,]@data – huckfinn Mar 12 '16 at 7:16
  • And, wait for it, "attr" is a primitive in base. Sorry, I am not trying to give you a hard time but, this has come up for me when writing functions that fail unexpectedly. A colleague of mine just had an issue specifying an offset in a negative binomial model that was failing in some very strange ways with the model statement and predict. Turns out that she had a column in her data called "offset" that was in one model being accepted as a covariate but when called using offset=(log(offset)) was tanking because it was seeing the column call as a function. This is just good to keep in mind. – Jeffrey Evans Mar 14 '16 at 16:28
  • @Jeffrey I don't worry, Does R and it's packages left free some letter combination to name an object that could be an attribute or data table ;-) – huckfinn Mar 16 '16 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.