Skip to content

Commit

Permalink
Allow Shapefile to read .zip files
Browse files Browse the repository at this point in the history
We should subscribe to the FileIO interface so we can deal with streaming files as well...
  • Loading branch information
asinghvi17 committed Apr 26, 2024
1 parent a9acd12 commit 6dc7db8
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 7 deletions.
16 changes: 9 additions & 7 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@ OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"

[extensions]
ShapefileMakieExt = "Makie"
ShapefileZipFileExt = "ZipFile"

[compat]
DBFTables = "1.2"
Extents = "0.1"
GeoFormatTypes = "0.4"
GeoInterface = "1.0"
GeoInterfaceMakie = "0.1"
GeoInterfaceRecipes = "1.0"
Makie = "0.20"
Makie = "0.20, 0.21"
OrderedCollections = "1"
RecipesBase = "1"
Tables = "0.2, 1"
julia = "1.9"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"

[extensions]
ShapefileMakieExt = "Makie"

[extras]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Expand Down
39 changes: 39 additions & 0 deletions ext/ShapefileZipFileExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module ShapefileZipFileExt
import ZipFile, Shapefile
import Shapefile: _read_shp_from_zipfile
function _read_shp_from_zipfile(zipfile)
r = ZipFile.Reader(zipfile)
# need to get dbx
shpdata, shxdata, dbfdata, prjdata = nothing, nothing, nothing, nothing
for f in r.files
fn = f.name
lfn = lowercase(fn)
if endswith(lfn, ".shp")
shpdata = IOBuffer(read(f))
elseif endswith(lfn, ".shx")
shxdata = read(f, Shapefile.IndexHandle)
elseif endswith(lfn, ".dbf")
dbfdata = Shapefile.DBFTables.Table(IOBuffer(read(f)))
elseif endswith(lfn, "prj")
prjdata = try
Shapefile.GeoFormatTypes.ESRIWellKnownText(Shapefile.GeoFormatTypes.CRS(), read(f, String))
catch
@warn "Projection file $zipfile/$lfn appears to be corrupted. `nothing` used for `crs`"
nothing
end
end
end
close(r)
@assert shpdata !== nothing
shp = if shxdata !== nothing # we have shxdata/index
read(shpdata, Shapefile.Handle, shxdata)
else
read(shpdata, Shapefile.Handle)
end
if prjdata !== nothing
shp.crs = prjdata
end
return Shapefile.Table(shp, dbfdata)
end

end
17 changes: 17 additions & 0 deletions src/Shapefile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,21 @@ include("extent.jl")
include("plotrecipes.jl")
include("writer.jl")

function __init__()
# Register an error hint, so that if a user tries to read a zipfile and fails, they get a helpful error message
# that includes the ShapefileZipFileExt package.
Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs
if exc.f == _read_shp_from_zipfile
if isnothing(Base.get_extension(Shapefile, :ShapefileZipFileExt))
print(io, "\nPlease load the ")
printstyled(io, "ZipFile", color=:cyan)
println(io, " package to read zipfiles into Shapefile.Table objects.")
println(io, "You can do this by typing: ")
printstyled(io, "using ZipFile", color=:cyan, bold = true)
println(io, "\ninto your REPL or code.")
end
end
end
end

end # module
5 changes: 5 additions & 0 deletions src/table.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ function Table(shp::Handle{T}, dbf::DBFTables.Table) where {T}
Table{T}(shp, dbf)
end
function Table(path::AbstractString)
if endswith(path, ".zip")
return _read_shp_from_zipfile(path)
end
paths = _shape_paths(path)
isfile(paths.shp) || throw(ArgumentError("File not found: $(paths.dbf)"))
isfile(paths.dbf) || throw(ArgumentError("File not found: $(paths.dbf)"))
Expand All @@ -80,6 +83,8 @@ function Table(path::AbstractString)
return Shapefile.Table(shp, dbf)
end

function _read_shp_from_zipfile end

getshp(t::Table) = getfield(t, :shp)
getdbf(t::Table) = getfield(t, :dbf)

Expand Down

0 comments on commit 6dc7db8

Please sign in to comment.