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:
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
andinterconnection
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 forgeojsonio
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"