@things-factory/weather 9.1.19 → 10.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,8 +12,16 @@ async function renderWeatherMap(type, data, width = 1800, height = 900) {
12
12
  console.log('브라우저 인스턴스 획득');
13
13
  const page = await browser.newPage();
14
14
  // 모든 콘솔 로그를 캡처하여 Node.js 프로세스에 출력
15
- page.on('console', msg => {
16
- console.log(`PAGE LOG [${msg.type()}]: ${msg.text()}`);
15
+ page.on('console', async (msg) => {
16
+ const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)));
17
+ if (args.some(a => a !== undefined)) {
18
+ console.log(`PAGE LOG [${msg.type()}]:`, ...args.filter(a => a !== undefined));
19
+ }
20
+ else {
21
+ const text = msg.text();
22
+ if (text)
23
+ console.log(`PAGE LOG [${msg.type()}]:`, text);
24
+ }
17
25
  });
18
26
  // 오류 이벤트도 추적 (예: Unhandled Promise Rejection)
19
27
  page.on('pageerror', err => {
@@ -1 +1 @@
1
- {"version":3,"file":"chrome-renderer.js","sourceRoot":"","sources":["../../server/services/chrome-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { getBrowserPool } from './headless-pool'\n\nexport async function renderWeatherMap(\n type: string,\n data: {\n lat: number\n lng: number\n value: number\n description: number\n temperature: number\n humidity: number\n cloudCoverage: number\n solarRadiation: number\n windSpeed: number\n windDirection: number\n precipitation?: number\n }[],\n width: number = 1800,\n height: number = 900\n): Promise<Buffer> {\n console.log(`renderWeatherMap 호출됨: 타입=${type}, 데이터 개수=${data.length}`)\n\n if (!data || data.length === 0) {\n throw new Error('데이터가 없습니다. PNG 생성을 중단합니다.')\n }\n\n const browser = await getBrowserPool().acquire()\n\n try {\n console.log('브라우저 인스턴스 획득')\n const page = await browser.newPage()\n\n // 모든 콘솔 로그를 캡처하여 Node.js 프로세스에 출력\n page.on('console', msg => {\n console.log(`PAGE LOG [${msg.type()}]: ${msg.text()}`)\n })\n\n // 오류 이벤트도 추적 (예: Unhandled Promise Rejection)\n page.on('pageerror', err => {\n console.error('BROWSER PAGE ERROR:', err)\n })\n\n await page.setViewport({ width, height })\n\n await page.setContent(`\n <!DOCTYPE html>\n <html>\n <head>\n <style>body { margin: 0; }</style>\n </head>\n <body>\n <canvas id=\"weatherCanvas\" width=\"${width}\" height=\"${height}\"></canvas>\n </body>\n </html>\n `)\n\n console.log('HTML 콘텐츠 설정 완료')\n\n await page.evaluate(\n (type, data, width, height) => {\n try {\n console.log('Canvas 렌더링 시작')\n\n const canvas = document.getElementById('weatherCanvas') as HTMLCanvasElement\n if (!canvas) {\n throw new Error('캔버스를 찾을 수 없습니다.')\n }\n\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n throw new Error('CanvasRenderingContext2D를 가져올 수 없습니다.')\n }\n\n ctx.clearRect(0, 0, width, height)\n\n function latLngToPixel(lat, lng) {\n if (isNaN(lat) || isNaN(lng)) return null\n const x = Math.floor(((lng + 180) / 360) * width)\n const y = Math.floor(((90 - lat) / 180) * height)\n if (x < 0 || x >= width || y < 0 || y >= height) return null\n return { x, y }\n }\n\n function valueToRGB(type, dataPoint) {\n if (!dataPoint || typeof dataPoint.value !== 'number') {\n console.warn('잘못된 데이터 포인트:', dataPoint)\n return { r: 0, g: 0, b: 0, a: 0 }\n }\n switch (type) {\n case 'temperature':\n return { r: Math.min(255, Math.max(0, (dataPoint.value + 50) * 2.55)), g: 0, b: 0, a: 255 }\n case 'humidity':\n return { r: 0, g: Math.min(255, Math.max(0, dataPoint.humidity * 2.55)), b: 0, a: 255 }\n case 'wind':\n return { r: Math.min(255, Math.max(0, dataPoint.windSpeed * 5)), g: 0, b: 255, a: 255 }\n case 'precipitation':\n return { r: 0, g: 0, b: Math.min(255, Math.max(0, dataPoint.precipitation * 2.55)), a: 255 }\n default:\n return { r: 255, g: 255, b: 255, a: 255 }\n }\n }\n\n function calculateColor(type, value) {\n switch (type) {\n case 'temperature':\n return { r: Math.min(255, Math.max(0, (value + 50) * 2.55)), g: 0, b: 0, a: 255 }\n case 'humidity':\n return { r: 0, g: Math.min(255, Math.max(0, value * 2.55)), b: 0, a: 255 }\n case 'wind':\n return { r: Math.min(255, Math.max(0, value * 5)), g: 0, b: 255, a: 255 }\n case 'precipitation':\n return { r: 0, g: 0, b: Math.min(255, Math.max(0, value * 2.55)), a: 255 }\n default:\n return { r: 255, g: 255, b: 255, a: 255 }\n }\n }\n\n function renderInterpolated(ctx, data, type, width, height) {\n const imageData = ctx.createImageData(width, height)\n const maxDistance = width * 0.05 // 최대 영향 거리 (이미지 너비의 5%)\n\n // 모든 픽셀에 대해 계산\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n // 픽셀의 위/경도 계산\n const lng = (x / width) * 360 - 180\n const lat = 90 - (y / height) * 180\n\n // 역거리 가중치 보간법\n let weightedSum = 0\n let totalWeight = 0\n\n data.forEach(point => {\n const pixelX = ((point.lng + 180) / 360) * width\n const pixelY = ((90 - point.lat) / 180) * height\n const distance = Math.sqrt(Math.pow(x - pixelX, 2) + Math.pow(y - pixelY, 2))\n\n if (distance <= maxDistance && typeof point.value === 'number') {\n const weight = 1 / (Math.pow(distance, 2) + 0.1)\n weightedSum += point.value * weight\n totalWeight += weight\n }\n })\n\n const index = (y * width + x) * 4\n if (totalWeight > 0) {\n const value = weightedSum / totalWeight\n const color = calculateColor(type, value)\n\n imageData.data[index] = color.r\n imageData.data[index + 1] = color.g\n imageData.data[index + 2] = color.b\n imageData.data[index + 3] = color.a\n } else {\n // 영향 범위 내 데이터 없음\n imageData.data[index + 3] = 0 // 투명하게\n }\n }\n }\n\n ctx.putImageData(imageData, 0, 0)\n }\n\n // 보간 렌더링 호출\n renderInterpolated(ctx, data, type, width, height)\n\n console.log('Canvas 렌더링 완료')\n ;(window as any).renderComplete = true\n } catch (err) {\n console.error('렌더링 오류:', err)\n ;(window as any).renderComplete = false\n }\n },\n type,\n data,\n width,\n height\n )\n\n console.log('렌더링 완료')\n\n const renderComplete = await page\n .waitForFunction('window.renderComplete === true', { timeout: 10000 })\n .catch(err => {\n console.error('렌더링 완료 신호 감지 실패:', err)\n return false\n })\n\n if (!renderComplete) {\n throw new Error('렌더링이 완료되지 않았습니다.')\n }\n\n const pngBuffer = await page.screenshot({\n type: 'png',\n omitBackground: true\n })\n\n console.log(`PNG 생성 완료: ${pngBuffer.length} 바이트`)\n return pngBuffer\n } catch (error) {\n console.error('날씨 맵 렌더링 오류:', error)\n throw error\n } finally {\n await getBrowserPool().release(browser)\n }\n}\n"]}
1
+ {"version":3,"file":"chrome-renderer.js","sourceRoot":"","sources":["../../server/services/chrome-renderer.ts"],"names":[],"mappings":";;AAEA,4CAkNC;AApND,mDAAgD;AAEzC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,IAYG,EACH,QAAgB,IAAI,EACpB,SAAiB,GAAG;IAEpB,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IAEtE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC9C,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,IAAA,8BAAc,GAAE,CAAC,OAAO,EAAE,CAAA;IAEhD,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAC3B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QAEpC,kCAAkC;QAClC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE;YAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAE7F,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAA;YAChF,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;gBACvB,IAAI,IAAI;oBAAE,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;YAC1D,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,8CAA8C;QAC9C,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE;YACzB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAA;QAC3C,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAEzC,MAAM,IAAI,CAAC,UAAU,CAAC;;;;;;;8CAOoB,KAAK,aAAa,MAAM;;;KAGjE,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;QAE7B,MAAM,IAAI,CAAC,QAAQ,CACjB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACH,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;gBAE5B,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAsB,CAAA;gBAC5E,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;gBACpC,CAAC;gBAED,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;gBACnC,IAAI,CAAC,GAAG,EAAE,CAAC;oBACT,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;gBAC1D,CAAC;gBAED,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;gBAElC,SAAS,aAAa,CAAC,GAAG,EAAE,GAAG;oBAC7B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC;wBAAE,OAAO,IAAI,CAAA;oBACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;oBACjD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,CAAA;oBACjD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM;wBAAE,OAAO,IAAI,CAAA;oBAC5D,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;gBACjB,CAAC;gBAED,SAAS,UAAU,CAAC,IAAI,EAAE,SAAS;oBACjC,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;wBACtD,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAA;wBACvC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;oBACnC,CAAC;oBACD,QAAQ,IAAI,EAAE,CAAC;wBACb,KAAK,aAAa;4BAChB,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBAC7F,KAAK,UAAU;4BACb,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBACzF,KAAK,MAAM;4BACT,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBACzF,KAAK,eAAe;4BAClB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBAC9F;4BACE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;oBAC7C,CAAC;gBACH,CAAC;gBAED,SAAS,cAAc,CAAC,IAAI,EAAE,KAAK;oBACjC,QAAQ,IAAI,EAAE,CAAC;wBACb,KAAK,aAAa;4BAChB,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBACnF,KAAK,UAAU;4BACb,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBAC5E,KAAK,MAAM;4BACT,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBAC3E,KAAK,eAAe;4BAClB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;wBAC5E;4BACE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAA;oBAC7C,CAAC;gBACH,CAAC;gBAED,SAAS,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;oBACxD,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;oBACpD,MAAM,WAAW,GAAG,KAAK,GAAG,IAAI,CAAA,CAAC,wBAAwB;oBAEzD,eAAe;oBACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;4BAC/B,cAAc;4BACd,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,GAAG,GAAG,CAAA;4BACnC,MAAM,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,GAAG,CAAA;4BAEnC,cAAc;4BACd,IAAI,WAAW,GAAG,CAAC,CAAA;4BACnB,IAAI,WAAW,GAAG,CAAC,CAAA;4BAEnB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gCACnB,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAA;gCAChD,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAA;gCAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;gCAE7E,IAAI,QAAQ,IAAI,WAAW,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oCAC/D,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;oCAChD,WAAW,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,CAAA;oCACnC,WAAW,IAAI,MAAM,CAAA;gCACvB,CAAC;4BACH,CAAC,CAAC,CAAA;4BAEF,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;4BACjC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;gCACpB,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,CAAA;gCACvC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gCAEzC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;gCAC/B,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;gCACnC,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;gCACnC,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAA;4BACrC,CAAC;iCAAM,CAAC;gCACN,iBAAiB;gCACjB,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA,CAAC,OAAO;4BACvC,CAAC;wBACH,CAAC;oBACH,CAAC;oBAED,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;gBACnC,CAAC;gBAED,YAAY;gBACZ,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;gBAElD,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAC3B;gBAAC,MAAc,CAAC,cAAc,GAAG,IAAI,CAAA;YACxC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAC5B;gBAAC,MAAc,CAAC,cAAc,GAAG,KAAK,CAAA;YACzC,CAAC;QACH,CAAC,EACD,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,MAAM,CACP,CAAA;QAED,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAErB,MAAM,cAAc,GAAG,MAAM,IAAI;aAC9B,eAAe,CAAC,gCAAgC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;aACrE,KAAK,CAAC,GAAG,CAAC,EAAE;YACX,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAA;YACtC,OAAO,KAAK,CAAA;QACd,CAAC,CAAC,CAAA;QAEJ,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACrC,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;YACtC,IAAI,EAAE,KAAK;YACX,cAAc,EAAE,IAAI;SACrB,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,CAAC,MAAM,MAAM,CAAC,CAAA;QACjD,OAAO,SAAS,CAAA;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;QACpC,MAAM,KAAK,CAAA;IACb,CAAC;YAAS,CAAC;QACT,MAAM,IAAA,8BAAc,GAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACzC,CAAC;AACH,CAAC","sourcesContent":["import { getBrowserPool } from './headless-pool'\n\nexport async function renderWeatherMap(\n type: string,\n data: {\n lat: number\n lng: number\n value: number\n description: number\n temperature: number\n humidity: number\n cloudCoverage: number\n solarRadiation: number\n windSpeed: number\n windDirection: number\n precipitation?: number\n }[],\n width: number = 1800,\n height: number = 900\n): Promise<Buffer> {\n console.log(`renderWeatherMap 호출됨: 타입=${type}, 데이터 개수=${data.length}`)\n\n if (!data || data.length === 0) {\n throw new Error('데이터가 없습니다. PNG 생성을 중단합니다.')\n }\n\n const browser = await getBrowserPool().acquire()\n\n try {\n console.log('브라우저 인스턴스 획득')\n const page = await browser.newPage()\n\n // 모든 콘솔 로그를 캡처하여 Node.js 프로세스에 출력\n page.on('console', async msg => {\n const args = await Promise.all(msg.args().map(arg => arg.jsonValue().catch(() => undefined)))\n\n if (args.some(a => a !== undefined)) {\n console.log(`PAGE LOG [${msg.type()}]:`, ...args.filter(a => a !== undefined))\n } else {\n const text = msg.text()\n if (text) console.log(`PAGE LOG [${msg.type()}]:`, text)\n }\n })\n\n // 오류 이벤트도 추적 (예: Unhandled Promise Rejection)\n page.on('pageerror', err => {\n console.error('BROWSER PAGE ERROR:', err)\n })\n\n await page.setViewport({ width, height })\n\n await page.setContent(`\n <!DOCTYPE html>\n <html>\n <head>\n <style>body { margin: 0; }</style>\n </head>\n <body>\n <canvas id=\"weatherCanvas\" width=\"${width}\" height=\"${height}\"></canvas>\n </body>\n </html>\n `)\n\n console.log('HTML 콘텐츠 설정 완료')\n\n await page.evaluate(\n (type, data, width, height) => {\n try {\n console.log('Canvas 렌더링 시작')\n\n const canvas = document.getElementById('weatherCanvas') as HTMLCanvasElement\n if (!canvas) {\n throw new Error('캔버스를 찾을 수 없습니다.')\n }\n\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n throw new Error('CanvasRenderingContext2D를 가져올 수 없습니다.')\n }\n\n ctx.clearRect(0, 0, width, height)\n\n function latLngToPixel(lat, lng) {\n if (isNaN(lat) || isNaN(lng)) return null\n const x = Math.floor(((lng + 180) / 360) * width)\n const y = Math.floor(((90 - lat) / 180) * height)\n if (x < 0 || x >= width || y < 0 || y >= height) return null\n return { x, y }\n }\n\n function valueToRGB(type, dataPoint) {\n if (!dataPoint || typeof dataPoint.value !== 'number') {\n console.warn('잘못된 데이터 포인트:', dataPoint)\n return { r: 0, g: 0, b: 0, a: 0 }\n }\n switch (type) {\n case 'temperature':\n return { r: Math.min(255, Math.max(0, (dataPoint.value + 50) * 2.55)), g: 0, b: 0, a: 255 }\n case 'humidity':\n return { r: 0, g: Math.min(255, Math.max(0, dataPoint.humidity * 2.55)), b: 0, a: 255 }\n case 'wind':\n return { r: Math.min(255, Math.max(0, dataPoint.windSpeed * 5)), g: 0, b: 255, a: 255 }\n case 'precipitation':\n return { r: 0, g: 0, b: Math.min(255, Math.max(0, dataPoint.precipitation * 2.55)), a: 255 }\n default:\n return { r: 255, g: 255, b: 255, a: 255 }\n }\n }\n\n function calculateColor(type, value) {\n switch (type) {\n case 'temperature':\n return { r: Math.min(255, Math.max(0, (value + 50) * 2.55)), g: 0, b: 0, a: 255 }\n case 'humidity':\n return { r: 0, g: Math.min(255, Math.max(0, value * 2.55)), b: 0, a: 255 }\n case 'wind':\n return { r: Math.min(255, Math.max(0, value * 5)), g: 0, b: 255, a: 255 }\n case 'precipitation':\n return { r: 0, g: 0, b: Math.min(255, Math.max(0, value * 2.55)), a: 255 }\n default:\n return { r: 255, g: 255, b: 255, a: 255 }\n }\n }\n\n function renderInterpolated(ctx, data, type, width, height) {\n const imageData = ctx.createImageData(width, height)\n const maxDistance = width * 0.05 // 최대 영향 거리 (이미지 너비의 5%)\n\n // 모든 픽셀에 대해 계산\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n // 픽셀의 위/경도 계산\n const lng = (x / width) * 360 - 180\n const lat = 90 - (y / height) * 180\n\n // 역거리 가중치 보간법\n let weightedSum = 0\n let totalWeight = 0\n\n data.forEach(point => {\n const pixelX = ((point.lng + 180) / 360) * width\n const pixelY = ((90 - point.lat) / 180) * height\n const distance = Math.sqrt(Math.pow(x - pixelX, 2) + Math.pow(y - pixelY, 2))\n\n if (distance <= maxDistance && typeof point.value === 'number') {\n const weight = 1 / (Math.pow(distance, 2) + 0.1)\n weightedSum += point.value * weight\n totalWeight += weight\n }\n })\n\n const index = (y * width + x) * 4\n if (totalWeight > 0) {\n const value = weightedSum / totalWeight\n const color = calculateColor(type, value)\n\n imageData.data[index] = color.r\n imageData.data[index + 1] = color.g\n imageData.data[index + 2] = color.b\n imageData.data[index + 3] = color.a\n } else {\n // 영향 범위 내 데이터 없음\n imageData.data[index + 3] = 0 // 투명하게\n }\n }\n }\n\n ctx.putImageData(imageData, 0, 0)\n }\n\n // 보간 렌더링 호출\n renderInterpolated(ctx, data, type, width, height)\n\n console.log('Canvas 렌더링 완료')\n ;(window as any).renderComplete = true\n } catch (err) {\n console.error('렌더링 오류:', err)\n ;(window as any).renderComplete = false\n }\n },\n type,\n data,\n width,\n height\n )\n\n console.log('렌더링 완료')\n\n const renderComplete = await page\n .waitForFunction('window.renderComplete === true', { timeout: 10000 })\n .catch(err => {\n console.error('렌더링 완료 신호 감지 실패:', err)\n return false\n })\n\n if (!renderComplete) {\n throw new Error('렌더링이 완료되지 않았습니다.')\n }\n\n const pngBuffer = await page.screenshot({\n type: 'png',\n omitBackground: true\n })\n\n console.log(`PNG 생성 완료: ${pngBuffer.length} 바이트`)\n return pngBuffer\n } catch (error) {\n console.error('날씨 맵 렌더링 오류:', error)\n throw error\n } finally {\n await getBrowserPool().release(browser)\n }\n}\n"]}