Skip to content

Commit 38f9eb8

Browse files
authored
Merge pull request #2 from JuliaGeo/feat/write-support
Full read/write support.
2 parents 1575c07 + f2b5ee6 commit 38f9eb8

File tree

9 files changed

+209
-37
lines changed

9 files changed

+209
-37
lines changed

.github/dependabot.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
4+
version: 2
5+
updates:
6+
- package-ecosystem: "" # See documentation for possible values
7+
directory: "/" # Location of package manifests
8+
schedule:
9+
interval: "weekly"

.github/workflows/CI.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
version:
21-
- '1.6'
21+
- 'lts'
2222
- '1'
2323
- 'nightly'
2424
os:
@@ -29,17 +29,19 @@ jobs:
2929
- x64
3030
steps:
3131
- uses: actions/checkout@v2
32-
- uses: julia-actions/setup-julia@v1
32+
- uses: julia-actions/setup-julia@v2
3333
with:
3434
version: ${{ matrix.version }}
3535
arch: ${{ matrix.arch }}
3636
- uses: julia-actions/cache@v1
3737
- uses: julia-actions/julia-buildpkg@v1
3838
- uses: julia-actions/julia-runtest@v1
3939
- uses: julia-actions/julia-processcoverage@v1
40-
- uses: codecov/codecov-action@v2
40+
- uses: codecov/codecov-action@v5
4141
with:
4242
files: lcov.info
43+
env:
44+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
4345
docs:
4446
name: Documentation
4547
runs-on: ubuntu-latest

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
test/.cpenv
77
.CondaPkg
88
*.arrow
9+
10+
# pixi environments
11+
.pixi
12+
*.egg-info

Project.toml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
name = "GeoArrow"
22
uuid = "5bc3a8d9-1bfb-4624-ba94-a391279174d6"
33
authors = ["Maarten Pronk <git@evetion.nl> and contributors"]
4-
version = "0.1.0"
4+
version = "0.2.0"
55

66
[deps]
77
Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45"
8+
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
9+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
810
Extents = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
911
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
1012
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
1113
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
14+
Proj = "c94c279d-25a6-4763-9509-64d165bea63e"
15+
StringViews = "354b36f9-a18e-4713-926e-db85100087ba"
16+
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1217
WellKnownGeometry = "0f680547-7be7-4555-8820-bb198eeb646b"
1318

1419
[compat]
15-
Arrow = "2.4"
20+
Arrow = "2.8"
21+
DataAPI = "1"
22+
DataFrames = "1.7.0"
1623
Extents = "0.1"
1724
GeoFormatTypes = "0.4"
1825
GeoInterface = "1.2"
1926
JSON3 = "1.14"
27+
Proj = "1.8"
28+
PythonCall = "0.9.23"
29+
StringViews = "1.3"
30+
Tables = "1.12"
2031
WellKnownGeometry = "0.2"
21-
julia = "1.6"
32+
julia = "1.10"
2233

2334
[extras]
2435
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"

src/GeoArrow.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ using GeoFormatTypes
55
using JSON3
66
using WellKnownGeometry
77
using Extents
8+
using Tables
9+
using StringViews
10+
using Proj
11+
using DataAPI
12+
using DataFrames
813

914
include("type.jl")
1015
include("arrow.jl")

src/arrow.jl

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,41 +36,68 @@ function ArrowTypes.JuliaType(::Val{BOX}, x, metadata)
3636
end
3737
ArrowTypes.ArrowKind(::Type{Geometry}) = ArrowTypes.ListKind()
3838
ArrowTypes.ArrowKind(::Type{<:Geometry{PointTrait,D,T}}) where {D,T} = ArrowTypes.FixedSizeListKind{D,T}()
39-
ArrowTypes.ArrowKind(::Type{<:GeoFormatTypes.WellKnownBinary}) = ArrowTypes.PrimitiveKind()
40-
ArrowTypes.ArrowKind(::Type{<:GeoFormatTypes.WellKnownText}) = ArrowTypes.ListKind()
39+
ArrowTypes.ArrowKind(::Type{Wrapper}) = ArrowTypes.ListKind()
40+
ArrowTypes.ArrowKind(::Type{Wrapper{Seperated, T, G}}) where {T,G} = ArrowTypes.StructKind()
41+
ArrowTypes.ArrowKind(::Type{Wrapper{Interleaved, T, N, G}}) where {T,N,G} = ArrowTypes.ListKind()
42+
ArrowTypes.ArrowKind(::Type{Wrapper{Interleaved, PointTrait, N, G}}) where {N,G} = ArrowTypes.FixedSizeListKind{N,Float64}()
43+
ArrowTypes.ArrowKind(::Type{Wrapper{WellKnownText, T, N, G}}) where {T,N,G} = ArrowTypes.ListKind()
44+
ArrowTypes.ArrowKind(::Type{Wrapper{WellKnownBinary, T, N, G}}) where {T,N,G} = ArrowTypes.ListKind()
4145

42-
ArrowTypes.ArrowType(::Type{<:GeoFormatTypes.WellKnownBinary}) = Vector{UInt8}
43-
ArrowTypes.ArrowType(::Type{<:GeoFormatTypes.WellKnownText}) = String
4446
ArrowTypes.ArrowType(::Type{Geometry{X,D,T,G}}) where {X,D,T,G} = G
47+
ArrowTypes.ArrowType(::Type{Wrapper{WellKnownText,T,N,G}}) where {T,N,G} = String
48+
ArrowTypes.ArrowType(::Type{Wrapper{WellKnownBinary,T,N,G}}) where {T,N,G} = Vector{UInt8}
49+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,PointTrait,N,G}}) where {N,G} = NTuple{N,Float64}
50+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,LineStringTrait,N,G}}) where {N,G} = Vector{NTuple{N,Float64}}
51+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,MultiLineStringTrait,N,G}}) where {N,G} = Vector{Vector{NTuple{N,Float64}}}
52+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,MultiPointTrait,N,G}}) where {N,G} = Vector{NTuple{N,Float64}}
53+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,PolygonTrait,N,G}}) where {N,G} = Vector{Vector{NTuple{N,Float64}}}
54+
ArrowTypes.ArrowType(::Type{Wrapper{Interleaved,MultiPolygonTrait,N,G}}) where {N,G} = Vector{Vector{Vector{NTuple{N,Float64}}}}
55+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,PointTrait,N,G}}) where {N,G} = _named(NTuple{N,Float64})
56+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,LineStringTrait,N,G}}) where {N,G} = Vector{_named(NTuple{N,Float64})}
57+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,MultiLineStringTrait,N,G}}) where {N,G} = Vector{Vector{_named(NTuple{N,Float64})}}
58+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,MultiPointTrait,N,G}}) where {N,G} = Vector{_named(NTuple{N,Float64})}
59+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,PolygonTrait,N,G}}) where {N,G} = Vector{Vector{_named(NTuple{N,Float64})}}
60+
ArrowTypes.ArrowType(::Type{Wrapper{Seperated,MultiPolygonTrait,N,G}}) where {N,G} = Vector{Vector{Vector{_named(NTuple{N,Float64})}}}
4561

4662
ArrowTypes.arrowname(::Type{Geometry{PointTrait}}) = POINT
4763
ArrowTypes.arrowname(::Type{Geometry{LineStringTrait}}) = LINESTRING
4864
ArrowTypes.arrowname(::Type{Geometry{PolygonTrait}}) = POLYGON
4965
ArrowTypes.arrowname(::Type{Geometry{MultiPointTrait}}) = MULTIPOINT
5066
ArrowTypes.arrowname(::Type{Geometry{MultiLineStringTrait}}) = MULTILINESTRING
5167
ArrowTypes.arrowname(::Type{Geometry{MultiPolygonTrait}}) = MULTIPOLYGON
52-
ArrowTypes.arrowname(::Type{<:GeoFormatTypes.WellKnownBinary}) = WKB
53-
ArrowTypes.arrowname(::Type{<:GeoFormatTypes.WellKnownText}) = WKT
68+
ArrowTypes.arrowname(::Type{Wrapper{WellKnownBinary,T,N,G}}) where {T,N,G} = WKB
69+
ArrowTypes.arrowname(::Type{Wrapper{WellKnownText,T,N,G}}) where {T,N,G} = WKT
70+
ArrowTypes.arrowname(::Type{Wrapper{E,PointTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = POINT
71+
ArrowTypes.arrowname(::Type{Wrapper{E,LineStringTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = LINESTRING
72+
ArrowTypes.arrowname(::Type{Wrapper{E,PolygonTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = POLYGON
73+
ArrowTypes.arrowname(::Type{Wrapper{E,MultiPointTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = MULTIPOINT
74+
ArrowTypes.arrowname(::Type{Wrapper{E,MultiLineStringTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = MULTILINESTRING
75+
ArrowTypes.arrowname(::Type{Wrapper{E,MultiPolygonTrait,N,G}}) where {E<:AbstractNativeEncoding,N,G} = MULTIPOLYGON
5476
ArrowTypes.arrowname(::Type{Extents.Extent}) = BOX
5577

5678
ArrowTypes.toarrow(x::Geometry) = x.geom
57-
ArrowTypes.toarrow(x::GeoFormatTypes.WellKnownText) = GeoFormatTypes.val(x)
58-
ArrowTypes.toarrow(x::GeoFormatTypes.WellKnownBinary) = GeoFormatTypes.val(x)
59-
ArrowTypes.toarrow(x::Extents.Extent{(:X, :Y)}) = (; xmin=ex.X[1], ymin=ex.Y[1], xmax=ex.X[2], ymax=ex.Y[2])
60-
ArrowTypes.toarrow(x::Extents.Extent{(:X, :Y, :Z)}) = (; xmin=ex.X[1], ymin=ex.Y[1], zmin=ex.Z[1], xmax=ex.X[2], ymax=ex.Y[2], zmax=ex.Z[2])
61-
ArrowTypes.toarrow(x::Extents.Extent{(:X, :Y, :Z, :M)}) = (; xmin=ex.X[1], ymin=ex.Y[1], zmin=ex.Z[1], mmin=ex.M[1], xmax=ex.X[2], ymax=ex.Y[2], zmax=ex.Z[2], mmax=ex.M[2])
79+
ArrowTypes.toarrow(x::Wrapper{E,T,<:Geometry}) where {E,T} = x.geom
80+
ArrowTypes.toarrow(x::Wrapper) = data(x)
81+
ArrowTypes.toarrow(ex::Extents.Extent{(:X, :Y)}) = (; xmin=ex.X[1], ymin=ex.Y[1], xmax=ex.X[2], ymax=ex.Y[2])
82+
ArrowTypes.toarrow(ex::Extents.Extent{(:X, :Y, :Z)}) = (; xmin=ex.X[1], ymin=ex.Y[1], zmin=ex.Z[1], xmax=ex.X[2], ymax=ex.Y[2], zmax=ex.Z[2])
83+
ArrowTypes.toarrow(ex::Extents.Extent{(:X, :Y, :Z, :M)}) = (; xmin=ex.X[1], ymin=ex.Y[1], zmin=ex.Z[1], mmin=ex.M[1], xmax=ex.X[2], ymax=ex.Y[2], zmax=ex.Z[2], mmax=ex.M[2])
6284

6385
ArrowTypes.fromarrow(::Type{GeoFormatTypes.WellKnownBinary}, x) = GeoFormatTypes.WellKnownBinary(GeoFormatTypes.Geom(), x)
64-
ArrowTypes.fromarrow(::Type{GeoFormatTypes.WellKnownText}, x) = GeoFormatTypes.WellKnownText(GeoFormatTypes.Geom(), x)
65-
86+
ArrowTypes.fromarrow(::Type{GeoFormatTypes.WellKnownText}, x) = GeoFormatTypes.WellKnownText(GeoFormatTypes.Geom(), String(x)) # should be StringView
6687
function ArrowTypes.fromarrow(::Type{Geometry{X}}, x) where {X}
6788
nt = nested_eltype(x)
68-
D = length(nt.types)
69-
T = nt.types[1]
70-
return Geometry{X,D,T}(x)
89+
D = length(nonmissingtype(nt).types)
90+
return Geometry{X,D,Float64}(x)
91+
end
92+
function fromarrow(::Type{GeoArrow.Geometry{X}}, nt::NamedTuple) where X
93+
return Geometry{X,length(nt),Float64}(nt)
7194
end
7295
ArrowTypes.fromarrow(::Type{Extents.Extent}, x) = Extents.Extent(X=(x.xmin, x.xmax), Y=(x.ymin, x.ymax))
7396

7497
nested_eltype(x) = nested_eltype(typeof(x))
7598
nested_eltype(::Type{T}) where {T<:AbstractArray} = nested_eltype(eltype(T))
7699
nested_eltype(::Type{T}) where {T} = T
100+
101+
_named(::Type{NTuple{2, T}}) where {T} = @NamedTuple{x::Float64, y::Float64}
102+
_named(::Type{NTuple{3, T}}) where {T} = @NamedTuple{x::Float64, y::Float64, z::Float64}
103+
_named(::Type{NTuple{4, T}}) where {T} = @NamedTuple{x::Float64, y::Float64, z::Float64, m::Float64}

src/io.jl

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,27 @@
44
Write a geospatial table to a file. Like Arrow.write, but with geospatial metadata.
55
Any kwargs are passed to Arrow.write.
66
"""
7-
function write(path, t; kwargs...)
8-
projjson = ""
9-
crs = Dict("crs" => projjson)
10-
colmetadata =
11-
Dict(:geometry => ["ARROW:extension:metadata" => JSON3.write(crs)])
12-
Arrow.write(path, t; colmetadata, kwargs...)
7+
function write(path, t; geocolumns=GeoInterface.geometrycolumns(t), crs=GeoInterface.crs(t), encoding::AbstractEncoding=Interleaved(), kwargs...)
8+
9+
if isnothing(crs)
10+
dcrs = Dict{String,String}()
11+
else
12+
pcrs = convert(Proj.CRS, crs)
13+
jcrs = convert(ProjJSON, pcrs)
14+
dcrs = Dict("crs" => GeoFormatTypes.val(jcrs))
15+
end
16+
ct = Tables.columntable(t)
17+
colmetadata = Dict{Symbol,Vector{Pair{String,String}}}()
18+
for column in geocolumns
19+
column in Tables.columnnames(t) || error("Geometry column $column not found in table")
20+
data = Tables.getcolumn(t, column)
21+
T = nonmissingtype(Tables.columntype(t, column))
22+
GeoInterface.isgeometry(T) || error("Geometry in $column must support the GeoInterface")
23+
ct = merge(ct, NamedTuple{(column,)}((Wrapper.(Ref(encoding), data),)))
24+
25+
colmetadata[column] = ["ARROW:extension:metadata" => JSON3.write(dcrs)]
26+
end
27+
Arrow.write(path, ct; colmetadata, kwargs...)
1328
end
1429

1530
"""
@@ -19,7 +34,23 @@ Read a geospatial table from a file. Like Arrow.Table, but with geospatial metad
1934
Any kwargs are passed to Arrow.Table.
2035
"""
2136
function read(path; kwargs...)
22-
t = Arrow.Table(path; kwargs...)
23-
meta = Arrow.getmetadata(t)
37+
at = Arrow.Table(path; kwargs...)
38+
t = DataFrame(at, copycols=false)
39+
40+
# set GeoInterface metadata
41+
names = []
42+
for (column, metadata) in DataAPI.colmetadata(t)
43+
"ARROW:extension:name" in keys(metadata) || continue
44+
startswith(metadata["ARROW:extension:name"], "geoarrow.") || continue
45+
push!(names, Symbol(column))
46+
47+
"ARROW:extension:metadata" in keys(metadata) || continue
48+
extmetadata = metadata["ARROW:extension:metadata"]
49+
isempty(extmetadata) && continue
50+
crs = get(JSON3.read(extmetadata), :crs, nothing)
51+
isnothing(crs) || DataAPI.metadata!(t, "GEOINTERFACE:crs", crs)
52+
end
53+
isempty(names) || DataAPI.metadata!(t, "GEOINTERFACE:geometrycolumns", Tuple(names))
54+
2455
return t
2556
end

src/type.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
# For reading
12
struct Geometry{X,D,T,G}
23
geom::G
34
end
5+
Base.:(==)(x::Geometry{X,D,T,G}, y::Geometry{X,D,T,G}) where {X,D,T,G} = x.geom == y.geom
46
Base.show(io::IO, x::Geometry{X,D,T}) where {X,D,T} = print(io, "$X geometry in $(D)D with eltype $T")
57
Geometry{X,D,T}(x) where {X,D,T} = Geometry{X,D,T,typeof(x)}(x)
68
Geometry{PointTrait}(x::Vararg{T,D}) where {T,D} = Geometry{PointTrait,D,T}(reinterpret(NTuple{D,T}, x))
@@ -13,13 +15,46 @@ Base.getindex(x::Geometry{PointTrait,D,T}, i) where {D,T} = Base.getindex(x.geom
1315
GeoInterface.isgeometry(::Type{<:Geometry}) = true
1416
GeoInterface.ncoord(_, ::Geometry{X,D}) where {X,D} = D
1517
GeoInterface.getcoord(::PointTrait, g::Geometry, i) = Base.getindex(g.geom, i)
18+
GeoInterface.getcoord(::PointTrait, g::Geometry{X,D,T,<:GeoFormatTypes.MixedFormat}, i) where {X,D,T} = getcoord(PointTrait(), g.geom, i)
1619
GeoInterface.geomtrait(::Geometry{X}) where {X} = X()
1720
GeoInterface.ngeom(_, g::Geometry) = length(g.geom)
21+
GeoInterface.ngeom(t, g::Geometry{X,D,T,<:GeoFormatTypes.MixedFormat}) where {X,D,T} = ngeom(t, g.geom)
1822
GeoInterface.getgeom(_, g::Geometry, i) = Base.getindex(g, i)
23+
GeoInterface.getgeom(t, g::Geometry{X,D,T,<:GeoFormatTypes.MixedFormat}, i) where {X,D,T} = getgeom(t, g.geom, i)
1924

2025
childtrait(::LineStringTrait) = PointTrait
2126
childtrait(::LinearRingTrait) = PointTrait
2227
childtrait(::PolygonTrait) = LinearRingTrait
2328
childtrait(::MultiPointTrait) = PointTrait
2429
childtrait(::MultiLineStringTrait) = LineStringTrait
2530
childtrait(::MultiPolygonTrait) = PolygonTrait
31+
32+
# For writing
33+
abstract type AbstractEncoding end
34+
abstract type AbstractNativeEncoding <: AbstractEncoding end
35+
struct Seperated <: AbstractNativeEncoding end
36+
struct Interleaved <: AbstractNativeEncoding end
37+
struct WellKnownBinary <: AbstractEncoding end
38+
struct WellKnownText <: AbstractEncoding end
39+
40+
struct Wrapper{E,T,N,G}
41+
geom::G
42+
end
43+
Base.:(==)(x::Wrapper{E,T,N,G}, y::Wrapper{E,T,N,G}) where {E,T,N,G} = x.geom == y.geom
44+
Base.show(io::IO, ::Wrapper{E,T,G}) where {E,T,G} = print(io, "$T geometry encoded as $E")
45+
Wrapper(e::AbstractEncoding, x) = Wrapper{typeof(e),typeof(GeoInterface.geomtrait(x)),GeoInterface.ncoord(x),typeof(x)}(x)
46+
Wrapper(x) = Wrapper(Interleaved(), x)
47+
48+
data(x::Wrapper{E,T,N,G}) where {E,T,N,G} = _coordinates(E(), T(), Val{N}(), x.geom)
49+
data(x::Wrapper{WellKnownBinary,T,G}) where {T,G} = getwkb(x.geom).val
50+
data(x::Wrapper{WellKnownText,T,G}) where {T,G} = getwkt(x.geom).val
51+
52+
_coordinates(::Interleaved, t::AbstractPointTrait, ::Val{N}, geom) where N = NTuple{N,Float64}(getcoord(t, geom))
53+
_coordinates(::Seperated, t::AbstractPointTrait, ::Val{N}, geom) where N = nt(NTuple{N,Float64}(getcoord(t, geom)))
54+
function _coordinates(E::AbstractNativeEncoding, t::AbstractGeometryTrait, N, geom)
55+
map(x -> _coordinates(E, GeoInterface.geomtrait(x), N, x), getgeom(t, geom))
56+
end
57+
58+
nt(x::NTuple{2,Float64}) = NamedTuple{(:x, :y)}(x)
59+
nt(x::NTuple{3,Float64}) = NamedTuple{(:x, :y, :z)}(x)
60+
nt(x::NTuple{4,Float64}) = NamedTuple{(:x, :y, :z, :m)}(x)

test/runtests.jl

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ using Arrow
33
using GeoInterface
44
using Downloads
55
using Test
6-
# ENV["JULIA_CONDAPKG_OFFLINE"] = true
6+
using GeoFormatTypes
7+
using DataFrames
8+
using Extents
9+
10+
# ENV["JULIA_CONDAPKG_OFFLINE"] = true # for running locally
711
ENV["JULIA_CONDAPKG_ENV"] = joinpath(@__DIR__, ".cpenv")
812
using PythonCall
9-
# ga = pyimport("geoarrow.pyarrow")
1013
feather = pyimport("pyarrow.feather")
1114

1215
mkpath(joinpath(@__DIR__, "data/write"))
@@ -38,21 +41,66 @@ mkpath(joinpath(@__DIR__, "data/write"))
3841
GeoArrow.write(io, t; compress=:zstd)
3942
seekstart(io)
4043
nt = GeoArrow.read(io, convert=true)
41-
ngeom = t.geometry[1]
42-
@test GeoInterface.isgeometry(geom)
44+
ngeom = nt.geometry[1]
45+
@test GeoInterface.testgeometry(ngeom)
4346

44-
@test ngeom == geom
47+
@test GeoInterface.coordinates(ngeom) == GeoInterface.coordinates(geom)
4548
end
4649
end
4750
end
4851
@testset "Python" begin
4952
for arrowfn in filter(endswith("arrow"), readdir("data", join=true))
5053
@testset "$arrowfn" begin
51-
t = Arrow.Table(arrowfn)
54+
t = GeoArrow.read(arrowfn)
55+
geom = t.geometry[1]
56+
5257
fn = joinpath("data/write", basename(arrowfn))
5358
GeoArrow.write(fn, t)
54-
pt = feather.read_table(fn)
59+
60+
# Read with Python
61+
# gdf = geopandas.read_feather(fn)
62+
# print(gdf.geometry.type)
63+
t = feather.read_table(fn)
64+
meta = t.schema.field(-1).metadata
65+
@test length(meta.keys()) == 2
66+
@test any(occursin.("geoarrow", string.(meta.values())))
67+
68+
# Read with Julia
69+
tt = GeoArrow.read(fn)
70+
tt.geometry[1] == geom
5571
end
5672
end
5773
end
74+
@testset "Encodings" begin
75+
g = GeoFormatTypes.WellKnownText(GeoFormatTypes.Geom(), "POINT (1 2)")
76+
77+
w = GeoArrow.Wrapper(GeoArrow.WellKnownText(), g)
78+
@test ArrowTypes.ArrowKind(typeof(w)) == ArrowTypes.ListKind()
79+
@test ArrowTypes.ArrowType(typeof(w)) == String
80+
@test ArrowTypes.arrowname(typeof(w)) == Symbol("geoarrow.wkt")
81+
@test ArrowTypes.toarrow(w) == "POINT (1.0 2.0)"
82+
83+
w = GeoArrow.Wrapper(GeoArrow.WellKnownBinary(), g)
84+
@test ArrowTypes.ArrowKind(typeof(w)) == ArrowTypes.ListKind()
85+
@test ArrowTypes.ArrowType(typeof(w)) == Vector{UInt8}
86+
@test ArrowTypes.toarrow(w)[1:10] == UInt8[0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
87+
88+
w = GeoArrow.Wrapper(g) # Encoding defaults to Interleaved
89+
@test ArrowTypes.ArrowKind(typeof(w)) == ArrowTypes.FixedSizeListKind{2,Float64}()
90+
@test ArrowTypes.ArrowType(typeof(w)) == NTuple{2,Float64}
91+
@test ArrowTypes.arrowname(typeof(w)) == Symbol("geoarrow.point")
92+
@test ArrowTypes.toarrow(w) == (1.0, 2.0)
93+
94+
w = GeoArrow.Wrapper(GeoArrow.Seperated(), g)
95+
@test ArrowTypes.ArrowKind(typeof(w)) == ArrowTypes.StructKind()
96+
@test ArrowTypes.ArrowType(typeof(w)) == @NamedTuple{x::Float64, y::Float64}
97+
@test ArrowTypes.arrowname(typeof(w)) == Symbol("geoarrow.point")
98+
@test ArrowTypes.toarrow(w) == (; x=1.0, y=2.0)
99+
end
100+
@testset "Simple" begin
101+
df = DataFrame(a=1, geometry=[(1.,2.)])
102+
GeoArrow.write("simple.arrow", df)
103+
dfn = GeoArrow.read("simple.arrow")
104+
@test GeoInterface.isgeometry(dfn.geometry[1])
105+
end
58106
end

0 commit comments

Comments
 (0)