Interaction of GeoJSON with GeoInterface methods

Hi,

I’d like to read a .geojson file and perform some geometrical operations on the geometries (union, contains, etc.).
Using LibGEOS or ArchGDAL works fine, but i’ve got some troubles with GeoJSON (maybe I’m missing something).

Say I have a featureCollection named col:

julia> typeof(col)
GeoJSON.FeatureCollection{GeoJSON.Feature{JSON3.Object{Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}}, JSON3.Object{Vector{UInt8}, Vector{UInt64}}, JSON3.Array{JSON3.Object, Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}}

When I try to perform a GI.contains check, I get:

julia> import GeoInterface as GI
julia> GI.contains(col.geometry[2], col.geometry[1])
ERROR: MethodError: no method matching contains(::GeoInterface.PointTrait, ::GeoInterface.PointTrait, ::GeoJSON.Point{JSON3.Object{Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}}, ::GeoJSON.Point{JSON3.Object{Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}})
Closest candidates are:
  contains(::Union{GeoInterface.CircularStringTrait, GeoInterface.CompoundCurveTrait, GeoInterface.CurvePolygonTrait, GeoInterface.GeometryCollectionTrait, GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait, GeoInterface.MultiLineStringTrait, GeoInterface.MultiPointTrait, GeoInterface.MultiPolygonTrait, GeoInterface.MultiSurfaceTrait, GeoInterface.PointTrait, GeoInterface.PolygonTrait, GeoInterface.PolyhedralSurfaceTrait, GeoInterface.TINTrait, GeoInterface.TriangleTrait}, ::Union{GeoInterface.CircularStringTrait, GeoInterface.CompoundCurveTrait, GeoInterface.CurvePolygonTrait, GeoInterface.GeometryCollectionTrait, GeoInterface.LineStringTrait, GeoInterface.LinearRingTrait, GeoInterface.MultiLineStringTrait, GeoInterface.MultiPointTrait, GeoInterface.MultiPolygonTrait, GeoInterface.MultiSurfaceTrait, GeoInterface.PointTrait, GeoInterface.PolygonTrait, GeoInterface.PolyhedralSurfaceTrait, GeoInterface.TINTrait, GeoInterface.TriangleTrait}, ::ArchGDAL.AbstractGeometry, ::ArchGDAL.AbstractGeometry) at ~/.julia/packages/ArchGDAL/pPZyn/src/geointerface.jl:184
  contains(::GeoInterface.AbstractGeometryTrait, ::GeoInterface.AbstractGeometryTrait, ::LibGEOS.AbstractGeometry, ::LibGEOS.AbstractGeometry) at ~/.julia/packages/LibGEOS/Wbmhr/src/geo_interface.jl:145
  contains(::Any, ::Any) at ~/.julia/packages/GeoInterface/246K4/src/interface.jl:485
Stacktrace:
 [1] contains(a::GeoJSON.Point{JSON3.Object{Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}}, b::GeoJSON.Point{JSON3.Object{Vector{UInt8}, SubArray{UInt64, 1, Vector{UInt64}, Tuple{UnitRange{Int64}}, true}}})
   @ GeoInterface ~/.julia/packages/GeoInterface/246K4/src/interface.jl:485
 [2] top-level scope
   @ REPL[270]:1

Otherwise, it works fine with e.g. ArchGDAL:

julia> typeof(p)
ArchGDAL.IGeometry{ArchGDAL.wkbPolygon}

julia> GI.contains(p, p)
true

Before I fill an issue on Github, any ideas?
Or maybe I’m trying to do something out of the package’s scope?

As a workaround, I currently load the geojson using GeoDataFrames. That’s a bit of a heavy machinery for that simple task.

Try master of GeoJSON.jl. we are about to register major changes.

Thanks for the quick answer.
Unfortunately, this doesn’t change the outcome:

julia> using GeoJSON

julia> poi = GeoJSON.read(read("test.geojson"))
FeatureCollection with 1 Features

julia> poi[1].geometry
2D Polygonwith 1 sub-geometries

julia> import GeoInterface as GI

julia> GI.geomtrait(poi[1].geometry)
GeoInterface.PolygonTrait()

julia> GI.contains(poi[1].geometry, poi[1].geometry)
ERROR: MethodError: no method matching contains(::GeoInterface.PolygonTrait, ::GeoInterface.PolygonTrait, ::GeoJSON.Polygon{2, Float32}, ::GeoJSON.Polygon{2, Float32})
Closest candidates are:
  contains(::Any, ::Any) at ~/.julia/packages/GeoInterface/246K4/src/interface.jl:485
Stacktrace:
 [1] contains(a::GeoJSON.Polygon{2, Float32}, b::GeoJSON.Polygon{2, Float32})
   @ GeoInterface ~/.julia/packages/GeoInterface/246K4/src/interface.jl:485
 [2] top-level scope
   @ REPL[5]:1

The error message is different, though.

BTW, I’m running on v1.8.5.

Oh sorry I misunderstood.

contains is not defined for GeoJSON.jl. There is no default method.

So you have to choose a package method. I have PRs to both LibGeos and ArchGDAL so you can do ArchGDAL.contains or LibGEOS.contains for GeoJSON.jl or any other geometries. But they are not merged because they need the GeoInterface wrappers PR, which is nearly done.

But there is a lot of machinery to orchestrate here so l these nethods can just work on any possible object, and its not finished.

@evetion this situation is pretty confusing still I guess

Hmm yeah, this is sorta expected, but not documented for end-users.
And being able to test an interface would be nice for us developers.

From what I understand, you will now let ArchGDAL and LibGEOS now about wrapper types, and then we can add a generic fallback to a wrapper convert for the API? I’m not sure how the last part would work generically.

Besides, ideally we also want GI.intersects. Do we want to try and solve this via pkg extensions, or how Plots solves it with pyplot() to choose a backend?

The wrappers are just for the conversion tests, but I also used them to make sure the wrappers are useful.

There are generic methods to convert from any geometry types.

But yeah we have just avoided the backend problem so far, but Im not sure if its better than just doing e.g. LibGEOS.intersects. Basically all of those methods are covered by my PRs.

Thanks guys for your supports.

Being able to choose a backend (with a default, of course) would allow to be readily compatible with your geometric library of choice, and simplify the maintenance of GeoJSON regarding support of geometric entities (i.e. no need to reinvent the wheel one more time).

You may already be aware of that of course, but it’s just to say, from my point of view (as an end-user), this would make a lot of sense.

Also, this may allow GeoDataFrames to defer the responsibility of .geojson parsing to GeoJSON?