/** * 根据经纬度范围和瓦片级别生成细分网格 * 用于globe模式下的球面渲染 */ import { MercatorCoordinate } from 'maplibre-gl' export interface TileMeshOptions { /** 经纬度边界 [west, south, east, north] */ bounds: [number, number, number, number]; /** 瓦片缩放级别 */ z: number; /** 细分级别,默认为瓦片级别的2倍 */ subdivisions?: number; } export interface TileMeshResult { /** 顶点数据 (x, y, u, v) 坐标对:位置和纹理坐标 */ vertices: Float32Array; /** 索引数据 */ indices: Uint16Array | Uint32Array; /** 是否使用32位索引 */ uses32bitIndices: boolean; /** 顶点数量 */ vertexCount: number; /** 三角形数量 */ triangleCount: number; } /** * 设备性能等级 */ export enum PerformanceLevel { LOW = 'low', // 低端设备 MEDIUM = 'medium', // 中端设备 HIGH = 'high' // 高端设备 } /** * 根据当前缩放级别和设备性能计算最佳细分数量 */ export function calculateOptimalSubdivisions( zoomLevel: number, performanceLevel: PerformanceLevel = PerformanceLevel.MEDIUM, options?: { /** 最小细分数量,默认4 */ minSubdivisions?: number; /** 最大细分数量,默认64 */ maxSubdivisions?: number; /** 是否为Globe模式,Globe模式需要更多细分,默认true */ isGlobeMode?: boolean; /** 视口区域大小(像素),影响所需细分程度 */ viewportSize?: { width: number; height: number }; } ): number { const { minSubdivisions = 4, maxSubdivisions = 64, isGlobeMode = true, viewportSize } = options || {}; // 基础细分计算:随着缩放级别指数增长 let baseSubdivisions: number; if (zoomLevel <= 2) { // 非常低的缩放级别,使用最少细分 baseSubdivisions = minSubdivisions; } else if (zoomLevel <= 6) { // 低到中等缩放级别:线性增长 baseSubdivisions = minSubdivisions + (zoomLevel - 2) * 2; } else if (zoomLevel <= 12) { // 中等到高缩放级别:较快增长 baseSubdivisions = 12 + (zoomLevel - 6) * 3; } else { // 高缩放级别:平缓增长避免性能问题 baseSubdivisions = 30 + (zoomLevel - 12) * 1.5; } // 设备性能调整系数 const performanceMultipliers = { [PerformanceLevel.LOW]: 0.6, // 低端设备减少40%细分 [PerformanceLevel.MEDIUM]: 1.0, // 中端设备保持基础细分 [PerformanceLevel.HIGH]: 1.4 // 高端设备增加40%细分 }; baseSubdivisions *= performanceMultipliers[performanceLevel]; // Globe模式调整:球面渲染需要更多细分来避免失真 if (isGlobeMode) { baseSubdivisions *= 1.2; } // 视口大小调整:大视口需要更多细分保证质量 if (viewportSize) { const viewportArea = viewportSize.width * viewportSize.height; const standardArea = 1920 * 1080; // 标准1080p面积 const sizeMultiplier = Math.sqrt(viewportArea / standardArea); baseSubdivisions *= Math.min(1.5, Math.max(0.7, sizeMultiplier)); } // 确保在合理范围内并且为2的幂次(对GPU更友好) const clampedSubdivisions = Math.max(minSubdivisions, Math.min(maxSubdivisions, baseSubdivisions)); // 向最近的2的幂次取整(4, 8, 16, 32, 64等) const powerOfTwo = Math.pow(2, Math.round(Math.log2(clampedSubdivisions))); return Math.max(minSubdivisions, Math.min(maxSubdivisions, powerOfTwo)); } /** * 检测设备性能等级(简化版本) */ export function detectPerformanceLevel(): PerformanceLevel { if (typeof window === 'undefined') return PerformanceLevel.HIGH; // 检查硬件并发数 const hardwareConcurrency = navigator.hardwareConcurrency || 4; // 检查内存信息(如果可用) const memory = (navigator as any).deviceMemory; // 检查WebGL能力 const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl2') || canvas.getContext('webgl'); if (!gl) return PerformanceLevel.LOW; const renderer = gl.getParameter(gl.RENDERER) || ''; const vendor = gl.getParameter(gl.VENDOR) || ''; // 基于多个指标综合判断 let score = 0; // CPU核心数评分 if (hardwareConcurrency >= 8) score += 3; else if (hardwareConcurrency >= 4) score += 2; else score += 1; // 内存评分 if (memory) { if (memory >= 8) score += 3; else if (memory >= 4) score += 2; else score += 1; } else { score += 2; // 默认中等 } // GPU评分(简化判断) if (renderer.toLowerCase().includes('intel')) { score += 1; // 集成显卡通常性能较低 } else if (renderer.toLowerCase().includes('nvidia') || renderer.toLowerCase().includes('amd')) { score += 3; // 独立显卡性能较好 } else { score += 2; // 默认 } // 根据总分判断性能等级 if (score <= 4) return PerformanceLevel.LOW; else if (score <= 7) return PerformanceLevel.MEDIUM; else return PerformanceLevel.HIGH; } /** * 将经纬度转换为Web Mercator坐标 (归一化到0-1范围) */ function lonLatToMercator(lon: number, lat: number): [number, number] { const mercator = MercatorCoordinate.fromLngLat({ lng: lon, lat: lat }) return [mercator.x, mercator.y] // const x = (lon + 180) / 360; // const latRad = (lat * Math.PI) / 180; // const y = (1 - Math.log(Math.tan(latRad / 2 + Math.PI / 4)) / Math.PI) / 2; // return [x, y]; } /** * 创建细分的瓦片网格 */ export function createSubdividedTileMesh(options: TileMeshOptions): TileMeshResult { const { bounds, z, subdivisions } = options; const [west, south, east, north] = bounds; // 根据瓦片级别确定细分级别 // 更高的瓦片级别需要更多的细分来在globe模式下保持平滑 const subdivLevel = subdivisions ?? Math.max(8, Math.min(32, Math.pow(2, Math.max(0, z - 5)))); // 将经纬度边界转换为归一化的Web Mercator坐标 const [mercWest, mercNorth] = lonLatToMercator(west, north); const [mercEast, mercSouth] = lonLatToMercator(east, south); // 创建顶点网格 const verticesPerRow = subdivLevel + 1; const verticesPerCol = subdivLevel + 1; const totalVertices = verticesPerRow * verticesPerCol; // 每个顶点包含4个float值:x, y, u, v (位置 + 纹理坐标) const vertices = new Float32Array(totalVertices * 4); // 生成顶点 for (let row = 0; row < verticesPerCol; row++) { for (let col = 0; col < verticesPerRow; col++) { const vertexIndex = (row * verticesPerRow + col) * 4; // 在归一化空间中插值 const u = col / subdivLevel; const v = row / subdivLevel; // 计算实际的mercator坐标 (位置) const x = mercWest + (mercEast - mercWest) * u; const y = mercNorth + (mercSouth - mercNorth) * v; // 设置顶点数据:位置(x, y) + 纹理坐标(u, v) vertices[vertexIndex] = x; // 位置 x vertices[vertexIndex + 1] = y; // 位置 y vertices[vertexIndex + 2] = u; // 纹理坐标 u vertices[vertexIndex + 3] = v; // 纹理坐标 v } } // 创建三角形索引 const trianglesPerRow = subdivLevel; const trianglesPerCol = subdivLevel; const totalTriangles = trianglesPerRow * trianglesPerCol * 2; const totalIndices = totalTriangles * 3; // 判断是否需要32位索引 const uses32bitIndices = totalVertices > 65535; const indices = uses32bitIndices ? new Uint32Array(totalIndices) : new Uint16Array(totalIndices); let indexOffset = 0; // 生成三角形索引 (每个四边形分成两个三角形) for (let row = 0; row < trianglesPerCol; row++) { for (let col = 0; col < trianglesPerRow; col++) { // 四边形的四个顶点索引 const topLeft = row * verticesPerRow + col; const topRight = topLeft + 1; const bottomLeft = (row + 1) * verticesPerRow + col; const bottomRight = bottomLeft + 1; // 第一个三角形 (左上角) indices[indexOffset++] = topLeft; indices[indexOffset++] = bottomLeft; indices[indexOffset++] = topRight; // 第二个三角形 (右下角) indices[indexOffset++] = topRight; indices[indexOffset++] = bottomLeft; indices[indexOffset++] = bottomRight; } } return { vertices, indices, uses32bitIndices, vertexCount: totalVertices, triangleCount: totalTriangles }; } /** * 根据瓦片坐标和级别创建细分网格 */ export function createSubdividedTileMeshFromTileCoords( x: number, y: number, z: number, subdivisions?: number ): TileMeshResult { // 计算瓦片的经纬度边界 const n = Math.pow(2, z); const west = (x / n) * 360 - 180; const east = ((x + 1) / n) * 360 - 180; const latRad1 = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n))); const latRad2 = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 1) / n))); const north = (latRad1 * 180) / Math.PI; const south = (latRad2 * 180) / Math.PI; return createSubdividedTileMesh({ bounds: [west, south, east, north], z, subdivisions }); } /** * 创建覆盖整个世界的细分网格 */ export function createWorldSubdividedMesh(z: number, subdivisions?: number): TileMeshResult { return createSubdividedTileMesh({ bounds: [-180, -85.0511, 180, 85.0511], // Web Mercator的纬度范围 z, subdivisions }); } /** * 使用示例函数:为特定地理区域创建高精度细分网格 */ export function createRegionSubdividedMesh( /** 中心经度 */ centerLon: number, /** 中心纬度 */ centerLat: number, /** 经度范围(度) */ lonRange: number, /** 纬度范围(度) */ latRange: number, /** 瓦片缩放级别 */ z: number, /** 自定义细分级别 */ subdivisions?: number ): TileMeshResult { const west = centerLon - lonRange / 2; const east = centerLon + lonRange / 2; const south = centerLat - latRange / 2; const north = centerLat + latRange / 2; return createSubdividedTileMesh({ bounds: [west, south, east, north], z, subdivisions }); } /** * 预设区域网格创建器 */ export const RegionMeshPresets = { /** 新加坡区域 */ singapore: (z: number, subdivisions?: number) => createSubdividedTileMesh({ bounds: [103.6, 1.2, 104.0, 1.5], z, subdivisions }), /** 中国大陆区域 */ china: (z: number, subdivisions?: number) => createSubdividedTileMesh({ bounds: [65.24686921730095, 11.90274236858339, 138.85323419021077, 55.34323805611308], z, subdivisions }), /** 美国本土区域 */ usa: (z: number, subdivisions?: number) => createSubdividedTileMesh({ bounds: [-125, 25, -66, 49], z, subdivisions }), /** 欧洲区域 */ europe: (z: number, subdivisions?: number) => createSubdividedTileMesh({ bounds: [-10, 35, 40, 70], z, subdivisions }) }; /** * 便捷函数:自动计算最佳细分并创建世界网格 */ export function createOptimalWorldMesh( zoomLevel: number, performanceLevel?: PerformanceLevel, options?: { viewportSize?: { width: number; height: number }; isGlobeMode?: boolean; minSubdivisions?: number; maxSubdivisions?: number; } ): TileMeshResult { const detectedPerformance = performanceLevel ?? detectPerformanceLevel(); const optimalSubdivisions = calculateOptimalSubdivisions( zoomLevel, detectedPerformance, options ); return createWorldSubdividedMesh(zoomLevel, optimalSubdivisions); } /** * 便捷函数:自动计算最佳细分并创建指定区域网格 */ export function createOptimalRegionMesh( bounds: [number, number, number, number], zoomLevel: number, performanceLevel?: PerformanceLevel, options?: { viewportSize?: { width: number; height: number }; isGlobeMode?: boolean; minSubdivisions?: number; maxSubdivisions?: number; } ): TileMeshResult { const detectedPerformance = performanceLevel ?? detectPerformanceLevel(); const optimalSubdivisions = calculateOptimalSubdivisions( zoomLevel, detectedPerformance, options ); return createSubdividedTileMesh({ bounds, z: zoomLevel, subdivisions: optimalSubdivisions }); } /** * 获取细分级别建议的可读描述 */ export function getSubdivisionRecommendation(zoomLevel: number, performanceLevel: PerformanceLevel): { subdivisions: number; description: string; triangleCount: number; estimatedMemoryMB: number; } { const subdivisions = calculateOptimalSubdivisions(zoomLevel, performanceLevel); const triangleCount = subdivisions * subdivisions * 2; const vertexCount = (subdivisions + 1) * (subdivisions + 1); // 估算内存使用 (顶点 + 索引) const vertexMemory = vertexCount * 4 * 4; // 4个float32 per vertex const indexMemory = triangleCount * 3 * (vertexCount > 65535 ? 4 : 2); // 3 indices per triangle const estimatedMemoryMB = (vertexMemory + indexMemory) / (1024 * 1024); let description: string; if (subdivisions <= 8) { description = "低细分 - 高性能,适合远距离视图"; } else if (subdivisions <= 16) { description = "中等细分 - 平衡性能和质量"; } else if (subdivisions <= 32) { description = "高细分 - 高质量,适合近距离视图"; } else { description = "超高细分 - 最高质量,需要高端设备"; } return { subdivisions, description, triangleCount, estimatedMemoryMB: Math.round(estimatedMemoryMB * 100) / 100 }; }