1

I have a list of XY coordinates along multiple lines. The lines are separated by a single identifier at the start of each block of coordinates. How do I convert this to multiple lines linking the identifier to the lines in R so that I can output them as a shapefile? The problem is in parsing the data to link the ID with the subsequent coordinates.

The data is in the following format:

ID1
3285.48 -63.32
3285.14 -64.14
3284.67 -63.56
3285.00 -62.77
ID2
3299.84 -76.82
3299.25 -75.38
3299.96 -75.76
ID3
3299.76 -86.92
3299.77 -86.89
3299.76 -87.04
3299.76 -87.23
3299.74 -87.25
3299.68 -87.22
3299.68 -87.11

1

You can read in irregular data if you know the max number of columns by giving column names and a fill argument:

> data = read.table("./lines.txt",col.names=c("x","y"),fill=TRUE, stringsAsFactors=FALSE)
> data
         x      y
1      ID1     NA
2  3285.48 -63.32
3  3285.14 -64.14
4  3284.67 -63.56
5  3285.00 -62.77
6      ID2     NA
7  3299.84 -76.82
8  3299.25 -75.38
9  3299.96 -75.76
10     ID3     NA

Then the lines are grouped by how many NA are in the second column so far:

> data$group = cumsum(is.na(data$y))
> data
         x      y group
1      ID1     NA     1
2  3285.48 -63.32     1
3  3285.14 -64.14     1
4  3284.67 -63.56     1
5  3285.00 -62.77     1
6      ID2     NA     2
7  3299.84 -76.82     2
8  3299.25 -75.38     2
9  3299.96 -75.76     2
10     ID3     NA     3

Then you can pull out the IDS:

> IDS = data$x[is.na(data$y)]
> IDS
[1] "ID1" "ID2" "ID3"

And filter them out:

> data = data[!is.na(data$y),]
> data
         x      y group
2  3285.48 -63.32     1
3  3285.14 -64.14     1
4  3284.67 -63.56     1
5  3285.00 -62.77     1
7  3299.84 -76.82     2
8  3299.25 -75.38     2
9  3299.96 -75.76     2
11 3299.76 -86.92     3

Then split by group gives you a list of data frames you can apply work to:

> par(ask=TRUE)
> lapply(split(data,data$group), function(d){plot(d$x,d$y,type="l")})
Hit <Return> to see next plot: 
Hit <Return> to see next plot: 
Hit <Return> to see next plot: 

Replace that function with whatever you want to do with each line data set, and attach the IDS extracted in the previous step as needed.

Note at this point the columns might still be character so coercion to numeric might be helpful via as.numeric.

If you want to make sf spatial lines data frame, then proceed:

data$x = as.numeric(data$x)
library(sf)
lfc = do.call(st_sfc,
    lapply(split(data, data$group),
      function(d){st_linestring(as.matrix(d[,1:2]))}))
lfd = st_sf(data.frame(ID=IDS, geom=lfc))
plot(lfd,lwd=5)

to get...

enter image description here

  • that fill part is sweet, really neat – mdsumner Apr 9 at 0:14
  • This is a great answer to the problem @Spacedman - do you think it would scale well with very large datasets (millions of lines)? I have tried to do something similar with the unix utility awk but like you I had to go through the data multiple times processing and writing to get a fairly simple additional column? – xyzblue Apr 9 at 14:23
  • Took about ten seconds to process a 1,000,000 line file that had 10,000 features each with 100 points in it, resulting in a spatial sf data frame with 10,000 features. – Spacedman Apr 9 at 15:29
  • Well that seems sufficiently fast! Thanks again @Spacedman – xyzblue Apr 9 at 20:10

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.