/** * 地图工具函数库 * 包含坐标转换、地图计算、数据处理等工具函数 */ // import Vue from 'vue' // // // 渲染vue组件并获取dom结构 // export function renderComponents(comp, params) { // const vNodeDom = document.createElement("div") // document.body.appendChild(vNodeDom) // // const SubVue = Vue.extend(comp) // const app = new SubVue({ // el: vNodeDom, // propsData: params // }) // return app.$el // } /** * 对象深度合并函数 * @param target 目标对象,合并结果会存储在此对象中 * @param source 源对象,提供要合并的属性 * @returns {any} 合并后的目标对象 * @example * ```javascript * const target = { a: 1, b: { c: 2 } }; * const source = { b: { d: 3 }, e: 4 }; * deepMerge(target, source); // { a: 1, b: { c: 2, d: 3 }, e: 4 } * ``` */ export function deepMerge(target: any, source: any): any { for (let key of Object.keys(source)) { if (source[key] instanceof Object && !Array.isArray(source[key])) { if (!target[key]) Object.assign(target, {[key]: {}}); deepMerge(target[key], source[key]); } else { Object.assign(target, {[key]: source[key]}); } } return target; } /** * 递归格式化坐标数组,将WGS-84坐标转换为GCJ-02(高德坐标) * 支持多种坐标格式:点坐标、线坐标、面坐标和对象数组 * @param position 坐标数组或对象数组 * @returns {any} 转换后的坐标数组或对象数组 * @example * ```javascript * // 点坐标 * wgs84ToGcj02Format([116.404, 39.915]); * * // 线坐标 * wgs84ToGcj02Format([[116.404, 39.915], [116.405, 39.916]]); * * // 对象数组 * wgs84ToGcj02Format([{lng: 116.404, lat: 39.915}, {lng: 116.405, lat: 39.916}]); * ``` */ export function wgs84ToGcj02Format(position: any): any { // 判断是否是二维数组(线或面坐标) if (position.every((item: any) => Array.isArray(item))) { return position.map((item: any) => { return wgs84ToGcj02Format(item); }); } // 判断是否是对象数组 else if (position.every((item: any) => typeof item === "object")) { return position.map((item: any) => { let data = wgs84ToGcj02Format([item.lng, item.lat]); return { ...item, lng: data[0], lat: data[1], }; }); } // 单点坐标 else { return wgs84ToGcj02(position[0], position[1]); } } /** * WGS-84 转 GCJ-02(高德坐标) * @param {number} wgsLng - WGS-84经度 * @param {number} wgsLat - WGS-84纬度 * @returns {[number, number]} GCJ-02经纬度数组 [经度, 纬度] * @example * ```javascript * wgs84ToGcj02(116.404, 39.915); // 返回转换后的高德坐标 * ``` */ export function wgs84ToGcj02(wgsLng: any, wgsLat: any): any { if (!isCorrectPosition(wgsLng, wgsLat)) { return [wgsLng, wgsLat]; } const a = 6378245.0; // 长半轴 const ee = 0.00669342162296594323; // 扁率 /** * 纬度转换函数 * @param x 经度偏移 * @param y 纬度偏移 * @returns {number} 纬度转换值 */ function transformLat(x: any, y: any): any { let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0; ret += ((160.0 * Math.sin((y * Math.PI) / 3.0) + 320 * Math.sin((y * Math.PI) / 30.0)) * 2.0) / 3.0; return ret; } /** * 经度转换函数 * @param x 经度偏移 * @param y 纬度偏移 * @returns {number} 经度转换值 */ function transformLng(x: any, y: any): any { let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(x * Math.PI)) * 2.0) / 3.0; ret += ((150.0 * Math.sin((x * Math.PI) / 3.0) + 300.0 * Math.sin((x * Math.PI) / 15.0)) * 2.0) / 3.0; return ret; } let dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0); let dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0); const radLat = (wgsLat / 180.0) * Math.PI; let magic = Math.sin(radLat); magic = 1 - ee * magic * magic; const sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI); dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI); const gcjLat = wgsLat + dLat; const gcjLng = wgsLng + dLng; return [gcjLng, gcjLat]; } /** * 判断是否为正确的坐标范围 * @param {number} lng - 经度 * @param {number} lat - 纬度 * @returns {boolean} 坐标是否在有效范围内 * @example * ```javascript * isCorrectPosition(116.404, 39.915); // true * isCorrectPosition(200, 100); // false * ``` */ export function isCorrectPosition(lng: any, lat: any): boolean { return lng <= 180 && lng >= -180 && lat <= 90 && lat >= -90; } /** * 根据矩形左上角和右下角坐标计算矩形的完整坐标数组 * @param leftTop 左上角坐标 [经度, 纬度] * @param rightBottom 右下角坐标 [经度, 纬度] * @returns {Array>} 矩形的完整坐标数组,包含5个点(首尾相同以闭合矩形) * @example * ```javascript * createRectangleCoordinates([116.404, 39.915], [116.405, 39.914]); * // 返回:[ * // [116.404, 39.915], // 左上角 * // [116.405, 39.915], // 右上角 * // [116.405, 39.914], // 右下角 * // [116.404, 39.914], // 左下角 * // [116.404, 39.915] // 回到左上角,闭合矩形 * // ] * ``` */ export function createRectangleCoordinates(leftTop: any, rightBottom: any) { const [leftTopLng, leftTopLat] = leftTop; const [rightBottomLng, rightBottomLat] = rightBottom; // 计算四个角点 const leftTopPoint = [Number(leftTopLng), Number(leftTopLat)]; const rightTopPoint = [Number(rightBottomLng), Number(leftTopLat)]; const rightBottomPoint = [Number(rightBottomLng), Number(rightBottomLat)]; const leftBottomPoint = [Number(leftTopLng), Number(rightBottomLat)]; // 返回五个点的坐标对(首尾相同,形成闭合矩形) return [ leftTopPoint, rightTopPoint, rightBottomPoint, leftBottomPoint, leftTopPoint, // 闭合矩形 ]; } /** * 根据缩放级别返回该层级的“中间高度” * @param {number} level - 缩放级别 [1, 18] * @example * ```javascript * getLevelMiddleHeight(11); // 返回level=11对应的中间高度 * ``` */ export function getLevelMiddleHeight(level: number): any { if (level == 18) { return 0; } const EARTH_RADIUS = 6378137; // WGS84 赤道半径(米) const clampedLevel = Math.min(18, Math.max(1, level)); // 可选:自动 clamp const height = (Math.PI * EARTH_RADIUS) / Math.pow(2, clampedLevel); return parseFloat(height.toFixed(2)); } /** * 根据高度反推地图缩放级别(1~18) * @param {number} height - 当前视野高度(米) * @returns {number} 对应的地图缩放级别 * @example * ```javascript * getHeightToLevel(100000); // 根据高度返回对应的缩放级别 * ``` */ export function getHeightToLevel(height: number): number { if (height > 32000000) return 1; if (height <= 0) return 18; // 解公式:height = 32e6 / 2^(level-2) let level = 2 + Math.log(32000000 / height) / Math.log(2); level = Math.floor(level); // 限制在 1~18 return Math.max(1, Math.min(18, level)); } /** * 将坐标数组中的字符串类型元素统一转换为数字类型 * 支持一维、二维、三维坐标数组 * @param position 字符串坐标数组 * @returns {number[] | number[][] | number[][][]} 转换后的数字坐标数组 * @example * ```javascript * convertPosition(["116.404", "39.915"]); // [116.404, 39.915] * convertPosition([["116.404", "39.915"], ["116.405", "39.916"]]); // 二维数组转换 * ``` */ export function convertPosition( position: string[] | string[][] | string[][][] ): number[] | number[][] | number[][][] { return position.map((item) => { if (Array.isArray(item)) { return item.map((innerItem) => { if (Array.isArray(innerItem)) { // 第三层,将每个字符串转数字 return innerItem.map((str) => Number(str)); } else { return Number(innerItem); } }); } else { return Number(item); } }) as number[] | number[][] | number[][][]; } /** * mars3d 地图重写瓦片加载失败的回调函数 * 用于过滤掉控制台中瓦片加载失败的报错,提高开发体验 * @example * ```javascript * // 在地图初始化后调用 * mars3dTileErrorHandler(); * ``` */ export function mars3dTileErrorHandler() { // 确保 Cesium 已加载 if (typeof Cesium !== "undefined" && Cesium.TileProviderError) { const originalReportError = Cesium.TileProviderError.reportError; Cesium.TileProviderError.reportError = function ( existingError: any, provider: any, errorEvent: any, message: any, x: any, y: any, level: any, error: any ) { // 判断是否为“瓦片不存在”类错误(如 404、fetch 失败等) const shouldIgnore = message?.includes("Failed to obtain image") || message?.includes("无法获得图块") || message?.includes("404") || (error && error.status === 404) || message?.includes("NetworkError") || message?.includes("not found"); if (shouldIgnore) { // 静默处理:不打印日志,但仍可触发 errorEvent(供自定义监听) if (Cesium.defined(errorEvent) && errorEvent.numberOfListeners > 0) { let tileError = existingError; if (Cesium.defined(existingError)) { tileError.provider = provider; tileError.message = message; tileError.x = x; tileError.y = y; tileError.level = level; tileError.retry = false; tileError.error = error; tileError.timesRetried = Cesium.defaultValue(tileError.timesRetried, 0) + 1; } else { tileError = new Cesium.TileProviderError( provider, message, x, y, level, 0, error ); } errorEvent.raiseEvent(tileError); return tileError; } else { // 没人监听,直接返回错误对象 return ( existingError || new Cesium.TileProviderError( provider, message, x, y, level, 0, error ) ); } } // 非忽略错误:走原始逻辑 return originalReportError.apply(this, arguments); }; } } /** * Cesium专用工具函数 */ /** * 将WGS84坐标转换为Cesium笛卡尔坐标 */ export function wgs84ToCartesian( lng: number, lat: number, alt: number = 0 ): any { return Cesium.Cartesian3.fromDegrees(lng, lat, alt); } /** * 将Cesium笛卡尔坐标转换为WGS84坐标 */ export function cartesianToWgs84(cartesian: any): { lng: number; lat: number; alt: number; } { const cartographic = Cesium.Cartographic.fromCartesian(cartesian); return { lng: Cesium.Math.toDegrees(cartographic.longitude), lat: Cesium.Math.toDegrees(cartographic.latitude), alt: cartographic.height, }; } /** * 计算两点间的距离(米) */ export function calculateDistance(pos1: any, pos2: any): number { const cartesian1 = wgs84ToCartesian(pos1.lng, pos1.lat, pos1.alt || 0); const cartesian2 = wgs84ToCartesian(pos2.lng, pos2.lat, pos2.alt || 0); return Cesium.Cartesian3.distance(cartesian1, cartesian2); } /** * 创建颜色材质 */ export function createColorMaterial(color: string, opacity: number = 1.0): any { return new Cesium.ColorMaterialProperty( Cesium.Color.fromCssColorString(color).withAlpha(opacity) ); } /** * 创建图片材质 */ export function createImageMaterial(imageUrl: string): any { return new Cesium.ImageMaterialProperty({ image: imageUrl, transparent: true, }); } /** * 计算包围球 */ export function calculateBoundingSphere( positions: Array<[number, number, number?]> ): any { const cartesians = positions.map((pos) => wgs84ToCartesian(pos[0], pos[1], pos[2] || 0) ); return Cesium.BoundingSphere.fromPoints(cartesians); } /** * 创建实体集合 */ export function createEntityCollection(id: string): any { return new Cesium.CustomDataSource(id); }