【2025最新】ArcGIS for JS 实现随着时间变化而变化的热力图
ArcGIS for JavaScript 实现随着时间变化而变化的热力图
在数据可视化领域,热力图是展现空间数据密度分布的 “利器”,而随时间动态变化的热力图更能直观呈现数据的时空演变规律 —— 比如城市商品房月度销售热度的变化趋势。本文适用于 ArcGIS Maps SDK for JavaScri pt 4.28~4.33 版本,实现热力图随月份动态切换的效果,本文为热力图渲染进阶版,若想查看基础可查阅【2025最新】ArcGIS for JavaScript 快速实现热力图渲染 🗺️📊
文章目录
- ArcGIS for JavaScript 实现随着时间变化而变化的热力图
- 一、前置准备:环境与数据 📦
- 1. 核心技术栈
- 2. 数据格式示例(商品房.csv)
- 二、核心代码实现:从 0 到 1 搭建动态热力图 🔧
- 1. 页面结构与样式基础
- 2. 核心逻辑:图层配置与时间轴联动
- (1)模块导入与底图加载
- (2)CSV 图层配置:定义热力图渲染规则
- (3)地图与时间轴初始化
- (4)关键联动:时间轴变化 → 热力图更新
- 三、所有代码 📊
- index.html
- 四、常见问题排查 ❌
| 工具 /插件/系统 名 | 版本 | 说明 |
|---|---|---|
| ArcGIS JS API | 4.28~4.33 | 地图核心能力(底图加载、视图渲染) |
| 天地图服务 | - | 提供街道、卫星、地形等底图数据源 |

一、前置准备:环境与数据 📦
1. 核心技术栈
-
ArcGIS API for JavaScript 4.33:提供地图渲染、图层管理、时间轴控件等核心能力
-
天地图底图:通过自定义加载器接入,让地图更贴合国内场景
-
CSV 数据:存储商品房销售数据,需包含关键字段:
-
地理坐标:
经度、纬度(用于定位空间位置) -
时间维度:
月份(格式如 2024-01,用于时间轴筛选) -
数值维度:
销售量(热力图的 “密度来源”)
-
2. 数据格式示例(商品房.csv)
| 地区 | longitude | latitude | 月份 | 销售量 |
|---|---|---|---|---|
| 朝阳区 | 116.48 | 39.93 | 2024-01 | 2850 |
| 海淀区 | 116.31 | 39.99 | 2024-01 | 3210 |
| 朝阳区 | 116.48 | 39.93 | 2024-02 | 2560 |
二、核心代码实现:从 0 到 1 搭建动态热力图 🔧
1. 页面结构与样式基础
首先创建 HTML 骨架,定义地图容器和时间轴容器,并设置全屏样式(确保地图占满页面):
<!DOCTYPE html><html lang="zh"><head><meta charset="utf-8" /><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /><title>商品房销售时间轴热力图</title><!-- 引入ArcGIS核心资源 --><script type="module" src="https://js.arcgis.com/4.33/map-components/"></script><link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" /><style>/* 地图全屏样式 */html, body, #viewDiv {padding: 0; margin: 0;height: 100%; width: 100%;}/* 时间轴固定在底部 */#time-slider {position: absolute;left: 5%; right: 5%; bottom: 20px;z-index: 100; /* 确保在地图上方显示 */}</style></head><body><div id="viewDiv"></div> <!-- 地图容器 --><div id="time-slider"></div> <!-- 时间轴容器 --></body></html>
2. 核心逻辑:图层配置与时间轴联动
这部分是 “动态热力图” 的关键,主要实现 3 个核心功能:
① 加载 CSV 数据并配置热力图渲染;② 初始化时间轴控件;③ 监听时间轴变化,实时更新热力图。
(1)模块导入与底图加载
先导入 ArcGIS 所需模块,再加载天地图底图(需申请天地图 key,也可以直接在《天地图底图加载》)中直接复制,如果没有 key,可先用 ArcGIS 自带底图(如basemap: "streets")临时测试。:
<script type="module">// 导入ArcGIS核心模块const [Map, CSVLayer, MapView, Legend, TimeSlider, reactiveUtils] = await $arcgis.import(["@arcgis/core/Map.js","@arcgis/core/layers/CSVLayer.js","@arcgis/core/views/MapView.js","@arcgis/core/widgets/Legend.js","@arcgis/core/widgets/TimeSlider.js","@arcgis/core/core/reactiveUtils.js",]);// 加载天地图底图(自定义加载器)import { loadTiandituBasemap } from './js/tiandituLoader.js';const { tiandituBasemap } = await loadTiandituBasemap();</script>
(2)CSV 图层配置:定义热力图渲染规则
通过CSVLayer加载销售数据,并配置热力图渲染器(颜色渐变、密度字段等)和时间信息(时间字段、时间范围):
// 1. 数据弹窗模板(点击热力图时显示的信息)const template = {title: "{地区}",content: "{月份} - 销售量:{销售量}套",};// 2. 热力图渲染器(核心!控制颜色渐变和密度计算)const renderer = {type: "heatmap",colorStops: [ // 颜色从透明黄到红棕色渐变,数值越高颜色越深{ color: "rgba(255, 255, 0, 0)", ratio: 0 }, // 透明(无数据){ color: "#ffff00", ratio: 0.083 }, // 亮黄{ color: "#ff9800", ratio: 0.332 }, // 橙色{ color: "#ff4500", ratio: 0.581 }, // 橙红{ color: "#b30f00", ratio: 1 } // 红棕(最高密度)],field: "销售量", // 用于计算密度的字段(销售量越高,热力越集中)maxDensity: 58, // 初始最大密度(后续会动态更新)minDensity: 0,};// 3. 时间信息配置(定义时间维度规则)const timeInfo = {startField: "月份", // 开始时间字段endField: "月份", // 结束时间字段(因是月度数据,起止一致)fullTimeExtent: { // 完整时间范围(2024年1-12月)start: new Date("2024-01"),end: new Date("2024-12")},timeInterval: { // 时间间隔(1个月)value: 1,unit: "months"}};// 4. 创建CSV图层const layer = new CSVLayer({url: "./商品房.csv", // 数据文件路径title: "商品房月度销售热力",popupTemplate: template, // 弹窗模板renderer: renderer, // 热力图渲染器timeInfo: timeInfo, // 时间信息timeEnabled: true, // 启用时间筛选outFields: ["*"], // 返回所有字段});
(3)地图与时间轴初始化
创建地图视图,并初始化时间轴控件(TimeSlider),设置时间范围为 2024 年全年,间隔 1 个月:
// 1. 创建地图(绑定底图和CSV图层)const map = new Map({basemap: tiandituBasemap,layers: [layer],});// 2. 创建地图视图(定位到北京,缩放级别10)const view = new MapView({container: "viewDiv",center: [116.4074, 39.9042], // 北京经纬度zoom: 10,map: map,});// 3. 添加图例控件(右上角显示)view.ui.add(new Legend({ view: view }),"top-right");// 4. 创建时间轴控件const timeSlider = new TimeSlider({container: "time-slider", // 绑定容器view: view,mode: "instant", // 即时更新模式(拖动滑块立即刷新)fullTimeExtent: {start: new Date("2024-01-01"),end: new Date("2024-12-01"),},stops: { // 时间轴刻度(每月1个刻度)interval: { value: 1, unit: "months" }},labelsVisible: true, // 显示时间标签});
(4)关键联动:时间轴变化 → 热力图更新
通过reactiveUtils.watch监听时间轴的timeExtent变化,实时筛选当前月份的数据,并动态调整热力图最大密度(确保不同月份的热力对比更准确):
// 监听时间轴变化reactiveUtils.watch(() => timeSlider.timeExtent, // 监听时间范围变化(timeExtent) => {// 1. 格式化当前选中的时间(提取日期部分,如2024-01-01)const start = timeExtent.start.toISOString().split('T')[0];const where = `月份 = DATE '${start}'`; // SQL筛选条件(当前月份)// 2. 筛选当前月份的数据(只显示选中月份的热力)const layerView = await view.whenLayerView(layer);layerView.featureEffect = { filter: { where } };// 3. 动态更新热力图最大密度(根据当前月份的最大销售量计算)layer.queryFeatures({returnGeometry: false, // 不需要几何信息,只查属性outStatistics: [{ // 统计当前月份的最大销售量onStatisticField: "销售量",outStatisticFieldName: "max_sales",statisticType: "max"}],where: where // 筛选当前月份}).then(result => {const maxSales = result.features[0].attributes['max_sales'];layer.renderer.maxDensity = maxSales / 100; // 调整密度系数(根据数据量级优化)console.log(`当前月份最大销售量:${maxSales}套,热力最大密度:${maxSales/100}`);});});// 图层加载完成后,自动定位到数据范围layer.when(() => {console.log("CSV图层加载完成!");view.goTo(layer.fullExtent.expand(1.5)); // 扩大1.5倍范围,避免数据贴边}).catch((error) => {console.error("图层加载失败:", error);});
三、所有代码 📊
index.html
<!DOCTYPE html>
<html lang="zh"><head><meta charset="utf-8" /><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /><title>热力图</title><script type="module" src="https://js.arcgis.com/4.33/map-components/"></script><link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" /><style>html,body,#viewDiv {padding: 0;margin: 0;height: 100%;width: 100%;}#time-slider {position: absolute;left: 5%;right: 5%;bottom: 20px;}</style><script src="https://js.arcgis.com/4.33/"></script><script type="module">const [Map, CSVLayer, MapView, Legend, TimeSlider, reactiveUtils] = await $arcgis.import(["@arcgis/core/Map.js","@arcgis/core/layers/CSVLayer.js","@arcgis/core/views/MapView.js","@arcgis/core/widgets/Legend.js","@arcgis/core/widgets/TimeSlider.js","@arcgis/core/core/reactiveUtils.js",]);// 天地图加载模块import { loadTiandituBasemap } from './js/tiandituLoader.js';const { tiandituBasemap } = await loadTiandituBasemap();const url = "./商品房.csv";const template = {title: "{地区}",content: "{月份} - 销售量 {销售量}.",};// 最大像素强度用于指定颜色// 从colorStops属性中的连续颜色渐变const renderer = {type: "heatmap",colorStops: [{ color: "rgba(255, 255, 0, 0)", ratio: 0 }, // 透明黄色(起始){ color: "#ffff00", ratio: 0.083 }, // 亮黄色{ color: "#ffd700", ratio: 0.166 }, // 金黄色{ color: "#ffc107", ratio: 0.249 }, // 琥珀色{ color: "#ff9800", ratio: 0.332 }, // 橙色{ color: "#ff7043", ratio: 0.415 }, // 深橙色{ color: "#ff5722", ratio: 0.498 }, // 橙红色{ color: "#ff4500", ratio: 0.581 }, // 橙红色(加深){ color: "#ff3a00", ratio: 0.664 }, // 浅红色{ color: "#ff2400", ratio: 0.747 }, // 红色(加深){ color: "#e61900", ratio: 0.83 }, // 深红色{ color: "#cc1400", ratio: 0.913 }, // 暗深红色{ color: "#b30f00", ratio: 1 } // 红棕色(终点)],field: "销售量",maxDensity: 58,minDensity: 0,};// 时间字段配置const timeInfo = {startField: "月份",endField: "月份",fullTimeExtent: {start: new Date("2024-01"),end: new Date("2024-12")},timeInterval: {value: 1,unit: "months"}};const layer = new CSVLayer({url: url,title: "热力显示",copyright: "模拟数据",popupTemplate: template,renderer: renderer,timeInfo: timeInfo,timeEnabled: true,labelsVisible: true,outFields: ["*"]});const map = new Map({basemap: tiandituBasemap,layers: [layer],});const view = new MapView({container: "viewDiv",center: [116.4074, 39.9042],zoom: 10,map: map,});view.ui.add(new Legend({view: view,}),"top-right",);const layerView = await view.whenLayerView(layer);// 创建时间滑块const timeSlider = new TimeSlider({container: "time-slider",view: view,mode: "instant",fullTimeExtent: {start: new Date("2024-01-01"),end: new Date("2024-12-01"),},stops: {interval: {value: 1,unit: "months",},},labelsVisible: true});// 确保图层加载完成后再显示layer.when(() => {console.log("图层加载完成");view.goTo(layer.fullExtent.expand(1.5));}).catch((error) => {console.error("图层加载失败:", error);});console.log("热力图应用已初始化");reactiveUtils.watch(() => timeSlider.timeExtent,(timeExtent) => {const start = timeExtent.start.toISOString().split('T')[0];const end = timeExtent.end.toISOString().split('T')[0];const where = `月份 = DATE '${start}' `layerView.featureEffect = {filter: { where }};// 查询所有要素的销售量,取最大值layer.queryFeatures({returnGeometry: false,outStatistics: [{// 要计算最大值的字段(需与 CSV 中字段名完全一致,含中文和括号)onStatisticField: "销售量",// 自定义统计结果的字段名(后续从结果中通过此名获取最大值)outStatisticFieldName: "max_sales",// 统计类型:最大值statisticType: "max"}],where}).then(result => {const maxSales = result.features[0].attributes['max_sales'];// 动态更新渲染器的最大密度layer.renderer.maxDensity = maxSales / 100;console.log(`最大销售量:${maxSales / 100}`);});},);</script>
</head><body><div id="viewDiv"></div><div id="time-slider"></div>
</body></html>
四、常见问题排查 ❌
- 热力图不显示?
-
检查 CSV 文件路径是否正确(相对路径需与 HTML 同级);
-
确认 CSV 字段名与代码一致(如 “销售量” 不能错写为 “销量”);
-
打开浏览器控制台(F12),查看是否有数据加载错误。
- 时间轴拖动无反应?
-
检查
timeInfo的startField和endField是否为 CSV 中的时间字段; -
确认
timeSlider的fullTimeExtent与数据的时间范围匹配。
- 弹窗不显示?
- 检查
popupTemplate中的字段名是否与 CSV 一致(如{地区}需对应 CSV 的 “地区” 字段)。
通过以上步骤,就能快速实现一个 “随时间动态变化的热力图” 啦!无论是商品房销售、人口流动还是交通流量分析,这个方案都能直接复用,只需替换 CSV 数据和调整渲染参数即可。如果有更复杂的需求(如多维度筛选、自定义底图),可以进一步扩展 ArcGIS API 的其他模块~
#ArcGISforJS
