| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- const puppeteer = require("puppeteer-core");
- const CHROME_DEFAULT =
- "C:/Program Files/Google/Chrome/Application/chrome.exe";
- /**
- * 无头渲染:打开门户三维路由,等待 viewer,点"日照分析"挂载组件,
- * 自己加载实景三维场景(倾斜摄影) + DSM 高程,再调 __sunlightRun({geojson,date})
- * (内部自动 设置范围(采样高程) → 采光率分析 → 导出),返回结果图。
- */
- async function renderScene(opts) {
- const {
- url,
- route = "/checkmodel",
- token = "",
- range = null,
- date = "",
- terrainUrl = "",
- sceneUrl = "",
- iserverHost = "192.168.60.2:8090",
- chromePath = CHROME_DEFAULT,
- } = opts;
- function evalTimeout(page, fn, ...args) {
- return Promise.race([
- page.evaluate(fn, ...args),
- new Promise((_, rej) =>
- setTimeout(() => rej(new Error("evaluate 超时")), 300000)
- ),
- ]);
- }
- const browser = await puppeteer.launch({
- executablePath: chromePath,
- headless: "new",
- args: [
- "--no-sandbox",
- "--disable-setuid-sandbox",
- "--enable-gpu",
- "--ignore-gpu-blocklist",
- "--window-size=1600,900",
- ],
- });
- try {
- const page = await browser.newPage();
- await page.setViewport({ width: 1600, height: 900 });
- // 转发无头页面日志
- page.on("console", (m) => console.log("[页]", m.type(), m.text()));
- page.on("pageerror", (e) => console.log("[页ERROR]", String(e && e.message || e)));
- page.on("dialog", (d) => d.accept());
- if (token) {
- await page.setCookie({
- name: "Admin-Token",
- value: token,
- domain: new URL(url).hostname,
- path: "/",
- });
- }
- await page.goto(url + route, {
- waitUntil: "domcontentloaded",
- timeout: 120000,
- });
- await page.waitForFunction(
- "window.viewer && window.viewer.scene && window.viewer.scene.canvas",
- { timeout: 120000 }
- );
- try {
- await page.waitForFunction(
- "window.viewer.scene.canvas.width > 0 && window.viewer.scene.canvas.height > 0",
- { timeout: 60000 }
- );
- } catch (e) {
- /* 画布 0 尺寸则走兜底截图 */
- }
- await new Promise((r) => setTimeout(r, 8000));
- // 打开日照分析(挂载 SunlightAnalysis,注入 __sunlightRun)
- let hasHook = false;
- try {
- await page.evaluate(() => {
- const btn = Array.from(document.querySelectorAll("button")).find(
- (b) => (b.innerText || "").trim() === "日照分析"
- );
- if (btn) btn.click();
- });
- await page.waitForFunction(
- "typeof window.__sunlightRun === 'function'",
- { timeout: 30000 }
- );
- hasHook = true;
- } catch (e) {
- console.warn("open 日照分析 error", e.message);
- }
- // 自己加载实景三维场景(倾斜摄影)
- if (sceneUrl) {
- const resolved = sceneUrl.replace("{s}", iserverHost);
- console.log("[renderer] 加载实景三维场景:", resolved);
- try {
- await evalTimeout(page, async (u) => {
- if (typeof window.viewer === "undefined") return;
- try {
- await window.viewer.scene.open(u);
- console.log("实景场景 open 完成");
- } catch (e) {
- console.warn("scene.open error", e);
- }
- }, resolved);
- } catch (e) {
- console.warn("[renderer] 打开场景异常", e.message);
- }
- try {
- await page.waitForFunction(
- "window.viewer.scene.layers && window.viewer.scene.layers.layerQueue.length > 0",
- { timeout: 90000 }
- );
- console.log("[renderer] 实景图层已就绪, layerQueue=",
- await page.evaluate(() => window.viewer.scene.layers.layerQueue.length)
- );
- } catch (e) {
- console.warn("[renderer] 等待实景图层超时", e.message);
- }
- await new Promise((r) => setTimeout(r, 5000));
- }
- // 自己加载 DSM 高程
- if (terrainUrl) {
- console.log("[renderer] 加载 DSM 高程:", terrainUrl.slice(0, 90));
- try {
- await evalTimeout(page, async (u) => {
- if (typeof Cesium === "undefined" || !window.viewer) return;
- window.viewer.scene.terrainProvider =
- new Cesium.EllipsoidTerrainProvider({});
- const tp = new Cesium.CesiumTerrainProvider({
- url: u,
- isSct: true,
- requestVertexNormals: true,
- });
- try {
- tp.visible = true;
- } catch (e) {}
- window.viewer.terrainProvider = tp;
- try {
- await tp.readyPromise;
- } catch (e) {
- console.warn("terrain ready error", e);
- }
- }, terrainUrl);
- console.log("[renderer] 地形已加载");
- } catch (e) {
- console.warn("[renderer] 地形加载失败", e.message);
- }
- await new Promise((r) => setTimeout(r, 3000));
- }
- if (range && hasHook) {
- // 调 __sunlightRun({geojson,date}):内部自动 设置范围(采样高程)→采光率分析→导出→返回图片
- const dataUrl = await evalTimeout(
- page,
- (args) =>
- window.__sunlightRun({
- geojson: args.range,
- date: args.date || undefined,
- }),
- { range, date: date || "" }
- );
- return {
- ok: true,
- jpegBase64: dataUrl.split(",")[1],
- note: "sunlight-export",
- };
- }
- // 兜底:截取当前三维场景
- const shot = await page.screenshot({
- encoding: "base64",
- type: "jpeg",
- quality: 90,
- });
- return {
- ok: true,
- jpegBase64: shot,
- note: hasHook ? "scene-capture(hook-ok-no-range)" : "scene-capture(no-hook)",
- };
- } catch (e) {
- return { ok: false, error: String((e && e.message) || e) };
- } finally {
- await browser.close().catch(() => {});
- }
- }
- module.exports = { renderScene };
|