ArcGIS JS 基础教程(7):Global与Local场景模式

简介: ArcGIS JS 基础教程(7):Global与Local场景模式

零、写在前面

📌 本系列教程完整目录:ArcGIS JS 系列基础教程(100个项目常用热门功能)

💡 在线示例:完整可运行的 HTML 示例,无需任何环境配置,可直接在浏览器中打开体验
🗂️ 专栏导航:收藏 + 关注,专栏文章第一时间送达
❤️ 一键三连:点赞(给教程充电)+ 评论(提问必回)+ 收藏(下次再看)


一、功能介绍

SceneView 支持两种场景模式:Global(全球球面模式) 和 Local(局部平面模式),通过 viewingMode 属性控制。

  • Global 模式:在 WGS84 球面坐标系下渲染完整的地球球体,适合展示大范围、跨区域的三维数据。地球曲率真实可见,支持全球尺度漫游。
  • Local 模式:在投影坐标系下将场景渲染为局部平面区域,适合城市级或园区级的精细化展示。平面投影下量算更精准,无地球曲率干扰。

两种模式对高程数据的处理方式也不同:Global 模式下高程贴合球面曲率;Local 模式下高程在投影平面上拉伸,精度更高。根据项目尺度正确选择模式,是三维场景构建的关键第一步。

二、功能实现

SceneView.viewingMode 属性决定场景模式,可选值为 "global"(默认)或 "local"。

Global 模式关键配置:

  • 坐标系:WGS84(EPSG:4326),经纬度直接驱动
  • map.ground 加载全球高程服务 world-elevation
  • 相机参数:camera.position 使用 [longitude, latitude, altitude]

Local 模式关键配置:

  • 坐标系:需使用投影坐标系(如 Web Mercator EPSG:3857,或地方投影带)
  • 通过设置 extent 约束场景显示范围,避免平面无限延伸
  • 局部模式下相机 position 的 x/y 为投影平面坐标,z 仍为高程

动态切换注意: Global 与 Local 模式不能在同一个 SceneView 实例中动态切换,需要销毁原有视图、创建新的 SceneView 实例。实际项目中通常在入口页提供模式选择,或维护两个独立页面。

核心代码如下:

  • 创建 Global 模式:viewingMode: "global"(可省略,默认即为 global)
  • 创建 Local 模式:viewingMode: "local",并建议同时设置 constraints.clipToExtent 限制渲染范围
  • Local 模式需确保底图和支持的图层在投影坐标系下可用(Web Mercator 兼容性最好)

三、功能应用

场景 推荐模式 理由
全球物流航线可视化 Global 需要展示跨大洲的球面航线,地球曲率不可忽略
智慧园区精细化管理 Local 园区范围小,平面坐标系下量算和建模更精准
城市数字孪生(区县尺度) Local 城市级场景在平面投影下建筑对齐更自然
气象/海洋全球数据展示 Global 全球尺度的气象云图、洋流需要在球面上连续展示
应急指挥(局部区域) Local 局部区域分析无需地球曲率,平面量算更符合业务习惯
航空航天态势感知 Global 卫星轨道、飞行轨迹跨越全球,球面模式是唯一选择

选择原则:

  • 数据范围 大于 100km(如全省、全国、全球)→ 选 Global
  • 数据范围 小于 50km(如园区、城区)→ 选 Local
  • 介于之间需结合业务:涉及球面几何(大圆航线)选 Global,涉及精密量算选 Local

四、核心代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>第7课:Global与Local场景模式</title>
    <link rel="stylesheet" href="https://js.arcgis.com/5.0/esri/themes/light/main.css">
    <script type="module" src="https://js.arcgis.com/5.0/"></script>
    <style>
        body {
     margin: 0; padding: 0; font-family: "Microsoft YaHei", sans-serif; }
        #mapContainer {
     width: 100vw; height: 100vh; }
        .page-title {
    
            position: absolute;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(255,255,255,0.95);
            padding: 10px 24px;
            border-radius: 6px;
            font-size: 18px;
            font-weight: bold;
            z-index: 100;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        }
        .control-panel {
    
            position: absolute;
            top: 80px;
            left: 20px;
            background: rgba(255,255,255,0.95);
            padding: 16px;
            border-radius: 8px;
            box-shadow: 0 2px 12px rgba(0,0,0,0.15);
            z-index: 100;
            min-width: 220px;
        }
        .mode-badge {
    
            display: inline-block;
            padding: 4px 12px;
            border-radius: 12px;
            font-size: 13px;
            font-weight: bold;
            margin-bottom: 12px;
        }
        .badge-global {
     background: #1890ff; color: white; }
        .badge-local  {
     background: #52c41a; color: white; }
        .btn-group {
     display: flex; flex-direction: column; gap: 8px; }
        .btn-group button {
    
            padding: 10px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 13px;
            font-weight: 500;
        }
        .btn-global {
     background: #1890ff; color: white; }
        .btn-local  {
     background: #52c41a; color: white; }
        .btn-compare {
     background: #722ed1; color: white; }
        .btn-group button:hover {
     opacity: 0.85; }
        .info-box {
    
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(255,255,255,0.95);
            padding: 12px 20px;
            border-radius: 8px;
            font-size: 13px;
            line-height: 1.8;
            z-index: 100;
            box-shadow: 0 2px 12px rgba(0,0,0,0.15);
            max-width: 600px;
        }
        .info-box code {
     background: #f0f0f0; padding: 1px 4px; border-radius: 3px; }
    </style>
</head>
<body>
<h1 class="page-title">第7课:Global 与 Local 场景模式</h1>

<div class="control-panel">
    <div id="modeBadge" class="mode-badge badge-global">当前模式:GLOBAL</div>
    <div class="btn-group">
        <button class="btn-global" id="btnGlobal">切换为 Global 模式</button>
        <button class="btn-local"  id="btnLocal">切换为 Local 模式</button>
        <button class="btn-compare" id="btnCompare">对比:北京 vs 小范围园区</button>
    </div>
</div>

<div class="info-box">
    🌐 <b>Global 模式</b>:球面坐标系(WGS84),适合<code>全省/全国/全球</code>尺度<br>
    📐 <b>Local 模式</b>:平面投影坐标系,适合<code>园区/城区</code>尺度,量算更精准
</div>

<div id="mapContainer"></div>

<script type="module">
    const Map = await $arcgis.import("@arcgis/core/Map.js");
    const SceneView = await $arcgis.import("@arcgis/core/views/SceneView.js");
    const getTianditu = await $arcgis.import("https://openlayers.vip/examples/resources/tianditu.js");

    // ========== 天地图图层 ==========
    const vecLayers = getTianditu.default({
     type: "vec_w" });

    // ========== 创建 Map ==========
    const map = new Map({
    
        basemap: {
     baseLayers: [vecLayers.base, vecLayers.anno] },
        ground: {
    
            surface: {
    
                elevationLayers: [{
    
                    url: "https://www.geosceneonline.cn/image/rest/services/OpenData/ChinaTerrain3D/ImageServer/"
                }]
            }
        }
    });

    // ========== 初始为 Global 模式 ==========
    let currentMode = "global";

    let view = new SceneView({
    
        container: "mapContainer",
        map: map,
        viewingMode: "global",       // 关键属性:global | local
        center: [116.39, 39.9],
        zoom: 10,
        tilt: 60,
        constraints: {
    
            altitude: {
     max: 20000000 }  // 限制最大高度,避免缩太小
        }
    });

    // ========== 更新 UI 徽章 ==========
    function updateBadge(mode) {
    
        const badge = document.getElementById("modeBadge");
        if (mode === "global") {
    
            badge.className = "mode-badge badge-global";
            badge.textContent = "当前模式:GLOBAL(球面)";
        } else {
    
            badge.className = "mode-badge badge-local";
            badge.textContent = "当前模式:LOCAL(平面)";
        }
    }

    // ========== 切换为 Global 模式 ==========
    function switchToGlobal() {
    
        if (currentMode === "global") return;
        currentMode = "global";
        // 销毁旧视图,创建新视图(viewingMode 创建后不可更改)
        view.destroy();
        view = new SceneView({
    
            container: "mapContainer",
            map: map,
            viewingMode: "global",
            center: [116.39, 39.9],
            zoom: 10,
            tilt: 60
        });
        updateBadge("global");
    };

    // ========== 切换为 Local 模式 ==========
    function switchToLocal() {
    
        if (currentMode === "local") return;
        currentMode = "local";
        view.destroy();
        view = new SceneView({
    
            container: "mapContainer",
            map: map,
            viewingMode: "local",        // 关键:切换为局部平面模式
            center: [116.39, 39.9],
            zoom: 14,
            tilt: 55,
            constraints: {
    
                clipToExtent: true,       // 限制仅渲染 extent 范围内
                altitude: {
     max: 50000 }
            }
        });
        updateBadge("local");
    };

    // ========== 对比演示:分别定位到全球视角和局部园区 ==========
    function compareModes() {
    
        if (currentMode === "global") {
    
            // 当前是 Global → 飞到北京上空(展示球面效果)
            view.goTo({
    
                position: [116.39, 39.9, 2500000],
                tilt: 0,
                heading: 0
            });
        } else {
    
            // 当前是 Local → 定位到小规模园区范围
            view.goTo({
    
                position: [116.39, 39.9, 2000],
                tilt: 55,
                heading: 0
            });
        }
    };

    document.getElementById("btnGlobal").onclick = switchToGlobal;
    document.getElementById("btnLocal").onclick  = switchToLocal;
    document.getElementById("btnCompare").onclick = compareModes;

    updateBadge("global");
    view.when(() => {
    });
</script>
</body>
</html>

五、在线示例

🔗 在线体验地址(GitHub资源,等待时间较长,或者架梯子):https://southjor.github.io/arcgis-examples/lessons/lesson7.html

在这里插入图片描述

操作说明:

  1. 点击「切换为 Local 模式」按钮,观察地球从球面变为平面的视觉效果
  2. 在 Global 模式下大幅缩小,可看到完整地球球体
  3. 在 Local 模式下放大,建筑与地面贴合更自然,无球面扭曲

六、关键API说明

API 说明
SceneView.viewingMode 场景模式:"global"(球面,默认)/ "local"(平面)
SceneView.constraints.clipToExtent Local 模式下是否仅渲染 extent 范围内(避免平面无限延伸)
SceneView.constraints.altitude.max 限制最大视角高度,Global 模式下防止缩太小看不到地球
camera.position Global 模式为 [lng, lat, alt];Local 模式 x/y 为投影坐标
map.ground 两种模式下高程数据的处理和显示方式不同,Local 精度更高
view.destroy() 切换模式时需先销毁旧视图实例,再创建新模式视图

两种模式核心差异对比

对比维度 Global 模式 Local 模式
坐标系 WGS84(球面) 投影坐标系(平面)
地球曲率 有(真实球面) 无(平面投影)
适用尺度 全省/全国/全球 园区/城区
量算精度 球面距离(大范围准确) 平面距离(小范围更精准)
高程处理 贴合球面 平面拉伸,精度更高
性能建议 注意全球数据加载性能 可加载更高精度局部数据

七、系列导航

⬅️ 上一篇:ArcGIS JS 基础教程(6):地图弹窗信息窗口

⬅️ 下一篇:ArcGIS JS 基础教程(8):环境光照与阴影


💡 小贴士:如果你在 Local 模式下使用自定义切片底图,请确保切片服务的坐标系为投影坐标系(如 EPSG:3857),否则会出现图像偏移或无法显示的问题。

相关文章
|
云安全 安全 网络安全
|
7天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
1996 1
|
14天前
|
JSON 程序员 数据格式
最新版Notepad++下载、安装、配置保姆级教程(附官网安装包)
Notepad++ 是一款免费开源、轻量快速的 Windows 文本编辑器,支持多标签、语法高亮、代码折叠与宏录制,体积小、启动快、资源占用低,兼容 x64/x32/ARM 架构,是程序员和文字工作者的高效利器。
2938 0
|
13天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
2190 44
|
10天前
|
Web App开发 人工智能 前端开发
2026 年 AI 浏览器怎么选?Ego Lite、Tabbit、Chrome DevTools MCP 深度对比,开发者到底该用哪个?
2026年AI浏览器选型指南:Ego Lite专注多Agent协同(Mac优先),Tabbit国产首选,原生支持Windows与Codex控制;Chrome DevTools MCP非浏览器而是开发者利器,深度集成Console/Network/Performance调试能力,让AI真正“看懂并验证”前端代码。
1398 3
|
7天前
|
安全 应用服务中间件 Go
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
|
7天前
|
开发者 索引
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
Open Plugin Market 是一个开放插件索引平台,收录800+优质DSH插件,支持分类浏览、智能推荐与随机发现。无需安装市场,粘贴索引地址即可一键接入,网页版直访 dshopen.org,助力小开发者被看见。(239字)
1584 1
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
|
6天前
|
人工智能 Linux API
Magpie, 轻量本地AI网关!免费Token用不完!
Magpie 是开源AI编程模型调度网关,上线一月获1.8万Star。它通过本地高性能网关统一托管多平台API密钥、账号与免费额度,实现跨Agent的模型秒级切换、静默额度轮换与资源复用,显著降低碎片化管理成本。(239字)
Magpie, 轻量本地AI网关!免费Token用不完!
|
6天前
|
安全 程序员 应用服务中间件
PHP 被曝两大致命漏洞,请所有程序员抓紧做这件事!
PHP 官方发布了紧急安全更新,一口气修复了两个严重漏洞。如果网站和服务器用了 PHP 应该怎么处理?漏洞的原理是什么?本文给你讲清楚
672 0

热门文章

最新文章