ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版)
本文适用 ArcGIS JS API 4.28-4.33 版本,核心是实现一款进阶版地图卷帘组件。该组件在基础卷帘功能上,新增并强化了三大关键特性:多图层切换 、自定义滑块样式 与动态光影效果 ,并支持对 Swipe 组件参数的动态修改。
本文为
文章目录
- [ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版)](#ArcGIS for JS 实现地图卷帘效果,动态修改参数(进阶版))
 
| 工具 /插件/系统 名 | 版本 | 说明 | 
|---|---|---|
| ArcGIS JS API | 4.28~4.33 | 地图核心能力(底图加载、视图渲染) | 
| 天地图服务 | - | 提供街道、卫星、地形等底图数据源 | 
效果图
| 效果图 | 
|---|
![]()  | 
功能展示
- 垂直/水平方向的卷帘切换
 - 自定义卷帘位置调整
 - 可选择对比的图层
 - 键盘方向键控制
 - 重置和全图查看功能
 - 美观的UI设计和动画效果
 
实现步骤
1. 基础HTML结构搭建
首先,我们需要创建基本的HTML页面结构,包括地图容器、控制面板和提示信息:
            
            
              html
              
              
            
          
          <div id="viewDiv"></div>
<div id="controlPanel">
    <!-- 控制面板内容 -->
</div>
<div class="instruction">
    <i class="fa fa-hand-pointer-o" aria-hidden="true"></i> 拖动卷帘滑块或使用键盘方向键调整位置
</div>
        viewDiv:用于显示地图的容器controlPanel:包含各种控制元素的面板instruction:底部的操作提示
2. 样式设计
为了使界面美观且易用,我们需要设计相应的CSS样式:
            
            
              css
              
              
            
          
          /* 基础样式 */
html, body, #viewDiv {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
}
/* 控制面板样式 */
#controlPanel {
    position: absolute;
    top: 10px;
    right: 10px;
    background-color: white;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    z-index: 100;
    width: 280px;
}
/* 自定义卷帘样式 */
.esri-swipe__handle {
    background-color: #0079c1 !important;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) !important;
}
/* 动态光影效果 */
@keyframes glow {
    0% { box-shadow: 0 0 5px rgba(0, 121, 193, 0.5); }
    50% { box-shadow: 0 0 20px rgba(0, 121, 193, 0.8); }
    100% { box-shadow: 0 0 5px rgba(0, 121, 193, 0.5); }
}
.esri-swipe__divider {
    animation: glow 2s infinite;
}
        特别注意对卷帘组件的样式定制,通过!important覆盖默认样式,并添加了光影动画效果增强视觉体验。
3. 引入必要的库
我们需要引入ArcGIS API for JavaScript和Font Awesome图标库:
            
            
              html
              
              
            
          
          <!-- ArcGIS API for JavaScript -->
<link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.28/"></script>
<!-- Font Awesome -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
        4. 初始化地图和图层
首先,我们需要导入ArcGIS API的核心模块并创建地图实例:
            
            
              javascript
              
              
            
          
          const [Map, MapView, Swipe] = await $arcgis.import([
    "@arcgis/core/Map",
    "@arcgis/core/views/MapView",
    "@arcgis/core/widgets/Swipe"
]);
// 创建地图
const map = new Map();
// 创建地图视图
const view = new MapView({
    container: "viewDiv",
    map: map,
    center: [104, 35], // 中国中心位置
    zoom: 4
});
        接下来,定义并加载需要对比的图层:
            
            
              javascript
              
              
            
          
          // 定义可用于对比的图层
const layers = [
    {
        id: "streets",
        name: "街道地图",
        type: "tile",
        url: getUrlTemplate('vec'),
        opacity: 1
    },
    {
        id: "satellite",
        name: "卫星影像",
        type: "tile",
        url: getUrlTemplate('img'),
        opacity: 1
    },
    {
        id: "topo",
        name: "地形图",
        type: "tile",
        url: getUrlTemplate('cta'),
        opacity: 1
    }
];
// 加载所有图层
const layerInstances = {};
layers.forEach(layerInfo => {
    let layer = new WebTileLayer({
        urlTemplate: layerInfo.url,
        opacity: layerInfo.opacity,
        copyright: "天地图 © 国家地理信息公共服务平台",
        spatialReference: config.spatialReference,
        tileInfo: tileInfo
    });
    if (layer) {
        layerInstances[layerInfo.id] = layer;
        map.add(layer);
    }
});
        5. 创建图层选择器
为了允许用户切换对比的图层,我们需要创建下拉选择器并填充图层选项:
            
            
              javascript
              
              
            
          
          // 填充图层选择器
const leadingLayerSelect = document.getElementById("leadingLayerSelect");
const trailingLayerSelect = document.getElementById("trailingLayerSelect");
layers.forEach(layerInfo => {
    // 为右侧图层选择器添加选项
    const leadingOption = document.createElement("option");
    leadingOption.value = layerInfo.id;
    leadingOption.textContent = layerInfo.name;
    leadingLayerSelect.appendChild(leadingOption);
    // 为左侧图层选择器添加选项
    const trailingOption = document.createElement("option");
    trailingOption.value = layerInfo.id;
    trailingOption.textContent = layerInfo.name;
    trailingLayerSelect.appendChild(trailingOption);
});
// 设置默认选择的图层
leadingLayerSelect.value = "streets";
trailingLayerSelect.value = "satellite";
        6. 初始化卷帘组件
创建Swipe组件并添加到地图视图中:
            
            
              javascript
              
              
            
          
          let swipeWidget = new Swipe({
    view: view,
    leadingLayers: [layerInstances["streets"]],
    trailingLayers: [layerInstances["satellite"]],
    position: 50, // 初始位置在50%处
    direction: "vertical", // 初始为垂直方向
    visibleElements: {
        handle: true,
        divider: true
    }
});
// 将卷帘组件添加到视图
view.ui.add(swipeWidget);
        7. 添加交互功能
为了使卷帘工具更加易用,我们需要添加各种交互功能:
卷帘位置调整
            
            
              javascript
              
              
            
          
          const swipePositionSlider = document.getElementById("swipePosition");
const positionValue = document.getElementById("positionValue");
swipePositionSlider.addEventListener("input", function () {
    const position = parseInt(this.value);
    swipeWidget.position = position;
    positionValue.textContent = position + "%";
});
        卷帘方向切换
            
            
              javascript
              
              
            
          
          const verticalBtn = document.getElementById("verticalBtn");
const horizontalBtn = document.getElementById("horizontalBtn");
verticalBtn.addEventListener("click", function () {
    swipeWidget.direction = "vertical";
    verticalBtn.classList.add("active");
    horizontalBtn.classList.remove("active");
});
horizontalBtn.addEventListener("click", function () {
    swipeWidget.direction = "horizontal";
    horizontalBtn.classList.add("active");
    verticalBtn.classList.remove("active");
});
        图层切换功能
            
            
              javascript
              
              
            
          
          leadingLayerSelect.addEventListener("change", function () {
    const selectedLayerId = this.value;
    const selectedLayer = layerInstances[selectedLayerId];
    
    if (selectedLayer) {
        swipeWidget.leadingLayers.removeAll();
        swipeWidget.leadingLayers.add(selectedLayer);
    }
});
trailingLayerSelect.addEventListener("change", function () {
    const selectedLayerId = this.value;
    const selectedLayer = layerInstances[selectedLayerId];
    
    if (selectedLayer) {
        swipeWidget.trailingLayers.removeAll();
        swipeWidget.trailingLayers.add(selectedLayer);
    }
});
        键盘控制
            
            
              javascript
              
              
            
          
          document.addEventListener("keydown", function (event) {
    if (event.key === "ArrowLeft" || event.key === "ArrowRight" ||
        event.key === "ArrowUp" || event.key === "ArrowDown") {
        
        event.preventDefault();
        let newPosition = swipeWidget.position;
        
        // 根据方向键和当前卷帘方向调整位置
        if (swipeWidget.direction === "vertical") {
            if (event.key === "ArrowLeft") {
                newPosition = Math.max(0, newPosition - 1);
            } else if (event.key === "ArrowRight") {
                newPosition = Math.min(100, newPosition + 1);
            }
        } else {
            if (event.key === "ArrowUp") {
                newPosition = Math.max(0, newPosition - 1);
            } else if (event.key === "ArrowDown") {
                newPosition = Math.min(100, newPosition + 1);
            }
        }
        
        swipeWidget.position = newPosition;
        // 更新滑块和显示值
        swipePositionSlider.value = newPosition;
        positionValue.textContent = newPosition + "%";
    }
});
        重置和全图功能
            
            
              javascript
              
              
            
          
          // 重置按钮事件
document.getElementById("resetBtn").addEventListener("click", function () {
    // 重置卷帘位置
    swipeWidget.position = 50;
    swipePositionSlider.value = 50;
    positionValue.textContent = "50%";
    
    // 重置图层选择
    leadingLayerSelect.value = "streets";
    trailingLayerSelect.value = "satellite";
    
    // 重置卷帘方向
    swipeWidget.direction = "vertical";
    verticalBtn.classList.add("active");
    horizontalBtn.classList.remove("active");
});
// 全图按钮事件
document.getElementById("fullExtentBtn").addEventListener("click", function () {
    view.goTo({
        center: [104, 35],
        zoom: 4
    });
});
        所有代码
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 for JS 地图卷帘效果进阶</title>
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 100%;
            width: 100%;
        }
        #controlPanel {
            position: absolute;
            top: 10px;
            right: 10px;
            background-color: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            z-index: 100;
            width: 280px;
        }
        .control-section {
            margin-bottom: 15px;
        }
        .control-section:last-child {
            margin-bottom: 0;
        }
        .control-label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            color: #333;
        }
        .slider-container {
            display: flex;
            align-items: center;
        }
        .slider-container input[type="range"] {
            flex-grow: 1;
            margin-right: 10px;
        }
        .slider-value {
            width: 50px;
            text-align: right;
        }
        .button-group {
            display: flex;
            gap: 8px;
            margin-top: 10px;
        }
        button {
            padding: 8px 12px;
            background-color: #0079c1;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }
        button:hover {
            background-color: #005a8c;
        }
        button.reset {
            background-color: #f04e31;
        }
        button.reset:hover {
            background-color: #d93a1e;
        }
        .layer-selector {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            background-color: white;
        }
        .instruction {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 10px 20px;
            border-radius: 20px;
            font-size: 14px;
            z-index: 100;
            pointer-events: none;
        }
        /* 自定义卷帘滑块样式 */
        .esri-swipe__handle {
            /* width: 4px !important; */
            background-color: #0079c1 !important;
            box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) !important;
        }
        .esri-swipe__handle::before,
        .esri-swipe__handle::after {
            width: 30px !important;
            height: 30px !important;
            background-color: #0079c1 !important;
            border: 2px solid white !important;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3) !important;
        }
        /* 动态光影效果 */
        @keyframes glow {
            0% {
                box-shadow: 0 0 5px rgba(0, 121, 193, 0.5);
            }
            50% {
                box-shadow: 0 0 20px rgba(0, 121, 193, 0.8);
            }
            100% {
                box-shadow: 0 0 5px rgba(0, 121, 193, 0.5);
            }
        }
        .esri-swipe__divider {
            animation: glow 2s infinite;
        }
    </style>
    <!-- ArcGIS API for JavaScript -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css">
    <script src="https://js.arcgis.com/4.28/"></script>
    <!-- Font Awesome -->
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
</head>
<body>
    <div id="viewDiv"></div>
    <div id="controlPanel">
        <div class="control-section">
            <div class="control-label">卷帘位置</div>
            <div class="slider-container">
                <input type="range" id="swipePosition" min="0" max="100" value="50" class="slider">
                <span id="positionValue" class="slider-value">50%</span>
            </div>
        </div>
        <div class="control-section">
            <div class="control-label">卷帘方向</div>
            <div class="button-group">
                <button id="verticalBtn" class="active"><i class="fa fa-arrows-v" aria-hidden="true"></i> 垂直</button>
                <button id="horizontalBtn"><i class="fa fa-arrows-h" aria-hidden="true"></i> 水平</button>
            </div>
        </div>
        <div class="control-section">
            <div class="control-label">右侧图层</div>
            <select id="leadingLayerSelect" class="layer-selector"></select>
        </div>
        <div class="control-section">
            <div class="control-label">左侧图层</div>
            <select id="trailingLayerSelect" class="layer-selector"></select>
        </div>
        <div class="control-section">
            <div class="button-group">
                <button id="resetBtn" class="reset"><i class="fa fa-refresh" aria-hidden="true"></i> 重置</button>
                <button id="fullExtentBtn"><i class="fa fa-arrows-alt" aria-hidden="true"></i> 全图</button>
            </div>
        </div>
    </div>
    <div class="instruction">
        <i class="fa fa-hand-pointer-o" aria-hidden="true"></i> 拖动卷帘滑块或使用键盘方向键调整位置
    </div>
    <script type="module">
        const [Map,
            MapView,
            Swipe] = await $arcgis.import([
                "@arcgis/core/Map",
                "@arcgis/core/views/MapView",
                "@arcgis/core/widgets/Swipe"
            ])
        import { loadTiandituBasemap } from './js/tiandituLoader.js';
        const {
            config,
            getUrlTemplate,
            WebTileLayer, tileInfo } = await loadTiandituBasemap();
        // 定义可用于对比的图层
        const layers = [
            {
                id: "streets",
                name: "街道地图",
                type: "tile",
                url: getUrlTemplate('vec'),
                opacity: 1
            },
            {
                id: "satellite",
                name: "卫星影像",
                type: "tile",
                url: getUrlTemplate('img'),
                opacity: 1
            },
            {
                id: "topo",
                name: "地形图",
                type: "tile",
                url: getUrlTemplate('cta'),
                opacity: 1
            }
        ];
        // 创建地图
        const map = new Map();
        // 创建地图视图
        const view = new MapView({
            container: "viewDiv",
            map: map,
            center: [104, 35], // 中国中心位置
            zoom: 4
        });
        // 加载所有图层
        const layerInstances = {};
        layers.forEach(layerInfo => {
            let layer = new WebTileLayer({
                urlTemplate: layerInfo.url,
                opacity: layerInfo.opacity,
                // subDomains: config.subDomains,
                copyright: "天地图 © 国家地理信息公共服务平台",
                spatialReference: config.spatialReference,
                tileInfo: tileInfo
            });
            if (layer) {
                layerInstances[layerInfo.id] = layer;
                map.add(layer);
            }
        });
        // 填充图层选择器
        const leadingLayerSelect = document.getElementById("leadingLayerSelect");
        const trailingLayerSelect = document.getElementById("trailingLayerSelect");
        layers.forEach(layerInfo => {
            const leadingOption = document.createElement("option");
            leadingOption.value = layerInfo.id;
            leadingOption.textContent = layerInfo.name;
            leadingLayerSelect.appendChild(leadingOption);
            const trailingOption = document.createElement("option");
            trailingOption.value = layerInfo.id;
            trailingOption.textContent = layerInfo.name;
            trailingLayerSelect.appendChild(trailingOption);
        });
        // 默认选择的图层
        leadingLayerSelect.value = "streets";
        trailingLayerSelect.value = "satellite";
        // 创建卷帘微件
        let swipeWidget = new Swipe({
            view: view,
            leadingLayers: [layerInstances["streets"]],
            trailingLayers: [layerInstances["satellite"]],
            position: 50,
            direction: "vertical",
            visibleElements: {
                handle: true,
                divider: true
            }
        });
        view.ui.add(swipeWidget);
        // 更新图层选择器事件
        leadingLayerSelect.addEventListener("change", function () {
            const selectedLayerId = this.value;
            const selectedLayer = layerInstances[selectedLayerId];
            if (selectedLayer) {
                // 移除当前的leading图层
                swipeWidget.leadingLayers.removeAll();
                // 添加新的leading图层
                swipeWidget.leadingLayers.add(selectedLayer);
    }
        });
        trailingLayerSelect.addEventListener("change", function () {
            const selectedLayerId = this.value;
            const selectedLayer = layerInstances[selectedLayerId];
            if (selectedLayer) {
                // 移除当前的trailing图层
                swipeWidget.trailingLayers.removeAll();
                // 添加新的trailing图层
                swipeWidget.trailingLayers.add(selectedLayer);
                // 更新透明度滑块
                document.getElementById("trailingOpacity").value = selectedLayer.opacity * 100;
                document.getElementById("trailingOpacityValue").textContent = (selectedLayer.opacity * 100) + "%";
            }
        });
        // 卷帘位置滑块事件
        const swipePositionSlider = document.getElementById("swipePosition");
        const positionValue = document.getElementById("positionValue");
        swipePositionSlider.addEventListener("input", function () {
            const position = parseInt(this.value);
            swipeWidget.position = position;
            positionValue.textContent = position + "%";
        });
        // 卷帘方向按钮事件
        const verticalBtn = document.getElementById("verticalBtn");
        const horizontalBtn = document.getElementById("horizontalBtn");
        verticalBtn.addEventListener("click", function () {
            swipeWidget.direction = "vertical";
            verticalBtn.classList.add("active");
            horizontalBtn.classList.remove("active");
        });
        horizontalBtn.addEventListener("click", function () {
            swipeWidget.direction = "horizontal";
            horizontalBtn.classList.add("active");
            verticalBtn.classList.remove("active");
        });
        // 重置按钮事件
        document.getElementById("resetBtn").addEventListener("click", function () {
            // 重置卷帘位置
            swipeWidget.position = 50;
            swipePositionSlider.value = 50;
            positionValue.textContent = "50%";
            // 重置透明度
            const leadingLayer = swipeWidget.leadingLayers.getItemAt(0);
            const trailingLayer = swipeWidget.trailingLayers.getItemAt(0);
            if (leadingLayer) {
                leadingLayer.opacity = 1;
                document.getElementById("leadingOpacity").value = 100;
                document.getElementById("leadingOpacityValue").textContent = "100%";
            }
            if (trailingLayer) {
                trailingLayer.opacity = 1;
                document.getElementById("trailingOpacity").value = 100;
                document.getElementById("trailingOpacityValue").textContent = "100%";
            }
            // 重置图层选择
            leadingLayerSelect.value = "streets";
            trailingLayerSelect.value = "satellite";
            // 重置卷帘方向
            swipeWidget.direction = "vertical";
            verticalBtn.classList.add("active");
            horizontalBtn.classList.remove("active");
        });
        // 全图按钮事件
        document.getElementById("fullExtentBtn").addEventListener("click", function () {
            view.goTo({
                center: [104, 35],
                zoom: 4
            });
        });
        // 键盘方向键控制卷帘位置
        document.addEventListener("keydown", function (event) {
            if (event.key === "ArrowLeft" || event.key === "ArrowRight" ||
                event.key === "ArrowUp" || event.key === "ArrowDown") {
                event.preventDefault();
                let newPosition = swipeWidget.position;
                // 根据方向键和当前卷帘方向调整位置
                if (swipeWidget.direction === "vertical") {
                    if (event.key === "ArrowLeft") {
                        newPosition = Math.max(0, newPosition - 1);
                    } else if (event.key === "ArrowRight") {
                        newPosition = Math.min(100, newPosition + 1);
                    }
                } else {
                    if (event.key === "ArrowUp") {
                        newPosition = Math.max(0, newPosition - 1);
                    } else if (event.key === "ArrowDown") {
                        newPosition = Math.min(100, newPosition + 1);
                    }
                }
                swipeWidget.position = newPosition;
            }
        });
        // 初始化透明度滑块值
        document.getElementById("leadingOpacity").value = 100;
        document.getElementById("leadingOpacityValue").textContent = "100%";
        document.getElementById("trailingOpacity").value = 100;
        document.getElementById("trailingOpacityValue").textContent = "100%";
        // 添加按钮激活样式
        verticalBtn.classList.add("active");
    </script>
</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,
            vecLayer,
            cvaLayer
        };
    } catch (error) {
        console.error("天地图加载失败:", error);
        throw new Error("天地图公共模块加载异常,请检查依赖和配置");
    }
}
        