arcgis-time-animation について
arcgis-time-animationは、ArcGISで時間対応マップを開発する人向けのスキルです。ArcGIS API for JavaScriptを使った時間範囲、レイヤーのフィルター、TimeSlider操作、再生、アニメーションの方法を解説します。
主な機能
- 01時間範囲と間隔の設定
- 02TimeSliderのモードと再生操作
- 03時間対応レイヤーとクエリのフィルター
- 04時間変化を監視して表示を更新
- 05時系列データのアニメーション
活用シーン
- 01ArcGISマップに時間スライダーを追加
- 02日付範囲でマップレイヤーを絞り込み
- 03時系列データの変化をアニメーション表示
- 04指定期間のレイヤー地物をクエリ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ saschabrunnerch/ arcgis-maps-sdk-js-ai-context --skill arcgis-time-animationインストール後、Claude Code で/arcgis-time-animationを入力して読み込まれたか確認します。
詳細
ArcGIS Time Animation
時間データ、時間対応レイヤー、時間フィルタリング、アニメーションコントロールを使用する場合に、このスキルを使用します。
インポートパターン
ESM(npm)
import TimeExtent from "@arcgis/core/TimeExtent.js";
import TimeInterval from "@arcgis/core/TimeInterval.js";
import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
CDN(動的インポート)
const TimeExtent = await $arcgis.import("@arcgis/core/TimeExtent.js");
const TimeInterval = await $arcgis.import("@arcgis/core/TimeInterval.js");
const TimeSlider = await $arcgis.import("@arcgis/core/widgets/TimeSlider.js");
TimeExtent
開始日と終了日で構成される時間範囲を表します。
const timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-12-31"),
});
// Apply to view (filters all time-aware layers)
view.timeExtent = timeExtent;
// Instant in time (start === end)
const instant = new TimeExtent({
start: new Date("2024-06-15"),
end: new Date("2024-06-15"),
});
// No time filter (show all data)
view.timeExtent = null;
TimeInterval
時間の長さを表します。
const interval = new TimeInterval({
value: 1,
unit: "months",
});
サポートされる単位: milliseconds、seconds、minutes、hours、days、weeks、months、years、decades、centuries
TimeSlider コンポーネント
arcgis-time-slider(マップコンポーネント)
<arcgis-map item-id="YOUR_WEBMAP_ID">
<arcgis-time-slider slot="bottom-left" mode="time-window" loop time-visible>
</arcgis-time-slider>
</arcgis-map>
<script type="module">
const map = document.querySelector("arcgis-map");
const timeSlider = document.querySelector("arcgis-time-slider");
await map.viewOnReady();
// Configure from layer
const layer = map.view.map.layers.find((l) => l.timeInfo);
if (layer) {
await layer.load();
timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
timeSlider.stops = { interval: layer.timeInfo.interval };
}
</script>
TimeSlider ウィジェット(コア API)
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: {
start: new Date("2020-01-01"),
end: new Date("2024-12-31"),
},
timeExtent: {
start: new Date("2024-01-01"),
end: new Date("2024-03-31"),
},
mode: "time-window",
playRate: 1000,
loop: true,
stops: {
interval: {
value: 1,
unit: "months",
},
},
});
TimeSlider のプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
fullTimeExtent | TimeExtent | — | ウィジェットで表示できる完全な時間範囲 |
timeExtent | TimeExtent | — | 現在選択されている時間範囲 |
mode | string | "instant" | アニメーションモード |
stops | object | — | スライダーのスナップ位置 |
playRate | number | 1000 | ステップ間のミリ秒数 |
loop | boolean | false | 最後に達したときに最初から再生する |
layout | string | "auto" | "auto"、"compact"、"wide" |
disabled | boolean | false | 操作を無効にする |
TimeSlider のモード
// Instant - single point in time
mode: "instant";
// Time Window - range with start and end
mode: "time-window";
// Cumulative from Start - everything from start to current
mode: "cumulative-from-start";
// Cumulative from End - everything from current to end
mode: "cumulative-from-end";
カスタムストップ
// Interval-based stops
stops: {
interval: { value: 1, unit: "weeks" }
}
// Specific dates
stops: {
dates: [
new Date("2024-01-01"),
new Date("2024-04-01"),
new Date("2024-07-01"),
new Date("2024-10-01")
]
}
// Number of evenly distributed stops
stops: {
count: 12
}
再生コントロール
timeSlider.play();
timeSlider.stop();
時間の変更を監視する
timeSlider.watch("timeExtent", (timeExtent) => {
console.log("New time range:", timeExtent.start, "to", timeExtent.end);
});
timeSlider.watch("viewModel.state", (state) => {
console.log("State:", state); // "ready", "playing", "disabled"
});
カスタムラベル
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: { start, end },
tickConfigs: [
{
mode: "position",
values: [
new Date("2021-01-01"),
new Date("2022-01-01"),
new Date("2023-01-01"),
],
labelsVisible: true,
labelFormatFunction: (value) => value.getFullYear().toString(),
},
],
labelFormatFunction: (value, type, element, layout) => {
const date = new Date(value);
if (type === "min" || type === "max") {
return date.toLocaleDateString();
}
return date.toLocaleDateString("en-US", {
month: "short",
year: "numeric",
});
},
});
時間対応レイヤー
時間情報を持つ FeatureLayer
const featureLayer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0",
timeInfo: {
startField: "event_date",
endField: "end_date",
interval: {
value: 1,
unit: "days",
},
},
});
// Check if layer supports time
await featureLayer.load();
if (featureLayer.timeInfo) {
console.log("Time field:", featureLayer.timeInfo.startField);
console.log("Full extent:", featureLayer.timeInfo.fullTimeExtent);
}
TimeInfo のプロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
startField | string | 開始日フィールド名(必須) |
endField | string | 終了日フィールド名(任意) |
fullTimeExtent | TimeExtent | データの完全な時間範囲 |
interval | TimeInterval | 推奨されるアニメーション間隔 |
trackIdField | string | トラック識別フィールド(StreamLayer 用) |
その他の時間対応レイヤー
// ImageryLayer
const imageryLayer = new ImageryLayer({
url: "...",
timeInfo: { startField: "acquisition_date" },
});
// MapImageLayer
const mapImageLayer = new MapImageLayer({
url: "...",
timeInfo: { startField: "date_field" },
});
// StreamLayer
const streamLayer = new StreamLayer({
url: "wss://services.arcgis.com/.../StreamServer",
timeInfo: {
trackIdField: "vehicle_id",
startField: "timestamp",
},
purgeOptions: {
displayCount: 1000,
age: 5,
},
});
レイヤーからの TimeSlider の初期化
async function setupTimeSlider(view, layer) {
await layer.load();
if (!layer.timeInfo) {
console.warn("Layer is not time-aware");
return null;
}
const timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: layer.timeInfo.fullTimeExtent,
mode: "time-window",
playRate: 1000,
loop: true,
stops: {
interval: layer.timeInfo.interval || { value: 1, unit: "months" },
},
});
timeSlider.watch("timeExtent", (extent) => {
document.getElementById("currentTime").textContent =
`${extent.start.toLocaleDateString()} - ${extent.end.toLocaleDateString()}`;
});
return timeSlider;
}
時間によるフィルタリング
クライアント側のフィルター
// Filter all time-aware layers via view
view.timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-06-30"),
});
// Filter specific layer via layerView
const layerView = await view.whenLayerView(featureLayer);
layerView.filter = {
timeExtent: new TimeExtent({
start: new Date("2024-03-01"),
end: new Date("2024-03-31"),
}),
};
時間を指定したクエリ
const query = featureLayer.createQuery();
query.timeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-12-31"),
});
query.where = "status = 'active'";
query.returnGeometry = true;
const results = await featureLayer.queryFeatures(query);
アニメーションパターン
統計情報を使用する TimeSlider
timeSlider.watch("timeExtent", async (timeExtent) => {
const query = featureLayer.createQuery();
query.timeExtent = timeExtent;
query.outStatistics = [
{
statisticType: "count",
onStatisticField: "OBJECTID",
outStatisticFieldName: "count",
},
{
statisticType: "sum",
onStatisticField: "value",
outStatisticFieldName: "total",
},
];
const result = await featureLayer.queryFeatures(query);
const stats = result.features[0].attributes;
document.getElementById("count").textContent = stats.count;
document.getElementById("total").textContent = stats.total;
});
時間に基づくハイライト表示
let highlightHandle;
timeSlider.watch("timeExtent", async (timeExtent) => {
if (highlightHandle) highlightHandle.remove();
const query = featureLayer.createQuery();
query.timeExtent = timeExtent;
const layerView = await view.whenLayerView(featureLayer);
const results = await featureLayer.queryFeatures(query);
highlightHandle = layerView.highlight(results.features);
});
手動アニメーションループ
async function animateOverTime(layer, startDate, endDate, intervalDays) {
const current = new Date(startDate);
while (current <= endDate) {
const nextDate = new Date(current);
nextDate.setDate(nextDate.getDate() + intervalDays);
view.timeExtent = new TimeExtent({
start: current,
end: nextDate,
});
await new Promise((resolve) => setTimeout(resolve, 500));
current.setDate(current.getDate() + intervalDays);
}
}
TimeZoneLabel コンポーネント
<arcgis-map>
<arcgis-time-zone-label slot="bottom-left"></arcgis-time-zone-label>
</arcgis-map>
時間に基づくグラフィックスの表示設定
// Set time visibility on individual graphics
graphic.visibilityTimeExtent = new TimeExtent({
start: new Date("2024-01-01"),
end: new Date("2024-06-30"),
});
よくある問題
-
タイムゾーンの問題:日付は JavaScript のローカルタイムゾーンの影響を受けます。一貫性を保つには UTC の日付を使用してください。
const date = new Date("2024-06-15T00:00:00Z"); -
レイヤーの読み込みが必要:
timeInfoはawait layer.load()の後にのみ使用できます。 -
TimeExtent が適用されない:すべての時間対応レイヤーをフィルタリングするには、ビューの
timeExtentを設定する必要があります。 -
Null でフィルターを解除:すべてのデータを表示する(時間フィルターなし)には、
view.timeExtent = nullを使用します。 -
パフォーマンス:時間範囲が広いと多数のフィーチャが返される場合があります。可能な場合はサーバー側クエリを使用してください。
-
Stops の設定が必要:TimeSlider では、スライダーの位置を定義するために
stopsを設定する必要があります。
参考サンプル
timeslider— TimeSlider ウィジェットの基本timeslider-filter— TimeSlider によるデータのフィルタリングtimeslider-component-filter— フィルタリング機能を備えた TimeSlider コンポーネントtime-layer— 時間対応レイヤーの操作widgets-timeslider— TimeSlider ウィジェットの例widgets-timeslider-offset— タイムゾーンオフセットを指定した TimeSliderlayers-scenelayer-time— 時間対応の SceneLayerlayers-voxel-time— 時間対応の VoxelLayerlayers-graphics-visibilitytimeextent— 時間に基づくグラフィックスの表示設定
関連スキル
arcgis-imagery— 時間を含む多次元画像arcgis-layers— レイヤーの設定arcgis-widgets-ui— ウィジェットの配置とスロット