diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2164c8f4..01a46a31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ permissions: contents: read jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -23,14 +23,11 @@ jobs: - ubuntu-latest - macOS-latest - windows-latest - arch: - - x64 steps: - uses: actions/checkout@v5 - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - uses: julia-actions/cache@v2 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 diff --git a/src/io.jl b/src/io.jl index a75ee075..b7db6ed7 100644 --- a/src/io.jl +++ b/src/io.jl @@ -82,7 +82,7 @@ Other supported kwargs are passed to the [ArchGDAL read](https://yeesian.com/Arc The `options` keyword argument can be used to pass GDAL open options. """ function read(driver::ArchGDALDriver, fn::AbstractString; layer = nothing, kwargs...) - _isvalidlocal(fn) || error("File not found.") + _isvalidlocal(fn) || error("Can't find local file $fn.") t = AG.read(fn; kwargs...) do ds ds.ptr == C_NULL && error("Unable to open $fn.") if AG.nlayer(ds) > 1 && isnothing(layer) diff --git a/src/utils.jl b/src/utils.jl index d85d8e32..78bbfc62 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -211,5 +211,9 @@ function _reproject( end function _isvalidlocal(fn) - startswith(fn, "/vsi") || occursin(":", fn) || isfile(fn) || isdir(fn) + startswith(fn, "/vsi") || + startswith(fn, "\\\\") || + occursin("://", fn) || + isfile(fn) || + isdir(fn) end diff --git a/test/runtests.jl b/test/runtests.jl index c3e8ae69..299973ee 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -29,7 +29,7 @@ end @testitem "Read non-existent shapefile" setup = [Setup] begin fne = "/bla.shp" - @test_throws ErrorException("File not found.") GDF.read(fne) + @test_throws ErrorException("Can't find local file /bla.shp.") GDF.read(fne) end @testitem "Read shapefile with layer id" setup = [Setup] begin @@ -211,7 +211,6 @@ end GI.isfeaturecollection(::Vector{<:NamedTuple}) = true GI.geomtrait(::Vector{<:NamedTuple}) = GI.FeatureCollectionTrait() # TODO Make issue GeoInterface.jl GI.crs(::GI.FeatureCollectionTrait, ::Vector{<:NamedTuple}) = nothing - GI.isfeaturecollection(::Vector{<:NamedTuple}) = true GI.geometrycolumns(::Vector{<:NamedTuple}) = (:foo,) @test isfile(GDF.write(tfn, table)) end @@ -250,7 +249,7 @@ end @testitem "Non existing Windows path #78" setup = [Setup] begin wfn = "C:\\non_existing_folder\\non_existing_file.shp" - @test_throws ErrorException("Unable to open $wfn.") GDF.read(wfn) + @test_throws ErrorException("Can't find local file $wfn.") GDF.read(wfn) end @testitem "Shapefile" setup = [Setup] begin @@ -312,8 +311,8 @@ end GDF.write(GDF.ArchGDALDriver(), "test.fgb", df2) end -@testitem "GeoParquet" setup = [Setup] begin - Sys.iswindows() && return # Skip on Windows. See GDAL.jl#146 +# Skip on Windows. See GDAL.jl#146 +@testitem "GeoParquet" setup = [Setup] tags = [:nowindows] begin using GeoParquet fn = joinpath(testdatadir, "example.parquet") df = GDF.read(fn) @@ -333,7 +332,7 @@ end using GeoArrow fn = joinpath(testdatadir, "example-multipolygon_z.arrow") df = GDF.read(fn) - ENV["OGR_ARROW_ALLOW_ALL_DIMS"] = "YES" + AG.setconfigoption("OGR_ARROW_ALLOW_ALL_DIMS", "YES") df2 = GDF.read(GDF.ArchGDALDriver(), fn) @test sort(names(df)) == sort(names(df2)) @test nrow(df) == nrow(df2) @@ -348,19 +347,46 @@ end end @testitem "Combination of drivers" setup = [Setup] begin + broken_combos = [ + (GDF.GeoParquetDriver(), GDF.ShapefileDriver()), # ArgumentError: Shapefiles can only contain geometries of the same type + (GDF.GeoParquetDriver(), GDF.FlatGeobufDriver()), # ICreateFeature: Mismatched geometry type. Feature geometry type is Polygon, expected layer geometry type is Multi Polygon + (GDF.GeoArrowDriver(), GDF.ArchGDALDriver()), # BoundsError: attempt to access @NamedTuple{x::Union{Missing, Float64}, y::Union{Missing, Float64}, z::Union{Missing, Float64}} at index [4] + (GDF.GeoArrowDriver(), GDF.GeoJSONDriver()), # ArgumentError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer + (GDF.GeoArrowDriver(), GDF.ShapefileDriver()), # BoundsError: attempt to access @NamedTuple{x::Union{Missing, Float64}, y::Union{Missing, Float64}, z::Union{Missing, Float64}} at index [4] + (GDF.GeoArrowDriver(), GDF.FlatGeobufDriver()), # BoundsError: attempt to access @NamedTuple{x::Union{Missing, Float64}, y::Union{Missing, Float64}, z::Union{Missing, Float64}} at index [4] + (GDF.GeoArrowDriver(), GDF.GeoArrowDriver()), # BoundsError: attempt to access @NamedTuple{x::Union{Missing, Float64}, y::Union{Missing, Float64}, z::Union{Missing, Float64}} at index [4] + (GDF.GeoArrowDriver(), GDF.GeoParquetDriver()), # UndefVarError: `wkbZM` not defined in `WellKnownGeometry` + (GDF.ArchGDALDriver(), GDF.GeoArrowDriver()), # exception = TypeError: in Vararg, in count, expected Int64, got a value of type Int32 + ] + if Sys.iswindows() + # GDAL.jl#146, GeoParquet on Windows is broken + push!(broken_combos, (GDF.ArchGDALDriver(), GDF.GeoParquetDriver())) + push!(broken_combos, (GDF.ShapefileDriver(), GDF.GeoParquetDriver())) + push!(broken_combos, (GDF.GeoJSONDriver(), GDF.GeoParquetDriver())) + push!(broken_combos, (GDF.FlatGeobufDriver(), GDF.GeoParquetDriver())) + push!(broken_combos, (GDF.GeoArrowDriver(), GDF.GeoParquetDriver())) + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.ArchGDALDriver())) # not sure why this one fails + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.ShapefileDriver())) # not sure why this one fails + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.GeoJSONDriver())) # not sure why this one fails + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.FlatGeobufDriver())) # not sure why this one fails + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.GeoArrowDriver())) # not sure why this one fails + push!(broken_combos, (GDF.GeoParquetDriver(), GDF.GeoParquetDriver())) # not sure why this one fails + end + drivers = [ - (GDF.ArchGDALDriver(), "test.gpkg", true, (;)) - (GDF.GeoJSONDriver(), "test.geojson", true, (;)) - (GDF.ShapefileDriver(), "test.shp", false, (; force = true)) - (GDF.FlatGeobufDriver(), "test.fgb", false, (;)) # No write support yet - (GDF.GeoParquetDriver(), "test_native.parquet", true, (;)) + (GDF.ArchGDALDriver(), "test.gpkg", (;)) + (GDF.GeoJSONDriver(), "test.geojson", (;)) + (GDF.ShapefileDriver(), joinpath(testdatadir, "sites.shp"), (; force = true)) + (GDF.FlatGeobufDriver(), joinpath(testdatadir, "countries.fgb"), (;)) # No write support yet + (GDF.GeoParquetDriver(), joinpath(testdatadir, "example.parquet"), (;)) + (GDF.GeoArrowDriver(), joinpath(testdatadir, "example-multipolygon_z.arrow"), (;)) # Broken ] - for ((driver, fn, can_write), (driver_b, fn_b, can_write_b, kwargs)) in + for ((driver_in, fn_in), (driver_out, fn_out, kwargs)) in Iterators.product(drivers, drivers) - can_write_b || continue - @debug "Testing $driver with $driver_b" - df = GDF.read(driver, fn) - GDF.write(driver_b, "temp" * fn_b, df; kwargs...) + (driver_in, driver_out) in broken_combos && continue + @info "Testing reading with $driver_in and writing with $driver_out." + df = GDF.read(driver_in, fn_in) + GDF.write(driver_out, "temp" * basename(fn_out), df; kwargs...) end end @@ -418,4 +444,6 @@ end @test GI.geometrycolumns(t) == (:geom,) end -@run_package_tests +filter(ti) = !(:nowindows in ti.tags && Sys.iswindows()) + +@run_package_tests filter = filter