TwoDMarkersChart1.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import puppeteer from "puppeteer";
  2. import fs from "fs-extra";
  3. import path from "path";
  4. import FormData from "form-data";
  5. import { colorSchemes } from "../colors.js";
  6. import axios from "axios";
  7. export const generateTwoDMarkersChart1 = async (
  8. data,
  9. bucketName,
  10. objectName
  11. ) => {
  12. try {
  13. // 创建临时目录
  14. const tempDir = path.join(process.cwd(), "images");
  15. await fs.ensureDir(tempDir);
  16. const tempFilePath = path.join(
  17. tempDir,
  18. `temp_scatter_chart_${Date.now()}.jpeg`
  19. );
  20. // 获取 plotly.js 的绝对路径
  21. const plotlyPath = path.join(
  22. process.cwd(),
  23. "src",
  24. "public",
  25. "js",
  26. "plotly-3.0.1.min.js"
  27. );
  28. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  29. // 创建浏览器实例
  30. const browser = await puppeteer.launch({
  31. headless: "new",
  32. args: ["--no-sandbox", "--disable-setuid-sandbox"],
  33. });
  34. try {
  35. const page = await browser.newPage();
  36. // 提取散点数据和线数据
  37. const scatterData = data.data.filter(
  38. (item) => item.engineName !== "合同功率曲线"
  39. )[0]; // 点数据
  40. const lineData = data.data.filter(
  41. (item) =>
  42. item.engineName === "合同功率曲线" ||
  43. item.enginName === "合同功率曲线"
  44. )[0]; // 线数据
  45. // 提取唯一时间标签,并计算 tickvals 和 ticktext
  46. const uniqueTimeLabels = scatterData.colorbar
  47. ? [...new Set(scatterData.colorbar)]
  48. : [...new Set(scatterData.color)];
  49. const tickvals = uniqueTimeLabels.map((_, index) => index + 1);
  50. const ticktext = uniqueTimeLabels.map((dateStr) => {
  51. const date = new Date(dateStr);
  52. return date.toLocaleDateString("en-CA", {
  53. year: "numeric",
  54. month: "2-digit",
  55. });
  56. });
  57. const timeMapping = uniqueTimeLabels.reduce((acc, curr, index) => {
  58. acc[curr] = index + 1;
  59. return acc;
  60. }, {});
  61. // 计算颜色值映射
  62. let colorValues = scatterData.colorbar
  63. ? scatterData.colorbar.map((date) => timeMapping[date])
  64. : scatterData.color.map((date) => timeMapping[date]);
  65. // 绘制散点图
  66. const scatterTrace = {
  67. x: scatterData.xData,
  68. y: scatterData.yData,
  69. mode: "markers",
  70. type: "scattergl", // 使用 scattergl 提高性能
  71. text: scatterData.engineName, // 提示文本
  72. marker: {
  73. color: colorValues,
  74. colorscale: [
  75. [0, "#F9FDD2"],
  76. [0.15, "#E9F6BD"],
  77. [0.3, "#C2E3B9"],
  78. [0.45, "#8AC8BE"],
  79. [0.6, "#5CA8BF"],
  80. [0.75, "#407DB3"],
  81. [0.9, "#2E4C9A"],
  82. [1, "#1B2973"],
  83. ],
  84. size: new Array(scatterData.xData.length).fill(6), // 点的大小
  85. },
  86. hovertemplate: `${data.xaixs}: %{x} <br> ${data.yaixs}: %{y} <br> 时间: %{customdata}<extra></extra>`,
  87. customdata: scatterData.colorbar || scatterData.color, // 将格式化后的时间存入 customdata
  88. };
  89. // 绘制线图
  90. let lineTrace = {};
  91. if (lineData) {
  92. lineTrace = {
  93. x: lineData.xData,
  94. y: lineData.yData,
  95. mode: "lines+markers", // 线和点同时显示
  96. type: "scattergl", // 使用 scattergl 类型
  97. text: lineData.engineName, // 提示文本
  98. line: {
  99. color: "red", // 线条颜色
  100. },
  101. };
  102. }
  103. console.log(lineData, lineTrace, "2222");
  104. // 图表布局
  105. const layout = {
  106. title: {
  107. text: scatterData.title,
  108. font: {
  109. size: 16,
  110. weight: "bold",
  111. },
  112. },
  113. xaxis: {
  114. title: {
  115. text: data.xaixs,
  116. },
  117. gridcolor: "rgb(255,255,255)",
  118. tickcolor: "rgb(255,255,255)",
  119. backgroundcolor: "#e5ecf6",
  120. showbackground: true,
  121. },
  122. yaxis: {
  123. title: {
  124. text: data.yaixs,
  125. },
  126. gridcolor: "rgb(255,255,255)",
  127. tickcolor: "rgb(255,255,255)",
  128. backgroundcolor: "#e5ecf6",
  129. showbackground: true,
  130. },
  131. showlegend: false,
  132. plot_bgcolor: "#e5ecf6",
  133. gridcolor: "#fff",
  134. };
  135. // 准备 HTML 内容
  136. const htmlContent = `
  137. <!DOCTYPE html>
  138. <html>
  139. <head>
  140. <meta charset="UTF-8">
  141. <title>2D 散点图</title>
  142. <script>${plotlyContent}</script>
  143. </head>
  144. <body>
  145. <div id="chart" style="width: 100%; height: 600px"></div>
  146. <script>
  147. const traces = [${JSON.stringify(scatterTrace)}${
  148. lineTrace ? `, ${JSON.stringify(lineTrace)}` : ""
  149. }];
  150. const layout = ${JSON.stringify(layout)};
  151. Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
  152. window.chartRendered = true; // 确保在图表渲染完成后设置
  153. console.log("图表渲染完成");
  154. }).catch((error) => {
  155. console.error("图表渲染错误:", error); // 捕获渲染错误
  156. });
  157. </script>
  158. </body>
  159. </html>
  160. `;
  161. // 设置页面内容
  162. await page.setContent(htmlContent, {
  163. waitUntil: "networkidle0",
  164. });
  165. // 等待图表渲染完成,延长超时时间
  166. await page.waitForFunction(() => window.chartRendered === true, {
  167. timeout: 150000, // 延长到 150 秒
  168. });
  169. // 截图并保存到临时文件
  170. const chartElement = await page.$("#chart");
  171. await chartElement.screenshot({
  172. path: tempFilePath,
  173. type: "jpeg",
  174. });
  175. // 上传图片到服务器
  176. const formData = new FormData();
  177. formData.append("file", fs.createReadStream(tempFilePath));
  178. // return formData;
  179. // 发送上传请求
  180. const response = await axios.post(
  181. `${process.env.API_BASE_URL}/examples/upload`,
  182. { filePath: tempFilePath, bucketName, objectName }
  183. );
  184. return response?.data?.url;
  185. } catch (error) {
  186. console.error("生成2D散点图失败:", error);
  187. throw error;
  188. } finally {
  189. await browser.close();
  190. }
  191. } catch (error) {
  192. console.error("生成2D散点图失败:", error);
  193. throw error;
  194. }
  195. };