ArcGIS 点聚合功能实现
一、教程概述
本教程基于 ArcGIS API for JavaScript 4.33 版本,详细讲解如何实现地图点聚合功能。通过随机生成 5000 个地理点数据,结合天地图底图,实现点数据的自动聚合展示、聚合标签、弹窗及图例等核心功能,适用于大数据量地理点的可视化场景。
文章目录
- [ArcGIS 点聚合功能实现](#ArcGIS 点聚合功能实现)
工具 /插件/系统 名 | 版本 | 说明 |
---|---|---|
ArcGIS for JavaScript | 4.33 | 适用4.28 - 4.33版本 |
效果图
效果图 1 | 效果图 2 | 效果图 3 |
---|---|---|
![]() |
![]() |
![]() |
二、实现的核心功能
- 点聚合功能:实现点数据的自动聚合与分散,依据比例尺动态调整(超过 50000 比例尺不聚合);配置聚合点的专属弹窗,展示统计信息;生成聚合标签,直观呈现聚合数量。
- 详情弹框:支持点击聚合点查看详情,实现聚合区域的清晰标识。
- 添加图例控件展示图层信息。
- 加载天地图作为底图 案例中已经将渲染天地图(点击直达)文章中的内容额外封装成为loadTiandituBasemap方法,文章内提供了文中所需的封装代码,有其他需要的可自行封装,这里不多赘述。
(二)基础页面结构
创建包含地图容器和信息面板的 HTML 结构,设置全屏样式确保地图占满可视区域:
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<div id="legendDiv"></div>
</div>
<style>
html, body, #viewDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#infoDiv {
background: white;
padding: 10px;
}
</style>
三、核心功能分步实现
(一)模块导入与初始化
采用 ES 模块方式导入所需 ArcGIS 核心模块,确保按需加载关键组件:
// 导入天地图加载工具
import { loadTiandituBasemap } from './js/tiandituLoader.js';
// 导入 ArcGIS 核心模块
const [Map, MapView, FeatureLayer, Legend, Expand, clusterLabelCreator, clusterPopupCreator, reactiveUtils] = await \$arcgis.import([
"@arcgis/core/WebMap.js",
"@arcgis/core/views/MapView.js",
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/widgets/Legend.js",
"@arcgis/core/widgets/Expand.js",
"@arcgis/core/smartMapping/labels/clusters.js",
"@arcgis/core/smartMapping/popup/clusters.js",
"@arcgis/core/core/reactiveUtils.js",
]);
// 加载天地图底图配置
const { tiandituBasemap } = await loadTiandituBasemap();
(二)生成随机点数据
创建 generateRandomPoints
函数,生成指定数量的地理点数据,包含随机经纬度、类别及数值属性:
function generateRandomPoints(count) {
const points = [];
for (let i = 0; i < count; i++) {
// 经度范围:0-119°E,纬度范围:30-60°N
const lon = Math.random() * 119;
const lat = Math.random() * 30 + 30;
// 随机分配 0-2 三类数据
const category = Math.floor(Math.random() * 3);
points.push({
geometry: { type: "point", longitude: lon, latitude: lat },
attributes: {
ObjectID: i,
category: category,
value: Math.floor(Math.random() * 100)
}
});
}
return points;
}
(三)创建要素图层
定义包含点数据的要素图层,配置字段结构、输出字段及基础弹窗模板:
const layer = new FeatureLayer({
source: generateRandomPoints(5000), // 加载5000个随机点
title: "点聚合",
fields: [
{ name: "ObjectID", alias: "ObjectID", type: "oid" },
{ name: "category", alias: "类别", type: "integer" },
{ name: "value", alias: "值", type: "integer" }
],
outFields: ["*"], // 输出所有字段
popupTemplate: { title: "{name}", content: [] }
});
(四)初始化地图与视图
创建地图实例并绑定天地图底图,配置地图视图的容器、中心点及初始缩放级别:
// 初始化地图
const map = new Map({
basemap: tiandituBasemap, // 绑定天地图底图
layers: [layer] // 添加要素图层
});
// 初始化地图视图
const view = new MapView({
container: "viewDiv", // 绑定地图容器
map,
center: [116.39, 39.9], // 中心点:北京坐标
zoom: 4 // 初始缩放级别
});
(五)配置点聚合功能
通过 generateClusterConfig
函数配置聚合参数,包含弹窗模板、标签样式及聚合触发条件:
async function generateClusterConfig(layer) {
// 生成聚合弹窗模板
const popupTemplate = await clusterPopupCreator
.getTemplates({ layer })
.then(res => res.primaryTemplate.value);
// 生成聚合标签样式
const { labelingInfo, clusterMinSize } = await clusterLabelCreator
.getLabelSchemes({ layer, view })
.then(schemes => schemes.primaryScheme);
// 返回聚合配置
return {
type: "cluster",
popupTemplate, // 聚合弹窗
labelingInfo, // 聚合标签
clusterMinSize, // 最小聚合尺寸
maxScale: 50000 // 最大聚合比例尺(超过此值不聚合)
};
}
// 图层加载完成后启用聚合
layer.when()
.then(generateClusterConfig)
.then(featureReduction => {
layer.featureReduction = featureReduction;
})
.catch(error => console.error(error));
(六)添加控件与交互优化
-
聚合弹窗样式优化:修改聚合区域的边界样式,提升视觉效果
reactiveUtils.whenOnce(() => view.popup.viewModel)
.then(() => { view.popup.viewModel.selectedClusterBoundaryFeature.symbol = { type: "simple-fill", style: "solid", color: "rgba(50,50,50,0.15)", outline: { width: 0.5, color: "rgba(50,50,50,0.25)" } }; });
-
添加图例控件:创建图例并通过折叠面板添加到地图右上角
// 创建图例
const legend = new Legend({ view, container: "legendDiv" });
// 创建折叠面板
const infoExpand = new Expand({
view, content: document.getElementById("infoDiv"), expandIcon: "list-bullet", expanded: true // 默认展开
});
// 添加到地图UI
view.ui.add(infoExpand, "top-right");
四、全部代码
index.html
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>ArcGIS 点聚合基础示例</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.33/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.33/"></script>
<style>
html,
body,
#viewDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#infoDiv {
background: white;
padding: 10px;
}
</style>
<script type="module">
import { loadTiandituBasemap } from './js/tiandituLoader.js';
const [
Map,
MapView,
FeatureLayer,
Legend,
Expand,
clusterLabelCreator,
clusterPopupCreator,
reactiveUtils,
] = await $arcgis.import([
"@arcgis/core/WebMap.js",
"@arcgis/core/views/MapView.js",
"@arcgis/core/layers/FeatureLayer.js",
"@arcgis/core/widgets/Legend.js",
"@arcgis/core/widgets/Expand.js",
"@arcgis/core/smartMapping/labels/clusters.js",
"@arcgis/core/smartMapping/popup/clusters.js",
"@arcgis/core/core/reactiveUtils.js",
]);
const { tileInfo, config, getUrlTemplate, tiandituBasemap, Basemap, WebTileLayer } = await loadTiandituBasemap();
const layer = new FeatureLayer({
source: generateRandomPoints(5000),
title: "点聚合",
fields: [{
name: "ObjectID",
alias: "ObjectID",
type: "oid"
}, {
name: "category",
alias: "类别",
type: "integer"
}, {
name: "value",
alias: "值",
type: "integer"
}],
outFields: ["*"],
popupTemplate: {
title: "{category}",
content: "类别:{category},值:{value}",
},
});
const map = new Map({
basemap: tiandituBasemap,
layers: [layer],
});
const view = new MapView({
container: "viewDiv",
map,
center: [116.39, 39.9],
zoom: 4,
});
reactiveUtils
.whenOnce(() => view.popup.viewModel)
.then(() => {
// Override the default symbol representing the cluster extent
view.popup.viewModel.selectedClusterBoundaryFeature.symbol = {
type: "simple-fill",
style: "solid",
color: "rgba(50,50,50,0.15)",
outline: {
width: 0.5,
color: "rgba(50,50,50,0.25)",
},
};
});
const legend = new Legend({
view,
container: "legendDiv",
});
const infoDiv = document.getElementById("infoDiv");
view.ui.add(
new Expand({
view,
content: infoDiv,
expandIcon: "list-bullet",
expanded: true,
}),
"top-right",
);
layer
.when()
.then(generateClusterConfig)
.then((featureReduction) => {
layer.featureReduction = featureReduction;
})
.catch((error) => {
console.error(error);
});
async function generateClusterConfig(layer) {
// generates default popupTemplate
const popupTemplate = await clusterPopupCreator
.getTemplates({ layer })
.then((popupTemplateResponse) => popupTemplateResponse.primaryTemplate.value);
// generates default labelingInfo
const { labelingInfo, clusterMinSize } = await clusterLabelCreator
.getLabelSchemes({ layer, view })
.then((labelSchemes) => labelSchemes.primaryScheme);
return {
type: "cluster",
popupTemplate,
labelingInfo,
clusterMinSize,
maxScale: 50000,
};
}
function generateRandomPoints(count) {
const points = [];
for (let i = 0; i < count; i++) {
const lon = (Math.random() * 119) ; // 经度范围
const lat = (Math.random() * 30) + 30; // 纬度范围
// 随机分配类别
const category = Math.floor(Math.random() * 3);
points.push({
geometry: {
type: "point",
longitude: lon,
latitude: lat
},
attributes: {
ObjectID: i,
category: category,
value: Math.floor(Math.random() * 100)
}
});
}
return points;
}
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<div id="legendDiv"></div>
</div>
</body>
</html>
tiandituLoader.js
/**
* 天地图加载公共模块
* 功能:封装天地图底图加载逻辑,返回配置好的Basemap实例
* 依赖:ArcGIS API 4.x
*/
export async function loadTiandituBasemap() {
try {
// 1. 按需导入ArcGIS核心模块
const [
WebTileLayer,
Basemap,
TileInfo
] = await $arcgis.import([
"@arcgis/core/layers/WebTileLayer",
"@arcgis/core/Basemap",
"@arcgis/core/layers/support/TileInfo",
]);
// 2. 配置参数(可根据需求调整)
const config = {
tk:你的密匙, // 天地图密钥
spatialReference: { wkid: 4326 }, // 目标坐标系(WGS84)
subDomains: ["0", "1", "2", "3", "4", "5", "6", "7"], // 多子域名
tileMatrixSet: "c", // 天地图瓦片矩阵集
layerType: {
vec: "vec", // 矢量底图
cva: "cva" // 矢量注记
}
};
// 3. 定义瓦片信息(匹配WGS84坐标系的瓦片规则)
const tileInfo = new TileInfo({
dpi: 90.71428571427429,
rows: 256,
cols: 256,
compressionQuality: 0,
origin: { x: -180, y: 90 },
spatialReference: config.spatialReference,
lods: [
{ level: 2, levelValue: 2, resolution: 0.3515625, scale: 147748796.52937502 },
{ level: 3, levelValue: 3, resolution: 0.17578125, scale: 73874398.264687508 },
{ level: 4, levelValue: 4, resolution: 0.087890625, scale: 36937199.132343754 },
{ level: 5, levelValue: 5, resolution: 0.0439453125, scale: 18468599.566171877 },
{ level: 6, levelValue: 6, resolution: 0.02197265625, scale: 9234299.7830859385 },
{ level: 7, levelValue: 7, resolution: 0.010986328125, scale: 4617149.8915429693 },
{ level: 8, levelValue: 8, resolution: 0.0054931640625, scale: 2308574.9457714846 },
{ level: 9, levelValue: 9, resolution: 0.00274658203125, scale: 1154287.4728857423 },
{ level: 10, levelValue: 10, resolution: 0.001373291015625, scale: 577143.73644287116 },
{ level: 11, levelValue: 11, resolution: 0.0006866455078125, scale: 288571.86822143558 },
{ level: 12, levelValue: 12, resolution: 0.00034332275390625, scale: 144285.93411071779 },
{ level: 13, levelValue: 13, resolution: 0.000171661376953125, scale: 72142.967055358895 },
{ level: 14, levelValue: 14, resolution: 8.58306884765625e-005, scale: 36071.483527679447 },
{ level: 15, levelValue: 15, resolution: 4.291534423828125e-005, scale: 18035.741763839724 },
{ level: 16, levelValue: 16, resolution: 2.1457672119140625e-005, scale: 9017.8708819198619 },
{ level: 17, levelValue: 17, resolution: 1.0728836059570313e-005, scale: 4508.9354409599309 },
{ level: 18, levelValue: 18, resolution: 5.3644180297851563e-006, scale: 2254.4677204799655 },
{ level: 19, levelValue: 19, resolution: 2.68220901489257815e-006, scale: 1127.23386023998275 },
{ level: 20, levelValue: 20, resolution: 1.341104507446289075e-006, scale: 563.616930119991375 }
]
});
// 4. 构建天地图URL模板(支持多子域名)
const getUrlTemplate = (layer) => {
return `http://t0.tianditu.gov.cn/${layer}_${config.tileMatrixSet}/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${layer}&STYLE=default&TILEMATRIXSET=${config.tileMatrixSet}&TILEMATRIX={level}&TILEROW={row}&TILECOL={col}&FORMAT=tiles&tk=${config.tk}`;
};
// 5. 创建矢量底图图层
const vecLayer = new WebTileLayer({
urlTemplate: getUrlTemplate(config.layerType.vec),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
});
// 6. 创建矢量注记图层
const cvaLayer = new WebTileLayer({
urlTemplate: getUrlTemplate(config.layerType.cva),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
});
// 7. 创建自定义底图并返回
const tiandituBasemap = new Basemap({
baseLayers: [vecLayer],
referenceLayers: [cvaLayer],
title: "天地图矢量图(WGS84)",
id: "tianditu-vector-wgs84"
});
return {
tileInfo,
config,
getUrlTemplate,
tiandituBasemap,
WebTileLayer,
Basemap
};
} catch (error) {
console.error("天地图加载失败:", error);
throw new Error("天地图公共模块加载异常,请检查依赖和配置");
}
}