map-animation について
map-animationは、映像制作者やデータを用いた解説向けのエディトリアルな地図アニメーションを作るスキルです。Google Earth StudioとAfter Effectsのカメラ移動、GeoJSON/SVGマップのオーバーレイ、12fpsのグラフィック表現を組み合わせます。
主な機能
- 01Google Earth Studioでカメラ移動を作成
- 02GeoJSONまたはSVGからベクターマップを構築
- 03ピン、ルート、地域、ラベルをアニメーション化
- 04グラフィック動作を12fpsに量子化
- 05座標配列からシーケンスを生成
活用シーン
- 01ドキュメンタリーで場所を示す
- 02ルート、移住、サプライチェーンを可視化
- 03地理的な地域を強調してラベル付け
- 04イベントデータから地図動画を繰り返し生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ map-animation-skills --skill map-animationインストール後、Claude Code で/map-animationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/earth-studio-to-ae.mdvector-maps.md詳細
マップアニメーション
エディトリアルな「Vox風」のアニメーションマップを作成します。ベースマップの上をカメラがズーム、周回、パンしながら、位置ピンが落ち、ルートが描画され、地域がハイライトされ、ラベルが表示されます。動きは意図的な12fpsのカクつきに揃え、写真のようではなくグラフィックとして見せます。制作方法は2つあります。Google Earth Studioでフォトリアルなベース映像を作成してAfter Effectsで合成する方法と、RemotionやD3で座標配列を使って動かす、コードでレンダリング可能なベクターマップ(GeoJSON/SVG)です。
使用する場面
- 場所を示すドキュメンタリー/解説映像のパート:地球から都市へズームする、ランドマークを周回する、国境に沿ってパンする。
- 地理上の移動を示す:2地点間にルートを描く、サプライチェーン、移住、飛行経路を示す。
- 国、州、地域をハイライトしてラベルを付ける。地理を段階的に解説する。
- 座標やイベントのリストを、繰り返し使えるデータ駆動型の動画用マップシーケンスに変換する。
制作方法を選ぶ
| 必要なもの | 制作方法 | 出力 |
|---|---|---|
| フォトリアルな地形、実際の衛星画像/3D建物、シネマティックなカメラ | Google Earth Studio → After Effects | 画像シーケンス+カメラ.jsx。AEで合成 |
| すっきりとしたエディトリアルなベクター調、完全な制御、データ駆動型 | RemotionまたはD3のベクター(GeoJSON/SVG) | コードでレンダリングしたMP4/GIF、またはウェブSVG |
| どちらの上にもピン/ルート/ハイライト/ラベルを重ねる | オーバーレイレイヤー | AEのシェイプ/ヌルレイヤー、またはReact/SVGコンポーネント |
多くの「Vox風」作品では、これらを組み合わせます。ベースのカメラ移動にはEarth Studioを使い、その上にベクターオーバーレイでピン、ルート、ラベルを重ねます。
特徴的な表現:12fpsのカクつき
このスタイルを最もよく特徴づけるのは、24fpsの編集の中でマップグラフィックを12fpsでアニメーションさせることです。ベース映像と音声は24fpsで滑らかに再生し、グラフィックレイヤー(カメラに連動するピン、描画されるルート、ポップするラベル)は12fpsで段階的に変化させます。その結果、滑らかな動画ではなく、意図的に手作りした「インフォグラフィック」のような動きになります。
- **計算方法:**24fpsでは、グラフィックレイヤーの各値を2フレーム保持します(偶数フレームでのみ更新)。30fpsでは、約2~3フレーム保持します(約12.5/10fps)。
- **After Effects:**レイヤー/コンポに
posterizeTime(12)を適用するか、グラフィックを12fpsのプリコンポジションにまとめて、24fpsのマスター内に配置します。 - **Remotion/コード:**アニメーション値を計算する前にフレームをスナップします。
// Quantize the timeline to 12fps "steps" while the comp renders at 24fps.
const STEP = Math.round(fps / 12); // 2 at 24fps
const f = Math.floor(frame / STEP) * STEP; // use `f`, not `frame`, for graphic motion
const progress = interpolate(f, [0, 48], [0, 1], { extrapolateRight: "clamp" });
ベース映像/カメラは滑らかな状態(24fps)に保ち、カクつかせるのはグラフィックオーバーレイだけにします。すべてをカクつかせると、スタイルではなくフレーム落ちのように見えます。
制作方法A — Google Earth Studio → After Effects
Earth Studioは、Googleの3D地球上でカメラにキーフレームを設定できるブラウザツールです。AEに読み込む2つのファイルをエクスポートします。レンダリングされたフレームの画像シーケンスと、After Effectsで正確な3Dカメラを再構築する**カメラトラック.jsx**スクリプトです。
- Earth Studioでキーフレームを設定する(ブラウザ):カメラ位置(緯度/経度/高度)、傾き、方位、視野角にキーフレームを設定します。自動イージングを使うか、キーフレームごとにカスタムイージングを設定します。動きはゆっくりと意図的にし、急旋回ではなく場所を見せる動きにします。
- エクスポート → レンダリング:画像シーケンス(JPG/PNG)を選び、解像度とfpsを設定します。「3D Camera Export」/After Effectsのチェックボックスを有効にして、フレームと一緒に
.jsxおよびトラックポイントのファイルも出力します。 - **After Effectsで:**画像シーケンスをフッテージとして読み込み、**File → Scripts → Run Script File…**を選択して、エクスポートした
.jsxを実行します。Earth Studioの動きとフレーム単位で一致するキーフレームを持つ3Dカメラとヌルレイヤーを含むコンポが作成されます。 - **オーバーレイをトラックに親子付けする:**ピン/ラベルのヌルレイヤーを、読み込んだ3Dカメラ/トラックのヌルレイヤーに親子付けします。これにより、カメラの移動中もマップ上の正しい位置に固定されます(
.jsxには、Earth Studioで指定した座標のトラックポイントが含まれます)。 - **カクつきを適用する:**オーバーレイのプリコンポジションのみに適用し(
posterizeTime(12))、ベース映像のカラーグレーディングを行ってからレンダリングします。
正確なキーフレーム/エクスポート設定、.jsxの読み込み手順、トラックポイントをスクリーン空間から緯度/経度に対応付ける方法については、references/earth-studio-to-ae.mdを参照してください。
パス B — ベクターマップ(GeoJSON / SVG)
すっきりとしたエディトリアルな見た目にするには、GeoJSON を SVG パスに投影してアニメーションさせます。座標が正しく配置されるように、実際の投影法を使います。
import { geoMercator, geoPath } from "d3-geo";
const projection = geoMercator().fitSize([1920, 1080], geojson); // fit map to frame
const toPath = geoPath(projection);
const [x, y] = projection([lng, lat]); // any lat/long → screen pixel
geojson.features を <path d={toPath(f)} /> として描画します。同じ projection([lng,lat]) で、各ピンの座標を画面上の位置に変換するため、カメラ(scale/translate 変換)が動いても、ピン、ルート、ラベルはすべて同じ座標空間を共有します。
ベクター上のカメラ移動は、対象の緯度・経度に向けて CSS/transform でズームとパンを行います。
const [tx, ty] = projection([targetLng, targetLat]);
const scale = interpolate(f, [0, 36], [1, 4], { extrapolateRight: "clamp" });
// transform-origin at the target so the zoom homes in on it
const transform = `translate(${960 - tx*scale}px, ${540 - ty*scale}px) scale(${scale})`;
オーバーレイ:ピン、ルート、ハイライト、ラベル
ピンのドロップ(落として、静止させ、必要に応じてパルスさせる)— 座標の配列に沿って時間差で表示します。
const pins = [{ name:"Kyiv", lng:30.52, lat:50.45 }, { name:"Lviv", lng:24.03, lat:49.84 }];
pins.map((p, i) => {
const local = f - i * STEP * 4; // stagger, quantized to the 12fps step
const drop = spring({ frame: local, fps, config:{ damping: 12 } }); // overshoot = settle
const [x, y] = projection([p.lng, p.lat]);
return <Pin key={p.name} x={x} y={y - 40*(1-drop)} opacity={local > 0 ? 1 : 0} />;
});
ルートの描画 — 投影法を使ってパスを作成し、stroke-dashoffset で線を描画します。
// route = [[lng,lat], ...]; project each point, join into an SVG path
const d = route.map((c,i) => (i ? "L" : "M") + projection(c).join(" ")).join(" ");
// in the component: pathLength=1, strokeDashoffset = 1 - drawProgress
const draw = interpolate(f, [12, 60], [0, 1], { extrapolateLeft:"clamp", extrapolateRight:"clamp" });
pathLength="1" を指定すると、ルートの長さが正規化されるため、1 つの進捗値(0→1)で描画できます。線の先頭に沿って移動するドットを、offset-distance で追加します。大圏航路/曲線の飛行ルートには、d3.geoInterpolate(a, b) で中間点をサンプリングします。
地域のハイライト — 該当する GeoJSON フィーチャーを塗りつぶす/線で強調し、その他は暗くして(フォーカス+コンテキスト)表示します。
const isTarget = (f) => f.properties.name === "France";
// target: fill accent, opacity 1, stroke up; others: opacity .35
塗りの不透明度を補間してハイライトを表示するか、クリップパスマスクでワイプ表示します。色は意味と結び付け、同じシーンの途中で決して割り当て直さないでください。
ラベルコールアウト — ピンのラベル、または引き出し線付きのボックスです。ピンが配置された後にフェード/スライド表示し、projection([lng,lat]) に固定してカメラを追従させます。ラベルは水平に保ち、安全領域内に配置してください。地図と一緒に回転させないでください。
データ駆動のシーケンス
順序付き配列を 1 つ使ってシーケンス全体を制御し、地図自体が物語を語るようにデータから再レンダリングします。
const beats = [
{ lng: 30.52, lat: 50.45, label: "Kyiv", hold: 48 },
{ lng: 24.03, lat: 49.84, label: "Lviv", hold: 36, route: true },
];
// Each beat = a Sequence: camera flies to (lng,lat), pin drops, label pops, route draws if set.
// Total duration = sum(holds); compute it in calculateMetadata, not by hand.
1 つの配列で、カメラのターゲット、ピンの順序、ルートの区間、ラベルを同期させます。データを入れ替えるだけで新しい地図シーケンスを作成でき、キーフレームを設定し直す必要はありません。
出力チェックリスト
- カメラの動きはゆっくりと意図的に(急激に動かさず、状況を示す)し、すべてのキーフレームでイージングを使います。
- グラフィックオーバーレイは 12fps でカクつかせ、ベース映像/カメラは滑らかに保ちます(24fps)。
- カメラの移動中も、ピン/ルート/ラベルを緯度・経度に固定します(トラックに親子付けする/投影法を共有する)。
- ルートは読み進める順序で描画し、ハイライトはフォーカス+コンテキストを使います(対象を明るくし、その他を暗くする)。
- ラベルは水平で読みやすく、安全領域内に配置し、色のルールに一貫性を持たせます。
- データ駆動のパスでは、座標/ビート配列を唯一の信頼できる情報源にします。
納品と検証(レンダリングした静止画 → MP4)
パッケージ化されたヘルパー(
scripts/):scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.pngで静止画をタイル状に並べ、scripts/probe-mp4.sh out.mp4 [WxH] [fps]でエンコード結果を検証します。詳しくはscripts/README.mdを参照してください。
コードでレンダリング可能な方法は Remotion のコンポジションです。フレームが決定的なので、シーク用ハーネスなしで任意の正確なフレームをヘッドレスでレンダリングできます。納品物が MP4/GIF(ベクターマップ、データ駆動、正確なラベル)の場合や、データセットごとにバッチ処理する場合は、この方法を使います。Earth Studio + AE のコンポジションの場合、納品物は AE プロジェクトとレンダリング済み映像になり、マスターコンポジションをスクラブして検証します。
出力要件:
- コンポジションが登録された Remotion プロジェクト(
<Composition>+ zodschema+defaultProps)。すべての動きはフレーム駆動にします(タイマー/Date.now()/Math.random()は使用しません)。 - 納品物 = レンダリング済みの
out/*.mp4(新しいデータからマップを再レンダリングできるよう、プロジェクトとbeats/座標の配列も含めます)。 - 12fps のスタッターはハードコードせず、
fpsから計算します(フレームを量子化します)。これにより、fps を変更しても維持されます。 - 再生時間がデータに依存する(ビートの保持時間の合計など)場合は、手動で設定せず、
calculateMetadataで計算します。
検証ループ — 静止画をレンダリング → 確認 → エンコード。 まず単一フレームをレンダリングします(低コストで、エンコード不要)。それらを確認し、フレームが正しくなってから一度だけエンコードします。
# Frame-exact stills at start / each beat / end — render with the SHIPPED props (the real beats array)
npx remotion still MapSeq out/f-start.png --frame=0 --props='{...}'
npx remotion still MapSeq out/f-beat2.png --frame=N --props='{...}' # mid a beat's hold
npx remotion still MapSeq out/f-end.png --frame=L --props='{...}' # L = durationInFrames - 1
# Inspect each PNG — FIDELITY: every pin sits on the correct lat/long, the route connects the right
# cities, the highlighted region is the intended one, labels are spelled right and readable. And land
# on TWO adjacent frames inside a move to confirm the overlay holds for 2 frames (the 12fps step),
# while the base camera is still advancing every frame.
# ARTIFACTS: labels off-canvas/clipped, pins drifting off their coordinate, route overshooting,
# missing map tiles/fonts, projection cutoff at the antimeridian.
# Only after the stills check out, encode:
npx remotion render MapSeq out/map.mp4 --props='{...}'
npx remotion compositionsはdurationInFrames/fpsを読み取り、最終フレームとビートごとのサンプルフレームを選びます。- データ駆動/バッチ処理: すべてのデータセットをバッチレンダリングする前に、代表的な
beats配列を1つ、静止画で検証します。投影やラベルの不具合は N 回ではなく、一度で見つけられます。 - README 用デモ GIF を手軽に作成:
npx remotion render MapSeq out/demo.gif --codec=gif。
完了前の確認事項:
npx remotion stillでフレーム 0、各ビートの中間、最終フレームをエラーなくレンダリングできること(エラーや、タイル/フォントの欠落がないこと)。- すべてのピン/ルート/ラベルが、確認したすべてのフレームで正しい投影座標に配置されていること(既知の都市でスポットチェックします)。
- 隣り合う2つのオーバーレイフレームが同一であること(12fps のステップで保持される)。同時にベースカメラは進んでいること。スタッターが実際に発生し、グラフィックレイヤーのみに適用されていることを確認します。
- フレーム駆動のみであること。
Date.now()/Math.random()/タイマーは使用せず、出荷する beats が正しくレンダリングされること(defaultPropsだけでなく)。 - MP4 全体がエンコードされ、再生できること。(任意)README 用の GIF もレンダリングします。
参考ファイル
references/earth-studio-to-ae.md— Google Earth Studio のキーフレームとエクスポート設定、After Effects の.jsxカメラインポートワークフロー、オーバーレイをトラックポイントにペアレント化する方法、緯度/経度 ↔ 画面座標のマッピング、オーバーレイのプリコンポジションへの 12fps スタッターの適用。references/vector-maps.md— GeoJSON の投影設定、ベクターカメラを緯度/経度にズーム/パンする方法、ピンのドロップ/ルートの描画/領域のハイライト/ラベルコールアウトのレシピ、データ駆動のbeatsパターン、コードと AE 向けの 12fps スタッター量子化の完全なレシピ。