1

I should preface that I am terrible at loops in R and I recognize this question is similar to this post Batch convert .csv files to .shp files in R. However, I was not able to leave a comment to see if this user found a solution on this thread because I do not have enough reputation points and the suggested solutions did not help me.

I have multiple .csv files that contain GPS points of animals. I would like to create multiple shapefiles for spatial analysis. I have tried creating a loop to read in the .csv file, make spatial data from csv file with latitudes and longitudes, transform the spatial data frame to a UTM projection so that I can calculate distances and then write the file as a shapefile. Here is the loop I have tried, but I think my indexing in the out and utm_out is incorrect.

Here is some test data; remember to set your working directory before writing the .csv:

#write sample .csv for animal 1
ID1<-rep(1, 3)
Latitude1<-c(25.48146, 25.49211, 25.47954)
Longitude1<-c(-84.66530, -84.64892, -84.69765)

df1<-data.frame(ID1, Latitude1, Longitude1)
colnames(df1)<-c("ID", "Latitude", "Longitude")
write.csv(df1, "df1.csv", row.names = FALSE)


#write sample .csv for animal 2
ID2<-rep(2, 2)
Latitude2<-c(28.48146, 28.49211)
Longitude2<-c(-88.66530, -88.64892)

df2<-data.frame(ID2, Latitude2, Longitude2)
colnames(df2)<-c("ID", "Latitude", "Longitude")
write.csv(df2, "df2.csv", row.names = FALSE)


#create a list of file names in my working directory where .csv files are located
all.files<-list.files(pattern="\\.csv")

#set the points geographic coordinate system
points_crs <- crs("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")

#write a loop to read in each file, specify the file as a spatial points data frame & project then write as .shp file
for(i in 1:length(all.files)) {
 file<-read.csv(all.files[i], header=TRUE, sep = ",", stringsAsFactors = FALSE) #read files
 coords<-file[c("Longitude", "Latitude")] #select coordinates from the file
 out<-SpatialPointsDataFrame(
      coords = coords,
      file,
      proj4string = points_crs) #create Spatial Points Data Frame and specify geographic coordinate system = points_crs
 utm_out<-spTransform(out, crs("+init=epsg:32616")) #transform to UTM
 writeOGR(utm_out[[i]],dsn="C:/Users/Desktop/Shapefile_test", 
 "*", driver="ESRI Shapefile")
}

This gives me the following: Error: inherits(obj, "Spatial") is not TRUE

I've also tried:

for(i in 1:length(all.files)) {
file<-read.csv(all.files[i], header=TRUE, sep = ",", stringsAsFactors = FALSE)
coords<-file[c("Longitude", "Latitude")]
out<-SpatialPointsDataFrame(
     coords = coords,
     file,
     proj4string = points_crs)
utm_out<-spTransform(out[[i]], crs("+init=epsg:32616"))
writeOGR(utm_out[[i]],dsn="C:/Users/Desktop/Shapefile_test", "*", driver="ESRI Shapefile")
}

This produces: Error in (function (classes, fdef, mtable) : unable to find an inherited method for function ‘spTransform’ for signature ‘"integer", "CRS"’

Ideally, the output file will be something like "animal1.shp" "animal2.shp"...etc.

Alternatively, I do have animal 1 and 2 in one file. I could bring in this file, set the projection and then create multiple subsets for each unique animal ID and write the subset to a .shp file, but I am having issues with subsetting the spatial data and I think that is a topic for another thread.

Thanks in advance for your assistance.

kawilki
  • 25
  • 8
  • 1
    The best way to work through something like this is to get rid of the loop, and test on a single file until everything works. Then either wrap your code in a function, or write the loop at the end. Two issues I see right off the bat, `crs` needs to be capitalized: `CRS`. `out` and `utm_out` are not lists, you don't need `[[]]`. – Mako212 Jan 24 '18 at 22:29
  • Also, please list all the libraries you're using in future questions. – Mako212 Jan 24 '18 at 22:30
  • Also, if your example requires creating files, it's nice to provide the code to clean it up afterwards. Create a new folder `dir.create("exampleFolder"); setwd("exampleFolder")` then to clean it up `setwd(".."); unlink("exampleFolder", recursive= TRUE, force= TRUE)` – Mako212 Jan 24 '18 at 22:49
  • @Mako212 thank you for your response. My apologies, I did forget to post the packages I used. Whenever I make a loop, the first thing I do is run it line by line to make sure it works and then try to wrap it into a loop. My code worked fine outside of the loop -- crs does not need to be capitalized. I also had the raster package loaded, so it is likely because of this package that lowercase crs works. My problem with loops always seems to be with the indexing. I didn't know [[ ]] was specific to lists, thanks for that helpful info! – kawilki Jan 25 '18 at 14:16
  • I also didn't know about dir.create()...and unlink() so that will be helpful info moving forward using stackoverflow, thank so much – kawilki Jan 25 '18 at 14:40
  • `[]` and `[[]]` aren't unique to lists, they can be used on data frames too, however they're used for indexing, in other words accessing specific elements of an object. The reason you didn't need them in your provided code is that you're using the for loop to pass list items through the code one at a time. As soon as you read in `all.files[i]` your input object is no longer a list as it is passed through the remainder of the loop, so you don't need `[]` or `[[]]` any more. – Mako212 Jan 26 '18 at 23:43

2 Answers2

2

Here is a minor variation on the solution by Mako212

library(raster)

all.files <- list.files(pattern="\\.csv$")
out.files <- gsub("\\.csv$", ".shp")
crs <- CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")

for(i in 1:length(all.files)) {
    d <- read.csv(all.files[i], stringsAsFactors = FALSE)
    sp <- SpatialPointsDataFrame(coords = d[c("Longitude", "Latitude")], d, proj4string = crs) 
    utm <- spTransform(sp, CRS("+proj=utm +zone=16 +datum=WGS84"))
    shapefile(utm, out.files[i])
}
Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63
  • 1
    This is nice and concise, thanks for your suggestion @RobertH. I tried your code, but the out.files line does not seem to work. It gives me the following warning: " argument "x" is missing, with no default" and then it fails in the loop. – kawilki Feb 02 '18 at 15:03
1

Expanding on my comment, it's important to test batch processing operations like this on a single file first, and then adapt your solution as necessary to process the batch. My first step in troubleshooting your issue was to strip away the for loop, and try running the code with the first file,all.files[1], and it still failed, indicating there was at least one issue not related to the loop.

Try this out. I've changed crs to CRS because the function in sp is capitalized. Your loop range can be simplified to for(i in all.files), and I removed the attempts to access non-existent lists with out and utm_out

require(sp)
require(rgdal)   

points_crs <- CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")

#write a loop to read in each file, specify the file as a spatial points data frame & project then write as .shp file
for(i in all.files) {
 file <- read.csv(i, header=TRUE, sep = ",", stringsAsFactors = FALSE) #read files
 coords <- file[c("Longitude", "Latitude")] #select coordinates from the file
 out <- SpatialPointsDataFrame(
      coords = coords,
      file,
      proj4string = points_crs) #create Spatial Points Data Frame and specify geographic coordinate system = points_crs
 names<-substr(i, 1, nchar(all.files)-4)
 utm_out <- spTransform(out, CRS("+init=epsg:32616")) #transform to UTM
 writeOGR(utm_out,dsn="/path/Shapefile_test", layer=names, driver="ESRI Shapefile")
}

Edit:

I had to modify the writeOGR line by specifying a layer name:

writeOGR(utm_out,dsn="/path/Shapefile_test", layer="test", driver="ESRI Shapefile")
Mako212
  • 6,787
  • 1
  • 18
  • 37
  • thank you for your suggestion, but unfortunately, this does not work. I do get a shapefile, but I need a shapefile for each .csv file. Thus, for this example, I need 2 shapefiles files named, "animal1.shp", "animal2.shp". The suggested revision to my loop only creates one file named "test". Any other suggestions you have would be most helpful. Thank you in advance! – kawilki Jan 25 '18 at 14:27
  • 1
    SOLUTION: after utm_out, add the following line: names<-substr(i, 1, nchar(all.files)-4) and then writeOGR(utm_out,dsn="/path/Shapefile_test", layer=names, driver="ESRI Shapefile"). Now everything works just as I need. Thanks for your help @Mako212 – kawilki Jan 25 '18 at 15:12
  • @kawilki Yep, you got it. I updated my solution to reflect the above comment. – Mako212 Jan 25 '18 at 16:25