From eb37a18553d4a216078e2e1726504e89dabbf006 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 30 Jan 2025 23:22:08 +0100 Subject: [PATCH 1/2] Always use GeometryVector as column (currently a noop). --- docs/src/reference/changes.md | 1 + src/GeoDataFrames.jl | 1 + src/exports.jl | 72 +++++++++++------------------------ src/io.jl | 12 +++--- src/vector.jl | 21 ++++++++++ test/runtests.jl | 4 +- 6 files changed, 52 insertions(+), 59 deletions(-) create mode 100644 src/vector.jl diff --git a/docs/src/reference/changes.md b/docs/src/reference/changes.md index 865b6db..9fef829 100644 --- a/docs/src/reference/changes.md +++ b/docs/src/reference/changes.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add new driver framework to read files using native drivers. - Added native driver package extensions on GeoJSON, ShapeFile, GeoParquet, GeoArrow and FlatGeobuf. - Geometry columns are now wrapped in a GeometryVector, allowing for future improvements. +- Now exports GeoInterface methods instead of ArchGDAL methods for geometry operation. ## v0.3.11 - Changed `reproject` to work on a DataFrame, correctly setting the (crs) metadata diff --git a/src/GeoDataFrames.jl b/src/GeoDataFrames.jl index 9f81b78..1dd5d19 100644 --- a/src/GeoDataFrames.jl +++ b/src/GeoDataFrames.jl @@ -8,6 +8,7 @@ import GeoInterface as GI using DataAPI using Reexport +include("vector.jl") include("exports.jl") include("drivers.jl") include("io.jl") diff --git a/src/exports.jl b/src/exports.jl index ea040db..d6ff3f7 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -1,16 +1,16 @@ -@reexport using ArchGDAL: +@reexport using GeoInterface: intersects, equals, disjoint, touches, crosses, within, contains, overlaps -@reexport using ArchGDAL: boundary, convexhull, buffer -@reexport using ArchGDAL: intersection, union, difference, symdifference, distance -@reexport using ArchGDAL: geomlength, geomarea, centroid -@reexport using ArchGDAL: isvalid, issimple, isring, geomarea, centroid -@reexport using ArchGDAL: - createpoint, - createlinestring, - createlinearring, - createpolygon, - createmultilinestring, - createmultipolygon +@reexport using GeoInterface: boundary, convexhull, buffer +@reexport using GeoInterface: intersection, union, difference, symdifference, distance +@reexport using GeoInterface: area, centroid +@reexport using GeoInterface: isvalid, issimple, isring, geomarea, centroid +# @reexport using GeoInterface: +# createpoint, +# createlinestring, +# createlinearring, +# createpolygon, +# createmultilinestring, +# createmultipolygon @reexport using Extents @reexport using GeoInterface: crs @@ -49,41 +49,13 @@ export AbstractWellKnownText, WellKnownText, WellKnownText2 #=GeoJSON,=# -AG.intersects(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.intersects.(a, b) -AG.equals(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.equals.(a, b) -AG.disjoint(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.disjoint.(a, b) -AG.touches(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.touches.(a, b) -AG.crosses(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.crosses.(a, b) -AG.within(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.within.(a, b) -AG.contains(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.contains.(a, b) -AG.overlaps(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.overlaps.(a, b) -AG.intersection(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.intersection.(a, b) -AG.union(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.union.(a, b) -AG.difference(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.difference.(a, b) -AG.symdifference(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.symdifference.(a, b) -AG.distance(a::Vector{AG.IGeometry{T}}, b::Vector{AG.IGeometry{X}}) where {X, T} = - AG.distance.(a, b) - -AG.boundary(v::Vector{AG.IGeometry{T}}) where {T} = AG.boundary.(v) -AG.convexhull(v::Vector{AG.IGeometry{T}}) where {T} = AG.convexhull.(v) -AG.buffer(v::Vector{AG.IGeometry{T}}, d) where {T} = AG.buffer.(v, d) -AG.transform!(v::Vector{AG.IGeometry{T}}, d) where {T} = AG.buffer.(v, d) -AG.geomlength(v::Vector{AG.IGeometry{T}}) where {T} = AG.geomlength.(v) -AG.geomarea(v::Vector{AG.IGeometry{T}}) where {T} = AG.geomarea.(v) -AG.centroid(v::Vector{AG.IGeometry{T}}) where {T} = AG.centroid.(v) -AG.isempty(v::Vector{AG.IGeometry{T}}) where {T} = AG.isempty.(v) -AG.isvalid(v::Vector{AG.IGeometry{T}}) where {T} = AG.isvalid.(v) -AG.issimple(v::Vector{AG.IGeometry{T}}) where {T} = AG.issimple.(v) -AG.isring(v::Vector{AG.IGeometry{T}}) where {T} = AG.isring.(v) +GI.boundary(v::GeometryVector) = GI.boundary.(v) +GI.convexhull(v::GeometryVector) = GI.convexhull.(v) +GI.buffer(v::GeometryVector, d) = GI.buffer.(v, d) +GI.length(v::GeometryVector) = GI.geomlength.(v) +GI.area(v::GeometryVector) = GI.area.(v) +GI.centroid(v::GeometryVector) = GI.centroid.(v) +GI.isempty(v::GeometryVector) = GI.isempty.(v) +GI.isvalid(v::GeometryVector) = GI.isvalid.(v) +GI.issimple(v::GeometryVector) = GI.issimple.(v) +GI.isring(v::GeometryVector) = GI.isring.(v) diff --git a/src/io.jl b/src/io.jl index cb70916..25ae038 100644 --- a/src/io.jl +++ b/src/io.jl @@ -50,7 +50,11 @@ Read a file into a DataFrame. Any kwargs are passed to the driver, by default se function read(fn; kwargs...) ext = last(splitext(fn)) dr = driver(ext) - read(dr, fn; kwargs...) + df = read(dr, fn; kwargs...) + for geom in getgeometrycolumns(df) + df[!, geom] = GeometryVector(df[!, geom]) + end + df end @deprecate read(fn::AbstractString, layer::Union{AbstractString, Integer}; kwargs...) read( @@ -93,12 +97,6 @@ function read(driver::ArchGDALDriver, fn::AbstractString; layer=nothing, kwargs. return t end -@deprecate read(fn::AbstractString, layer::Union{AbstractString, Integer}; kwargs...) read( - fn; - layer, - kwargs..., -) - function read(::ArchGDALDriver, ds, layer) df, gnames, sr = AG.getlayer(ds, layer) do table if table.ptr == C_NULL diff --git a/src/vector.jl b/src/vector.jl new file mode 100644 index 0000000..b0a4f99 --- /dev/null +++ b/src/vector.jl @@ -0,0 +1,21 @@ +""" + GeometryVector(A) + +A vector of geometries, as used in GeoDataFrames. +""" +struct GeometryVector{T} <: AbstractArray{T, 1} + A::Vector{T} + # TODO Add spatial index + # TODO Add crs +end + +Base.parent(G::GeometryVector) = G.A +Base.size(G::GeometryVector) = size(parent(G)) +Base.length(G::GeometryVector) = length(parent(G)) +Base.IndexStyle(::Type{<:GeometryVector}) = IndexLinear() +# TODO Invalidate spatial index +Base.getindex(G::GeometryVector, i::Int) = getindex(parent(G), i) +Base.setindex!(G::GeometryVector, v, i::Int) = setindex!(parent(G), v, i) + +# https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array +Base.similar(G::GeometryVector, ::Type{T}, dims::Dims) where {T} = GeometryVector(similar(parent(G), T, dims)) diff --git a/test/runtests.jl b/test/runtests.jl index de53cfe..0eeadbe 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -196,10 +196,10 @@ unknown_crs = GFT.WellKnownText( end @testset "Spatial operations" begin - table = DataFrame(; geometry = AG.createpoint.(coords), name = "test") + table = DataFrame(; geometry = GDF.GeometryVector(AG.createpoint.(coords)), name = "test") # Buffer to also write polygons - table.geometry = AG.buffer(table.geometry, 10) + table.geometry = GDF.buffer(table.geometry, 10) GDF.write(joinpath(testdatadir, "test_polygons.shp"), table) GDF.write(joinpath(testdatadir, "test_polygons.gpkg"), table) GDF.write(joinpath(testdatadir, "test_polygons.geojson"), table) From 06f68c150c56439837fccbb5cf5b7276d62e2b77 Mon Sep 17 00:00:00 2001 From: Maarten Pronk Date: Thu, 30 Jan 2025 23:34:51 +0100 Subject: [PATCH 2/2] Geomarea doesn't exist. --- src/exports.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exports.jl b/src/exports.jl index d6ff3f7..e16c9cf 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -3,7 +3,7 @@ @reexport using GeoInterface: boundary, convexhull, buffer @reexport using GeoInterface: intersection, union, difference, symdifference, distance @reexport using GeoInterface: area, centroid -@reexport using GeoInterface: isvalid, issimple, isring, geomarea, centroid +@reexport using GeoInterface: isvalid, issimple, isring, centroid # @reexport using GeoInterface: # createpoint, # createlinestring,