@things-factory/weather 9.0.0-beta.41

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/assets/images/wind.png +0 -0
  3. package/client/bootstrap.ts +1 -0
  4. package/client/index.ts +0 -0
  5. package/client/ol-components/webgl-canvas-layer.ts +413 -0
  6. package/client/ol-components/webgl-image-static.ts +174 -0
  7. package/client/ol-components/wind.ts +120 -0
  8. package/client/pages/weather-page.ts +318 -0
  9. package/client/route.ts +7 -0
  10. package/client/tsconfig.json +11 -0
  11. package/config/config.development.js +25 -0
  12. package/config/config.production.js +7 -0
  13. package/data/geo-json/countries-max-geo.json +1 -0
  14. package/dist-client/bootstrap.d.ts +1 -0
  15. package/dist-client/bootstrap.js +2 -0
  16. package/dist-client/bootstrap.js.map +1 -0
  17. package/dist-client/index.d.ts +0 -0
  18. package/dist-client/index.js +2 -0
  19. package/dist-client/index.js.map +1 -0
  20. package/dist-client/ol-components/webgl-canvas-layer.d.ts +61 -0
  21. package/dist-client/ol-components/webgl-canvas-layer.js +364 -0
  22. package/dist-client/ol-components/webgl-canvas-layer.js.map +1 -0
  23. package/dist-client/ol-components/webgl-image-static.d.ts +41 -0
  24. package/dist-client/ol-components/webgl-image-static.js +135 -0
  25. package/dist-client/ol-components/webgl-image-static.js.map +1 -0
  26. package/dist-client/ol-components/wind.d.ts +3 -0
  27. package/dist-client/ol-components/wind.js +104 -0
  28. package/dist-client/ol-components/wind.js.map +1 -0
  29. package/dist-client/pages/weather-page.d.ts +18 -0
  30. package/dist-client/pages/weather-page.js +300 -0
  31. package/dist-client/pages/weather-page.js.map +1 -0
  32. package/dist-client/route.d.ts +1 -0
  33. package/dist-client/route.js +8 -0
  34. package/dist-client/route.js.map +1 -0
  35. package/dist-client/tsconfig.tsbuildinfo +1 -0
  36. package/dist-server/controllers/open-meteo-adapter.d.ts +4 -0
  37. package/dist-server/controllers/open-meteo-adapter.js +83 -0
  38. package/dist-server/controllers/open-meteo-adapter.js.map +1 -0
  39. package/dist-server/controllers/types.d.ts +55 -0
  40. package/dist-server/controllers/types.js +3 -0
  41. package/dist-server/controllers/types.js.map +1 -0
  42. package/dist-server/controllers/weather-controller.d.ts +12 -0
  43. package/dist-server/controllers/weather-controller.js +164 -0
  44. package/dist-server/controllers/weather-controller.js.map +1 -0
  45. package/dist-server/index.d.ts +1 -0
  46. package/dist-server/index.js +4 -0
  47. package/dist-server/index.js.map +1 -0
  48. package/dist-server/routers/geo-router.d.ts +59 -0
  49. package/dist-server/routers/geo-router.js +277 -0
  50. package/dist-server/routers/geo-router.js.map +1 -0
  51. package/dist-server/routers/weather-router.d.ts +1 -0
  52. package/dist-server/routers/weather-router.js +10 -0
  53. package/dist-server/routers/weather-router.js.map +1 -0
  54. package/dist-server/routes.d.ts +1 -0
  55. package/dist-server/routes.js +11 -0
  56. package/dist-server/routes.js.map +1 -0
  57. package/dist-server/services/chrome-renderer.d.ts +13 -0
  58. package/dist-server/services/chrome-renderer.js +137 -0
  59. package/dist-server/services/chrome-renderer.js.map +1 -0
  60. package/dist-server/services/headless-pool.d.ts +4 -0
  61. package/dist-server/services/headless-pool.js +89 -0
  62. package/dist-server/services/headless-pool.js.map +1 -0
  63. package/dist-server/tsconfig.tsbuildinfo +1 -0
  64. package/dist-server/utils/svg-utils.d.ts +35 -0
  65. package/dist-server/utils/svg-utils.js +89 -0
  66. package/dist-server/utils/svg-utils.js.map +1 -0
  67. package/dist-server/utils/tile-utils.d.ts +38 -0
  68. package/dist-server/utils/tile-utils.js +64 -0
  69. package/dist-server/utils/tile-utils.js.map +1 -0
  70. package/package.json +45 -0
  71. package/server/controllers/open-meteo-adapter.ts +91 -0
  72. package/server/controllers/types.ts +63 -0
  73. package/server/controllers/weather-controller.ts +176 -0
  74. package/server/index.ts +1 -0
  75. package/server/routers/geo-router.ts +297 -0
  76. package/server/routers/weather-router.ts +8 -0
  77. package/server/routes.ts +10 -0
  78. package/server/services/chrome-renderer.ts +175 -0
  79. package/server/services/data.json +8438 -0
  80. package/server/services/headless-pool.ts +97 -0
  81. package/server/tsconfig.json +10 -0
  82. package/server/utils/svg-utils.ts +108 -0
  83. package/server/utils/tile-utils.ts +77 -0
  84. package/things-factory.config.js +8 -0
  85. package/translations/en.json +1 -0
  86. package/translations/ja.json +1 -0
  87. package/translations/ko.json +1 -0
  88. package/translations/ms.json +1 -0
  89. package/translations/zh.json +1 -0
@@ -0,0 +1,97 @@
1
+ import * as genericPool from 'generic-pool'
2
+ import { config } from '@things-factory/env'
3
+
4
+ // puppeteer 모듈 동적 로드 (에러 처리 포함)
5
+ let puppeteer
6
+ try {
7
+ puppeteer = require('puppeteer')
8
+ } catch (err) {
9
+ console.error('Puppeteer 로드 실패:', err)
10
+ }
11
+
12
+ // 브라우저 풀 인스턴스
13
+ let browserPool
14
+
15
+ /**
16
+ * 헤드리스 브라우저 풀 가져오기 (싱글톤 패턴)
17
+ */
18
+ export function getBrowserPool() {
19
+ if (!browserPool) {
20
+ browserPool = genericPool.createPool(
21
+ {
22
+ // 브라우저 인스턴스 생성
23
+ create() {
24
+ console.log('날씨 서비스용 headless 브라우저 인스턴스 생성')
25
+ return initializeChromium()
26
+ },
27
+
28
+ // 브라우저 인스턴스 유효성 검사
29
+ validate(browser) {
30
+ return Promise.race([
31
+ new Promise(res => setTimeout(() => res(false), 1500)),
32
+ browser
33
+ .version()
34
+ .then(_ => true)
35
+ .catch(_ => false)
36
+ ])
37
+ },
38
+
39
+ // 브라우저 인스턴스 파괴
40
+ destroy(browser) {
41
+ return browser.close()
42
+ }
43
+ },
44
+ {
45
+ // 풀 설정
46
+ min: 1, // 최소 인스턴스 수
47
+ max: 5, // 최대 인스턴스 수
48
+ testOnBorrow: true,
49
+ acquireTimeoutMillis: 10000,
50
+ idleTimeoutMillis: 30000 // 30초 후 유휴 인스턴스 제거
51
+ }
52
+ )
53
+
54
+ // 애플리케이션 종료 시 정리
55
+ process.on('exit', async () => {
56
+ if (browserPool) {
57
+ await browserPool.drain()
58
+ await browserPool.clear()
59
+ }
60
+ })
61
+ }
62
+
63
+ return browserPool
64
+ }
65
+
66
+ /**
67
+ * Chromium 초기화
68
+ */
69
+ async function initializeChromium() {
70
+ // puppeteer가 없으면 오류
71
+ if (!puppeteer) {
72
+ throw new Error('Puppeteer is not available')
73
+ }
74
+
75
+ // 크로미움 경로 (환경설정에서 가져옴)
76
+ const CHROMIUM_PATH = config.get('CHROMIUM_PATH')
77
+
78
+ // 실행 설정
79
+ const launchSettings = {
80
+ args: ['--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
81
+ headless: 'new' // 'new' = 최신 헤드리스 모드
82
+ }
83
+
84
+ // 크로미움 경로가 설정되어 있으면 사용
85
+ if (CHROMIUM_PATH) {
86
+ launchSettings['executablePath'] = CHROMIUM_PATH
87
+ }
88
+
89
+ try {
90
+ // 브라우저 실행
91
+ const browser = await puppeteer.launch(launchSettings)
92
+ return browser
93
+ } catch (err) {
94
+ console.error('크로미움 초기화 실패:', err)
95
+ throw err
96
+ }
97
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig-base.json",
3
+ "compilerOptions": {
4
+ "strict": false,
5
+ "module": "commonjs",
6
+ "outDir": "../dist-server",
7
+ "baseUrl": "./"
8
+ },
9
+ "include": ["./**/*"]
10
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * SVG 생성 유틸리티 함수
3
+ */
4
+
5
+ /**
6
+ * SVG 시작 태그 생성
7
+ */
8
+ export function createSvgStart(width: number = 256, height: number = 256): string {
9
+ return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`
10
+ }
11
+
12
+ /**
13
+ * SVG 종료 태그 생성
14
+ */
15
+ export function createSvgEnd(): string {
16
+ return '</svg>'
17
+ }
18
+
19
+ /**
20
+ * SVG 그라데이션 정의 생성
21
+ */
22
+ export function createSvgDefs(gradients: Array<{ id: string; color: string; opacity?: number }>): string {
23
+ let defs = '<defs>'
24
+
25
+ gradients.forEach(gradient => {
26
+ const opacity = gradient.opacity !== undefined ? gradient.opacity : 0.8
27
+ defs += `
28
+ <radialGradient id="${gradient.id}" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
29
+ <stop offset="0%" style="stop-color:${gradient.color}; stop-opacity:${opacity}" />
30
+ <stop offset="100%" style="stop-color:${gradient.color}; stop-opacity:0" />
31
+ </radialGradient>`
32
+ })
33
+
34
+ defs += '</defs>'
35
+ return defs
36
+ }
37
+
38
+ /**
39
+ * SVG 원 요소 생성
40
+ */
41
+ export function createSvgCircle(cx: number, cy: number, radius: number, fill: string, opacity?: number): string {
42
+ const opacityAttr = opacity !== undefined ? ` opacity="${opacity}"` : ''
43
+ return `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="${fill}"${opacityAttr} />`
44
+ }
45
+
46
+ /**
47
+ * SVG 라인 요소 생성
48
+ */
49
+ export function createSvgLine(
50
+ x1: number,
51
+ y1: number,
52
+ x2: number,
53
+ y2: number,
54
+ stroke: string,
55
+ strokeWidth: number = 1,
56
+ opacity?: number
57
+ ): string {
58
+ const opacityAttr = opacity !== undefined ? ` opacity="${opacity}"` : ''
59
+ return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${stroke}" stroke-width="${strokeWidth}"${opacityAttr} />`
60
+ }
61
+
62
+ /**
63
+ * SVG 화살표 생성 (바람 방향 표시용)
64
+ */
65
+ export function createSvgArrow(
66
+ x: number,
67
+ y: number,
68
+ angle: number,
69
+ length: number,
70
+ color: string,
71
+ strokeWidth: number = 1,
72
+ opacity?: number
73
+ ): string {
74
+ const opacityAttr = opacity !== undefined ? ` opacity="${opacity}"` : ''
75
+
76
+ // 라디안으로 변환
77
+ const rad = (angle * Math.PI) / 180
78
+
79
+ // 화살표 끝점 계산
80
+ const endX = x + length * Math.cos(rad)
81
+ const endY = y + length * Math.sin(rad)
82
+
83
+ // 화살표 머리 계산 (각도에서 30도 틀어진 두 지점)
84
+ const headSize = length * 0.25
85
+ const head1X = endX - headSize * Math.cos(rad + Math.PI / 6)
86
+ const head1Y = endY - headSize * Math.sin(rad + Math.PI / 6)
87
+ const head2X = endX - headSize * Math.cos(rad - Math.PI / 6)
88
+ const head2Y = endY - headSize * Math.sin(rad - Math.PI / 6)
89
+
90
+ return `
91
+ <g${opacityAttr}>
92
+ <line x1="${x}" y1="${y}" x2="${endX}" y2="${endY}" stroke="${color}" stroke-width="${strokeWidth}" />
93
+ <line x1="${endX}" y1="${endY}" x2="${head1X}" y2="${head1Y}" stroke="${color}" stroke-width="${strokeWidth}" />
94
+ <line x1="${endX}" y1="${endY}" x2="${head2X}" y2="${head2Y}" stroke="${color}" stroke-width="${strokeWidth}" />
95
+ </g>
96
+ `
97
+ }
98
+
99
+ /**
100
+ * 날씨 타일용 SVG 생성
101
+ */
102
+ export function createWeatherTileSvg(width: number, height: number, elements: string[]): string {
103
+ return `
104
+ ${createSvgStart(width, height)}
105
+ ${elements.join('\n')}
106
+ ${createSvgEnd()}
107
+ `
108
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 타일 좌표 관련 유틸리티 함수
3
+ */
4
+
5
+ export interface TileBounds {
6
+ west: number
7
+ south: number
8
+ east: number
9
+ north: number
10
+ }
11
+
12
+ /**
13
+ * 타일 좌표를 위경도 범위로 변환
14
+ */
15
+ export function getTileBounds(z: number, x: number, y: number): TileBounds {
16
+ const n = Math.pow(2, z)
17
+ const west = (x / n) * 360 - 180
18
+ const east = ((x + 1) / n) * 360 - 180
19
+ const north = Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / n))) * (180 / Math.PI)
20
+ const south = Math.atan(Math.sinh(Math.PI * (1 - (2 * (y + 1)) / n))) * (180 / Math.PI)
21
+
22
+ return { west, south, east, north }
23
+ }
24
+
25
+ /**
26
+ * 위경도를 타일 좌표로 변환
27
+ */
28
+ export function latLonToTile(lat: number, lon: number, zoom: number): { x: number; y: number } {
29
+ const latRad = (lat * Math.PI) / 180
30
+ const n = Math.pow(2, zoom)
31
+ const x = Math.floor(((lon + 180) / 360) * n)
32
+ const y = Math.floor(((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * n)
33
+
34
+ return { x, y }
35
+ }
36
+
37
+ /**
38
+ * 픽셀 좌표를 위경도로 변환
39
+ */
40
+ export function pixelToLatLon(x: number, y: number, z: number): { lat: number; lon: number } {
41
+ const size = Math.pow(2, z) * 256
42
+ const lon = (x / size) * 360 - 180
43
+
44
+ const n = Math.PI - (2 * Math.PI * y) / size
45
+ const lat = (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
46
+
47
+ return { lat, lon }
48
+ }
49
+
50
+ /**
51
+ * 위경도를 픽셀 좌표로 변환
52
+ */
53
+ export function latLonToPixel(lat: number, lon: number, z: number): { x: number; y: number } {
54
+ const size = Math.pow(2, z) * 256
55
+ const x = ((lon + 180) / 360) * size
56
+
57
+ const latRad = (lat * Math.PI) / 180
58
+ const y = ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * size
59
+
60
+ return { x, y }
61
+ }
62
+
63
+ /**
64
+ * 두 위경도 사이의 거리 계산 (Haversine 공식)
65
+ */
66
+ export function getDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
67
+ const R = 6371 // 지구 반경 (km)
68
+ const dLat = (lat2 - lat1) * (Math.PI / 180)
69
+ const dLon = (lon2 - lon1) * (Math.PI / 180)
70
+
71
+ const a =
72
+ Math.sin(dLat / 2) * Math.sin(dLat / 2) +
73
+ Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2)
74
+
75
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
76
+ return R * c
77
+ }
@@ -0,0 +1,8 @@
1
+ import route from './dist-client/route'
2
+ import bootstrap from './dist-client/bootstrap'
3
+
4
+ export default {
5
+ route,
6
+ routes: [{ tagname: 'weather-page', page: 'weather-page' }],
7
+ bootstrap
8
+ }
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ {}