diff --git a/Project.toml b/Project.toml index 5cc42077..87d491b3 100644 --- a/Project.toml +++ b/Project.toml @@ -6,22 +6,21 @@ desc = "A high level API for GDAL - Geospatial Data Abstraction Library" version = "0.3.1" [deps] -DataStreams = "9a8bc11e-79be-5b39-94d7-1ccc349a1a85" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a" GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] -DataStreams = "0.4.2" GDAL = "1.0.2" GeoInterface = "0.4, 0.5" +Tables = "1" julia = "1" [extras] BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Dates", "Statistics", "Test", "BinaryProvider"] +test = ["BinaryProvider", "Statistics", "Test"] diff --git a/src/ArchGDAL.jl b/src/ArchGDAL.jl index 8b4c0259..2047a16d 100644 --- a/src/ArchGDAL.jl +++ b/src/ArchGDAL.jl @@ -1,7 +1,7 @@ module ArchGDAL import GDAL, GeoInterface - import DataStreams: Data + using Tables: Tables import GeoInterface: coordinates, geotype using Dates @@ -26,7 +26,7 @@ module ArchGDAL include("context.jl") include("base/iterators.jl") include("base/display.jl") - include("datastreams.jl") + include("tables.jl") include("geointerface.jl") mutable struct DriverManager diff --git a/src/tables.jl b/src/tables.jl new file mode 100644 index 00000000..92678a8d --- /dev/null +++ b/src/tables.jl @@ -0,0 +1,68 @@ +struct GeoTable{T <: NamedTuple} <:AbstractVector{T} + parsed_shapefile::T +end + +function geotable(layer::Union{IFeatureLayer, FeatureLayer}, i::Int) + featuredefn = layerdefn(layer) + ngeometries = ngeom(featuredefn) + nfield = ArchGDAL.nfield(featuredefn) + featuredefn = layerdefn(layer) + nfeat = nfeature(layer) + + d = Dict{String, Vector}() + + for field_no in 0:nfield-1 + field = getfielddefn(featuredefn, field_no) + name = getname(field) + typ = _FIELDTYPE[gettype(field)] + d[name] = typ[] + end + + d["geometry"] = IGeometry[] + + for fid in 0:nfeat-1 + getfeature(layer, fid) do feature + for (k, v) in pairs(d) + if k == "geometry" + val = getgeom(feature, 0) + else + val = getfield(feature, k) + end + push!(v, val) + end + end + end + keys_tup = () + for _key in keys(d) + keys_tup = (keys_tup..., Symbol(_key)) + end + vals_tup = Tuple(values(d)) + + #Using the tables interface + RowTable = rowtable(NamedTuple{keys_tup}(vals_tup)) + return GeoTable(reshape(RowTable, (1,length(RowTable)))) +end + +# "Struct representing a singe record in a shapefile" +# struct Row{T} +# ###### +# end + +Tables.istable(::Type{<:GeoTable}) = true +Tables.rowaccess(::Type{<:GeoTable}) = true +Tables.rows(g::GeoTable) = g + +# Base.IteratorSize(::Type{<:GeoTable}) = Base.HasLength() +# Base.length(fc::GeoTable) = length(geotable(g)) +# Base.IteratorEltype(::Type{<:GeoTable}) = Base.HasEltype() + + +# "Iterate over the rows of a Shapefile.Table, yielding a Shapefile.Row for each row" +# Base.iterate(g::GeoTable, st=1) = st > length(g) ? nothing : (Row(st, g), st + 1 + + + + +# Tables.schema(g::geotable) = Tables.Schema() + + diff --git a/test/runtests.jl b/test/runtests.jl index 72fafc0a..a247c651 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,7 +8,7 @@ include("remotefiles.jl") @testset "ArchGDAL" begin cd(dirname(@__FILE__)) do isdir("tmp") || mkpath("tmp") - include("test_datastreams.jl") + include("test_tables.jl") include("test_gdal_tutorials.jl") include("test_geometry.jl") include("test_types.jl") diff --git a/test/test_tables.jl b/test/test_tables.jl new file mode 100644 index 00000000..0abe41f6 --- /dev/null +++ b/test/test_tables.jl @@ -0,0 +1,64 @@ +using Test +import ArchGDAL; const AG = ArchGDAL +using Tables + +# @testset "DataStream Support" begin +# df = AG.read("data/point.geojson") do dataset +# DataStreams.Data.close!(DataStreams.Data.stream!( +# AG.Source(AG.getlayer(dataset,0)), DataStreams.Data.Table +# )) +# end +# @test df.FID == [2.0, 3.0, 0.0, 3.0] +# @test df.pointname == ["point-a", "point-b", "a", "b"] +# @test AG.toWKT.(df.geometry0) == [ +# "POINT (100 0)", +# "POINT (100.2785 0.0893)", +# "POINT (100 0)", +# "POINT (100.2785 0.0893)" +# ] +# end + +dataset = AG.read(joinpath(@__DIR__, "data/point.geojson")) +layer = AG.getlayer(dataset, 0) + +nfeat = AG.nfeature(layer) +nfield = AG.nfield(layer) +featuredefn = AG.layerdefn(layer) +ngeometries = AG.ngeom(featuredefn) + +Tables.istable(layer::AG.AbstractFeatureLayer) = true +Tables.rowaccess(layer::AG.AbstractFeatureLayer) = true + +function Tables.schema(layer::AG.AbstractFeatureLayer) + # TODO include names and types of geometry columns + featuredefn = AG.layerdefn(layer) + fielddefns = (AG.getfielddefn(featuredefn, i) for i in 0:nfield-1) + names = Tuple(AG.getname(fielddefn) for fielddefn in fielddefns) + types = Tuple(AG._FIELDTYPE[AG.gettype(fielddefn)] for fielddefn in fielddefns) + Tables.Schema(names, types) +end + +schema = Tables.schema(layer) + +function Tables.rows(layer::AG.AbstractFeatureLayer) + # TODO return an iterator rather than a vector of NamedTuples + schema = Tables.schema(layer) + T = NamedTuple{schema.names, Tuple{schema.types...}} + AG.resetreading!(layer) + nfeat = AG.nfeature(layer) + nfield = AG.nfield(layer) + rows = T[] + for _ in 1:nfeat + AG.nextfeature(layer) do feature + # AG.getgeom(feature, 0) # TODO + push!(rows, T(AG.getfield(feature, j) for j in 0:nfield-1)) + end + end + return rows +end + +Tables.rows(layer) + +# TODO getcolumn support +# using DataFrames +# DataFrame(layer)