Support `Base.filter` on `FeatureCollection`
It would be convenient if you could do something like filter(f -> f.NAME == "France", collection), but currently filtering is not supported.
Right now, I'm naivley pirating this like so:
function Base.filter(f::Function, fc::GeoJSON.FeatureCollection)
features = [feat for feat in fc if f(feat)]
GeoJSON.FeatureCollection(; bbox=nothing, features, crs=getfield(fc, :crs))
end
But I'm sure there is potential for improvement.
Is there a reason not to convert to a DataFrames.jl DataFrame and filter that?
Well my use case was GeoMakie, which works directly on FeatureCollections.
You can pass the geometry column of a dataframe to GeoMakie and it should just work.
It doesn't, though. Here's a reproducer:
using CairoMakie, GeoMakie, GeoMakie.GeoJSON
using DataFrames, HTTP
url = "https://raw.githubusercontent.com/" *
"nvkelso/natural-earth-vector/master/geojson/" *
"ne_10m_admin_0_countries.geojson"
df = GeoJSON.read(HTTP.get(url).body) |> DataFrame
let fig = Figure()
ax = GeoAxis(fig[1, 1])
poly!(ax, df.geometry)
fig
end
this throws a conversion error:
ERROR: `Makie.convert_arguments` for the plot type Scatter and its conversion trait PointBased() was unsuccessful.
The signature that could not be converted was:
::Vector{Any}
[...]
I suspect it has to do with the geometry column having both polygon and multipolygon entries:
julia> df.geometry
258-element Vector{Union{GeoJSON.MultiPolygon{2, Float32}, GeoJSON.Polygon{2, Float32}}}:
2D MultiPolygonwith 264 sub-geometries
2D MultiPolygonwith 17 sub-geometries
2D MultiPolygonwith 163 sub-geometries
2D Polygonwith 1 sub-geometries
[...]
(there seems to be a space missing in the show method for polygons, btw.)
So I guess it might just be an XY problem, but maybe having filter(f, ::FeatureCollection) would still make sense?
ah yeah, somehow the type doesn't union to GeoJSON.AbstractGeometry, which is a bummer.
In this case you can do something like this: https://github.com/MakieOrg/GeoMakie.jl/blob/3ba224449aea3c78d81931b5a9a177be360d1ec6/src/conversions.jl#L16
basically forcibly convert all of your geometries to multipolygons, so that the array has the correct type.
As an aside GeoMakie.jl could accept any Tables.jl compatible table and get the GeoInterface.geometrycolumns column too remove one step.
But yeah that dispatch on mixed geometry vectors is annoying in a lot of places.
@asinghvi17 would wrapping a mixed column as a GeometryCollection be another hack to make that work?
I'm not sure that GeoMakie would accept a geometrycollection, given that it has to go to multiple plot types. Perhaps after Makie v0.21 once we figure out this specapi thing...
We could special case it for the poly recipe so it only plots the polygon components of the geometrycollection though, that would probably work.
Anyway, I will close this. Operations like filter should just use a dataframe, we cant implement everything everywhere.
If you want to use filter on a GeoJSON feature collection, you can use TableOperations.jl, which is implementation-agnostic - though it will probably be slower than DataFrames!