Automated Ocean Current Visualization Pipeline Using Linux and MATLAB
This workflow demonstrates a reproducible pipeline for generating time-series animated visualizations of ocean surface currents, leveraging Linux command-line tools and MATLAB scripting. The process spans data preprocessing, georeferenced quiver plotting, frame annotation, and video synthesis.
Data Extraction and Preparation
NetCDF files containing 3D current fields are subset to surface layer (siglay=1) and essential variables using ncks. A Bash loop handles batch processing with zero-padded identifiers:
for idx in {1..30}; do
padded=$(printf "%04d" "$idx")
ncks -C -v lonc,latc,u,v,Times -F -d siglay,1 "superior_${padded}.nc" "current/cur_${padded}.nc"
done
Geospatial Plotting in MATLAB
A MATLAB script renders each time step as a high-resolution PNG:
- Loads Google Maps satellite imagery as a static background (cached once via
plot_google_map). - Filters model output to a target bounding box (
target_axis = [-90 -87 46.7 48.2]). - Applies magnitude clipping (
±0.2 m/s) and spatial subsampling for clarity. - Overlays velocity vectors scaled by
0.5, adds timestamp, scale bar, and branding.
Key snippet (simplified):
% Load and subset data
load_nc(['cur_', sprintf('%.4d', day_num), '.nc']);
mask = (lonc > -90) & (lonc < -87) & (latc > 46.7) & (latc < 48.2);
xlon = lonc(mask) - 360; xlat = latc(mask);
u_sub = squeeze(u(:,:,mask)); v_sub = squeeze(v(:,:,mask));
% Clip extremes and plot
u_sub = max(min(u_sub, 0.2), -0.2);
v_sub = max(min(v_sub, 0.2), -0.2);
figure('Visible','off');
openfig('fig_google_map_png_superior.fig');
quiver(xlon, xlat, u_sub(t,:) * 0.5, v_sub(t,:) * 0.5, 'Color','m');
title(sprintf('Lake Superior — %s UTC', Times(t,1:19)));
saveas(gcf, sprintf('frames/day%.3d_h%.2d.png', day_num, t));
Video Assembly with FFmpeg
Daily animations are built from annotated frames:
- Logo overlay using
composite(ImageMagick) with precise pixel offsets. - Encoding with
ffmpegat 3 FPS, 1980×1980 resolution, H.264 High Profile, CRF 23.
for day in {213..243}; do
date_str=$(date -d "2023-01-01 +$((day-1)) days" +%Y-%m-%d)
out_dir="fig_uv_5m_avg_zoomIn_daily/${date_str}"
mkdir -p "$out_dir"
# Annotate frames
for hour in $(seq -w 1 24); do
composite -blend 100 -geometry +152+698 \
logo_tmp.png "frames/day$(printf "%03d" $day)_h${hour}.png" \
"${out_dir}/labeled_${hour}.png"
done
# Encode daily movie
ffmpeg -framerate 3 -i "${out_dir}/labeled_%02d.png" \
-s:v 1980x1980 -c:v libx264 -profile:v high -crf 23 \
-pix_fmt yuv420p -r 30 "${out_dir}/current_${date_str}.mp4"
done
Multi-Day Compilation
To concatenate daily videos into a continuous sequence, generate a concat list and use FFmpeg’s concat demuxer:
# Build list
for day in {213..214}; do
d=$(date -d "2023-01-01 +$((day-1)) days" +%Y-%m-%d)
echo "file current_${d}.mp4"
done > video_list.txt
# Merge without re-encoding
ffmpeg -f concat -safe 0 -i video_list.txt -c copy final_current_sequence.mp4
For direct image-to-video conversion (e.g., when frames are pre-rendered), use:
ffmpeg -framerate 3 -i "frames/cur_day%03d_h%02d.png" \
-s:v 1980x1980 -c:v libx264 -crf 23 -pix_fmt yuv420p \
-r 30 lake_superior_currents.mp4