Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive individual ATL03 photons from ATL08. #73

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Aria2_jll = "9ab3bdc3-1250-5043-8fac-ac7e82d2cbc9"
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
Expand Down
228 changes: 228 additions & 0 deletions src/ICESat-2/ATL08.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using DataInterpolations: LinearInterpolation
using Statistics

"""
points(g::ICESat2_Granule{:ATL08}; tracks=icesat2_tracks, step=1, canopy=false, ground=true, bbox::Union{Nothing,Extent,NamedTuple} = nothing)

Expand Down Expand Up @@ -298,3 +301,228 @@
)
nt
end

function photons(

Check warning on line 305 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L305

Added line #L305 was not covered by tests
granule::ICESat2_Granule{:ATL08};
tracks = icesat2_tracks,
step = 1,
)
nts = HDF5.h5open(granule.url, "r") do file
t_offset = open_dataset(file, "ancillary_data/atlas_sdp_gps_epoch")[1]::Float64 + gps_offset

Check warning on line 311 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L310-L311

Added lines #L310 - L311 were not covered by tests

# Determine number of loops over tracks and ground and/or canopy
ftracks = filter(track -> haskey(file, track) && haskey(open_group(file, track), "land_segments"), tracks)

Check warning on line 314 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L314

Added line #L314 was not covered by tests

map(ftracks) do track
track_nt = _photons(granule, file, track, t_offset)
replace!(x -> x === fill_value ? NaN : x, track_nt.height)
track_nt

Check warning on line 319 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L316-L319

Added lines #L316 - L319 were not covered by tests
end
end
return PartitionedTable(nts, granule)

Check warning on line 322 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L322

Added line #L322 was not covered by tests
end

round_step(x, step) = round(Int, x / step, RoundDown) * step

Check warning on line 325 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L325

Added line #L325 was not covered by tests

function _photons(

Check warning on line 327 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L327

Added line #L327 was not covered by tests
::ICESat2_Granule{:ATL08},
file::HDF5.H5DataStore,
track::AbstractString,
t_offset::Float64,
)
group = open_group(file, track)
step = 1
h_te_best_fit = open_dataset(group, "land_segments/terrain/h_te_best_fit")[:]::Vector{Float32}
h_te_best_fit_20m = vec(open_dataset(group, "land_segments/terrain/h_te_best_fit_20m")[:, :]::Array{Float32})

Check warning on line 336 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L333-L336

Added lines #L333 - L336 were not covered by tests
# he = repeat(open_dataset(group, "land_segments/terrain/h_te_uncertainty")[:]::Vector{Float32}, inner = 5)
subset_te_flag = open_dataset(group, "land_segments/terrain/subset_te_flag")[:, :]::Matrix{Int8}
h_canopy = open_dataset(group, "land_segments/canopy/h_canopy")[:]::Vector{Float32}
h_canopy_20m = vec(open_dataset(group, "land_segments/canopy/h_canopy_20m")[:, :]::Array{Float32})

Check warning on line 340 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L338-L340

Added lines #L338 - L340 were not covered by tests
# he = repeat(open_dataset(group, "land_segments/canopy/h_canopy_uncertainty")[1:step:end]::Vector{Float32}, inner = 5)
subset_can_flag = open_dataset(group, "land_segments/canopy/subset_can_flag")[:, :]::Matrix{Int8}
longitude_20m = vec(open_dataset(group, "land_segments/longitude_20m")[:, 1:step:end]::Array{Float32})
longitude = open_dataset(group, "land_segments/longitude")[:]::Vector{Float32}
latitude_20m = vec(open_dataset(group, "land_segments/latitude_20m")[:, 1:step:end]::Array{Float32})
latitude = open_dataset(group, "land_segments/latitude")[:]::Vector{Float32}

Check warning on line 346 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L342-L346

Added lines #L342 - L346 were not covered by tests

mask20 = (h_te_best_fit_20m .!= fill_value) .& (longitude_20m .!= fill_value) .& (latitude_20m .!= fill_value)
mask = (h_te_best_fit .!= fill_value) .& (longitude .!= fill_value) .| (latitude .!= fill_value)

Check warning on line 349 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L348-L349

Added lines #L348 - L349 were not covered by tests

delta_time = open_dataset(group, "land_segments/delta_time")[1:step:end]::Vector{Float64}
lon_interpol = LinearInterpolation(Float64.(longitude)[mask], delta_time[mask])
lat_interpol = LinearInterpolation(Float64.(latitude)[mask], delta_time[mask])
time_interpol = LinearInterpolation(delta_time, 1:1:length(delta_time))
time20m_interpol = time_interpol(range(1 - 2 // 5, length(delta_time) + 2 // 5, length = 5 * length(delta_time)))
height20m_interpol = LinearInterpolation(h_te_best_fit_20m[mask20], time20m_interpol[mask20])

Check warning on line 356 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L351-L356

Added lines #L351 - L356 were not covered by tests
# lon20m_interpolation = LinearInterpolation(Float64.(vec(longitude_20m)), time20m_interpol)
# lat20m_interpolation = LinearInterpolation(Float64.(vec(latitude_20m)), time20m_interpol)

# sensitivity = repeat(open_dataset(group, "land_segments/snr")[1:step:end]::Vector{Float32}, inner = 5)
# clouds = repeat(open_dataset(group, "land_segments/layer_flag")[1:step:end]::Vector{Int8}, inner = 5)
# scattered = repeat(open_dataset(group, "land_segments/msw_flag")[1:step:end]::Vector{Int8}, inner = 5)
# saturated = repeat(open_dataset(group, "land_segments/sat_flag")[1:step:end]::Vector{Int8}, inner = 5)
# phr = repeat(open_dataset(group, "land_segments/ph_removal_flag")[1:step:end]::Vector{Int8}, inner = 5)
# dem = repeat(open_dataset(group, "land_segments/dem_h")[1:step:end]::Vector{Float32}, inner = 5)
# times = unix2datetime.(t .+ t_offset)
atlas_beam_type = read_attribute(group, "atlas_beam_type")::String
spot_number = read_attribute(group, "atlas_spot_number")::String

Check warning on line 368 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L367-L368

Added lines #L367 - L368 were not covered by tests

# asr = repeat(open_dataset(group, "land_segments/asr")[1:step:end]::Vector{Float32}, inner = 5)
# nph = repeat(open_dataset(group, "land_segments/n_seg_ph")[1:step:end]::Vector{Int32}, inner = 5)

ph_ndx_beg = read(open_dataset(group, "land_segments/ph_ndx_beg"))::Vector{Int64}
segment_id_beg = read(open_dataset(group, "land_segments/segment_id_beg"))::Vector{Int32}
segment_id_end = read(open_dataset(group, "land_segments/segment_id_end"))::Vector{Int32}

Check warning on line 375 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L373-L375

Added lines #L373 - L375 were not covered by tests
# n_seg_ph = read(open_dataset(group, "land_segments/n_seg_ph"))::Vector{Int32}

@assert issorted(segment_id_beg) "segment_id_beg is not sorted"
@assert issorted(segment_id_end) "segment_id_end is not sorted"

Check warning on line 379 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L378-L379

Added lines #L378 - L379 were not covered by tests

classed_pc_flag = read(group, "signal_photons/classed_pc_flag")::Array{Int8,1}
ph_segment_id = read(group, "signal_photons/ph_segment_id")::Array{Int32,1}
@assert issorted(ph_segment_id) "ph_segment_id is not sorted"

Check warning on line 383 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L381-L383

Added lines #L381 - L383 were not covered by tests

# Find the 100m and 20m indices for each individual photon
# Nothing lines up in terms of segments. Each 100m segment can
# use 1 to 5 20m segments, including jumps between segment numbers.
# The photon segment_ids are wider than the segment ids mentioned in the 100m segments
# and the number of photons used is less than provided.

# Find index to 100m segments for attributes
index100m = searchsortedfirst.(Ref(segment_id_beg), ph_segment_id) .- 1
index100m[insorted.(ph_segment_id, Ref(segment_id_beg))] .+= 1
index100me = searchsortedfirst.(Ref(segment_id_end), ph_segment_id)
is100m = index100m .== index100me
index100m[.!is100m] .= 1 # set to valid index

Check warning on line 396 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L392-L396

Added lines #L392 - L396 were not covered by tests

@assert index100m[ph_ndx_beg] == 1:length(ph_ndx_beg)

Check warning on line 398 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L398

Added line #L398 was not covered by tests

# 100m can group less than 5 20m segments (!)
# @info unique(segment_id_end .- segment_id_beg .+ 1)

# Find valid segments
# Per the ATBD 2.1.18 and 2.2.7, each 20m segment is only valid
# if there are at least 10 photons, and 3 classified photons.
# Canopy segments therefore also require at least 3 terrain photons
# on top of a required 3 canopy photons.
canopy_valid = falses(length(ph_segment_id))
terrain_valid = falses(length(ph_segment_id))
segments = unique(ph_segment_id)
segments_nph = zeros(Int16, size(segments))
@views for (i, segment) in enumerate(segments)
I = searchsorted(ph_segment_id, segment)
nt = count(==(1), classed_pc_flag[I])
nc = count(>=(2), classed_pc_flag[I])
tv = length(I) >= 10 & nt >= 3 # 10 photons, 3 ground
cv = tv & nc >= 3 # valid ground and 3 canopy
terrain_valid[I] .= tv
canopy_valid[I] .= cv
segments_nph[i] = nt
end

Check warning on line 421 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L408-L421

Added lines #L408 - L421 were not covered by tests

valid_segment = falses(length(ph_segment_id))
@views for (i, class) in enumerate(classed_pc_flag)
if class == 1
valid_segment[i] = terrain_valid[i]
elseif class >= 2
valid_segment[i] = canopy_valid[i]

Check warning on line 428 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L423-L428

Added lines #L423 - L428 were not covered by tests
end
end

Check warning on line 430 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L430

Added line #L430 was not covered by tests

# Assign segment id to 20m photons
segments20m = zeros(Int32, size(subset_te_flag))
@views for I in eachindex(segment_id_beg)
validt = subset_te_flag[:, I]
begi = segment_id_beg[I]
endi = segment_id_end[I]

Check warning on line 437 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L433-L437

Added lines #L433 - L437 were not covered by tests

number_of_segments = endi - begi + 1
if number_of_segments == 5
segments20m[:, I] = begi:endi

Check warning on line 441 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L439-L441

Added lines #L439 - L441 were not covered by tests
else
i = findfirst(x -> x >= 0, validt)
if length(i:5) < number_of_segments

Check warning on line 444 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L443-L444

Added lines #L443 - L444 were not covered by tests
# TODO Match by time?
@error "Can't place $number_of_segments segments ($begi:$endi) to $validt at $i"

Check warning on line 446 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L446

Added line #L446 was not covered by tests
else
segments20m[i:i+number_of_segments-1, I] = begi:endi

Check warning on line 448 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L448

Added line #L448 was not covered by tests
end
end
end
segments20mv = vec(segments20m)
is20m = insorted.(ph_segment_id, Ref(sort(unique(segments20mv))))
prev = 1
for i in eachindex(segments20mv)
segment = segments20mv[i]
if segment == 0
segments20mv[i] = prev

Check warning on line 458 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L451-L458

Added lines #L451 - L458 were not covered by tests
else
prev = segment

Check warning on line 460 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L460

Added line #L460 was not covered by tests
end
end
@assert issorted(segments20mv)

Check warning on line 463 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L462-L463

Added lines #L462 - L463 were not covered by tests

index20m = searchsortedfirst.(Ref(segments20mv), ph_segment_id)
index20m[.!is20m] .= 1

Check warning on line 466 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L465-L466

Added lines #L465 - L466 were not covered by tests

d = read(group, "signal_photons/d_flag")::Array{Int8,1}

Check warning on line 468 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L468

Added line #L468 was not covered by tests

# relative height about what?
ph_h = read(group, "signal_photons/ph_h")::Array{Float32,1}
ph_h[ph_h.==fill_value] .= NaN
delta_time = read(group, "signal_photons/delta_time")::Vector{Float64}

Check warning on line 473 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L471-L473

Added lines #L471 - L473 were not covered by tests
# lon = lon20m_interpolation(delta_time)
lon = lon_interpol(delta_time)

Check warning on line 475 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L475

Added line #L475 was not covered by tests
# lat = lat20m_interpolation(delta_time)
lat = lat_interpol(delta_time)
height = height20m_interpol(delta_time) .+ ph_h
times = unix2datetime.(delta_time .+ t_offset)
atlas_beam_type = read_attribute(group, "atlas_beam_type")::String

Check warning on line 480 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L477-L480

Added lines #L477 - L480 were not covered by tests

terrain_height100m = h_te_best_fit[index100m]
terrain_height100m[.!is100m] .= NaN
terrain_height100m[findall(==(fill_value), terrain_height100m)] .= NaN

Check warning on line 484 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L482-L484

Added lines #L482 - L484 were not covered by tests

terrain_height20m = h_te_best_fit_20m[index20m]
terrain_height20m[.!is20m] .= NaN
terrain_height20m[findall(==(fill_value), terrain_height20m)] .= NaN

Check warning on line 488 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L486-L488

Added lines #L486 - L488 were not covered by tests

canopy_height100m = h_canopy[index100m]
canopy_height100m[.!is100m] .= NaN
canopy_height100m[findall(==(fill_value), canopy_height100m)] .= NaN

Check warning on line 492 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L490-L492

Added lines #L490 - L492 were not covered by tests

canopy_height20m = h_canopy_20m[index20m]
canopy_height20m[.!is20m] .= NaN
canopy_height20m[findall(==(fill_value), canopy_height20m)] .= NaN

Check warning on line 496 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L494-L496

Added lines #L494 - L496 were not covered by tests

nt = (

Check warning on line 498 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L498

Added line #L498 was not covered by tests
longitude = lon,
latitude = lat,
height = height,
terrain_height20m = terrain_height20m,
terrain_height100m = terrain_height100m,
canopy_height20m = canopy_height20m,
canopy_height100m = canopy_height100m,
# height_error = he,
datetime = times,
segment_id = ph_segment_id,
valid_segment = valid_segment,
is20m = is20m,
is100m = is100m,
# quality = q,
# phr = Bool.(phr),
# sensitivity = sensitivity,
# scattered = scattered,
# saturated = saturated,
# clouds = Bool.(clouds),
track = Fill(track, length(times)),
strong_beam = Fill(atlas_beam_type == "strong", length(times)),
classification = classed_pc_flag,
d_flag = Bool.(d),
# height_reference = dem,
detector_id = Fill(parse(Int8, spot_number), length(times)),
# reflectance = asr,
# nphotons = nph,
)
nt

Check warning on line 527 in src/ICESat-2/ATL08.jl

View check run for this annotation

Codecov / codecov/patch

src/ICESat-2/ATL08.jl#L527

Added line #L527 was not covered by tests
end
Loading