Home > Software design >  Convert json object to spatial lines data frame
Convert json object to spatial lines data frame

Time:12-31

I downloaded a geojson file of Iraq's electrical grid from EnergyData. You can download the file yourself here and visit the webpage here

I tried to use geojsonio::geojson_read to read the file into R, but it threw errors. Another user helpfully pointed out that the file format provided by the Energy Data website is incorrect and does not comply with geojson format, so the filename is misleading. So they recommended I use jsonlite::read_json. While that function does read the file into R, it's not a spatial object. It's a multi-level list. I also tried sf::st_read but the object is a messy data frame. I need my final object to be a spatial lines data frame, so I can merge and crop it with another spatial lines objects and write out the resulting shapefile. But when I try sp::SpatialLinesDataFrame I can't index into the object correctly to connect the lines in the right order. I need the object in R to look like the picture on the web page, and instead its just a hot mess.

Here's my code:

library(geojsonio)
library(jsonlite)
library(sf)
library(sp)

Iraq_electric_json <- jsonlite::read_json("Filepath/electric-network-iraq.geojson")

Iraq_electric_df <- sf::st_read("Filepath/electric-network-iraq.geojson")

# ERRORS HERE
Sldf <- SpatialLinesDataFrame(data = Iraq_electric_df$geometry)

I posted part one of my question (how to read in a geojson file) here:

geojsonio::geojson_read error for a geojson file "conversion from feature type sfc_GEOMETRY to sp is not supported"

But since part 2 of the question (changing object format) is so different from part 1, I decided to post a whole new question.

CodePudding user response:

This is long, because the solution is mostly to dig around and figure out how you want to work with this file. There are a few potential issues here:

  • Like I mentioned in your first post, the file might be malformed as far as the structure for a GeoJSON file. Some methods of reading it throw a warning that the last line is incomplete; a trailing empty line is part of the expected structure in json files.
  • The nodes and interconnection attributes are arrays, and I think R functions don't know what to do with those; sf functions know to read these as list-columns but that might not be available for geojsonio ones.
  • There's a mix of geometry types.

That last point is the big one, because it prevents you from using sp::Spatial* classes—those are only for single geometry types (SpatialGrid, SpatialPolygons, etc.). This file has both points and lines mixed together, so you need something like sf that can hold mixed geometry types.

A few ways I tried reading this, some of which return lists that you could further dig into, some return sf objects:

library(dplyr)
library(sf)

path <- "https://development-data-hub-s3-public.s3.amazonaws.com/ddhfiles/145188/electric-network-iraq.geojson"

# sf
iraq1 <- st_read(path, drivers = "GeoJSON")
#> options:        GeoJSON 
#> Reading layer `electric-network-iraq' from data source 
#>   `https://development-data-hub-s3-public.s3.amazonaws.com/ddhfiles/145188/electric-network-iraq.geojson' 
#>   using driver `GeoJSON'
#> Simple feature collection with 48 features and 6 fields
#> Geometry type: GEOMETRY
#> Dimension:     XY
#> Bounding box:  xmin: 40.99849 ymin: 30.50785 xmax: 47.85893 ymax: 37.88342
#> Geodetic CRS:  WGS 84

# list
iraq2 <- jsonlite::read_json(path)
str(iraq2, max.level = 1)
#> List of 2
#>  $ type    : chr "FeatureCollection"
#>  $ features:List of 48

# list
iraq3 <- readLines(path) %>%
  geojson::to_geojson() %>%
  jsonlite::fromJSON()
#> Warning in readLines(path): incomplete final line found on 'https://development-
#> data-hub-s3-public.s3.amazonaws.com/ddhfiles/145188/electric-network-
#> iraq.geojson'
#> Registered S3 method overwritten by 'geojsonlint':
#>   method         from 
#>   print.location dplyr

# sf, same warning about incomplete final line
iraq4 <- suppressWarnings(geojsonsf::geojson_sf(path))
#> Registered S3 method overwritten by 'geojsonsf':
#>   method        from   
#>   print.geojson geojson

I'll go ahead working with the first one, an sf object with a mixed geometry type (just generic GEOMETRY). Note here that some are lines, some are points, and that 2 columns are lists of strings.

head(iraq1)
#> Simple feature collection with 6 features and 6 fields
#> Geometry type: GEOMETRY
#> Dimension:     XY
#> Bounding box:  xmin: 41.12771 ymin: 36.34583 xmax: 43.12683 ymax: 37.88342
#> Geodetic CRS:  WGS 84
#>   transmissionPower lineType      name nodeType            nodes
#> 1               400   single      <NA>     <NA>    Batman, Zakho
#> 2                NA     <NA>     Zakho     city                 
#> 3               400   single      <NA>     <NA> Mosul Dam, Zakho
#> 4                NA     <NA> Mosul Dam      dam                 
#> 5               400   double      <NA>     <NA> Mosul, Mosul Dam
#> 6                NA     <NA>     Mosul     city                 
#>   interconnection                       geometry
#> 1   Syria, Turkey LINESTRING (41.12771 37.883...
#> 2                      POINT (42.68304 37.14215)
#> 3                 LINESTRING (42.68304 37.142...
#> 4                      POINT (42.83355 36.62586)
#> 5                 LINESTRING (42.83355 36.625...
#> 6                      POINT (43.12683 36.34583)

plot(iraq1["geometry"])

If you really need an sp::Spatial object, you'll have to separate the points and lines. I split them based on the number of dimensions (0 for points, 1 for lines) and wrote out GeoJSON files, which are now readable, and then read them back in. In the end you get one SpatialPointsDataFrame and one SpatialLinesDataFrame. You don't have to write them to files; you could just pass each data frame to sf::as_Spatial and get the same sp classes.

iraq1 %>%
  mutate(dimension = st_dimension(geometry)) %>%
  split(.$dimension) %>%
  purrr::iwalk(~geojsonio::geojson_write(.x, file = sprintf("iraq_repaired_dim%s.geojson", .y)))

iraq_pts <- geojsonio::geojson_read("iraq_repaired_dim0.geojson", what = "sp")
iraq_lines <- geojsonio::geojson_read("iraq_repaired_dim1.geojson", what = "sp")

class(iraq_pts)
#> [1] "SpatialPointsDataFrame"
#> attr(,"package")
#> [1] "sp"
class(iraq_lines)
#> [1] "SpatialLinesDataFrame"
#> attr(,"package")
#> [1] "sp"
  • Related