0

Suppose I have a data.frame object that has a column called geojson with GeoJSON strings in each row.

How can I transform this data.frame into an sf object that would allow me to perform geospatial operations on it?

Here's a quick example:

library(tidyverse)
library(dplyr)
library(sf)
library(geojsonsf)

df = data.frame(row_id = c(1,2,3),
                categories = c('Cat1','Cat1','Cat2'),
                geojson = c('{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,0],[1,0]],"type": "LineString"}}]}',
                            '{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,1],[1,1]],"type": "LineString"}}]}',
                            '{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,2],[1,2]],"type": "LineString"}},
                                                                       {"type": "Feature","properties": {},"geometry": {"coordinates": [[0,3],[1,3]],"type": "LineString"}}]}'))

And here is my (very dirty and not fully functional) solution so far:

# Trying to create one geometry for every row in the original dataset
for (i in 1:nrow(df)){
  this_geom = df$geojson[i] %>%  # First, we access the GeoJSON text
    geojson_sf() %>%  # Then we use the use the GeoJSON text and create a whole sf dataframe
    group_by() %>% # Then we need to dissolve/unite everything into one single geometry
    summarize(geometry = st_union(geometry)) %>% 
    .$geometry[1] # Finally, I try to retrieve only the actual geometry from the result above
  df$geometry[i] = this_geom # and store it into the original dataframe in a column called `geometry`
}

# Creating an sf-aware object
gdf = df %>% 
  st_as_sf(sf_column_name = "geometry")

The first chunk of the code above (the for-loop) doesn't throw any errors, but the second bit (creating the gdf object) does. I get the following error:

Error in st_sf(x, ..., agr = agr, sf_column_name = sf_column_name) : 
  no simple features geometry column present

I think I'm messing up in the creation of the geometry column. Because when I print it out, this is what it looks like:

> print(df$geometry)
[[1]]
Geometry set for 1 feature 
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 0
Geodetic CRS:  WGS 84
LINESTRING (0 0, 1 0)

[[2]]
Geometry set for 1 feature 
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 0 ymin: 1 xmax: 1 ymax: 1
Geodetic CRS:  WGS 84
LINESTRING (0 1, 1 1)

[[3]]
Geometry set for 1 feature 
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 0 ymin: 2 xmax: 1 ymax: 2
Geodetic CRS:  WGS 84
LINESTRING (0 2, 1 2)

It looks like it's not actually creating a proper column of data, but instead a list of items.

Is there a way that I can change this to make sf recognize the new geometries I'm creating so it'll allow me to make the gdf object?

2
  • 3
    I can do this with a one liner, since your example code only has one feature per row, so there's no need to union anything. gdf = st_as_sf(data.frame(df, geojson_sf(df$geojson))) does the business. Can you provide more complex data with multiple features in each row?
    – Spacedman
    Commented Mar 30 at 14:15
  • Thanks for the comment! I've made a slight change to the data to include more complex GeoJSON data. Now, when I run your code, it yields an error about expecting a different amount of results: Error in data.frame(df, geojson_sf(df$geojson)) : arguments imply differing number of rows: 3, 4
    – Felipe D.
    Commented Apr 1 at 17:57

1 Answer 1

1

First time using {geojsonsf}, so I don't have a flash of genius proposing an approach much more elegant, but I should make your current code work, I guess.

The last operation of your pipe .$geometry[1] was not executable for me, but why don't you simply use sf::st_geometry() when you're interested in extracting exactly this part of your simple feature object only?

The output looks not that bad:

# no need for {tidyverse} here
library(dplyr)
library(sf)
#> Linking to GEOS 3.11.2, GDAL 3.7.2, PROJ 9.3.0; sf_use_s2() is TRUE
library(geojsonsf)

df <- data.frame(row_id = c(1,2,3),
                categories = c('Cat1','Cat1','Cat2'),
                geojson = c('{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,0],[1,0]],"type": "LineString"}}]}',
                            '{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,1],[1,1]],"type": "LineString"}}]}',
                            '{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"coordinates": [[0,2],[1,2]],"type": "LineString"}}]}'))


for (i in 1:nrow(df)){
  
  df$geometry[i] <- df$geojson[i] |>
    geojson_sf() |> 
    group_by()  |> 
    summarize(geometry = st_union(geometry)) |> 
    st_geometry()
}

gdf <- st_as_sf(df, sf_column_name = "geometry", crs = "epsg:4326")

gdf$geometry
#> Geometry set for 3 features 
#> Geometry type: LINESTRING
#> Dimension:     XY
#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 2
#> Geodetic CRS:  WGS 84
#> LINESTRING (0 0, 1 0)
#> LINESTRING (0 1, 1 1)
#> LINESTRING (0 2, 1 2)
2
  • Thank you so much for the help here! This worked! I'd only recommend also adding a crs argument to the st_as_sf method to help make the data easier to read in other software if you export it to disk (like to a GPKG file). In my real world case, I needed to use st_as_sf(df, sf_column_name = "geometry", crs = st_crs(4326)). Thanks again!
    – Felipe D.
    Commented Apr 1 at 18:05
  • 1
    You're welcome and absolutely right. The clean way would definitely be to assign a proper crs to your object. Let me edit my answer accordingly.
    – dimfalk
    Commented Apr 1 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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