【2025最新】ArcGIS for JS二维底图与三维地图的切换 - 指南
2025-10-25 18:40 tlnshuju 阅读(4) 评论(0) 收藏 举报【2025最新】ArcGIS for JS二维地图与三维地图的切换
本文适用 ArcGIS JS API 4.28-4.33 版本,是 【2025最新】ArcGIS for JS 街道、卫星、地形地貌底图切换》扩展教程,在实现支持街道图、卫星图、地形地貌图基础上,进一步实现了 3D 地图的切换功能。
文章目录
| 工具 /插件/系统 名 | 版本 | 说明 |
|---|---|---|
| ArcGIS JS API | 4.28~4.33 | 地图核心能力(底图加载、视图渲染) |
| Tailwind CSS | 最新 | 快速构建高颜值 UI,无需手写复杂 CSS |
| Font Awesome | 4.7.0 | 提供地图图标,增强视觉效果 |
| 天地图服务 | - | 提供街道、卫星、地形等底图数据源 |
效果图
| 效果图 |
|---|
![]() |
一、核心功能实现
针对基础引入过程,本文将不过多赘述,有需要的,可自行查阅 【2025最新】ArcGIS for JS 街道、卫星、地形地貌底图切换》
1.1 模块加载与初始化
首先导入 ArcGIS 核心模块,并初始化应用配置对象:
// 导入天地图加载工具(需自行实现tiandituLoader.js)
import { loadTiandituBasemap } from './js/tiandituLoader.js';
// 加载ArcGIS核心模块
const [
Map, MapView, SceneView, SceneLayer, WebMap, WebScene
] = await \$arcgis.import( [
'@arcgis/core/Map.js', // 2D地图核心类
'@arcgis/core/views/MapView.js', // 2D地图视图
"@arcgis/core/views/SceneView.js", // 3D地图视图
"@arcgis/core/layers/SceneLayer.js", // 3D场景图层
"@arcgis/core/WebMap.js", // Web地图类
"@arcgis/core/WebScene.js" // Web场景类
])
// 应用配置对象,管理地图视图状态
const appConfig = {
mapView: null, // 2D视图实例
sceneView: null, // 3D视图实例
activeView: null, // 当前活跃视图
container: "viewDiv", // 地图容器ID
};
// 加载天地图配置信息
const { tileInfo, config, getUrlTemplate, tiandituBasemap, Basemap, WebTileLayer } = await loadTiandituBasemap();
1.2 底图定义
定义三种 2D 底图(街道图、卫星图、地形地貌图)和一种 3D 场景:
1.2.1 街道图底图
直接使用天地图加载工具返回的街道图底图:
const streetsBasemap = tiandituBasemap; // 天地图街道图
1.2.2 卫星图底图
由卫星影像层和标注层组成:
const satelliteBasemap = new Basemap({
baseLayers: [
// 卫星影像层
new WebTileLayer({
urlTemplate: getUrlTemplate('img'), // 天地图卫星影像URL模板
subDomains: config.subDomains, // 子域名
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference, // 空间参考(默认Web Mercator)
tileInfo: tileInfo // 瓦片信息
}),
// 卫星图标注层
new WebTileLayer({
urlTemplate: getUrlTemplate('cia'), // 天地图卫星标注URL模板
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
})
],
title: "卫星图",
id: "satellite"
});
1.2.3 地形地貌图底图
类似卫星图结构,由地形层和标注层组成:
const terrainBasemap = new Basemap({
baseLayers: [
// 地形层
new WebTileLayer({
urlTemplate: getUrlTemplate('ter'), // 天地图地形URL模板
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
}),
// 地形标注层
new WebTileLayer({
urlTemplate: getUrlTemplate('cta'), // 天地图地形标注URL模板
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
})
],
title: "地形地貌图", // 注意:原代码此处标题错误,已修正
id: "terrain" // 注意:原代码此处ID错误,已修正
});
1.2.4 3D 场景
使用 ArcGIS Online 上的公共 3D 场景:
const scene = new WebScene({
portalItem: {
id: "c8cf26d7acab4e45afcd5e20080983c1", // ArcGIS Online场景ID
},
});
1.3 视图创建
实现通用的视图创建函数,支持 2D 和 3D 视图:
function createView(type) {
let view;
if (type === "2d") {
// 创建2D视图
view = new MapView({
container: appConfig.container,
map: map,
center: [116.39, 39.9], // 默认北京坐标
zoom: 12, // 默认缩放级别
// 环境配置,添加背景色
environment: {
background: {
type: "color",
color: [240, 240, 240]
}
}
});
// 监听2D视图鼠标移动事件,显示坐标
view.on("pointer-move", (event) => {
if (view.interacting) return; // 交互中不更新坐标
const point = view.toMap(event); // 转换屏幕坐标为地图坐标
document.getElementById("coordinates").textContent =
\`经度: \${point.longitude.toFixed(6)} , 纬度: \${point.latitude.toFixed(6)}\`;
});
} else {
// 创建3D视图
view = new SceneView({
zoom: 12,
center: [-122.43759993450347, 37.772798684981126], // 默认旧金山坐标
// 初始不设置容器,切换时再绑定
});
// 监听3D视图鼠标移动事件,显示坐标
view.on("pointer-move", (event) => {
if (view.interacting) return;
const point = view.toMap(event);
document.getElementById("coordinates").textContent =
\`经度: \${point.longitude.toFixed(6)} , 纬度: \${point.latitude.toFixed(6)}\`;
});
}
return view;
}
// 初始化2D和3D视图
const map = new Map({
basemap: streetsBasemap // 默认2D底图
});
appConfig.mapView = createView("2d");
appConfig.sceneView = createView("3d");
appConfig.activeView = appConfig.mapView; // 默认激活2D视图
1.4 底图切换逻辑
实现底图切换的核心功能,包括按钮状态更新、视图切换和底图更新:
1.4.1 底图更新函数
const updateBasemap = (newBasemap, buttonId) => {
// 1. 移除所有按钮的激活状态
document.querySelectorAll(' [id^="basemap-"]').forEach(btn => {
btn.classList.remove('basemap-btn-active');
});
// 2. 设置当前按钮为激活状态
document.getElementById(buttonId).classList.add('basemap-btn-active');
// 3. 更新信息面板中的当前底图名称
const basemapTitle = buttonId === 'basemap-3d' ? '3D图' : newBasemap.title;
document.getElementById("current-basemap").textContent = basemapTitle;
// 4. 执行视图切换
switchView(newBasemap, buttonId);
};
1.4.2 视图切换函数
处理 2D 和 3D 视图的切换,保持视图位置和缩放级别一致性:
function switchView(newBasemap, buttonId) {
// 1. 保存当前视图的视角信息
const activeViewpoint = appConfig.activeView.viewpoint.clone();
// 2. 计算缩放级别转换因子(解决纬度对距离的影响)
const latitude = appConfig.activeView.center.latitude;
const scaleConversionFactor = Math.cos((latitude * Math.PI) / 180.0);
activeViewpoint.scale *= scaleConversionFactor; // 调整缩放级别
// 3. 解绑当前视图的容器
appConfig.activeView.container = null;
// 4. 切换到目标视图
if (buttonId !== 'basemap-3d') {
// 切换到2D视图
appConfig.mapView.viewpoint = activeViewpoint; // 应用保存的视角
appConfig.mapView.container = appConfig.container; // 绑定容器
appConfig.mapView.map.basemap = newBasemap; // 更新2D底图
appConfig.activeView = appConfig.mapView; // 更新活跃视图
} else {
// 切换到3D视图
appConfig.sceneView.viewpoint = activeViewpoint; // 应用保存的视角
appConfig.sceneView.container = appConfig.container; // 绑定容器
appConfig.activeView = appConfig.sceneView; // 更新活跃视图
}
}
1.5 绑定按钮事件
为每个底图切换按钮绑定点击事件:
// 街道图切换
document.getElementById("basemap-streets").addEventListener("click", () => {
updateBasemap(streetsBasemap, "basemap-streets");
});
// 卫星图切换
document.getElementById("basemap-satellite").addEventListener("click", () => {
updateBasemap(satelliteBasemap, "basemap-satellite");
});
// 地形地貌图切换
document.getElementById("basemap-terrain").addEventListener("click", () => {
updateBasemap(terrainBasemap, "basemap-terrain");
});
// 3D图切换
document.getElementById("basemap-3d").addEventListener("click", () => {
updateBasemap(null, "basemap-3d");
});
二、全部代码
index.html
ArcGIS 底图切换示例
<script src="https://js.arcgis.com/4.33/"></script>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#0079c1',
secondary: '#5cc0ff',
dark: '#1e293b',
light: '#f8fafc'
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
底图切换
地图信息
当前底图: 街道图
坐标: 经度: -- , 纬度: --
<script type="module">
import { loadTiandituBasemap } from './js/tiandituLoader.js';
// 加载 ArcGIS 模块
const [
Map, MapView, SceneView, SceneLayer, WebMap, WebScene
] = await $arcgis.import([
'@arcgis/core/Map.js',
'@arcgis/core/views/MapView.js',
"@arcgis/core/views/SceneView.js",
"@arcgis/core/layers/SceneLayer.js",
"@arcgis/core/WebMap.js",
"@arcgis/core/WebScene.js",
])
const appConfig = {
mapView: null,
sceneView: null,
activeView: null,
container: "viewDiv", // use same container for views
};
const { tileInfo, config, getUrlTemplate, tiandituBasemap, Basemap, WebTileLayer } = await loadTiandituBasemap();
// 1. 定义三个不同的底图
// 街道图底图
const streetsBasemap = tiandituBasemap
// 卫星图底图
const satelliteBasemap = new Basemap({
baseLayers: [
new WebTileLayer({
urlTemplate: getUrlTemplate('img'),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
}),
new WebTileLayer({
urlTemplate: getUrlTemplate('cia'),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
})
],
title: "卫星图",
id: "satellite"
});
// 地形地貌图底图
const terrainBasemap = new Basemap({
baseLayers: [
new WebTileLayer({
urlTemplate: getUrlTemplate('ter'),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
}),
new WebTileLayer({
urlTemplate: getUrlTemplate('cta'),
subDomains: config.subDomains,
copyright: "天地图 © 国家地理信息公共服务平台",
spatialReference: config.spatialReference,
tileInfo: tileInfo
})
],
title: "卫星图",
id: "satellite"
});
// 2. 创建地图实例,默认使用街道图
// 2d图层
const map = new Map({
basemap: streetsBasemap
});
appConfig.mapView = createView("2d");
appConfig.mapView.map = map;
appConfig.activeView = appConfig.mapView;
// 3D 图层
const scene = new WebScene({
portalItem: {
// autocasts as new PortalItem()
id: "c8cf26d7acab4e45afcd5e20080983c1",
},
});
appConfig.sceneView = createView("3d");
appConfig.sceneView.map = scene;
// 3. 创建地图视图
function createView(type) {
let view;
if (type === "2d") {
view = new MapView({
container: appConfig.container,
map: map,
center: [116.39, 39.9], // 北京坐标
zoom: 12,
// 添加淡入淡出过渡效果
environment: {
background: {
type: "color",
color: [240, 240, 240]
}
}
});
} else {
view = new SceneView({
zoom: 12,
center: [-122.43759993450347, 37.772798684981126],
// container: appConfig.container,
});
}
return view;
}
// 4. 底图切换逻辑
const updateBasemap = (newBasemap, buttonId) => {
// 移除所有按钮的活跃状态
document.querySelectorAll('[id^="basemap-"]').forEach(btn => {
btn.classList.remove('basemap-btn-active');
});
// 设置当前按钮为活跃状态
document.getElementById(buttonId).classList.add('basemap-btn-active');
// 更新当前底图显示文本
document.getElementById("current-basemap").textContent = newBasemap && newBasemap.title || "图";
switchView(newBasemap, buttonId)
};
// 切换
function switchView(newBasemap, buttonId) {
// const is3D = appConfig.activeView.type === "3d";
const activeViewpoint = appConfig.activeView.viewpoint.clone();
// Compute scale conversion factor with cosine of latitude to account for distance distortion as latitude moves away from the equator
const latitude = appConfig.activeView.center.latitude;
const scaleConversionFactor = Math.cos((latitude * Math.PI) / 180.0);
appConfig.activeView.container = null;
if (buttonId != 'basemap-3d') {
const is3D = appConfig.activeView.type === "3d";
activeViewpoint.scale = scaleConversionFactor;
appConfig.mapView.viewpoint = activeViewpoint;
appConfig.mapView.container = appConfig.container;
appConfig.mapView.map.basemap = newBasemap;
appConfig.activeView = appConfig.mapView;
} else {
activeViewpoint.scale = scaleConversionFactor;
appConfig.sceneView.viewpoint = activeViewpoint;
appConfig.sceneView.container = appConfig.container;
appConfig.activeView = appConfig.sceneView;
}
}
// 5. 绑定按钮事件
document.getElementById("basemap-streets").addEventListener("click", () => {
updateBasemap(streetsBasemap, "basemap-streets");
});
document.getElementById("basemap-satellite").addEventListener("click", () => {
updateBasemap(satelliteBasemap, "basemap-satellite");
});
document.getElementById("basemap-terrain").addEventListener("click", () => {
updateBasemap(terrainBasemap, "basemap-terrain");
});
document.getElementById("basemap-3d").addEventListener("click", () => {
updateBasemap(null, "basemap-3d");
});
</script>
tiandituLoader.js
文件放置/js/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("天地图公共模块加载异常,请检查依赖和配置");
}
}

浙公网安备 33010602011771号