CRAN_Status_Badge downloads CRAN RStudio mirror downloads Github Stars R build status Coverage Status


A simple, low-dependency and fast converter between GeoJSON and Simple Feature objects in R.



As per GeoJSON (RFC 7946 specification), foreign members are ignored, and nested objects and arrays inside the properties object are converted to string/characters.

Also, as per the specification, CRS

The coordinate reference system for all GeoJSON coordinates is a geographic coordinate reference system, using the World Geodetic System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units of decimal degrees. This is equivalent to the coordinate reference system identified by the Open Geospatial Consortium (OGC) URN urn:ogc:def:crs:OGC::CRS84

From v1.3.2, if your coordinates are in a different CRS you can specify the CRS & proj4string values in the geojson_sf() and geojson_sfc() functions.


Install the CRAN version with


To install the development version

# install.packages("devtools")

Why did you build it?

To quickly parse between GeoJSON and sf objects, and to handle cases not supported by sf, e.g. arrays of geometries

What do you mean, ‘cases not supported’

For example, sf can’t read an array of GeoJSON objects, so I wanted to make this work

js <- c(
      "type": "FeatureCollection",
      "features": [
        "type": "Feature",
        "properties": {"id":1},
        "geometry": {"type": "Point", "coordinates": [100.0, 0.0]}

sf <- geojson_sf( js )
#  Simple feature collection with 3 features and 1 field
#  geometry type:  GEOMETRY
#  dimension:      XY
#  bbox:           xmin: -1 ymin: -1 xmax: 100 ymax: 1
#  z_range:        zmin: NA zmax: NA
#  m_range:        mmin: NA mmax: NA
#  CRS:            4326
#    id                geometry
#  1 NA             POINT (0 0)
#  2 NA LINESTRING (-1 -1, 1 1)
#  3  1           POINT (100 0)

And going the other way you can also return a vector of GeoJSON

js <- sf_geojson( sf, atomise = T )
#  {"type":"Feature","properties":{"id":null},"geometry":{"type":"Point","coordinates":[0.0,0.0]}} 
#  {"type":"Feature","properties":{"id":null},"geometry":{"type":"LineString","coordinates":[[-1.0,-1.0],[1.0,1.0]]}} 
#  {"type":"Feature","properties":{"id":1.0},"geometry":{"type":"Point","coordinates":[100.0,0.0]}}

What’s the benefit of ‘atomising’?

It’s useful for when you work with geospatial databases and want an individual record for each individual feature.

What happens if you don’t atomise?

You get a single GeoJSON object

sf_geojson( sf )
#  {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":null},"geometry":{"type":"Point","coordinates":[0.0,0.0]}},{"type":"Feature","properties":{"id":null},"geometry":{"type":"LineString","coordinates":[[-1.0,-1.0],[1.0,1.0]]}},{"type":"Feature","properties":{"id":1.0},"geometry":{"type":"Point","coordinates":[100.0,0.0]}}]}

Can you remove the properites and just return the geometries

Yes. Call sfc_geojson() on the sfc object.

sfc_geojson( sf$geometry )
#  {"type":"Point","coordinates":[0.0,0.0]} 
#  {"type":"LineString","coordinates":[[-1.0,-1.0],[1.0,1.0]]} 
#  {"type":"Point","coordinates":[100.0,0.0]}

If I have an sf object without any properties, why does it ‘atomise’ by default?

sf$id <- NULL
sf_geojson( sf )
#  {"type":"Point","coordinates":[0.0,0.0]} 
#  {"type":"LineString","coordinates":[[-1.0,-1.0],[1.0,1.0]]} 
#  {"type":"Point","coordinates":[100.0,0.0]}

The simplify argument is TRUE by default, and it will try and ‘simplify’ the GeoJSON. If there are no properties in the sf object, then the GeoJSON won’t have any properties.

However, if you set simplify = FALSE you’ll get a FeatureCollection with an empty properties field.

sf_geojson(sf, simplify = F)
#  {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[0.0,0.0]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-1.0,-1.0],[1.0,1.0]]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[100.0,0.0]}}]}

How fast is it?

This benchmark shows a comparison with library(sf) for converting a string of GeoJSON of 3,221 counties in the US in to an sf object

myurl <- ""
geo <- readLines(myurl)
geo <- paste0(geo, collapse = "")


    geojsonsf = {
    sf = {
        sf::st_read(geo, quiet = T)
    times = 2

#Unit: milliseconds
#      expr       min        lq      mean    median        uq       max  neval
# geojsonsf  709.2268  709.2268  722.0626  722.0626  734.8984  734.8984      2
#        sf 1867.6840 1867.6840 1958.7968 1958.7968 2049.9097 2049.9097      2