import map from "./map"; import layer from "./layer/layer"; import point from "./graphic/point"; import numPoint from "./graphic/numPoint"; import imagePoint from "./graphic/imagePoint"; import divPoint from "./graphic/divPoint"; import label from "./graphic/label"; import line from "./graphic/line"; import circle from "./graphic/circle"; import rectangle from "./graphic/rectangle"; import polygon from "./graphic/polygon"; import route from "./other/route"; import heatMap from "./layer/heatMap"; import cluster from "./layer/cluster"; import pointCloud from "./layer/pointCloud"; import geoJson from "./layer/geoJson"; import { mars3dTileErrorHandler } from "./util"; /** * HnMap类是一个地图工具类,用于创建和管理地图对象及其相关元素 * 支持mars3d、高德地图和思极地图三种地图引擎 */ /** * 地图初始化配置选项 */ interface MapOption { lng: number; // 地图中心点经度 lat: number; // 地图中心点纬度 level: number; // 地图初始缩放级别 pitch: number; // 地图初始俯仰角度 gaode_key: string; // 高德地图API密钥 sj_app_key: string; // 思极地图应用密钥 sj_app_secret: string; // 思极地图应用密钥 sj_js_url: string; // 思极地图JS API加载地址 sj_style: string; // 思极地图样式 sj_route_net: boolean; // 是否显示思极地图路网 // 添加Cesium相关配置 cesium_accessToken: string; // Cesium Ion访问令牌 cesium_terrainProvider: any; // 离线地形配置 cesium_imageryProvider: any; // 离线影像瓦片配置 cesium_annotationProvider: any; // 离线注记瓦片配置 } /** * HnMap主类,提供统一的地图操作API */ export default class HnMap { id: string | null = null; // 地图容器ID option: MapOption | null = null; // 地图配置选项 mapType: string | null = null; // 地图类型:mars3d、gaode、siji map: any = null; // 底层地图实例 Layer: any = null; // 图层管理实例 Point: any = null; // 点要素类 NumPoint: any = null; // 数字点要素类 ImagePoint: any = null; // 图片点要素类 DivPoint: any = null; // DOM点要素类 Label: any = null; // 标签要素类 Line: any = null; // 线要素类 Circle: any = null; // 圆要素类 Rectangle: any = null; // 矩形要素类 Polygon: any = null; // 多边形要素类 Route: any = null; // 路线规划类 HeatMap: any = null; // 热力图类 Cluster: any = null; // 聚类图层类 PointCloud: any = null; // 点云图层类 GeoJson: any = null; // GeoJSON图层类 // 构造函数访问控制标志 private static allowConstruction = false; /** * HnMap类的构造函数 * @private 私有构造函数,只能通过create静态方法创建实例 * @param id 地图容器的ID * @param option 地图的配置选项 * @param mapType 地图的类型 * @param map 底层地图实例 */ private constructor( id: string, option: MapOption, mapType: string, map: any ) { if (!HnMap.allowConstruction) { throw new Error("请使用 HnMap.create() 创建实例"); } this.id = id; this.option = option; this.mapType = mapType; this.map = map; // 此时map已创建完成 } /** * 创建HnMap实例的静态方法 * @param id 地图容器的DOM元素ID * @param option 地图的配置选项 * @param mapType 用户期望的地图类型(如 'mars3d', 'gaode', 'siji') * @returns Promise 返回HnMap实例的Promise * @example * ```javascript * HnMap.create('mapContainer', { * lng: 112.55074, * lat: 37.7979042, * level: 11, * gaode_key: 'your-gaode-key', * sj_app_key: 'your-siji-key', * sj_app_secret: 'your-siji-secret' * }, 'mars3d').then(map => { * // 使用地图实例 * }); * ``` */ static async create(id: string, option: MapOption, mapType: string) { // 获取资源加载的基础路径 const basePath = window.location.pathname.endsWith("/") ? window.location.pathname : window.location.pathname.substring( 0, window.location.pathname.lastIndexOf("/") + 1 ); // 预加载turf地理空间分析库(所有地图类型都需要) await loadResource(basePath + "lib/turf/turf.min.js", "js"); // 尝试创建地图,支持失败回退机制 let innerMap: any = null; let finalMapType = mapType; try { // 尝试创建指定类型的地图 innerMap = await HnMap.tryCreateMap(id, option, mapType, basePath); } catch (error) { console.warn(`[${mapType}] 地图初始化失败,尝试回退到 mars3d`, error); // 回退到mars3d地图 finalMapType = "mars3d"; try { innerMap = await HnMap.tryCreateMap(id, option, "mars3d", basePath); } catch (fallbackError) { console.error("mars3d 回退也失败了!", fallbackError); throw new Error("所有地图类型均无法加载,请检查网络或资源配置"); } } // 创建HnMap实例 HnMap.allowConstruction = true; try { const instance = new HnMap(id, option, finalMapType, innerMap); instance.initModules(); // 初始化所有地图模块 return instance; } finally { // 恢复构造函数访问控制 HnMap.allowConstruction = false; } } /** * 尝试根据地图类型加载资源并创建地图实例 * @private * @param id 地图容器ID * @param option 地图配置选项 * @param mapType 地图类型 * @param basePath 资源加载基础路径 * @returns Promise 返回地图实例的Promise */ private static async tryCreateMap( id: string, option: MapOption, mapType: string, basePath: string ): Promise { // 1. 加载对应地图平台的资源 switch (mapType) { case "mars3d": // 加载Cesium资源 await loadResource(basePath + "lib/mars3d-Cesium/Widgets/widgets.css", "css"); await loadResource(basePath + "lib/mars3d-Cesium/Cesium.js", "js"); // 加载mars3d资源 await loadResource(basePath + "lib/mars3d/mars3d.css", "css"); await loadResource(basePath + "lib/mars3d/mars3d.js", "js"); // 加载mars3d热力图插件 await loadResource( basePath + "lib/mars3d/plugins/heatmap/heatmap.js", "js" ); await loadResource( basePath + "lib/mars3d/plugins/heatmap/mars3d-heatmap.js", "js" ); // 初始化mars3d瓦片错误处理 mars3dTileErrorHandler(); break; case "gaode": // 加载高德地图API await loadResource( `https://webapi.amap.com/maps?v=2.0&key=${option.gaode_key}&plugin=AMap.HeatMap,AMap.MarkerCluster,AMap.MoveAnimation`, "js" ); // 加载3D模型所需的Three.js资源 await loadResource( "https://a.amap.com/jsapi_demos/static/data3d/lib/three.117.js", "js" ); await loadResource( "https://a.amap.com/jsapi_demos/static/data3d/lib/GLTFLoader.117.min.js", "js" ); break; case "siji": // 加载思极地图JS API await loadResource(option.sj_js_url, "js"); // 检查SGMap全局变量是否已定义 if (typeof SGMap === "undefined") { throw new Error("siji 地图 JS 加载成功但 SGMap 未定义"); } // 登录思极地图 await SGMap.tokenTask.login(option.sj_app_key, option.sj_app_secret); // 加载思极地图插件 await SGMap.plugin([ "SGMap.DrawPolygonHandler", "SGMap.DrawCircleHandler", "SGMap.DrawRectangleHandler", "SGMap.GeocodingTask", "SGMap.RoadNetLayer", ]); break; case "cesium": // 加载Cesium资源 await HnMap.loadCesiumResources(option, basePath); break; default: throw new Error(`不支持的地图类型: ${mapType}`); } // 2. 创建地图对象实例 const MapClass = map({ id, option, mapType }); const innerMap = await MapClass.create(id, option); // 这里可能会失败(如容器不存在、key无效等) return innerMap; } // index.ts - private static async loadCesiumResources( option: MapOption, basePath: string ): Promise { // 使用CDN版本避免本地路径问题 // const cesiumVersion = "1.110.1"; // const cdnBaseUrl = `https://cdn.jsdelivr.net/npm/cesium@${cesiumVersion}/Build/Cesium/`; // console.log("使用CDN版本的Cesium:", cdnBaseUrl); await loadResource(basePath + "lib/Cesium/Widgets/widgets.css", "css"); await loadResource(basePath + "lib/Cesium/Cesium.js", "js"); // console.log(basePath, "=========cnd======="); // await loadResource(basePath + "lib/Cesium/cdn/widgets.css", "css"); // await loadResource(basePath + "lib/Cesium/cdn/Cesium.js", "js"); // 等待Cesium完全加载 await new Promise((resolve, reject) => { const maxWaitTime = 10000; const startTime = Date.now(); const checkCesium = () => { const currentTime = Date.now(); if (typeof Cesium !== "undefined") { // console.log(`Cesium ${Cesium.VERSION} 加载成功`); // 设置Cesium基础URL为CDN地址 // window.CESIUM_BASE_URL = cdnBaseUrl; // 设置访问令牌 if (option.cesium_accessToken && !option.cesium_imageryProvider) { Cesium.Ion.defaultAccessToken = option.cesium_accessToken; } resolve(); } else if (currentTime - startTime > maxWaitTime) { reject(new Error("Cesium加载超时")); } else { setTimeout(checkCesium, 100); } }; checkCesium(); }); } /** * 初始化地图模块,注册所有图形要素类 * @private */ private initModules() { this.Layer = layer(this); // 基础图层类 this.Point = point(this); // 点要素类 this.NumPoint = numPoint(this); // 数字点要素类 this.ImagePoint = imagePoint(this); // 图片点要素类 this.DivPoint = divPoint(this); // DOM点要素类 this.Label = label(this); // 标签要素类 this.Line = line(this); // 线要素类 this.Circle = circle(this); // 圆要素类 this.Rectangle = rectangle(this); // 矩形要素类 this.Polygon = polygon(this); // 多边形要素类 this.Route = route(this); // 路线规划类 this.HeatMap = heatMap(this); // 热力图类 this.Cluster = cluster(this); // 聚类图层类 this.PointCloud = pointCloud(this); // 点云图层类 this.GeoJson = geoJson(this); // GeoJSON图层类 } } /** * 检查资源是否已加载 * @param path 资源的路径 * @returns {boolean} 资源是否已加载 */ const isResourceLoaded = (path: string): boolean => { // 检查脚本 const scripts = Array.from(document.getElementsByTagName("script")); if (scripts.some((script) => script.src.includes(path))) { return true; } // 检查样式 const links = Array.from(document.getElementsByTagName("link")); return links.some((link) => link.href.includes(path)); }; /** * 加载资源的方法 * @param path 资源的路径 * @param type 资源的类型,'css'或'js' * @returns {Promise} 加载完成的Promise */ const loadResource = (path: string, type: string): Promise => { return new Promise((resolve, reject) => { // 如果资源已加载,直接返回 if (isResourceLoaded(path)) { resolve(); return; } if (type === "css") { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = path; link.onload = () => resolve(); link.onerror = reject; document.head.appendChild(link); } else { const script = document.createElement("script"); script.src = path; script.onload = () => resolve(); script.onerror = reject; document.body.appendChild(script); } }); };