A toolbar-based QGIS plugin for authoring map camera animations via keyframes. Record camera positions from the main map canvas, bake them into per-frame viewport polygons, preview the result inside QGIS, then drive an Atlas export to produce an image sequence for video production.
Status: experimental — v0.2.0
Minimum QGIS version: 3.44
Author: Eric Lawrey — Australian Institute of Marine Science (AIMS)
The plugin does not create videos directly. Instead it produces a standard QGIS Atlas layout that maps exactly one page per animation frame. Any external tool (FFmpeg, DaVinci Resolve, etc.) can then assemble the exported image sequence into a video.
Standard 30 fps export:
See test-animation/ for the source GeoPackage and instructions to reproduce these.
Create Animation → Add / edit keyframes → Bake Frames
→ Preview → Setup Atlas Layout → Export via Atlas → Encode video
| Requirement | Version |
|---|---|
| QGIS | 3.44 or later |
| Python | 3.10 or later (bundled with QGIS) |
| GDAL/OGR | bundled with QGIS |
No additional Python packages are required.
- Go to the Releases
page and download
map_animation_helper.zipfrom the latest release. - In QGIS, open Plugins → Manage and Install Plugins → Install from ZIP.
- Select the downloaded ZIP file and click Install Plugin.
- Make sure your map is set up with the layers you want to animate.
- Click Create Animation in the Map Animation Helper toolbar.
- Choose a save location for the GeoPackage (e.g.
my_animation.gpkg). - Select a CRS. Using the project CRS is recommended.
- Three layers are added to the project:
animation_keyframes— edit this layer to author the animationanimation_baked— generated by Bake; do not edit manuallyanimation_settings— one-row settings table
- Navigate the map canvas to the first camera position.
- Click Add Keyframe. A point is added at the canvas centre.
- Repeat for each position in your animation.
- Use the QGIS attribute table to adjust
duration_s(seconds to the next keyframe) andeasingfor each keyframe.
| Action | Description |
|---|---|
| Show Keyframe | Move the canvas to the selected keyframe's saved position |
| Update Keyframe | Overwrite the selected keyframe with the current canvas state |
| Insert Keyframe | Insert a new keyframe after the selected one |
| Move Earlier / Move Later | Reorder keyframes without editing seq manually |
Select exactly one feature in the animation_keyframes layer before using
these actions.
Click Bake Frames. The plugin:
- validates all keyframes,
- interpolates camera state at every export frame interval (
fps_export), - writes one polygon feature per frame to
animation_baked.
The animation_settings layer stores fps_export (default 30) and
fps_preview (default 10). Edit these directly in the attribute table.
Click Preview to play the animation in the main map canvas at
fps_preview speed. Click Stop Preview (the same button) or press
Escape to stop. The canvas stays at the last displayed frame when
playback ends.
If keyframes have been modified since the last bake, the plugin auto-bakes before starting the preview.
Click Setup Atlas Layout. The plugin:
- creates a print layout named Animation_{gpkg name} (derived from the GeoPackage filename, so multiple animations get separate layouts),
- sets the page size to match the aspect ratio of the canvas when the animation was created (long edge = 297 mm),
- adds a full-page map item driven by the
animation_bakedAtlas coverage, - configures data-defined extent and rotation from baked attributes,
- opens the layout designer.
From the layout designer, configure output format and resolution in
Atlas → Export Atlas, then export. The filename_stub field on each
baked feature (frame_000000, frame_000001, …) is used as the filename
for each exported image.
The framerate that will be generated by the Bake Frames operation is set
in the fps_export attribute of the animation_settings table. Edit this
value by right clicking on the animation_settings then Open Attribute Table.
Enable editing the modify the settings as required. You will need to rebake any
existing animations. The video generation FPS will also need to be adjusted to
match.
| Button | Precondition | Description |
|---|---|---|
| Create Animation | None | Create a new GeoPackage and animation schema |
| Add Keyframe | Active layer = keyframes layer | Append a keyframe from current canvas |
| Show Keyframe | Exactly 1 keyframe selected | Move canvas to selected keyframe |
| Update Keyframe | Exactly 1 keyframe selected | Overwrite selected keyframe from canvas |
| Insert Keyframe | Exactly 1 keyframe selected | Insert a new keyframe after selected |
| Move Earlier | Exactly 1 keyframe selected | Swap seq with the previous keyframe |
| Move Later | Exactly 1 keyframe selected | Swap seq with the next keyframe |
| Bake Frames | Active layer = keyframes layer | Regenerate animation_baked |
| Preview | Active layer = keyframes layer | Play / stop baked animation in canvas |
| Setup Atlas Layout | Active layer = keyframes layer | Create and open the Atlas print layout |
All data is stored in a single GeoPackage.
| Field | Type | Description |
|---|---|---|
seq |
Integer | Playback order (gaps allowed; no duplicates) |
duration_s |
Double | Seconds to next keyframe (or terminal hold for last) |
easing |
Text | linear, ease_in, ease_out, ease_in_out, smooth_linear |
view_width |
Double | Viewport width in map units |
view_height |
Double | Viewport height in map units |
scale |
Double | Map scale denominator |
rotation |
Double | Rotation in degrees clockwise |
The camera centre position is stored in the point geometry itself (no
center_x/center_y attributes). This means keyframes can be repositioned
using the standard QGIS Move Feature tool.
One feature per frame. The polygon geometry is the rotated viewport rectangle, which can be used directly as the Atlas coverage extent.
| Field | Type | Description |
|---|---|---|
frame_no |
Integer | Zero-based frame number |
time_ms |
Integer | Milliseconds from animation start |
src_seq_a |
Integer | Source start keyframe seq |
src_seq_b |
Integer | Source end keyframe seq |
center_x |
Double | Interpolated centre X |
center_y |
Double | Interpolated centre Y |
view_width |
Double | Interpolated viewport width |
view_height |
Double | Interpolated viewport height |
scale |
Double | Interpolated scale |
rotation |
Double | Interpolated rotation |
filename_stub |
Text | e.g. frame_000042 |
One row only. Edit directly in the attribute table.
| Field | Default | Description |
|---|---|---|
fps_preview |
10 | Frames per second for in-canvas preview |
fps_export |
30 | Frames per second for bake and Atlas export |
dirty |
1 | 1 = baked output is out of date |
author_canvas_w_px |
— | Canvas pixel width when animation was created |
author_canvas_h_px |
— | Canvas pixel height when animation was created |
plugin_version |
— | Plugin version string |
last_baked_utc |
— | ISO timestamp of last successful bake |
| Value | Behaviour |
|---|---|
linear |
Constant speed |
ease_in |
Starts slow, accelerates (cubic) |
ease_out |
Starts fast, decelerates (cubic) |
ease_in_out |
Slow–fast–slow (cubic) |
smooth_linear |
Smooth velocity interpolation |
Easing applies to position, zoom, and rotation simultaneously within each
segment. Zoom (scale, view_width, view_height) uses geometric
interpolation so that each frame covers an equal zoom ratio, giving a
perceptually smooth zoom.
- Aspect ratio: the authored canvas aspect ratio is preserved in the layout page size. If you resize the QGIS window significantly after creating the animation, re-run Setup Atlas Layout to get an updated page.
- Image size must be even: the width and height of the exported image should be even in size for the conversion to MP4 to work.
- Resolution: set DPI in the Atlas export dialog. 150 dpi is sufficient for web video; 300 dpi for print-quality frames.
- Filename format: the
filename_stubfield (frame_000000…) produces zero-padded names that sort correctly for FFmpeg and similar tools. - Export all frames to a single folder before running FFmpeg.
FFmpeg is a free, open-source command-line tool that assembles image sequences into video files. It is not bundled with QGIS and must be installed separately.
- Go to https://www.gyan.dev/ffmpeg/builds/ and download the latest ffmpeg-release-essentials.zip build.
- Extract the ZIP to a permanent location, for example
C:\ffmpeg. - Add the
binfolder to your system PATH:- Open Start → Search → "Edit the system environment variables".
- Click Environment Variables.
- Under System variables, select Path and click Edit.
- Click New and enter
C:\ffmpeg\bin. - Click OK on all dialogs.
- Open a new Command Prompt or PowerShell window and verify:
ffmpeg -version
Using Homebrew (install Homebrew first if needed):
brew install ffmpegVerify:
ffmpeg -versionsudo apt update && sudo apt install ffmpegVerify:
ffmpeg -versionAfter exporting all Atlas pages to a folder (e.g. frames/), open a
terminal or Command Prompt, change into that folder, and run one of the
commands below.
Important: the
-frameratevalue must match thefps_exportsetting you used in theanimation_settingstable (default 30).
Tip — odd pixel dimensions: H.264 requires width and height to be divisible by 2. If your exported images have an odd dimension (e.g. 1771×1240) FFmpeg will fail with
width not divisible by 2. Add-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2"to crop by at most one pixel on each axis. Re-exporting hours of Atlas frames at a different DPI just to fix a one-pixel issue is not worth it.
ffmpeg -framerate 30 -i frame_%06d.png -c:v libx264 -pix_fmt yuv420p output.mp4-framerate 30— playback speed; adjust to match yourfps_export.-i frame_%06d.png— input pattern matchingframe_000000.png,frame_000001.png, etc.-pix_fmt yuv420p— required for compatibility with most video players and web platforms.
ffmpeg -framerate 30 -i frame_%06d.png -c:v libx264 -crf 18 -pix_fmt yuv420p output.mp4-crf controls quality: lower = better quality, larger file (range 0–51;
18 is visually near-lossless, 23 is the default).
ffmpeg -framerate 30 -i frame_%06d.png -c:v libx265 -crf 20 -pix_fmt yuv420p output.mp4Note: H.265 is not supported by all players. Prefer H.264 for maximum compatibility.
ffmpeg -framerate 30 -i frame_%06d.png -c:v png output.movffmpeg -framerate 10 -i frame_%06d.png -vf "fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gifAdjust scale=800 to set the output width in pixels; height is computed
automatically.
Export only a specific frame range (e.g. frames 60–120):
ffmpeg -framerate 30 -start_number 60 -i frame_%06d.png -frames:v 60 \
-c:v libx264 -pix_fmt yuv420p output_clip.mp4Double playback speed (no re-encode needed):
ffmpeg -i output.mp4 -filter:v "setpts=0.5*PTS" -c:v libx264 -pix_fmt yuv420p output_fast.mp4You can create a map animation with simulated motion blur by rendering at a higher frame rate,
then down sampling in time. We use tmix to blend frames. For full 360 shutter angle sampling
use equal weights.
ffmpeg -framerate 120 -i <path to atlas export>\frame_%06d.png -vf "tmix=frames=4:weights='0 1 1 0',fps=30,crop=trunc(iw/2)*2:trunc(ih/2)*2,format=yuv420p" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p motion-blur.mp4Parameter summary:
-
-framerate 120Treats the input image sequence as 120 FPS. -
-i <path to atlas export>\frame_%06d.pngReads numbered PNG frames such asframe_000001.png,frame_000002.png, etc. -
-vf "tmix=frames=4:weights='1 1 1 1',fps=30,crop=trunc(iw/2)*2:trunc(ih/2)*2,format=yuv420p"Applies video filters (must be a single-vfargument; multiple-vfflags override each other):tmix=frames=4blends every 4 successive frames togetherweights='0 1 1 0'combine the two middle frames to have a 180 degree camera angle. Blending all four frames results in motion that is too blurry.fps=30outputs the final video at 30 FPScrop=trunc(iw/2)*2:trunc(ih/2)*2trims by at most 1 pixel to ensure even dimensionsformat=yuv420pconverts to a widely compatible pixel format
-
-c:v libx264Encodes the video using H.264. -
-crf 18Sets output quality. Lower values give higher quality and larger files. The sample videos use a crf of 30. -
-preset slowUses slower encoding for better compression efficiency. -
-pix_fmt yuv420pEnsures the final file is compatible with common video players. -
motion-blur.mp4Output file name.
Example simulated motion blur (rendered at 120 fps, blended down to 30 fps):
- One animation per GeoPackage.
- No timeline panel — keyframe timing is edited via the attribute table.
- The plugin does not detect manual edits to keyframes (e.g. moving points or editing attributes directly). Re-bake manually after such changes.
- No automatic Atlas layout configuration beyond the single map item.
- Preview does not restore the pre-preview canvas view on stop.
- CRS mismatch between the project and keyframe layer may produce distorted bounding boxes; best practice is to keep both in the same CRS.
MIT — see LICENSE.
Everything below is for contributors and developers working on the plugin source code.
- QGIS 3.44 or later
- Git
- Conda (Miniconda or Miniforge recommended)
-
Clone this repository:
git clone https://github.com/open-AIMS/qgis-animated-atlas.git cd qgis-animated-atlas -
Create the conda environment (used for the test-animation data download script; the plugin itself runs inside QGIS's Python):
conda env create -f environment.yml conda activate qgis-animated-atlas
-
Symlink the plugin into your QGIS plugins directory. Find the directory via Settings → User Profiles → Open Active Profile Folder then navigate into
python/plugins/. Typical paths:# Windows %APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\ # Linux ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/ # macOS ~/Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins/Windows (run Command Prompt as Administrator):
mklink /D "%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\map_animation_helper" "C:\path\to\qgis-animated-atlas\map_animation_helper"
Linux / macOS:
ln -s /path/to/qgis-animated-atlas/map_animation_helper \ ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/map_animation_helperUsing a symlink means edits to the source are reflected immediately without copying files after each change.
-
Restart QGIS (or reload plugins) and enable Map Animation Helper in Plugins → Manage and Install Plugins.
-
Install the Plugin Reloader plugin from the QGIS plugin repository to reload the plugin without restarting QGIS during development.
map_animation_helper/
__init__.py Plugin entry point
metadata.txt QGIS plugin metadata
plugin.py Plugin class and toolbar wiring
actions.py Toolbar action handlers
schema.py GeoPackage layer / table creation
layer_io.py Layer resolution and editing helpers
settings_io.py Settings row read / write
capture.py Canvas state capture and restore
validation.py Layer and feature validation
bake.py Interpolation and bake algorithm
geometry.py Rotated viewport polygon, angle maths
preview.py Timer-based preview playback
ui_messages.py User-facing messages
test-animation/ Test animation data and reproduction scripts
README.md
LICENSE
CHANGELOG.md
CONTRIBUTING.md
plugin-spec_v2.md Functional specification
environment.yml Conda environment for dev scripts
package_plugin.py Builds installable ZIP
The installable ZIP must contain only the map_animation_helper/ folder
(not the whole repository). To create it:
python package_plugin.pyThis produces map_animation_helper.zip in the repository root. To publish
a release:
- Bump the version in
map_animation_helper/metadata.txt. - Add a dated entry in
CHANGELOG.md. - Commit and tag:
git tag v0.x.0 - Push the tag:
git push origin v0.x.0 - Create a GitHub Release from the tag and attach
map_animation_helper.zip.
See CONTRIBUTING.md.
