14

I've got a table with place references and x and y coordinates in a given coordinate reference system. I want to turn that into a simple features data frame. How can I create that?

I thought it might be:

data_frame(place = "London", 
           lat = 51.5074, lon = 0.1278, 
           epsg = 4326) %>%
  group_by(place) %>%
  mutate(feature = st_point(c(lon, lat)))

But that leads to an error:

Error in mutate_impl(.data, dots) : Column feature must be length 1 (the group size), not 2

This is probably pretty simple to do, I'm just not seeing it readily discussed in the documentation. Most spatial analysts seem to demand better data by default :).

I also thought to try:

data_frame(place = "London", 
           lat = 51.5074, lon = 0.1278, 
           epsg = 4326) %>%
  group_by(place) %>%
  do(with(., {
    p <- st_point(c(lon, lat))
    pcol <- st_as_sfc(p)
    st_as_sf(data_frame(place = place,
                        point = pcol),
             crs = epsg)
  }))

At the end of the pipe, I want a simple features data frame that I can plot and manipulate like any other.

Another rub with what I'm trying to do is that I've got a data frame with a column for EPSG. I need to create this simple features data frame for each place and combine that all together into a larger simple features data frame.

wdkrnls
  • 3,946
  • 2
  • 29
  • 52
  • So what's the desired output here? You are trying to store a `st_point` object in the data.frame? – MrFlick Mar 08 '18 at 20:26
  • 2
    Check `st_as_sf`. [How to Convert data frame to spatial coordinates](https://stackoverflow.com/a/45484314/1851712) – Henrik Mar 08 '18 at 20:45
  • @MrFlick I want to create a simple features data frame like nc in the documentation. I have thousands of these coordinates stored in a database with a reference to their espg's. But it's not stored in any GIS friendly way by default. – wdkrnls Mar 08 '18 at 20:51
  • Thanks, @Henrik. I think that's what I wanted to know. Somehow I missed the coords argument. – wdkrnls Mar 08 '18 at 20:53
  • If it's a duplicate, there is a broader part to the question based on my earlier comment. I've added it to the question. – wdkrnls Mar 08 '18 at 20:59

2 Answers2

28

Your attempt and the accepted answers are unnecessarily complicated and terribly confusing. Just go with st_as_sf (which by the way also easily migrates all objects from the outdated sp class (SpatialPolygonsDataFrames and the like)):

df <- data.frame(place = "London", 
       lat = 51.5074, lon = 0.1278,
       population = 8500000) # just to add some value that is plotable
projcrs <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
df <- st_as_sf(x = df,                         
           coords = c("lon", "lat"),
           crs = projcrs)

And we are done, as easy as that.

Just to visualise it:

library(tmap)
data("World")    
tm_shape(World[World$iso_a3 == "GBR", ]) + tm_polygons("pop_est") + 
    tm_shape(df) + tm_bubbles("population")

tmap way

Or with the new amazing geom_sf from ggplot2:

library(ggplot2)
ggplot(World) + geom_sf() + geom_sf(data = df, shape = 4, col = "red", size = 5)

ggplot2 way

  • 2
    Please read the last part of the OP's question - the case where a data.frame has multiple values for epsg, which then need to be transformed to a common epsg. That is what my answer addresses. `st_as_sf` crs argument does not accept multiple values – sebdalgarno Jan 09 '19 at 16:02
4

UPDATE The answer from @Franz Plumpton is the correct correct solution with a single epsg. My answer below is only necessary when each row of a data.frame has a different epsg. Otherwise, this would be a duplicate (as pointed out by @Henrik above).

library(sf)
library(tibble)

df <- data_frame(place = c("London", "Kalamazoo"), 
           lat = c(51.5074, 396088), lon = c(0.1278, 5452158),
           epsg = c(4326, 32610))

l <- lapply(unique(df$place), function(x){
  df <- df[df$place == x,]
  epsg <- df$epsg[1]
  df  <-  st_as_sf(df, coords = c('lon', 'lat'), crs = epsg)
}) 

you could then transform all to the same epsg and combine into a single data.frame:

do.call(rbind, lapply(l, function(x) x <- st_transform(x, 4326)))
sebdalgarno
  • 2,351
  • 6
  • 23