Skip to content

Commit

Permalink
Merge branch 'main' into chore(deps)--update-ice-floe-tracker-to-0.6.…
Browse files Browse the repository at this point in the history
…3-registry
  • Loading branch information
hollandjg authored Feb 11, 2025
2 parents 3ed6510 + 18efcf8 commit 42ab7a8
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 96 deletions.
27 changes: 19 additions & 8 deletions IFTPipeline.jl/src/h5.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ function choose_dtype(mx::T) where {T<:Integer}
throw("$mx can't be represented by any of $types")
end


"""
makeh5files(pathtosampleimg, resdir)
Expand Down Expand Up @@ -79,7 +78,9 @@ Each HDF5 file has the following structure:
The `floe_properties` group contains a floe properties matrix `properties` for `labeled_image` and associated `column_names`.
The `index` group contains the spatial coordinates in the source image coordinate reference system (default NSIDC polar stereographic, meters) and geographic coordinates (latitude and longitude, decimal degrees). Estimated satellite overpass time `time` is provided in Unix timestamp format (seconds since 1970-01-01 00:00 UTC).
"""
function makeh5files(; pathtosampleimg::String, resdir::String, iftversion=IceFloeTracker.IFTVERSION)
function makeh5files(;
pathtosampleimg::String, resdir::String, iftversion=IceFloeTracker.IFTVERSION
)
latlondata = getlatlon(pathtosampleimg)

ptpath = joinpath(resdir, "passtimes.jls")
Expand Down Expand Up @@ -137,8 +138,15 @@ function makeh5files(; pathtosampleimg::String, resdir::String, iftversion=IceFl
return nothing
end


function makeh5files_single(; passtime::DateTime, iftversion::Union{String,Nothing}=nothing, truecolor::String, falsecolor::String, labeled::String, props::String, output::String)
function makeh5files_single(;
passtime::DateTime,
iftversion::Union{String,Nothing}=nothing,
truecolor::String,
falsecolor::String,
labeled::String,
props::String,
output::String,
)
latlondata = getlatlon(truecolor)
ptsunix = Int64(Dates.datetime2unix(passtime))
labeled_ = load_labeled_img(labeled)
Expand Down Expand Up @@ -167,9 +175,13 @@ function makeh5files_single(; passtime::DateTime, iftversion::Union{String,Nothi
g["y"] = latlondata["Y"]

g = create_group(file, "floe_properties")
write_dataset(g, "properties", [copy(row) for row in eachrow(props_)]) # `copy(row)` converts the DataSetRow to a NamedTuple
attrs(g)["Description of properties"] = """ Area units (`area`, `convex_area`) are in sq. kilometers, length units (`minor_axis_length`, `major_axis_length`, and `perimeter`) in kilometers, and `orientation` in radians (see the description of properties attribute.) Latitude and longitude coordinates are in degrees, and the stereographic coordinates`x` and `y` are in meters relative to the NSIDC north polar stereographic projection. Generated using the `regionprops` function from the `skimage` package. See https://scikit-image.org/docs/0.20.x/api/skimage.measure.html#regionprops
"""
@info nrow(props_)
if nrow(props_) > 0
write_dataset(g, "properties", [copy(row) for row in eachrow(props_)]) # `copy(row)` converts the DataSetRow to a NamedTuple
attrs(g)["Description of properties"] = """Area units (`area`, `convex_area`) are in sq. kilometers, length units (`minor_axis_length`, `major_axis_length`, and `perimeter`) in kilometers, and `orientation` in radians (see the description of properties attribute.) Latitude and longitude coordinates are in degrees, and the stereographic coordinates`x` and `y` are in meters relative to the NSIDC north polar stereographic projection. Generated using the `regionprops` function from the `skimage` package. See https://scikit-image.org/docs/0.20.x/api/skimage.measure.html#regionprops"""
else
attrs(g)["Description of properties"] = """No floes detected"""
end

mx = maximum(labeled_)
T = choose_dtype(mx)
Expand All @@ -182,7 +194,6 @@ function makeh5files_single(; passtime::DateTime, iftversion::Union{String,Nothi

attrs(obj)["description"] = "Connected components of the segmented floe image using a 3x3 structuring element. The property matrix consists of the properties of each connected component."
write_dataset(obj, dtype, imgdata)

end
return nothing
end
209 changes: 121 additions & 88 deletions IFTPipeline.jl/test/test-h5.jl
Original file line number Diff line number Diff line change
@@ -1,89 +1,122 @@
testlisteq = (a, b) -> @test Set(a) == Set(b)
pathtosampleimg = joinpath(@__DIR__, "test_inputs/input_pipeline/20220914.aqua.falsecolor.250m.tiff")
resdir = joinpath(dirname(pathtosampleimg), "h5")

originalbbox = (latitude=[81, 79], longitude=[-22, -12])

latlondata = getlatlon(pathtosampleimg)

getcorners(m) = [m[1, 1], m[end, end]]
latcorners = getcorners(latlondata["latitude"])
loncorners = getcorners(latlondata["longitude"])

ptpath = joinpath(resdir, "passtimes.jls")
passtimes = deserialize(ptpath)
ptsunix = Int64.(Dates.datetime2unix.(passtimes))

fnpath = joinpath(resdir, "filenames.jls")
truecolor_refs, falsecolor_refs = deserialize(fnpath)

floespath = joinpath(resdir, "segmented_floes.jls") # for labeled_image
floes = deserialize(floespath)

propspath = joinpath(resdir, "floe_props.jls")
props = deserialize(propspath)

lb = label_components(floes[1])

makeh5files(; pathtosampleimg, resdir)

h5path = joinpath(resdir, "hdf5-files", "20220914T1244.aqua.labeled_image.250m.h5")

@testset "h5.jl" begin

# validate computed lat/lon corners
@test all(originalbbox.latitude .≈ round.(latcorners))
@test all(originalbbox.longitude .≈ round.(loncorners))


# open h5 file
fid = h5open(h5path, "r")

@test typeof(fid) == HDF5.File

# top level attributes
@test attrs(fid)["iftversion"] == string(IceFloeTracker.IFTVERSION)
@test attrs(fid)["fname_falsecolor"] == falsecolor_refs[1]
@test attrs(fid)["fname_truecolor"] == truecolor_refs[1]
@test attrs(fid)["crs"] == latlondata["crs"]

# groups
testlisteq(keys(fid), ["floe_properties", "index"])
keys_index = [k for k in keys(fid["index"]) if k ["latitude", "longitude"]]
testlisteq(keys_index, ["time", "x", "y"])
testlisteq(keys(fid["floe_properties"]), ["column_names", "labeled_image", "properties"])

# check index group datasets
g = fid["index"]
t = read(g["time"])
x = read(g["x"])
y = read(g["y"])

@test t == ptsunix[1]
@test x == latlondata["X"]
@test y == latlondata["Y"]

# check floe_properties group datasets
g = fid["floe_properties"]
colnames = read(g["column_names"])
lb = read(g["labeled_image"])
props = read(g["properties"])

testlisteq(colnames, ["area", "convex_area", "major_axis_length", "minor_axis_length", "orientation", "perimeter", "latitude", "longitude", "x", "y"])

@test typeof(lb) == Matrix{UInt8}
@test typeof(props) == Matrix{Float64}
close(fid)

@test_throws "can't be represented" choose_dtype(-1)
@test choose_dtype(100) == UInt8
@test choose_dtype(300) == UInt16
@test choose_dtype(70_000) == UInt32
@test choose_dtype(BigInt(2)^64 - 1) == UInt64
@test choose_dtype(BigInt(2)^64) == UInt128
@test choose_dtype(BigInt(2)^128 - 1) == UInt128
@test_throws "can't be represented" choose_dtype(BigInt(2)^128)
@testset "hdf5 export" begin
@testset "normal case" begin
testlisteq = (a, b) -> @test Set(a) == Set(b)
pathtosampleimg = joinpath(
@__DIR__, "test_inputs/input_pipeline/20220914.aqua.falsecolor.250m.tiff"
)
resdir = joinpath(dirname(pathtosampleimg), "h5")

originalbbox = (latitude=[81, 79], longitude=[-22, -12])

latlondata = getlatlon(pathtosampleimg)

getcorners(m) = [m[1, 1], m[end, end]]
latcorners = getcorners(latlondata["latitude"])
loncorners = getcorners(latlondata["longitude"])

ptpath = joinpath(resdir, "passtimes.jls")
passtimes = deserialize(ptpath)
ptsunix = Int64.(Dates.datetime2unix.(passtimes))

fnpath = joinpath(resdir, "filenames.jls")
truecolor_refs, falsecolor_refs = deserialize(fnpath)

floespath = joinpath(resdir, "segmented_floes.jls") # for labeled_image
floes = deserialize(floespath)

propspath = joinpath(resdir, "floe_props.jls")
props = deserialize(propspath)

lb = label_components(floes[1])

makeh5files(; pathtosampleimg, resdir)

h5path = joinpath(resdir, "hdf5-files", "20220914T1244.aqua.labeled_image.250m.h5")

@testset "h5.jl" begin

# validate computed lat/lon corners
@test all(originalbbox.latitude .≈ round.(latcorners))
@test all(originalbbox.longitude .≈ round.(loncorners))

# open h5 file
fid = h5open(h5path, "r")

@test typeof(fid) == HDF5.File

# top level attributes
@test attrs(fid)["iftversion"] == string(IceFloeTracker.IFTVERSION)
@test attrs(fid)["fname_falsecolor"] == falsecolor_refs[1]
@test attrs(fid)["fname_truecolor"] == truecolor_refs[1]
@test attrs(fid)["crs"] == latlondata["crs"]

# groups
testlisteq(keys(fid), ["floe_properties", "index"])
keys_index = [k for k in keys(fid["index"]) if k ["latitude", "longitude"]]
testlisteq(keys_index, ["time", "x", "y"])
testlisteq(
keys(fid["floe_properties"]),
["column_names", "labeled_image", "properties"],
)

# check index group datasets
g = fid["index"]
t = read(g["time"])
x = read(g["x"])
y = read(g["y"])

@test t == ptsunix[1]
@test x == latlondata["X"]
@test y == latlondata["Y"]

# check floe_properties group datasets
g = fid["floe_properties"]
colnames = read(g["column_names"])
lb = read(g["labeled_image"])
props = read(g["properties"])

testlisteq(
colnames,
[
"area",
"convex_area",
"major_axis_length",
"minor_axis_length",
"orientation",
"perimeter",
"latitude",
"longitude",
"x",
"y",
],
)

@test typeof(lb) == Matrix{UInt8}
@test typeof(props) == Matrix{Float64}
close(fid)

@test_throws "can't be represented" choose_dtype(-1)
@test choose_dtype(100) == UInt8
@test choose_dtype(300) == UInt16
@test choose_dtype(70_000) == UInt32
@test choose_dtype(BigInt(2)^64 - 1) == UInt64
@test choose_dtype(BigInt(2)^64) == UInt128
@test choose_dtype(BigInt(2)^128 - 1) == UInt128
@test_throws "can't be represented" choose_dtype(BigInt(2)^128)
end

# clean up
rm(dirname(h5path); recursive=true)
end
@testset "empty props" begin
example_data_dir = joinpath(test_data_dir, "cloudy")
IFTPipeline.makeh5files_single(;
passtime=Dates.DateTime("2006-05-13T14:55:34"),
truecolor=joinpath(example_data_dir, "truecolor.tiff"),
falsecolor=joinpath(example_data_dir, "falsecolor.tiff"),
labeled=joinpath(example_data_dir, "labeled.tiff"),
props=joinpath(example_data_dir, "labeled.props.csv"),
output="example.h5", #mktemp()[1],
)
end
end

# clean up
rm(dirname(h5path), recursive=true)
1 change: 1 addition & 0 deletions IFTPipeline.jl/test/test_inputs/cloudy/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
These data are from the Fram Strait, observed using Terra on 2006-05-13 and resolved at 250m.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions IFTPipeline.jl/test/test_inputs/cloudy/labeled.props.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"area","min_row","min_col","max_row","max_col","row_centroid","col_centroid","convex_area","label","major_axis_length","minor_axis_length","orientation","perimeter"
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions IFTPipeline.jl/test/test_inputs/cloudy/overpass.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2006-05-13T14:55:34Z
Binary file not shown.

0 comments on commit 42ab7a8

Please sign in to comment.