Skip to content

open-AIMS/qgis-animated-atlas

Repository files navigation

Map Animation Helper — QGIS Plugin

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)


Overview

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.

Example output

Test animation with 5 keyframes

Standard 30 fps export:

https://github.com/open-AIMS/qgis-animated-atlas/raw/main/test-animation/test1.mp4

See test-animation/ for the source GeoPackage and instructions to reproduce these.

Workflow at a glance

Create Animation  →  Add / edit keyframes  →  Bake Frames
      →  Preview  →  Setup Atlas Layout  →  Export via Atlas  →  Encode video

Requirements

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.


Installation

  1. Go to the Releases page and download map_animation_helper.zip from the latest release.
  2. In QGIS, open Plugins → Manage and Install Plugins → Install from ZIP.
  3. Select the downloaded ZIP file and click Install Plugin.

Quick start

1 — Create an animation

  1. Make sure your map is set up with the layers you want to animate.
  2. Click Create Animation in the Map Animation Helper toolbar.
  3. Choose a save location for the GeoPackage (e.g. my_animation.gpkg).
  4. Select a CRS. Using the project CRS is recommended.
  5. Three layers are added to the project:
    • animation_keyframes — edit this layer to author the animation
    • animation_baked — generated by Bake; do not edit manually
    • animation_settings — one-row settings table

2 — Add keyframes

  1. Navigate the map canvas to the first camera position.
  2. Click Add Keyframe. A point is added at the canvas centre.
  3. Repeat for each position in your animation.
  4. Use the QGIS attribute table to adjust duration_s (seconds to the next keyframe) and easing for each keyframe.

3 — Edit keyframes

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.

4 — Bake frames

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.

5 — Preview

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.

6 — Set up the Atlas layout

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_baked Atlas 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.

Frequently Asked Questions

How do I change the frame rate of the generated animation

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.


Toolbar reference

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

Data model

All data is stored in a single GeoPackage.

animation_keyframes (Point)

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.

animation_baked (Polygon)

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

animation_settings (no geometry)

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

Easing functions

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.


Atlas export tips

  • 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_stub field (frame_000000 …) produces zero-padded names that sort correctly for FFmpeg and similar tools.
  • Export all frames to a single folder before running FFmpeg.

Creating a video with 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.

Installing FFmpeg

Windows

  1. Go to https://www.gyan.dev/ffmpeg/builds/ and download the latest ffmpeg-release-essentials.zip build.
  2. Extract the ZIP to a permanent location, for example C:\ffmpeg.
  3. Add the bin folder 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.
  4. Open a new Command Prompt or PowerShell window and verify:
    ffmpeg -version
    

macOS

Using Homebrew (install Homebrew first if needed):

brew install ffmpeg

Verify:

ffmpeg -version

Linux (Debian / Ubuntu)

sudo apt update && sudo apt install ffmpeg

Verify:

ffmpeg -version

Assembling the image sequence into a video

After 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 -framerate value must match the fps_export setting you used in the animation_settings table (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.

Standard H.264 MP4 — best compatibility (YouTube, web, most players)

ffmpeg -framerate 30 -i frame_%06d.png -c:v libx264 -pix_fmt yuv420p output.mp4
  • -framerate 30 — playback speed; adjust to match your fps_export.
  • -i frame_%06d.png — input pattern matching frame_000000.png, frame_000001.png, etc.
  • -pix_fmt yuv420p — required for compatibility with most video players and web platforms.

Higher quality H.264 (larger file, better detail)

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).

H.265 / HEVC MP4 — smaller file, same quality

ffmpeg -framerate 30 -i frame_%06d.png -c:v libx265 -crf 20 -pix_fmt yuv420p output.mp4

Note: H.265 is not supported by all players. Prefer H.264 for maximum compatibility.

Lossless PNG video (large file, no quality loss)

ffmpeg -framerate 30 -i frame_%06d.png -c:v png output.mov

GIF (short animations only — large file, 256-colour limit)

ffmpeg -framerate 10 -i frame_%06d.png -vf "fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif

Adjust scale=800 to set the output width in pixels; height is computed automatically.


Trimming or speeding up the video

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.mp4

Double playback speed (no re-encode needed):

ffmpeg -i output.mp4 -filter:v "setpts=0.5*PTS" -c:v libx264 -pix_fmt yuv420p output_fast.mp4

Motion blur video export

You 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.mp4

Parameter summary:

  • -framerate 120 Treats the input image sequence as 120 FPS.

  • -i <path to atlas export>\frame_%06d.png Reads numbered PNG frames such as frame_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 -vf argument; multiple -vf flags override each other):

    • tmix=frames=4 blends every 4 successive frames together
    • weights='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=30 outputs the final video at 30 FPS
    • crop=trunc(iw/2)*2:trunc(ih/2)*2 trims by at most 1 pixel to ensure even dimensions
    • format=yuv420p converts to a widely compatible pixel format
  • -c:v libx264 Encodes the video using H.264.

  • -crf 18 Sets output quality. Lower values give higher quality and larger files. The sample videos use a crf of 30.

  • -preset slow Uses slower encoding for better compression efficiency.

  • -pix_fmt yuv420p Ensures the final file is compatible with common video players.

  • motion-blur.mp4 Output file name.

Example simulated motion blur (rendered at 120 fps, blended down to 30 fps):

https://github.com/open-AIMS/qgis-animated-atlas/raw/main/test-animation/test1-motion-blur-180.mp4

Known limitations

  • 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.

License

MIT — see LICENSE.



Development

Everything below is for contributors and developers working on the plugin source code.


Development setup

Prerequisites

  • QGIS 3.44 or later
  • Git
  • Conda (Miniconda or Miniforge recommended)

Clone and install

  1. Clone this repository:

    git clone https://github.com/open-AIMS/qgis-animated-atlas.git
    cd qgis-animated-atlas
  2. 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
  3. 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_helper

    Using a symlink means edits to the source are reflected immediately without copying files after each change.

  4. Restart QGIS (or reload plugins) and enable Map Animation Helper in Plugins → Manage and Install Plugins.

  5. Install the Plugin Reloader plugin from the QGIS plugin repository to reload the plugin without restarting QGIS during development.


Repository structure

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

Packaging a release

The installable ZIP must contain only the map_animation_helper/ folder (not the whole repository). To create it:

python package_plugin.py

This produces map_animation_helper.zip in the repository root. To publish a release:

  1. Bump the version in map_animation_helper/metadata.txt.
  2. Add a dated entry in CHANGELOG.md.
  3. Commit and tag: git tag v0.x.0
  4. Push the tag: git push origin v0.x.0
  5. Create a GitHub Release from the tag and attach map_animation_helper.zip.

Contributing

See CONTRIBUTING.md.

About

QGIS plugin that allows keyframe animation

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages