From 506af24663e096c540deca4573fc9e34e8765f9a Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Mon, 22 Nov 2021 06:49:09 +0100 Subject: [PATCH 01/14] 1st draft with steal geometry and parametric composite types --- src/base/display.jl | 4 +- src/base/iterators.jl | 22 +++- src/constants.jl | 15 +++ src/dataset.jl | 7 +- src/ogr/feature.jl | 98 +++++++++++--- src/ogr/featuredefn.jl | 64 ++++++++- src/ogr/featurelayer.jl | 32 ++++- src/ogr/fielddefn.jl | 44 ++++++- src/tables.jl | 56 ++++++-- src/types.jl | 278 +++++++++++++++++++++++++++++++++++++++- 10 files changed, 574 insertions(+), 46 deletions(-) diff --git a/src/base/display.jl b/src/base/display.jl index 3f7cbe11..63f97bc1 100644 --- a/src/base/display.jl +++ b/src/base/display.jl @@ -107,7 +107,7 @@ function Base.show( end # assumes that the layer is reset, and will reset it after display -function Base.show(io::IO, layer::AbstractFeatureLayer)::Nothing +function Base.show(io::IO, layer::DUAL_AbstractFeatureLayer)::Nothing if layer.ptr == C_NULL print(io, "NULL FeatureLayer") return nothing @@ -213,7 +213,7 @@ function Base.show(io::IO, gfd::AbstractGeomFieldDefn)::Nothing return nothing end -function Base.show(io::IO, feature::Feature)::Nothing +function Base.show(io::IO, feature::DUAL_AbstractFeature)::Nothing if feature.ptr == C_NULL print(io, "NULL Feature") return nothing diff --git a/src/base/iterators.jl b/src/base/iterators.jl index 5beb30c0..f333fb9f 100644 --- a/src/base/iterators.jl +++ b/src/base/iterators.jl @@ -13,11 +13,29 @@ function Base.iterate( end end +@generated function Base.iterate( + layer::FDP_AbstractFeatureLayer{FD}, + state::Integer = 0, +) where {FD<:FDType} + return quote + layer.ptr == C_NULL && return nothing + state == 0 && resetreading!(layer) + ptr = GDAL.ogr_l_getnextfeature(layer.ptr) + return if ptr == C_NULL + resetreading!(layer) + nothing + else + (FDP_Feature{$FD}(ptr; ownedby = layer), state + 1) + end + end +end + Base.eltype(layer::AbstractFeatureLayer)::DataType = Feature +Base.eltype(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} = FDP_Feature{FD} -Base.IteratorSize(::Type{<:AbstractFeatureLayer}) = Base.SizeUnknown() +Base.IteratorSize(::Type{<:DUAL_AbstractFeatureLayer}) = Base.SizeUnknown() -Base.length(layer::AbstractFeatureLayer)::Integer = nfeature(layer, true) +Base.length(layer::DUAL_AbstractFeatureLayer)::Integer = nfeature(layer, true) struct BlockIterator{T<:Integer} rows::T diff --git a/src/constants.jl b/src/constants.jl index 73eb41b6..278e6aa0 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -1,4 +1,19 @@ const StringList = Ptr{Cstring} +""" + @exported_enum EnumName[::BaseType] value1[=x] value2[=y] + +Call `@enum` on arguments, export `EnumName`` and all its instances +""" +#TODO: debug macro +macro exported_enum(T, syms...) + return esc(quote + @enum($T, $(syms...)) + export $T + for inst in Symbol.(instances($T)) + eval($(Expr(:quote, :(export $(Expr(:$, :inst)))))) + end + end) +end """ The value of `GDALDataType` could be different from `GDAL.GDALDataType`. diff --git a/src/dataset.jl b/src/dataset.jl index 719e8775..8b19d9d7 100644 --- a/src/dataset.jl +++ b/src/dataset.jl @@ -515,6 +515,12 @@ the application. """ getlayer(dataset::AbstractDataset, i::Integer)::IFeatureLayer = IFeatureLayer(GDAL.gdaldatasetgetlayer(dataset.ptr, i), ownedby = dataset) +function getFDPlayer(dataset::AbstractDataset, i::Integer)::FDP_IFeatureLayer + ptr::GDAL.OGRLayerH = GDAL.gdaldatasetgetlayer(dataset.ptr, i) + fd_ptr = GDAL.ogr_l_getlayerdefn(ptr) + FD = getFDType(fd_ptr) + return FDP_IFeatureLayer{FD}(ptr, ownedby = dataset) +end """ getlayer(dataset::AbstractDataset) @@ -531,7 +537,6 @@ function getlayer(dataset::AbstractDataset)::IFeatureLayer GDAL.gdaldatasetgetlayer(dataset.ptr, 0), ownedby = dataset, ) - end unsafe_getlayer(dataset::AbstractDataset, i::Integer)::FeatureLayer = diff --git a/src/ogr/feature.jl b/src/ogr/feature.jl index 42d5c9ab..c154be32 100644 --- a/src/ogr/feature.jl +++ b/src/ogr/feature.jl @@ -24,6 +24,12 @@ function destroy(feature::Feature)::Nothing feature.ptr = C_NULL return nothing end +function destroy(fdp_feature::FDP_AbstractFeature) + GDAL.ogr_f_destroy(fdp_feature.ptr) + fdp_feature.ptr = C_NULL + fdp_feature.ownedby = nothing + return nothing +end """ setgeom!(feature::Feature, geom::AbstractGeometry) @@ -53,7 +59,7 @@ end Returns a clone of the geometry corresponding to the feature. """ -function getgeom(feature::Feature)::IGeometry +function getgeom(feature::DUAL_AbstractFeature)::IGeometry result = GDAL.ogr_f_getgeometryref(feature.ptr) return if result == C_NULL IGeometry() @@ -79,6 +85,9 @@ Fetch number of fields on this feature. This will always be the same as the field count for the OGRFeatureDefn. """ nfield(feature::Feature)::Integer = GDAL.ogr_f_getfieldcount(feature.ptr) +@generated function nfield(::FDP_AbstractFeature{FD}) where {FD<:FDType} + return :($(_nft(FD))) +end """ getfielddefn(feature::Feature, i::Integer) @@ -95,6 +104,15 @@ internal reference, and should not be deleted or modified. """ getfielddefn(feature::Feature, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_f_getfielddefnref(feature.ptr, i)) +function getfielddefn( + fdp_feature::FDP_Feature{FD}, + i::Integer, +) where {FD<:FDType} + return FTP_IFieldDefnView{_ftvec(FD)[i+1]}( + GDAL.ogr_f_getfielddefnref(fdp_feature.ptr, i); + ownedby = getfeaturedefn(fdp_feature), + ) +end """ findfieldindex(feature::Feature, name::Union{AbstractString, Symbol}) @@ -112,7 +130,7 @@ the field index, or `nothing` if no matching field is found. This is a cover for the `OGRFeatureDefn::GetFieldIndex()` method. """ function findfieldindex( - feature::Feature, + feature::DUAL_AbstractFeature, name::Union{AbstractString,Symbol}, )::Union{Integer,Nothing} i = GDAL.ogr_f_getfieldindex(feature.ptr, name) @@ -132,7 +150,7 @@ Test if a field has ever been assigned a value or not. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -isfieldset(feature::Feature, i::Integer)::Bool = +isfieldset(feature::DUAL_AbstractFeature, i::Integer)::Bool = Bool(GDAL.ogr_f_isfieldset(feature.ptr, i)) """ @@ -164,7 +182,7 @@ Test if a field is null. ### References * https://gdal.org/development/rfc/rfc67_nullfieldvalues.html """ -isfieldnull(feature::Feature, i::Integer)::Bool = +isfieldnull(feature::DUAL_AbstractFeature, i::Integer)::Bool = Bool(GDAL.ogr_f_isfieldnull(feature.ptr, i)) """ @@ -227,7 +245,7 @@ Fetch field value as integer. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asint(feature::Feature, i::Integer)::Int32 = +asint(feature::DUAL_AbstractFeature, i::Integer)::Int32 = GDAL.ogr_f_getfieldasinteger(feature.ptr, i) """ @@ -239,7 +257,7 @@ Fetch field value as integer 64 bit. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asint64(feature::Feature, i::Integer)::Int64 = +asint64(feature::DUAL_AbstractFeature, i::Integer)::Int64 = GDAL.ogr_f_getfieldasinteger64(feature.ptr, i) """ @@ -251,7 +269,7 @@ Fetch field value as a double. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asdouble(feature::Feature, i::Integer)::Float64 = +asdouble(feature::DUAL_AbstractFeature, i::Integer)::Float64 = GDAL.ogr_f_getfieldasdouble(feature.ptr, i) """ @@ -263,7 +281,7 @@ Fetch field value as a string. * `feature`: the feature that owned the field. * `i`: the field to fetch, from 0 to GetFieldCount()-1. """ -asstring(feature::Feature, i::Integer)::String = +asstring(feature::DUAL_AbstractFeature, i::Integer)::String = GDAL.ogr_f_getfieldasstring(feature.ptr, i) """ @@ -281,7 +299,7 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asintlist(feature::Feature, i::Integer)::Vector{Int32} +function asintlist(feature::DUAL_AbstractFeature, i::Integer)::Vector{Int32} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasintegerlist(feature.ptr, i, n) return (n.x == 0) ? Int32[] : unsafe_wrap(Vector{Int32}, ptr, n.x) @@ -302,7 +320,7 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asint64list(feature::Feature, i::Integer)::Vector{Int64} +function asint64list(feature::DUAL_AbstractFeature, i::Integer)::Vector{Int64} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasinteger64list(feature.ptr, i, n) return (n.x == 0) ? Int64[] : unsafe_wrap(Vector{Int64}, ptr, n.x) @@ -323,7 +341,10 @@ the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. If *pnCount is zero on return the returned pointer may be NULL or non-NULL. """ -function asdoublelist(feature::Feature, i::Integer)::Vector{Float64} +function asdoublelist( + feature::DUAL_AbstractFeature, + i::Integer, +)::Vector{Float64} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasdoublelist(feature.ptr, i, n) return (n.x == 0) ? Float64[] : unsafe_wrap(Vector{Float64}, ptr, n.x) @@ -342,7 +363,7 @@ Fetch field value as a list of strings. the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. """ -asstringlist(feature::Feature, i::Integer)::Vector{String} = +asstringlist(feature::DUAL_AbstractFeature, i::Integer)::Vector{String} = GDAL.ogr_f_getfieldasstringlist(feature.ptr, i) """ @@ -358,7 +379,7 @@ Fetch field value as binary. the field value. This list is internal, and should not be modified, or freed. Its lifetime may be very brief. """ -function asbinary(feature::Feature, i::Integer)::Vector{UInt8} +function asbinary(feature::DUAL_AbstractFeature, i::Integer)::Vector{UInt8} n = Ref{Cint}() ptr = GDAL.ogr_f_getfieldasbinary(feature.ptr, i, n) return (n.x == 0) ? UInt8[] : unsafe_wrap(Vector{UInt8}, ptr, n.x) @@ -377,7 +398,7 @@ OFTDate, OFTTime and OFTDateTime fields. ### Returns `true` on success or `false` on failure. """ -function asdatetime(feature::Feature, i::Integer)::DateTime +function asdatetime(feature::DUAL_AbstractFeature, i::Integer)::DateTime pyr = Ref{Cint}() pmth = Ref{Cint}() pday = Ref{Cint}() @@ -438,7 +459,8 @@ function getdefault(feature::Feature, i::Integer)::Union{String,Nothing} return getdefault(getfielddefn(feature, i)) end -getfield(feature::Feature, i::Nothing)::Missing = missing +#! @yeesian What is the use of this function ? +getfield(feature::DUAL_AbstractFeature, i::Nothing)::Missing = missing const _FETCHFIELD = Dict{OGRFieldType,Function}( OFTInteger => asint, @@ -455,6 +477,10 @@ const _FETCHFIELD = Dict{OGRFieldType,Function}( OFTInteger64List => asint64list, ) +@generated function getfields_asfuncs(::Type{FD}) where {FD<:FDType} + return ((_FETCHFIELD[T.parameters[1]] for T in _ftvec(FD))...,) +end + """ getfield(feature, i) @@ -487,7 +513,25 @@ function getfield(feature::Feature, i::Integer) end end -function getfield(feature::Feature, name::Union{AbstractString,Symbol}) +@generated function getfield( + fdp_feature::FDP_AbstractFeature{FD}, + i::Integer, +) where {FD<:FDType} + return quote + return if !isfieldset(fdp_feature, i) + nothing + elseif isfieldnull(fdp_feature, i) + missing + else + $(getfields_asfuncs(FD))[i+1](fdp_feature, i) + end + end +end + +function getfield( + feature::DUAL_AbstractFeature, + name::Union{AbstractString,Symbol}, +) return getfield(feature, findfieldindex(feature, name)) end @@ -639,6 +683,9 @@ Fetch number of geometry fields on this feature. This will always be the same as the geometry field count for OGRFeatureDefn. """ ngeom(feature::Feature)::Integer = GDAL.ogr_f_getgeomfieldcount(feature.ptr) +@generated function ngeom(::FDP_AbstractFeature{FD}) where {FD<:FDType} + return :($(_ngt(FD))) +end """ getgeomdefn(feature::Feature, i::Integer) @@ -673,7 +720,7 @@ the geometry field index, or -1 if no matching geometry field is found. This is a cover for the `OGRFeatureDefn::GetGeomFieldIndex()` method. """ function findgeomindex( - feature::Feature, + feature::DUAL_AbstractFeature, name::Union{AbstractString,Symbol} = "", )::Integer return GDAL.ogr_f_getgeomfieldindex(feature.ptr, name) @@ -688,7 +735,7 @@ Returns a clone of the feature geometry at index `i`. * `feature`: the feature to get geometry from. * `i`: geometry field to get. """ -function getgeom(feature::Feature, i::Integer)::IGeometry +function getgeom(feature::DUAL_AbstractFeature, i::Integer)::IGeometry result = GDAL.ogr_f_getgeomfieldref(feature.ptr, i) return if result == C_NULL IGeometry() @@ -697,6 +744,19 @@ function getgeom(feature::Feature, i::Integer)::IGeometry end end +function stealgeom(feature::DUAL_AbstractFeature, i::Integer) + return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(feature.ptr)) : + getgeom(feature, i) +end +function stealgeom( + feature::DUAL_AbstractFeature, + name::Union{AbstractString,Symbol}, +) + i = findgeomindex(feature, name) + return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(feature.ptr)) : + getgeom(feature, i) +end + function unsafe_getgeom(feature::Feature, i::Integer)::Geometry result = GDAL.ogr_f_getgeomfieldref(feature.ptr, i) return if result == C_NULL @@ -707,7 +767,7 @@ function unsafe_getgeom(feature::Feature, i::Integer)::Geometry end function getgeom( - feature::Feature, + feature::DUAL_AbstractFeature, name::Union{AbstractString,Symbol}, )::IGeometry i = findgeomindex(feature, name) diff --git a/src/ogr/featuredefn.jl b/src/ogr/featuredefn.jl index bc7e40ec..4df81018 100644 --- a/src/ogr/featuredefn.jl +++ b/src/ogr/featuredefn.jl @@ -44,12 +44,22 @@ function destroy(featuredefn::FeatureDefn)::Nothing featuredefn.ptr = C_NULL return nothing end +function destroy(fdp_featuredefn::FDP_FeatureDefn) + GDAL.ogr_fd_destroy(fdp_featuredefn.ptr) + fdp_featuredefn.ptr = C_NULL + fdp_featuredefn.ownedby = nothing + return nothing +end "Destroy a feature definition view" function destroy(featuredefn::IFeatureDefnView)::Nothing featuredefn.ptr = C_NULL return nothing end +function destroy(fdp_ifeaturedefnview::FDP_IFeatureDefnView) + fdp_ifeaturedefnview.ptr = C_NULL + return fdp_ifeaturedefnview.ownedby = nothing +end """ release(featuredefn::FeatureDefn) @@ -76,6 +86,9 @@ Fetch number of fields on the passed feature definition. """ nfield(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getfieldcount(featuredefn.ptr) +@generated function nfield(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} + return :($(_nft(FD))) +end """ getfielddefn(featuredefn::FeatureDefn, i::Integer) @@ -93,9 +106,29 @@ object should not be modified or freed by the application. getfielddefn(featuredefn::FeatureDefn, i::Integer)::FieldDefn = FieldDefn(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) +function getfielddefn( + fdp_featuredefn::FDP_FeatureDefn{FD}, + i::Integer = 0, +) where {FD<:FDType} + return FTP_FieldDefn{_ftvec(FD)[i+1]}( + GDAL.ogr_fd_getfielddefn(fdp_featuredefn.ptr, i); + ownedby = fdp_featuredefn, + ) +end + getfielddefn(featuredefn::IFeatureDefnView, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) +function getfielddefn( + fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, + i::Integer = 0, +) where {FD<:FDType} + return FTP_IFieldDefnView{_ftvec(FD)[i+1]}( + GDAL.ogr_fd_getfielddefn(fdp_ifeaturedefnview.ptr, i); + ownedby = fdp_ifeaturedefnview, + ) +end + """ findfieldindex(featuredefn::AbstractFeatureDefn, name::Union{AbstractString, Symbol}) @@ -258,6 +291,9 @@ Fetch number of geometry fields on the passed feature definition. """ ngeom(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getgeomfieldcount(featuredefn.ptr) +@generated function ngeom(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} + return :($(_ngt(FD))) +end """ getgeomdefn(featuredefn::FeatureDefn, i::Integer = 0) @@ -271,12 +307,32 @@ Fetch geometry field definition of the passed feature definition. an internal field definition object or `NULL` if invalid index. This object should not be modified or freed by the application. """ -getgeomdefn(featuredefn::FeatureDefn, i::Integer = 0)::GeomFieldDefn = +getgeomdefn(featuredefn::AbstractFeatureDefn, i::Integer = 0)::GeomFieldDefn = GeomFieldDefn(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) +function getgeomdefn( + fdp_featuredefn::FDP_FeatureDefn{FD}, + i::Integer = 0, +) where {FD<:FDType} + return GFTP_GeomFieldDefn{_gtvec(FD)[i+1]}( + GDAL.ogr_fd_getgeomfielddefn(fdp_featuredefn.ptr, i); + ownedby = fdp_featuredefn, + ) +end + getgeomdefn(featuredefn::IFeatureDefnView, i::Integer = 0)::IGeomFieldDefnView = IGeomFieldDefnView(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) +function getgeomdefn( + fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, + i::Integer = 0, +) where {FD<:FDType} + return GFTP_IGeomFieldDefnView{_gtvec(FD)[i+1]}( + GDAL.ogr_fd_getgeomfielddefn(fdp_ifeaturedefnview.ptr, i); + ownedby = fdp_ifeaturedefnview, + ) +end + """ findgeomindex(featuredefn::AbstractFeatureDefn, name::AbstractString = "") @@ -370,3 +426,9 @@ Fetch feature definition. """ getfeaturedefn(feature::Feature)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_f_getdefnref(feature.ptr)) +function getfeaturedefn(fdp_feature::FDP_Feature{FD}) where {FD<:FDType} + return FDP_IFeatureDefnView{FD}( + GDAL.ogr_f_getdefnref(fdp_feature.ptr); + ownedby = fdp_feature.ownedby, + ) +end diff --git a/src/ogr/featurelayer.jl b/src/ogr/featurelayer.jl index d1c1e19b..c6cde280 100644 --- a/src/ogr/featurelayer.jl +++ b/src/ogr/featurelayer.jl @@ -1,10 +1,19 @@ +#! @yeesian Why is there a difference between interactive and non-interactive forms of +#! layer type regarding ownedby and spatialref properties function destroy(layer::AbstractFeatureLayer)::Nothing layer.ptr = C_NULL return nothing end +function destroy(fdp_layer::FDP_AbstractFeatureLayer) + # No specific GDAL object destructor for layer, it will be handled by the dataset closing + fdp_layer.ptr = C_NULL + fdp_layer.ownedby = nothing + fdp_layer.spatialref = nothing + return nothing +end -function destroy(layer::IFeatureLayer)::Nothing +function destroy(layer::Union{IFeatureLayer,FDP_IFeatureLayer})::Nothing layer.ptr = C_NULL layer.ownedby = Dataset() layer.spatialref = SpatialRef() @@ -110,14 +119,15 @@ end Return the layer name. """ -getname(layer::AbstractFeatureLayer)::String = GDAL.ogr_l_getname(layer.ptr) +getname(layer::DUAL_AbstractFeatureLayer)::String = + GDAL.ogr_l_getname(layer.ptr) """ getgeomtype(layer::AbstractFeatureLayer) Return the layer geometry type. """ -getgeomtype(layer::AbstractFeatureLayer)::OGRwkbGeometryType = +getgeomtype(layer::DUAL_AbstractFeatureLayer)::OGRwkbGeometryType = GDAL.ogr_l_getgeomtype(layer.ptr) """ @@ -355,7 +365,7 @@ Reset feature reading to start on the first feature. This affects `nextfeature()`. """ -function resetreading!(layer::L)::L where {L<:AbstractFeatureLayer} +function resetreading!(layer::L)::L where {L<:DUAL_AbstractFeatureLayer} GDAL.ogr_l_resetreading(layer.ptr) return layer end @@ -556,6 +566,12 @@ The `featuredefn` is owned by the `layer` and should not be modified. """ layerdefn(layer::AbstractFeatureLayer)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_l_getlayerdefn(layer.ptr)) +function layerdefn(fdp_layer::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return FDP_IFeatureDefnView{FD}( + GDAL.ogr_l_getlayerdefn(fdp_layer.ptr); + ownedby = fdp_layer, + ) +end """ findfieldindex(layer::AbstractFeatureLayer, @@ -585,7 +601,7 @@ Fetch the feature count in this layer, or `-1` if the count is not known. * `force`: flag indicating whether the count should be computed even if it is expensive. (`false` by default.) """ -nfeature(layer::AbstractFeatureLayer, force::Bool = false)::Integer = +nfeature(layer::DUAL_AbstractFeatureLayer, force::Bool = false)::Integer = GDAL.ogr_l_getfeaturecount(layer.ptr, force) """ @@ -594,6 +610,9 @@ nfeature(layer::AbstractFeatureLayer, force::Bool = false)::Integer = Fetch number of geometry fields on the feature layer. """ ngeom(layer::AbstractFeatureLayer)::Integer = ngeom(layerdefn(layer)) +@generated function ngeom(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return :($(_ngt(FD))) +end """ nfield(layer::AbstractFeatureLayer) @@ -601,6 +620,9 @@ ngeom(layer::AbstractFeatureLayer)::Integer = ngeom(layerdefn(layer)) Fetch number of fields on the feature layer. """ nfield(layer::AbstractFeatureLayer)::Integer = nfield(layerdefn(layer)) +@generated function nfield(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return :($(_nft(FD))) +end """ envelope(layer::AbstractFeatureLayer, force::Bool = false) diff --git a/src/ogr/fielddefn.jl b/src/ogr/fielddefn.jl index 1ec4feab..d1dfedbe 100644 --- a/src/ogr/fielddefn.jl +++ b/src/ogr/fielddefn.jl @@ -14,11 +14,22 @@ function destroy(fielddefn::FieldDefn)::Nothing fielddefn.ptr = C_NULL return nothing end +function destroy(ftp_fielddefn::FTP_FieldDefn) + GDAL.ogr_fld_destroy(ftp_fielddefn) + ftp_fielddefn.ptr = C_NULL + ftp_fielddefn.ownedby = nothing + return nothing +end function destroy(fielddefn::IFieldDefnView)::Nothing fielddefn.ptr = C_NULL return nothing end +function destroy(ftp_fielddefn::FTP_IFieldDefnView) + ftp_fielddefn.ptr = C_NULL + ftp_fielddefn.ownedby = nothing + return nothing +end "Set the name of this field." function setname!(fielddefn::FieldDefn, name::AbstractString)::FieldDefn @@ -27,12 +38,15 @@ function setname!(fielddefn::FieldDefn, name::AbstractString)::FieldDefn end "Fetch the name of this field." -getname(fielddefn::AbstractFieldDefn)::String = +getname(fielddefn::DUAL_AbstractFieldDefn)::String = GDAL.ogr_fld_getnameref(fielddefn.ptr) "Fetch the type of this field." gettype(fielddefn::AbstractFieldDefn)::OGRFieldType = GDAL.ogr_fld_gettype(fielddefn.ptr) +@generated function gettype(::FTP_AbstractFieldDefn{FType{T,ST}}) where {T,ST} + return :($T) +end "Set the type of this field." function settype!(fielddefn::FieldDefn, etype::OGRFieldType)::FieldDefn @@ -53,6 +67,11 @@ field subtype. """ getsubtype(fielddefn::AbstractFieldDefn)::OGRFieldSubType = GDAL.ogr_fld_getsubtype(fielddefn.ptr) +@generated function getsubtype( + ::FTP_AbstractFieldDefn{FType{T,ST}}, +) where {T,ST} + return :($ST) +end """ setsubtype!(fielddefn::FieldDefn, subtype::OGRFieldSubType) @@ -98,6 +117,11 @@ function getfieldtype( gettype(fielddefn) end end +@generated function getfieldtype( + ::FTP_AbstractFieldDefn{FType{T,ST}}, +) where {T,ST} + return ST != OFSTNone ? :($ST) : :($T) +end """ getjustify(fielddefn::AbstractFieldDefn) @@ -335,12 +359,25 @@ function destroy(geomdefn::GeomFieldDefn)::Nothing geomdefn.spatialref = SpatialRef() return nothing end +function destroy(gftp_geomfielddefn::GFTP_GeomFieldDefn) + GDAL.ogr_gfld_destroy(gftp_geomfielddefn) + gftp_geomfielddefn.ptr = C_NULL + gftp_geomfielddefn.ownedby = nothing + gftp_geomfielddefn.spatialref = nothing + return nothing +end "Destroy a geometry field definition." function destroy(geomdefn::IGeomFieldDefnView)::Nothing geomdefn.ptr = C_NULL return nothing end +function destroy(gftp_igeomfielddefnview::GFTP_IGeomFieldDefnView) + gftp_igeomfielddefnview.ptr = C_NULL + gftp_igeomfielddefnview.ownedby = nothing + gftp_igeomfielddefnview.spatialref = nothing + return nothing +end "Set the name of this field." function setname!(geomdefn::GeomFieldDefn, name::AbstractString)::GeomFieldDefn @@ -349,11 +386,12 @@ function setname!(geomdefn::GeomFieldDefn, name::AbstractString)::GeomFieldDefn end "Fetch name of this field." -getname(geomdefn::AbstractGeomFieldDefn)::String = +getname(geomdefn::DUAL_AbstractGeomFieldDefn)::String = GDAL.ogr_gfld_getnameref(geomdefn.ptr) "Fetch geometry type of this field." -gettype(geomdefn::AbstractGeomFieldDefn)::OGRwkbGeometryType = +#TODO: Maybe faster to specialize on GFTP_AbstractGeomFieldDefn and retrieve type from type parameter +gettype(geomdefn::DUAL_AbstractGeomFieldDefn)::OGRwkbGeometryType = GDAL.ogr_gfld_gettype(geomdefn.ptr) "Set the geometry type of this field." diff --git a/src/tables.jl b/src/tables.jl index 7d42f0fa..6654b26f 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -1,17 +1,17 @@ -function Tables.schema(layer::AbstractFeatureLayer)::Nothing +function Tables.schema(::DUAL_AbstractFeatureLayer)::Nothing return nothing end -Tables.istable(::Type{<:AbstractFeatureLayer})::Bool = true -Tables.rowaccess(::Type{<:AbstractFeatureLayer})::Bool = true +Tables.istable(::Type{<:DUAL_AbstractFeatureLayer})::Bool = true +Tables.rowaccess(::Type{<:DUAL_AbstractFeatureLayer})::Bool = true -function Tables.rows(layer::T)::T where {T<:AbstractFeatureLayer} +function Tables.rows(layer::T)::T where {T<:DUAL_AbstractFeatureLayer} return layer end -function Tables.getcolumn(row::Feature, i::Int) +function Tables.getcolumn(row::AbstractFeature, i::Int) if i > nfield(row) - return getgeom(row, i - nfield(row) - 1) + return stealgeom(row, i - nfield(row) - 1) elseif i > 0 return getfield(row, i - 1) else @@ -19,12 +19,38 @@ function Tables.getcolumn(row::Feature, i::Int) end end +function Tables.getcolumn( + row::FDP_AbstractFeature{FD}, + i::T, +) where {FD<:FDType,T<:Integer} + if i > nfield(row) + return stealgeom(row, i - nfield(row) - 1) + else + return getfield(row, i - 1) + end +end + function Tables.getcolumn(row::Feature, name::Symbol) field = getfield(row, name) if !ismissing(field) return field end - geom = getgeom(row, name) + geom = stealgeom(row, name) + if geom.ptr != C_NULL + return geom + end + return missing +end + +function Tables.getcolumn( + row::FDP_AbstractFeature{FD}, + name::Symbol, +) where {FD<:FDType} + field = getfield(row, name) + if !ismissing(field) + return field + end + geom = stealgeom(row, name) if geom.ptr != C_NULL return geom end @@ -32,7 +58,7 @@ function Tables.getcolumn(row::Feature, name::Symbol) end function Tables.columnnames( - row::Feature, + row::DUAL_AbstractFeature, )::NTuple{Int64(nfield(row) + ngeom(row)),Symbol} geom_names, field_names = schema_names(getfeaturedefn(row)) return (geom_names..., field_names...) @@ -47,3 +73,17 @@ function schema_names(featuredefn::IFeatureDefnView) ) return (geom_names, field_names, featuredefn, fielddefns) end + +#TODO: check wether some functions used in schema_names could be optimized +function schema_names( + fdp_featuredefn::FDP_IFeatureDefnView{FD}, +) where {FD<:FDType} + fielddefns = + (getfielddefn(fdp_featuredefn, i) for i in 0:nfield(fdp_featuredefn)-1) + field_names = (Symbol(getname(fielddefn)) for fielddefn in fielddefns) + geom_names = collect( + Symbol(getname(getgeomdefn(fdp_featuredefn, i - 1))) for + i in 1:ngeom(fdp_featuredefn) + ) + return (geom_names, field_names, fdp_featuredefn, fielddefns) +end diff --git a/src/types.jl b/src/types.jl index a2936a2b..e9fc3f78 100644 --- a/src/types.jl +++ b/src/types.jl @@ -10,16 +10,73 @@ abstract type AbstractSpatialRef end abstract type AbstractDataset end # needs to have a `ptr::GDAL.GDALDatasetH` attribute -abstract type AbstractFeatureDefn end +#! AbstractOFType could also be a non parameterized abstract type with +#! OFType{OGRFieldType, OGRFieldSubType} instead of +#! OFType{T,OGRFieldSubType} <: AbstractOFType{T} +abstract type AbstractFType{OGRFieldType} end #! NEW abstract type for fields to group field types by OGRFieldType +struct FType{T,OGRFieldSubType} <: AbstractFType{T} end #! NEW type for fields +function getFType(ptr::GDAL.OGRFieldDefnH) + return FType{ + convert(OGRFieldType, GDAL.ogr_fld_gettype(ptr)), + convert(OGRFieldSubType, GDAL.ogr_fld_getsubtype(ptr)), + } +end +abstract type AbstractGType end #! NEW abstract type for geometries +struct GType{OGRwkbGeometryType} <: AbstractGType end #! NEW type for geometries +function getGType(ptr::GDAL.OGRGeomFieldDefnH) + return GType{convert(OGRwkbGeometryType, GDAL.ogr_gfld_gettype(ptr))} +end + +#! NEW simple FeatureDefn type, could later maybe(?) replaced by full FeatureDefn type in the definitions below +FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter +@generated function _ngt(::Type{T}) where {T<:FDType} + return :(length($T.parameters[1].parameters)) +end +@generated function _gtvec(::Type{T}) where {T<:FDType} + return :(tuple($T.parameters[1].parameters...)) +end +@generated function _nft(::Type{T}) where {T<:FDType} + return :(length($T.parameters[2].parameters)) +end +@generated function _ftvec(::Type{T}) where {T<:FDType} + return :(tuple($T.parameters[2].parameters...)) +end +function getFDType(ptr::GDAL.OGRFeatureDefnH) + ng = GDAL.ogr_fd_getgeomfieldcount(ptr) + gflddefn_ptrs = (GDAL.ogr_fd_getgeomfielddefn(ptr, i - 1) for i in 1:ng) + TG = Tuple{(G for G in getGType.(gflddefn_ptrs))...} + nf = GDAL.ogr_fd_getfieldcount(ptr) + flddefn_ptrs = (GDAL.ogr_fd_getfielddefn(ptr, i - 1) for i in 1:nf) + TF = Tuple{(F for F in getFType.(flddefn_ptrs))...} + return Tuple{TG,TF} +end +#! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH (both Ptr{Cvoid})) and we cannot dispatch on it +# getFDType(ptr::GDAL.OGRLayerH) = getFDType(GDAL.ogr_l_getlayerdefn(ptr)) + +abstract type DUAL_AbstractFeatureDefn end #! NEW abstract type supertype of AbstractFeatureDefn and FDP_AbstractFeatureDefn +abstract type AbstractFeatureDefn <: DUAL_AbstractFeatureDefn end +abstract type FDP_AbstractFeatureDefn{FD<:FDType} <: DUAL_AbstractFeatureDefn end #! NEW abstract type to group FDP_FeatureDefn type instances # needs to have a `ptr::GDAL.OGRFeatureDefnH` attribute -abstract type AbstractFeatureLayer end +abstract type DUAL_AbstractFeatureLayer end #! NEW abstract type supertype of AbstractFeatureLayer and FDP_AbstractFeatureLayer +abstract type AbstractFeatureLayer <: DUAL_AbstractFeatureLayer end +abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer end #! NEW abstract type to group FDP_FeatureLayer type instances # needs to have a `ptr::GDAL.OGRLayerH` attribute -abstract type AbstractFieldDefn end +abstract type DUAL_AbstractFeature end #! NEW abstract type supertype of AbstractFeature and FDP_AbstractFeature +abstract type AbstractFeature <: DUAL_AbstractFeature end #! NEW abstract type to group Feature and IFeature (if created) +abstract type FDP_AbstractFeature{FD<:FDType} <: DUAL_AbstractFeature end #! NEW abstract type to group FDP_Feature type instances +# needs to have a `ptr::GDAL.OGRFeatureH attribute + +abstract type DUAL_AbstractFieldDefn end #! NEW abstract type, supertype of AbstractFieldDefn and FTP_AbstractFieldDefn +abstract type AbstractFieldDefn <: DUAL_AbstractFieldDefn end +abstract type FTP_AbstractFieldDefn{FT<:FType} <: DUAL_AbstractFieldDefn end #! NEW abstract type to group FTP_FieldDefn type instances # needs to have a `ptr::GDAL.OGRFieldDefnH` attribute -abstract type AbstractGeomFieldDefn end +abstract type DUAL_AbstractGeomFieldDefn end #! NEW abstract type, supertype of AbstractGeomFieldDefn and GFTP_AbstractGeomFieldDefn +abstract type AbstractGeomFieldDefn <: DUAL_AbstractGeomFieldDefn end +abstract type GFTP_AbstractGeomFieldDefn{GFT<:GType} <: + DUAL_AbstractGeomFieldDefn end #! NEW abstract type to group OGTP_FieldDefn type instances # needs to have a `ptr::GDAL.OGRGeomFieldDefnH` attribute abstract type AbstractRasterBand{T} <: AbstractDiskArray{T,2} end @@ -67,6 +124,34 @@ mutable struct IFieldDefnView <: AbstractFieldDefn end end +#! NEW FTP_FieldDefn +mutable struct FTP_FieldDefn{FT} <: FTP_AbstractFieldDefn{FT} + ptr::GDAL.OGRFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + + function FTP_FieldDefn{FT}( + ptr::GDAL.OGRFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + ) where {FT<:FType} + return new(ptr, ownedby) + end +end + +#! NEW FTP_IFieldDefnView +mutable struct FTP_IFieldDefnView{FT} <: FTP_AbstractFieldDefn{FT} + ptr::GDAL.OGRFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + + function FTP_IFieldDefnView{FT}( + ptr::GDAL.OGRFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + ) where {FT<:FType} + ftp_ifielddefnview = new(ptr, ownedby) + finalizer(destroy, ftp_ifielddefnview) + return ftp_ifielddefnview + end +end + mutable struct GeomFieldDefn <: AbstractGeomFieldDefn ptr::GDAL.OGRGeomFieldDefnH spatialref::AbstractSpatialRef @@ -89,6 +174,38 @@ mutable struct IGeomFieldDefnView <: AbstractGeomFieldDefn end end +#! NEW GFTP_GeomFieldDefn +mutable struct GFTP_GeomFieldDefn{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} + ptr::GDAL.OGRGeomFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + spatialref::Union{Nothing,AbstractSpatialRef} + + function GFTP_GeomFieldDefn{GFT}( + ptr::GDAL.OGRGeomFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {GFT<:GType} + return new(ptr, ownedby, spatialref) + end +end + +#! NEW GFTP_IGeomFieldDefnView +mutable struct GFTP_IGeomFieldDefnView{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} + ptr::GDAL.OGRGeomFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + spatialref::Union{Nothing,AbstractSpatialRef} + + function GFTP_IGeomFieldDefnView{GFT}( + ptr::GDAL.OGRGeomFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {GFT<:GType} + gftp_igeomfielddefnview = new(ptr, ownedby, spatialref) + finalizer(destroy, gftp_igeomfielddefnview) + return gftp_igeomfielddefnview + end +end + mutable struct RasterAttrTable ptr::GDAL.GDALRasterAttributeTableH end @@ -139,7 +256,7 @@ mutable struct IFeatureLayer <: AbstractFeatureLayer end end -mutable struct Feature +mutable struct Feature <: AbstractFeature ptr::GDAL.OGRFeatureH end @@ -157,6 +274,80 @@ mutable struct IFeatureDefnView <: AbstractFeatureDefn end end +#! NEW FeatureDefn parameterized FeatureDefn +mutable struct FDP_FeatureDefn{FD} <: FDP_AbstractFeatureDefn{FD} + ptr::GDAL.OGRFeatureDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} + + function FDP_FeatureDefn{FD}( + ptr::GDAL.OGRFeatureDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, + ) where {FD<:FDType} + return new(ptr, ownedby) + end +end + +#! NEW FeatureDefn parameterized IFeatureDefnView +mutable struct FDP_IFeatureDefnView{FD} <: FDP_AbstractFeatureDefn{FD} + ptr::GDAL.OGRFeatureDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} + + function FDP_IFeatureDefnView{FD}( + ptr::GDAL.OGRFeatureDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, + ) where {FD<:FDType} + fdp_ifeaturedefnview = new(ptr, ownedby) + finalizer(destroy, fdp_ifeaturedefnview) + return fdp_ifeaturedefnview + end +end + +#! NEW FeatureDefn parameterized Feature +mutable struct FDP_Feature{FD} <: FDP_AbstractFeature{FD} + ptr::GDAL.OGRFeatureH + ownedby::Union{Nothing,FDP_AbstractFeatureLayer} + + function FDP_Feature{FD}( + ptr::GDAL.OGRFeatureH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureLayer} = nothing, + ) where {FD<:FDType} + return new(ptr, ownedby) + end +end +#TODO: Add a ifeatureview on the model of ifeaturedefnview? + +#! NEW FeatureDefn parameterized FeatureLayer +mutable struct FDP_FeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} + ptr::GDAL.OGRLayerH + ownedby::AbstractDataset + spatialref::Union{Nothing,AbstractSpatialRef} + + function FDP_FeatureLayer{FD}( + ptr::GDAL.OGRLayerH = C_NULL; + ownedby::AbstractDataset = Dataset(), + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {FD<:FDType} + return new(ptr, ownedby, spatialref) + end +end + +#! NEW FeatureDefn parameterized IFeatureLayer +mutable struct FDP_IFeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} + ptr::GDAL.OGRLayerH + ownedby::AbstractDataset + spatialref::Union{Nothing,AbstractSpatialRef} + + function FDP_IFeatureLayer{FD}( + ptr::GDAL.OGRLayerH = C_NULL; + ownedby::AbstractDataset = Dataset(), + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {FD<:FDType} + layer = new(ptr, ownedby, spatialref) + finalizer(destroy, layer) + return layer + end +end + "Fetch the pixel data type for this band." pixeltype(ptr::GDAL.GDALRasterBandH)::DataType = convert(GDALDataType, GDAL.gdalgetrasterdatatype(ptr)) @@ -220,6 +411,19 @@ mutable struct Geometry{OGRwkbGeometryType} <: AbstractGeometry end _geomtype(::Geometry{T}) where {T} = T +#! NEW Geometry +# mutable struct Geom{T<:GType} <: AbstractGeometry +# ptr::GDAL.OGRGeometryH +# ownedby::Union{Nothing,DUAL_AbstractFeature} + +# function Geom{T}( +# ptr::GDAL.OGRGeometryH = C_NULL, +# ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, +# ) where {T<:GType} +# return new(ptr, ownedby) +# end +# end + mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry ptr::GDAL.OGRGeometryH @@ -231,6 +435,21 @@ mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry end _geomtype(::IGeometry{T}) where {T} = T +#! NEW IGeometry +# mutable struct IGeom{T<:GType} <: AbstractGeometry +# ptr::GDAL.OGRGeometryH +# ownedby::Union{Nothing,DUAL_AbstractFeature} + +# function IGeom{T}( +# ptr::GDAL.OGRGeometryH = C_NULL, +# ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, +# ) where {T<:GType} +# igeom = new(ptr, ownedby) +# finalizer(destroy, igeom) +# return igeom +# end +# end + mutable struct ColorTable ptr::GDAL.GDALColorTableH end @@ -291,6 +510,55 @@ end OFTInteger64List::GDAL.OFTInteger64List, ) +# Default DataType = LAST, for duplicated (oftid, ofstid) values +const DataType_2_OGRFieldType_OGRFieldSubType_mapping = Base.ImmutableDict( + Bool => (OFTInteger, OFSTBoolean), + Int8 => (OFTInteger, OFSTNone), + Int16 => (OFTInteger, OFSTInt16), + Int32 => (OFTInteger, OFSTNone), # Default OFTInteger + Vector{Bool} => (OFTIntegerList, OFSTBoolean), + Vector{Int16} => (OFTIntegerList, OFSTInt16), + Vector{Int32} => (OFTIntegerList, OFSTNone), # Default OFTIntegerList + Float16 => (OFTReal, OFSTNone), + Float32 => (OFTReal, OFSTFloat32), + Float64 => (OFTReal, OFSTNone), # Default OFTReal + Vector{Float16} => (OFTRealList, OFSTNone), + Vector{Float32} => (OFTRealList, OFSTFloat32), + Vector{Float64} => (OFTRealList, OFSTNone), # Default OFTRealList + String => (OFTString, OFSTNone), + Vector{String} => (OFTStringList, OFSTNone), + Vector{UInt8} => (OFTBinary, OFSTNone), + Dates.Date => (OFTDate, OFSTNone), + Dates.Time => (OFTTime, OFSTNone), + Dates.DateTime => (OFTDateTime, OFSTNone), + Int64 => (OFTInteger64, OFSTNone), + Vector{Int64} => (OFTInteger64List, OFSTNone), +) + +# Conversions from DataType to OFType +const DataType2FType = Base.ImmutableDict( + ( + k => FType{v...} for + (k, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping + )..., +) +GDALDataTypes = Union{keys(DataType2FType)...} +@generated function convert(::Type{FType}, ::Type{T}) where {T<:GDALDataTypes} + result = get(DataType2FType, T, missing) + !ismissing(result) || throw(MethodError(convert, (FType, T))) + return :($(result)) +end +#! PROBABLY NOT NECESSARY: Conversions from OFType to DataType +# const FType2DataType = Base.ImmutableDict((v => k for (k, v) in DataType2FType)...) +# # GDALFTypes = Union{keys(FType2DataType)...} +# @generated function convert(::Type{DataType}, ::Type{T}) where T<:FType +# result = get(FType2DataType, T, missing) +# result !=== missing || error( +# "$T is not an FType corresponding to a valid GDAL (OGRFieldType, OGRFieldSubType) couple. \nPlease use one of the following: \n$(join((FType{v...} for (_, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping), "\n"))", +# ) +# return :($(result)) +# end + @convert( OGRFieldType::DataType, OFTInteger::Bool, From a146c793c61e591a812a70db91d4bf5d76fb4a11 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 26 Nov 2021 06:22:41 +0100 Subject: [PATCH 02/14] `NamedTuple` in `FDType` to avoid too many calls to `ogr_f_getfieldindex` --- src/dataset.jl | 2 +- src/ogr/feature.jl | 26 ++++++++++++++++++++++---- src/ogr/featuredefn.jl | 26 ++++++++++++++++++++++---- src/ogr/featurelayer.jl | 10 ++++++++++ src/types.jl | 30 ++++++++++++++++++++---------- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/dataset.jl b/src/dataset.jl index 8b19d9d7..181241db 100644 --- a/src/dataset.jl +++ b/src/dataset.jl @@ -518,7 +518,7 @@ getlayer(dataset::AbstractDataset, i::Integer)::IFeatureLayer = function getFDPlayer(dataset::AbstractDataset, i::Integer)::FDP_IFeatureLayer ptr::GDAL.OGRLayerH = GDAL.gdaldatasetgetlayer(dataset.ptr, i) fd_ptr = GDAL.ogr_l_getlayerdefn(ptr) - FD = getFDType(fd_ptr) + FD = _getFDType(fd_ptr) return FDP_IFeatureLayer{FD}(ptr, ownedby = dataset) end diff --git a/src/ogr/feature.jl b/src/ogr/feature.jl index c154be32..b4191cd9 100644 --- a/src/ogr/feature.jl +++ b/src/ogr/feature.jl @@ -108,7 +108,7 @@ function getfielddefn( fdp_feature::FDP_Feature{FD}, i::Integer, ) where {FD<:FDType} - return FTP_IFieldDefnView{_ftvec(FD)[i+1]}( + return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( GDAL.ogr_f_getfielddefnref(fdp_feature.ptr, i); ownedby = getfeaturedefn(fdp_feature), ) @@ -130,7 +130,7 @@ the field index, or `nothing` if no matching field is found. This is a cover for the `OGRFeatureDefn::GetFieldIndex()` method. """ function findfieldindex( - feature::DUAL_AbstractFeature, + feature::AbstractFeature, name::Union{AbstractString,Symbol}, )::Union{Integer,Nothing} i = GDAL.ogr_f_getfieldindex(feature.ptr, name) @@ -140,6 +140,15 @@ function findfieldindex( i end end +@generated function findfieldindex( + ::FDP_AbstractFeature{FD}, + name::Union{AbstractString,Symbol}, +) where {FD<:FDType} + return quote + i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end """ isfieldset(feature::Feature, i::Integer) @@ -478,7 +487,7 @@ const _FETCHFIELD = Dict{OGRFieldType,Function}( ) @generated function getfields_asfuncs(::Type{FD}) where {FD<:FDType} - return ((_FETCHFIELD[T.parameters[1]] for T in _ftvec(FD))...,) + return ((_FETCHFIELD[T.parameters[1]] for T in _fttypes(FD))...,) end """ @@ -720,11 +729,20 @@ the geometry field index, or -1 if no matching geometry field is found. This is a cover for the `OGRFeatureDefn::GetGeomFieldIndex()` method. """ function findgeomindex( - feature::DUAL_AbstractFeature, + feature::AbstractFeature, name::Union{AbstractString,Symbol} = "", )::Integer return GDAL.ogr_f_getgeomfieldindex(feature.ptr, name) end +@generated function findgeomindex( + ::FDP_AbstractFeature{FD}, + name::Union{AbstractString,Symbol} = "", +) where {FD<:FDType} + return return quote + i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end """ getgeom(feature::Feature, i::Integer) diff --git a/src/ogr/featuredefn.jl b/src/ogr/featuredefn.jl index 4df81018..86f14f11 100644 --- a/src/ogr/featuredefn.jl +++ b/src/ogr/featuredefn.jl @@ -110,7 +110,7 @@ function getfielddefn( fdp_featuredefn::FDP_FeatureDefn{FD}, i::Integer = 0, ) where {FD<:FDType} - return FTP_FieldDefn{_ftvec(FD)[i+1]}( + return FTP_FieldDefn{_fttypes(FD)[i+1]}( GDAL.ogr_fd_getfielddefn(fdp_featuredefn.ptr, i); ownedby = fdp_featuredefn, ) @@ -123,7 +123,7 @@ function getfielddefn( fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, i::Integer = 0, ) where {FD<:FDType} - return FTP_IFieldDefnView{_ftvec(FD)[i+1]}( + return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( GDAL.ogr_fd_getfielddefn(fdp_ifeaturedefnview.ptr, i); ownedby = fdp_ifeaturedefnview, ) @@ -147,6 +147,15 @@ function findfieldindex( )::Integer return GDAL.ogr_fd_getfieldindex(featuredefn.ptr, name) end +@generated function findfieldindex( + ::FDP_AbstractFeatureDefn{FD}, + name::Union{AbstractString,Symbol}, +) where {FD<:FDType} + return return quote + i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end """ addfielddefn!(featuredefn::FeatureDefn, fielddefn::FieldDefn) @@ -314,7 +323,7 @@ function getgeomdefn( fdp_featuredefn::FDP_FeatureDefn{FD}, i::Integer = 0, ) where {FD<:FDType} - return GFTP_GeomFieldDefn{_gtvec(FD)[i+1]}( + return GFTP_GeomFieldDefn{_gttypes(FD)[i+1]}( GDAL.ogr_fd_getgeomfielddefn(fdp_featuredefn.ptr, i); ownedby = fdp_featuredefn, ) @@ -327,7 +336,7 @@ function getgeomdefn( fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, i::Integer = 0, ) where {FD<:FDType} - return GFTP_IGeomFieldDefnView{_gtvec(FD)[i+1]}( + return GFTP_IGeomFieldDefnView{_gttypes(FD)[i+1]}( GDAL.ogr_fd_getgeomfielddefn(fdp_ifeaturedefnview.ptr, i); ownedby = fdp_ifeaturedefnview, ) @@ -350,6 +359,15 @@ function findgeomindex( )::Integer return GDAL.ogr_fd_getgeomfieldindex(featuredefn.ptr, name) end +@generated function findgeomindex( + ::FDP_AbstractFeatureDefn{FD}, + name::AbstractString = "", +) where {FD<:FDType} + return return quote + i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end """ addgeomdefn!(featuredefn::FeatureDefn, geomfielddefn::AbstractGeomFieldDefn) diff --git a/src/ogr/featurelayer.jl b/src/ogr/featurelayer.jl index c6cde280..26930974 100644 --- a/src/ogr/featurelayer.jl +++ b/src/ogr/featurelayer.jl @@ -590,6 +590,16 @@ function findfieldindex( )::Integer return GDAL.ogr_l_findfieldindex(layer.ptr, field, exactmatch) end +@generated function findfieldindex( + ::FDP_AbstractFeatureLayer{FD}, + field::Union{AbstractString,Symbol}, + #! Note that exactmatch::Bool is not used in GDAL except when OGRAPISPY_ENABLED is true => dropped +) where {FD<:FDType} + return return quote + i = findfirst(isequal(Symbol(field)), $(_ftnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end """ nfeature(layer::AbstractFeatureLayer, force::Bool = false) diff --git a/src/types.jl b/src/types.jl index e9fc3f78..a36a6453 100644 --- a/src/types.jl +++ b/src/types.jl @@ -29,29 +29,39 @@ end #! NEW simple FeatureDefn type, could later maybe(?) replaced by full FeatureDefn type in the definitions below FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter +FDType = Tuple{ + NamedTuple{NG,<:Tuple{Vararg{GType}}} where NG, + NamedTuple{NF,<:Tuple{Vararg{FType}}} where NF, +} @generated function _ngt(::Type{T}) where {T<:FDType} - return :(length($T.parameters[1].parameters)) + return :(length($T.types[1].types)) end -@generated function _gtvec(::Type{T}) where {T<:FDType} - return :(tuple($T.parameters[1].parameters...)) +@generated function _gtnames(::Type{T}) where {T<:FDType} + return :(tuple($T.types[1].names...)) +end +@generated function _gttypes(::Type{T}) where {T<:FDType} + return :(tuple($T.types[1].types...)) end @generated function _nft(::Type{T}) where {T<:FDType} - return :(length($T.parameters[2].parameters)) + return :(length($T.types[2].types)) +end +@generated function _ftnames(::Type{T}) where {T<:FDType} + return :(tuple($T.types[2].names...)) end -@generated function _ftvec(::Type{T}) where {T<:FDType} - return :(tuple($T.parameters[2].parameters...)) +@generated function _fttypes(::Type{T}) where {T<:FDType} + return :(tuple($T.types[2].types...)) end -function getFDType(ptr::GDAL.OGRFeatureDefnH) +function _getFDType(ptr::GDAL.OGRFeatureDefnH) #! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH (both Ptr{Cvoid})) and we cannot dispatch on it ng = GDAL.ogr_fd_getgeomfieldcount(ptr) gflddefn_ptrs = (GDAL.ogr_fd_getgeomfielddefn(ptr, i - 1) for i in 1:ng) + NG = tuple(Symbol.(GDAL.ogr_gfld_getnameref.(gflddefn_ptrs))...) TG = Tuple{(G for G in getGType.(gflddefn_ptrs))...} nf = GDAL.ogr_fd_getfieldcount(ptr) flddefn_ptrs = (GDAL.ogr_fd_getfielddefn(ptr, i - 1) for i in 1:nf) + NF = tuple(Symbol.(GDAL.ogr_fld_getnameref.(flddefn_ptrs))...) TF = Tuple{(F for F in getFType.(flddefn_ptrs))...} - return Tuple{TG,TF} + return Tuple{NamedTuple{NG,TG},NamedTuple{NF,TF}} end -#! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH (both Ptr{Cvoid})) and we cannot dispatch on it -# getFDType(ptr::GDAL.OGRLayerH) = getFDType(GDAL.ogr_l_getlayerdefn(ptr)) abstract type DUAL_AbstractFeatureDefn end #! NEW abstract type supertype of AbstractFeatureDefn and FDP_AbstractFeatureDefn abstract type AbstractFeatureDefn <: DUAL_AbstractFeatureDefn end From c42f1162274e3857885bc8ed601f617472181853 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Wed, 8 Dec 2021 08:05:35 +0100 Subject: [PATCH 03/14] New `convert` functions for `_infergeomtype` and specialized version of `Tables.eachcolumns` for ArchGDAL --- Project.toml | 2 + src/ArchGDAL.jl | 2 + src/constants.jl | 230 +++++++++++++++++++++++++------------ src/ogr/feature.jl | 43 ++++++- src/tables_columns.jl | 257 ++++++++++++++++++++++++++++++++++++++++++ src/types.jl | 223 +++++++++++++++++++----------------- 6 files changed, 576 insertions(+), 181 deletions(-) create mode 100644 src/tables_columns.jl diff --git a/Project.toml b/Project.toml index a14b062f..050895be 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" +GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" @@ -21,6 +22,7 @@ CEnum = "0.4" ColorTypes = "0.10, 0.11" DiskArrays = "0.2.4" GDAL = "1.1.3" +GeneralizedGenerated = "0.3" GeoFormatTypes = "0.3" GeoInterface = "0.4, 0.5" ImageCore = "0.8, 0.9" diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index 955f0b54..17252bf8 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -8,6 +8,7 @@ using Tables: Tables using ImageCore: Normed, N0f8, N0f16, N0f32, ImageCore using ColorTypes: ColorTypes using CEnum +using GeneralizedGenerated const GFT = GeoFormatTypes @@ -35,6 +36,7 @@ include("context.jl") include("base/iterators.jl") include("base/display.jl") include("tables.jl") +include("tables_columns.jl") include("geointerface.jl") include("convert.jl") diff --git a/src/constants.jl b/src/constants.jl index 278e6aa0..7d624073 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -313,80 +313,162 @@ convert(GDAL.OGRwkbGeometryType, ArchGDAL.wkbUnknown) wkbUnknown::OGRwkbGeometryType = 0x00000000 ``` """ -@enum( - OGRwkbGeometryType, - wkbUnknown = 0, - wkbPoint = 1, - wkbLineString = 2, - wkbPolygon = 3, - wkbMultiPoint = 4, - wkbMultiLineString = 5, - wkbMultiPolygon = 6, - wkbGeometryCollection = 7, - wkbCircularString = 8, - wkbCompoundCurve = 9, - wkbCurvePolygon = 10, - wkbMultiCurve = 11, - wkbMultiSurface = 12, - wkbCurve = 13, - wkbSurface = 14, - wkbPolyhedralSurface = 15, - wkbTIN = 16, - wkbTriangle = 17, - wkbNone = 18, - wkbLinearRing = 19, - wkbCircularStringZ = 20, - wkbCompoundCurveZ = 21, - wkbCurvePolygonZ = 22, - wkbMultiCurveZ = 23, - wkbMultiSurfaceZ = 24, - wkbCurveZ = 25, - wkbSurfaceZ = 26, - wkbPolyhedralSurfaceZ = 27, - wkbTINZ = 28, - wkbTriangleZ = 29, - wkbPointM = 30, - wkbLineStringM = 31, - wkbPolygonM = 32, - wkbMultiPointM = 33, - wkbMultiLineStringM = 34, - wkbMultiPolygonM = 35, - wkbGeometryCollectionM = 36, - wkbCircularStringM = 37, - wkbCompoundCurveM = 38, - wkbCurvePolygonM = 39, - wkbMultiCurveM = 40, - wkbMultiSurfaceM = 41, - wkbCurveM = 42, - wkbSurfaceM = 43, - wkbPolyhedralSurfaceM = 44, - wkbTINM = 45, - wkbTriangleM = 46, - wkbPointZM = 47, - wkbLineStringZM = 48, - wkbPolygonZM = 49, - wkbMultiPointZM = 50, - wkbMultiLineStringZM = 51, - wkbMultiPolygonZM = 52, - wkbGeometryCollectionZM = 53, - wkbCircularStringZM = 54, - wkbCompoundCurveZM = 55, - wkbCurvePolygonZM = 56, - wkbMultiCurveZM = 57, - wkbMultiSurfaceZM = 58, - wkbCurveZM = 59, - wkbSurfaceZM = 60, - wkbPolyhedralSurfaceZM = 61, - wkbTINZM = 62, - wkbTriangleZM = 63, - wkbPoint25D = 64, - wkbLineString25D = 65, - wkbPolygon25D = 66, - wkbMultiPoint25D = 67, - wkbMultiLineString25D = 68, - wkbMultiPolygon25D = 69, - wkbGeometryCollection25D = 70, -) +# @enum( +# OGRwkbGeometryType, +# wkbUnknown = 0, +# wkbPoint = 1, +# wkbLineString = 2, +# wkbPolygon = 3, +# wkbMultiPoint = 4, +# wkbMultiLineString = 5, +# wkbMultiPolygon = 6, +# wkbGeometryCollection = 7, +# wkbCircularString = 8, +# wkbCompoundCurve = 9, +# wkbCurvePolygon = 10, +# wkbMultiCurve = 11, +# wkbMultiSurface = 12, +# wkbCurve = 13, +# wkbSurface = 14, +# wkbPolyhedralSurface = 15, +# wkbTIN = 16, +# wkbTriangle = 17, +# wkbNone = 100, +# wkbLinearRing = 101, +# wkbCircularStringZ = 20, +# wkbCompoundCurveZ = 21, +# wkbCurvePolygonZ = 22, +# wkbMultiCurveZ = 23, +# wkbMultiSurfaceZ = 24, +# wkbCurveZ = 25, +# wkbSurfaceZ = 26, +# wkbPolyhedralSurfaceZ = 27, +# wkbTINZ = 28, +# wkbTriangleZ = 29, +# wkbPointM = 30, +# wkbLineStringM = 31, +# wkbPolygonM = 32, +# wkbMultiPointM = 33, +# wkbMultiLineStringM = 34, +# wkbMultiPolygonM = 35, +# wkbGeometryCollectionM = 36, +# wkbCircularStringM = 37, +# wkbCompoundCurveM = 38, +# wkbCurvePolygonM = 39, +# wkbMultiCurveM = 40, +# wkbMultiSurfaceM = 41, +# wkbCurveM = 42, +# wkbSurfaceM = 43, +# wkbPolyhedralSurfaceM = 44, +# wkbTINM = 45, +# wkbTriangleM = 46, +# wkbPointZM = 47, +# wkbLineStringZM = 48, +# wkbPolygonZM = 49, +# wkbMultiPointZM = 50, +# wkbMultiLineStringZM = 51, +# wkbMultiPolygonZM = 52, +# wkbGeometryCollectionZM = 53, +# wkbCircularStringZM = 54, +# wkbCompoundCurveZM = 55, +# wkbCurvePolygonZM = 56, +# wkbMultiCurveZM = 57, +# wkbMultiSurfaceZM = 58, +# wkbCurveZM = 59, +# wkbSurfaceZM = 60, +# wkbPolyhedralSurfaceZM = 61, +# wkbTINZM = 62, +# wkbTriangleZM = 63, +# wkbPoint25D = 64, +# wkbLineString25D = 65, +# wkbPolygon25D = 66, +# wkbMultiPoint25D = 67, +# wkbMultiLineString25D = 68, +# wkbMultiPolygon25D = 69, +# wkbGeometryCollection25D = 70, +# ) +@enum OGRwkbGeometryType::UInt32 begin + wkbUnknown = 0x00000000 + wkbPoint = 0x00000001 + wkbLineString = 0x00000002 + wkbPolygon = 0x00000003 + wkbMultiPoint = 0x00000004 + wkbMultiLineString = 0x00000005 + wkbMultiPolygon = 0x00000006 + wkbGeometryCollection = 0x00000007 + wkbCircularString = 0x00000008 + wkbCompoundCurve = 0x00000009 + wkbCurvePolygon = 0x0000000a + wkbMultiCurve = 0x0000000b + wkbMultiSurface = 0x0000000c + wkbCurve = 0x0000000d + wkbSurface = 0x0000000e + wkbPolyhedralSurface = 0x0000000f + wkbTIN = 0x00000010 + wkbTriangle = 0x00000011 + wkbNone = 0x00000064 + wkbLinearRing = 0x00000065 + wkbCircularStringZ = 0x000003f0 + wkbCompoundCurveZ = 0x000003f1 + wkbCurvePolygonZ = 0x000003f2 + wkbMultiCurveZ = 0x000003f3 + wkbMultiSurfaceZ = 0x000003f4 + wkbCurveZ = 0x000003f5 + wkbSurfaceZ = 0x000003f6 + wkbPolyhedralSurfaceZ = 0x000003f7 + wkbTINZ = 0x000003f8 + wkbTriangleZ = 0x000003f9 + wkbPointM = 0x000007d1 + wkbLineStringM = 0x000007d2 + wkbPolygonM = 0x000007d3 + wkbMultiPointM = 0x000007d4 + wkbMultiLineStringM = 0x000007d5 + wkbMultiPolygonM = 0x000007d6 + wkbGeometryCollectionM = 0x000007d7 + wkbCircularStringM = 0x000007d8 + wkbCompoundCurveM = 0x000007d9 + wkbCurvePolygonM = 0x000007da + wkbMultiCurveM = 0x000007db + wkbMultiSurfaceM = 0x000007dc + wkbCurveM = 0x000007dd + wkbSurfaceM = 0x000007de + wkbPolyhedralSurfaceM = 0x000007df + wkbTINM = 0x000007e0 + wkbTriangleM = 0x000007e1 + wkbPointZM = 0x00000bb9 + wkbLineStringZM = 0x00000bba + wkbPolygonZM = 0x00000bbb + wkbMultiPointZM = 0x00000bbc + wkbMultiLineStringZM = 0x00000bbd + wkbMultiPolygonZM = 0x00000bbe + wkbGeometryCollectionZM = 0x00000bbf + wkbCircularStringZM = 0x00000bc0 + wkbCompoundCurveZM = 0x00000bc1 + wkbCurvePolygonZM = 0x00000bc2 + wkbMultiCurveZM = 0x00000bc3 + wkbMultiSurfaceZM = 0x00000bc4 + wkbCurveZM = 0x00000bc5 + wkbSurfaceZM = 0x00000bc6 + wkbPolyhedralSurfaceZM = 0x00000bc7 + wkbTINZM = 0x00000bc8 + wkbTriangleZM = 0x00000bc9 + wkbPoint25D = 0x80000001 + wkbLineString25D = 0x80000002 + wkbPolygon25D = 0x80000003 + wkbMultiPoint25D = 0x80000004 + wkbMultiLineString25D = 0x80000005 + wkbMultiPolygon25D = 0x80000006 + wkbGeometryCollection25D = 0x80000007 +end +@assert begin + all( + string.(instances(OGRwkbGeometryType)) .== + string.(instances(GDAL.OGRwkbGeometryType)), + ) && all( + Integer.(instances(OGRwkbGeometryType)) .== + Integer.(instances(GDAL.OGRwkbGeometryType)), + ) +end """ The value of `OGRwkbByteOrder` could be different from `GDAL.OGRwkbByteOrder`. diff --git a/src/ogr/feature.jl b/src/ogr/feature.jl index b4191cd9..cefc5fc5 100644 --- a/src/ogr/feature.jl +++ b/src/ogr/feature.jl @@ -486,7 +486,7 @@ const _FETCHFIELD = Dict{OGRFieldType,Function}( OFTInteger64List => asint64list, ) -@generated function getfields_asfuncs(::Type{FD}) where {FD<:FDType} +@generated function _get_fields_asfuncs(::Type{FD}) where {FD<:FDType} return ((_FETCHFIELD[T.parameters[1]] for T in _fttypes(FD))...,) end @@ -532,18 +532,49 @@ end elseif isfieldnull(fdp_feature, i) missing else - $(getfields_asfuncs(FD))[i+1](fdp_feature, i) + $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) end end end -function getfield( - feature::DUAL_AbstractFeature, - name::Union{AbstractString,Symbol}, -) +@generated function getfield( + fdp_feature::FDP_AbstractFeature{FD}, + i::Integer, + ::Val{K}, +) where {FD<:FDType,K} + return quote + return if !isfieldset(fdp_feature, i) + nothing + elseif isfieldnull(fdp_feature, i) + missing + else + $(_get_fields_asfuncs(FD)[K])(fdp_feature, i) + end + end +end + +function getfield(feature::Feature, name::Union{AbstractString,Symbol}) return getfield(feature, findfieldindex(feature, name)) end +@generated function getfield( + fdp_feature::FDP_AbstractFeature{FD}, + name::Union{AbstractString,Symbol}, +) where {FD<:FDType} + return quote + i = findfieldindex(fdp_feature, name) + return if i === nothing + missing + elseif !isfieldset(fdp_feature, i) + nothing + elseif isfieldnull(fdp_feature, i) + missing + else + @inbounds $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) + end + end +end + """ setfield!(feature::Feature, i::Integer, value) setfield!(feature::Feature, i::Integer, value::DateTime, tzflag::Int = 0) diff --git a/src/tables_columns.jl b/src/tables_columns.jl new file mode 100644 index 00000000..09e426ce --- /dev/null +++ b/src/tables_columns.jl @@ -0,0 +1,257 @@ +#----- Tables.jl functions specialization for ArchGDAL FDP_AbstractLayer -----# + +@generated function _get_getgeom_funcs(::Type{FD}) where {FD<:FDType} + NG = _ngt(FD) + f0 = mk_function( + ArchGDAL, + :( + function (row) + return (geomptr = GDAL.ogr_f_stealgeometry(row.ptr)) == C_NULL ? missing : IGeometry(geomptr) + end + ), + ) + return NG == 1 ? (f0,) : + ( + f0, + ( + mk_function( + ArchGDAL, + :( + function (row) + return ( + geomptr = + GDAL.ogr_f_getgeomfieldref(feature.ptr, $k) + ) == C_NULL ? missing : IGeometry(geomptr) + end + ), + ) for k in 2:NG + )..., + ) +end +@generated function _get_getfield_funcs(::Type{FD}) where {FD<:FDType} + NF = _nft(FD) + fafs = _get_fields_asfuncs(FD) + return ( + ( + mk_function( + ArchGDAL, + :( + function (row) + return !(isfieldset(row, $(k - 1))) ? nothing : + isfieldnull(row, $(k - 1)) ? missing : + $(fafs[k])(row, $(k - 1)) + end + ), + ) for k in 1:NF + )..., + ) +end + +const SPECIALIZATION_THRESHOLD = 100 + +#TODO: Investigation how to make use of types for possible speedup and if not +#TODO: merge the two versions of eachcolumns +@inline @generated function Tables.eachcolumns( + f::F, + sch::Tables.Schema{names,types}, + row::FDP_AbstractFeature{FD}, + columns::S, + args..., +) where {F,names,types,FD<:FDType,S} + ng = _ngt(FD) + nf = _nft(FD) + ggfs = _get_getgeom_funcs(FD) + gffs = _get_getfield_funcs(FD) + rex = Expr(:block) + for i in 1:ng + push!( + rex.args, + :(f($(ggfs[i])(row), $i, names[$i], columns[$i], args...)), + ) + end + for i in 1:nf + push!( + rex.args, + :(f( + $(gffs[i])(row), + $(i + ng), + names[$(i + ng)], + columns[$(i + ng)], + args..., + )), + ) + end + return rex +end + +@inline @generated function eachcolumns( + f::F, + sch::Tables.Schema{names,nothing}, + row::FDP_AbstractFeature{FD}, + columns::S, + args..., +) where {F,names,FD<:FDType,S} + ng = _ngt(FD) + nf = _nft(FD) + ggfs = _get_getgeom_funcs(FD) + gffs = _get_getfield_funcs(FD) + rex = Expr(:block) + for i in 1:ng + push!( + rex.args, + :(f($(ggfs[i])(row), $i, names[$i], columns[$i], args...)), + ) + end + for i in 1:nf + push!( + rex.args, + :(f( + $(gffs[i])(row), + $(i + ng), + names[$(i + ng)], + columns[$(i + ng)], + args..., + )), + ) + end + return rex +end + +allocatecolumn(T, len) = Array{T,1}(undef, len) + +@inline function _allocatecolumns( + ::Tables.Schema{names,types}, + len, +) where {names,types} + if @generated + vals = Tuple( + :(allocatecolumn($(fieldtype(types, i)), len)) for + i in 1:fieldcount(types) + ) + return :(NamedTuple{$(map(Symbol, names))}(($(vals...),))) + else + return NamedTuple{map(Symbol, names)}( + Tuple( + allocatecolumn(fieldtype(types, i), len) for + i in 1:fieldcount(types) + ), + ) + end +end + +@inline function allocatecolumns( + sch::Tables.Schema{names,types}, + len, +) where {names,types} + if fieldcount(types) <= SPECIALIZATION_THRESHOLD + return _allocatecolumns(sch, len) + else + return NamedTuple{map(Symbol, names)}( + Tuple( + allocatecolumn(fieldtype(types, i), len) for + i in 1:fieldcount(types) + ), + ) + end +end + +@inline function add!( + dest::AbstractArray, + val, + ::Union{Base.HasLength,Base.HasShape}, + row, +) + return setindex!(dest, val, row) +end +@inline add!(dest::AbstractArray, val, T, row) = push!(dest, val) + +replacex(t, col::Int, x) = ntuple(i -> i == col ? x : t[i], length(t)) + +@inline function add_or_widen!( + val, + col::Int, + nm, + dest::AbstractArray{T}, + fdp_feature, + updated, + L, +) where {T} + if val isa T || promote_type(typeof(val), T) <: T + add!(dest, val, L, fdp_feature) + return + else + new = allocatecolumn(promote_type(T, typeof(val)), length(dest)) + fdp_feature > 1 && copyto!(new, 1, dest, 1, fdp_feature - 1) + add!(new, val, L, fdp_feature) + updated[] = replacex(updated[], col, new) + return + end +end + +function __buildcolumns(fdp_layer, st, sch, columns, rownbr, updated) + while true + state = iterate(fdp_layer, st) + state === nothing && break + row, st = state + rownbr += 1 + eachcolumns( + add_or_widen!, + sch, + row, + columns, + rownbr, + updated, + Base.IteratorSize(fdp_layer), + ) + columns !== updated[] && return __buildcolumns( + fdp_layer, + st, + sch, + updated[], + rownbr, + updated, + ) + end + return updated +end + +struct EmptyVector <: AbstractVector{Union{}} + len::Int +end +Base.IndexStyle(::Type{EmptyVector}) = Base.IndexLinear() +Base.size(x::EmptyVector) = (x.len,) +Base.getindex(x::EmptyVector, i::Int) = throw(UndefRefError()) + +function _buildcolumns(fdp_layer, fdp_feature, st, sch, columns, updated) + eachcolumns( + add_or_widen!, + sch, + fdp_feature, + columns, + 1, + updated, + Base.IteratorSize(fdp_layer), + ) + return __buildcolumns(fdp_layer, st, sch, updated[], 1, updated) +end + +@inline function Tables.columns( + fdp_layer::T, +) where {FD<:FDType,T<:FDP_AbstractFeatureLayer{FD}} + state = iterate(fdp_layer) + fdp_feature, st = state + names = Tuple(Tables.columnnames(fdp_feature)) + len = Base.haslength(T) ? length(rowitr) : 0 + sch = Tables.Schema(names, nothing) + columns = Tuple(EmptyVector(len) for _ in 1:length(names)) + return NamedTuple{map(Symbol, names)}( + _buildcolumns( + fdp_layer, + fdp_feature, + st, + sch, + columns, + Ref{Any}(columns), + )[], + ) +end diff --git a/src/types.jl b/src/types.jl index a36a6453..b483f643 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,9 +1,6 @@ import DiskArrays: AbstractDiskArray import Base.convert -abstract type AbstractGeometry <: GeoInterface.AbstractGeometry end -# needs to have a `ptr::GDAL.OGRGeometryH` attribute - abstract type AbstractSpatialRef end # needs to have a `ptr::GDAL.OGRSpatialReferenceH` attribute @@ -63,6 +60,11 @@ function _getFDType(ptr::GDAL.OGRFeatureDefnH) #! There no type difference betwe return Tuple{NamedTuple{NG,TG},NamedTuple{NF,TF}} end +abstract type DUAL_AbstractGeometry <: GeoInterface.AbstractGeometry end #! NEW abstract type supertype of AbstractGeometry and GP_AbstractGeometry +abstract type AbstractGeometry <: DUAL_AbstractGeometry end +abstract type GP_AbstractGeometry{G<:GType} <: DUAL_AbstractGeometry end #! NEW abstract type to group GP_Geometry instances +# needs to have a `ptr::GDAL.OGRGeometryH` attribute + abstract type DUAL_AbstractFeatureDefn end #! NEW abstract type supertype of AbstractFeatureDefn and FDP_AbstractFeatureDefn abstract type AbstractFeatureDefn <: DUAL_AbstractFeatureDefn end abstract type FDP_AbstractFeatureDefn{FD<:FDType} <: DUAL_AbstractFeatureDefn end #! NEW abstract type to group FDP_FeatureDefn type instances @@ -421,18 +423,26 @@ mutable struct Geometry{OGRwkbGeometryType} <: AbstractGeometry end _geomtype(::Geometry{T}) where {T} = T +function _inferGType(ptr::GDAL.OGRGeometryH = C_NULL)::Type{<:GType} + return if ptr != C_NULL + GType{OGRwkbGeometryType(Int32(GDAL.ogr_g_getgeometrytype(ptr)))} + else + GType{wkbUnknown} + end +end + #! NEW Geometry -# mutable struct Geom{T<:GType} <: AbstractGeometry -# ptr::GDAL.OGRGeometryH -# ownedby::Union{Nothing,DUAL_AbstractFeature} - -# function Geom{T}( -# ptr::GDAL.OGRGeometryH = C_NULL, -# ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, -# ) where {T<:GType} -# return new(ptr, ownedby) -# end -# end +mutable struct GP_Geometry{G} <: GP_AbstractGeometry{G} + ptr::GDAL.OGRGeometryH + ownedby::Union{Nothing,DUAL_AbstractFeature} + + function GP_Geometry{G}( + ptr::GDAL.OGRGeometryH = C_NULL, + ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, + ) where {G<:GType} + return new{_inferGType(ptr)}(ptr, ownedby) + end +end mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry ptr::GDAL.OGRGeometryH @@ -446,19 +456,19 @@ end _geomtype(::IGeometry{T}) where {T} = T #! NEW IGeometry -# mutable struct IGeom{T<:GType} <: AbstractGeometry -# ptr::GDAL.OGRGeometryH -# ownedby::Union{Nothing,DUAL_AbstractFeature} - -# function IGeom{T}( -# ptr::GDAL.OGRGeometryH = C_NULL, -# ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, -# ) where {T<:GType} -# igeom = new(ptr, ownedby) -# finalizer(destroy, igeom) -# return igeom -# end -# end +mutable struct GP_IGeometry{G} <: GP_AbstractGeometry{G} + ptr::GDAL.OGRGeometryH + ownedby::Union{Nothing,DUAL_AbstractFeature} + + function GP_IGeometry{G}( + ptr::GDAL.OGRGeometryH = C_NULL, + ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, + ) where {G<:GType} + gp_igeometry = new{_inferGType(ptr)}(ptr, ownedby) + finalizer(destroy, gp_igeometry) + return gp_igeometry + end +end mutable struct ColorTable ptr::GDAL.GDALColorTableH @@ -713,80 +723,91 @@ end OGRSTUInches::GDAL.OGRSTUInches, ) -@convert( - OGRwkbGeometryType::GDAL.OGRwkbGeometryType, - wkbUnknown::GDAL.wkbUnknown, - wkbPoint::GDAL.wkbPoint, - wkbLineString::GDAL.wkbLineString, - wkbPolygon::GDAL.wkbPolygon, - wkbMultiPoint::GDAL.wkbMultiPoint, - wkbMultiLineString::GDAL.wkbMultiLineString, - wkbMultiPolygon::GDAL.wkbMultiPolygon, - wkbGeometryCollection::GDAL.wkbGeometryCollection, - wkbCircularString::GDAL.wkbCircularString, - wkbCompoundCurve::GDAL.wkbCompoundCurve, - wkbCurvePolygon::GDAL.wkbCurvePolygon, - wkbMultiCurve::GDAL.wkbMultiCurve, - wkbMultiSurface::GDAL.wkbMultiSurface, - wkbCurve::GDAL.wkbCurve, - wkbSurface::GDAL.wkbSurface, - wkbPolyhedralSurface::GDAL.wkbPolyhedralSurface, - wkbTIN::GDAL.wkbTIN, - wkbTriangle::GDAL.wkbTriangle, - wkbNone::GDAL.wkbNone, - wkbLinearRing::GDAL.wkbLinearRing, - wkbCircularStringZ::GDAL.wkbCircularStringZ, - wkbCompoundCurveZ::GDAL.wkbCompoundCurveZ, - wkbCurvePolygonZ::GDAL.wkbCurvePolygonZ, - wkbMultiCurveZ::GDAL.wkbMultiCurveZ, - wkbMultiSurfaceZ::GDAL.wkbMultiSurfaceZ, - wkbCurveZ::GDAL.wkbCurveZ, - wkbSurfaceZ::GDAL.wkbSurfaceZ, - wkbPolyhedralSurfaceZ::GDAL.wkbPolyhedralSurfaceZ, - wkbTINZ::GDAL.wkbTINZ, - wkbTriangleZ::GDAL.wkbTriangleZ, - wkbPointM::GDAL.wkbPointM, - wkbLineStringM::GDAL.wkbLineStringM, - wkbPolygonM::GDAL.wkbPolygonM, - wkbMultiPointM::GDAL.wkbMultiPointM, - wkbMultiLineStringM::GDAL.wkbMultiLineStringM, - wkbMultiPolygonM::GDAL.wkbMultiPolygonM, - wkbGeometryCollectionM::GDAL.wkbGeometryCollectionM, - wkbCircularStringM::GDAL.wkbCircularStringM, - wkbCompoundCurveM::GDAL.wkbCompoundCurveM, - wkbCurvePolygonM::GDAL.wkbCurvePolygonM, - wkbMultiCurveM::GDAL.wkbMultiCurveM, - wkbMultiSurfaceM::GDAL.wkbMultiSurfaceM, - wkbCurveM::GDAL.wkbCurveM, - wkbSurfaceM::GDAL.wkbSurfaceM, - wkbPolyhedralSurfaceM::GDAL.wkbPolyhedralSurfaceM, - wkbTINM::GDAL.wkbTINM, - wkbTriangleM::GDAL.wkbTriangleM, - wkbPointZM::GDAL.wkbPointZM, - wkbLineStringZM::GDAL.wkbLineStringZM, - wkbPolygonZM::GDAL.wkbPolygonZM, - wkbMultiPointZM::GDAL.wkbMultiPointZM, - wkbMultiLineStringZM::GDAL.wkbMultiLineStringZM, - wkbMultiPolygonZM::GDAL.wkbMultiPolygonZM, - wkbGeometryCollectionZM::GDAL.wkbGeometryCollectionZM, - wkbCircularStringZM::GDAL.wkbCircularStringZM, - wkbCompoundCurveZM::GDAL.wkbCompoundCurveZM, - wkbCurvePolygonZM::GDAL.wkbCurvePolygonZM, - wkbMultiCurveZM::GDAL.wkbMultiCurveZM, - wkbMultiSurfaceZM::GDAL.wkbMultiSurfaceZM, - wkbCurveZM::GDAL.wkbCurveZM, - wkbSurfaceZM::GDAL.wkbSurfaceZM, - wkbPolyhedralSurfaceZM::GDAL.wkbPolyhedralSurfaceZM, - wkbTINZM::GDAL.wkbTINZM, - wkbTriangleZM::GDAL.wkbTriangleZM, - wkbPoint25D::GDAL.wkbPoint25D, - wkbLineString25D::GDAL.wkbLineString25D, - wkbPolygon25D::GDAL.wkbPolygon25D, - wkbMultiPoint25D::GDAL.wkbMultiPoint25D, - wkbMultiLineString25D::GDAL.wkbMultiLineString25D, - wkbMultiPolygon25D::GDAL.wkbMultiPolygon25D, - wkbGeometryCollection25D::GDAL.wkbGeometryCollection25D, -) +# @convert( +# OGRwkbGeometryType::GDAL.OGRwkbGeometryType, +# wkbUnknown::GDAL.wkbUnknown, +# wkbPoint::GDAL.wkbPoint, +# wkbLineString::GDAL.wkbLineString, +# wkbPolygon::GDAL.wkbPolygon, +# wkbMultiPoint::GDAL.wkbMultiPoint, +# wkbMultiLineString::GDAL.wkbMultiLineString, +# wkbMultiPolygon::GDAL.wkbMultiPolygon, +# wkbGeometryCollection::GDAL.wkbGeometryCollection, +# wkbCircularString::GDAL.wkbCircularString, +# wkbCompoundCurve::GDAL.wkbCompoundCurve, +# wkbCurvePolygon::GDAL.wkbCurvePolygon, +# wkbMultiCurve::GDAL.wkbMultiCurve, +# wkbMultiSurface::GDAL.wkbMultiSurface, +# wkbCurve::GDAL.wkbCurve, +# wkbSurface::GDAL.wkbSurface, +# wkbPolyhedralSurface::GDAL.wkbPolyhedralSurface, +# wkbTIN::GDAL.wkbTIN, +# wkbTriangle::GDAL.wkbTriangle, +# wkbNone::GDAL.wkbNone, +# wkbLinearRing::GDAL.wkbLinearRing, +# wkbCircularStringZ::GDAL.wkbCircularStringZ, +# wkbCompoundCurveZ::GDAL.wkbCompoundCurveZ, +# wkbCurvePolygonZ::GDAL.wkbCurvePolygonZ, +# wkbMultiCurveZ::GDAL.wkbMultiCurveZ, +# wkbMultiSurfaceZ::GDAL.wkbMultiSurfaceZ, +# wkbCurveZ::GDAL.wkbCurveZ, +# wkbSurfaceZ::GDAL.wkbSurfaceZ, +# wkbPolyhedralSurfaceZ::GDAL.wkbPolyhedralSurfaceZ, +# wkbTINZ::GDAL.wkbTINZ, +# wkbTriangleZ::GDAL.wkbTriangleZ, +# wkbPointM::GDAL.wkbPointM, +# wkbLineStringM::GDAL.wkbLineStringM, +# wkbPolygonM::GDAL.wkbPolygonM, +# wkbMultiPointM::GDAL.wkbMultiPointM, +# wkbMultiLineStringM::GDAL.wkbMultiLineStringM, +# wkbMultiPolygonM::GDAL.wkbMultiPolygonM, +# wkbGeometryCollectionM::GDAL.wkbGeometryCollectionM, +# wkbCircularStringM::GDAL.wkbCircularStringM, +# wkbCompoundCurveM::GDAL.wkbCompoundCurveM, +# wkbCurvePolygonM::GDAL.wkbCurvePolygonM, +# wkbMultiCurveM::GDAL.wkbMultiCurveM, +# wkbMultiSurfaceM::GDAL.wkbMultiSurfaceM, +# wkbCurveM::GDAL.wkbCurveM, +# wkbSurfaceM::GDAL.wkbSurfaceM, +# wkbPolyhedralSurfaceM::GDAL.wkbPolyhedralSurfaceM, +# wkbTINM::GDAL.wkbTINM, +# wkbTriangleM::GDAL.wkbTriangleM, +# wkbPointZM::GDAL.wkbPointZM, +# wkbLineStringZM::GDAL.wkbLineStringZM, +# wkbPolygonZM::GDAL.wkbPolygonZM, +# wkbMultiPointZM::GDAL.wkbMultiPointZM, +# wkbMultiLineStringZM::GDAL.wkbMultiLineStringZM, +# wkbMultiPolygonZM::GDAL.wkbMultiPolygonZM, +# wkbGeometryCollectionZM::GDAL.wkbGeometryCollectionZM, +# wkbCircularStringZM::GDAL.wkbCircularStringZM, +# wkbCompoundCurveZM::GDAL.wkbCompoundCurveZM, +# wkbCurvePolygonZM::GDAL.wkbCurvePolygonZM, +# wkbMultiCurveZM::GDAL.wkbMultiCurveZM, +# wkbMultiSurfaceZM::GDAL.wkbMultiSurfaceZM, +# wkbCurveZM::GDAL.wkbCurveZM, +# wkbSurfaceZM::GDAL.wkbSurfaceZM, +# wkbPolyhedralSurfaceZM::GDAL.wkbPolyhedralSurfaceZM, +# wkbTINZM::GDAL.wkbTINZM, +# wkbTriangleZM::GDAL.wkbTriangleZM, +# wkbPoint25D::GDAL.wkbPoint25D, +# wkbLineString25D::GDAL.wkbLineString25D, +# wkbPolygon25D::GDAL.wkbPolygon25D, +# wkbMultiPoint25D::GDAL.wkbMultiPoint25D, +# wkbMultiLineString25D::GDAL.wkbMultiLineString25D, +# wkbMultiPolygon25D::GDAL.wkbMultiPolygon25D, +# wkbGeometryCollection25D::GDAL.wkbGeometryCollection25D, +# ) + +# Conversions below assume that both +# - OGRwkbGeometryType Enum instances and +# - GDAL.OGRwkbGeometryType CEnum.Cenum instances +# have same Integer assigned values +function convert(::Type{OGRwkbGeometryType}, gogtinst::GDAL.OGRwkbGeometryType) + return OGRwkbGeometryType(Integer(gogtinst)) +end +function convert(::Type{GDAL.OGRwkbGeometryType}, ogtinst::OGRwkbGeometryType) + return GDAL.OGRwkbGeometryType(Integer(ogtinst)) +end function basetype(gt::OGRwkbGeometryType)::OGRwkbGeometryType wkbGeomType = convert(GDAL.OGRwkbGeometryType, gt) From b7b6cdd4d778d6af72ad548caa964344dfa77e81 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Thu, 16 Dec 2021 08:42:09 +0100 Subject: [PATCH 04/14] Implemented new `Tables.columns` functions for `FDP_AbstractFeatureLayer` and `AbstractFeatureLayer` --- Project.toml | 2 + src/ArchGDAL.jl | 2 + src/tables.jl | 50 +++-- src/tables_columns.jl | 469 ++++++++++++++++++++++++++---------------- src/types.jl | 66 +++++- 5 files changed, 390 insertions(+), 199 deletions(-) diff --git a/Project.toml b/Project.toml index 050895be..ecab279a 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" +RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] @@ -26,5 +27,6 @@ GeneralizedGenerated = "0.3" GeoFormatTypes = "0.3" GeoInterface = "0.4, 0.5" ImageCore = "0.8, 0.9" +RuntimeGeneratedFunctions = "0.5" Tables = "1" julia = "1.3" diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index 17252bf8..4220493f 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -9,6 +9,8 @@ using ImageCore: Normed, N0f8, N0f16, N0f32, ImageCore using ColorTypes: ColorTypes using CEnum using GeneralizedGenerated +using RuntimeGeneratedFunctions +RuntimeGeneratedFunctions.init(@__MODULE__) const GFT = GeoFormatTypes diff --git a/src/tables.jl b/src/tables.jl index 6654b26f..cbf69959 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -1,5 +1,29 @@ -function Tables.schema(::DUAL_AbstractFeatureLayer)::Nothing - return nothing +# function Tables.schema(::AbstractFeatureLayer)::Nothing +# return nothing +# end + +function Tables.schema(layer::AbstractFeatureLayer) + geom_names, field_names, featuredefn, fielddefns = + schema_names(layerdefn(layer)) + ngeom = ArchGDAL.ngeom(featuredefn) + geom_types = + (IGeometry{gettype(getgeomdefn(featuredefn, i))} for i in 0:ngeom-1) + field_types = + (convert(DataType, gettype(fielddefn)) for fielddefn in fielddefns) + return Tables.Schema( + (geom_names..., field_names...), + (geom_types..., field_types...), + ) +end + +@generated function Tables.schema( + ::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + gnames = _gtnames(FD) + fnames = _ftnames(FD) + gtypes = (convert(IGeometry, gt) for gt in _gttypes(FD)) + ftypes = (get(FType2DataType, ft, missing) for ft in _fttypes(FD)) + return Tables.Schema((gnames..., fnames...), (gtypes..., ftypes...)) end Tables.istable(::Type{<:DUAL_AbstractFeatureLayer})::Bool = true @@ -10,23 +34,25 @@ function Tables.rows(layer::T)::T where {T<:DUAL_AbstractFeatureLayer} end function Tables.getcolumn(row::AbstractFeature, i::Int) - if i > nfield(row) - return stealgeom(row, i - nfield(row) - 1) - elseif i > 0 - return getfield(row, i - 1) + ng = ngeom(row) + return if i <= ng + geom = stealgeom(row, i - 1) + geom.ptr != C_NULL ? geom : missing else - return missing + getfield(row, i - ng - 1) end end function Tables.getcolumn( row::FDP_AbstractFeature{FD}, - i::T, -) where {FD<:FDType,T<:Integer} - if i > nfield(row) - return stealgeom(row, i - nfield(row) - 1) + i::Int, +) where {FD<:FDType} + ng = ngeom(row) + return if i <= ng + geom = stealgeom(row, i - 1) + geom.ptr != C_NULL ? geom : missing else - return getfield(row, i - 1) + getfield(row, i - ng - 1) end end diff --git a/src/tables_columns.jl b/src/tables_columns.jl index 09e426ce..099ae594 100644 --- a/src/tables_columns.jl +++ b/src/tables_columns.jl @@ -1,6 +1,13 @@ -#----- Tables.jl functions specialization for ArchGDAL FDP_AbstractLayer -----# +################################### +# Trial with GeneralizedGenerated # +################################### +# - Runtime generated functions for: +# - geom columns: _get_getgeom_funcs_gg +# - field columns: _get_getfield_funcs_gg +# - Feature to columns line function: FDPf2c_gg +# - (Tables.)columns function: FDPcolumns_gg -@generated function _get_getgeom_funcs(::Type{FD}) where {FD<:FDType} +function _get_getgeom_funcs_gg(::Type{FD}) where {FD<:FDType} NG = _ngt(FD) f0 = mk_function( ArchGDAL, @@ -28,7 +35,8 @@ )..., ) end -@generated function _get_getfield_funcs(::Type{FD}) where {FD<:FDType} + +function _get_getfield_funcs_gg(::Type{FD}) where {FD<:FDType} NF = _nft(FD) fafs = _get_fields_asfuncs(FD) return ( @@ -47,211 +55,320 @@ end ) end -const SPECIALIZATION_THRESHOLD = 100 - -#TODO: Investigation how to make use of types for possible speedup and if not -#TODO: merge the two versions of eachcolumns -@inline @generated function Tables.eachcolumns( - f::F, - sch::Tables.Schema{names,types}, - row::FDP_AbstractFeature{FD}, - columns::S, - args..., -) where {F,names,types,FD<:FDType,S} +@generated function FDPf2c_gg( + fdp_feature::FDP_AbstractFeature{FD}, + i::Int, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} ng = _ngt(FD) nf = _nft(FD) - ggfs = _get_getgeom_funcs(FD) - gffs = _get_getfield_funcs(FD) + ggfs = _get_getgeom_funcs_gg(FD) + gffs = _get_getfield_funcs_gg(FD) rex = Expr(:block) - for i in 1:ng - push!( - rex.args, - :(f($(ggfs[i])(row), $i, names[$i], columns[$i], args...)), - ) + push!(rex.args, Expr(:inbounds, true)) + for j in 1:ng + push!(rex.args, :(cols[$j][i] = $(ggfs[j])(fdp_feature))) end - for i in 1:nf - push!( - rex.args, - :(f( - $(gffs[i])(row), - $(i + ng), - names[$(i + ng)], - columns[$(i + ng)], - args..., - )), - ) + for j in ng+1:ng+nf + push!(rex.args, :(cols[$j][i] = $(gffs[j-ng])(fdp_feature))) end + push!(rex.args, Expr(:inbounds, :pop)) + push!(rex.args, :(nothing)) return rex end -@inline @generated function eachcolumns( - f::F, - sch::Tables.Schema{names,nothing}, - row::FDP_AbstractFeature{FD}, - columns::S, - args..., -) where {F,names,FD<:FDType,S} +function FDPfillcolumns_gg!( + fdp_layer::FDP_AbstractFeatureLayer{FD}, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + state = 0 + while true + next = iterate(fdp_layer, state) + next === nothing && break + fdp_feature, state = next + FDPf2c_gg(fdp_feature, state, cols) + end +end + +function FDPcolumns_gg( + fdp_layer::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + len = length(fdp_layer) + gdal_sch = Tables.schema(fdp_layer) + ng = _ngt(FD) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + FDPfillcolumns_gg!(fdp_layer, cols) + return NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names)}([ + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ]), + ) +end + +####################################### +# Trial with RuntimeGeneratedFunction # +####################################### +# - Runtime generated functions for: +# - geom columns: _get_getgeom_funcs_rgf +# - field columns: _get_getfield_funcs_rgf +# - Feature to columns line function: FDPf2c_rgf +# - (Tables.)columns function: FDPcolumns_rgf + +function _get_getgeom_funcs_rgf(::Type{FD}) where {FD<:FDType} + NG = _ngt(FD) + f0 = @RuntimeGeneratedFunction( + ArchGDAL, + :( + function (row) + return (geomptr = GDAL.ogr_f_stealgeometry(row.ptr)) == C_NULL ? missing : IGeometry(geomptr) + end + ), + ) + return NG == 1 ? (f0,) : + ( + f0, + ( + @RuntimeGeneratedFunction( + ArchGDAL, + :( + function (row) + return ( + geomptr = + GDAL.ogr_f_getgeomfieldref(feature.ptr, $k) + ) == C_NULL ? missing : IGeometry(geomptr) + end + ), + ) for k in 2:NG + )..., + ) +end + +function _get_getfield_funcs_rgf(::Type{FD}) where {FD<:FDType} + NF = _nft(FD) + fafs = _get_fields_asfuncs(FD) + return ( + ( + @RuntimeGeneratedFunction( + ArchGDAL, + :( + function (row) + return !(isfieldset(row, $(k - 1))) ? nothing : + isfieldnull(row, $(k - 1)) ? missing : + $(fafs[k])(row, $(k - 1)) + end + ), + ) for k in 1:NF + )..., + ) +end + +@generated function FDPf2c_rgf( + fdp_feature::FDP_AbstractFeature{FD}, + i::Int, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} ng = _ngt(FD) nf = _nft(FD) - ggfs = _get_getgeom_funcs(FD) - gffs = _get_getfield_funcs(FD) + ggfs = _get_getgeom_funcs_rgf(FD) + gffs = _get_getfield_funcs_rgf(FD) rex = Expr(:block) - for i in 1:ng - push!( - rex.args, - :(f($(ggfs[i])(row), $i, names[$i], columns[$i], args...)), - ) + push!(rex.args, Expr(:inbounds, true)) + for j in 1:ng + push!(rex.args, :(cols[$j][i] = $(ggfs[j])(fdp_feature))) end - for i in 1:nf - push!( - rex.args, - :(f( - $(gffs[i])(row), - $(i + ng), - names[$(i + ng)], - columns[$(i + ng)], - args..., - )), - ) + for j in ng+1:ng+nf + push!(rex.args, :(cols[$j][i] = $(gffs[j-ng])(fdp_feature))) end + push!(rex.args, Expr(:inbounds, :pop)) + push!(rex.args, :(nothing)) return rex end -allocatecolumn(T, len) = Array{T,1}(undef, len) - -@inline function _allocatecolumns( - ::Tables.Schema{names,types}, - len, -) where {names,types} - if @generated - vals = Tuple( - :(allocatecolumn($(fieldtype(types, i)), len)) for - i in 1:fieldcount(types) - ) - return :(NamedTuple{$(map(Symbol, names))}(($(vals...),))) - else - return NamedTuple{map(Symbol, names)}( - Tuple( - allocatecolumn(fieldtype(types, i), len) for - i in 1:fieldcount(types) - ), - ) - end -end - -@inline function allocatecolumns( - sch::Tables.Schema{names,types}, - len, -) where {names,types} - if fieldcount(types) <= SPECIALIZATION_THRESHOLD - return _allocatecolumns(sch, len) - else - return NamedTuple{map(Symbol, names)}( - Tuple( - allocatecolumn(fieldtype(types, i), len) for - i in 1:fieldcount(types) - ), - ) +function FDPfillcolumns_rgf!( + fdp_layer::FDP_AbstractFeatureLayer{FD}, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + state = 0 + while true + next = iterate(fdp_layer, state) + next === nothing && break + fdp_feature, state = next + FDPf2c_rgf(fdp_feature, state, cols) end end -@inline function add!( - dest::AbstractArray, - val, - ::Union{Base.HasLength,Base.HasShape}, - row, -) - return setindex!(dest, val, row) +function FDPcolumns_rgf( + fdp_layer::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + len = length(fdp_layer) + gdal_sch = Tables.schema(fdp_layer) + ng = _ngt(FD) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + FDPfillcolumns_rgf!(fdp_layer, cols) + return NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names)}([ + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ]), + ) end -@inline add!(dest::AbstractArray, val, T, row) = push!(dest, val) -replacex(t, col::Int, x) = ntuple(i -> i == col ? x : t[i], length(t)) +######################################## +# Trial with plain generated functions # +######################################## +# - Feature to columns line function: FDPf2c_pg +# - (Tables.)columns function: FDPfillcolumns_pg -@inline function add_or_widen!( - val, - col::Int, - nm, - dest::AbstractArray{T}, - fdp_feature, - updated, - L, -) where {T} - if val isa T || promote_type(typeof(val), T) <: T - add!(dest, val, L, fdp_feature) - return - else - new = allocatecolumn(promote_type(T, typeof(val)), length(dest)) - fdp_feature > 1 && copyto!(new, 1, dest, 1, fdp_feature - 1) - add!(new, val, L, fdp_feature) - updated[] = replacex(updated[], col, new) - return +@generated function FDPf2c_pg( + fdp_feature::FDP_AbstractFeature{FD}, + i::Int, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + ng = _ngt(FD) + nf = _nft(FD) + return quote + @inbounds for j in 1:($nf+$ng) + cols[j][i] = Tables.getcolumn(fdp_feature, j) + end + return nothing end end -function __buildcolumns(fdp_layer, st, sch, columns, rownbr, updated) +function FDPfillcolumns_pg!( + fdp_layer::FDP_AbstractFeatureLayer{FD}, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + state = 0 while true - state = iterate(fdp_layer, st) - state === nothing && break - row, st = state - rownbr += 1 - eachcolumns( - add_or_widen!, - sch, - row, - columns, - rownbr, - updated, - Base.IteratorSize(fdp_layer), - ) - columns !== updated[] && return __buildcolumns( - fdp_layer, - st, - sch, - updated[], - rownbr, - updated, - ) + next = iterate(fdp_layer, state) + next === nothing && break + fdp_feature, state = next + FDPf2c_pg(fdp_feature, state, cols) end - return updated end -struct EmptyVector <: AbstractVector{Union{}} - len::Int +function FDPcolumns_pg( + fdp_layer::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + len = length(fdp_layer) + gdal_sch = Tables.schema(fdp_layer) + ng = _ngt(FD) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + FDPfillcolumns_pg!(fdp_layer, cols) + return NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names)}([ + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ]), + ) end -Base.IndexStyle(::Type{EmptyVector}) = Base.IndexLinear() -Base.size(x::EmptyVector) = (x.len,) -Base.getindex(x::EmptyVector, i::Int) = throw(UndefRefError()) -function _buildcolumns(fdp_layer, fdp_feature, st, sch, columns, updated) - eachcolumns( - add_or_widen!, - sch, - fdp_feature, - columns, - 1, - updated, - Base.IteratorSize(fdp_layer), +######################################################### +# Best trials for FDP layer selected for Tables.columns # +######################################################### + +# Intermediary steps performance measurement commented below +function Tables.columns( + fdp_layer::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + len = length(fdp_layer) + gdal_sch = Tables.schema(fdp_layer) + ng = _ngt(FD) + # print("\tinit columns : ") + # @time columns = [ + # [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + # [Vector{Union{Missing,Nothing,T}}(missing, len) for T in gdal_sch.types[ng+1:end]] + # ] + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + # print("\tfill vectors : ") + # @time FDPfillcolumns_pg!(fdp_layer, cols) + FDPfillcolumns_pg!(fdp_layer, cols) + # print("\ttrim col types :") + # @time trimmed_col_types = Tuple(promote_type(unique(typeof(e) for e in c)...) for c in cols) + # print("\tconv cols types :") + # @time type_trimmed_columns = [ + # convert(Vector{trimmed_col_types[i]}, c) for (i, c) in enumerate(cols) + # ] + # print("\ttuples to NT : ") + # @time nt = NamedTuple{gdal_sch.names}( + # NTuple{length(gdal_sch.names)}(type_trimmed_columns), + # ) + # return nt + return NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names)}([ + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ]), ) - return __buildcolumns(fdp_layer, st, sch, updated[], 1, updated) end -@inline function Tables.columns( - fdp_layer::T, -) where {FD<:FDType,T<:FDP_AbstractFeatureLayer{FD}} - state = iterate(fdp_layer) - fdp_feature, st = state - names = Tuple(Tables.columnnames(fdp_feature)) - len = Base.haslength(T) ? length(rowitr) : 0 - sch = Tables.Schema(names, nothing) - columns = Tuple(EmptyVector(len) for _ in 1:length(names)) - return NamedTuple{map(Symbol, names)}( - _buildcolumns( - fdp_layer, - fdp_feature, - st, - sch, - columns, - Ref{Any}(columns), - )[], - ) +################################### +# Tables.columns for normal layer # +################################### + +function f2c(feature::AbstractFeature, i::Int, cols::Vector{Vector{T} where T}) + ng = ngeom(feature) + nf = nfield(feature) + @inbounds for j in 1:(nf+ng) + cols[j][i] = Tables.getcolumn(feature, j) + end + return nothing +end + +function fillcolumns!( + layer::AbstractFeatureLayer, + cols::Vector{Vector{T} where T}, +) + state = 0 + while true + next = iterate(layer, state) + next === nothing && break + feature, state = next + f2c(feature, state, cols) + end +end + +function Tables.columns(layer::AbstractFeatureLayer) + len = length(layer) + gdal_sch = Tables.schema(layer) + ng = ngeom(layer) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + fillcolumns!(layer, cols) + return cols + # return NamedTuple{gdal_sch.names}( + # NTuple{length(gdal_sch.names)}([ + # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for c in cols + # ]), + # ) end diff --git a/src/types.jl b/src/types.jl index b483f643..c5f5cd00 100644 --- a/src/types.jl +++ b/src/types.jl @@ -25,7 +25,7 @@ function getGType(ptr::GDAL.OGRGeomFieldDefnH) end #! NEW simple FeatureDefn type, could later maybe(?) replaced by full FeatureDefn type in the definitions below -FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter +#TODO delete: FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter FDType = Tuple{ NamedTuple{NG,<:Tuple{Vararg{GType}}} where NG, NamedTuple{NF,<:Tuple{Vararg{FType}}} where NF, @@ -49,14 +49,25 @@ end return :(tuple($T.types[2].types...)) end function _getFDType(ptr::GDAL.OGRFeatureDefnH) #! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH (both Ptr{Cvoid})) and we cannot dispatch on it - ng = GDAL.ogr_fd_getgeomfieldcount(ptr) + ng = GDAL.ogr_fd_getgeomfieldcount(ptr)::Int32 gflddefn_ptrs = (GDAL.ogr_fd_getgeomfielddefn(ptr, i - 1) for i in 1:ng) - NG = tuple(Symbol.(GDAL.ogr_gfld_getnameref.(gflddefn_ptrs))...) - TG = Tuple{(G for G in getGType.(gflddefn_ptrs))...} - nf = GDAL.ogr_fd_getfieldcount(ptr) + NG = tuple( + ( + Symbol(GDAL.ogr_gfld_getnameref(gflddefn_ptr)::String) for + gflddefn_ptr in gflddefn_ptrs + )..., + ) + TG = Tuple{(getGType(gflddefn_ptr) for gflddefn_ptr in gflddefn_ptrs)...} + nf = GDAL.ogr_fd_getfieldcount(ptr)::Int32 flddefn_ptrs = (GDAL.ogr_fd_getfielddefn(ptr, i - 1) for i in 1:nf) - NF = tuple(Symbol.(GDAL.ogr_fld_getnameref.(flddefn_ptrs))...) - TF = Tuple{(F for F in getFType.(flddefn_ptrs))...} + NF = tuple( + ( + Symbol(GDAL.ogr_fld_getnameref(flddefn_ptr)::String) for + flddefn_ptr in flddefn_ptrs + )..., + ) + TF = Tuple{(getFType(flddefn_ptr) for flddefn_ptr in flddefn_ptrs)...} + # TF = Tuple{ntuple(i -> getFType(flddefn_ptrs[i]), nf)...} => to use in case later conversion from FType to DataType has to be implemented return Tuple{NamedTuple{NG,TG},NamedTuple{NF,TF}} end @@ -74,6 +85,9 @@ abstract type DUAL_AbstractFeatureLayer end #! NEW abstract type supertype of Ab abstract type AbstractFeatureLayer <: DUAL_AbstractFeatureLayer end abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer end #! NEW abstract type to group FDP_FeatureLayer type instances # needs to have a `ptr::GDAL.OGRLayerH` attribute +@generated function _getFD(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return FD +end abstract type DUAL_AbstractFeature end #! NEW abstract type supertype of AbstractFeature and FDP_AbstractFeature abstract type AbstractFeature <: DUAL_AbstractFeature end #! NEW abstract type to group Feature and IFeature (if created) @@ -555,7 +569,7 @@ const DataType_2_OGRFieldType_OGRFieldSubType_mapping = Base.ImmutableDict( Vector{Int64} => (OFTInteger64List, OFSTNone), ) -# Conversions from DataType to OFType +# Conversions from DataType to FType const DataType2FType = Base.ImmutableDict( ( k => FType{v...} for @@ -568,12 +582,14 @@ GDALDataTypes = Union{keys(DataType2FType)...} !ismissing(result) || throw(MethodError(convert, (FType, T))) return :($(result)) end -#! PROBABLY NOT NECESSARY: Conversions from OFType to DataType -# const FType2DataType = Base.ImmutableDict((v => k for (k, v) in DataType2FType)...) +# Conversion from FType to DataType not implemented cause it creates a mess +# use get(FType2DataType, FT, missing) instead +const FType2DataType = + Base.ImmutableDict((v => k for (k, v) in DataType2FType)...) # # GDALFTypes = Union{keys(FType2DataType)...} # @generated function convert(::Type{DataType}, ::Type{T}) where T<:FType # result = get(FType2DataType, T, missing) -# result !=== missing || error( +# result !== missing || error( # "$T is not an FType corresponding to a valid GDAL (OGRFieldType, OGRFieldSubType) couple. \nPlease use one of the following: \n$(join((FType{v...} for (_, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping), "\n"))", # ) # return :($(result)) @@ -809,6 +825,34 @@ function convert(::Type{GDAL.OGRwkbGeometryType}, ogtinst::OGRwkbGeometryType) return GDAL.OGRwkbGeometryType(Integer(ogtinst)) end +# Conversion between Geometry or IGeometry subtypes and GType subtypes +function convert(::Type{Geometry}, G::Type{GType{T}}) where {T} + return Geometry{T} +end +function convert(::Type{IGeometry}, ::Type{GType{T}}) where {T} + return IGeometry{T} +end +function convert(::Type{GType}, ::Type{Geometry{T}}) where {T} + return GType{T} +end +function convert(::Type{GType}, ::Type{IGeometry{T}}) where {T} + return GType{T} +end + +# Conversion between GP_Geometry or GP_IGeometry subtypes and GType subtypes +function convert(::Type{GP_Geometry}, ::Type{G}) where {G<:GType} + return GP_Geometry{G} +end +function convert(::Type{GType}, ::Type{GP_Geometry{G}}) where {G<:GType} + return G +end +function convert(::Type{GP_IGeometry}, ::Type{G}) where {G<:GType} + return GP_IGeometry{G} +end +function convert(::Type{GType}, ::Type{GP_IGeometry{G}}) where {G<:GType} + return G +end + function basetype(gt::OGRwkbGeometryType)::OGRwkbGeometryType wkbGeomType = convert(GDAL.OGRwkbGeometryType, gt) wkbGeomType &= (~0x80000000) # Remove 2.5D flag. From f496f69567a4c268e2c112882733752c1e61a55c Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Sun, 19 Dec 2021 07:56:45 +0100 Subject: [PATCH 05/14] Tables unit test repair and `schema` functions cleaning - Reverted from `stealgeom` to `getgeom` in `getcolumn` to repair Tables unit tests - `Tables.schema` back to return `nothing` and `Schema` extraction functions from FeatureDefn called `gdal_schema` since the result is not a reliable `Table.Schema` (see Shapefile driver handling of mixed Line and MultiLine handling) --- src/tables.jl | 18 +++++++++--------- src/tables_columns.jl | 24 ++++++++++++------------ test/test_tables.jl | 20 ++++++++++---------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index cbf69959..bffc1b71 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -1,8 +1,8 @@ -# function Tables.schema(::AbstractFeatureLayer)::Nothing -# return nothing -# end +function Tables.schema(::AbstractFeatureLayer)::Nothing + return nothing +end -function Tables.schema(layer::AbstractFeatureLayer) +function gdal_schema(layer::AbstractFeatureLayer) geom_names, field_names, featuredefn, fielddefns = schema_names(layerdefn(layer)) ngeom = ArchGDAL.ngeom(featuredefn) @@ -16,7 +16,7 @@ function Tables.schema(layer::AbstractFeatureLayer) ) end -@generated function Tables.schema( +@generated function gdal_schema( ::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} gnames = _gtnames(FD) @@ -36,7 +36,7 @@ end function Tables.getcolumn(row::AbstractFeature, i::Int) ng = ngeom(row) return if i <= ng - geom = stealgeom(row, i - 1) + geom = getgeom(row, i - 1) geom.ptr != C_NULL ? geom : missing else getfield(row, i - ng - 1) @@ -49,7 +49,7 @@ function Tables.getcolumn( ) where {FD<:FDType} ng = ngeom(row) return if i <= ng - geom = stealgeom(row, i - 1) + geom = getgeom(row, i - 1) geom.ptr != C_NULL ? geom : missing else getfield(row, i - ng - 1) @@ -61,7 +61,7 @@ function Tables.getcolumn(row::Feature, name::Symbol) if !ismissing(field) return field end - geom = stealgeom(row, name) + geom = getgeom(row, name) if geom.ptr != C_NULL return geom end @@ -76,7 +76,7 @@ function Tables.getcolumn( if !ismissing(field) return field end - geom = stealgeom(row, name) + geom = getgeom(row, name) if geom.ptr != C_NULL return geom end diff --git a/src/tables_columns.jl b/src/tables_columns.jl index 099ae594..6205f46c 100644 --- a/src/tables_columns.jl +++ b/src/tables_columns.jl @@ -94,7 +94,7 @@ function FDPcolumns_gg( fdp_layer::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} len = length(fdp_layer) - gdal_sch = Tables.schema(fdp_layer) + gdal_sch = gdal_schema(fdp_layer) ng = _ngt(FD) cols = [ [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] @@ -208,7 +208,7 @@ function FDPcolumns_rgf( fdp_layer::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} len = length(fdp_layer) - gdal_sch = Tables.schema(fdp_layer) + gdal_sch = gdal_schema(fdp_layer) ng = _ngt(FD) cols = [ [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] @@ -264,7 +264,7 @@ function FDPcolumns_pg( fdp_layer::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} len = length(fdp_layer) - gdal_sch = Tables.schema(fdp_layer) + gdal_sch = gdal_schema(fdp_layer) ng = _ngt(FD) cols = [ [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] @@ -291,10 +291,10 @@ function Tables.columns( fdp_layer::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} len = length(fdp_layer) - gdal_sch = Tables.schema(fdp_layer) + gdal_sch = gdal_schema(fdp_layer) ng = _ngt(FD) # print("\tinit columns : ") - # @time columns = [ + # @time cols = [ # [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] # [Vector{Union{Missing,Nothing,T}}(missing, len) for T in gdal_sch.types[ng+1:end]] # ] @@ -355,7 +355,7 @@ end function Tables.columns(layer::AbstractFeatureLayer) len = length(layer) - gdal_sch = Tables.schema(layer) + gdal_sch = gdal_schema(layer) ng = ngeom(layer) cols = [ [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] @@ -365,10 +365,10 @@ function Tables.columns(layer::AbstractFeatureLayer) ] ] fillcolumns!(layer, cols) - return cols - # return NamedTuple{gdal_sch.names}( - # NTuple{length(gdal_sch.names)}([ - # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for c in cols - # ]), - # ) + return NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names)}([ + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ]), + ) end diff --git a/test/test_tables.jl b/test/test_tables.jl index 2e463739..77aef838 100644 --- a/test/test_tables.jl +++ b/test/test_tables.jl @@ -39,11 +39,11 @@ using Tables (:point, :linestring, :id, :zoom, :location) @test ismissing(Tables.getcolumn(features[2], -5)) @test ismissing(Tables.getcolumn(features[2], 0)) - @test Tables.getcolumn(features[1], 1) == "5.1" - @test Tables.getcolumn(features[1], 2) == "1.0" - @test Tables.getcolumn(features[1], 3) == "Mumbai" - @test AG.toWKT(Tables.getcolumn(features[1], 4)) == "POINT (30 10)" - @test AG.toWKT(Tables.getcolumn(features[1], 5)) == + @test Tables.getcolumn(features[1], 3) == "5.1" + @test Tables.getcolumn(features[1], 4) == "1.0" + @test Tables.getcolumn(features[1], 5) == "Mumbai" + @test AG.toWKT(Tables.getcolumn(features[1], 1)) == "POINT (30 10)" + @test AG.toWKT(Tables.getcolumn(features[1], 2)) == "LINESTRING (30 10,10 30,40 40)" @test Tables.getcolumn(features[1], :id) == "5.1" @test Tables.getcolumn(features[1], :zoom) == "1.0" @@ -58,11 +58,11 @@ using Tables (:point, :linestring, :id, :zoom, :location) @test ismissing(Tables.getcolumn(features[2], -5)) @test ismissing(Tables.getcolumn(features[2], 0)) - @test Tables.getcolumn(features[2], 1) == "5.2" - @test Tables.getcolumn(features[2], 2) == "2.0" - @test Tables.getcolumn(features[2], 3) == "New Delhi" - @test AG.toWKT(Tables.getcolumn(features[2], 4)) == "POINT (35 15)" - @test AG.toWKT(Tables.getcolumn(features[2], 5)) == + @test Tables.getcolumn(features[2], 3) == "5.2" + @test Tables.getcolumn(features[2], 4) == "2.0" + @test Tables.getcolumn(features[2], 5) == "New Delhi" + @test AG.toWKT(Tables.getcolumn(features[2], 1)) == "POINT (35 15)" + @test AG.toWKT(Tables.getcolumn(features[2], 2)) == "LINESTRING (35 15,15 35,45 45)" @test Tables.getcolumn(features[2], :id) == "5.2" @test Tables.getcolumn(features[2], :zoom) == "2.0" From 3412a908ce0ba5134c3a2da492dedcd933a68f88 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Mon, 20 Dec 2021 06:38:47 +0100 Subject: [PATCH 06/14] Fixes for Julia >= 1.7 - Fixed `NamedTuple` creation in `Tables.columns` for Julia >= 1.7 - Fixed `_gtnames` for Julia >= 1.7 --- src/tables_columns.jl | 33 ++++++++++++--------------------- src/types.jl | 4 ++-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/tables_columns.jl b/src/tables_columns.jl index 6205f46c..67d453ec 100644 --- a/src/tables_columns.jl +++ b/src/tables_columns.jl @@ -286,18 +286,12 @@ end # Best trials for FDP layer selected for Tables.columns # ######################################################### -# Intermediary steps performance measurement commented below function Tables.columns( fdp_layer::FDP_AbstractFeatureLayer{FD}, ) where {FD<:FDType} len = length(fdp_layer) gdal_sch = gdal_schema(fdp_layer) ng = _ngt(FD) - # print("\tinit columns : ") - # @time cols = [ - # [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - # [Vector{Union{Missing,Nothing,T}}(missing, len) for T in gdal_sch.types[ng+1:end]] - # ] cols = [ [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] [ @@ -305,22 +299,14 @@ function Tables.columns( T in gdal_sch.types[ng+1:end] ] ] - # print("\tfill vectors : ") - # @time FDPfillcolumns_pg!(fdp_layer, cols) FDPfillcolumns_pg!(fdp_layer, cols) - # print("\ttrim col types :") - # @time trimmed_col_types = Tuple(promote_type(unique(typeof(e) for e in c)...) for c in cols) - # print("\tconv cols types :") - # @time type_trimmed_columns = [ - # convert(Vector{trimmed_col_types[i]}, c) for (i, c) in enumerate(cols) - # ] - # print("\ttuples to NT : ") - # @time nt = NamedTuple{gdal_sch.names}( - # NTuple{length(gdal_sch.names)}(type_trimmed_columns), - # ) - # return nt + # Below works only with Julia version >= 1.7 + #return = NamedTuple{gdal_sch.names}([ + # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for + # c in cols + # ]) return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names)}([ + NTuple{length(gdal_sch.names),Vector{T} where T}([ convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for c in cols ]), @@ -365,8 +351,13 @@ function Tables.columns(layer::AbstractFeatureLayer) ] ] fillcolumns!(layer, cols) + # Below works only with Julia version >= 1.7 + #return = NamedTuple{gdal_sch.names}([ + # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for + # c in cols + # ]) return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names)}([ + NTuple{length(gdal_sch.names),Vector{T} where T}([ convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for c in cols ]), diff --git a/src/types.jl b/src/types.jl index c5f5cd00..b77a7fbe 100644 --- a/src/types.jl +++ b/src/types.jl @@ -34,7 +34,7 @@ FDType = Tuple{ return :(length($T.types[1].types)) end @generated function _gtnames(::Type{T}) where {T<:FDType} - return :(tuple($T.types[1].names...)) + return :(tuple($T.types[1].parameters[1]...)) end @generated function _gttypes(::Type{T}) where {T<:FDType} return :(tuple($T.types[1].types...)) @@ -43,7 +43,7 @@ end return :(length($T.types[2].types)) end @generated function _ftnames(::Type{T}) where {T<:FDType} - return :(tuple($T.types[2].names...)) + return :(tuple($T.types[2].parameters[1]...)) end @generated function _fttypes(::Type{T}) where {T<:FDType} return :(tuple($T.types[2].types...)) From b32ebde8a71b01c342c76efd0123b3a6330dccad Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 08:03:40 +0100 Subject: [PATCH 07/14] Confined use of parametric types to Tables.jl interface --- benchmark/remotefiles.jl | 10 +- src/ArchGDAL.jl | 2 +- src/base/display.jl | 8 +- src/base/iterators.jl | 18 - src/constants.jl | 74 ---- src/dataset.jl | 15 +- src/ogr/feature.jl | 106 ----- src/ogr/featuredefn.jl | 80 ---- src/ogr/featurelayer.jl | 34 +- src/ogr/fielddefn.jl | 38 -- src/tables.jl | 111 ++--- src/tables2.jl | 909 +++++++++++++++++++++++++++++++++++++++ src/tables_columns.jl | 365 ---------------- src/types.jl | 398 ----------------- test/runtests.jl | 1 + test/test_tables2.jl | 374 ++++++++++++++++ 16 files changed, 1354 insertions(+), 1189 deletions(-) create mode 100644 src/tables2.jl delete mode 100644 src/tables_columns.jl create mode 100644 test/test_tables2.jl diff --git a/benchmark/remotefiles.jl b/benchmark/remotefiles.jl index a5af3477..d0e7d40b 100644 --- a/benchmark/remotefiles.jl +++ b/benchmark/remotefiles.jl @@ -16,12 +16,10 @@ julia> open(filepath/filename) do f end ``` """ -remotefiles = [ - ( - "data/road.zip", - "058bdc549d0fc5bfb6deaef138e48758ca79ae20df79c2fb4c40cb878f48bfd8", - ), -] +remotefiles = [( + "data/road.zip", + "058bdc549d0fc5bfb6deaef138e48758ca79ae20df79c2fb4c40cb878f48bfd8", +)] function verify(path::AbstractString, hash::AbstractString) @assert occursin(r"^[0-9a-f]{64}$", hash) diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index 4220493f..d39c5b44 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -38,7 +38,7 @@ include("context.jl") include("base/iterators.jl") include("base/display.jl") include("tables.jl") -include("tables_columns.jl") +include("tables2.jl") include("geointerface.jl") include("convert.jl") diff --git a/src/base/display.jl b/src/base/display.jl index 63f97bc1..3dcf81f5 100644 --- a/src/base/display.jl +++ b/src/base/display.jl @@ -107,7 +107,7 @@ function Base.show( end # assumes that the layer is reset, and will reset it after display -function Base.show(io::IO, layer::DUAL_AbstractFeatureLayer)::Nothing +function Base.show(io::IO, layer::DUAL_AbstractFeatureLayer) if layer.ptr == C_NULL print(io, "NULL FeatureLayer") return nothing @@ -172,7 +172,7 @@ function Base.show(io::IO, layer::DUAL_AbstractFeatureLayer)::Nothing return nothing end -function Base.show(io::IO, featuredefn::AbstractFeatureDefn)::Nothing +function Base.show(io::IO, featuredefn::DUAL_AbstractFeatureDefn)::Nothing if featuredefn.ptr == C_NULL print(io, "NULL FeatureDefn") return nothing @@ -195,7 +195,7 @@ function Base.show(io::IO, featuredefn::AbstractFeatureDefn)::Nothing return nothing end -function Base.show(io::IO, fd::AbstractFieldDefn)::Nothing +function Base.show(io::IO, fd::DUAL_AbstractFieldDefn)::Nothing if fd.ptr == C_NULL print(io, "NULL FieldDefn") return nothing @@ -204,7 +204,7 @@ function Base.show(io::IO, fd::AbstractFieldDefn)::Nothing return nothing end -function Base.show(io::IO, gfd::AbstractGeomFieldDefn)::Nothing +function Base.show(io::IO, gfd::DUAL_AbstractGeomFieldDefn)::Nothing if gfd.ptr == C_NULL print(io, "NULL GeomFieldDefn") return nothing diff --git a/src/base/iterators.jl b/src/base/iterators.jl index f333fb9f..4d8d9fb5 100644 --- a/src/base/iterators.jl +++ b/src/base/iterators.jl @@ -13,25 +13,7 @@ function Base.iterate( end end -@generated function Base.iterate( - layer::FDP_AbstractFeatureLayer{FD}, - state::Integer = 0, -) where {FD<:FDType} - return quote - layer.ptr == C_NULL && return nothing - state == 0 && resetreading!(layer) - ptr = GDAL.ogr_l_getnextfeature(layer.ptr) - return if ptr == C_NULL - resetreading!(layer) - nothing - else - (FDP_Feature{$FD}(ptr; ownedby = layer), state + 1) - end - end -end - Base.eltype(layer::AbstractFeatureLayer)::DataType = Feature -Base.eltype(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} = FDP_Feature{FD} Base.IteratorSize(::Type{<:DUAL_AbstractFeatureLayer}) = Base.SizeUnknown() diff --git a/src/constants.jl b/src/constants.jl index 7d624073..22bab376 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -313,80 +313,6 @@ convert(GDAL.OGRwkbGeometryType, ArchGDAL.wkbUnknown) wkbUnknown::OGRwkbGeometryType = 0x00000000 ``` """ -# @enum( -# OGRwkbGeometryType, -# wkbUnknown = 0, -# wkbPoint = 1, -# wkbLineString = 2, -# wkbPolygon = 3, -# wkbMultiPoint = 4, -# wkbMultiLineString = 5, -# wkbMultiPolygon = 6, -# wkbGeometryCollection = 7, -# wkbCircularString = 8, -# wkbCompoundCurve = 9, -# wkbCurvePolygon = 10, -# wkbMultiCurve = 11, -# wkbMultiSurface = 12, -# wkbCurve = 13, -# wkbSurface = 14, -# wkbPolyhedralSurface = 15, -# wkbTIN = 16, -# wkbTriangle = 17, -# wkbNone = 100, -# wkbLinearRing = 101, -# wkbCircularStringZ = 20, -# wkbCompoundCurveZ = 21, -# wkbCurvePolygonZ = 22, -# wkbMultiCurveZ = 23, -# wkbMultiSurfaceZ = 24, -# wkbCurveZ = 25, -# wkbSurfaceZ = 26, -# wkbPolyhedralSurfaceZ = 27, -# wkbTINZ = 28, -# wkbTriangleZ = 29, -# wkbPointM = 30, -# wkbLineStringM = 31, -# wkbPolygonM = 32, -# wkbMultiPointM = 33, -# wkbMultiLineStringM = 34, -# wkbMultiPolygonM = 35, -# wkbGeometryCollectionM = 36, -# wkbCircularStringM = 37, -# wkbCompoundCurveM = 38, -# wkbCurvePolygonM = 39, -# wkbMultiCurveM = 40, -# wkbMultiSurfaceM = 41, -# wkbCurveM = 42, -# wkbSurfaceM = 43, -# wkbPolyhedralSurfaceM = 44, -# wkbTINM = 45, -# wkbTriangleM = 46, -# wkbPointZM = 47, -# wkbLineStringZM = 48, -# wkbPolygonZM = 49, -# wkbMultiPointZM = 50, -# wkbMultiLineStringZM = 51, -# wkbMultiPolygonZM = 52, -# wkbGeometryCollectionZM = 53, -# wkbCircularStringZM = 54, -# wkbCompoundCurveZM = 55, -# wkbCurvePolygonZM = 56, -# wkbMultiCurveZM = 57, -# wkbMultiSurfaceZM = 58, -# wkbCurveZM = 59, -# wkbSurfaceZM = 60, -# wkbPolyhedralSurfaceZM = 61, -# wkbTINZM = 62, -# wkbTriangleZM = 63, -# wkbPoint25D = 64, -# wkbLineString25D = 65, -# wkbPolygon25D = 66, -# wkbMultiPoint25D = 67, -# wkbMultiLineString25D = 68, -# wkbMultiPolygon25D = 69, -# wkbGeometryCollection25D = 70, -# ) @enum OGRwkbGeometryType::UInt32 begin wkbUnknown = 0x00000000 wkbPoint = 0x00000001 diff --git a/src/dataset.jl b/src/dataset.jl index 181241db..57a8b32d 100644 --- a/src/dataset.jl +++ b/src/dataset.jl @@ -515,12 +515,6 @@ the application. """ getlayer(dataset::AbstractDataset, i::Integer)::IFeatureLayer = IFeatureLayer(GDAL.gdaldatasetgetlayer(dataset.ptr, i), ownedby = dataset) -function getFDPlayer(dataset::AbstractDataset, i::Integer)::FDP_IFeatureLayer - ptr::GDAL.OGRLayerH = GDAL.gdaldatasetgetlayer(dataset.ptr, i) - fd_ptr = GDAL.ogr_l_getlayerdefn(ptr) - FD = _getFDType(fd_ptr) - return FDP_IFeatureLayer{FD}(ptr, ownedby = dataset) -end """ getlayer(dataset::AbstractDataset) @@ -530,13 +524,10 @@ Fetch the first layer and raise an error if `dataset` contains more than one lay The returned layer remains owned by the `dataset` and should not be deleted by the application. """ -function getlayer(dataset::AbstractDataset)::IFeatureLayer +function getlayer(dataset::AbstractDataset) nlayer(dataset) == 1 || error("Dataset has multiple layers. Specify the layer number or name") - return IFeatureLayer( - GDAL.gdaldatasetgetlayer(dataset.ptr, 0), - ownedby = dataset, - ) + return getlayer(dataset, 0) end unsafe_getlayer(dataset::AbstractDataset, i::Integer)::FeatureLayer = @@ -544,7 +535,7 @@ unsafe_getlayer(dataset::AbstractDataset, i::Integer)::FeatureLayer = function unsafe_getlayer(dataset::AbstractDataset)::FeatureLayer nlayer(dataset) == 1 || error("Dataset has multiple layers. Specify the layer number or name") - return FeatureLayer(GDAL.gdaldatasetgetlayer(dataset.ptr, 0)) + return unsafe_getlayer(dataset, 0) end """ diff --git a/src/ogr/feature.jl b/src/ogr/feature.jl index cefc5fc5..569b98dd 100644 --- a/src/ogr/feature.jl +++ b/src/ogr/feature.jl @@ -24,12 +24,6 @@ function destroy(feature::Feature)::Nothing feature.ptr = C_NULL return nothing end -function destroy(fdp_feature::FDP_AbstractFeature) - GDAL.ogr_f_destroy(fdp_feature.ptr) - fdp_feature.ptr = C_NULL - fdp_feature.ownedby = nothing - return nothing -end """ setgeom!(feature::Feature, geom::AbstractGeometry) @@ -85,9 +79,6 @@ Fetch number of fields on this feature. This will always be the same as the field count for the OGRFeatureDefn. """ nfield(feature::Feature)::Integer = GDAL.ogr_f_getfieldcount(feature.ptr) -@generated function nfield(::FDP_AbstractFeature{FD}) where {FD<:FDType} - return :($(_nft(FD))) -end """ getfielddefn(feature::Feature, i::Integer) @@ -104,15 +95,6 @@ internal reference, and should not be deleted or modified. """ getfielddefn(feature::Feature, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_f_getfielddefnref(feature.ptr, i)) -function getfielddefn( - fdp_feature::FDP_Feature{FD}, - i::Integer, -) where {FD<:FDType} - return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( - GDAL.ogr_f_getfielddefnref(fdp_feature.ptr, i); - ownedby = getfeaturedefn(fdp_feature), - ) -end """ findfieldindex(feature::Feature, name::Union{AbstractString, Symbol}) @@ -140,15 +122,6 @@ function findfieldindex( i end end -@generated function findfieldindex( - ::FDP_AbstractFeature{FD}, - name::Union{AbstractString,Symbol}, -) where {FD<:FDType} - return quote - i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) - return i !== nothing ? i - 1 : nothing - end -end """ isfieldset(feature::Feature, i::Integer) @@ -468,7 +441,6 @@ function getdefault(feature::Feature, i::Integer)::Union{String,Nothing} return getdefault(getfielddefn(feature, i)) end -#! @yeesian What is the use of this function ? getfield(feature::DUAL_AbstractFeature, i::Nothing)::Missing = missing const _FETCHFIELD = Dict{OGRFieldType,Function}( @@ -486,10 +458,6 @@ const _FETCHFIELD = Dict{OGRFieldType,Function}( OFTInteger64List => asint64list, ) -@generated function _get_fields_asfuncs(::Type{FD}) where {FD<:FDType} - return ((_FETCHFIELD[T.parameters[1]] for T in _fttypes(FD))...,) -end - """ getfield(feature, i) @@ -522,59 +490,10 @@ function getfield(feature::Feature, i::Integer) end end -@generated function getfield( - fdp_feature::FDP_AbstractFeature{FD}, - i::Integer, -) where {FD<:FDType} - return quote - return if !isfieldset(fdp_feature, i) - nothing - elseif isfieldnull(fdp_feature, i) - missing - else - $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) - end - end -end - -@generated function getfield( - fdp_feature::FDP_AbstractFeature{FD}, - i::Integer, - ::Val{K}, -) where {FD<:FDType,K} - return quote - return if !isfieldset(fdp_feature, i) - nothing - elseif isfieldnull(fdp_feature, i) - missing - else - $(_get_fields_asfuncs(FD)[K])(fdp_feature, i) - end - end -end - function getfield(feature::Feature, name::Union{AbstractString,Symbol}) return getfield(feature, findfieldindex(feature, name)) end -@generated function getfield( - fdp_feature::FDP_AbstractFeature{FD}, - name::Union{AbstractString,Symbol}, -) where {FD<:FDType} - return quote - i = findfieldindex(fdp_feature, name) - return if i === nothing - missing - elseif !isfieldset(fdp_feature, i) - nothing - elseif isfieldnull(fdp_feature, i) - missing - else - @inbounds $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) - end - end -end - """ setfield!(feature::Feature, i::Integer, value) setfield!(feature::Feature, i::Integer, value::DateTime, tzflag::Int = 0) @@ -723,9 +642,6 @@ Fetch number of geometry fields on this feature. This will always be the same as the geometry field count for OGRFeatureDefn. """ ngeom(feature::Feature)::Integer = GDAL.ogr_f_getgeomfieldcount(feature.ptr) -@generated function ngeom(::FDP_AbstractFeature{FD}) where {FD<:FDType} - return :($(_ngt(FD))) -end """ getgeomdefn(feature::Feature, i::Integer) @@ -765,15 +681,6 @@ function findgeomindex( )::Integer return GDAL.ogr_f_getgeomfieldindex(feature.ptr, name) end -@generated function findgeomindex( - ::FDP_AbstractFeature{FD}, - name::Union{AbstractString,Symbol} = "", -) where {FD<:FDType} - return return quote - i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) - return i !== nothing ? i - 1 : nothing - end -end """ getgeom(feature::Feature, i::Integer) @@ -793,19 +700,6 @@ function getgeom(feature::DUAL_AbstractFeature, i::Integer)::IGeometry end end -function stealgeom(feature::DUAL_AbstractFeature, i::Integer) - return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(feature.ptr)) : - getgeom(feature, i) -end -function stealgeom( - feature::DUAL_AbstractFeature, - name::Union{AbstractString,Symbol}, -) - i = findgeomindex(feature, name) - return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(feature.ptr)) : - getgeom(feature, i) -end - function unsafe_getgeom(feature::Feature, i::Integer)::Geometry result = GDAL.ogr_f_getgeomfieldref(feature.ptr, i) return if result == C_NULL diff --git a/src/ogr/featuredefn.jl b/src/ogr/featuredefn.jl index 86f14f11..444a0f3f 100644 --- a/src/ogr/featuredefn.jl +++ b/src/ogr/featuredefn.jl @@ -44,22 +44,12 @@ function destroy(featuredefn::FeatureDefn)::Nothing featuredefn.ptr = C_NULL return nothing end -function destroy(fdp_featuredefn::FDP_FeatureDefn) - GDAL.ogr_fd_destroy(fdp_featuredefn.ptr) - fdp_featuredefn.ptr = C_NULL - fdp_featuredefn.ownedby = nothing - return nothing -end "Destroy a feature definition view" function destroy(featuredefn::IFeatureDefnView)::Nothing featuredefn.ptr = C_NULL return nothing end -function destroy(fdp_ifeaturedefnview::FDP_IFeatureDefnView) - fdp_ifeaturedefnview.ptr = C_NULL - return fdp_ifeaturedefnview.ownedby = nothing -end """ release(featuredefn::FeatureDefn) @@ -86,9 +76,6 @@ Fetch number of fields on the passed feature definition. """ nfield(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getfieldcount(featuredefn.ptr) -@generated function nfield(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} - return :($(_nft(FD))) -end """ getfielddefn(featuredefn::FeatureDefn, i::Integer) @@ -106,29 +93,9 @@ object should not be modified or freed by the application. getfielddefn(featuredefn::FeatureDefn, i::Integer)::FieldDefn = FieldDefn(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) -function getfielddefn( - fdp_featuredefn::FDP_FeatureDefn{FD}, - i::Integer = 0, -) where {FD<:FDType} - return FTP_FieldDefn{_fttypes(FD)[i+1]}( - GDAL.ogr_fd_getfielddefn(fdp_featuredefn.ptr, i); - ownedby = fdp_featuredefn, - ) -end - getfielddefn(featuredefn::IFeatureDefnView, i::Integer)::IFieldDefnView = IFieldDefnView(GDAL.ogr_fd_getfielddefn(featuredefn.ptr, i)) -function getfielddefn( - fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, - i::Integer = 0, -) where {FD<:FDType} - return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( - GDAL.ogr_fd_getfielddefn(fdp_ifeaturedefnview.ptr, i); - ownedby = fdp_ifeaturedefnview, - ) -end - """ findfieldindex(featuredefn::AbstractFeatureDefn, name::Union{AbstractString, Symbol}) @@ -147,15 +114,6 @@ function findfieldindex( )::Integer return GDAL.ogr_fd_getfieldindex(featuredefn.ptr, name) end -@generated function findfieldindex( - ::FDP_AbstractFeatureDefn{FD}, - name::Union{AbstractString,Symbol}, -) where {FD<:FDType} - return return quote - i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) - return i !== nothing ? i - 1 : nothing - end -end """ addfielddefn!(featuredefn::FeatureDefn, fielddefn::FieldDefn) @@ -300,9 +258,6 @@ Fetch number of geometry fields on the passed feature definition. """ ngeom(featuredefn::AbstractFeatureDefn)::Integer = GDAL.ogr_fd_getgeomfieldcount(featuredefn.ptr) -@generated function ngeom(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} - return :($(_ngt(FD))) -end """ getgeomdefn(featuredefn::FeatureDefn, i::Integer = 0) @@ -319,29 +274,9 @@ should not be modified or freed by the application. getgeomdefn(featuredefn::AbstractFeatureDefn, i::Integer = 0)::GeomFieldDefn = GeomFieldDefn(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) -function getgeomdefn( - fdp_featuredefn::FDP_FeatureDefn{FD}, - i::Integer = 0, -) where {FD<:FDType} - return GFTP_GeomFieldDefn{_gttypes(FD)[i+1]}( - GDAL.ogr_fd_getgeomfielddefn(fdp_featuredefn.ptr, i); - ownedby = fdp_featuredefn, - ) -end - getgeomdefn(featuredefn::IFeatureDefnView, i::Integer = 0)::IGeomFieldDefnView = IGeomFieldDefnView(GDAL.ogr_fd_getgeomfielddefn(featuredefn.ptr, i)) -function getgeomdefn( - fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, - i::Integer = 0, -) where {FD<:FDType} - return GFTP_IGeomFieldDefnView{_gttypes(FD)[i+1]}( - GDAL.ogr_fd_getgeomfielddefn(fdp_ifeaturedefnview.ptr, i); - ownedby = fdp_ifeaturedefnview, - ) -end - """ findgeomindex(featuredefn::AbstractFeatureDefn, name::AbstractString = "") @@ -359,15 +294,6 @@ function findgeomindex( )::Integer return GDAL.ogr_fd_getgeomfieldindex(featuredefn.ptr, name) end -@generated function findgeomindex( - ::FDP_AbstractFeatureDefn{FD}, - name::AbstractString = "", -) where {FD<:FDType} - return return quote - i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) - return i !== nothing ? i - 1 : nothing - end -end """ addgeomdefn!(featuredefn::FeatureDefn, geomfielddefn::AbstractGeomFieldDefn) @@ -444,9 +370,3 @@ Fetch feature definition. """ getfeaturedefn(feature::Feature)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_f_getdefnref(feature.ptr)) -function getfeaturedefn(fdp_feature::FDP_Feature{FD}) where {FD<:FDType} - return FDP_IFeatureDefnView{FD}( - GDAL.ogr_f_getdefnref(fdp_feature.ptr); - ownedby = fdp_feature.ownedby, - ) -end diff --git a/src/ogr/featurelayer.jl b/src/ogr/featurelayer.jl index 26930974..92de8258 100644 --- a/src/ogr/featurelayer.jl +++ b/src/ogr/featurelayer.jl @@ -1,19 +1,9 @@ - -#! @yeesian Why is there a difference between interactive and non-interactive forms of -#! layer type regarding ownedby and spatialref properties function destroy(layer::AbstractFeatureLayer)::Nothing layer.ptr = C_NULL return nothing end -function destroy(fdp_layer::FDP_AbstractFeatureLayer) - # No specific GDAL object destructor for layer, it will be handled by the dataset closing - fdp_layer.ptr = C_NULL - fdp_layer.ownedby = nothing - fdp_layer.spatialref = nothing - return nothing -end -function destroy(layer::Union{IFeatureLayer,FDP_IFeatureLayer})::Nothing +function destroy(layer::IFeatureLayer)::Nothing layer.ptr = C_NULL layer.ownedby = Dataset() layer.spatialref = SpatialRef() @@ -566,12 +556,6 @@ The `featuredefn` is owned by the `layer` and should not be modified. """ layerdefn(layer::AbstractFeatureLayer)::IFeatureDefnView = IFeatureDefnView(GDAL.ogr_l_getlayerdefn(layer.ptr)) -function layerdefn(fdp_layer::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} - return FDP_IFeatureDefnView{FD}( - GDAL.ogr_l_getlayerdefn(fdp_layer.ptr); - ownedby = fdp_layer, - ) -end """ findfieldindex(layer::AbstractFeatureLayer, @@ -590,16 +574,6 @@ function findfieldindex( )::Integer return GDAL.ogr_l_findfieldindex(layer.ptr, field, exactmatch) end -@generated function findfieldindex( - ::FDP_AbstractFeatureLayer{FD}, - field::Union{AbstractString,Symbol}, - #! Note that exactmatch::Bool is not used in GDAL except when OGRAPISPY_ENABLED is true => dropped -) where {FD<:FDType} - return return quote - i = findfirst(isequal(Symbol(field)), $(_ftnames(FD))) - return i !== nothing ? i - 1 : nothing - end -end """ nfeature(layer::AbstractFeatureLayer, force::Bool = false) @@ -620,9 +594,6 @@ nfeature(layer::DUAL_AbstractFeatureLayer, force::Bool = false)::Integer = Fetch number of geometry fields on the feature layer. """ ngeom(layer::AbstractFeatureLayer)::Integer = ngeom(layerdefn(layer)) -@generated function ngeom(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} - return :($(_ngt(FD))) -end """ nfield(layer::AbstractFeatureLayer) @@ -630,9 +601,6 @@ end Fetch number of fields on the feature layer. """ nfield(layer::AbstractFeatureLayer)::Integer = nfield(layerdefn(layer)) -@generated function nfield(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} - return :($(_nft(FD))) -end """ envelope(layer::AbstractFeatureLayer, force::Bool = false) diff --git a/src/ogr/fielddefn.jl b/src/ogr/fielddefn.jl index d1dfedbe..0d0690bd 100644 --- a/src/ogr/fielddefn.jl +++ b/src/ogr/fielddefn.jl @@ -14,22 +14,11 @@ function destroy(fielddefn::FieldDefn)::Nothing fielddefn.ptr = C_NULL return nothing end -function destroy(ftp_fielddefn::FTP_FieldDefn) - GDAL.ogr_fld_destroy(ftp_fielddefn) - ftp_fielddefn.ptr = C_NULL - ftp_fielddefn.ownedby = nothing - return nothing -end function destroy(fielddefn::IFieldDefnView)::Nothing fielddefn.ptr = C_NULL return nothing end -function destroy(ftp_fielddefn::FTP_IFieldDefnView) - ftp_fielddefn.ptr = C_NULL - ftp_fielddefn.ownedby = nothing - return nothing -end "Set the name of this field." function setname!(fielddefn::FieldDefn, name::AbstractString)::FieldDefn @@ -44,9 +33,6 @@ getname(fielddefn::DUAL_AbstractFieldDefn)::String = "Fetch the type of this field." gettype(fielddefn::AbstractFieldDefn)::OGRFieldType = GDAL.ogr_fld_gettype(fielddefn.ptr) -@generated function gettype(::FTP_AbstractFieldDefn{FType{T,ST}}) where {T,ST} - return :($T) -end "Set the type of this field." function settype!(fielddefn::FieldDefn, etype::OGRFieldType)::FieldDefn @@ -67,11 +53,6 @@ field subtype. """ getsubtype(fielddefn::AbstractFieldDefn)::OGRFieldSubType = GDAL.ogr_fld_getsubtype(fielddefn.ptr) -@generated function getsubtype( - ::FTP_AbstractFieldDefn{FType{T,ST}}, -) where {T,ST} - return :($ST) -end """ setsubtype!(fielddefn::FieldDefn, subtype::OGRFieldSubType) @@ -117,11 +98,6 @@ function getfieldtype( gettype(fielddefn) end end -@generated function getfieldtype( - ::FTP_AbstractFieldDefn{FType{T,ST}}, -) where {T,ST} - return ST != OFSTNone ? :($ST) : :($T) -end """ getjustify(fielddefn::AbstractFieldDefn) @@ -359,25 +335,12 @@ function destroy(geomdefn::GeomFieldDefn)::Nothing geomdefn.spatialref = SpatialRef() return nothing end -function destroy(gftp_geomfielddefn::GFTP_GeomFieldDefn) - GDAL.ogr_gfld_destroy(gftp_geomfielddefn) - gftp_geomfielddefn.ptr = C_NULL - gftp_geomfielddefn.ownedby = nothing - gftp_geomfielddefn.spatialref = nothing - return nothing -end "Destroy a geometry field definition." function destroy(geomdefn::IGeomFieldDefnView)::Nothing geomdefn.ptr = C_NULL return nothing end -function destroy(gftp_igeomfielddefnview::GFTP_IGeomFieldDefnView) - gftp_igeomfielddefnview.ptr = C_NULL - gftp_igeomfielddefnview.ownedby = nothing - gftp_igeomfielddefnview.spatialref = nothing - return nothing -end "Set the name of this field." function setname!(geomdefn::GeomFieldDefn, name::AbstractString)::GeomFieldDefn @@ -390,7 +353,6 @@ getname(geomdefn::DUAL_AbstractGeomFieldDefn)::String = GDAL.ogr_gfld_getnameref(geomdefn.ptr) "Fetch geometry type of this field." -#TODO: Maybe faster to specialize on GFTP_AbstractGeomFieldDefn and retrieve type from type parameter gettype(geomdefn::DUAL_AbstractGeomFieldDefn)::OGRwkbGeometryType = GDAL.ogr_gfld_gettype(geomdefn.ptr) diff --git a/src/tables.jl b/src/tables.jl index bffc1b71..a56e67ff 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -16,20 +16,10 @@ function gdal_schema(layer::AbstractFeatureLayer) ) end -@generated function gdal_schema( - ::FDP_AbstractFeatureLayer{FD}, -) where {FD<:FDType} - gnames = _gtnames(FD) - fnames = _ftnames(FD) - gtypes = (convert(IGeometry, gt) for gt in _gttypes(FD)) - ftypes = (get(FType2DataType, ft, missing) for ft in _fttypes(FD)) - return Tables.Schema((gnames..., fnames...), (gtypes..., ftypes...)) -end - -Tables.istable(::Type{<:DUAL_AbstractFeatureLayer})::Bool = true -Tables.rowaccess(::Type{<:DUAL_AbstractFeatureLayer})::Bool = true +Tables.istable(::Type{<:AbstractFeatureLayer})::Bool = true +Tables.rowaccess(::Type{<:AbstractFeatureLayer})::Bool = true -function Tables.rows(layer::T)::T where {T<:DUAL_AbstractFeatureLayer} +function Tables.rows(layer::T)::T where {T<:AbstractFeatureLayer} return layer end @@ -43,19 +33,6 @@ function Tables.getcolumn(row::AbstractFeature, i::Int) end end -function Tables.getcolumn( - row::FDP_AbstractFeature{FD}, - i::Int, -) where {FD<:FDType} - ng = ngeom(row) - return if i <= ng - geom = getgeom(row, i - 1) - geom.ptr != C_NULL ? geom : missing - else - getfield(row, i - ng - 1) - end -end - function Tables.getcolumn(row::Feature, name::Symbol) field = getfield(row, name) if !ismissing(field) @@ -68,23 +45,8 @@ function Tables.getcolumn(row::Feature, name::Symbol) return missing end -function Tables.getcolumn( - row::FDP_AbstractFeature{FD}, - name::Symbol, -) where {FD<:FDType} - field = getfield(row, name) - if !ismissing(field) - return field - end - geom = getgeom(row, name) - if geom.ptr != C_NULL - return geom - end - return missing -end - function Tables.columnnames( - row::DUAL_AbstractFeature, + row::AbstractFeature, )::NTuple{Int64(nfield(row) + ngeom(row)),Symbol} geom_names, field_names = schema_names(getfeaturedefn(row)) return (geom_names..., field_names...) @@ -100,16 +62,57 @@ function schema_names(featuredefn::IFeatureDefnView) return (geom_names, field_names, featuredefn, fielddefns) end -#TODO: check wether some functions used in schema_names could be optimized -function schema_names( - fdp_featuredefn::FDP_IFeatureDefnView{FD}, -) where {FD<:FDType} - fielddefns = - (getfielddefn(fdp_featuredefn, i) for i in 0:nfield(fdp_featuredefn)-1) - field_names = (Symbol(getname(fielddefn)) for fielddefn in fielddefns) - geom_names = collect( - Symbol(getname(getgeomdefn(fdp_featuredefn, i - 1))) for - i in 1:ngeom(fdp_featuredefn) - ) - return (geom_names, field_names, fdp_featuredefn, fielddefns) +############################################################# +# Tables.columns on AbstractFeatture layer for normal layer # +############################################################# + +function f2c(feature::AbstractFeature, i::Int, cols::Vector{Vector{T} where T}) + ng = ngeom(feature) + nf = nfield(feature) + @inbounds for j in 1:(nf+ng) + cols[j][i] = Tables.getcolumn(feature, j) + end + return nothing +end + +function fillcolumns!( + layer::AbstractFeatureLayer, + cols::Vector{Vector{T} where T}, +) + state = 0 + while true + next = iterate(layer, state) + next === nothing && break + feature, state = next + f2c(feature, state, cols) + end +end + +function Tables.columns(layer::AbstractFeatureLayer) + len = length(layer) + gdal_sch = gdal_schema(layer) + ng = ngeom(layer) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + fillcolumns!(layer, cols) + return if VERSION < v"1.7" + NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names),Vector{T} where T}([ + convert( + Vector{promote_type(unique(typeof(e) for e in c)...)}, + c, + ) for c in cols + ]), + ) + else + NamedTuple{gdal_sch.names}( + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ) + end end diff --git a/src/tables2.jl b/src/tables2.jl new file mode 100644 index 00000000..833dcff3 --- /dev/null +++ b/src/tables2.jl @@ -0,0 +1,909 @@ +#*############################################################################# +#* Parametric ArchGDAL vector data types for ArchGDAL.Table # +#*############################################################################# +# 1. Definition and associated methods of FType, GType and FDType. +# Used as parameters for parametric ArchGDAL vector data types +# 2. Types hierarchy of parametric ArchGDAL vector data types +# 3. Parametric ArchGDAL vector data types definitions +# In parenthesis: objects with commented definition or to define +# - GFTP_GeomFieldDefn and GFTP_IGeomFieldDefnView +# - FTP_FieldDefn and FTP_IFieldDefnView +# - FDP_FeatureDefn and FDP_IFeatureDefnView +# - FDP_Feature (and FDP_IFeatureView) +# - (GP_Geometry and GP_IGeometry) +# - (FDP_FeatureLayer and) FDP_IFeatureLayer +# 4. Conversion function for parametric ArchGDAL vector data types +# 5. Subset of ArchGDAL vector functions adpated and optimized for +# parametric ArchGDAL vector data types +# a. Methods for GFTP_AbstractGeomFieldDefn => none found useful yet +# b. Methofs for FTP_AbstractFieldDefn +# c. Methods for FDP_AbstractFeatureDefn +# d. Methods for FDP_AbstractFeature +# e. Methods for FDP_AbstractFeatureLayer +############################################################################### + +############################################################################### +# 1. Definition of FType, GType and FDType definition # +############################################################################### + +#! AbstractOFType could also be a non parameterized abstract type with +#! OFType{OGRFieldType, OGRFieldSubType} instead of +#! OFType{T,OGRFieldSubType} <: AbstractOFType{T} +abstract type AbstractFType{OGRFieldType} end +struct FType{T,OGRFieldSubType} <: AbstractFType{T} end +function getFType(ptr::GDAL.OGRFieldDefnH) + return FType{ + convert(OGRFieldType, GDAL.ogr_fld_gettype(ptr)), + convert(OGRFieldSubType, GDAL.ogr_fld_getsubtype(ptr)), + } +end +abstract type AbstractGType end +struct GType{OGRwkbGeometryType} <: AbstractGType end +function getGType(ptr::GDAL.OGRGeomFieldDefnH) + return GType{convert(OGRwkbGeometryType, GDAL.ogr_gfld_gettype(ptr))} +end + +#! NEW simple FeatureDefn type, could later maybe(?) replaced by full +#! FeatureDefn type in the definitions below +#TODO delete: FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter +FDType = Tuple{ + NamedTuple{NG,<:Tuple{Vararg{GType}}} where NG, + NamedTuple{NF,<:Tuple{Vararg{FType}}} where NF, +} +@generated function _ngt(::Type{T}) where {T<:FDType} + return :(length($T.types[1].types)) +end +@generated function _gtnames(::Type{T}) where {T<:FDType} + return :(tuple($T.types[1].parameters[1]...)) +end +@generated function _gttypes(::Type{T}) where {T<:FDType} + return :(tuple($T.types[1].types...)) +end +@generated function _nft(::Type{T}) where {T<:FDType} + return :(length($T.types[2].types)) +end +@generated function _ftnames(::Type{T}) where {T<:FDType} + return :(tuple($T.types[2].parameters[1]...)) +end +@generated function _fttypes(::Type{T}) where {T<:FDType} + return :(tuple($T.types[2].types...)) +end +#! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH +#! (both Ptr{Cvoid})) and we cannot dispatch on it +function _getFDType(ptr::GDAL.OGRFeatureDefnH) + ng = GDAL.ogr_fd_getgeomfieldcount(ptr)::Int32 + gflddefn_ptrs = (GDAL.ogr_fd_getgeomfielddefn(ptr, i - 1) for i in 1:ng) + NG = tuple( + ( + Symbol(GDAL.ogr_gfld_getnameref(gflddefn_ptr)::String) for + gflddefn_ptr in gflddefn_ptrs + )..., + ) + TG = Tuple{(getGType(gflddefn_ptr) for gflddefn_ptr in gflddefn_ptrs)...} + nf = GDAL.ogr_fd_getfieldcount(ptr)::Int32 + flddefn_ptrs = (GDAL.ogr_fd_getfielddefn(ptr, i - 1) for i in 1:nf) + NF = tuple( + ( + Symbol(GDAL.ogr_fld_getnameref(flddefn_ptr)::String) for + flddefn_ptr in flddefn_ptrs + )..., + ) + TF = Tuple{(getFType(flddefn_ptr) for flddefn_ptr in flddefn_ptrs)...} + # TF = Tuple{ntuple(i -> getFType(flddefn_ptrs[i]), nf)...} => to use in case later conversion from FType to DataType has to be implemented + return Tuple{NamedTuple{NG,TG},NamedTuple{NF,TF}} +end + +############################################################################### +# 2. Types hierarchy of parametric ArchGDAL vector data types # +############################################################################### + +abstract type GFTP_AbstractGeomFieldDefn{GFT<:GType} <: + DUAL_AbstractGeomFieldDefn end +abstract type FTP_AbstractFieldDefn{FT<:FType} <: DUAL_AbstractFieldDefn end +abstract type FDP_AbstractFeatureDefn{FD<:FDType} <: DUAL_AbstractFeatureDefn end +abstract type FDP_AbstractFeature{FD<:FDType} <: DUAL_AbstractFeature end +abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer end + +############################################################################### +# 3. Definition of parametric ArchGDAL vector data types # +############################################################################### + +#! NEW GFTP_GeomFieldDefn +# mutable struct GFTP_GeomFieldDefn{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} +# ptr::GDAL.OGRGeomFieldDefnH +# ownedby::Union{Nothing,FDP_AbstractFeatureDefn} +# spatialref::Union{Nothing,AbstractSpatialRef} + +# function GFTP_GeomFieldDefn{GFT}( +# ptr::GDAL.OGRGeomFieldDefnH = C_NULL; +# ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, +# spatialref::Union{Nothing,AbstractSpatialRef} = nothing, +# ) where {GFT<:GType} +# return new(ptr, ownedby, spatialref) +# end +# end + +# function destroy(gftp_geomfielddefn::GFTP_GeomFieldDefn) +# GDAL.ogr_gfld_destroy(gftp_geomfielddefn) +# gftp_geomfielddefn.ptr = C_NULL +# gftp_geomfielddefn.ownedby = nothing +# gftp_geomfielddefn.spatialref = nothing +# return nothing +# end + +#! NEW GFTP_IGeomFieldDefnView +mutable struct GFTP_IGeomFieldDefnView{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} + ptr::GDAL.OGRGeomFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + spatialref::Union{Nothing,AbstractSpatialRef} + + function GFTP_IGeomFieldDefnView{GFT}( + ptr::GDAL.OGRGeomFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {GFT<:GType} + gftp_igeomfielddefnview = new(ptr, ownedby, spatialref) + finalizer(destroy, gftp_igeomfielddefnview) + return gftp_igeomfielddefnview + end +end + +function destroy(gftp_igeomfielddefnview::GFTP_IGeomFieldDefnView) + gftp_igeomfielddefnview.ptr = C_NULL + gftp_igeomfielddefnview.ownedby = nothing + gftp_igeomfielddefnview.spatialref = nothing + return nothing +end + +#! NEW FTP_FieldDefn +# mutable struct FTP_FieldDefn{FT} <: FTP_AbstractFieldDefn{FT} +# ptr::GDAL.OGRFieldDefnH +# ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + +# function FTP_FieldDefn{FT}( +# ptr::GDAL.OGRFieldDefnH = C_NULL; +# ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, +# ) where {FT<:FType} +# return new(ptr, ownedby) +# end +# end + +# function destroy(ftp_fielddefn::FTP_FieldDefn) +# GDAL.ogr_fld_destroy(ftp_fielddefn) +# ftp_fielddefn.ptr = C_NULL +# ftp_fielddefn.ownedby = nothing +# return nothing +# end + +#! NEW FTP_IFieldDefnView +mutable struct FTP_IFieldDefnView{FT} <: FTP_AbstractFieldDefn{FT} + ptr::GDAL.OGRFieldDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} + + function FTP_IFieldDefnView{FT}( + ptr::GDAL.OGRFieldDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, + ) where {FT<:FType} + ftp_ifielddefnview = new(ptr, ownedby) + finalizer(destroy, ftp_ifielddefnview) + return ftp_ifielddefnview + end +end + +function destroy(ftp_fielddefn::FTP_IFieldDefnView) + ftp_fielddefn.ptr = C_NULL + ftp_fielddefn.ownedby = nothing + return nothing +end + +#! NEW FeatureDefn parameterized FeatureDefn and IFeatureDefnView +# mutable struct FDP_FeatureDefn{FD} <: FDP_AbstractFeatureDefn{FD} +# ptr::GDAL.OGRFeatureDefnH +# ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} + +# function FDP_FeatureDefn{FD}( +# ptr::GDAL.OGRFeatureDefnH = C_NULL; +# ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, +# ) where {FD<:FDType} +# return new(ptr, ownedby) +# end +# end + +# function destroy(fdp_featuredefn::FDP_FeatureDefn) +# GDAL.ogr_fd_destroy(fdp_featuredefn.ptr) +# fdp_featuredefn.ptr = C_NULL +# fdp_featuredefn.ownedby = nothing +# return nothing +# end + +mutable struct FDP_IFeatureDefnView{FD} <: FDP_AbstractFeatureDefn{FD} + ptr::GDAL.OGRFeatureDefnH + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} + + function FDP_IFeatureDefnView{FD}( + ptr::GDAL.OGRFeatureDefnH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, + ) where {FD<:FDType} + fdp_ifeaturedefnview = new(ptr, ownedby) + finalizer(destroy, fdp_ifeaturedefnview) + return fdp_ifeaturedefnview + end +end + +function destroy(fdp_ifeaturedefnview::FDP_IFeatureDefnView) + fdp_ifeaturedefnview.ptr = C_NULL + fdp_ifeaturedefnview.ownedby = nothing + return nothing +end + +#! NEW FeatureDefn parameterized Feature +# mutable struct FDP_Feature{FD} <: FDP_AbstractFeature{FD} +# ptr::GDAL.OGRFeatureH +# ownedby::Union{Nothing,FDP_AbstractFeatureLayer} + +# function FDP_Feature{FD}( +# ptr::GDAL.OGRFeatureH = C_NULL; +# ownedby::Union{Nothing,FDP_AbstractFeatureLayer} = nothing, +# ) where {FD<:FDType} +# return new(ptr, ownedby) +# end +# end + +function destroy(fdp_feature::FDP_AbstractFeature) + GDAL.ogr_f_destroy(fdp_feature.ptr) + fdp_feature.ptr = C_NULL + fdp_feature.ownedby = nothing + return nothing +end + +#! NEW FeatureDefn parameterized IFeature +mutable struct FDP_IFeature{FD} <: FDP_AbstractFeature{FD} + ptr::GDAL.OGRFeatureH + ownedby::Union{Nothing,FDP_AbstractFeatureLayer} + + function FDP_IFeature{FD}( + ptr::GDAL.OGRFeatureH = C_NULL; + ownedby::Union{Nothing,FDP_AbstractFeatureLayer} = nothing, + ) where {FD<:FDType} + fdp_ifeature = new(ptr, ownedby) + finalizer(destroy, fdp_ifeature) + return fdp_ifeature + end +end + +#! NEW Geometry and IGeometry +# abstract type GP_AbstractGeometry{G<:GType} <: GeoInterface.AbstractGeometry end + +# function _inferGType(ptr::GDAL.OGRGeometryH = C_NULL)::Type{<:GType} +# return ptr != C_NULL ? +# GType{OGRwkbGeometryType(Int32(GDAL.ogr_g_getgeometrytype(ptr)))} : +# GType{wkbUnknown} +# end + +# mutable struct GP_Geometry{G} <: GP_AbstractGeometry{G} +# ptr::GDAL.OGRGeometryH +# ownedby::Union{Nothing,FDP_AbstractFeature} + +# function GP_Geometry{G}( +# ptr::GDAL.OGRGeometryH = C_NULL, +# ownedby::Union{Nothing,FDP_AbstractFeature} = nothing, +# ) where {G<:GType} +# return GP_Geometry{_inferGType(ptr)}(ptr, ownedby) +# end +# end + +# mutable struct GP_IGeometry{G} <: GP_AbstractGeometry{G} +# ptr::GDAL.OGRGeometryH +# ownedby::Union{Nothing,FDP_AbstractFeature} + +# function GP_IGeometry{G}( +# ptr::GDAL.OGRGeometryH = C_NULL, +# ownedby::Union{Nothing,FDP_AbstractFeature} = nothing, +# ) where {G<:GType} +# gp_igeometry = new{_inferGType(ptr)}(ptr, ownedby) +# finalizer(destroy, gp_igeometry) +# return gp_igeometry +# end +# end + +# #! NEW FeatureDefn parameterized FeatureLayer +# mutable struct FDP_FeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} +# ptr::GDAL.OGRLayerH +# ownedby::AbstractDataset +# spatialref::Union{Nothing,AbstractSpatialRef} + +# function FDP_FeatureLayer{FD}( +# ptr::GDAL.OGRLayerH = C_NULL; +# ownedby::AbstractDataset = Dataset(), +# spatialref::Union{Nothing,AbstractSpatialRef} = nothing, +# ) where {FD<:FDType} +# return new(ptr, ownedby, spatialref) +# end +# end + +#! NEW FeatureDefn parameterized IFeatureLayer +mutable struct FDP_IFeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} + ptr::GDAL.OGRLayerH + ownedby::Union{Nothing,AbstractDataset} + spatialref::Union{Nothing,AbstractSpatialRef} + + function FDP_IFeatureLayer{FD}( + ptr::GDAL.OGRLayerH = C_NULL; + ownedby::AbstractDataset = Dataset(), + spatialref::Union{Nothing,AbstractSpatialRef} = nothing, + ) where {FD<:FDType} + fdp_layer = new(ptr, ownedby, spatialref) + finalizer(destroy, fdp_layer) + return fdp_layer + end +end + +function destroy(fdp_layer::FDP_AbstractFeatureLayer) + # No specific GDAL object destructor for layer, it will be handled by the dataset closing + fdp_layer.ptr = C_NULL + fdp_layer.ownedby = nothing + fdp_layer.spatialref = nothing + return nothing +end + +############################################################################### +# 4. Conversion function for parametric ArchGDAL vector data types # +############################################################################### + +# Default DataType = LAST, for duplicated (oftid, ofstid) values +const DataType_2_OGRFieldType_OGRFieldSubType_mapping = Base.ImmutableDict( + Bool => (OFTInteger, OFSTBoolean), + Int8 => (OFTInteger, OFSTNone), + Int16 => (OFTInteger, OFSTInt16), + Int32 => (OFTInteger, OFSTNone), # Default OFTInteger + Vector{Bool} => (OFTIntegerList, OFSTBoolean), + Vector{Int16} => (OFTIntegerList, OFSTInt16), + Vector{Int32} => (OFTIntegerList, OFSTNone), # Default OFTIntegerList + Float16 => (OFTReal, OFSTNone), + Float32 => (OFTReal, OFSTFloat32), + Float64 => (OFTReal, OFSTNone), # Default OFTReal + Vector{Float16} => (OFTRealList, OFSTNone), + Vector{Float32} => (OFTRealList, OFSTFloat32), + Vector{Float64} => (OFTRealList, OFSTNone), # Default OFTRealList + String => (OFTString, OFSTNone), + Vector{String} => (OFTStringList, OFSTNone), + Vector{UInt8} => (OFTBinary, OFSTNone), + Dates.Date => (OFTDate, OFSTNone), + Dates.Time => (OFTTime, OFSTNone), + Dates.DateTime => (OFTDateTime, OFSTNone), + Int64 => (OFTInteger64, OFSTNone), + Vector{Int64} => (OFTInteger64List, OFSTNone), +) + +const OGRField_DataTypes = Union{ + Missing, + Nothing, + keys(DataType_2_OGRFieldType_OGRFieldSubType_mapping)..., +} + +# Conversions from DataType to FType +const DataType2FType = Base.ImmutableDict( + ( + k => FType{v...} for + (k, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping + )..., +) +# GDALDataTypes = Union{keys(DataType2FType)...} +# @generated function convert(::Type{FType}, ::Type{T}) where {T<:GDALDataTypes} +# result = get(DataType2FType, T, missing) +# !ismissing(result) || throw(MethodError(convert, (FType, T))) +# return :($(result)) +# end +# #! Conversion from FType to DataType not implemented because it creates a mess +# #! use get(FType2DataType, FT, missing) instead +const FType2DataType = + Base.ImmutableDict((v => k for (k, v) in DataType2FType)...) +# # # GDALFTypes = Union{keys(FType2DataType)...} +# # @generated function convert(::Type{DataType}, ::Type{T}) where T<:FType +# # result = get(FType2DataType, T, missing) +# # result !== missing || error( +# # "$T is not an FType corresponding to a valid GDAL (OGRFieldType, OGRFieldSubType) couple. \nPlease use one of the following: \n$(join((FType{v...} for (_, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping), "\n"))", +# # ) +# # return :($(result)) +# # end + +# Conversion between Geometry or IGeometry subtypes and GType subtypes +# function convert(::Type{Geometry}, G::Type{GType{T}}) where {T} +# return Geometry{T} +# end +function convert(::Type{IGeometry}, ::Type{GType{T}}) where {T} + return IGeometry{T} +end +# function convert(::Type{GType}, ::Type{Geometry{T}}) where {T} +# return GType{T} +# end +# function convert(::Type{GType}, ::Type{IGeometry{T}}) where {T} +# return GType{T} +# end + +# Conversion between GP_Geometry or GP_IGeometry subtypes and GType subtypes +# function convert(::Type{GP_Geometry}, ::Type{G}) where {G<:GType} +# return GP_Geometry{G} +# end +# function convert(::Type{GType}, ::Type{GP_Geometry{G}}) where {G<:GType} +# return G +# end +# function convert(::Type{GP_IGeometry}, ::Type{G}) where {G<:GType} +# return GP_IGeometry{G} +# end +# function convert(::Type{GType}, ::Type{GP_IGeometry{G}}) where {G<:GType} +# return G +# end + +############################################################################### +# 5.a Methods for GFTP_AbstractGeomFieldDefn # +############################################################################### + +# None found useful to specialize yet + +############################################################################### +# 5.b Methods for FTP_AbstractFieldDefn # +############################################################################### + +@generated function gettype(::FTP_AbstractFieldDefn{FType{T,ST}}) where {T,ST} + return :($T) +end + +@generated function getsubtype( + ::FTP_AbstractFieldDefn{FType{T,ST}}, +) where {T,ST} + return :($ST) +end + +@generated function getfieldtype(::FTP_AbstractFieldDefn{FType{T,ST}}) where {T,ST} + return ST != OFSTNone ? :($ST) : :($T) +end + +############################################################################### +# 5.c Methods for FDP_AbstractFeatureDefn # +############################################################################### + +# Geometries methods +@generated function ngeom(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} + return :($(_ngt(FD))) +end + +# function getgeomdefn( +# fdp_featuredefn::FDP_FeatureDefn{FD}, +# i::Integer = 0, +# ) where {FD<:FDType} +# return GFTP_GeomFieldDefn{_gttypes(FD)[i+1]}( +# GDAL.ogr_fd_getgeomfielddefn(fdp_featuredefn.ptr, i); +# ownedby = fdp_featuredefn, +# ) +# end + +function getgeomdefn( + fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, + i::Integer = 0, +) where {FD<:FDType} + return GFTP_IGeomFieldDefnView{_gttypes(FD)[i+1]}( + GDAL.ogr_fd_getgeomfielddefn(fdp_ifeaturedefnview.ptr, i); + ownedby = fdp_ifeaturedefnview, + ) +end + +# @generated function findgeomindex( +# ::FDP_AbstractFeatureDefn{FD}, +# name::AbstractString = "", +# ) where {FD<:FDType} +# return return quote +# i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) +# return i !== nothing ? i - 1 : nothing +# end +# end + +# Fields methods +@generated function nfield(::FDP_AbstractFeatureDefn{FD}) where {FD<:FDType} + return :($(_nft(FD))) +end + +# function getfielddefn( +# fdp_featuredefn::FDP_FeatureDefn{FD}, +# i::Integer = 0, +# ) where {FD<:FDType} +# return FTP_FieldDefn{_fttypes(FD)[i+1]}( +# GDAL.ogr_fd_getfielddefn(fdp_featuredefn.ptr, i); +# ownedby = fdp_featuredefn, +# ) +# end + +function getfielddefn( + fdp_ifeaturedefnview::FDP_IFeatureDefnView{FD}, + i::Integer = 0, +) where {FD<:FDType} + return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( + GDAL.ogr_fd_getfielddefn(fdp_ifeaturedefnview.ptr, i); + ownedby = fdp_ifeaturedefnview, + ) +end + +# @generated function findfieldindex( +# ::FDP_AbstractFeatureDefn{FD}, +# name::Union{AbstractString,Symbol}, +# ) where {FD<:FDType} +# return return quote +# i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) +# return i !== nothing ? i - 1 : nothing +# end +# end + +function getfeaturedefn(fdp_feature::FDP_IFeature{FD}) where {FD<:FDType} + return FDP_IFeatureDefnView{FD}( + GDAL.ogr_f_getdefnref(fdp_feature.ptr); + ownedby = fdp_feature.ownedby, + ) +end + +############################################################################### +# 5.d Methods for FDP_AbstractFeature # +############################################################################### + +# Geometries +@generated function ngeom(::FDP_AbstractFeature{FD}) where {FD<:FDType} + return :($(_ngt(FD))) +end + +@generated function findgeomindex( + ::FDP_AbstractFeature{FD}, + name::Union{AbstractString,Symbol} = "", +) where {FD<:FDType} + return return quote + i = findfirst(isequal(Symbol(name)), $(_gtnames(FD))) + return i !== nothing ? i - 1 : nothing + end +end + +function stealgeom( + fdp_feature::FDP_AbstractFeature{FD}, + i::Integer, +) where {FD<:FDType} + return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(fdp_feature.ptr)) : + getgeom(fdp_feature, i) +end + +# function stealgeom( +# fdp_feature::FDP_AbstractFeature{FD}, +# name::Union{AbstractString,Symbol}, +# ) where {FD<:FDType} +# i = findgeomindex(fdp_feature, name) +# return i == 0 ? IGeometry(GDAL.ogr_f_stealgeometry(fdp_feature.ptr)) : +# getgeom(fdp_feature, i) +# end + +# Fields +@generated function nfield(::FDP_AbstractFeature{FD}) where {FD<:FDType} + return :($(_nft(FD))) +end + +function getfielddefn(fdp_feature::FDP_IFeature{FD}, i::Integer) where {FD<:FDType} + return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( + GDAL.ogr_f_getfielddefnref(fdp_feature.ptr, i); + ownedby = getfeaturedefn(fdp_feature), + ) +end + +# @generated function findfieldindex( +# ::FDP_AbstractFeature{FD}, +# name::Union{AbstractString,Symbol}, +# ) where {FD<:FDType} +# return quote +# i = findfirst(isequal(Symbol(name)), $(_ftnames(FD))) +# return i !== nothing ? i - 1 : nothing +# end +# end + +@generated function _get_fields_asfuncs(::Type{FD}) where {FD<:FDType} + return ((_FETCHFIELD[T.parameters[1]] for T in _fttypes(FD))...,) +end + +@generated function getfield( + fdp_feature::FDP_AbstractFeature{FD}, + i::Integer, +) where {FD<:FDType} + return quote + return if !isfieldset(fdp_feature, i) + nothing + elseif isfieldnull(fdp_feature, i) + missing + else + $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) + end + end +end + +# @generated function getfield( +# fdp_feature::FDP_AbstractFeature{FD}, +# name::Union{AbstractString,Symbol}, +# ) where {FD<:FDType} +# return quote +# i = findfieldindex(fdp_feature, name) +# return if i === nothing +# missing +# elseif !isfieldset(fdp_feature, i) +# nothing +# elseif isfieldnull(fdp_feature, i) +# missing +# else +# @inbounds $(_get_fields_asfuncs(FD))[i+1](fdp_feature, i) +# end +# end +# end + +function getindex(row::FDP_AbstractFeature{FD}, i::Int) where {FD<:FDType} + ng = ngeom(row) + return if i <= ng + geom = getgeom(row, i - 1) + geom.ptr != C_NULL ? geom : missing + else + getfield(row, i - ng - 1) + end +end + +# function getindex(row::FDP_AbstractFeature{FD}, name::Symbol) where {FD<:FDType} +# field = getfield(row, name) +# if !ismissing(field) +# return field +# end +# geom = getgeom(row, name) +# if geom.ptr != C_NULL +# return geom +# end +# return missing +# end + +function getindex!(row::FDP_AbstractFeature{FD}, i::Int) where {FD<:FDType} + ng = ngeom(row) + return if i <= ng + geom = stealgeom(row, i - 1) + geom.ptr != C_NULL ? geom : missing + else + getfield(row, i - ng - 1) + end +end + +# function getindex!(row::FDP_AbstractFeature{FD}, name::Symbol) where {FD<:FDType} +# field = getfield(row, name) +# if !ismissing(field) +# return field +# end +# geom = stealgeom(row, name) +# if geom.ptr != C_NULL +# return geom +# end +# return missing +# end + +############################################################################### +# 5.e Methods for FDP_AbstractFeatureLayer # +############################################################################### + +@generated function _getFD(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return FD +end + +function getFDPlayer(dataset::AbstractDataset, i::Integer)::FDP_IFeatureLayer + ptr::GDAL.OGRLayerH = GDAL.gdaldatasetgetlayer(dataset.ptr, i) + fd_ptr = GDAL.ogr_l_getlayerdefn(ptr) + FD = _getFDType(fd_ptr) + return FDP_IFeatureLayer{FD}(ptr, ownedby = dataset) +end + +function getFDPlayer(dataset::AbstractDataset) + nlayer(dataset) == 1 || + error("Dataset has multiple layers. Specify the layer number or name") + return getFDPlayer(dataset, 0) +end + +@generated function Base.iterate( + layer::FDP_AbstractFeatureLayer{FD}, + state::Integer = 0, +) where {FD<:FDType} + return quote + layer.ptr == C_NULL && return nothing + state == 0 && resetreading!(layer) + ptr = GDAL.ogr_l_getnextfeature(layer.ptr) + return if ptr == C_NULL + resetreading!(layer) + nothing + else + (FDP_IFeature{$FD}(ptr; ownedby = layer), state + 1) + end + end +end + +# function Base.eltype(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} +# return FDP_IFeature{FD} +# end + +function layerdefn(fdp_layer::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} + return FDP_IFeatureDefnView{FD}( + GDAL.ogr_l_getlayerdefn(fdp_layer.ptr); + ownedby = fdp_layer, + ) +end + +# @generated function findfieldindex( +# ::FDP_AbstractFeatureLayer{FD}, +# field::Union{AbstractString,Symbol}, +# #! Note that exactmatch::Bool is not used in GDAL except when OGRAPISPY_ENABLED is true => dropped +# ) where {FD<:FDType} +# return return quote +# i = findfirst(isequal(Symbol(field)), $(_ftnames(FD))) +# return i !== nothing ? i - 1 : nothing +# end +# end + +# @generated function ngeom(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} +# return :($(_ngt(FD))) +# end + +# @generated function nfield(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} +# return :($(_nft(FD))) +# end + +@generated function gdal_schema( + ::FDP_AbstractFeatureLayer{FD}, +) where {FD<:FDType} + gnames = _gtnames(FD) + fnames = _ftnames(FD) + gtypes = (convert(IGeometry, gt) for gt in _gttypes(FD)) + ftypes = (get(FType2DataType, ft, missing) for ft in _fttypes(FD)) + return Tables.Schema((gnames..., fnames...), (gtypes..., ftypes...)) +end + +####################################################################### +# Tables.columns on FDP_AbstractFeatureLayer with generated functions # +####################################################################### +# - Feature to columns line function: FDPf2c +# - (Tables.)columns function: FDPfillcolumns + +@generated function FDPf2c( + fdp_feature::FDP_AbstractFeature{FD}, + i::Int, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + ng = _ngt(FD) + nf = _nft(FD) + return quote + @inbounds for j in 1:($nf+$ng) + cols[j][i] = getindex(fdp_feature, j) + end + return nothing + end +end + +@generated function FDPf2c!( + fdp_feature::FDP_AbstractFeature{FD}, + i::Int, + cols::Vector{Vector{T} where T}, +) where {FD<:FDType} + ng = _ngt(FD) + nf = _nft(FD) + return quote + @inbounds for j in 1:($nf+$ng) + cols[j][i] = getindex!(fdp_feature, j) + end + return nothing + end +end + +function FDPfillcolumns!( + fdp_layer::FDP_AbstractFeatureLayer{FD}, + cols::Vector{Vector{T} where T}, + preserve::Bool = true, +) where {FD<:FDType} + state = 0 + if preserve + while true + next = iterate(fdp_layer, state) + next === nothing && break + fdp_feature, state = next + FDPf2c(fdp_feature, state, cols) + end + else + while true + next = iterate(fdp_layer, state) + next === nothing && break + fdp_feature, state = next + FDPf2c!(fdp_feature, state, cols) + end + end +end + +function _getcols( + fdp_layer::FDP_AbstractFeatureLayer{FD}; + preserve::Bool, +) where {FD<:FDType} + len = length(fdp_layer) + gdal_sch = gdal_schema(fdp_layer) + ng = _ngt(FD) + cols = [ + [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] + [ + Vector{Union{Missing,Nothing,T}}(missing, len) for + T in gdal_sch.types[ng+1:end] + ] + ] + FDPfillcolumns!(fdp_layer, cols, preserve) + return if VERSION < v"1.7" + NamedTuple{gdal_sch.names}( + NTuple{length(gdal_sch.names),Vector{T} where T}([ + convert( + Vector{promote_type(unique(typeof(e) for e in c)...)}, + c, + ) for c in cols + ]), + ) + else # Shorter code + NamedTuple{gdal_sch.names}( + convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) + for c in cols + ) + end +end + +#*############################################################################# +#* ArchGDAL.Table object # +#*############################################################################# + +struct Table + cols::T where {T<:NamedTuple} +end + +# Table constructors +function Table(layer::AbstractFeatureLayer) + return Table( + _getcols( + FDP_IFeatureLayer{_getFDType(layerdefn(layer).ptr)}( + layer.ptr::GDAL.OGRLayerH; + ownedby = layer.ownedby, + ); + preserve = true, + ), + ) +end +function Table(dataset::AbstractDataset, i::Integer) + return Table(_getcols(getFDPlayer(dataset, i); preserve = false)) +end +function Table(dataset::AbstractDataset) + return Table(_getcols(getFDPlayer(dataset); preserve = false)) +end +function Table(file::String, i::Integer; kwargs...) + return Table( + _getcols(getFDPlayer(read(file; kwargs...), i); preserve = false), + ) +end +function Table(file::String; kwargs...) + return Table(_getcols(getFDPlayer(read(file; kwargs...)); preserve = false)) +end + +#*############################################################################# +#* Table's Tables.jl interface # +#*############################################################################# + +Tables.istable(::Table) = true +Tables.schema(table::Table) = Tables.schema(table.cols) +#TODO after completion of PR #243: Tables.materializer(table::Table) = XXX + +# Table Tables.Columns interface +Tables.columnaccess(::Table) = true +Tables.columns(table::Table) = table.cols + +# Table Tables.Rows interface +Tables.rowaccess(::Table) = true +function Tables.rows(table::Table) + return [ + NamedTuple{ + fieldnames(typeof(table.cols)), + Tuple{eltype.(fieldtypes(typeof(table.cols)))...}, + }( + Base.getindex(c, k) for c in table.cols + ) for k in 1:length(table.cols[begin]) + ] +end \ No newline at end of file diff --git a/src/tables_columns.jl b/src/tables_columns.jl deleted file mode 100644 index 67d453ec..00000000 --- a/src/tables_columns.jl +++ /dev/null @@ -1,365 +0,0 @@ -################################### -# Trial with GeneralizedGenerated # -################################### -# - Runtime generated functions for: -# - geom columns: _get_getgeom_funcs_gg -# - field columns: _get_getfield_funcs_gg -# - Feature to columns line function: FDPf2c_gg -# - (Tables.)columns function: FDPcolumns_gg - -function _get_getgeom_funcs_gg(::Type{FD}) where {FD<:FDType} - NG = _ngt(FD) - f0 = mk_function( - ArchGDAL, - :( - function (row) - return (geomptr = GDAL.ogr_f_stealgeometry(row.ptr)) == C_NULL ? missing : IGeometry(geomptr) - end - ), - ) - return NG == 1 ? (f0,) : - ( - f0, - ( - mk_function( - ArchGDAL, - :( - function (row) - return ( - geomptr = - GDAL.ogr_f_getgeomfieldref(feature.ptr, $k) - ) == C_NULL ? missing : IGeometry(geomptr) - end - ), - ) for k in 2:NG - )..., - ) -end - -function _get_getfield_funcs_gg(::Type{FD}) where {FD<:FDType} - NF = _nft(FD) - fafs = _get_fields_asfuncs(FD) - return ( - ( - mk_function( - ArchGDAL, - :( - function (row) - return !(isfieldset(row, $(k - 1))) ? nothing : - isfieldnull(row, $(k - 1)) ? missing : - $(fafs[k])(row, $(k - 1)) - end - ), - ) for k in 1:NF - )..., - ) -end - -@generated function FDPf2c_gg( - fdp_feature::FDP_AbstractFeature{FD}, - i::Int, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - ng = _ngt(FD) - nf = _nft(FD) - ggfs = _get_getgeom_funcs_gg(FD) - gffs = _get_getfield_funcs_gg(FD) - rex = Expr(:block) - push!(rex.args, Expr(:inbounds, true)) - for j in 1:ng - push!(rex.args, :(cols[$j][i] = $(ggfs[j])(fdp_feature))) - end - for j in ng+1:ng+nf - push!(rex.args, :(cols[$j][i] = $(gffs[j-ng])(fdp_feature))) - end - push!(rex.args, Expr(:inbounds, :pop)) - push!(rex.args, :(nothing)) - return rex -end - -function FDPfillcolumns_gg!( - fdp_layer::FDP_AbstractFeatureLayer{FD}, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - state = 0 - while true - next = iterate(fdp_layer, state) - next === nothing && break - fdp_feature, state = next - FDPf2c_gg(fdp_feature, state, cols) - end -end - -function FDPcolumns_gg( - fdp_layer::FDP_AbstractFeatureLayer{FD}, -) where {FD<:FDType} - len = length(fdp_layer) - gdal_sch = gdal_schema(fdp_layer) - ng = _ngt(FD) - cols = [ - [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - [ - Vector{Union{Missing,Nothing,T}}(missing, len) for - T in gdal_sch.types[ng+1:end] - ] - ] - FDPfillcolumns_gg!(fdp_layer, cols) - return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names)}([ - convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) - for c in cols - ]), - ) -end - -####################################### -# Trial with RuntimeGeneratedFunction # -####################################### -# - Runtime generated functions for: -# - geom columns: _get_getgeom_funcs_rgf -# - field columns: _get_getfield_funcs_rgf -# - Feature to columns line function: FDPf2c_rgf -# - (Tables.)columns function: FDPcolumns_rgf - -function _get_getgeom_funcs_rgf(::Type{FD}) where {FD<:FDType} - NG = _ngt(FD) - f0 = @RuntimeGeneratedFunction( - ArchGDAL, - :( - function (row) - return (geomptr = GDAL.ogr_f_stealgeometry(row.ptr)) == C_NULL ? missing : IGeometry(geomptr) - end - ), - ) - return NG == 1 ? (f0,) : - ( - f0, - ( - @RuntimeGeneratedFunction( - ArchGDAL, - :( - function (row) - return ( - geomptr = - GDAL.ogr_f_getgeomfieldref(feature.ptr, $k) - ) == C_NULL ? missing : IGeometry(geomptr) - end - ), - ) for k in 2:NG - )..., - ) -end - -function _get_getfield_funcs_rgf(::Type{FD}) where {FD<:FDType} - NF = _nft(FD) - fafs = _get_fields_asfuncs(FD) - return ( - ( - @RuntimeGeneratedFunction( - ArchGDAL, - :( - function (row) - return !(isfieldset(row, $(k - 1))) ? nothing : - isfieldnull(row, $(k - 1)) ? missing : - $(fafs[k])(row, $(k - 1)) - end - ), - ) for k in 1:NF - )..., - ) -end - -@generated function FDPf2c_rgf( - fdp_feature::FDP_AbstractFeature{FD}, - i::Int, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - ng = _ngt(FD) - nf = _nft(FD) - ggfs = _get_getgeom_funcs_rgf(FD) - gffs = _get_getfield_funcs_rgf(FD) - rex = Expr(:block) - push!(rex.args, Expr(:inbounds, true)) - for j in 1:ng - push!(rex.args, :(cols[$j][i] = $(ggfs[j])(fdp_feature))) - end - for j in ng+1:ng+nf - push!(rex.args, :(cols[$j][i] = $(gffs[j-ng])(fdp_feature))) - end - push!(rex.args, Expr(:inbounds, :pop)) - push!(rex.args, :(nothing)) - return rex -end - -function FDPfillcolumns_rgf!( - fdp_layer::FDP_AbstractFeatureLayer{FD}, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - state = 0 - while true - next = iterate(fdp_layer, state) - next === nothing && break - fdp_feature, state = next - FDPf2c_rgf(fdp_feature, state, cols) - end -end - -function FDPcolumns_rgf( - fdp_layer::FDP_AbstractFeatureLayer{FD}, -) where {FD<:FDType} - len = length(fdp_layer) - gdal_sch = gdal_schema(fdp_layer) - ng = _ngt(FD) - cols = [ - [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - [ - Vector{Union{Missing,Nothing,T}}(missing, len) for - T in gdal_sch.types[ng+1:end] - ] - ] - FDPfillcolumns_rgf!(fdp_layer, cols) - return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names)}([ - convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) - for c in cols - ]), - ) -end - -######################################## -# Trial with plain generated functions # -######################################## -# - Feature to columns line function: FDPf2c_pg -# - (Tables.)columns function: FDPfillcolumns_pg - -@generated function FDPf2c_pg( - fdp_feature::FDP_AbstractFeature{FD}, - i::Int, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - ng = _ngt(FD) - nf = _nft(FD) - return quote - @inbounds for j in 1:($nf+$ng) - cols[j][i] = Tables.getcolumn(fdp_feature, j) - end - return nothing - end -end - -function FDPfillcolumns_pg!( - fdp_layer::FDP_AbstractFeatureLayer{FD}, - cols::Vector{Vector{T} where T}, -) where {FD<:FDType} - state = 0 - while true - next = iterate(fdp_layer, state) - next === nothing && break - fdp_feature, state = next - FDPf2c_pg(fdp_feature, state, cols) - end -end - -function FDPcolumns_pg( - fdp_layer::FDP_AbstractFeatureLayer{FD}, -) where {FD<:FDType} - len = length(fdp_layer) - gdal_sch = gdal_schema(fdp_layer) - ng = _ngt(FD) - cols = [ - [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - [ - Vector{Union{Missing,Nothing,T}}(missing, len) for - T in gdal_sch.types[ng+1:end] - ] - ] - FDPfillcolumns_pg!(fdp_layer, cols) - return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names)}([ - convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) - for c in cols - ]), - ) -end - -######################################################### -# Best trials for FDP layer selected for Tables.columns # -######################################################### - -function Tables.columns( - fdp_layer::FDP_AbstractFeatureLayer{FD}, -) where {FD<:FDType} - len = length(fdp_layer) - gdal_sch = gdal_schema(fdp_layer) - ng = _ngt(FD) - cols = [ - [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - [ - Vector{Union{Missing,Nothing,T}}(missing, len) for - T in gdal_sch.types[ng+1:end] - ] - ] - FDPfillcolumns_pg!(fdp_layer, cols) - # Below works only with Julia version >= 1.7 - #return = NamedTuple{gdal_sch.names}([ - # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for - # c in cols - # ]) - return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names),Vector{T} where T}([ - convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) - for c in cols - ]), - ) -end - -################################### -# Tables.columns for normal layer # -################################### - -function f2c(feature::AbstractFeature, i::Int, cols::Vector{Vector{T} where T}) - ng = ngeom(feature) - nf = nfield(feature) - @inbounds for j in 1:(nf+ng) - cols[j][i] = Tables.getcolumn(feature, j) - end - return nothing -end - -function fillcolumns!( - layer::AbstractFeatureLayer, - cols::Vector{Vector{T} where T}, -) - state = 0 - while true - next = iterate(layer, state) - next === nothing && break - feature, state = next - f2c(feature, state, cols) - end -end - -function Tables.columns(layer::AbstractFeatureLayer) - len = length(layer) - gdal_sch = gdal_schema(layer) - ng = ngeom(layer) - cols = [ - [Vector{Union{Missing,IGeometry}}(missing, len) for _ in 1:ng] - [ - Vector{Union{Missing,Nothing,T}}(missing, len) for - T in gdal_sch.types[ng+1:end] - ] - ] - fillcolumns!(layer, cols) - # Below works only with Julia version >= 1.7 - #return = NamedTuple{gdal_sch.names}([ - # convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) for - # c in cols - # ]) - return NamedTuple{gdal_sch.names}( - NTuple{length(gdal_sch.names),Vector{T} where T}([ - convert(Vector{promote_type(unique(typeof(e) for e in c)...)}, c) - for c in cols - ]), - ) -end diff --git a/src/types.jl b/src/types.jl index b77a7fbe..25d617d1 100644 --- a/src/types.jl +++ b/src/types.jl @@ -7,102 +7,28 @@ abstract type AbstractSpatialRef end abstract type AbstractDataset end # needs to have a `ptr::GDAL.GDALDatasetH` attribute -#! AbstractOFType could also be a non parameterized abstract type with -#! OFType{OGRFieldType, OGRFieldSubType} instead of -#! OFType{T,OGRFieldSubType} <: AbstractOFType{T} -abstract type AbstractFType{OGRFieldType} end #! NEW abstract type for fields to group field types by OGRFieldType -struct FType{T,OGRFieldSubType} <: AbstractFType{T} end #! NEW type for fields -function getFType(ptr::GDAL.OGRFieldDefnH) - return FType{ - convert(OGRFieldType, GDAL.ogr_fld_gettype(ptr)), - convert(OGRFieldSubType, GDAL.ogr_fld_getsubtype(ptr)), - } -end -abstract type AbstractGType end #! NEW abstract type for geometries -struct GType{OGRwkbGeometryType} <: AbstractGType end #! NEW type for geometries -function getGType(ptr::GDAL.OGRGeomFieldDefnH) - return GType{convert(OGRwkbGeometryType, GDAL.ogr_gfld_gettype(ptr))} -end - -#! NEW simple FeatureDefn type, could later maybe(?) replaced by full FeatureDefn type in the definitions below -#TODO delete: FDType = Tuple{NTuple{NG,GType} where NG,NTuple{NF,FType} where NF} #! Type alias for FD parameter -FDType = Tuple{ - NamedTuple{NG,<:Tuple{Vararg{GType}}} where NG, - NamedTuple{NF,<:Tuple{Vararg{FType}}} where NF, -} -@generated function _ngt(::Type{T}) where {T<:FDType} - return :(length($T.types[1].types)) -end -@generated function _gtnames(::Type{T}) where {T<:FDType} - return :(tuple($T.types[1].parameters[1]...)) -end -@generated function _gttypes(::Type{T}) where {T<:FDType} - return :(tuple($T.types[1].types...)) -end -@generated function _nft(::Type{T}) where {T<:FDType} - return :(length($T.types[2].types)) -end -@generated function _ftnames(::Type{T}) where {T<:FDType} - return :(tuple($T.types[2].parameters[1]...)) -end -@generated function _fttypes(::Type{T}) where {T<:FDType} - return :(tuple($T.types[2].types...)) -end -function _getFDType(ptr::GDAL.OGRFeatureDefnH) #! There no type difference between GDAL.OGRFeatureDefnH and GDAL.OGRLayerH (both Ptr{Cvoid})) and we cannot dispatch on it - ng = GDAL.ogr_fd_getgeomfieldcount(ptr)::Int32 - gflddefn_ptrs = (GDAL.ogr_fd_getgeomfielddefn(ptr, i - 1) for i in 1:ng) - NG = tuple( - ( - Symbol(GDAL.ogr_gfld_getnameref(gflddefn_ptr)::String) for - gflddefn_ptr in gflddefn_ptrs - )..., - ) - TG = Tuple{(getGType(gflddefn_ptr) for gflddefn_ptr in gflddefn_ptrs)...} - nf = GDAL.ogr_fd_getfieldcount(ptr)::Int32 - flddefn_ptrs = (GDAL.ogr_fd_getfielddefn(ptr, i - 1) for i in 1:nf) - NF = tuple( - ( - Symbol(GDAL.ogr_fld_getnameref(flddefn_ptr)::String) for - flddefn_ptr in flddefn_ptrs - )..., - ) - TF = Tuple{(getFType(flddefn_ptr) for flddefn_ptr in flddefn_ptrs)...} - # TF = Tuple{ntuple(i -> getFType(flddefn_ptrs[i]), nf)...} => to use in case later conversion from FType to DataType has to be implemented - return Tuple{NamedTuple{NG,TG},NamedTuple{NF,TF}} -end - abstract type DUAL_AbstractGeometry <: GeoInterface.AbstractGeometry end #! NEW abstract type supertype of AbstractGeometry and GP_AbstractGeometry abstract type AbstractGeometry <: DUAL_AbstractGeometry end -abstract type GP_AbstractGeometry{G<:GType} <: DUAL_AbstractGeometry end #! NEW abstract type to group GP_Geometry instances # needs to have a `ptr::GDAL.OGRGeometryH` attribute abstract type DUAL_AbstractFeatureDefn end #! NEW abstract type supertype of AbstractFeatureDefn and FDP_AbstractFeatureDefn abstract type AbstractFeatureDefn <: DUAL_AbstractFeatureDefn end -abstract type FDP_AbstractFeatureDefn{FD<:FDType} <: DUAL_AbstractFeatureDefn end #! NEW abstract type to group FDP_FeatureDefn type instances # needs to have a `ptr::GDAL.OGRFeatureDefnH` attribute abstract type DUAL_AbstractFeatureLayer end #! NEW abstract type supertype of AbstractFeatureLayer and FDP_AbstractFeatureLayer abstract type AbstractFeatureLayer <: DUAL_AbstractFeatureLayer end -abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer end #! NEW abstract type to group FDP_FeatureLayer type instances # needs to have a `ptr::GDAL.OGRLayerH` attribute -@generated function _getFD(::FDP_AbstractFeatureLayer{FD}) where {FD<:FDType} - return FD -end abstract type DUAL_AbstractFeature end #! NEW abstract type supertype of AbstractFeature and FDP_AbstractFeature abstract type AbstractFeature <: DUAL_AbstractFeature end #! NEW abstract type to group Feature and IFeature (if created) -abstract type FDP_AbstractFeature{FD<:FDType} <: DUAL_AbstractFeature end #! NEW abstract type to group FDP_Feature type instances # needs to have a `ptr::GDAL.OGRFeatureH attribute abstract type DUAL_AbstractFieldDefn end #! NEW abstract type, supertype of AbstractFieldDefn and FTP_AbstractFieldDefn abstract type AbstractFieldDefn <: DUAL_AbstractFieldDefn end -abstract type FTP_AbstractFieldDefn{FT<:FType} <: DUAL_AbstractFieldDefn end #! NEW abstract type to group FTP_FieldDefn type instances # needs to have a `ptr::GDAL.OGRFieldDefnH` attribute abstract type DUAL_AbstractGeomFieldDefn end #! NEW abstract type, supertype of AbstractGeomFieldDefn and GFTP_AbstractGeomFieldDefn abstract type AbstractGeomFieldDefn <: DUAL_AbstractGeomFieldDefn end -abstract type GFTP_AbstractGeomFieldDefn{GFT<:GType} <: - DUAL_AbstractGeomFieldDefn end #! NEW abstract type to group OGTP_FieldDefn type instances # needs to have a `ptr::GDAL.OGRGeomFieldDefnH` attribute abstract type AbstractRasterBand{T} <: AbstractDiskArray{T,2} end @@ -150,34 +76,6 @@ mutable struct IFieldDefnView <: AbstractFieldDefn end end -#! NEW FTP_FieldDefn -mutable struct FTP_FieldDefn{FT} <: FTP_AbstractFieldDefn{FT} - ptr::GDAL.OGRFieldDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} - - function FTP_FieldDefn{FT}( - ptr::GDAL.OGRFieldDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, - ) where {FT<:FType} - return new(ptr, ownedby) - end -end - -#! NEW FTP_IFieldDefnView -mutable struct FTP_IFieldDefnView{FT} <: FTP_AbstractFieldDefn{FT} - ptr::GDAL.OGRFieldDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} - - function FTP_IFieldDefnView{FT}( - ptr::GDAL.OGRFieldDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, - ) where {FT<:FType} - ftp_ifielddefnview = new(ptr, ownedby) - finalizer(destroy, ftp_ifielddefnview) - return ftp_ifielddefnview - end -end - mutable struct GeomFieldDefn <: AbstractGeomFieldDefn ptr::GDAL.OGRGeomFieldDefnH spatialref::AbstractSpatialRef @@ -200,38 +98,6 @@ mutable struct IGeomFieldDefnView <: AbstractGeomFieldDefn end end -#! NEW GFTP_GeomFieldDefn -mutable struct GFTP_GeomFieldDefn{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} - ptr::GDAL.OGRGeomFieldDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} - spatialref::Union{Nothing,AbstractSpatialRef} - - function GFTP_GeomFieldDefn{GFT}( - ptr::GDAL.OGRGeomFieldDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, - spatialref::Union{Nothing,AbstractSpatialRef} = nothing, - ) where {GFT<:GType} - return new(ptr, ownedby, spatialref) - end -end - -#! NEW GFTP_IGeomFieldDefnView -mutable struct GFTP_IGeomFieldDefnView{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} - ptr::GDAL.OGRGeomFieldDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} - spatialref::Union{Nothing,AbstractSpatialRef} - - function GFTP_IGeomFieldDefnView{GFT}( - ptr::GDAL.OGRGeomFieldDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureDefn} = nothing, - spatialref::Union{Nothing,AbstractSpatialRef} = nothing, - ) where {GFT<:GType} - gftp_igeomfielddefnview = new(ptr, ownedby, spatialref) - finalizer(destroy, gftp_igeomfielddefnview) - return gftp_igeomfielddefnview - end -end - mutable struct RasterAttrTable ptr::GDAL.GDALRasterAttributeTableH end @@ -300,80 +166,6 @@ mutable struct IFeatureDefnView <: AbstractFeatureDefn end end -#! NEW FeatureDefn parameterized FeatureDefn -mutable struct FDP_FeatureDefn{FD} <: FDP_AbstractFeatureDefn{FD} - ptr::GDAL.OGRFeatureDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} - - function FDP_FeatureDefn{FD}( - ptr::GDAL.OGRFeatureDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, - ) where {FD<:FDType} - return new(ptr, ownedby) - end -end - -#! NEW FeatureDefn parameterized IFeatureDefnView -mutable struct FDP_IFeatureDefnView{FD} <: FDP_AbstractFeatureDefn{FD} - ptr::GDAL.OGRFeatureDefnH - ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} - - function FDP_IFeatureDefnView{FD}( - ptr::GDAL.OGRFeatureDefnH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} = nothing, - ) where {FD<:FDType} - fdp_ifeaturedefnview = new(ptr, ownedby) - finalizer(destroy, fdp_ifeaturedefnview) - return fdp_ifeaturedefnview - end -end - -#! NEW FeatureDefn parameterized Feature -mutable struct FDP_Feature{FD} <: FDP_AbstractFeature{FD} - ptr::GDAL.OGRFeatureH - ownedby::Union{Nothing,FDP_AbstractFeatureLayer} - - function FDP_Feature{FD}( - ptr::GDAL.OGRFeatureH = C_NULL; - ownedby::Union{Nothing,FDP_AbstractFeatureLayer} = nothing, - ) where {FD<:FDType} - return new(ptr, ownedby) - end -end -#TODO: Add a ifeatureview on the model of ifeaturedefnview? - -#! NEW FeatureDefn parameterized FeatureLayer -mutable struct FDP_FeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} - ptr::GDAL.OGRLayerH - ownedby::AbstractDataset - spatialref::Union{Nothing,AbstractSpatialRef} - - function FDP_FeatureLayer{FD}( - ptr::GDAL.OGRLayerH = C_NULL; - ownedby::AbstractDataset = Dataset(), - spatialref::Union{Nothing,AbstractSpatialRef} = nothing, - ) where {FD<:FDType} - return new(ptr, ownedby, spatialref) - end -end - -#! NEW FeatureDefn parameterized IFeatureLayer -mutable struct FDP_IFeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} - ptr::GDAL.OGRLayerH - ownedby::AbstractDataset - spatialref::Union{Nothing,AbstractSpatialRef} - - function FDP_IFeatureLayer{FD}( - ptr::GDAL.OGRLayerH = C_NULL; - ownedby::AbstractDataset = Dataset(), - spatialref::Union{Nothing,AbstractSpatialRef} = nothing, - ) where {FD<:FDType} - layer = new(ptr, ownedby, spatialref) - finalizer(destroy, layer) - return layer - end -end - "Fetch the pixel data type for this band." pixeltype(ptr::GDAL.GDALRasterBandH)::DataType = convert(GDALDataType, GDAL.gdalgetrasterdatatype(ptr)) @@ -437,27 +229,6 @@ mutable struct Geometry{OGRwkbGeometryType} <: AbstractGeometry end _geomtype(::Geometry{T}) where {T} = T -function _inferGType(ptr::GDAL.OGRGeometryH = C_NULL)::Type{<:GType} - return if ptr != C_NULL - GType{OGRwkbGeometryType(Int32(GDAL.ogr_g_getgeometrytype(ptr)))} - else - GType{wkbUnknown} - end -end - -#! NEW Geometry -mutable struct GP_Geometry{G} <: GP_AbstractGeometry{G} - ptr::GDAL.OGRGeometryH - ownedby::Union{Nothing,DUAL_AbstractFeature} - - function GP_Geometry{G}( - ptr::GDAL.OGRGeometryH = C_NULL, - ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, - ) where {G<:GType} - return new{_inferGType(ptr)}(ptr, ownedby) - end -end - mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry ptr::GDAL.OGRGeometryH @@ -469,21 +240,6 @@ mutable struct IGeometry{OGRwkbGeometryType} <: AbstractGeometry end _geomtype(::IGeometry{T}) where {T} = T -#! NEW IGeometry -mutable struct GP_IGeometry{G} <: GP_AbstractGeometry{G} - ptr::GDAL.OGRGeometryH - ownedby::Union{Nothing,DUAL_AbstractFeature} - - function GP_IGeometry{G}( - ptr::GDAL.OGRGeometryH = C_NULL, - ownedby::Union{Nothing,DUAL_AbstractFeature} = nothing, - ) where {G<:GType} - gp_igeometry = new{_inferGType(ptr)}(ptr, ownedby) - finalizer(destroy, gp_igeometry) - return gp_igeometry - end -end - mutable struct ColorTable ptr::GDAL.GDALColorTableH end @@ -544,57 +300,6 @@ end OFTInteger64List::GDAL.OFTInteger64List, ) -# Default DataType = LAST, for duplicated (oftid, ofstid) values -const DataType_2_OGRFieldType_OGRFieldSubType_mapping = Base.ImmutableDict( - Bool => (OFTInteger, OFSTBoolean), - Int8 => (OFTInteger, OFSTNone), - Int16 => (OFTInteger, OFSTInt16), - Int32 => (OFTInteger, OFSTNone), # Default OFTInteger - Vector{Bool} => (OFTIntegerList, OFSTBoolean), - Vector{Int16} => (OFTIntegerList, OFSTInt16), - Vector{Int32} => (OFTIntegerList, OFSTNone), # Default OFTIntegerList - Float16 => (OFTReal, OFSTNone), - Float32 => (OFTReal, OFSTFloat32), - Float64 => (OFTReal, OFSTNone), # Default OFTReal - Vector{Float16} => (OFTRealList, OFSTNone), - Vector{Float32} => (OFTRealList, OFSTFloat32), - Vector{Float64} => (OFTRealList, OFSTNone), # Default OFTRealList - String => (OFTString, OFSTNone), - Vector{String} => (OFTStringList, OFSTNone), - Vector{UInt8} => (OFTBinary, OFSTNone), - Dates.Date => (OFTDate, OFSTNone), - Dates.Time => (OFTTime, OFSTNone), - Dates.DateTime => (OFTDateTime, OFSTNone), - Int64 => (OFTInteger64, OFSTNone), - Vector{Int64} => (OFTInteger64List, OFSTNone), -) - -# Conversions from DataType to FType -const DataType2FType = Base.ImmutableDict( - ( - k => FType{v...} for - (k, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping - )..., -) -GDALDataTypes = Union{keys(DataType2FType)...} -@generated function convert(::Type{FType}, ::Type{T}) where {T<:GDALDataTypes} - result = get(DataType2FType, T, missing) - !ismissing(result) || throw(MethodError(convert, (FType, T))) - return :($(result)) -end -# Conversion from FType to DataType not implemented cause it creates a mess -# use get(FType2DataType, FT, missing) instead -const FType2DataType = - Base.ImmutableDict((v => k for (k, v) in DataType2FType)...) -# # GDALFTypes = Union{keys(FType2DataType)...} -# @generated function convert(::Type{DataType}, ::Type{T}) where T<:FType -# result = get(FType2DataType, T, missing) -# result !== missing || error( -# "$T is not an FType corresponding to a valid GDAL (OGRFieldType, OGRFieldSubType) couple. \nPlease use one of the following: \n$(join((FType{v...} for (_, v) in DataType_2_OGRFieldType_OGRFieldSubType_mapping), "\n"))", -# ) -# return :($(result)) -# end - @convert( OGRFieldType::DataType, OFTInteger::Bool, @@ -739,81 +444,6 @@ const FType2DataType = OGRSTUInches::GDAL.OGRSTUInches, ) -# @convert( -# OGRwkbGeometryType::GDAL.OGRwkbGeometryType, -# wkbUnknown::GDAL.wkbUnknown, -# wkbPoint::GDAL.wkbPoint, -# wkbLineString::GDAL.wkbLineString, -# wkbPolygon::GDAL.wkbPolygon, -# wkbMultiPoint::GDAL.wkbMultiPoint, -# wkbMultiLineString::GDAL.wkbMultiLineString, -# wkbMultiPolygon::GDAL.wkbMultiPolygon, -# wkbGeometryCollection::GDAL.wkbGeometryCollection, -# wkbCircularString::GDAL.wkbCircularString, -# wkbCompoundCurve::GDAL.wkbCompoundCurve, -# wkbCurvePolygon::GDAL.wkbCurvePolygon, -# wkbMultiCurve::GDAL.wkbMultiCurve, -# wkbMultiSurface::GDAL.wkbMultiSurface, -# wkbCurve::GDAL.wkbCurve, -# wkbSurface::GDAL.wkbSurface, -# wkbPolyhedralSurface::GDAL.wkbPolyhedralSurface, -# wkbTIN::GDAL.wkbTIN, -# wkbTriangle::GDAL.wkbTriangle, -# wkbNone::GDAL.wkbNone, -# wkbLinearRing::GDAL.wkbLinearRing, -# wkbCircularStringZ::GDAL.wkbCircularStringZ, -# wkbCompoundCurveZ::GDAL.wkbCompoundCurveZ, -# wkbCurvePolygonZ::GDAL.wkbCurvePolygonZ, -# wkbMultiCurveZ::GDAL.wkbMultiCurveZ, -# wkbMultiSurfaceZ::GDAL.wkbMultiSurfaceZ, -# wkbCurveZ::GDAL.wkbCurveZ, -# wkbSurfaceZ::GDAL.wkbSurfaceZ, -# wkbPolyhedralSurfaceZ::GDAL.wkbPolyhedralSurfaceZ, -# wkbTINZ::GDAL.wkbTINZ, -# wkbTriangleZ::GDAL.wkbTriangleZ, -# wkbPointM::GDAL.wkbPointM, -# wkbLineStringM::GDAL.wkbLineStringM, -# wkbPolygonM::GDAL.wkbPolygonM, -# wkbMultiPointM::GDAL.wkbMultiPointM, -# wkbMultiLineStringM::GDAL.wkbMultiLineStringM, -# wkbMultiPolygonM::GDAL.wkbMultiPolygonM, -# wkbGeometryCollectionM::GDAL.wkbGeometryCollectionM, -# wkbCircularStringM::GDAL.wkbCircularStringM, -# wkbCompoundCurveM::GDAL.wkbCompoundCurveM, -# wkbCurvePolygonM::GDAL.wkbCurvePolygonM, -# wkbMultiCurveM::GDAL.wkbMultiCurveM, -# wkbMultiSurfaceM::GDAL.wkbMultiSurfaceM, -# wkbCurveM::GDAL.wkbCurveM, -# wkbSurfaceM::GDAL.wkbSurfaceM, -# wkbPolyhedralSurfaceM::GDAL.wkbPolyhedralSurfaceM, -# wkbTINM::GDAL.wkbTINM, -# wkbTriangleM::GDAL.wkbTriangleM, -# wkbPointZM::GDAL.wkbPointZM, -# wkbLineStringZM::GDAL.wkbLineStringZM, -# wkbPolygonZM::GDAL.wkbPolygonZM, -# wkbMultiPointZM::GDAL.wkbMultiPointZM, -# wkbMultiLineStringZM::GDAL.wkbMultiLineStringZM, -# wkbMultiPolygonZM::GDAL.wkbMultiPolygonZM, -# wkbGeometryCollectionZM::GDAL.wkbGeometryCollectionZM, -# wkbCircularStringZM::GDAL.wkbCircularStringZM, -# wkbCompoundCurveZM::GDAL.wkbCompoundCurveZM, -# wkbCurvePolygonZM::GDAL.wkbCurvePolygonZM, -# wkbMultiCurveZM::GDAL.wkbMultiCurveZM, -# wkbMultiSurfaceZM::GDAL.wkbMultiSurfaceZM, -# wkbCurveZM::GDAL.wkbCurveZM, -# wkbSurfaceZM::GDAL.wkbSurfaceZM, -# wkbPolyhedralSurfaceZM::GDAL.wkbPolyhedralSurfaceZM, -# wkbTINZM::GDAL.wkbTINZM, -# wkbTriangleZM::GDAL.wkbTriangleZM, -# wkbPoint25D::GDAL.wkbPoint25D, -# wkbLineString25D::GDAL.wkbLineString25D, -# wkbPolygon25D::GDAL.wkbPolygon25D, -# wkbMultiPoint25D::GDAL.wkbMultiPoint25D, -# wkbMultiLineString25D::GDAL.wkbMultiLineString25D, -# wkbMultiPolygon25D::GDAL.wkbMultiPolygon25D, -# wkbGeometryCollection25D::GDAL.wkbGeometryCollection25D, -# ) - # Conversions below assume that both # - OGRwkbGeometryType Enum instances and # - GDAL.OGRwkbGeometryType CEnum.Cenum instances @@ -825,34 +455,6 @@ function convert(::Type{GDAL.OGRwkbGeometryType}, ogtinst::OGRwkbGeometryType) return GDAL.OGRwkbGeometryType(Integer(ogtinst)) end -# Conversion between Geometry or IGeometry subtypes and GType subtypes -function convert(::Type{Geometry}, G::Type{GType{T}}) where {T} - return Geometry{T} -end -function convert(::Type{IGeometry}, ::Type{GType{T}}) where {T} - return IGeometry{T} -end -function convert(::Type{GType}, ::Type{Geometry{T}}) where {T} - return GType{T} -end -function convert(::Type{GType}, ::Type{IGeometry{T}}) where {T} - return GType{T} -end - -# Conversion between GP_Geometry or GP_IGeometry subtypes and GType subtypes -function convert(::Type{GP_Geometry}, ::Type{G}) where {G<:GType} - return GP_Geometry{G} -end -function convert(::Type{GType}, ::Type{GP_Geometry{G}}) where {G<:GType} - return G -end -function convert(::Type{GP_IGeometry}, ::Type{G}) where {G<:GType} - return GP_IGeometry{G} -end -function convert(::Type{GType}, ::Type{GP_IGeometry{G}}) where {G<:GType} - return G -end - function basetype(gt::OGRwkbGeometryType)::OGRwkbGeometryType wkbGeomType = convert(GDAL.OGRwkbGeometryType, gt) wkbGeomType &= (~0x80000000) # Remove 2.5D flag. diff --git a/test/runtests.jl b/test/runtests.jl index e0ac72a6..c95ec859 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,6 +10,7 @@ include("remotefiles.jl") include("test_doctest.jl") include("test_convert.jl") include("test_tables.jl") + include("test_tables2.jl") include("test_gdal_tutorials.jl") include("test_geometry.jl") include("test_types.jl") diff --git a/test/test_tables2.jl b/test/test_tables2.jl new file mode 100644 index 00000000..eb55d6d3 --- /dev/null +++ b/test/test_tables2.jl @@ -0,0 +1,374 @@ +using Test +import ArchGDAL +# const +AG = ArchGDAL +using Dates +using Tables + +@testset "Unit testing of parameterized types" begin + @testset "FType, GType and FDType helper functions" begin + FD = Tuple{ + NamedTuple{(Symbol(""),),Tuple{AG.GType{AG.wkbLineString}}}, + NamedTuple{ + (:gid, :roadcode), + Tuple{ + AG.FType{AG.OFTInteger,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + }, + }, + } + @test AG._ngt(FD) == 1 + @test AG._gtnames(FD) === (Symbol(""),) + @test AG._gttypes(FD) === (AG.GType{AG.wkbLineString},) + @test AG._nft(FD) == 2 + @test AG._ftnames(FD) === (:gid, :roadcode) + @test AG._fttypes(FD) === ( + AG.FType{AG.OFTInteger,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + ) + + @test AG.getGType( + AG.getgeomdefn( + AG.layerdefn( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ), + ).ptr, + ) == AG.GType{AG.wkbUnknown} + + @test AG.getFType( + AG.getfielddefn( + AG.layerdefn( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ), + ).ptr, + ) == AG.FType{AG.OFTString,AG.OFSTNone} + + @test AG._getFDType( + AG.layerdefn( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ).ptr, + ) == Tuple{ + NamedTuple{ + (:point, :linestring), + Tuple{AG.GType{AG.wkbUnknown},AG.GType{AG.wkbUnknown}}, + }, + NamedTuple{ + (:id, :zoom, :location), + Tuple{ + AG.FType{AG.OFTString,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + }, + }, + } + + @test AG._getFD( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ) == Tuple{ + NamedTuple{ + (:point, :linestring), + Tuple{AG.GType{AG.wkbUnknown},AG.GType{AG.wkbUnknown}}, + }, + NamedTuple{ + (:id, :zoom, :location), + Tuple{ + AG.FType{AG.OFTString,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + AG.FType{AG.OFTString,AG.OFSTNone}, + }, + }, + } + end + + @testset "Parameterized types constructors and destructors" begin + @testset "Tests for FTP_AbstractFieldDefn" begin + fielddefn = AG.getfielddefn( + AG.layerdefn( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ), + ) + @test AG.getname(fielddefn) == "id" + @test AG.gettype(fielddefn) == AG.OFTString + @test AG.getsubtype(fielddefn) == AG.OFSTNone + end + + @testset "Tests for GFTP_AbstractGeomFieldDefn" begin + geomdefn = AG.getgeomdefn( + AG.layerdefn( + AG.getFDPlayer( + AG.read( + "data/multi_geom.csv", + options = [ + "GEOM_POSSIBLE_NAMES=point,linestring", + "KEEP_GEOM_COLUMNS=NO", + ], + ), + 0, + ), + ), + ) + @test AG.getname(geomdefn) == "point" + @test AG.gettype(geomdefn) == AG.wkbUnknown + end + end +end + +@testset "Unit testing of parametric feature layer types and associated methods" begin + fdp_layer = AG.getFDPlayer(AG.read("data/unset_null_testcase.geojson")) + + # Parametric types display tests + @test sprint(print, fdp_layer) == + "Layer: unset_null_testcase\n Geometry 0 (): [wkbPoint], POINT (100 0), POINT (100.2785 0.0893), ...\n Field 0 (FID): [OFTReal], 2.0, 3.0, 0.0, 3.0\n Field 1 (pointname): [OFTString], point-a, nothing, missing, b\n" + @test sprint(print, AG.layerdefn(fdp_layer)) == + " Geometry (index 0): (wkbPoint)\n Field (index 0): FID (OFTReal)\n Field (index 1): pointname (OFTString)\n" + @test sprint(print, iterate(fdp_layer)[1]) == + "Feature\n (index 0) geom => POINT\n (index 0) FID => 2.0\n (index 1) pointname => point-a\n" + + # Tests on methods for DUAL_xxx abstract types + @test Base.IteratorSize(fdp_layer) == Base.SizeUnknown() + @test Base.length(fdp_layer) == 4 + # Create test data file + geojson_string = """ + { + "type": "FeatureCollection", + "crs": { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:OGC:1.3:CRS84" + } + }, + "features": [ + { + "type": "Feature", + "properties": { + "int": $(typemax(Int32)), + "int64": $(typemax(Int64)), + "double": $(floatmax(Float64)), + "string": "Hello", + "intlist": [1, $(typemax(Int32))], + "int64list": [1, $(typemax(Int64))], + "asdoublelist": [1.0, $(floatmax(Float64))], + "asstringlist": ["Hello", "World"], + "asbinary": "Hello", + "asdatetime": "2022-01-14T07:10:01", + }, + "geometry": {"type": "Point","coordinates": [100.0,0.0]} + } + ] + } + """ + open("data/test_DUALxxx_methods.geojson", "w") do io + return print(io, geojson_string) + end + fdp_feature = + iterate(AG.getFDPlayer(AG.read("data/test_DUALxxx_methods.geojson")))[1] + # Tests on methods for DUAL_xxx abstract types + @test AG.asint(fdp_feature, 0) == typemax(Int32) + @test AG.asint64(fdp_feature, 1) == typemax(Int64) + @test AG.asdouble(fdp_feature, 2) == floatmax(Float64) + @test AG.asstring(fdp_feature, 3) == "Hello" + @test AG.asintlist(fdp_feature, 4) == [1, typemax(Int32)] + @test AG.asint64list(fdp_feature, 5) == [1, typemax(Int64)] + @test AG.asdoublelist(fdp_feature, 6) == [1.0, floatmax(Float64)] + @test AG.asstringlist(fdp_feature, 7) == ["Hello", "World"] + @test AG.asbinary(fdp_feature, 8) == Vector{UInt8}("Hello") + @test AG.asdatetime(fdp_feature, 9) == DateTime(2022, 1, 14, 7, 10, 1) + @test AG.getfield(fdp_feature, nothing) === missing + @test AG.toWKT(AG.getgeom(fdp_feature, 0)) == "POINT (100 0)" + @test AG.toWKT(AG.getgeom(fdp_feature, "")) == "POINT (100 0)" + # Detete test data file + isfile("data/test_DUALxxx_methods.geojson") && + rm("data/test_DUALxxx_methods.geojson") +end + +@testset "Unit testing of Table object and its Tables.jl interface" begin + # Helper functions + toWKT_withmissings(::Missing) = missing + toWKT_withmissings(x::AG.AbstractGeometry) = AG.toWKT(x) + toWKT_withmissings(x::Any) = x + function toWKT_withmissings( + x::T, + ) where {T<:NamedTuple{N,<:Tuple{Vararg{Vector}}}} where {N} + return NamedTuple([k => toWKT_withmissings.(x[k]) for k in keys(x)]) + end + + @testset "Unit testing of Table object constructors" begin + # Table constructor from AG standard layer type + @test string( + toWKT_withmissings( + AG.Table( + AG.getlayer(AG.read("data/unset_null_testcase.geojson")), + ).cols, + ), + ) == string( + NamedTuple([ + Symbol("") => Union{Missing,String}[ + "POINT (100 0)", + "POINT (100.2785 0.0893)", + "POINT (100 0)", + missing, + ], + :FID => [2.0, 3.0, 0.0, 3.0], + :pointname => Union{Missing,Nothing,String}[ + "point-a", + nothing, + missing, + "b", + ], + ]), + ) + + # Table constructors from from dataset + @test string( + toWKT_withmissings( + AG.Table(AG.read("data/unset_null_testcase.geojson"), 0).cols, + ), + ) == string( + NamedTuple([ + Symbol("") => Union{Missing,String}[ + "POINT (100 0)", + "POINT (100.2785 0.0893)", + "POINT (100 0)", + missing, + ], + :FID => [2.0, 3.0, 0.0, 3.0], + :pointname => Union{Missing,Nothing,String}[ + "point-a", + nothing, + missing, + "b", + ], + ]), + ) + @test string( + toWKT_withmissings( + AG.Table(AG.read("data/unset_null_testcase.geojson")).cols, + ), + ) == string( + NamedTuple([ + Symbol("") => Union{Missing,String}[ + "POINT (100 0)", + "POINT (100.2785 0.0893)", + "POINT (100 0)", + missing, + ], + :FID => [2.0, 3.0, 0.0, 3.0], + :pointname => Union{Missing,Nothing,String}[ + "point-a", + nothing, + missing, + "b", + ], + ]), + ) + + # Table constructors from a file + @test string( + toWKT_withmissings( + AG.Table("data/unset_null_testcase.geojson", 0).cols, + ), + ) == string( + NamedTuple([ + Symbol("") => Union{Missing,String}[ + "POINT (100 0)", + "POINT (100.2785 0.0893)", + "POINT (100 0)", + missing, + ], + :FID => [2.0, 3.0, 0.0, 3.0], + :pointname => Union{Missing,Nothing,String}[ + "point-a", + nothing, + missing, + "b", + ], + ]), + ) + @test string( + toWKT_withmissings( + AG.Table("data/unset_null_testcase.geojson").cols, + ), + ) == string( + NamedTuple([ + Symbol("") => Union{Missing,String}[ + "POINT (100 0)", + "POINT (100.2785 0.0893)", + "POINT (100 0)", + missing, + ], + :FID => [2.0, 3.0, 0.0, 3.0], + :pointname => Union{Missing,Nothing,String}[ + "point-a", + nothing, + missing, + "b", + ], + ]), + ) + end + + @testset "Tables interface for AG.Table" begin + table = AG.Table("data/unset_null_testcase.geojson") + @test Tables.istable(table) == true + @test Tables.schema(table) == Tables.Schema( + (Symbol(""), :FID, :pointname), + Tuple{ + Union{Missing,AG.IGeometry{AG.wkbPoint}}, + Float64, + Union{Missing,Nothing,String}, + }, + ) + @test Tables.rowaccess(table) == true + @test Tables.columnaccess(table) == true + @test string(toWKT_withmissings(Tables.columns(table))) == + string(toWKT_withmissings(table.cols)) + @test string( + toWKT_withmissings(Tables.columntable(Tables.rows(table))), + ) == string(toWKT_withmissings(table.cols)) + end +end From 9feb711a45117bae7316c107211f210704e235dc Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 11:26:55 +0100 Subject: [PATCH 08/14] Added a new test data file for parametric AG types' tests --- test/remotefiles.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/remotefiles.jl b/test/remotefiles.jl index a8fe07de..513ff349 100644 --- a/test/remotefiles.jl +++ b/test/remotefiles.jl @@ -38,6 +38,10 @@ remotefiles = [ "data/utmsmall.tif", "f40dae6e8b5e18f3648e9f095e22a0d7027014bb463418d32f732c3756d8c54f", ), + ( + "data/unset_null_testcase.geojson", + "f4ebb3953ff0852723569d6fb1f8519632f5ff9f413e751fe63742a7f2b365b0" + ), ( "gdalworkshop/world.tif", "b376dc8af62f9894b5050a6a9273ac0763ae2990b556910d35d4a8f4753278bb", From 572e133c57a4b96db41d9b197c552dbb924fdc85 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 11:51:37 +0100 Subject: [PATCH 09/14] Added a second new test data file for parametric AG types' tests --- test/remotefiles.jl | 4 ++++ test/test_tables2.jl | 41 ++--------------------------------------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/test/remotefiles.jl b/test/remotefiles.jl index 513ff349..5acaa51f 100644 --- a/test/remotefiles.jl +++ b/test/remotefiles.jl @@ -42,6 +42,10 @@ remotefiles = [ "data/unset_null_testcase.geojson", "f4ebb3953ff0852723569d6fb1f8519632f5ff9f413e751fe63742a7f2b365b0" ), + ( + "data/test_DUALxxx_methods.geojson", + "a42d0528e49ad8fa170e20d6c7d928d8403c4fd07076aea71f0dafb83c10582c" + ), ( "gdalworkshop/world.tif", "b376dc8af62f9894b5050a6a9273ac0763ae2990b556910d35d4a8f4753278bb", diff --git a/test/test_tables2.jl b/test/test_tables2.jl index eb55d6d3..b863be20 100644 --- a/test/test_tables2.jl +++ b/test/test_tables2.jl @@ -1,7 +1,6 @@ using Test import ArchGDAL -# const -AG = ArchGDAL +const AG = ArchGDAL using Dates using Tables @@ -172,42 +171,9 @@ end # Tests on methods for DUAL_xxx abstract types @test Base.IteratorSize(fdp_layer) == Base.SizeUnknown() @test Base.length(fdp_layer) == 4 - # Create test data file - geojson_string = """ - { - "type": "FeatureCollection", - "crs": { - "type": "name", - "properties": { - "name": "urn:ogc:def:crs:OGC:1.3:CRS84" - } - }, - "features": [ - { - "type": "Feature", - "properties": { - "int": $(typemax(Int32)), - "int64": $(typemax(Int64)), - "double": $(floatmax(Float64)), - "string": "Hello", - "intlist": [1, $(typemax(Int32))], - "int64list": [1, $(typemax(Int64))], - "asdoublelist": [1.0, $(floatmax(Float64))], - "asstringlist": ["Hello", "World"], - "asbinary": "Hello", - "asdatetime": "2022-01-14T07:10:01", - }, - "geometry": {"type": "Point","coordinates": [100.0,0.0]} - } - ] - } - """ - open("data/test_DUALxxx_methods.geojson", "w") do io - return print(io, geojson_string) - end + # Tests on methods for DUAL_xxx abstract types fdp_feature = iterate(AG.getFDPlayer(AG.read("data/test_DUALxxx_methods.geojson")))[1] - # Tests on methods for DUAL_xxx abstract types @test AG.asint(fdp_feature, 0) == typemax(Int32) @test AG.asint64(fdp_feature, 1) == typemax(Int64) @test AG.asdouble(fdp_feature, 2) == floatmax(Float64) @@ -221,9 +187,6 @@ end @test AG.getfield(fdp_feature, nothing) === missing @test AG.toWKT(AG.getgeom(fdp_feature, 0)) == "POINT (100 0)" @test AG.toWKT(AG.getgeom(fdp_feature, "")) == "POINT (100 0)" - # Detete test data file - isfile("data/test_DUALxxx_methods.geojson") && - rm("data/test_DUALxxx_methods.geojson") end @testset "Unit testing of Table object and its Tables.jl interface" begin From d28f70cb0fce6eadc28ab579fab75037db0e0de0 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 18:26:05 +0100 Subject: [PATCH 10/14] Added new Tables.jl interface to BenchmarkCI --- benchmark/benchmarks.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index b30b7688..0aa519a1 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -25,15 +25,18 @@ road_shapefile_file = joinpath(road_shapefile_dir, splitpath(road_shapefile_dir)[end] * ".shp") # Benchmarks -SUITE["shapefile_to_table"]["frenchroads_with_GDAL.jl_via_vsizip"] = +SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_via_vsizip"] = @benchmarkable Tables.columns( - AG.getlayer( - AG.read("/vsizip/" * relpath($road_shapefile_ziparchive)), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally - 0, - ), + AG.getlayer(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally ) -SUITE["shapefile_to_table"]["frenchroads_with_GDAL.jl"] = +SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl"] = @benchmarkable Tables.columns(AG.getlayer(AG.read($road_shapefile_file), 0)) +SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_new_Tables.jl_interface_via_vsizip"] = + @benchmarkable Tables.columns( + AG.Table(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally + ) +SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_new_Tables.jl_interface"] = + @benchmarkable Tables.columns(AG.Table($road_shapefile_file)) SUITE["shapefile_to_table"]["frenchroads_with_Shapefile.jl"] = @benchmarkable begin Tables.columns(Shapefile.Table($road_shapefile_file)) From ef382dfd195d8976aae628c918b33bb288482cc4 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 18:42:56 +0100 Subject: [PATCH 11/14] Updated github actions for BenchmarkCI : Julia version --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c4ee141f..dcc1a2d9 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@latest with: - version: 1.6 + version: 1 - uses: julia-actions/julia-buildpkg@latest - name: Install dependencies run: julia -e 'using Pkg; pkg"add PkgBenchmark BenchmarkCI@0.1"' From 535fc5fcfe9e37d3089786cc22cfb835c5be191e Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Fri, 14 Jan 2022 18:53:34 +0100 Subject: [PATCH 12/14] BenchmarkCI: moved from Tables.columns to Tables.columntable for fairness between ArchGDAL and Shapefile --- benchmark/benchmarks.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 0aa519a1..c448b82c 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -25,19 +25,19 @@ road_shapefile_file = joinpath(road_shapefile_dir, splitpath(road_shapefile_dir)[end] * ".shp") # Benchmarks -SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_via_vsizip"] = - @benchmarkable Tables.columns( +SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_vsizip"] = + @benchmarkable Tables.columntable( AG.getlayer(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally ) -SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl"] = - @benchmarkable Tables.columns(AG.getlayer(AG.read($road_shapefile_file), 0)) -SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_new_Tables.jl_interface_via_vsizip"] = - @benchmarkable Tables.columns( +SUITE["shapefile_to_table"]["frenchroads_ArchGDAL"] = + @benchmarkable Tables.columntable(AG.getlayer(AG.read($road_shapefile_file), 0)) +SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_new_Tables_interface_vsizip"] = + @benchmarkable Tables.columntable( AG.Table(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally ) -SUITE["shapefile_to_table"]["frenchroads_with_ArchGDAL.jl_new_Tables.jl_interface"] = - @benchmarkable Tables.columns(AG.Table($road_shapefile_file)) -SUITE["shapefile_to_table"]["frenchroads_with_Shapefile.jl"] = +SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_new_Tables_interface"] = + @benchmarkable Tables.columntable(AG.Table($road_shapefile_file)) +SUITE["shapefile_to_table"]["frenchroads_Shapefile"] = @benchmarkable begin - Tables.columns(Shapefile.Table($road_shapefile_file)) + Tables.columntable(Shapefile.Table($road_shapefile_file)) end From 17bf5d2fc70ff35870822ca6a1586bb1778e1839 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Sat, 15 Jan 2022 07:58:01 +0100 Subject: [PATCH 13/14] Cleaning and code reformatting --- Project.toml | 3 --- benchmark/benchmarks.jl | 11 ++++++----- src/ArchGDAL.jl | 3 --- src/constants.jl | 15 --------------- src/tables2.jl | 11 ++++++++--- test/remotefiles.jl | 8 ++++---- 6 files changed, 18 insertions(+), 33 deletions(-) diff --git a/Project.toml b/Project.toml index 029178a2..7d64a95d 100644 --- a/Project.toml +++ b/Project.toml @@ -11,11 +11,9 @@ ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3" GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" -GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" -RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] @@ -26,6 +24,5 @@ GDAL = "1.3" GeoFormatTypes = "0.3" GeoInterface = "0.4, 0.5" ImageCore = "0.8, 0.9" -RuntimeGeneratedFunctions = "0.5" Tables = "1" julia = "1.6" diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index c448b82c..0c853291 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -30,14 +30,15 @@ SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_vsizip"] = AG.getlayer(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally ) SUITE["shapefile_to_table"]["frenchroads_ArchGDAL"] = - @benchmarkable Tables.columntable(AG.getlayer(AG.read($road_shapefile_file), 0)) + @benchmarkable Tables.columntable( + AG.getlayer(AG.read($road_shapefile_file), 0), + ) SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_new_Tables_interface_vsizip"] = @benchmarkable Tables.columntable( AG.Table(AG.read("/vsizip/" * relpath($road_shapefile_ziparchive))), # relpath is a workaround in case there are spaces in local fullpath (incompatible with /vsizip usage) when benchmarkpkg is run locally ) SUITE["shapefile_to_table"]["frenchroads_ArchGDAL_new_Tables_interface"] = @benchmarkable Tables.columntable(AG.Table($road_shapefile_file)) -SUITE["shapefile_to_table"]["frenchroads_Shapefile"] = - @benchmarkable begin - Tables.columntable(Shapefile.Table($road_shapefile_file)) - end +SUITE["shapefile_to_table"]["frenchroads_Shapefile"] = @benchmarkable begin + Tables.columntable(Shapefile.Table($road_shapefile_file)) +end diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index d39c5b44..622cbfda 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -8,9 +8,6 @@ using Tables: Tables using ImageCore: Normed, N0f8, N0f16, N0f32, ImageCore using ColorTypes: ColorTypes using CEnum -using GeneralizedGenerated -using RuntimeGeneratedFunctions -RuntimeGeneratedFunctions.init(@__MODULE__) const GFT = GeoFormatTypes diff --git a/src/constants.jl b/src/constants.jl index 22bab376..feb3a0b8 100644 --- a/src/constants.jl +++ b/src/constants.jl @@ -1,19 +1,4 @@ const StringList = Ptr{Cstring} -""" - @exported_enum EnumName[::BaseType] value1[=x] value2[=y] - -Call `@enum` on arguments, export `EnumName`` and all its instances -""" -#TODO: debug macro -macro exported_enum(T, syms...) - return esc(quote - @enum($T, $(syms...)) - export $T - for inst in Symbol.(instances($T)) - eval($(Expr(:quote, :(export $(Expr(:$, :inst)))))) - end - end) -end """ The value of `GDALDataType` could be different from `GDAL.GDALDataType`. diff --git a/src/tables2.jl b/src/tables2.jl index 833dcff3..5cce989e 100644 --- a/src/tables2.jl +++ b/src/tables2.jl @@ -455,7 +455,9 @@ end return :($ST) end -@generated function getfieldtype(::FTP_AbstractFieldDefn{FType{T,ST}}) where {T,ST} +@generated function getfieldtype( + ::FTP_AbstractFieldDefn{FType{T,ST}}, +) where {T,ST} return ST != OFSTNone ? :($ST) : :($T) end @@ -581,7 +583,10 @@ end return :($(_nft(FD))) end -function getfielddefn(fdp_feature::FDP_IFeature{FD}, i::Integer) where {FD<:FDType} +function getfielddefn( + fdp_feature::FDP_IFeature{FD}, + i::Integer, +) where {FD<:FDType} return FTP_IFieldDefnView{_fttypes(FD)[i+1]}( GDAL.ogr_f_getfielddefnref(fdp_feature.ptr, i); ownedby = getfeaturedefn(fdp_feature), @@ -906,4 +911,4 @@ function Tables.rows(table::Table) Base.getindex(c, k) for c in table.cols ) for k in 1:length(table.cols[begin]) ] -end \ No newline at end of file +end diff --git a/test/remotefiles.jl b/test/remotefiles.jl index 5acaa51f..a189ac4a 100644 --- a/test/remotefiles.jl +++ b/test/remotefiles.jl @@ -39,12 +39,12 @@ remotefiles = [ "f40dae6e8b5e18f3648e9f095e22a0d7027014bb463418d32f732c3756d8c54f", ), ( - "data/unset_null_testcase.geojson", - "f4ebb3953ff0852723569d6fb1f8519632f5ff9f413e751fe63742a7f2b365b0" + "data/unset_null_testcase.geojson", + "f4ebb3953ff0852723569d6fb1f8519632f5ff9f413e751fe63742a7f2b365b0", ), ( - "data/test_DUALxxx_methods.geojson", - "a42d0528e49ad8fa170e20d6c7d928d8403c4fd07076aea71f0dafb83c10582c" + "data/test_DUALxxx_methods.geojson", + "a42d0528e49ad8fa170e20d6c7d928d8403c4fd07076aea71f0dafb83c10582c", ), ( "gdalworkshop/world.tif", From 2ca3a4c81cc040744058684b32b1722d3ef4db71 Mon Sep 17 00:00:00 2001 From: mathieu17g <72861595+mathieu17g@users.noreply.github.com> Date: Sat, 15 Jan 2022 11:13:34 +0100 Subject: [PATCH 14/14] Updated comments in tables2.jl --- src/tables2.jl | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/tables2.jl b/src/tables2.jl index 5cce989e..390cce3d 100644 --- a/src/tables2.jl +++ b/src/tables2.jl @@ -108,7 +108,8 @@ abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer # 3. Definition of parametric ArchGDAL vector data types # ############################################################################### -#! NEW GFTP_GeomFieldDefn +#! NEW GFTP_GeomFieldDefn and GFTP_IGeomFieldDefnView +#! Unsafe version disabled as there is no usage for Table struct # mutable struct GFTP_GeomFieldDefn{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} # ptr::GDAL.OGRGeomFieldDefnH # ownedby::Union{Nothing,FDP_AbstractFeatureDefn} @@ -131,7 +132,6 @@ abstract type FDP_AbstractFeatureLayer{FD<:FDType} <: DUAL_AbstractFeatureLayer # return nothing # end -#! NEW GFTP_IGeomFieldDefnView mutable struct GFTP_IGeomFieldDefnView{GFT} <: GFTP_AbstractGeomFieldDefn{GFT} ptr::GDAL.OGRGeomFieldDefnH ownedby::Union{Nothing,FDP_AbstractFeatureDefn} @@ -155,7 +155,8 @@ function destroy(gftp_igeomfielddefnview::GFTP_IGeomFieldDefnView) return nothing end -#! NEW FTP_FieldDefn +#! NEW FTP_FieldDefn and FTP_IFieldDefnView +#! Unsafe version disabled as there is no usage for Table struct # mutable struct FTP_FieldDefn{FT} <: FTP_AbstractFieldDefn{FT} # ptr::GDAL.OGRFieldDefnH # ownedby::Union{Nothing,FDP_AbstractFeatureDefn} @@ -175,7 +176,6 @@ end # return nothing # end -#! NEW FTP_IFieldDefnView mutable struct FTP_IFieldDefnView{FT} <: FTP_AbstractFieldDefn{FT} ptr::GDAL.OGRFieldDefnH ownedby::Union{Nothing,FDP_AbstractFeatureDefn} @@ -197,6 +197,7 @@ function destroy(ftp_fielddefn::FTP_IFieldDefnView) end #! NEW FeatureDefn parameterized FeatureDefn and IFeatureDefnView +#! Unsafe version disabled as there is no usage for Table struct # mutable struct FDP_FeatureDefn{FD} <: FDP_AbstractFeatureDefn{FD} # ptr::GDAL.OGRFeatureDefnH # ownedby::Union{Nothing,FDP_AbstractFeatureLayer{FD}} @@ -236,7 +237,8 @@ function destroy(fdp_ifeaturedefnview::FDP_IFeatureDefnView) return nothing end -#! NEW FeatureDefn parameterized Feature +#! NEW FeatureDefn parameterized Feature and IFeature +#! Unsafe version disabled as there is no usage for Table struct # mutable struct FDP_Feature{FD} <: FDP_AbstractFeature{FD} # ptr::GDAL.OGRFeatureH # ownedby::Union{Nothing,FDP_AbstractFeatureLayer} @@ -256,7 +258,6 @@ function destroy(fdp_feature::FDP_AbstractFeature) return nothing end -#! NEW FeatureDefn parameterized IFeature mutable struct FDP_IFeature{FD} <: FDP_AbstractFeature{FD} ptr::GDAL.OGRFeatureH ownedby::Union{Nothing,FDP_AbstractFeatureLayer} @@ -271,7 +272,7 @@ mutable struct FDP_IFeature{FD} <: FDP_AbstractFeature{FD} end end -#! NEW Geometry and IGeometry +#! NEW Geometry and IGeometry => disabled since no performance gain identified yet # abstract type GP_AbstractGeometry{G<:GType} <: GeoInterface.AbstractGeometry end # function _inferGType(ptr::GDAL.OGRGeometryH = C_NULL)::Type{<:GType} @@ -306,7 +307,8 @@ end # end # end -# #! NEW FeatureDefn parameterized FeatureLayer +#! NEW FeatureDefn parameterized FeatureLayer and IFeatureLayer +#! Unsafe version disabled as there is no usage for Table struct # mutable struct FDP_FeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} # ptr::GDAL.OGRLayerH # ownedby::AbstractDataset @@ -321,7 +323,6 @@ end # end # end -#! NEW FeatureDefn parameterized IFeatureLayer mutable struct FDP_IFeatureLayer{FD} <: FDP_AbstractFeatureLayer{FD} ptr::GDAL.OGRLayerH ownedby::Union{Nothing,AbstractDataset} @@ -662,6 +663,7 @@ end # return missing # end +#! getindex which steals the geometry from the feature function getindex!(row::FDP_AbstractFeature{FD}, i::Int) where {FD<:FDType} ng = ngeom(row) return if i <= ng @@ -765,8 +767,9 @@ end ####################################################################### # Tables.columns on FDP_AbstractFeatureLayer with generated functions # ####################################################################### -# - Feature to columns line function: FDPf2c -# - (Tables.)columns function: FDPfillcolumns +# - Feature to columns line function: FDPf2c and FDP2c! (geometry stealing) +# - Feature layer to array of columns : FDPfillcolumns! with geometry stealing option +# - Feature layer to NamedTuple: _getcols with geometry stealing option @generated function FDPf2c( fdp_feature::FDP_AbstractFeature{FD}, @@ -891,6 +894,10 @@ end #*############################################################################# #* Table's Tables.jl interface # #*############################################################################# +# Usage of NamedTuples in Table struct brings native support of Tables.jl interface +# Usage of NamedTuples prevents extremely wide tables with # of columns > 67K +# which is due to Julia compiler limitation +# Should a need arise for larger tables, Table struct would have to be modified Tables.istable(::Table) = true Tables.schema(table::Table) = Tables.schema(table.cols)