mcpField
A
arcgis-time-animation

arcgis-time-animation

github.com/saschabrunnerch数据与数据库2358 次安装29 周前更新
代码仓库

关于 arcgis-time-animation

arcgis-time-animation 是面向使用 ArcGIS 构建时序地图的开发者技能,介绍如何通过 ArcGIS API for JavaScript 配置时间范围、筛选图层、使用 TimeSlider 控件及制作动画。

主要功能

  1. 01配置时间范围和间隔
  2. 02使用 TimeSlider 模式与播放控制
  3. 03筛选时序图层和查询
  4. 04监听时间变化并更新显示
  5. 05为时序数据制作动画

适用场景

  1. 01为 ArcGIS 地图添加时间滑块
  2. 02按日期范围筛选地图图层
  3. 03动态呈现时序数据的变化
  4. 04查询选定时段内的图层要素

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/saschabrunnerch/arcgis-maps-sdk-js-ai-context --skill arcgis-time-animation

安装后在 Claude Code 中输入/arcgis-time-animation确认已加载。

详细介绍

ArcGIS 时间动画

将此技能用于时序数据、时间感知图层、时间筛选和动画控件。

导入模式

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 属性
属性类型默认值说明
fullTimeExtentTimeExtent—微件可显示的完整时间范围
timeExtentTimeExtent—当前选定的时间范围
modestring"instant"动画模式
stopsobject—滑块的吸附点
playRatenumber1000步骤之间的毫秒数
loopbooleanfalse到达末尾时重新开始
layoutstring"auto""auto"、"compact"、"wide"
disabledbooleanfalse禁用交互
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 属性
属性类型说明
startFieldstring开始日期字段名称(必需)
endFieldstring结束日期字段名称(可选)
fullTimeExtentTimeExtent数据的完整时间范围
intervalTimeInterval建议的动画间隔
trackIdFieldstring轨迹标识符字段(用于 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"),
});

常见问题

  1. 时区问题:日期会受 JavaScript 本地时区影响;请使用 UTC 日期以保持一致。

    const date = new Date("2024-06-15T00:00:00Z");
    
  2. 图层必须已加载:只有在执行 await layer.load() 后,timeInfo 才可用。

  3. TimeExtent 未生效:必须设置视图的 timeExtent,才能筛选所有时间感知图层。

  4. 设为 null 可移除筛选:使用 view.timeExtent = null 显示所有数据(不进行时间筛选)。

  5. 性能:较大的时间范围可能会返回大量要素;请尽可能使用服务器端查询。

  6. 必须设置停止点:TimeSlider 需要配置 stops 来定义滑块位置。

参考示例

  • timeslider — 基本 TimeSlider 微件
  • timeslider-filter — 使用 TimeSlider 筛选数据
  • timeslider-component-filter — 带筛选功能的 TimeSlider 组件
  • time-layer — 使用时间感知图层
  • widgets-timeslider — TimeSlider 微件示例
  • widgets-timeslider-offset — 带时区偏移的 TimeSlider
  • layers-scenelayer-time — 时间感知 SceneLayer
  • layers-voxel-time — 时间感知 VoxelLayer
  • layers-graphics-visibilitytimeextent — 根据时间控制图形可见性

相关技能

  • arcgis-imagery — 带时间的多维影像
  • arcgis-layers — 图层配置
  • arcgis-widgets-ui — 微件放置和插槽

评论

支持 Markdown