@things-factory/weather 9.0.0-9.0.0-beta.59.0
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.
- package/CHANGELOG.md +8 -0
- package/assets/images/wind.png +0 -0
- package/assets/images/wind2.png +0 -0
- package/client/bootstrap.ts +1 -0
- package/client/index.ts +0 -0
- package/client/ol-components/webgl-canvas-layer.ts +413 -0
- package/client/ol-components/wind-data-tile-layer.ts +485 -0
- package/client/ol-components/wind-rasterized-layer.ts +437 -0
- package/client/pages/weather-page.ts +353 -0
- package/client/route.ts +7 -0
- package/client/tsconfig.json +11 -0
- package/config/config.development.js +28 -0
- package/config/config.production.js +10 -0
- package/data/geo-json/countries-max-geo.json +1 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +2 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +0 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/ol-components/webgl-canvas-layer.d.ts +61 -0
- package/dist-client/ol-components/webgl-canvas-layer.js +364 -0
- package/dist-client/ol-components/webgl-canvas-layer.js.map +1 -0
- package/dist-client/ol-components/wind-data-tile-layer.d.ts +59 -0
- package/dist-client/ol-components/wind-data-tile-layer.js +414 -0
- package/dist-client/ol-components/wind-data-tile-layer.js.map +1 -0
- package/dist-client/ol-components/wind-rasterized-layer.d.ts +59 -0
- package/dist-client/ol-components/wind-rasterized-layer.js +363 -0
- package/dist-client/ol-components/wind-rasterized-layer.js.map +1 -0
- package/dist-client/pages/weather-page.d.ts +23 -0
- package/dist-client/pages/weather-page.js +348 -0
- package/dist-client/pages/weather-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +8 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controllers/open-meteo-adapter.d.ts +4 -0
- package/dist-server/controllers/open-meteo-adapter.js +87 -0
- package/dist-server/controllers/open-meteo-adapter.js.map +1 -0
- package/dist-server/controllers/types.d.ts +55 -0
- package/dist-server/controllers/types.js +3 -0
- package/dist-server/controllers/types.js.map +1 -0
- package/dist-server/controllers/weather-controller.d.ts +12 -0
- package/dist-server/controllers/weather-controller.js +179 -0
- package/dist-server/controllers/weather-controller.js.map +1 -0
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +4 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/routers/geo-router.d.ts +59 -0
- package/dist-server/routers/geo-router.js +277 -0
- package/dist-server/routers/geo-router.js.map +1 -0
- package/dist-server/routers/weather-router.d.ts +1 -0
- package/dist-server/routers/weather-router.js +10 -0
- package/dist-server/routers/weather-router.js.map +1 -0
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +11 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/services/chrome-renderer.d.ts +13 -0
- package/dist-server/services/chrome-renderer.js +162 -0
- package/dist-server/services/chrome-renderer.js.map +1 -0
- package/dist-server/services/headless-pool.d.ts +4 -0
- package/dist-server/services/headless-pool.js +89 -0
- package/dist-server/services/headless-pool.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/dist-server/utils/svg-utils.d.ts +35 -0
- package/dist-server/utils/svg-utils.js +89 -0
- package/dist-server/utils/svg-utils.js.map +1 -0
- package/dist-server/utils/tile-utils.d.ts +38 -0
- package/dist-server/utils/tile-utils.js +64 -0
- package/dist-server/utils/tile-utils.js.map +1 -0
- package/package.json +46 -0
- package/server/controllers/open-meteo-adapter.ts +96 -0
- package/server/controllers/types.ts +63 -0
- package/server/controllers/weather-controller.ts +192 -0
- package/server/index.ts +1 -0
- package/server/routers/geo-router.ts +297 -0
- package/server/routers/weather-router.ts +8 -0
- package/server/routes.ts +10 -0
- package/server/services/chrome-renderer.ts +206 -0
- package/server/services/data.json +8438 -0
- package/server/services/headless-pool.ts +97 -0
- package/server/tsconfig.json +10 -0
- package/server/utils/svg-utils.ts +108 -0
- package/server/utils/tile-utils.ts +77 -0
- package/things-factory.config.js +8 -0
- package/translations/en.json +1 -0
- package/translations/ja.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { get as getProjection, transform } from 'ol/proj.js'
|
|
2
|
+
import { createXYZ } from 'ol/tilegrid.js'
|
|
3
|
+
import { WeatherLayerType } from '@hyperviz/weather'
|
|
4
|
+
import BaseLayer from 'ol/layer/Base.js'
|
|
5
|
+
import ImageLayer from 'ol/layer/Image.js'
|
|
6
|
+
import Static from 'ol/source/ImageStatic.js'
|
|
7
|
+
import Map from 'ol/Map.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 바람 데이터를 캔버스에 래스터화하여 표시하는 레이어
|
|
11
|
+
* 전체 데이터를 한 번에 처리하여 이미지로 변환해 표시하는 방식
|
|
12
|
+
*
|
|
13
|
+
* @class WindRasterizedLayer
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// --- 데이터 로딩 및 변환 로직 ---
|
|
17
|
+
|
|
18
|
+
/** 바람 데이터 이미지 로딩 Promise */
|
|
19
|
+
const windData: Promise<{ data: ArrayBuffer; width: number; height: number }> = new Promise((resolve, reject) => {
|
|
20
|
+
const image = new Image()
|
|
21
|
+
image.onload = () => {
|
|
22
|
+
const canvas = document.createElement('canvas')
|
|
23
|
+
const width = image.width
|
|
24
|
+
const height = image.height
|
|
25
|
+
canvas.width = width
|
|
26
|
+
canvas.height = height
|
|
27
|
+
|
|
28
|
+
const context = canvas.getContext('2d')!
|
|
29
|
+
context.drawImage(image, 0, 0)
|
|
30
|
+
const data = context.getImageData(0, 0, width, height).data
|
|
31
|
+
resolve({ data, width, height })
|
|
32
|
+
}
|
|
33
|
+
image.onerror = () => {
|
|
34
|
+
reject(new Error('바람 데이터 이미지 로드 실패'))
|
|
35
|
+
}
|
|
36
|
+
image.src = '/assets/images/wind.png'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 바이리니어 보간법을 사용하여 픽셀 값 계산
|
|
41
|
+
*/
|
|
42
|
+
function bilinearInterpolation(
|
|
43
|
+
xAlong: number,
|
|
44
|
+
yAlong: number,
|
|
45
|
+
v11: number,
|
|
46
|
+
v21: number,
|
|
47
|
+
v12: number,
|
|
48
|
+
v22: number
|
|
49
|
+
): number {
|
|
50
|
+
const q11 = (1 - xAlong) * (1 - yAlong) * v11
|
|
51
|
+
const q21 = xAlong * (1 - yAlong) * v21
|
|
52
|
+
const q12 = (1 - xAlong) * yAlong * v12
|
|
53
|
+
const q22 = xAlong * yAlong * v22
|
|
54
|
+
return q11 + q21 + q12 + q22
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 픽셀 배열에 대한 보간법 적용
|
|
59
|
+
*/
|
|
60
|
+
function interpolatePixels(
|
|
61
|
+
xAlong: number,
|
|
62
|
+
yAlong: number,
|
|
63
|
+
p11: number[],
|
|
64
|
+
p21: number[],
|
|
65
|
+
p12: number[],
|
|
66
|
+
p22: number[]
|
|
67
|
+
): number[] {
|
|
68
|
+
return p11.map((_, i) => bilinearInterpolation(xAlong, yAlong, p11[i], p21[i], p12[i], p22[i]))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 바람 데이터 관련 상수
|
|
72
|
+
const inputImageProjection = getProjection('EPSG:4326')!
|
|
73
|
+
const inputBands = 4 // RGBA
|
|
74
|
+
|
|
75
|
+
// 바람 속도 범위 (원본 이미지 값 기준)
|
|
76
|
+
const minU = -21.32
|
|
77
|
+
const maxU = 26.8
|
|
78
|
+
const deltaU = maxU - minU
|
|
79
|
+
const minV = -21.57
|
|
80
|
+
const maxV = 21.42
|
|
81
|
+
const deltaV = maxV - minV
|
|
82
|
+
|
|
83
|
+
// 바람 속도에 따른 색상 매핑 (속도, [R, G, B, A])
|
|
84
|
+
const windColorMap: Array<[number, [number, number, number, number]]> = [
|
|
85
|
+
[0, [0, 50, 170, 100]], // 약한 바람 (파란색)
|
|
86
|
+
[5, [0, 100, 220, 150]], // 중약한 바람
|
|
87
|
+
[10, [0, 150, 200, 180]], // 중간 바람 (청록색)
|
|
88
|
+
[15, [0, 200, 150, 200]], // 중강한 바람 (녹색)
|
|
89
|
+
[20, [150, 250, 0, 220]], // 강한 바람 (노란색)
|
|
90
|
+
[25, [200, 150, 0, 230]], // 매우 강한 바람 (주황색)
|
|
91
|
+
[30, [250, 50, 0, 250]] // 극심한 바람 (빨간색)
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 바람 속도에 따른 색상 계산
|
|
96
|
+
* @param speed 바람 속도 (m/s)
|
|
97
|
+
* @returns RGBA 색상 배열 [r, g, b, a] (0-255)
|
|
98
|
+
*/
|
|
99
|
+
function getWindColor(speed: number): [number, number, number, number] {
|
|
100
|
+
// 기본 색상 (약한 바람)
|
|
101
|
+
let color: [number, number, number, number] = [0, 50, 170, 100]
|
|
102
|
+
|
|
103
|
+
// 색상 맵에서 가장 가까운 색상 찾기
|
|
104
|
+
for (let i = 0; i < windColorMap.length - 1; i++) {
|
|
105
|
+
const [speed1, color1] = windColorMap[i]
|
|
106
|
+
const [speed2, color2] = windColorMap[i + 1]
|
|
107
|
+
|
|
108
|
+
if (speed >= speed1 && speed <= speed2) {
|
|
109
|
+
// 두 색상 사이 보간
|
|
110
|
+
const t = (speed - speed1) / (speed2 - speed1)
|
|
111
|
+
color = [
|
|
112
|
+
Math.floor(color1[0] * (1 - t) + color2[0] * t),
|
|
113
|
+
Math.floor(color1[1] * (1 - t) + color2[1] * t),
|
|
114
|
+
Math.floor(color1[2] * (1 - t) + color2[2] * t),
|
|
115
|
+
Math.floor(color1[3] * (1 - t) + color2[3] * t)
|
|
116
|
+
]
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 최대 색상 (매우 강한 바람)
|
|
122
|
+
if (speed > windColorMap[windColorMap.length - 1][0]) {
|
|
123
|
+
color = [...windColorMap[windColorMap.length - 1][1]]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return color
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// --- 레이어 클래스 구현 ---
|
|
130
|
+
|
|
131
|
+
export class WindRasterizedLayer {
|
|
132
|
+
private layer: ImageLayer<Static> | null = null
|
|
133
|
+
private dataPromise: Promise<{ data: ArrayBuffer; width: number; height: number }> | null = null
|
|
134
|
+
private canvas: HTMLCanvasElement | null = null
|
|
135
|
+
private ctx: CanvasRenderingContext2D | null = null
|
|
136
|
+
private width = 800
|
|
137
|
+
private height = 400
|
|
138
|
+
private map: Map | null = null
|
|
139
|
+
private useEnhancedColors: boolean = true
|
|
140
|
+
private isDisposed: boolean = false
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 바람 래스터화 레이어 생성자
|
|
144
|
+
* @param options 옵션 객체
|
|
145
|
+
*/
|
|
146
|
+
constructor(
|
|
147
|
+
options: {
|
|
148
|
+
width?: number
|
|
149
|
+
height?: number
|
|
150
|
+
useEnhancedColors?: boolean
|
|
151
|
+
} = {}
|
|
152
|
+
) {
|
|
153
|
+
this.dataPromise = windData
|
|
154
|
+
|
|
155
|
+
// 캔버스 크기 설정
|
|
156
|
+
if (options.width) this.width = options.width
|
|
157
|
+
if (options.height) this.height = options.height
|
|
158
|
+
|
|
159
|
+
// 색상 매핑 옵션
|
|
160
|
+
if (options.useEnhancedColors !== undefined) {
|
|
161
|
+
this.useEnhancedColors = options.useEnhancedColors
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 사전에 캔버스 준비
|
|
165
|
+
this.canvas = document.createElement('canvas')
|
|
166
|
+
this.canvas.width = this.width
|
|
167
|
+
this.canvas.height = this.height
|
|
168
|
+
this.ctx = this.canvas.getContext('2d')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 레이어 타입 반환 (호환성용)
|
|
173
|
+
*/
|
|
174
|
+
getType(): WeatherLayerType {
|
|
175
|
+
return 'wind'
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 향상된 색상 매핑 사용 여부 설정
|
|
180
|
+
*/
|
|
181
|
+
setUseEnhancedColors(use: boolean): void {
|
|
182
|
+
this.useEnhancedColors = use
|
|
183
|
+
this.updateLayer() // 변경사항 적용을 위해 레이어 업데이트
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* OpenLayers 레이어 객체 생성 또는 반환
|
|
188
|
+
*/
|
|
189
|
+
createLayer(): BaseLayer {
|
|
190
|
+
if (this.isDisposed) {
|
|
191
|
+
throw new Error('레이어가 이미 폐기되었습니다')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!this.canvas || !this.ctx) {
|
|
195
|
+
throw new Error('캔버스가 초기화되지 않았습니다')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 초기 이미지 렌더링
|
|
199
|
+
this.renderWindData()
|
|
200
|
+
|
|
201
|
+
// 정적 이미지 소스 생성
|
|
202
|
+
const source = new Static({
|
|
203
|
+
url: this.canvas.toDataURL(),
|
|
204
|
+
imageExtent: [-180, -90, 180, 90],
|
|
205
|
+
projection: 'EPSG:4326',
|
|
206
|
+
interpolate: true
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// 레이어 생성
|
|
210
|
+
this.layer = new ImageLayer({
|
|
211
|
+
source: source,
|
|
212
|
+
opacity: 0.7,
|
|
213
|
+
properties: {
|
|
214
|
+
useEnhancedColors: this.useEnhancedColors
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return this.layer
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 바람 데이터 렌더링
|
|
223
|
+
*/
|
|
224
|
+
async renderWindData(): Promise<void> {
|
|
225
|
+
if (!this.ctx || !this.canvas || this.isDisposed) return
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const { data, width, height } = await this.dataPromise!
|
|
229
|
+
|
|
230
|
+
// 캔버스 지우기
|
|
231
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
|
232
|
+
|
|
233
|
+
// 바람 데이터 시각화
|
|
234
|
+
const imgData = this.ctx.createImageData(this.canvas.width, this.canvas.height)
|
|
235
|
+
|
|
236
|
+
for (let y = 0; y < this.canvas.height; y++) {
|
|
237
|
+
for (let x = 0; x < this.canvas.width; x++) {
|
|
238
|
+
const idx = (y * this.canvas.width + x) * 4
|
|
239
|
+
|
|
240
|
+
// 위치 계산
|
|
241
|
+
const lon = -180 + (x / this.canvas.width) * 360
|
|
242
|
+
const lat = 90 - (y / this.canvas.height) * 180
|
|
243
|
+
|
|
244
|
+
// 원본 이미지에서의 위치 계산
|
|
245
|
+
const imgX = Math.floor((width * (lon + 180)) / 360)
|
|
246
|
+
const imgY = Math.floor((height * (90 - lat)) / 180)
|
|
247
|
+
|
|
248
|
+
// 경계 체크
|
|
249
|
+
if (imgX < 0 || imgX >= width || imgY < 0 || imgY >= height) {
|
|
250
|
+
continue
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 원본 데이터 인덱스
|
|
254
|
+
const dataIdx = (imgY * width + imgX) * inputBands
|
|
255
|
+
|
|
256
|
+
// 바람 속도 계산
|
|
257
|
+
const u = minU + (deltaU * data[dataIdx]) / 255
|
|
258
|
+
const v = minV + (deltaV * data[dataIdx + 1]) / 255
|
|
259
|
+
const speed = Math.sqrt(u * u + v * v)
|
|
260
|
+
|
|
261
|
+
// 색상 계산: 향상된 색상 매핑 또는 기본 스키마 사용
|
|
262
|
+
let r, g, b, a
|
|
263
|
+
|
|
264
|
+
if (this.useEnhancedColors) {
|
|
265
|
+
// 향상된 색상 매핑 사용
|
|
266
|
+
const color = getWindColor(speed)
|
|
267
|
+
;[r, g, b, a] = color
|
|
268
|
+
} else {
|
|
269
|
+
// 기본 색상 매핑 (녹청색 그라데이션)
|
|
270
|
+
const normalizedSpeed = Math.min(speed / 20, 1)
|
|
271
|
+
r = Math.floor(normalizedSpeed * 0)
|
|
272
|
+
g = Math.floor(normalizedSpeed * 150)
|
|
273
|
+
b = Math.floor(normalizedSpeed * 255)
|
|
274
|
+
a = Math.floor(normalizedSpeed * 200)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 이미지 데이터에 색상 설정
|
|
278
|
+
imgData.data[idx] = r
|
|
279
|
+
imgData.data[idx + 1] = g
|
|
280
|
+
imgData.data[idx + 2] = b
|
|
281
|
+
imgData.data[idx + 3] = a
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 캔버스에 이미지 데이터 적용
|
|
286
|
+
this.ctx.putImageData(imgData, 0, 0)
|
|
287
|
+
|
|
288
|
+
// 소스 업데이트
|
|
289
|
+
if (this.layer && !this.isDisposed) {
|
|
290
|
+
try {
|
|
291
|
+
const newSource = new Static({
|
|
292
|
+
url: this.canvas.toDataURL(),
|
|
293
|
+
imageExtent: [-180, -90, 180, 90],
|
|
294
|
+
projection: 'EPSG:4326',
|
|
295
|
+
interpolate: true
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
this.layer.setSource(newSource)
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error('소스 업데이트 실패:', error)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('바람 데이터 렌더링 실패:', error)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* 레이어 업데이트 요청
|
|
310
|
+
*/
|
|
311
|
+
updateLayer(): void {
|
|
312
|
+
if (this.isDisposed) return
|
|
313
|
+
|
|
314
|
+
this.renderWindData()
|
|
315
|
+
|
|
316
|
+
// 속성 업데이트 (향상된 색상 매핑 옵션)
|
|
317
|
+
if (this.layer) {
|
|
318
|
+
this.layer.set('useEnhancedColors', this.useEnhancedColors)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 레이어를 맵에 추가
|
|
324
|
+
*/
|
|
325
|
+
addToMap(map: Map): void {
|
|
326
|
+
if (this.isDisposed) {
|
|
327
|
+
console.warn('폐기된 레이어는 맵에 추가할 수 없습니다')
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this.map = map
|
|
332
|
+
|
|
333
|
+
if (!this.layer) {
|
|
334
|
+
this.layer = this.createLayer() as ImageLayer<Static>
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
this.map.addLayer(this.layer)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* 레이어를 맵에서 제거
|
|
342
|
+
*/
|
|
343
|
+
removeFromMap(): void {
|
|
344
|
+
if (!this.layer || !this.map || this.isDisposed) {
|
|
345
|
+
return
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
// 렌더링 플래그 설정 (렌더링 방지)
|
|
350
|
+
if (this.layer['rendered'] !== undefined) {
|
|
351
|
+
this.layer['rendered'] = false
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 소스 정리
|
|
355
|
+
const source = this.layer.getSource()
|
|
356
|
+
if (source) {
|
|
357
|
+
try {
|
|
358
|
+
// setUrl 메서드가 없으므로 소스 자체를 null로 설정
|
|
359
|
+
this.layer.setSource(null)
|
|
360
|
+
} catch (e) {
|
|
361
|
+
// 오류 무시
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 맵에서 레이어 제거
|
|
366
|
+
this.map.removeLayer(this.layer)
|
|
367
|
+
} catch (e) {
|
|
368
|
+
console.error('맵에서 레이어 제거 중 오류:', e)
|
|
369
|
+
} finally {
|
|
370
|
+
// 맵 참조 해제
|
|
371
|
+
this.map = null
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* 레이어 관련 리소스 해제
|
|
377
|
+
*/
|
|
378
|
+
dispose(): void {
|
|
379
|
+
if (this.isDisposed) {
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 이미 맵에서 제거되지 않았다면 제거
|
|
384
|
+
if (this.map && this.layer) {
|
|
385
|
+
this.removeFromMap()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
// 레이어 정리
|
|
390
|
+
if (this.layer) {
|
|
391
|
+
const source = this.layer.getSource()
|
|
392
|
+
if (source) {
|
|
393
|
+
try {
|
|
394
|
+
source.dispose()
|
|
395
|
+
} catch (e) {
|
|
396
|
+
// 오류 무시
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
// 레이어 자체 dispose 호출
|
|
402
|
+
this.layer.dispose()
|
|
403
|
+
} catch (e) {
|
|
404
|
+
// 오류 무시
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.layer = null
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 캔버스 정리
|
|
411
|
+
if (this.canvas) {
|
|
412
|
+
// 캔버스 메모리 해제 (크기를 최소화)
|
|
413
|
+
this.canvas.width = 1
|
|
414
|
+
this.canvas.height = 1
|
|
415
|
+
this.canvas = null
|
|
416
|
+
this.ctx = null
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// 데이터 참조 정리
|
|
420
|
+
this.dataPromise = null
|
|
421
|
+
|
|
422
|
+
// 폐기 플래그 설정
|
|
423
|
+
this.isDisposed = true
|
|
424
|
+
} catch (e) {
|
|
425
|
+
console.error('리소스 해제 중 오류:', e)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* 데이터 설정 메서드 (호환성용)
|
|
431
|
+
*/
|
|
432
|
+
setWeatherData(data: any): void {
|
|
433
|
+
// 이 구현에서는 외부 데이터를 직접 설정하지 않음
|
|
434
|
+
console.warn('WindRasterizedLayer에서 setWeatherData가 호출되었지만, 데이터는 정적입니다.')
|
|
435
|
+
this.updateLayer() // 데이터가 변경된 것처럼 레이어 갱신
|
|
436
|
+
}
|
|
437
|
+
}
|