geojson
aims to deal only with geojson data in a
lightweight way.
We’ve defined classes (S3
) following the GeoJSON spec.
These classes sort of overlap with sp
’s classes, but not
really. There’s also some overlap in GeoJSON classes with Well-Known
Text (WKT) classes, but GeoJSON has a subset of WKT’s classes.
The package geoops supports manipulations on the classes defined in this package. This package is used within geojsonio to make some tasks easier.
Stable CRAN version
install.packages("geojson")
Dev version
::install_github("ropensci/geojson") remotes
library("geojson")
Essentially a character string with S3 class geojson
attached to make it easy to perform operations on
<- "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-99.74,32.45]},\"properties\":{}}]}"
x as.geojson(x)
#> <geojson>
#> type: FeatureCollection
#> features (n): 1
#> features (geometry / length) [first 5]:
#> Point / 2
<- '{
x "type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [100.0, 0.0]
},
{
"type": "LineString",
"coordinates": [ [101.0, 0.0], [102.0, 1.0] ]
}
]
}'
<- geometrycollection(x))
(y #> <GeometryCollection>
#> geometries (n): 2
#> geometries (geometry / length):
#> Point / 2
#> LineString / 2
get the string
1]]
y[[#> [1] "{\n \"type\": \"GeometryCollection\",\n \"geometries\": [\n {\n \"type\": \"Point\",\n \"coordinates\": [100.0, 0.0]\n },\n {\n \"type\": \"LineString\",\n \"coordinates\": [ [101.0, 0.0], [102.0, 1.0] ]\n }\n ]\n}"
get the type
geo_type(y)
#> [1] "GeometryCollection"
pretty print the geojson
geo_pretty(y)
#> {
#> "type": "GeometryCollection",
#> "geometries": [
#> {
#> "type": "Point",
#> "coordinates": [
#> 100.0,
#> 0.0
#> ]
#> },
#> {
#> "type": "LineString",
#> "coordinates": [
#> [
#> 101.0,
#> 0.0
#> ],
#> [
#> 102.0,
#> 1.0
#> ]
#> ]
#> }
#> ]
#> }
#>
write to disk
geo_write(y, f <- tempfile(fileext = ".geojson"))
::fromJSON(f, FALSE)
jsonlite#> $type
#> [1] "GeometryCollection"
#>
#> $geometries
#> $geometries[[1]]
#> $geometries[[1]]$type
#> [1] "Point"
#>
#> $geometries[[1]]$coordinates
#> $geometries[[1]]$coordinates[[1]]
#> [1] 100
#>
#> $geometries[[1]]$coordinates[[2]]
#> [1] 0
#>
#>
#>
#> $geometries[[2]]
#> $geometries[[2]]$type
#> [1] "LineString"
#>
#> $geometries[[2]]$coordinates
#> $geometries[[2]]$coordinates[[1]]
#> $geometries[[2]]$coordinates[[1]][[1]]
#> [1] 101
#>
#> $geometries[[2]]$coordinates[[1]][[2]]
#> [1] 0
#>
#>
#> $geometries[[2]]$coordinates[[2]]
#> $geometries[[2]]$coordinates[[2]][[1]]
#> [1] 102
#>
#> $geometries[[2]]$coordinates[[2]][[2]]
#> [1] 1
Add properties
<- '{ "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]}'
x <- linestring(x) %>% feature() %>% properties_add(population = 1000)
res
res#> <Feature>
#> type: LineString
#> coordinates: [[100,0],[101,1]]
Get a property
properties_get(res, property = 'population')
#> 1000
Add crs
<- '{
crs "type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
}'
<- x %>% feature() %>% crs_add(crs)
z
z#> {
#> "type": "Feature",
#> "properties": {
#>
#> },
#> "geometry": {
#> "type": "LineString",
#> "coordinates": [
#> [
#> 100,
#> 0
#> ],
#> [
#> 101,
#> 1
#> ]
#> ]
#> },
#> "crs": {
#> "type": "name",
#> "properties": {
#> "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
#> }
#> }
#> }
Get crs
crs_get(z)
#> $type
#> [1] "name"
#>
#> $properties
#> $properties$name
#> [1] "urn:ogc:def:crs:OGC:1.3:CRS84"
Add bbox
<- x %>% feature() %>% bbox_add()
tt
tt#> {
#> "type": "Feature",
#> "properties": {
#>
#> },
#> "geometry": {
#> "type": "LineString",
#> "coordinates": [
#> [
#> 100,
#> 0
#> ],
#> [
#> 101,
#> 1
#> ]
#> ]
#> },
#> "bbox": [
#> 100,
#> 0,
#> 101,
#> 1
#> ]
#> }
Get bbox
bbox_get(tt)
#> [1] 100 0 101 1
<- '{ "type": "Point", "coordinates": [100.0, 0.0] }'
x <- point(x))
(pt #> <Point>
#> coordinates: [100,0]
library("tibble")
tibble(a = 1:5, b = list(pt))
#> # A tibble: 5 × 2
#> a b
#> <int> <list>
#> 1 1 <geopoint [1]>
#> 2 2 <geopoint [1]>
#> 3 3 <geopoint [1]>
#> 4 4 <geopoint [1]>
#> 5 5 <geopoint [1]>
<- '{ "type": "MultiLineString",
x "coordinates": [ [ [100.0, 0.0], [101.0, 1.0] ], [ [102.0, 2.0], [103.0, 3.0] ] ] }'
<- multilinestring(x))
(mls #> <MultiLineString>
#> no. lines: 2
#> no. nodes / line: 2, 2
#> coordinates: [[[100,0],[101,1]],[[102,2],[103,3]]]
tibble(a = 1:5, b = list(mls))
#> # A tibble: 5 × 2
#> a b
#> <int> <list>
#> 1 1 <gmltlnst [1]>
#> 2 2 <gmltlnst [1]>
#> 3 3 <gmltlnst [1]>
#> 4 4 <gmltlnst [1]>
#> 5 5 <gmltlnst [1]>
tibble(a = 1:5, b = list(pt), c = list(mls))
#> # A tibble: 5 × 3
#> a b c
#> <int> <list> <list>
#> 1 1 <geopoint [1]> <gmltlnst [1]>
#> 2 2 <geopoint [1]> <gmltlnst [1]>
#> 3 3 <geopoint [1]> <gmltlnst [1]>
#> 4 4 <geopoint [1]> <gmltlnst [1]>
#> 5 5 <geopoint [1]> <gmltlnst [1]>
Geobuf is a compact binary encoding for geographic data using protocol buffers https://github.com/mapbox/geobuf (via the protolite) package.
<- system.file("examples/test.pb", package = "geojson")
file <- from_geobuf(file))
(json #> {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[102,0.5]},"id":999,"properties":{"prop0":"value0","double":0.0123,"negative_int":-100,"positive_int":100,"negative_double":-1.2345e+16,"positive_double":1.2345e+16,"null":null,"array":[1,2,3.1],"object":{"foo":[5,6,7]}},"blabla":{"foo":[1,1,1]}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[102,0],[103,-1.1],[104,-3],[105,1]]},"id":123,"properties":{"custom1":"test","prop0":"value0","prop1":0}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[99,10],[101,0],[100,1],[99,10]]]},"id":"test-id","properties":{"prop0":"value0","prop1":{"this":"that"}},"custom1":"jeroen"},{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[102,2],[103,2],[103,3],[102,2]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.2,0.8],[100.2,0.2]]]]}},{"type":"Feature","geometry":{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[100,0]},{"type":"LineString","coordinates":[[101,0],[102,1]]},{"type":"MultiPoint","coordinates":[[100,0],[101,1]]},{"type":"MultiLineString","coordinates":[[[100,0],[101,1]],[[102,2],[103,3]]]},{"type":"MultiPolygon","coordinates":[[[[102,2],[103,2],[103,3],[102,3],[102,2]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]]}]}}]}
<- to_geobuf(json)
pb class(pb)
#> [1] "raw"
<- tempfile(fileext = ".pb")
f to_geobuf(json, f)
from_geobuf(f)
#> {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[102,0.5]},"id":999,"properties":{"prop0":"value0","double":0.0123,"negative_int":-100,"positive_int":100,"negative_double":-1.2345e+16,"positive_double":1.2345e+16,"null":null,"array":[1,2,3.1],"object":{"foo":[5,6,7]}},"blabla":{"foo":[1,1,1]}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[102,0],[103,-1.1],[104,-3],[105,1]]},"id":123,"properties":{"custom1":"test","prop0":"value0","prop1":0}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[99,10],[101,0],[100,1],[99,10]]]},"id":"test-id","properties":{"prop0":"value0","prop1":{"this":"that"}},"custom1":"jeroen"},{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[102,2],[103,2],[103,3],[102,2]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.2,0.8],[100.2,0.2]]]]}},{"type":"Feature","geometry":{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[100,0]},{"type":"LineString","coordinates":[[101,0],[102,1]]},{"type":"MultiPoint","coordinates":[[100,0],[101,1]]},{"type":"MultiLineString","coordinates":[[[100,0],[101,1]],[[102,2],[103,3]]]},{"type":"MultiPolygon","coordinates":[[[[102,2],[103,2],[103,3],[102,3],[102,2]]],[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]]}]}}]}
<- '{ "type": "Polygon",
x "coordinates": [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
]
}'
<- polygon(x)
y to_geobuf(y)
#> [1] 10 02 18 06 2a 1a 0a 18 08 04 12 01 04 1a 11 80 84 af 5f 00 80 89 7a 00 00
#> [26] 80 89 7a ff 88 7a 00
<- '{"type": "MultiPoint", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }'
x <- multipoint(x)
y to_geobuf(y)
#> [1] 10 02 18 06 2a 11 0a 0f 08 01 1a 0b 80 84 af 5f 00 80 89 7a 80 89 7a
read nd-GeoJSON
<- "https://raw.githubusercontent.com/ropensci/geojson/main/inst/examples/ndgeojson1.json"
url <- tempfile(fileext = ".geojsonl")
f download.file(url, f)
<- ndgeo_read(f, verbose = FALSE)
x
x#> <geojson>
#> type: FeatureCollection
#> features (n): 3
#> features (geometry / length) [first 5]:
#> Point / 2
#> Point / 2
#> Point / 2
Write nd-GeoJSON
One would think we could take the output of ndego_read()
above and pass to ndgeo_write()
. However, in this example,
the json is too big for jqr
to handle, the underlying
parser tool. So here’s a smaller example:
<- system.file("examples", "featurecollection2.geojson",
file package = "geojson")
<- paste0(readLines(file), collapse = " ")
str <- featurecollection(str))
(x #> <FeatureCollection>
#> type: FeatureCollection
#> no. features: 3
#> features (1st 5): Point, Point, Point
<- tempfile(fileext = ".geojson")
outfile ndgeo_write(x, outfile)
::stream_in(file(outfile))
jsonlite#> Found 3 records... Imported 3 records. Simplifying...
#> type id properties.NOME
#> 1 Feature 0 Sec de segunrança
#> 2 Feature 1 Teste
#> 3 Feature 3 Delacorte Theater
#> properties.URL
#> 1 http://www.theatermania.com/new-york/theaters/45th-street-theatre_2278/
#> 2 http://www.bestofoffbroadway.com/theaters/47streettheatre.html
#> 3 http://www.centralpark.com/pages/attractions/delacorte-theatre.html
#> properties.ADDRESS1 properties.CIDADE properties.CEP
#> 1 354 West 45th Street Goiânia 74250010
#> 2 304 West 47th Street New York NA
#> 3 Central Park - Mid-Park at 80th Street New York NA
#> properties.ZIP geometry.type geometry.coordinates
#> 1 NA Point -49.25624, -16.68961
#> 2 74250010 Point -49.27624, -16.65561
#> 3 74250010 Point -49.27726, -16.67906
geojson
in R doing
citation(package = 'geojson')
(This was originally setup without requiring any of the
GEOS/GDAL
stack but now the package sp depends on sf it
can’t be avoided without overhaul).