Skip to content

Commit

Permalink
document and test GeoJSON.read(path) (#77)
Browse files Browse the repository at this point in the history
* document and test `GeoJSON.read(path)`

Fixes #49.

* bump JSON3 compat
  • Loading branch information
visr committed Oct 10, 2023
1 parent a2a22a6 commit 9bb34c6
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Extents = "0.1"
GeoFormatTypes = "0.4"
GeoInterface = "1.2.1"
GeoInterfaceRecipes = "1"
JSON3 = "1.12"
JSON3 = "1.13"
StructTypes = "1"
Tables = "1"
julia = "1.6"
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ properties in the columns individually.

## Usage
GeoJSON only provides simple `read` and `write` methods.
`GeoJSON.read` takes a file path, string, IO, or bytes.

```julia
julia> using GeoJSON, DataFrames

julia> jsonbytes = read("path/to/a.geojson");

julia> fc = GeoJSON.read(jsonbytes)
julia> fc = GeoJSON.read("path/to/a.geojson")
FeatureCollection with 171 Features

julia> first(fc)
Expand All @@ -34,8 +33,7 @@ julia> write(fc)
```
### HTTP access
### HTTP access
To read JSON from a URL, use HTTP.jl
```julia
Expand Down
19 changes: 10 additions & 9 deletions src/io.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""
GeoJSON.read(json::String; lazyfc=false, ndim=2, numbertype=Float32)
GeoJSON.read(json; lazyfc=false, ndim=2, numbertype=Float32)
Read GeoJSON from a string to a GeoInterface.jl compatible object.
Set `ndim=3` for 3D geometries, which is also tried automatically when
parsing as `ndim=2` (default) fails. The `numbertype` is Float32 by default,
Float64 should be set when the precision is required.
Read GeoJSON to a GeoInterface.jl compatible object.
When reading in huge featurecollections (1M+ features), set `lazyfc=true`
to only parse them into memory when accessed.
# Arguments
- `json`: A file path, string, IO, or bytes (`AbstractVector{UInt8`) containing JSON to read.
- `lazyfc::Bool=false`: When reading in huge featurecollections (1M+ features),
set `lazyfc=true` to only parse them into memory when accessed.
- `ndim::Int=2`: Use 3 for 3D geometries, which is also used when 2D parsing fails.
- `numbertype::DataType=Float32`: Use Float64 when the precision is required.
"""
function read(io; lazyfc=false, ndim=2, numbertype=Float32)
if lazyfc
Expand Down Expand Up @@ -63,7 +64,7 @@ function _lower(obj)
elseif GI.isgeometry(obj)
if GI.is3d(obj)
_lower(GI.geomtrait(obj), Val{true}(), obj)
else
else
_lower(GI.geomtrait(obj), Val{false}(), obj)
end
else
Expand Down Expand Up @@ -94,7 +95,7 @@ function _to_vector_ntuple(::GI.PointTrait, is3d::Val{true}, geom)
end
function _to_vector_ntuple(::GI.AbstractGeometryTrait, is3d, geom)
map(GI.getgeom(geom)) do child_geom
_to_vector_ntuple(GI.geomtrait(child_geom), is3d, child_geom)
_to_vector_ntuple(GI.geomtrait(child_geom), is3d, child_geom)
end
end

Expand Down
58 changes: 40 additions & 18 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,38 @@ include("geojson_samples.jl")
@test occursin(":park", sprint(show, MIME"text/plain"(), fc[1]))
end

@testset "write to disk" begin
@testset "read and write methods" begin
# read string
fc = t
GeoJSON.write("Samples.json", fc)
fc1 = GeoJSON.read(read("Samples.json", String))
@test GI.extent(fc) == GI.extent(fc1) == Extent(X=(100.0f0, 105.0f0), Y=(0.0f0, 1.0f0))
f = GI.getfeature(fc, 1)
f1 = GI.getfeature(fc1, 1)
@test GI.geometry(f) == GI.geometry(f1)
@test GI.properties(f) == GI.properties(f1)
rm("Samples.json")
geom = GI.geometry(f)
prop = GI.properties(f)
path = tempname()
# write to path
GeoJSON.write(path, fc)
bytes = read(path)
# write to io
mktemp() do path, io
GeoJSON.write(io, fc)
close(io)
@test read(path) == bytes
end
# read bytes
fc_bytes = GeoJSON.read(bytes)
@test GI.extent(fc) == GI.extent(fc_bytes) == Extent(X=(100.0f0, 105.0f0), Y=(0.0f0, 1.0f0))
f_bytes = GI.getfeature(fc_bytes, 1)
@test GI.geometry(f_bytes) == geom
@test GI.properties(f_bytes) == prop
# read file
fc_file = GeoJSON.read(path)
@test GI.geometry(fc_file[1]) == geom
@test GI.properties(fc_file[1]) == prop
# read io
fc_io = open(path) do io
GeoJSON.read(io)
end
@test GI.geometry(fc_io[1]) == geom
@test GI.properties(fc_io[1]) == prop
end

@testset "GeoInterface" begin
Expand Down Expand Up @@ -325,19 +347,19 @@ include("geojson_samples.jl")
end

@testset "NamedTuple point order doesn't matter as long as it's known" begin
@test GeoJSON.write((X=1.0, Y=2.0)) ==
GeoJSON.write((Y=2.0, X=1.0)) ==
@test GeoJSON.write((X=1.0, Y=2.0)) ==
GeoJSON.write((Y=2.0, X=1.0)) ==
"{\"type\":\"Point\",\"coordinates\":[1.0,2.0]}"
@test GeoJSON.write((Z=3, X=1.0, Y=2.0)) ==
GeoJSON.write((Y=2.0, X=1.0, Z=3)) ==
GeoJSON.write((Y=2.0, Z=3, X=1.0)) ==
GeoJSON.write((X=1.0, Z=3, Y=2.0)) ==
@test GeoJSON.write((Z=3, X=1.0, Y=2.0)) ==
GeoJSON.write((Y=2.0, X=1.0, Z=3)) ==
GeoJSON.write((Y=2.0, Z=3, X=1.0)) ==
GeoJSON.write((X=1.0, Z=3, Y=2.0)) ==
"{\"type\":\"Point\",\"coordinates\":[1.0,2.0,3]}"
# M is not in the spec
@test GeoJSON.write((Z=3, X=1.0, Y=2.0, M=4)) ==
GeoJSON.write((Y=2.0, X=1.0, M=4, Z=3)) ==
GeoJSON.write((M=4, Y=2.0, Z=3, X=1.0)) ==
GeoJSON.write((X=1.0, Z=3, M=4, Y=2.0)) ==
@test GeoJSON.write((Z=3, X=1.0, Y=2.0, M=4)) ==
GeoJSON.write((Y=2.0, X=1.0, M=4, Z=3)) ==
GeoJSON.write((M=4, Y=2.0, Z=3, X=1.0)) ==
GeoJSON.write((X=1.0, Z=3, M=4, Y=2.0)) ==
"{\"type\":\"Point\",\"coordinates\":[1.0,2.0,3]}"
end

Expand Down

0 comments on commit 9bb34c6

Please sign in to comment.