Skip to content

Commit

Permalink
Dropped geomcols kwarg and WKT/WKB parsing in src/ and test/
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieu17g committed Nov 28, 2021
1 parent 6ae870c commit e8560e4
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 546 deletions.
184 changes: 9 additions & 175 deletions src/tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,14 @@ function _create_empty_layer_from_AGtypes(
end

"""
_infergeometryorfieldtypes(sch, rows, spgeomcols)
_infergeometryorfieldtypes(sch, rows)
Infer ArchGDAL field and geometry types from schema, `rows`' values (for WKT/WKB cases) and `geomcols` kwarg
Infer ArchGDAL field and geometry types from schema
"""
function _infergeometryorfieldtypes(
sch::Tables.Schema{names,types},
rows,
spgeomcols::Union{Nothing,Vector{String},Vector{Int}},
) where {names,types}
colnames = string.(sch.names)

Expand All @@ -159,159 +158,9 @@ function _infergeometryorfieldtypes(
end
end

#* CANNOT FIND A TESTCASE WHERE `state === nothing` COULD HAPPEN => COMMENTED FOR NOW
# # Return layer with FeatureDefn without any feature if table is empty, even
# # if it has a full featured schema
state = iterate(rows)
# if state === nothing
# (layer, _, _) =
# _create_empty_layer_from_AGtypes(colnames, AGtypes, name)
# return layer
# end

# Search in first rows for WKT strings or WKB binary data until for each
# columns with a compatible type (`String` or `Vector{UInt8}` tested
# through their converted value to `OGRFieldType`, namely: `OFTString` or
# `OFTBinary`), a non `missing` nor `nothing` value is found
maybeWKTcolinds = findall(
T -> T isa Tuple{OGRFieldType,OGRFieldSubType} && T[1] == OFTString,
AGtypes,
)
maybeWKBcolinds = findall(
T -> T isa Tuple{OGRFieldType,OGRFieldSubType} && T[1] == OFTBinary,
AGtypes,
)
if spgeomcols !== nothing
maybeWKTcolinds = maybeWKTcolinds spgeomcols
maybeWKBcolinds = maybeWKBcolinds spgeomcols
end
maybegeomcolinds = maybeWKTcolinds maybeWKBcolinds
if !Base.isempty(maybegeomcolinds)
@assert Base.isempty(maybeWKTcolinds maybeWKBcolinds)
testWKT = !Base.isempty(maybeWKTcolinds)
testWKB = !Base.isempty(maybeWKBcolinds)
maybegeomtypes = Dict(
zip(
maybegeomcolinds,
fill!(Vector{Type}(undef, length(maybegeomcolinds)), Union{}),
),
)
row, st = state
while testWKT || testWKB
if testWKT
for j in maybeWKTcolinds
if (val = row[j]) !== nothing && val !== missing
try
maybegeomtypes[j] = promote_type(
maybegeomtypes[j],
typeof(fromWKT(val)),
)
catch
pop!(maybegeomtypes, j)
end
end
end
maybeWKTcolinds = maybeWKTcolinds keys(maybegeomtypes)
testWKT = !Base.isempty(maybeWKTcolinds)
end
if testWKB
for j in maybeWKBcolinds
if (val = row[j]) !== nothing && val !== missing
try
maybegeomtypes[j] = promote_type(
maybegeomtypes[j],
typeof(fromWKB(val)),
)
catch
pop!(maybegeomtypes, j)
end
end
end
maybeWKBcolinds = maybeWKBcolinds keys(maybegeomtypes)
testWKB = !Base.isempty(maybeWKBcolinds)
end
state = iterate(rows, st)
state === nothing && break
row, st = state
end
state === nothing && begin
WKxgeomcolinds = findall(T -> T != Union{}, maybegeomtypes)
for j in WKxgeomcolinds
AGtypes[j] = (
_convert_cleantype_to_AGtype
_convert_coltype_to_cleantype
)(
maybegeomtypes[j],
)
end
end
end

# Verify after parsing that:
# - there is no column, not specified in `geomcols` kwarg, and found to be
# of a geometry eltype which is not a compatible GDAL field type
# (e.g. `IGeometry` or `GeoInterface.AbstractGeometry`)
# - there is no column specified in `geomcols` kwarg that could not be
# parsed as a geometry column
if spgeomcols !== nothing
foundgeomcols = findall(T -> T isa OGRwkbGeometryType, AGtypes)
if Set(spgeomcols) != Set(foundgeomcols)
diff = setdiff(spgeomcols, foundgeomcols)
if !Base.isempty(diff)
error(
"Column(s) $(join(string.(diff), ", ", " and ")) could not be parsed as geometry column(s)",
)
end
diff = setdiff(foundgeomcols, spgeomcols)
if !Base.isempty(diff)
error(
"Column(s) $(join(string.(diff), ", ", " and ")) is(are) composed of geometry objects that cannot be converted to a GDAL field type.\nConsider adding this(these) column(s) to `geomcols` kwarg or convert their values to WKT/WKB",
)
end
end
end

return AGtypes
end

"""
_check_normalize_geomcols_kwarg(geomcols)
Test coherence of `geomcols` kwarg with table schema
And normalize `geomcols` kwargs with indices of table schema names.
"""
function _check_normalize_geomcols_kwarg(
geomcols::Union{Nothing,Vector{String},Vector{Int}},
colnames = Vector{String},
)::Union{Nothing,Vector{Int}}
if geomcols === nothing
spgeomcols = nothing
elseif geomcols isa Vector{String}
if geomcols colnames
errored_geomcols = setdiff(geomcols, geomcols colnames)
error(
"Column(s) $(join(string.(errored_geomcols), ", ", " and ")) in `geomcols` kwarg is(are) not in table's columns names",
)
else
spgeomcols = findall(s -> s geomcols, colnames)
end
else
@assert geomcols isa Vector{Int}
if geomcols Vector(1:length(colnames))
errored_geomcols =
setdiff(geomcols, geomcols Vector(1:length(colnames)))
error(
"Column(s) $(join(string.(errored_geomcols), ", ", " and ")) in `geomcols` kwarg is(are) not in table's columns indices ranging from 1 to $(length(colnames))",
)
else
spgeomcols = geomcols
end
end

return spgeomcols
end

"""
_fromtable(sch, rows; name)
Expand All @@ -326,23 +175,18 @@ function _fromtable end
Handles the case where names and types in `sch` are different from `nothing`
# Implementation
1. test coherence: of `geomcols` kwarg with table schema
2. convert `rows`'s column types given in `sch` and a normalized version of `geomcols` kwarg, to either geometry types or field types and subtypes
3. split `rows`'s columns into geometry typed columns and field typed columns
4. create layer named `name` in a MEMORY dataset geomfields and fields types inferred from `rows`'s column types
5. populate layer with `rows` values
1. convert `rows`'s column types given in `sch` to either geometry types or field types and subtypes
2. split `rows`'s columns into geometry typed columns and field typed columns
3. create layer named `name` in a MEMORY dataset geomfields and fields types inferred from `rows`'s column types
4. populate layer with `rows` values
"""
function _fromtable(
sch::Tables.Schema{names,types},
rows;
layer_name::String,
geomcols::Union{Nothing,Vector{String},Vector{Int}} = nothing, # Default value set as a convenience for tests
)::IFeatureLayer where {names,types}
# Test coherence of `geomcols` and normalize it with indices for schema names
spgeomcols = _check_normalize_geomcols_kwarg(geomcols, string.(sch.names))

AGtypes = _infergeometryorfieldtypes(sch, rows, spgeomcols)
AGtypes = _infergeometryorfieldtypes(sch, rows)

(layer, geomindices, fieldindices) = _create_empty_layer_from_AGtypes(
string.(sch.names),
Expand Down Expand Up @@ -421,7 +265,6 @@ Construct an IFeatureLayer from a source implementing Tables.jl interface
## Keyword arguments
- `layer_name::String = ""`: name of the layer
- `geomcols::Union{Nothing, Vector{String}, Vector{Int}} = nothing`: if `geomcols` is different from nothing, only the specified columns (by names or number) will be converted to geomfields
## Restrictions
- Source must contains at least one geometry column
Expand Down Expand Up @@ -458,21 +301,12 @@ Layer: towns
Field 2 (location): [OFTString], missing, New Delhi
```
"""
function IFeatureLayer(
table;
layer_name::String = "layer",
geomcols::Union{Nothing,Vector{String},Vector{Int}} = nothing,
)
function IFeatureLayer(table; layer_name::String = "layer")
# Check tables interface's conformance
!Tables.istable(table) &&
throw(DomainError(table, "$table has not a Table interface"))
# Extract table data
rows = Tables.rows(table)
schema = Tables.schema(table)
return _fromtable(
schema,
rows;
layer_name = layer_name,
geomcols = geomcols,
)
return _fromtable(schema, rows; layer_name = layer_name)
end
Loading

0 comments on commit e8560e4

Please sign in to comment.