diff --git a/src/tables.jl b/src/tables.jl index c754d8a9..b3a70a88 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -1,5 +1,25 @@ -function Tables.schema(layer::AbstractFeatureLayer)::Nothing - return nothing +function Tables.schema( + layer::AbstractFeatureLayer, +)::Union{Nothing,Tables.Schema} + # If the layer has no features, calculate the schema from the layer + # otherwise let the features build the schema on the fly + # If we always build the schema, all isnullable (by default true) fields + # will result in columns with Union{Missing}. + nfeature(layer) == 0 || return nothing + ld = layerdefn(layer) + geom_names, field_names, _, fielddefns = schema_names(ld) + names = (geom_names..., field_names...) + types = Type[_datatype(getgeomdefn(ld, i - 1)) for i in 1:ngeom(ld)] + append!(types, map(_datatype, fielddefns)) + return Tables.Schema(names, types) +end + +function _datatype(fielddefn::IFieldDefnView) + return T = convert(DataType, getfieldtype(fielddefn)) +end + +function _datatype(fielddefn::IGeomFieldDefnView) + return IGeometry{gettype(fielddefn)} end Tables.istable(::Type{<:AbstractFeatureLayer})::Bool = true diff --git a/src/utils.jl b/src/utils.jl index 9e59f7ce..5359b840 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -207,7 +207,7 @@ macro ogrerr(code, message) "Unknown error." end - error($message * " ($detailmsg)") + error($(esc(message)) * " ($detailmsg)") end end end @@ -215,7 +215,7 @@ end macro cplerr(code, message) return quote if $(esc(code)) != GDAL.CE_None - error($message) + error($(esc(message))) end end end @@ -223,7 +223,7 @@ end macro cplwarn(code, message) return quote if $(esc(code)) != GDAL.CE_None - @warn $message + @warn $(esc(message)) end end end diff --git a/test/test_tables.jl b/test/test_tables.jl index b6ac469a..97e6345e 100644 --- a/test/test_tables.jl +++ b/test/test_tables.jl @@ -786,6 +786,45 @@ using Tables ) end end + @testset "Handle empty layers" begin + AG.read( + joinpath(@__DIR__, "data/multi_geom.csv"), + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ) do ds + layer = AG.copy(AG.getlayer(ds, 0)) + + # With features, no schema is defined + @test isnothing(Tables.schema(layer)) + # And tables are built up from features + table = Tables.columntable(layer) + @test Tables.columnnames(table) == + (:point, :linestring, :id, :zoom, :location) + @test Tables.rowcount(table) == 2 + + for i in 1:AG.nfeature(layer) + AG.deletefeature!(layer, i) + end + + # Without features, schema is still defined and table is empty + @test Tables.schema(layer) == Tables.Schema( + (:point, :linestring, :id, :zoom, :location), + ( + AG.IGeometry{AG.wkbUnknown}, + AG.IGeometry{AG.wkbUnknown}, + String, + String, + String, + ), + ) + table = Tables.columntable(layer) + @test Tables.rowcount(table) == 0 + @test Tables.columnnames(table) == + (:point, :linestring, :id, :zoom, :location) + end + end clean_test_dataset_files() end diff --git a/test/test_utils.jl b/test/test_utils.jl index 43e12565..db38caf4 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -3,9 +3,9 @@ import GDAL import ArchGDAL as AG "Test both that an ErrorException is thrown and that the message is as expected" -function eval_ogrerr(err, expected_message) - @test (@test_throws ErrorException AG.@ogrerr err "e:").value.msg == - "e: ($expected_message)" +function eval_ogrerr(err, expected_message, placeholder = "") + @test (@test_throws ErrorException AG.@ogrerr err "e $(placeholder):").value.msg == + "e $(placeholder): ($expected_message)" end @testset "test_utils.jl" begin @@ -18,7 +18,7 @@ end @testset "OGR Errors" begin @test isnothing(AG.@ogrerr GDAL.OGRERR_NONE "not an error") - eval_ogrerr(GDAL.OGRERR_NOT_ENOUGH_DATA, "Not enough data.") + eval_ogrerr(GDAL.OGRERR_NOT_ENOUGH_DATA, "Not enough data.", "foo") eval_ogrerr(GDAL.OGRERR_NOT_ENOUGH_MEMORY, "Not enough memory.") eval_ogrerr( GDAL.OGRERR_UNSUPPORTED_GEOMETRY_TYPE,