Open source TC-specific AMV derivation scripts.
- Tsukada, T., Horinouchi, T., & Tsujino, S. (2024). Wind distribution in the eye of tropical cyclone revealed by a novel atmospheric motion vector derivation. Journal of Geophysical Research: Atmospheres, 129, e2023JD040585. https://doi.org/10.1029/2023JD040585
- Horinouchi, T., S. Tsujino, M. Hayashi, U. Shimada, W. Yanase, A. Wada, and H. Yamada, 2023: Stationary and Transient Asymmetric Features in Tropical Cyclone Eye with Wavenumber-1 Instability: Case Study for Typhoon Haishen (2020) with Atmospheric Motion Vectors from 30-Second Imaging. Monthly Weather Review, 151, 253–273, https://doi.org/10.1175/MWR-D-22-0179.1.
v2.0.0 migrates to pyVTTrac v2 (native Fortran core, no Julia dependency)
and moves the core tracking logic into an installable opentcamv/ package.
See CHANGELOG.md for the full list of changes, including bug fixes that
change output versus v1 -- if you're upgrading from v1, read it before
comparing results.
openTCAMV/
├── README.md
├── CHANGELOG.md
├── requirements.txt
├── pyproject.toml
├── opentcamv/ # core tracking logic (installable package)
│ ├── cli.py # argparse definition + normalization
│ ├── data.py # input reading, time axis, Grid, mask
│ ├── params.py # TrackingSetup (derived parameters)
│ ├── output.py # output coords/Dataset/attrs, write, combine_omega
│ ├── rotation.py # --revrot window rotation
│ ├── tracking.py # per-tid0 time-window slicing + pyvttrac.track()
│ ├── aggregate.py # --vagg, trajectory concat, polar conversion
│ ├── screening.py # revrot restore, --vlim/--Vd/--Td/--Vth
│ ├── records.py # --record_initpos/--record_alongtraj/--out_cthmin/max/psr
│ ├── concat.py # time-axis concatenation (11_'s logic)
│ ├── trajectory_movie.py # trajectory animation (31_'s logic; self-contained, copy-and-use)
│ └── finalize/ # candidate selection (20_'s logic)
│ ├── io.py # load_candidates: (ns, omega, ...) working dataset
│ ├── window.py # median-filter window sizes
│ ├── masking.py # cth/IRdiff/score-threshold/striation masking
│ ├── candidates.py # priority computation, final assembly
│ └── selectors/ # iterative_median (default), relaxation (skeleton)
├── scripts/ # thin CLI drivers over opentcamv/
│ ├── 10_conduct_tracking.py
│ ├── 11_concat_flows_along_time.py
│ ├── 20_finalize_tracking.py
│ ├── 30_plot_velocity2d.py
│ └── 31_animate_trajectories.py
├── tests/
├── docs
│ └── sample
│ ├── README.md
│ ├── AMVs_it24.png
│ └── trajectories.gif
└── sample
├── sample.sh
└── (2017_Lan_aeqd_sample.nc) # see docs/sample/README.md
- numpy
- scipy
- pandas
- xarray
- dask
- pillow
- netcdf4
- pyVTTrac >= 2.2
- matplotlib (only for
30_plot_velocity2d.pyand31_animate_trajectories.py) - ffmpeg (only for
.mp4movies from31_animate_trajectories.py;.gifneeds nothing extra) - cmcrameri (only for Crameri's Scientific colour maps, such as batlow, in
31_animate_trajectories.py --image_cmap)
Target platforms are Linux and macOS (pyVTTrac v2's native Fortran core doesn't support Windows).
$ git clone https://github.com/tsukada-cs/openTCAMV.git
$ pip install -e openTCAMVThis installs opentcamv (editable) along with its dependencies, including pyVTTrac -- a plain pip install pyVTTrac is all that library itself needs now (no Julia, no git submodule). scripts/*.py can then be run directly; they import opentcamv without any sys.path setup.
For the plotting scripts (30_plot_velocity2d.py, 31_animate_trajectories.py), also run pip install matplotlib (or pip install -e "openTCAMV[plot]").
There is sample data and code available for a tutorial.
Please see docs/sample/README.md
Click here to open the description
This script perform the cloud tracking for satellite imagery.
The tracking is conducted using the pyVTTrac library, which is a cloud tracking algorithm based on the template matching method.
For the details of the tracking algorithm, please refer to the Tsukada et al. (2024).
Input file: NetCDF file that contains a tracked variable with (time, y, x) dimensions. The x and y dimensions should have "units" attribute with "km".
Basic usage
$ python 10_conduct_tracking.py ifn [-s start] [-e end] [-o ofn] [-n ntrac] [--ward ward] [--tidstep tidstep] [--traj_int traj_int] [-v vagg] [--polar] [--use_init_temp] [--no_subgrid] [--subgrid subgrid] [--method method] [--workers workers] [--itran itran] [--xgran xgran] [--xint xint] [--ygran ygran] [--yint yint] [--rgran rgran] [--rint rint] [--nath nath] [--ns ns [ns ...]] [--nsx nsx] [--nsy nsy] [--Vd Vd] [--Td Td] [--Vth Vth] [--Vs Vs] [--hs hs] [--Vc Vc] [--vlim vlim] [--Sth0 Sth0] [--Sth1 Sth1] [--Cth Cth] [--peak_inside_th peak_inside_th] [--itstep itstep] [--varname varname] [--maskvar maskvar] [--mask_lower_lim mask_lower_lim] [--mask_upper_lim mask_upper_lim] [--min_samples min_samples] [--out_subimage] [--out_score_ary] [--out_psr] [--sector sector] [--dtlimit dtlimit] [--ref_dt ref_dt] [--revrot revrot [revrot ...]] [--split_omega] [--record_initpos record_initpos] [--record_alongtraj record_alongtraj] [--cth cth] [--out_cthmin] [--out_cthmax] [--complevel complevel]Example (single Omega)
$ python 10_conduct_tracking.py ../sample/2017_Lan_aeqd_sample.nc --revrot 0.0020 --varname=B03 --ns=7 --ntrac=1 --Sth0=0.7 -o=../sample/2017_Lan_ns7_nt1_test.nc --ygran=-45:45 --xgran=-45:45 --traj_int=1 --Vs=10 --record_initpos cth B03 B13 B14 --out_cthmax --Vc=20 --Vd=20 --Td=60 --Vth=5Example (multiple Omega values, looped over within one process; default output is a single file with an omega dimension holding every value)
$ python 10_conduct_tracking.py ../sample/2017_Lan_aeqd_sample.nc --revrot 0.0000 0.0005 0.0010 0.0015 0.0020 0.0025 --varname=B03 --ns=7 --ntrac=1 --Sth0=0.7 -o=../sample/2017_Lan_ns7_nt1.nc --ygran=-45:45 --xgran=-45:45 --traj_int=1 --Vs=10 --record_initpos cth B03 B13 B14 --out_cthmax --Vc=20 --Vd=20 --Td=60 --Vth=5Example (same, but one file per Omega instead -- -o needs an <omega> placeholder)
$ python 10_conduct_tracking.py ../sample/2017_Lan_aeqd_sample.nc --revrot 0.0000 0.0005 0.0010 0.0015 0.0020 0.0025 --split_omega --varname=B03 --ns=7 --ntrac=1 --Sth0=0.7 -o='../sample/2017_Lan_ns7_nt1_rot<omega>.nc' --ygran=-45:45 --xgran=-45:45 --traj_int=1 --Vs=10 --record_initpos cth B03 B13 B14 --out_cthmax --Vc=20 --Vd=20 --Td=60 --Vth=5Example (multiple ns values as well -- one file per ns, each holding every Omega as above; -o needs an <ns> placeholder. The ns values run one after another, so peak memory is that of a single-ns run)
$ python 10_conduct_tracking.py ../sample/2017_Lan_aeqd_sample.nc --revrot 0.0000 0.0005 0.0010 0.0015 0.0020 0.0025 --varname=B03 --ns 7 9 11 --ntrac=1 --Sth0=0.7 -o='../sample/2017_Lan_ns<ns>_nt1.nc' --ygran=-45:45 --xgran=-45:45 --traj_int=1 --Vs=10 --record_initpos cth B03 B13 B14 --out_cthmax --Vc=20 --Vd=20 --Td=60 --Vth=5| Argument | Type | Default | Description | Choices |
|---|---|---|---|---|
| ifn | str | file path to input NetCDF file | ||
| -s, --start | str | start time in yyyymmddTHHMMSS format | ||
| -e, --end | str | end time in yyyymmddTHHMMSS format | ||
| -o, --ofn | str | ./tmp.nc | output NetCDF file path. Needs a <ns> placeholder when more than one --ns value is given, and a <omega> placeholder when --split_omega is set with more than one --revrot value |
|
| -n, --ntrac | int | 2 | The number of tracking for both forward and backward tracking | |
| --ward | str | bothward | time direction for tracking | bothward, forward, backward |
| --tidstep | int | 1 | time index interval of initial time for start tracking (1 means every time index) | |
| --traj_int | int | time index interval for output of trajectory | ||
| -v, --vagg | str | mean | how to aggregate the vectors; 'org' for original vectors without any aggregations, 'mean' for the velocity be averaging vectors, 'startend' for the veclocity by connecting the start and end points | org, mean, startend |
| --polar | bool | if specified, use polar coordinates points as initial template positioning, if not use Cartesian grid | ||
| --use_init_temp | bool | use initial template through tracking without updating the template | ||
| --no_subgrid | bool | alias for --subgrid=none |
||
| --subgrid | str | paraboloid | subgrid peak-refinement method | paraboloid, gaussian, none |
| --method | str | xcor | score method | xcor, ncov |
| --workers | int | OpenMP thread count. Unset = OpenMP default (usually all cores), 1 = sequential | ||
| --itran | slice | time-axis colon-separated slice of initial time for start tracking, with higher priority over --start and --end | ||
| --xgran | slice | -50:50 | x-axis colon-separated slice of initial template positions (with interval of --xint) | |
| --xint | float | 1.0 | x-axis interval of initial template positions in the (equally spaced grid) | |
| --ygran | slice | -50:50 | y-axis colon-separated slice of initial template positions (with interval of --yint) | |
| --yint | float | 1.0 | y-axis interval of initial template positions (equally spaced grid) | |
| --rgran | slice | 4:50 | r-axis colon-separated slice of initial template positions (equally spaced grid) | |
| --rint | int | 1 | r-axis interval of initial template positions (equally spaced grid) | |
| --nath | int | 60 | number of azimuthal initial template positions in polar coordinates | |
| --ns | int | [11] | template size(s) in pixel dimension. Accepts multiple values (nargs="+"), looped over one at a time within one process, each writing its own output file(s) (see -o) |
|
| --nsx | int | template width with higher priority over --ns (single --ns value only) |
||
| --nsy | int | template height with higher priority over --ns (single --ns value only) |
||
| --Vd | float | 20.0 | threshold to limit the maximum velocity difference between velocities obtained from forward and backward tracking as vectors (compared directly against Vd -- fixed in v2.0.0, see CHANGELOG.md) | |
| --Td | float | threshold to limit the maximum angle difference between velocities obtained from forward and backward tracking as vectors | ||
| --Vth | float | 5.0 | threshold speed for screening with --Td | |
| --Vs | float | 80.0 | search range for cloud tracking in velocity dimension (m/s) | |
| --hs | int | Search range for cloud tracking in pixel count with higher priority over --Vs | ||
| --Vc | float | 20.0 | threshold to limit the maximum velocity change between consecutive images | |
| --vlim | float | 120.0 | Threshold to limit the maximum speed (m/s) | |
| --Sth0 | float | 0.8 | minimum score required for the first-time tracking | |
| --Sth1 | float | 0.8 | minimum score required for the subsequent tracking | |
| --Cth | float | 3 | minimum contrast to track the template | |
| --peak_inside_th | float | |||
| --itstep | int | 1 | if >1, skip | |
| --varname | str | B03 | variable name of tracking target | |
| --maskvar | str | variable name for creating mask | ||
| --mask_lower_lim | float | lower limit for mask variable. --maskvar <= --lower_limit will be ignored when scoring | ||
| --mask_upper_lim | float | upper limit for mask variable. --maskvar >= --upper_limit will be ignored when scoring | ||
| --min_samples | int | 1 | minimum number of valid values to calculate score when using mask | |
| --out_subimage | bool | if output subimages | ||
| --out_score_ary | bool | if output score array | ||
| --out_psr | bool | if output Peak-To-Sidelobe ratio of the score field | ||
| --sector | str | limiting sectors used for tracking | ||
| --dtlimit | float | 200.0 | specify maximum time interval (in seconds) | |
| --ref_dt | float | reference time interval for calculating ixhw and iyhw from Vs (in seconds) | ||
| --revrot | float | [0.0] | angular velocity/velocities to rotate images (in rad/s). Positive (negative) value make crockwise (counterclocwise) rotation over time. Accepts multiple values (nargs="+"), looped over within one process; default output is a single file with all values along an omega dimension (see --split_omega) |
|
| --split_omega | bool | write one file per --revrot value (v1-compatible) instead of the default single combined file; requires a <omega> placeholder in -o/--ofn |
||
| --record_initpos | str | Record specified variable at their initial position | ||
| --record_alongtraj | str | Record specified variable along their trajectory | ||
| --cth | str | cth | cloud-top height variable name | |
| --out_cthmin | bool | if output minimum cloud-top height along each tracking | ||
| --out_cthmax | bool | if output maximum cloud-top height along each tracking | ||
| --complevel | int | 3 | compression level for output NetCDF file |
Click here to open the description
If tracking itself was split across processes/machines by time range
(memory constraints, cluster distribution -- not for speed: 10_ conduct_tracking.py already tracks every Omega and ns value within a
single process, so there's no per-Omega parallelism left to reassemble
here; for within-process speed use 10_'s --workers), this script
concatenates the resulting 10_conduct_tracking.py output files along the
time axis into a single input file for 20_finalize_tracking.py. Works
unchanged whether or not the inputs carry 10_'s omega/multi-ns
dimensions -- neither is ever the concat axis here.
Input file: NetCDF files generated by 10_conduct_tracking.py
Basic usage
$ python 11_concat_flows_along_time.py [-t target [target ...]] [-g glob_strings] [-e exclude_texts [exclude_texts ...]] [-d drop_vars [drop_vars ...]] [-o oname]Example
$ python 11_concat_flows_along_time.py -g '../sample/2017_Lan_ns7_nt1_it*.nc' --exclude_texts concat -o ../sample/2017_Lan_ns7_nt1_concat.nc| Argument | Type | Default | Description | Choices |
|---|---|---|---|---|
| -t, --target | str | NetCDF filenames to concatenate (alternative to -g) |
||
| -g, --glob_strings | str | File pattern to match netCDF files (alternative to -t) |
||
| -e, --exclude_texts | str | Exclude the filenames including this text from the concat list | ||
| -d, --drop_vars | str | Variable names to drop from the output file | ||
| -o, --oname | str | Output filename. Derived from --glob_strings if omitted |
Click here to open the description
This script finalizes the tracking results by selecting the best solution from the candidate solutions.
The candidate solutions are generated by the script 10_conduct_tracking.py.
Input file: NetCDF files generated by 10_conduct_tracking.py
Basic usage
$ python 20_finalize_tracking.py ifn_ns<ns>_nt1_rot<omega>.nc [--ns ns] [--omega omega] [-o ofn] [--Tw Tw] [--Hw Hw] [--xw xw] [--yw yw] [--rw rw] [--aw aw] [--dth dth] [--dc dc] [--useV] [-e max_epoch] [--IRdiff IRdiff] [--dIR dIR] [--cth cth] [--cthmin cthmin] [--cthmax cthmax] [--score_th score_th] [--out_final_omega] [--out_final_ns] [--out_final_medians] [--priority priority] [--exclude exclude] [--ref_flows ref_flows] [--tau_stri tau_stri] [--v_stri v_stri] [--cth_stri cth_stri] [--omega_stri omega_stri]Example 1
$ python 20_finalize_tracking.py ../sample/2017_Lan_ns7_nt1_rot<omega>.nc --ns 7 --omega 0.0000 0.0005 0.0010 0.0015 0.0020 0.0025 --cthmax 10 --exclude stf stb score_ary psr -o ../sample/2017_Lan_ns7_nt1_ref.ncExample 2 (with reference flows to apply striations treatment using the result of Example 1)
$ python 20_finalize_tracking.py ../sample/2017_Lan_ns7_nt1_rot<omega>.nc --ns 7 --omega 0.0000 0.0005 0.0010 0.0015 0.0020 0.0025 --cthmax 10 --exclude stf stb score_ary psr --ref_flows ../sample/2017_Lan_ns7_nt1_ref.nc --omega_stri 0.0015 -o ../sample/2017_Lan_ns7_nt1.nc| Argument | Type | Default | Description | Choices |
|---|---|---|---|---|
| ifns_rule | str | filename format used in f-string using as template size and as Ω_i | ||
| --ns | int | ns list | ||
| --omega | str | Ω_i list | ||
| -o, --ofn | str | output filename | ||
| --Tw | float | 1210 | time period of median filtering window (in sec) | |
| --Hw | float | 6 | width & height of median filtering window for Cartesian grid (in km) | |
| --xw | float | x width of median filtering window for Cartesian grid (in km) | ||
| --yw | float | y height of median filtering window for Cartesian grid (in km) | ||
| --rw | float | 6 | r width of median filtering window for polar grid (in km) | |
| --aw | float | 30 | azimuth width of median filtering window for polar grid (in degree) | |
| --dth | float | 10 | threshold to limit the maximum d2 for rejection of candidates (in m/s) | |
| --dc | float | 0.5 | coefficient to be multiplied by the L2 norm of median wind speed for rejection of candidates (0: all rejected, 0.5: half length, 1: same length) | |
| --useV | boolean | if specified, use absolute wind speed for median filtering, not using each velocity component | ||
| -e, --max_epoch | float | 20 | the maximum number of epochs for updating the solutions | |
| --IRdiff | str | the formula for IR difference. It evaluates the difference between two variables parsed with a minus sign. The grid with abs(IR1-IR2) >= --dIR are masked |
||
| --dIR | float | 2 | threshold to limit maximum IR difference (in K). The values larger than it will be masked. | |
| --cth | str | cth | cloud top height variable name | |
| --cthmin | float | threshold to limit the minimum cloud top height at initial position (in km) | ||
| --cthmax | float | threshold to limit the maximum cloud top height at initial position (in km) | ||
| --score_th | float | threshold to limit the minimum score of the candidates | ||
| --out_final_omega | boolean | output final_omega which is the used omega values in final solutions |
||
| --out_final_ns | boolean | output final_ns which is the used ns in final solutions |
||
| --out_final_medians | boolean | output final_medians which is the final median wind speeds |
||
| --priority | str | score | priority for selecting the best solution. score selects the solution with the highest score. dangv selects the solution with the smallest angular velocity difference from the reference angular velocity |
score, dangv |
| --exclude | str | drop specified variables from the final output file | ||
| --ref_flows | str | Reference flows to apply omega_stri procedure | ||
| --tau_stri | int | 24 | temporal window size (in index space) to compute the velocity contrast of --ref_flows for detection of striations | |
| --v_stri | float | 20 | threshold to limit the maximum velocity contrast for detection of striations (m/s) | |
| --cth_stri | int | threshold to limit the maximum cloud top height to get reference contrast | ||
| --omega_stri | float | 0.0 | threshold to limit the minimum Ω_i for the striation grids (rad/s) |
Click here to open the description
This script plots the 2D velocity field on the map.
Input file: NetCDF file generated by 20_finalize_tracking.py
Basic usage
$ python 30_plot_velocity2d.py ifn [--it it] [--sstep sstep] [--vector_duration vector_duration] [--key_speed key_speed] [-o odir]Example
$ python 30_plot_velocity2d.py ../sample/2017_Lan_ns7_nt1.nc --it=24| Argument | Type | Default | Description | Choices |
|---|---|---|---|---|
| "ifn" | str | Directory containing sample data | ||
| "--it" | int | 24 | Time index to plot | |
| "--sstep" | int | 2 | Spacing between drawn vectors | |
| "--vector_duration" | int | 90 | Duration of each vector in seconds. If 90, then vector length corresponds to the travel length during 90 sec | |
| "--key_speed" | int | 50 | Speed of the key vector | |
| "-o", "--odir" | str | Output directory. If None, make 'outputs' directory in the same directory of the input file |
This script animates the tracked parcels behind the AMVs: every template moves along its trajectory over the images it was tracked in, leaving a trail that thins with age -- a direct check that the tracking followed the clouds.
The drawing lives in opentcamv/trajectory_movie.py, a single self-contained file (numpy + matplotlib only) that you can copy into your own project without installing openTCAMV (see "Using trajectory_movie.py in your own code" below).
Click here to open the description
The movie walks through the images once. The parcels of the AMVs centered on an image move through the 2 ntrac + 1 images of their trajectories (one cycle), then one image is shown alone and the next cycle starts, so every image appears exactly once, at the image cadence. Each trail is widest at the parcel and thins linearly with age. With --corotate, every cycle is drawn in a frame rotating with the vortex (it coincides with the storm-centered frame at the AMV time, and the image turns with it), where the motion relative to the rotation -- what --revrot tracks -- is easier to follow.
The image is shown standardized on a gray scale (median removed, spread normalized), which brings out the cloud texture whatever the band. With --raw it is shown as it is, on a colorbar in its own units -- for infrared, the brightness temperature in a colormap such as --image_cmap batlow_r (cold clouds bright, as in IR imagery). --zoom_vlim puts the zoom panels on a scale of their own, each with its own colorbar: the eye, for example, often spans only a few kelvins.
- AMV file: NetCDF file generated by
10_conduct_tracking.pyor20_finalize_tracking.py, withxloc/yloc(rloc/alocunder--polar) andtime2. Track with--traj_int=1so that every image along a trajectory is recorded. - Image file: the NetCDF file that
10_conduct_tracking.pytracked (default: the AMV file'sifnattribute).
Basic usage
$ python 31_animate_trajectories.py ifn [--image image] [--varname varname] [-o ofn] [-s start] [-e end] [--omega omega] [--xlim xmin xmax] [--ylim ymin ymax] [--stride stride] [--checker] [--zoom cx cy half] [--zoom_stride zoom_stride] [--zoom_checker] [--titles titles ...] [--corotate corotate] [--color color] [--clim cmin cmax] [--cmap cmap] [--cmap_range lo hi] [--color_label color_label] [--color_extend {neither,min,max,both}] [--head_color head_color] [--size_scale size_scale] [--linewidth linewidth] [--tail_width tail_width] [--log] [--contrast contrast] [--contrast_fwhm contrast_fwhm] [--raw] [--image_cmap image_cmap] [--image_label image_label] [--no_image_colorbar] [--vlim vmin vmax] [--zoom_vlim vmin vmax] [--rings rings ...] [--rest rest] [--fps fps] [--dpi dpi] [--max_frames max_frames] [--title title] [--no_cycle_label] [--no_spacing]Example (the tutorial sample: the whole domain plus a zoom on the eye)
$ python 31_animate_trajectories.py ../sample/2017_Lan_ns7_nt1.nc --log --contrast 0.5 --zoom 0 0 15 --titles "Inner core" "Eye" --rings 10 20Example (in a frame co-rotating at 1.5e-3 rad/s, parcels colored by their speed relative to it)
$ python 31_animate_trajectories.py ../sample/2017_Lan_ns7_nt1.nc --log --contrast 0.5 --corotate 0.0015 --color speed --zoom 0 0 15 --titles "Inner core" "Eye" --rings 10 20 -o ../sample/outputs/2017_Lan_ns7_nt1_corot.mp4Example (AMVs tracked in an infrared band: the brightness temperature in batlow, the eye panel on a narrower scale)
$ python 31_animate_trajectories.py AMVs_B13.nc --raw --image_cmap batlow_r --vlim 190 300 --zoom_vlim 280 300 --image_label "Brightness temperature (K)" --zoom 0 0 15 --titles "Inner core" "Eye"Example (visible reflectance as it is, 0-115 %, with no image colorbar; parcels colored by the AMV speed on the brighter three quarters of batlow, one colorbar; the image time alone as the frame label)
$ python 31_animate_trajectories.py AMVs_B03.nc --raw --vlim 0 115 --no_image_colorbar --color amv_speed --cmap batlow --cmap_range 0.25 1 --clim 0 40 --color_extend max --color_label "Storm-relative wind speed (m/s)" --title "" --no_cycle_label --no_spacing --titles "~1 min" --xlim -50 50 --ylim -50 50 --stride 3 --size_scale 0.8 --rings 20 40| Argument | Type | Default | Description |
|---|---|---|---|
| ifn | str | AMV file from 10_conduct_tracking.py or 20_finalize_tracking.py |
|
| --image | str | the AMV file's ifn attribute |
image NetCDF the AMVs were tracked in |
| --varname | str | the AMV file's varname attribute |
image variable |
| -o, --ofn | str | outputs/<ifn name>_trajectories.mp4 next to ifn |
output movie: .mp4 (needs ffmpeg) or .gif |
| -s, --start | str | first image to show, e.g. 2017-10-21T02:30 |
|
| -e, --end | str | last image to show | |
| --omega | float | Ω_i candidate to show from a 10_ output holding several (omega dimension) |
|
| --xlim, --ylim | float x2 | the AMV grid | x and y range of the main panel (km) |
| --stride | int | 2 | draw every N-th AMV grid point in the main panel |
| --checker | bool | of those, keep every other one in a checkerboard (spacing x sqrt 2) | |
| --zoom | float x3 | add a zoom panel: center x, center y, half-width (km); repeatable | |
| --zoom_stride | int | 1 | --stride of the zoom panels |
| --zoom_checker | bool | --checker of the zoom panels |
|
| --titles | str ... | panel titles: the main panel's, then the zoom panels' | |
| --corotate | float | 0.0 | draw each cycle in a frame rotating counterclockwise at this angular velocity about the center (rad/s; 0 = storm-centered) |
| --color | str | yellow | parcel color; speed to color by each trajectory's start-to-end speed (storm-relative, or relative to the --corotate frame); or amv_speed by the speed of the AMV itself, |(vx, vy)| (|(vr, vt)| under --polar), storm-relative even under --corotate -- with --revrot the restored velocity, which the start-to-end chord of a curved trajectory underestimates |
| --clim | float x2 | 0 and the 98th percentile | speed range of --color speed or amv_speed (m/s) |
| --cmap | str | plasma | colormap of --color speed or amv_speed: a matplotlib name, or a Scientific colour map of Crameri such as batlow (needs cmcrameri) |
| --cmap_range | float x2 | use only this part of --cmap (fractions 0-1), e.g. 0.25 1 to drop its darkest quarter |
|
| --color_label | str | Speed (m s^-1) |
label of the parcel colorbar (in the co-rotating frame is added for --color speed under --corotate) |
| --color_extend | str | neither | arrow ends on the parcel colorbar for speeds outside --clim: neither, min, max or both |
| --head_color | str | --color |
fill of the parcel dots: a color, or image for the image color where the parcel first appears (the trails keep --color) |
| --size_scale | float | 1.0 | scale of the parcel dots' diameter (their area scales by its square) |
| --linewidth | float | 2.2 | trail width at the parcel (pt; 0.75x in the main panel when zoom panels are added) |
| --tail_width | float | 0.1 | trail width at the start of the trajectory, as a fraction of --linewidth |
| --log | bool | show the log of the image (visible reflectance) | |
| --contrast | float | 0.0 | fraction of the local mean removed from the image; 0.5 brings out the cloud texture |
| --contrast_fwhm | float | 8.0 | width of that local mean (km) |
| --raw | bool | show the image values themselves on a colorbar (e.g. brightness temperature) instead of standardizing them | |
| --image_cmap | str | gray | colormap of the image: a matplotlib name, or a Scientific colour map of Crameri such as batlow (needs cmcrameri) |
| --image_label | str | the variable's long_name and units |
label of the --raw colorbar |
| --no_image_colorbar | bool | under --raw, draw no colorbar for the image (it stays on the --vlim scale) |
|
| --vlim | float x2 | -2.4 2.4, or under --raw the first image's range over the main panel |
color limits of the image: of the standardized image, or in the image's units under --raw |
| --zoom_vlim | float x2 | --vlim |
--vlim of the zoom panels, each then with its own colorbar under --raw |
| --rings | float ... | radii of range rings about the center (km) | |
| --rest | int | 1 | images shown alone after each cycle |
| --fps | float | 2.0 | movie frames per second |
| --dpi | float | 110 | movie resolution |
| --max_frames | int | only the first N movie frames (quick test) | |
| --title | str | the image's name attribute + variable |
frame-label prefix |
| --no_cycle_label | bool | leave the cycle state (AMV time and tracking frame) out of the frame label; with --title "" it is the image time alone |
|
| --no_spacing | bool | leave the parcel spacing (parcels every N km) out of the panel titles |
opentcamv/trajectory_movie.py imports nothing from openTCAMV: copy that one file next to your scripts and it works with any tracker's output, given numpy and matplotlib (scipy only for its local_contrast helper).
from trajectory_movie import animate_trajectories, match_frames # with openTCAMV installed: from opentcamv.trajectory_movie import ...
# images: (nt, ny, nx) on pixel centers x (nx,), y (ny,) in km, taken at times (nt,) (datetime64)
# xloc, yloc: (nset, nstep, ...) parcel positions in km -- one set per AMV time, steps in time order, NaN where not tracked
# step_times: (nset, nstep) time of every step
frame_index = match_frames(step_times, times) # image index of every step
movie = animate_trajectories(images, x, y, xloc, yloc, frame_index, times=times, center=(0, 0), rings=[10, 20],
panels=[dict(stride=2), dict(center=(0, 0), half=15, title="Eye")])
movie.save("trajectories.mp4") # or .gif, which needs no ffmpegFor an openTCAMV output this reads (what 31_ does, without its options):
import xarray as xr
A = xr.open_dataset("2017_Lan_ns7_nt1.nc") # 20_ output, tracked with --traj_int=1
I = xr.open_dataset("2017_Lan_aeqd_sample.nc") # the file 10_ tracked
movie = animate_trajectories(I.B03, I.x.values, I.y.values, A.xloc.values, A.yloc.values,
match_frames(A.time2.values, I.t.values), times=I.t.values,
valid=A.vx.notnull().values, center=(0, 0), panels=[dict(stride=2)])images is read one frame at a time, so a lazily opened xarray.DataArray never has to fit in memory. movie.draw(i) renders movie frame i (movie.fig.savefig(...) then saves it), and movie.animation() returns a matplotlib FuncAnimation (in Jupyter: HTML(movie.animation().to_jshtml())). The docstring of animate_trajectories lists every option: panels, thinning, coloring by speed or any value, the co-rotating frame, and the image display.