Skip to content

Feat(tracker): add rotation calculations #232

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

Merged
merged 106 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
08e7204
fix(tracker): re-include masks in csv output
hollandjg Mar 10, 2025
31cb081
feat: add csv joiner utility
hollandjg Mar 10, 2025
b75bfc0
Merge branch 'main' into feat(tracker)--add-rotation-calculations
hollandjg Mar 10, 2025
7fa6d8b
remove psi column again
hollandjg Mar 10, 2025
5540838
feat(cli): add get_rotation_single command to cli
hollandjg Mar 10, 2025
a5c51cc
feat(csvjoin): add support for joining on ISO8601 timestamps
hollandjg Mar 12, 2025
e869919
feat(cli): add stub of rotations function
hollandjg Mar 12, 2025
9180765
test(cli): add test file for tracked floes with a satellite column
hollandjg Mar 12, 2025
d1bbb2e
feat(csvjoin): add support for different left and right columns
hollandjg Mar 12, 2025
73cf648
feat(workflow): add support for rotation task
hollandjg Mar 12, 2025
2a26614
add parsing of mask field
hollandjg Mar 12, 2025
aa31ca6
feat(rotation): add calculation of a single example rotation
hollandjg Mar 12, 2025
ed5be22
feat(rotation): output results for all rows in the input table
hollandjg Mar 12, 2025
eb53591
feat(rotation): add support for radians, degrees, and angular speeds …
hollandjg Mar 12, 2025
20df957
chore(rotation): add dependencies
hollandjg Mar 12, 2025
b27bc8f
fix(workflow): update syntax for csvjoin and get_rotation_single scripts
hollandjg Mar 12, 2025
ae26884
chore: add missing whitespace
hollandjg Mar 12, 2025
21f51a8
test(rotation): update path to tracked file
hollandjg Mar 12, 2025
945790a
test(rotation): add synthetic rotation example
hollandjg Mar 12, 2025
43e9501
test(rotation): add shorter test case
hollandjg Mar 12, 2025
f35b67f
test(rotation): add test-rotate file
hollandjg Mar 12, 2025
68176b0
chore: export get_rotation_single
hollandjg Mar 12, 2025
6f79737
test: fix function calls
hollandjg Mar 12, 2025
99423f9
test(rotation): correct df index
hollandjg Mar 12, 2025
d74f833
test: rename test files
hollandjg Mar 12, 2025
958bc98
rename rotation test set
hollandjg Mar 12, 2025
4fdd6bc
refactor(rotation): change results calculation into a list comprehension
hollandjg Mar 13, 2025
1900c2c
feat(rotation): convert time differences using Dates objects
hollandjg Mar 13, 2025
7d23449
Revert "refactor(rotation): change results calculation into a list co…
hollandjg Mar 13, 2025
7892667
refactor(rotation): update name of delta time column
hollandjg Mar 13, 2025
559a45e
docs(rotation): add docstring
hollandjg Mar 13, 2025
f6f8ce2
refactor(rotations): rename variables
hollandjg Mar 13, 2025
111610d
docs(rotation): update docstring
hollandjg Mar 13, 2025
0bbe296
docs(rotation): add comment on append
hollandjg Mar 13, 2025
edd8290
downgrade required dates
hollandjg Mar 13, 2025
c817c49
refactor(rotation): split get_rotation_measurement into simpler funct…
hollandjg Mar 13, 2025
a1c374f
remove single-line function for calculating rotation mismatch
hollandjg Mar 13, 2025
356dbea
refactor(rotation): split rotation function again for testing
hollandjg Mar 13, 2025
b638198
test(rotation): add testcases with less symmetric object
hollandjg Mar 13, 2025
8701a5f
test(rotation): update testing function
hollandjg Mar 13, 2025
00b0626
refactor(rotations): update the rotation function
hollandjg Mar 13, 2025
1a9218e
teat(rotation): add some more testcases with larger images
hollandjg Mar 14, 2025
6a36eea
chore: include LinearAlgebra dependency
hollandjg Mar 14, 2025
07d9509
test(rotation): update testcases to allow some to fail
hollandjg Mar 14, 2025
b580de5
test(rotation): add note on target fraction
hollandjg Mar 14, 2025
4418360
test: add LinearAlgebra dependency
hollandjg Mar 14, 2025
b44c7fd
test: update target correctness
hollandjg Mar 14, 2025
1eed181
test(rotation): remove test weakening
hollandjg Mar 14, 2025
220af60
feat(h5export): add support for zoned date times
hollandjg Mar 17, 2025
539ce51
feat(tracker): add support for zoned date-times
hollandjg Mar 17, 2025
2b77641
refactor(workflow): remove "Z" truncation on timestamps
hollandjg Mar 17, 2025
be035cc
fix(python): remove early instantiate call
hollandjg Mar 18, 2025
30a22f6
add new rotation calculations
hollandjg Apr 2, 2025
2d3e4d2
add extra dependencies
hollandjg Apr 2, 2025
fa53819
update rotation with new updates from Daniel
hollandjg Apr 7, 2025
cf9b66c
update tests to handle aliasing
hollandjg Apr 7, 2025
f9db046
add example vectors file
hollandjg Apr 7, 2025
2941adb
delete vectors file
hollandjg Apr 7, 2025
2c2f77e
add example floes tracked file
hollandjg Apr 7, 2025
71efc9d
uncomment test cases for rotations
hollandjg Apr 7, 2025
9805861
add debugging check to actions
hollandjg Apr 7, 2025
e1e9571
add additional check
hollandjg Apr 7, 2025
d961a13
add missing instantiate command
hollandjg Apr 7, 2025
eba42e3
Include Manifest for python setup
hollandjg Apr 7, 2025
f9b8b27
remove check registry steps
hollandjg Apr 7, 2025
8b1cd97
fix timezone issue in tracker test
hollandjg Apr 7, 2025
cf4c224
delete quaddirect rotation algorithm
hollandjg Apr 7, 2025
cc04b83
add timezones to test package
hollandjg Apr 7, 2025
eab5dda
Merge branch 'cleanup-latlon-2' into feat(tracker)--add-rotation-calc…
hollandjg Apr 7, 2025
ec0f33f
import relevant functions at top of file
hollandjg Apr 7, 2025
d55ccf7
update type of props argument
hollandjg Apr 7, 2025
c1afbed
remove imageview dependency
hollandjg Apr 7, 2025
4ad3e31
remove TestIMages dependency
hollandjg Apr 7, 2025
05b0c1c
remove lots of dependencies
hollandjg Apr 7, 2025
64bf6a5
remove CoordinateTransformations
hollandjg Apr 7, 2025
9fe84f2
update overpass files with Z timezones
hollandjg Apr 8, 2025
0af4a0c
rotations: cast string type to String
hollandjg Apr 8, 2025
984fb04
update rotations to include additional columns as required
hollandjg Apr 8, 2025
0c8189a
Add OrderedCollections dependency
hollandjg Apr 8, 2025
76ca0e0
add rotation step in testcases
hollandjg Apr 8, 2025
2999ae2
include satellite in output columns
hollandjg Apr 8, 2025
b75730d
add OrderedCollections compatibility
hollandjg Apr 8, 2025
858baae
fix catenation
hollandjg Apr 8, 2025
ebe2c0c
revert changes to rose-suite.conf
hollandjg Apr 8, 2025
121f9c9
remove cache directory
hollandjg Apr 8, 2025
9f24c6c
undo other changes to flow.cylc
hollandjg Apr 8, 2025
354a4d5
Merge branch 'main' into feat(tracker)--add-rotation-calculations
hollandjg Apr 11, 2025
cefdbe7
update rotations to use imported function from IceFloeTracker
hollandjg Apr 11, 2025
7d74d7d
rename measure_rotation from get_rotations_single
hollandjg Apr 11, 2025
d7cccb9
remove LinearAlgebra dependency
hollandjg Apr 11, 2025
976a00f
remove orderedColelctions dependency
hollandjg Apr 11, 2025
9f294b6
remove unneeded imports
hollandjg Apr 11, 2025
81e1b6f
remove interpolations dependency
hollandjg Apr 11, 2025
9b0f4a0
remove unneeded dependencies
hollandjg Apr 11, 2025
628e984
remove extra dependencies from test file
hollandjg Apr 11, 2025
541adb2
rename rotations to rotation
hollandjg Apr 11, 2025
d7e4aba
update docstring
hollandjg Apr 11, 2025
ab9974e
update testcases
hollandjg Apr 11, 2025
1422c30
fix testcase
hollandjg Apr 11, 2025
2d19993
remove additional columns option in CLI
hollandjg Apr 11, 2025
1dab817
remove outdated manifest file3
hollandjg Apr 17, 2025
1d3c606
update IceFloeTracker version
hollandjg Apr 17, 2025
814ba02
update pipeline CLI version
hollandjg Apr 17, 2025
535eef6
remove broken instantiate call. No idea why this works sometimes and …
hollandjg Apr 17, 2025
b78aab3
remove setup python project file
hollandjg Apr 17, 2025
ce823e8
remove project and manifest file for python setup
hollandjg Apr 17, 2025
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
6 changes: 6 additions & 0 deletions IFTPipeline.jl/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ version = "0.1.0"
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CSVFiles = "5d742f6a-9f54-50ce-8119-2520741973ca"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
Expand All @@ -20,13 +22,17 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53"

[compat]
ArgParse = "1.1"
DataFrames = "1.7.0"
Dates = "1.10.9"
Folds = "0.2"
HDF5 = "0.16, 0.17"
IceFloeTracker = "0.6.4"
LoggingExtras = "1.0"
Pkg = "1.9"
PyCall = "1.96"
TOML = "1.0"
TimeZones = "1.21.3"
4 changes: 3 additions & 1 deletion IFTPipeline.jl/src/IFTPipeline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ include("preprocess.jl")
include("feature-extraction.jl")
include("tracker.jl")
include("h5.jl")
include("rotations.jl")

export cache_vector,
sharpen,
Expand Down Expand Up @@ -77,7 +78,8 @@ export cache_vector,
mkclitrack!,
mkfilenames,
makeh5files,
getlatlon
getlatlon,
get_rotation_single

export IceFloeTracker
end
23 changes: 21 additions & 2 deletions IFTPipeline.jl/src/cli.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using ArgParse
using IceFloeTracker
using IFTPipeline
using Serialization
using TimeZones

function mkclipreprocess!(settings)
@add_arg_table! settings["preprocess"] begin
Expand Down Expand Up @@ -318,7 +319,7 @@ function mkclimakeh5_single!(settings)
"--passtime"
help = "Satellite pass time"
required = true
arg_type = DateTime
arg_type = ZonedDateTime

"--truecolor"
help = "Path to truecolor image"
Expand Down Expand Up @@ -498,7 +499,7 @@ function mkclitrack_single!(settings)
help = "Path to object with satellite pass times"
required = true
nargs = '+'
arg_type = DateTime
arg_type = ZonedDateTime

"--latlon"
help = "Path to geotiff image with latitude/longitude data"
Expand Down Expand Up @@ -645,6 +646,19 @@ function mkclilandmask_single!(settings)
return nothing
end

function mkcli_get_rotation_single!(settings)
@add_arg_table! settings["get_rotation_single"] begin
"--input", "-i"
help = "Tracked floes CSV file with a 'satellite' column"
required = true

"--output", "-o"
help = "Tracked floes CSV file with rotations calculated"
required = true
end
return nothing
end

function mkcli!(settings, common_args)
d = Dict(
"landmask" => mkclilandmask!,
Expand All @@ -658,6 +672,7 @@ function mkcli!(settings, common_args)
"makeh5files_single" => mkclimakeh5_single!,
"track" => mkclitrack!,
"track_single" => mkclitrack_single!,
"get_rotation_single" => mkcli_get_rotation_single!,
)

for t in keys(d)
Expand Down Expand Up @@ -714,6 +729,10 @@ function main()
"track_single"
help = "Pair ice floes in day k with ice floes in day k+1"
action = :command

"get_rotation_single"
help = "Get rotation of the ice floes"
action = :command
end

command_common_args = []
Expand Down
4 changes: 2 additions & 2 deletions IFTPipeline.jl/src/h5.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ function makeh5files(;
end

function makeh5files_single(;
passtime::DateTime,
passtime::ZonedDateTime,
iftversion::Union{String,Nothing}=nothing,
truecolor::String,
falsecolor::String,
Expand All @@ -148,7 +148,7 @@ function makeh5files_single(;
output::String,
)
latlondata = getlatlon(truecolor)
ptsunix = Int64(Dates.datetime2unix(passtime))
ptsunix = Int64(Dates.datetime2unix(DateTime(passtime)))
labeled_ = load_labeled_img(labeled)

if isnothing(iftversion)
Expand Down
136 changes: 136 additions & 0 deletions IFTPipeline.jl/src/rotations.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using DataFrames
using TimeZones
using Dates
using CSV

# Base.tryparse(::Type{ZonedDateTime}, str) = ZonedDateTime
# default_format(::Type{ZonedDateTime}) = Format("yyyy-mm-dd\\THH:MM:SS.sZ")

"""
Make a CSV of pairwise rotations between floes detected on adjacent days.

Loads the floes from the `input` CSV file, and uses the columns:
- `floe` ID
- `satellite` name
- `mask` – the binary mask (choose a column using argument `mask_column`)
- `passtime` in ISO8601 format (with trailing Z or +00:00), e.g. 2022-09-11T09:21:00+00:00 (choose a column using argument `time_column`)
- `date` of the overpass in YYYY-MM-DD format

Returns a CSV with one row per floe comparison.
In the following, `i=1` means the earlier observation, `i=2` the later.

Columns returned:
- `ID` of the floe
- Angle measures `theta_<deg,rad>` – angle between floe image in degrees or radians
- Time measurements:
- `delta_time_sec` – number of seconds between overpass in the two measurements
- `omega_<deg,rad>_per_<sec,hour,day>` – mean angular velocity of rotation in degrees or radians per second hour or day.
- Metadata
- `satellite<i>` – which satellite measurement `i` was from
- `date<i>` – which date measurement `i` was taken
- `datetime<i>` – which UTC time measurement `i`'s overpass occurred
- Original data
- `mask<i>` – the binary mask used for the measurement
"""
function get_rotation_single(;
input::String, output::String, mask_column=:mask, time_column=:passtime
)
input_df = DataFrame(CSV.File(input))

input_df[!, mask_column] = eval.(Meta.parse.(input_df[:, mask_column]))
input_df[!, time_column] = ZonedDateTime.(input_df[:, time_column])

results = []
for row in eachrow(input_df)
append!( # adds the 0 – n measurements from `get_rotation_measurements` to the results array
results,
get_rotation_measurements(
row, input_df; mask_column=mask_column, time_column=time_column
),
)
end
results_df = DataFrame(results)
@info results_df

FileIO.save(output, results_df)
return results_df
end

function get_rotation_measurements(
measurement::DataFrameRow, df::DataFrame; mask_column, time_column
)
filtered_df = subset(
df,
:ID => ByRow(==(measurement[:ID])),
:date => ByRow(==(measurement[:date] - Dates.Day(1))),
)

results = [
get_rotation_measurements(
earlier_measurement, measurement; mask_column, time_column
) for earlier_measurement in eachrow(filtered_df)
]

return results
end

function get_rotation_measurements(
row1::DataFrameRow, row2::DataFrameRow; mask_column, time_column
)
theta_rad = get_rotation(row1[mask_column], row2[mask_column])
theta_deg = rad2deg(theta_rad)

dt = row2[time_column] - row1[time_column]
dt_sec = dt / Dates.Second(1)
dt_hour = dt / Dates.Hour(1)
dt_day = dt / Dates.Day(1)

omega_deg_per_sec = (theta_deg) / (dt_sec)
omega_deg_per_hour = (theta_deg) / (dt_hour)
omega_deg_per_day = (theta_deg) / (dt_day)

omega_rad_per_sec = (theta_rad) / (dt_sec)
omega_rad_per_hour = (theta_rad) / (dt_hour)
omega_rad_per_day = (theta_rad) / (dt_day)

return (
ID=row1.ID,
theta_deg,
theta_rad,
delta_time_sec=dt_sec,
omega_deg_per_sec,
omega_deg_per_hour,
omega_deg_per_day,
omega_rad_per_sec,
omega_rad_per_hour,
omega_rad_per_day,
satellite1=row1.satellite,
satellite2=row2.satellite,
date1=row1.date,
date2=row2.date,
datetime1=row1[time_column],
datetime2=row2[time_column],
mask1=row1[mask_column],
mask2=row2[mask_column],
)
end

"""
Get the angle in radians to rotate mask1 to mask2.
"""
function get_rotation(
mask1, mask2; mxshift::Tuple{Int64,Int64}=(100, 100), mxrot::Float64=Float64(pi)
)
affine_map, _ = IceFloeTracker.Register.RegisterQD.qd_rigid(
IceFloeTracker.centered(mask1),
IceFloeTracker.centered(mask2),
mxshift,
mxrot;
print_interval=typemax(Int),
)
linear_map = affine_map.linear
cosθ = linear_map[1, 1]
sinθ = linear_map[2, 1]
θ = atan(sinθ, cosθ)
return θ
end
6 changes: 3 additions & 3 deletions IFTPipeline.jl/src/tracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ end
track_single(
imgs::Array{String},
props::Array{String},
passtimes::Array{DateTime},
passtimes::Array{ZonedDateTime},
latlon::String,
output::String,
...
Expand All @@ -65,7 +65,7 @@ Following are the default set of thresholds `condition_thresholds` used for floe
function track_single(;
imgs::Array{String},
props::Array{String},
passtimes::Array{DateTime},
passtimes::Array{ZonedDateTime},
latlon::String,
output::String,
dist::Array{Int}=[15, 30, 120],
Expand Down Expand Up @@ -139,7 +139,7 @@ function track_single(;
adduuid!(props_)

tracked_floes = long_tracker(props_, condition_thresholds, mc_thresholds)
FileIO.save(output, select!(tracked_floes, Not(:mask, :psi)))
FileIO.save(output, select!(tracked_floes, Not(:psi)))
return tracked_floes
end

Expand Down
1 change: 1 addition & 0 deletions IFTPipeline.jl/test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
Loading
Loading