// 过滤掉无经纬度的数据 export const filterNull = ( arr = [], longitudeKey = "longitude", latitudeKey = "latitude", ) => { return arr.filter(item => item[longitudeKey] && item[latitudeKey]); }; // 格式化多边形 export const formatPolygon = (item) => { const latitudeAndLongitude = []; item.position.forEach((item) => { latitudeAndLongitude.push(item.x); latitudeAndLongitude.push(item.y); }); return latitudeAndLongitude; }; // 分片加载方法 export const chunkedLoad = async (dataArray, chunkSize = 10, processItem) => { const results = []; for (let i = 0; i < dataArray.length; i += chunkSize) { const chunk = dataArray.slice(i, i + chunkSize); const chunkResults = await Promise.all(chunk.map(processItem)); results.push(...chunkResults.filter(Boolean)); } return results; }; // 创建文字图片 const createTextCanvas = (text) => { return new Promise((resolve) => { // 如果没有文本,则返回 null if (!text) { resolve(null); return; } const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // 1. 获取设备像素比 const ratio = window.devicePixelRatio || 1; // 2. 字体配置参数 const fontSize = 14 * ratio; // 按比例放大字体 const fontFamily = "Arial, sans-serif"; // 优先使用系统字体 const paddingX = Math.round(7 * ratio); const paddingY = Math.round(5 * ratio); // 3. 设置Canvas实际尺寸 ctx.font = `${fontSize}px ${fontFamily}`; const textMetrics = ctx.measureText(text); const textWidth = textMetrics.width; const canvasWidth = Math.ceil(textWidth + paddingX * 2); const canvasHeight = Math.ceil(fontSize + paddingY * 2); // 4. 设置Canvas显示尺寸 canvas.style.width = `${canvasWidth / ratio}px`; canvas.style.height = `${canvasHeight / ratio}px`; canvas.width = canvasWidth * ratio; canvas.height = canvasHeight * ratio; // 5. 缩放坐标系 ctx.scale(ratio, ratio); ctx.font = `${fontSize}px ${fontFamily}`; // 缩放后需要重新设置字体 // 6. 优化绘制参数 ctx.fillStyle = "rgba(20, 58, 142, 1)"; ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = "#ffffff"; ctx.textBaseline = "top"; ctx.textRendering = "geometricPrecision"; // 优化文字渲染 // 7. 整数坐标绘制 const drawX = Math.round(paddingX); const drawY = Math.round(paddingY); ctx.fillText(text, drawX, drawY); resolve({ base64: canvas.toDataURL(), width: canvasWidth, height: canvasHeight, }); }); }; // 创建图片 const createImgCanvas = (imageUrl) => { return new Promise((resolve, reject) => { // 创建Image对象 const img = new Image(); img.crossOrigin = "Anonymous"; // 处理跨域问题 img.src = imageUrl; // 图片加载成功 img.onload = () => { // 创建Canvas const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // 设置Canvas宽高与图片一致 canvas.width = img.width; canvas.height = img.height; // 将图片绘制到Canvas上 ctx.drawImage(img, 0, 0); // 获取Canvas的base64数据 const base64 = canvas.toDataURL(); // 返回结果 resolve({ base64, // Canvas的base64数据 width: img.width, // 图片宽度 height: img.height, // 图片高度 }); }; // 图片加载失败 img.onerror = (error) => { reject(new Error(`Failed to load image: ${error}`)); }; }); }; const imgCanvasCache = new Map(); // 创建图片和文字的Canvas export const getBillboardCanvases = async (imageUrl, text, spacing = 5) => { // 缓存检查逻辑 let imgPromise; if (imgCanvasCache.has(imageUrl)) { imgPromise = imgCanvasCache.get(imageUrl); } else { imgPromise = createImgCanvas(imageUrl); imgCanvasCache.set(imageUrl, imgPromise); } // 并行获取内容(图片使用缓存,文字始终新建) const [imgResult, textResult] = await Promise.all([ imgPromise, createTextCanvas(text), ]); // 如果没有文字,直接返回图片结果 if (!textResult) { return { base64: imgResult.base64, width: imgResult.width, height: imgResult.height, }; } // 创建合并Canvas const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // 计算合并尺寸 const maxWidth = Math.max(imgResult.width, textResult.width); const totalHeight = textResult.height + imgResult.height + spacing; // 设置Canvas尺寸 canvas.width = maxWidth; canvas.height = totalHeight; // 创建图片对象并等待加载 const loadImage = base64 => new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = reject; img.src = base64; }); // 并行加载图片和文字图像 const [image, textImage] = await Promise.all([ loadImage(imgResult.base64), loadImage(textResult.base64), ]); // 计算水平居中偏移量 const textX = (maxWidth - textResult.width) / 2; const imageX = (maxWidth - imgResult.width) / 2; // 绘制文字到Canvas(顶部对齐) ctx.drawImage(textImage, textX, 0); // y坐标为0表示顶部 // 绘制图片到Canvas(底部对齐) ctx.drawImage( image, imageX, // x坐标 textResult.height + spacing, // y坐标设置为文字高度 imgResult.width, imgResult.height, ); return { base64: canvas.toDataURL(), width: maxWidth, height: totalHeight, }; }; // 风向 export const windDirection = (angle) => { const arr = [ { directions: "北", minAngle: "348.76", maxAngle: "11.25" }, { directions: "北东北", minAngle: "11.26", maxAngle: "33.75" }, { directions: "东北", minAngle: "33.76", maxAngle: "56.25" }, { directions: "东东北", minAngle: "56.26", maxAngle: "78.75" }, { directions: "东", minAngle: "78.76", maxAngle: "101.25" }, { directions: "东东南", minAngle: "101.26", maxAngle: "123.75" }, { directions: "东南", minAngle: "123.76", maxAngle: "146.25" }, { directions: "南东南", minAngle: "146.26", maxAngle: "168.75" }, { directions: "南", minAngle: "168.76", maxAngle: "191.25" }, { directions: "南西南", minAngle: "191.26", maxAngle: "213.75" }, { directions: "西南", minAngle: "213.76", maxAngle: "236.25" }, { directions: "西西南", minAngle: "236.26", maxAngle: "258.75" }, { directions: "西", minAngle: "258.76", maxAngle: "281.25" }, { directions: "西西北", minAngle: "281.26", maxAngle: "303.75" }, { directions: "西北", minAngle: "303.76", maxAngle: "326.25" }, { directions: "北西北", minAngle: "326.26", maxAngle: "348.75" }, ]; for (let i = 0; i < arr.length; i++) { if (+angle >= +arr[i].minAngle && +angle <= +arr[i].maxAngle) { return `${arr[i].directions}风`; } } return "静风"; }; // 处理 Echarts 图标 X轴文字换行 export function textFormatter(value, maxLength = 4) { let ret = ""; // 拼接加 \n 返回的类目项 const valLength = value.length; // X轴类目项的文字个数 const rowN = Math.ceil(valLength / maxLength); // 类目项需要换行的行数 if (rowN > 1) { for (let i = 0; i < rowN; i++) { let temp = ""; const start = i * maxLength; const end = start + maxLength; temp = `${value.substring(start, end)}\n`; ret += temp; } return ret; } else { return value; } }