@things-factory/weather 9.0.0-beta.50 → 9.0.0-beta.52

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.
Binary file
Binary file
@@ -10,6 +10,8 @@ class WebGLImageStatic extends ImageStatic {
10
10
  private program: WebGLProgram | null = null
11
11
  private texture: WebGLTexture | null = null
12
12
  private buffer: WebGLBuffer | null = null
13
+ private opacity: number = 0.8
14
+ private blendMode: string = 'normal'
13
15
 
14
16
  constructor(option: {
15
17
  url: string
@@ -18,12 +20,18 @@ class WebGLImageStatic extends ImageStatic {
18
20
  imageExtent: Extent
19
21
  crossOrigin?: string
20
22
  attributions?: AttributionLike
23
+ opacity?: number
24
+ blendMode?: string
21
25
  }) {
22
26
  super({
23
27
  ...option,
24
28
  imageLoadFunction: (image, src) => this._loadImageWithWebGL(image, src)
25
29
  })
26
30
 
31
+ // 옵션에서 값 설정 (기본값 제공)
32
+ this.opacity = option.opacity !== undefined ? option.opacity : 0.8
33
+ this.blendMode = option.blendMode || 'normal'
34
+
27
35
  // WebGL 컨텍스트 설정
28
36
  this.canvas = document.createElement('canvas')
29
37
  this.gl = this.canvas.getContext('webgl', {
@@ -39,6 +47,30 @@ class WebGLImageStatic extends ImageStatic {
39
47
  this._initializeWebGL()
40
48
  }
41
49
 
50
+ /**
51
+ * 투명도 설정
52
+ */
53
+ public setOpacity(opacity: number): void {
54
+ if (opacity !== this.opacity) {
55
+ this.opacity = opacity
56
+ if (this.texture) {
57
+ this._renderTexture()
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 블렌드 모드 설정
64
+ */
65
+ public setBlendMode(blendMode: string): void {
66
+ if (blendMode !== this.blendMode) {
67
+ this.blendMode = blendMode
68
+ if (this.texture) {
69
+ this._renderTexture()
70
+ }
71
+ }
72
+ }
73
+
42
74
  /**
43
75
  * WebGL 초기화
44
76
  */
@@ -76,7 +108,27 @@ class WebGLImageStatic extends ImageStatic {
76
108
  this.gl.activeTexture(this.gl.TEXTURE0)
77
109
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)
78
110
  this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_texture'), 0)
79
- this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), 0.8)
111
+
112
+ // 사용자 지정 투명도 사용
113
+ this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), this.opacity)
114
+
115
+ // 블렌드 모드 설정
116
+ let blendModeValue = 0 // normal
117
+ switch (this.blendMode) {
118
+ case 'multiply':
119
+ blendModeValue = 1
120
+ break
121
+ case 'screen':
122
+ blendModeValue = 2
123
+ break
124
+ case 'color-dodge':
125
+ blendModeValue = 3
126
+ break
127
+ case 'invert':
128
+ blendModeValue = 4
129
+ break
130
+ }
131
+ this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_blendMode'), blendModeValue)
80
132
 
81
133
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer)
82
134
  const a_position = this.gl.getAttribLocation(this.program, 'a_position')
@@ -98,6 +150,11 @@ class WebGLImageStatic extends ImageStatic {
98
150
 
99
151
  img.onload = () => {
100
152
  if (!this.gl) return
153
+
154
+ // 캔버스 크기 설정
155
+ this.canvas!.width = img.width
156
+ this.canvas!.height = img.height
157
+
101
158
  this.texture = this.gl.createTexture()
102
159
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)
103
160
 
@@ -136,16 +193,29 @@ class WebGLImageStatic extends ImageStatic {
136
193
  precision highp float;
137
194
  uniform sampler2D u_texture;
138
195
  uniform float u_opacity;
196
+ uniform int u_blendMode;
139
197
  varying vec2 v_texCoord;
198
+
140
199
  void main() {
141
200
  vec4 texColor = texture2D(u_texture, v_texCoord);
201
+
202
+ // 완전 투명한 픽셀은 렌더링하지 않음
142
203
  if (texColor.a < 0.01) {
143
204
  discard;
144
205
  }
145
- // gl_FragColor = vec4(texColor.rgb, texColor.a * u_opacity);
146
-
147
- vec3 invertedColor = vec3(1.0) - texColor.rgb;
148
- gl_FragColor = vec4(invertedColor, texColor.a * u_opacity);
206
+
207
+ float temperature = texColor.r; // 예: 빨간 채널에 온도 정보가 있다고 가정
208
+ vec3 color;
209
+
210
+ if (temperature < 0.3) {
211
+ color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), temperature / 0.3); // 파란색에서 청록색으로
212
+ } else if (temperature < 0.6) {
213
+ color = mix(vec3(0.0, 1.0, 1.0), vec3(0.0, 1.0, 0.0), (temperature - 0.3) / 0.3); // 청록색에서 녹색으로
214
+ } else {
215
+ color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (temperature - 0.6) / 0.4); // 녹색에서 빨간색으로
216
+ }
217
+
218
+ gl_FragColor = vec4(color, texColor.a * u_opacity);
149
219
  }`
150
220
 
151
221
  const gl = this.gl!
@@ -178,7 +178,7 @@ export class WeatherPage extends PageView {
178
178
  // attributions: '© MapTiler'
179
179
  attributions: '© OpenStreetMap contributors, © CartoDB'
180
180
  }),
181
- background: 'tranparent',
181
+ background: 'transparent',
182
182
  opacity: 1
183
183
  })
184
184
 
@@ -187,7 +187,7 @@ export class WeatherPage extends PageView {
187
187
  url: 'https://{a-c}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}{r}.png'
188
188
  // url: 'https://{a-c}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png'
189
189
  }),
190
- background: 'tranparent',
190
+ background: 'transparent',
191
191
  opacity: 1
192
192
  })
193
193
 
@@ -206,6 +206,7 @@ export class WeatherPage extends PageView {
206
206
  style: new Style({
207
207
  stroke: new Stroke({
208
208
  color: '#858585',
209
+ // color: '#484848',
209
210
  width: 1
210
211
  })
211
212
  })
@@ -249,15 +250,15 @@ export class WeatherPage extends PageView {
249
250
  this.map = new Map({
250
251
  target: this.renderRoot.querySelector('#map') as HTMLElement,
251
252
  pixelRatio: Math.min(DEVICE_PIXEL_RATIO, 2),
252
- layers: [lightOSM, heatmapLayer, outlineLayer, labelOSM],
253
+ layers: [lightOSM, /*heatmapLayer, */ outlineLayer, labelOSM],
253
254
  view: new View({
254
255
  center: fromLonLat([127.5, 36.5]),
255
256
  zoom: 7.5
256
257
  })
257
258
  })
258
259
 
259
- // this._addWeatherLayer()
260
- this._addWindLayer()
260
+ this._addWeatherLayer()
261
+ // this._addWindLayer()
261
262
  }
262
263
 
263
264
  private _addWindLayer() {
@@ -292,16 +293,45 @@ export class WeatherPage extends PageView {
292
293
  console.log('🚨 기존 날씨 레이어 제거')
293
294
  }
294
295
 
296
+ // 날씨 타입별 최적 설정
297
+ let opacity, blendMode
298
+
299
+ switch (this.activeType) {
300
+ case 'temperature':
301
+ opacity = 0.75
302
+ blendMode = 'normal'
303
+ break
304
+ case 'humidity':
305
+ opacity = 0.65
306
+ blendMode = 'multiply'
307
+ break
308
+ case 'wind':
309
+ opacity = 0.8
310
+ blendMode = 'screen'
311
+ break
312
+ case 'precipitation':
313
+ opacity = 0.7
314
+ blendMode = 'color-dodge'
315
+ break
316
+ default:
317
+ opacity = 0.7
318
+ blendMode = 'normal'
319
+ }
320
+
295
321
  this.weatherLayer = new ImageLayer({
296
322
  source: new WebGLImageStatic({
297
- url: `/weather/map/${this.activeType}.png`,
323
+ url: `/weather/map/${this.activeType}.png?refresh=${Date.now()}`, // 캐시 방지
324
+ // 올바른 지구 투영 비율 사용
298
325
  imageExtent: [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892],
299
326
  interpolation: true,
300
- projection: 'EPSG:3857' // 좌표 시스템 설정
301
- })
327
+ projection: 'EPSG:3857'
328
+ // opacity: opacity,
329
+ // blendMode: blendMode
330
+ }),
331
+ opacity // WebGLImageStatic에서 투명도 처리
302
332
  })
303
333
 
304
- this.map.addLayer(this.weatherLayer)
334
+ this.map.getLayers().insertAt(1, this.weatherLayer)
305
335
 
306
336
  console.log('🚨 날씨 레이어 추가', this.map.getLayers())
307
337
  }
@@ -8,6 +8,8 @@ declare class WebGLImageStatic extends ImageStatic {
8
8
  private program;
9
9
  private texture;
10
10
  private buffer;
11
+ private opacity;
12
+ private blendMode;
11
13
  constructor(option: {
12
14
  url: string;
13
15
  projection: ProjectionLike;
@@ -15,7 +17,17 @@ declare class WebGLImageStatic extends ImageStatic {
15
17
  imageExtent: Extent;
16
18
  crossOrigin?: string;
17
19
  attributions?: AttributionLike;
20
+ opacity?: number;
21
+ blendMode?: string;
18
22
  });
23
+ /**
24
+ * 투명도 설정
25
+ */
26
+ setOpacity(opacity: number): void;
27
+ /**
28
+ * 블렌드 모드 설정
29
+ */
30
+ setBlendMode(blendMode: string): void;
19
31
  /**
20
32
  * WebGL 초기화
21
33
  */
@@ -7,6 +7,11 @@ class WebGLImageStatic extends ImageStatic {
7
7
  this.program = null;
8
8
  this.texture = null;
9
9
  this.buffer = null;
10
+ this.opacity = 0.8;
11
+ this.blendMode = 'normal';
12
+ // 옵션에서 값 설정 (기본값 제공)
13
+ this.opacity = option.opacity !== undefined ? option.opacity : 0.8;
14
+ this.blendMode = option.blendMode || 'normal';
10
15
  // WebGL 컨텍스트 설정
11
16
  this.canvas = document.createElement('canvas');
12
17
  this.gl = this.canvas.getContext('webgl', {
@@ -19,6 +24,28 @@ class WebGLImageStatic extends ImageStatic {
19
24
  }
20
25
  this._initializeWebGL();
21
26
  }
27
+ /**
28
+ * 투명도 설정
29
+ */
30
+ setOpacity(opacity) {
31
+ if (opacity !== this.opacity) {
32
+ this.opacity = opacity;
33
+ if (this.texture) {
34
+ this._renderTexture();
35
+ }
36
+ }
37
+ }
38
+ /**
39
+ * 블렌드 모드 설정
40
+ */
41
+ setBlendMode(blendMode) {
42
+ if (blendMode !== this.blendMode) {
43
+ this.blendMode = blendMode;
44
+ if (this.texture) {
45
+ this._renderTexture();
46
+ }
47
+ }
48
+ }
22
49
  /**
23
50
  * WebGL 초기화
24
51
  */
@@ -51,7 +78,25 @@ class WebGLImageStatic extends ImageStatic {
51
78
  this.gl.activeTexture(this.gl.TEXTURE0);
52
79
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
53
80
  this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_texture'), 0);
54
- this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), 0.8);
81
+ // 사용자 지정 투명도 사용
82
+ this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), this.opacity);
83
+ // 블렌드 모드 설정
84
+ let blendModeValue = 0; // normal
85
+ switch (this.blendMode) {
86
+ case 'multiply':
87
+ blendModeValue = 1;
88
+ break;
89
+ case 'screen':
90
+ blendModeValue = 2;
91
+ break;
92
+ case 'color-dodge':
93
+ blendModeValue = 3;
94
+ break;
95
+ case 'invert':
96
+ blendModeValue = 4;
97
+ break;
98
+ }
99
+ this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_blendMode'), blendModeValue);
55
100
  this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
56
101
  const a_position = this.gl.getAttribLocation(this.program, 'a_position');
57
102
  const a_texCoord = this.gl.getAttribLocation(this.program, 'a_texCoord');
@@ -70,6 +115,9 @@ class WebGLImageStatic extends ImageStatic {
70
115
  img.onload = () => {
71
116
  if (!this.gl)
72
117
  return;
118
+ // 캔버스 크기 설정
119
+ this.canvas.width = img.width;
120
+ this.canvas.height = img.height;
73
121
  this.texture = this.gl.createTexture();
74
122
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
75
123
  this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
@@ -101,16 +149,29 @@ class WebGLImageStatic extends ImageStatic {
101
149
  precision highp float;
102
150
  uniform sampler2D u_texture;
103
151
  uniform float u_opacity;
152
+ uniform int u_blendMode;
104
153
  varying vec2 v_texCoord;
154
+
105
155
  void main() {
106
156
  vec4 texColor = texture2D(u_texture, v_texCoord);
157
+
158
+ // 완전 투명한 픽셀은 렌더링하지 않음
107
159
  if (texColor.a < 0.01) {
108
160
  discard;
109
161
  }
110
- // gl_FragColor = vec4(texColor.rgb, texColor.a * u_opacity);
111
-
112
- vec3 invertedColor = vec3(1.0) - texColor.rgb;
113
- gl_FragColor = vec4(invertedColor, texColor.a * u_opacity);
162
+
163
+ float temperature = texColor.r; // 예: 빨간 채널에 온도 정보가 있다고 가정
164
+ vec3 color;
165
+
166
+ if (temperature < 0.3) {
167
+ color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), temperature / 0.3); // 파란색에서 청록색으로
168
+ } else if (temperature < 0.6) {
169
+ color = mix(vec3(0.0, 1.0, 1.0), vec3(0.0, 1.0, 0.0), (temperature - 0.3) / 0.3); // 청록색에서 녹색으로
170
+ } else {
171
+ color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (temperature - 0.6) / 0.4); // 녹색에서 빨간색으로
172
+ }
173
+
174
+ gl_FragColor = vec4(color, texColor.a * u_opacity);
114
175
  }`;
115
176
  const gl = this.gl;
116
177
  const vertexShader = this._compileShader(gl.VERTEX_SHADER, vertexShaderSource);
@@ -1 +1 @@
1
- {"version":3,"file":"webgl-image-static.js","sourceRoot":"","sources":["../../client/ol-components/webgl-image-static.ts"],"names":[],"mappings":"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","sourcesContent":["import ImageStatic from 'ol/source/ImageStatic'\nimport ImageWrapper from 'ol/Image'\nimport { Extent } from 'ol/extent'\nimport { ProjectionLike } from 'ol/proj'\nimport { AttributionLike } from 'ol/source/Source'\n\nclass WebGLImageStatic extends ImageStatic {\n private canvas: HTMLCanvasElement | null = null\n private gl: WebGLRenderingContext | null = null\n private program: WebGLProgram | null = null\n private texture: WebGLTexture | null = null\n private buffer: WebGLBuffer | null = null\n\n constructor(option: {\n url: string\n projection: ProjectionLike\n interpolation?: boolean\n imageExtent: Extent\n crossOrigin?: string\n attributions?: AttributionLike\n }) {\n super({\n ...option,\n imageLoadFunction: (image, src) => this._loadImageWithWebGL(image, src)\n })\n\n // WebGL 컨텍스트 설정\n this.canvas = document.createElement('canvas')\n this.gl = this.canvas.getContext('webgl', {\n alpha: true,\n antialias: false,\n depth: false\n }) as WebGLRenderingContext\n\n if (!this.gl) {\n throw new Error('WebGL을 초기화할 수 없습니다.')\n }\n\n this._initializeWebGL()\n }\n\n /**\n * WebGL 초기화\n */\n private _initializeWebGL() {\n this.program = this._createShaderProgram()\n\n if (!this.program) return\n\n this._initializeBuffers()\n }\n\n /**\n * WebGL에서 사용하는 버퍼 설정\n */\n private _initializeBuffers() {\n const vertices = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0])\n const gl = this.gl!\n\n this.buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n }\n\n /**\n * WebGL을 사용하여 렌더링\n */\n private _renderTexture() {\n if (!this.gl || !this.program || !this.texture || !this.buffer) return\n\n this.gl.viewport(0, 0, this.canvas!.width, this.canvas!.height)\n this.gl.clearColor(0, 0, 0, 0)\n this.gl.clear(this.gl.COLOR_BUFFER_BIT)\n this.gl.useProgram(this.program)\n\n this.gl.activeTexture(this.gl.TEXTURE0)\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)\n this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_texture'), 0)\n this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), 0.8)\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer)\n const a_position = this.gl.getAttribLocation(this.program, 'a_position')\n const a_texCoord = this.gl.getAttribLocation(this.program, 'a_texCoord')\n\n this.gl.enableVertexAttribArray(a_position)\n this.gl.enableVertexAttribArray(a_texCoord)\n this.gl.vertexAttribPointer(a_position, 2, this.gl.FLOAT, false, 16, 0)\n this.gl.vertexAttribPointer(a_texCoord, 2, this.gl.FLOAT, false, 16, 8)\n this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4)\n }\n\n /**\n * WebGL에서 기후 데이터 이미지를 로드\n */\n private _loadImageWithWebGL(image: ImageWrapper, src: string) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n img.onload = () => {\n if (!this.gl) return\n this.texture = this.gl.createTexture()\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)\n\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR)\n\n this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, img)\n\n this._renderTexture()\n ;(image.getImage() as HTMLImageElement | HTMLVideoElement).src = this.canvas!.toDataURL('image/png')\n\n this.gl = null\n this.canvas = null\n }\n\n img.src = src\n }\n\n /**\n * 셰이더 프로그램 생성\n */\n private _createShaderProgram(): WebGLProgram | null {\n const vertexShaderSource = `\n precision highp float;\n attribute vec4 a_position;\n attribute vec2 a_texCoord;\n varying vec2 v_texCoord;\n void main() {\n gl_Position = a_position;\n v_texCoord = a_texCoord;\n }`\n\n const fragmentShaderSource = `\n precision highp float;\n uniform sampler2D u_texture;\n uniform float u_opacity;\n varying vec2 v_texCoord;\n void main() {\n vec4 texColor = texture2D(u_texture, v_texCoord);\n if (texColor.a < 0.01) {\n discard;\n }\n // gl_FragColor = vec4(texColor.rgb, texColor.a * u_opacity);\n \n vec3 invertedColor = vec3(1.0) - texColor.rgb;\n gl_FragColor = vec4(invertedColor, texColor.a * u_opacity);\n }`\n\n const gl = this.gl!\n const vertexShader = this._compileShader(gl.VERTEX_SHADER, vertexShaderSource)\n const fragmentShader = this._compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource)\n if (!vertexShader || !fragmentShader) return null\n\n const program = gl.createProgram()!\n gl.attachShader(program, vertexShader)\n gl.attachShader(program, fragmentShader)\n gl.linkProgram(program)\n\n return gl.getProgramParameter(program, gl.LINK_STATUS) ? program : null\n }\n\n private _compileShader(type: number, source: string): WebGLShader | null {\n const gl = this.gl!\n\n const shader = gl.createShader(type)!\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n return gl.getShaderParameter(shader, gl.COMPILE_STATUS) ? shader : null\n }\n}\n\nexport default WebGLImageStatic\n"]}
1
+ {"version":3,"file":"webgl-image-static.js","sourceRoot":"","sources":["../../client/ol-components/webgl-image-static.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAA;AAM/C,MAAM,gBAAiB,SAAQ,WAAW;IASxC,YAAY,MASX;QACC,KAAK,iCACA,MAAM,KACT,iBAAiB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,IACvE,CAAA;QArBI,WAAM,GAA6B,IAAI,CAAA;QACvC,OAAE,GAAiC,IAAI,CAAA;QACvC,YAAO,GAAwB,IAAI,CAAA;QACnC,YAAO,GAAwB,IAAI,CAAA;QACnC,WAAM,GAAuB,IAAI,CAAA;QACjC,YAAO,GAAW,GAAG,CAAA;QACrB,cAAS,GAAW,QAAQ,CAAA;QAiBlC,qBAAqB;QACrB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;QAClE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,QAAQ,CAAA;QAE7C,gBAAgB;QAChB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC9C,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE;YACxC,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,KAAK;SACb,CAA0B,CAAA;QAE3B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QAED,IAAI,CAAC,gBAAgB,EAAE,CAAA;IACzB,CAAC;IAED;;OAEG;IACI,UAAU,CAAC,OAAe;QAC/B,IAAI,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;YACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,cAAc,EAAE,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,SAAiB;QACnC,IAAI,SAAS,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;YAC1B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,cAAc,EAAE,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,gBAAgB;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAA;QAE1C,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEzB,IAAI,CAAC,kBAAkB,EAAE,CAAA;IAC3B,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACvF,MAAM,EAAE,GAAG,IAAI,CAAC,EAAG,CAAA;QAEnB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,YAAY,EAAE,CAAA;QAC/B,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC3C,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,CAAA;IAC1D,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAM;QAEtE,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAO,CAAC,KAAK,EAAE,IAAI,CAAC,MAAO,CAAC,MAAM,CAAC,CAAA;QAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QAC9B,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAA;QACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAEhC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAA;QACvC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QACrD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAA;QAE3E,gBAAgB;QAChB,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAEtF,YAAY;QACZ,IAAI,cAAc,GAAG,CAAC,CAAA,CAAC,SAAS;QAChC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YACvB,KAAK,UAAU;gBACb,cAAc,GAAG,CAAC,CAAA;gBAClB,MAAK;YACP,KAAK,QAAQ;gBACX,cAAc,GAAG,CAAC,CAAA;gBAClB,MAAK;YACP,KAAK,aAAa;gBAChB,cAAc,GAAG,CAAC,CAAA;gBAClB,MAAK;YACP,KAAK,QAAQ;gBACX,cAAc,GAAG,CAAC,CAAA;gBAClB,MAAK;QACT,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,cAAc,CAAC,CAAA;QAE1F,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;QACxE,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;QAExE,IAAI,CAAC,EAAE,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAA;QAC3C,IAAI,CAAC,EAAE,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAA;QAC3C,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;QACvE,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;QACvE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,KAAmB,EAAE,GAAW;QAC1D,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE,CAAA;QACvB,GAAG,CAAC,WAAW,GAAG,WAAW,CAAA;QAE7B,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;YAChB,IAAI,CAAC,IAAI,CAAC,EAAE;gBAAE,OAAM;YAEpB,YAAY;YACZ,IAAI,CAAC,MAAO,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAA;YAC9B,IAAI,CAAC,MAAO,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;YAEhC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAA;YACtC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;YAErD,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAA;YACxF,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAA;YACxF,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAA;YACrF,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAA;YAErF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;YAEjG,IAAI,CAAC,cAAc,EAAE,CACpB;YAAC,KAAK,CAAC,QAAQ,EAA0C,CAAC,GAAG,GAAG,IAAI,CAAC,MAAO,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;YAEpG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;YACd,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QACpB,CAAC,CAAA;QAED,GAAG,CAAC,GAAG,GAAG,GAAG,CAAA;IACf,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,MAAM,kBAAkB,GAAG;;;;;;;;MAQzB,CAAA;QAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2B3B,CAAA;QAEF,MAAM,EAAE,GAAG,IAAI,CAAC,EAAG,CAAA;QACnB,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAA;QAC9E,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,eAAe,EAAE,oBAAoB,CAAC,CAAA;QACpF,IAAI,CAAC,YAAY,IAAI,CAAC,cAAc;YAAE,OAAO,IAAI,CAAA;QAEjD,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,EAAG,CAAA;QACnC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;QACtC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,cAAc,CAAC,CAAA;QACxC,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAEvB,OAAO,EAAE,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA;IACzE,CAAC;IAEO,cAAc,CAAC,IAAY,EAAE,MAAc;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAG,CAAA;QAEnB,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAE,CAAA;QACrC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QACxB,OAAO,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IACzE,CAAC;CACF;AAED,eAAe,gBAAgB,CAAA","sourcesContent":["import ImageStatic from 'ol/source/ImageStatic'\nimport ImageWrapper from 'ol/Image'\nimport { Extent } from 'ol/extent'\nimport { ProjectionLike } from 'ol/proj'\nimport { AttributionLike } from 'ol/source/Source'\n\nclass WebGLImageStatic extends ImageStatic {\n private canvas: HTMLCanvasElement | null = null\n private gl: WebGLRenderingContext | null = null\n private program: WebGLProgram | null = null\n private texture: WebGLTexture | null = null\n private buffer: WebGLBuffer | null = null\n private opacity: number = 0.8\n private blendMode: string = 'normal'\n\n constructor(option: {\n url: string\n projection: ProjectionLike\n interpolation?: boolean\n imageExtent: Extent\n crossOrigin?: string\n attributions?: AttributionLike\n opacity?: number\n blendMode?: string\n }) {\n super({\n ...option,\n imageLoadFunction: (image, src) => this._loadImageWithWebGL(image, src)\n })\n\n // 옵션에서 값 설정 (기본값 제공)\n this.opacity = option.opacity !== undefined ? option.opacity : 0.8\n this.blendMode = option.blendMode || 'normal'\n\n // WebGL 컨텍스트 설정\n this.canvas = document.createElement('canvas')\n this.gl = this.canvas.getContext('webgl', {\n alpha: true,\n antialias: false,\n depth: false\n }) as WebGLRenderingContext\n\n if (!this.gl) {\n throw new Error('WebGL을 초기화할 수 없습니다.')\n }\n\n this._initializeWebGL()\n }\n\n /**\n * 투명도 설정\n */\n public setOpacity(opacity: number): void {\n if (opacity !== this.opacity) {\n this.opacity = opacity\n if (this.texture) {\n this._renderTexture()\n }\n }\n }\n\n /**\n * 블렌드 모드 설정\n */\n public setBlendMode(blendMode: string): void {\n if (blendMode !== this.blendMode) {\n this.blendMode = blendMode\n if (this.texture) {\n this._renderTexture()\n }\n }\n }\n\n /**\n * WebGL 초기화\n */\n private _initializeWebGL() {\n this.program = this._createShaderProgram()\n\n if (!this.program) return\n\n this._initializeBuffers()\n }\n\n /**\n * WebGL에서 사용하는 버퍼 설정\n */\n private _initializeBuffers() {\n const vertices = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0])\n const gl = this.gl!\n\n this.buffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n }\n\n /**\n * WebGL을 사용하여 렌더링\n */\n private _renderTexture() {\n if (!this.gl || !this.program || !this.texture || !this.buffer) return\n\n this.gl.viewport(0, 0, this.canvas!.width, this.canvas!.height)\n this.gl.clearColor(0, 0, 0, 0)\n this.gl.clear(this.gl.COLOR_BUFFER_BIT)\n this.gl.useProgram(this.program)\n\n this.gl.activeTexture(this.gl.TEXTURE0)\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)\n this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_texture'), 0)\n\n // 사용자 지정 투명도 사용\n this.gl.uniform1f(this.gl.getUniformLocation(this.program, 'u_opacity'), this.opacity)\n\n // 블렌드 모드 설정\n let blendModeValue = 0 // normal\n switch (this.blendMode) {\n case 'multiply':\n blendModeValue = 1\n break\n case 'screen':\n blendModeValue = 2\n break\n case 'color-dodge':\n blendModeValue = 3\n break\n case 'invert':\n blendModeValue = 4\n break\n }\n this.gl.uniform1i(this.gl.getUniformLocation(this.program, 'u_blendMode'), blendModeValue)\n\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer)\n const a_position = this.gl.getAttribLocation(this.program, 'a_position')\n const a_texCoord = this.gl.getAttribLocation(this.program, 'a_texCoord')\n\n this.gl.enableVertexAttribArray(a_position)\n this.gl.enableVertexAttribArray(a_texCoord)\n this.gl.vertexAttribPointer(a_position, 2, this.gl.FLOAT, false, 16, 0)\n this.gl.vertexAttribPointer(a_texCoord, 2, this.gl.FLOAT, false, 16, 8)\n this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4)\n }\n\n /**\n * WebGL에서 기후 데이터 이미지를 로드\n */\n private _loadImageWithWebGL(image: ImageWrapper, src: string) {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n img.onload = () => {\n if (!this.gl) return\n\n // 캔버스 크기 설정\n this.canvas!.width = img.width\n this.canvas!.height = img.height\n\n this.texture = this.gl.createTexture()\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture)\n\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR)\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR)\n\n this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, img)\n\n this._renderTexture()\n ;(image.getImage() as HTMLImageElement | HTMLVideoElement).src = this.canvas!.toDataURL('image/png')\n\n this.gl = null\n this.canvas = null\n }\n\n img.src = src\n }\n\n /**\n * 셰이더 프로그램 생성\n */\n private _createShaderProgram(): WebGLProgram | null {\n const vertexShaderSource = `\n precision highp float;\n attribute vec4 a_position;\n attribute vec2 a_texCoord;\n varying vec2 v_texCoord;\n void main() {\n gl_Position = a_position;\n v_texCoord = a_texCoord;\n }`\n\n const fragmentShaderSource = `\n precision highp float;\n uniform sampler2D u_texture;\n uniform float u_opacity;\n uniform int u_blendMode;\n varying vec2 v_texCoord;\n \n void main() {\n vec4 texColor = texture2D(u_texture, v_texCoord);\n \n // 완전 투명한 픽셀은 렌더링하지 않음\n if (texColor.a < 0.01) {\n discard;\n }\n \n float temperature = texColor.r; // 예: 빨간 채널에 온도 정보가 있다고 가정\n vec3 color;\n\n if (temperature < 0.3) {\n color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), temperature / 0.3); // 파란색에서 청록색으로\n } else if (temperature < 0.6) {\n color = mix(vec3(0.0, 1.0, 1.0), vec3(0.0, 1.0, 0.0), (temperature - 0.3) / 0.3); // 청록색에서 녹색으로\n } else {\n color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (temperature - 0.6) / 0.4); // 녹색에서 빨간색으로\n }\n\n gl_FragColor = vec4(color, texColor.a * u_opacity);\n }`\n\n const gl = this.gl!\n const vertexShader = this._compileShader(gl.VERTEX_SHADER, vertexShaderSource)\n const fragmentShader = this._compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource)\n if (!vertexShader || !fragmentShader) return null\n\n const program = gl.createProgram()!\n gl.attachShader(program, vertexShader)\n gl.attachShader(program, fragmentShader)\n gl.linkProgram(program)\n\n return gl.getProgramParameter(program, gl.LINK_STATUS) ? program : null\n }\n\n private _compileShader(type: number, source: string): WebGLShader | null {\n const gl = this.gl!\n\n const shader = gl.createShader(type)!\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n return gl.getShaderParameter(shader, gl.COMPILE_STATUS) ? shader : null\n }\n}\n\nexport default WebGLImageStatic\n"]}
@@ -160,7 +160,7 @@ let WeatherPage = class WeatherPage extends PageView {
160
160
  // attributions: '© MapTiler'
161
161
  attributions: '© OpenStreetMap contributors, © CartoDB'
162
162
  }),
163
- background: 'tranparent',
163
+ background: 'transparent',
164
164
  opacity: 1
165
165
  });
166
166
  const labelOSM = new TileLayer({
@@ -168,7 +168,7 @@ let WeatherPage = class WeatherPage extends PageView {
168
168
  url: 'https://{a-c}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}{r}.png'
169
169
  // url: 'https://{a-c}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png'
170
170
  }),
171
- background: 'tranparent',
171
+ background: 'transparent',
172
172
  opacity: 1
173
173
  });
174
174
  const vectorTile = new VectorTile({
@@ -185,6 +185,7 @@ let WeatherPage = class WeatherPage extends PageView {
185
185
  style: new Style({
186
186
  stroke: new Stroke({
187
187
  color: '#858585',
188
+ // color: '#484848',
188
189
  width: 1
189
190
  })
190
191
  })
@@ -220,14 +221,14 @@ let WeatherPage = class WeatherPage extends PageView {
220
221
  this.map = new Map({
221
222
  target: this.renderRoot.querySelector('#map'),
222
223
  pixelRatio: Math.min(DEVICE_PIXEL_RATIO, 2),
223
- layers: [lightOSM, heatmapLayer, outlineLayer, labelOSM],
224
+ layers: [lightOSM, /*heatmapLayer, */ outlineLayer, labelOSM],
224
225
  view: new View({
225
226
  center: fromLonLat([127.5, 36.5]),
226
227
  zoom: 7.5
227
228
  })
228
229
  });
229
- // this._addWeatherLayer()
230
- this._addWindLayer();
230
+ this._addWeatherLayer();
231
+ // this._addWindLayer()
231
232
  }
232
233
  _addWindLayer() {
233
234
  if (this.windLayer) {
@@ -255,15 +256,42 @@ let WeatherPage = class WeatherPage extends PageView {
255
256
  this.map.removeLayer(this.weatherLayer);
256
257
  console.log('🚨 기존 날씨 레이어 제거');
257
258
  }
259
+ // 날씨 타입별 최적 설정
260
+ let opacity, blendMode;
261
+ switch (this.activeType) {
262
+ case 'temperature':
263
+ opacity = 0.75;
264
+ blendMode = 'normal';
265
+ break;
266
+ case 'humidity':
267
+ opacity = 0.65;
268
+ blendMode = 'multiply';
269
+ break;
270
+ case 'wind':
271
+ opacity = 0.8;
272
+ blendMode = 'screen';
273
+ break;
274
+ case 'precipitation':
275
+ opacity = 0.7;
276
+ blendMode = 'color-dodge';
277
+ break;
278
+ default:
279
+ opacity = 0.7;
280
+ blendMode = 'normal';
281
+ }
258
282
  this.weatherLayer = new ImageLayer({
259
283
  source: new WebGLImageStatic({
260
- url: `/weather/map/${this.activeType}.png`,
284
+ url: `/weather/map/${this.activeType}.png?refresh=${Date.now()}`, // 캐시 방지
285
+ // 올바른 지구 투영 비율 사용
261
286
  imageExtent: [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892],
262
287
  interpolation: true,
263
- projection: 'EPSG:3857' // 좌표 시스템 설정
264
- })
288
+ projection: 'EPSG:3857'
289
+ // opacity: opacity,
290
+ // blendMode: blendMode
291
+ }),
292
+ opacity // WebGLImageStatic에서 투명도 처리
265
293
  });
266
- this.map.addLayer(this.weatherLayer);
294
+ this.map.getLayers().insertAt(1, this.weatherLayer);
267
295
  console.log('🚨 날씨 레이어 추가', this.map.getLayers());
268
296
  }
269
297
  _changeWeatherType(type) {
@@ -1 +1 @@
1
- {"version":3,"file":"weather-page.js","sourceRoot":"","sources":["../../client/pages/weather-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\n\nimport Map from 'ol/Map.js'\nimport View from 'ol/View.js'\nimport TileLayer from 'ol/layer/Tile.js'\nimport { fromLonLat } from 'ol/proj.js'\nimport 'ol/ol.css'\nimport { MVT } from 'ol/format'\nimport { DEVICE_PIXEL_RATIO } from 'ol/has.js'\nimport Flow from 'ol/layer/Flow.js'\n\nimport ImageLayer from 'ol/layer/Image'\nimport WebGLImageStatic from '../ol-components/webgl-image-static'\nimport { VectorTile, XYZ } from 'ol/source'\nimport VectorSource from 'ol/source/Vector'\nimport colormap from 'colormap'\n\nimport Heatmap from 'ol/layer/Heatmap.js'\nimport Feature from 'ol/Feature.js'\nimport Point from 'ol/geom/Point.js'\nimport { Stroke, Style } from 'ol/style.js'\n\nimport wind from '../ol-components/wind.js'\nimport VectorTileLayer from 'ol/layer/VectorTile'\n\nconst colors = colormap({\n colormap: 'jet',\n nshades: 10,\n alpha: 0.3,\n format: 'rgba'\n})\n\n@customElement('weather-page')\nexport class WeatherPage extends PageView {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .map-container {\n flex: 1;\n display: flex;\n position: relative;\n background-color: #f9f9f9;\n overflow: hidden;\n }\n\n #map {\n flex: 1;\n }\n\n .controls {\n position: absolute;\n top: 10px;\n right: 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background: rgba(255, 255, 255, 0.8);\n padding: 15px;\n border-radius: 4px;\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n z-index: 10;\n }\n\n .weather-btn {\n cursor: pointer;\n padding: 8px 12px;\n background-color: #f0f0f0;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-weight: bold;\n transition: all 0.3s;\n }\n\n .weather-btn.active {\n background-color: #4281f5;\n color: white;\n border-color: #2d5ed9;\n }\n\n .refresh-btn {\n margin-top: 10px;\n cursor: pointer;\n padding: 8px 12px;\n background-color: #00c851;\n color: white;\n border: none;\n border-radius: 4px;\n font-weight: bold;\n transition: all 0.3s;\n }\n\n .refresh-btn:hover {\n background-color: #00a651;\n }\n `\n }\n\n @property({ type: String }) private activeType = 'temperature'\n @state() private map: any\n @state() private weatherLayer: any\n @state() private windLayer: any\n\n constructor() {\n super()\n }\n\n render() {\n return html`\n <div class=\"map-container\">\n <div id=\"map\"></div>\n\n <div class=\"controls\">\n <button\n class=\"weather-btn ${this.activeType === 'temperature' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('temperature')}\n >\n 온도\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'humidity' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('humidity')}\n >\n 습도\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'wind' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('wind')}\n >\n 바람\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'precipitation' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('precipitation')}\n >\n 강수량\n </button>\n <button class=\"refresh-btn\" @click=${this._refreshWeatherMap}>새로고침</button>\n </div>\n </div>\n `\n }\n\n connectedCallback(): void {\n super.connectedCallback()\n\n requestAnimationFrame(() => {\n this._initMap()\n })\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback()\n\n this.map.removeLayer(this.weatherLayer)\n this.weatherLayer.dispose()\n this.map.dispose()\n\n this.map = null\n this.weatherLayer = null\n }\n\n private _initMap() {\n const lightOSM = new TileLayer({\n source: new XYZ({\n // url: 'https://{a-c}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',\n url: 'https://{a-c}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'\n // url: 'https://api.maptiler.com/maps/toner-lines/{z}/{x}/{y}.png?key=uHikv5CybbRm7cDLjRca'\n // attributions: '© MapTiler'\n attributions: '© OpenStreetMap contributors, © CartoDB'\n }),\n background: 'tranparent',\n opacity: 1\n })\n\n const labelOSM = new TileLayer({\n source: new XYZ({\n url: 'https://{a-c}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}{r}.png'\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png'\n }),\n background: 'tranparent',\n opacity: 1\n })\n\n const vectorTile = new VectorTile({\n format: new MVT({\n layerName: 'countries',\n layers: ['countries']\n }),\n url: `/geo/tiles/{z}/{x}/{y}.mvt`,\n maxZoom: 10,\n projection: 'EPSG:3857'\n })\n\n const outlineLayer = new VectorTileLayer({\n source: vectorTile,\n style: new Style({\n stroke: new Stroke({\n color: '#858585',\n width: 1\n })\n })\n })\n\n console.log('🛠 벡터 타일 레이어:', outlineLayer, outlineLayer.getSource())\n\n /* heatmap */\n\n const generateHeatmapData = () => {\n const features: Feature[] = []\n for (let i = 0; i < 20000; i++) {\n // 더 많은 데이터 포인트 생성\n const lon = Math.random() * 360 - 180 // 전 세계 경도 (-180 ~ 180)\n const lat = Math.random() * 170 - 85 // 전 세계 위도 (-85 ~ 85)\n\n const weight = Math.random() * 1 // 가중치 (0 ~ 2)\n\n const feature = new Feature({\n geometry: new Point(fromLonLat([lon, lat])),\n weight: weight\n })\n features.push(feature)\n }\n return features\n }\n\n const vectorSource = new VectorSource({\n features: generateHeatmapData()\n })\n\n const heatmapLayer = new Heatmap({\n source: vectorSource,\n blur: 60, // 흐림 정도 증가\n radius: 60, // 점의 반경 증가\n weight: feature => feature.get('weight'), // 가중치 적용\n gradient: colors.map(c => `rgba(${c.join(',')})`), // 색상 적용\n opacity: 0.8\n })\n\n this.map = new Map({\n target: this.renderRoot.querySelector('#map') as HTMLElement,\n pixelRatio: Math.min(DEVICE_PIXEL_RATIO, 2),\n layers: [lightOSM, heatmapLayer, outlineLayer, labelOSM],\n view: new View({\n center: fromLonLat([127.5, 36.5]),\n zoom: 7.5\n })\n })\n\n // this._addWeatherLayer()\n this._addWindLayer()\n }\n\n private _addWindLayer() {\n if (this.windLayer) {\n this.map.removeLayer(this.windLayer)\n console.log('🚨 기존 날씨 레이어 제거')\n }\n\n const maxSpeed = 20\n\n const colorStops: (number | [number, number, number, number])[] = []\n for (let i = 0; i < colors.length; ++i) {\n colorStops.push((i * maxSpeed) / (colors.length - 1))\n colorStops.push(colors[i])\n }\n\n this.windLayer = new Flow({\n source: wind,\n maxSpeed,\n particles: 20000,\n style: {\n color: ['interpolate', ['linear'], ['get', 'speed'], ...colorStops]\n }\n })\n\n this.map.addLayer(this.windLayer)\n }\n\n private _addWeatherLayer() {\n if (this.weatherLayer) {\n this.map.removeLayer(this.weatherLayer)\n console.log('🚨 기존 날씨 레이어 제거')\n }\n\n this.weatherLayer = new ImageLayer({\n source: new WebGLImageStatic({\n url: `/weather/map/${this.activeType}.png`,\n imageExtent: [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892],\n interpolation: true,\n projection: 'EPSG:3857' // 좌표 시스템 설정\n })\n })\n\n this.map.addLayer(this.weatherLayer)\n\n console.log('🚨 날씨 레이어 추가', this.map.getLayers())\n }\n\n private _changeWeatherType(type: string) {\n if (this.activeType === type) return\n this.activeType = type\n this._addWeatherLayer()\n }\n\n private _refreshWeatherMap() {\n this._addWeatherLayer()\n }\n}\n"]}
1
+ {"version":3,"file":"weather-page.js","sourceRoot":"","sources":["../../client/pages/weather-page.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\n\nimport Map from 'ol/Map.js'\nimport View from 'ol/View.js'\nimport TileLayer from 'ol/layer/Tile.js'\nimport { fromLonLat } from 'ol/proj.js'\nimport 'ol/ol.css'\nimport { MVT } from 'ol/format'\nimport { DEVICE_PIXEL_RATIO } from 'ol/has.js'\nimport Flow from 'ol/layer/Flow.js'\n\nimport ImageLayer from 'ol/layer/Image'\nimport WebGLImageStatic from '../ol-components/webgl-image-static'\nimport { VectorTile, XYZ } from 'ol/source'\nimport VectorSource from 'ol/source/Vector'\nimport colormap from 'colormap'\n\nimport Heatmap from 'ol/layer/Heatmap.js'\nimport Feature from 'ol/Feature.js'\nimport Point from 'ol/geom/Point.js'\nimport { Stroke, Style } from 'ol/style.js'\n\nimport wind from '../ol-components/wind.js'\nimport VectorTileLayer from 'ol/layer/VectorTile'\n\nconst colors = colormap({\n colormap: 'jet',\n nshades: 10,\n alpha: 0.3,\n format: 'rgba'\n})\n\n@customElement('weather-page')\nexport class WeatherPage extends PageView {\n static get styles() {\n return css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .map-container {\n flex: 1;\n display: flex;\n position: relative;\n background-color: #f9f9f9;\n overflow: hidden;\n }\n\n #map {\n flex: 1;\n }\n\n .controls {\n position: absolute;\n top: 10px;\n right: 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n background: rgba(255, 255, 255, 0.8);\n padding: 15px;\n border-radius: 4px;\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n z-index: 10;\n }\n\n .weather-btn {\n cursor: pointer;\n padding: 8px 12px;\n background-color: #f0f0f0;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-weight: bold;\n transition: all 0.3s;\n }\n\n .weather-btn.active {\n background-color: #4281f5;\n color: white;\n border-color: #2d5ed9;\n }\n\n .refresh-btn {\n margin-top: 10px;\n cursor: pointer;\n padding: 8px 12px;\n background-color: #00c851;\n color: white;\n border: none;\n border-radius: 4px;\n font-weight: bold;\n transition: all 0.3s;\n }\n\n .refresh-btn:hover {\n background-color: #00a651;\n }\n `\n }\n\n @property({ type: String }) private activeType = 'temperature'\n @state() private map: any\n @state() private weatherLayer: any\n @state() private windLayer: any\n\n constructor() {\n super()\n }\n\n render() {\n return html`\n <div class=\"map-container\">\n <div id=\"map\"></div>\n\n <div class=\"controls\">\n <button\n class=\"weather-btn ${this.activeType === 'temperature' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('temperature')}\n >\n 온도\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'humidity' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('humidity')}\n >\n 습도\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'wind' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('wind')}\n >\n 바람\n </button>\n <button\n class=\"weather-btn ${this.activeType === 'precipitation' ? 'active' : ''}\"\n @click=${() => this._changeWeatherType('precipitation')}\n >\n 강수량\n </button>\n <button class=\"refresh-btn\" @click=${this._refreshWeatherMap}>새로고침</button>\n </div>\n </div>\n `\n }\n\n connectedCallback(): void {\n super.connectedCallback()\n\n requestAnimationFrame(() => {\n this._initMap()\n })\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback()\n\n this.map.removeLayer(this.weatherLayer)\n this.weatherLayer.dispose()\n this.map.dispose()\n\n this.map = null\n this.weatherLayer = null\n }\n\n private _initMap() {\n const lightOSM = new TileLayer({\n source: new XYZ({\n // url: 'https://{a-c}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',\n url: 'https://{a-c}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'\n // url: 'https://api.maptiler.com/maps/toner-lines/{z}/{x}/{y}.png?key=uHikv5CybbRm7cDLjRca'\n // attributions: '© MapTiler'\n attributions: '© OpenStreetMap contributors, © CartoDB'\n }),\n background: 'transparent',\n opacity: 1\n })\n\n const labelOSM = new TileLayer({\n source: new XYZ({\n url: 'https://{a-c}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}{r}.png'\n // url: 'https://{a-c}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png'\n }),\n background: 'transparent',\n opacity: 1\n })\n\n const vectorTile = new VectorTile({\n format: new MVT({\n layerName: 'countries',\n layers: ['countries']\n }),\n url: `/geo/tiles/{z}/{x}/{y}.mvt`,\n maxZoom: 10,\n projection: 'EPSG:3857'\n })\n\n const outlineLayer = new VectorTileLayer({\n source: vectorTile,\n style: new Style({\n stroke: new Stroke({\n color: '#858585',\n // color: '#484848',\n width: 1\n })\n })\n })\n\n console.log('🛠 벡터 타일 레이어:', outlineLayer, outlineLayer.getSource())\n\n /* heatmap */\n\n const generateHeatmapData = () => {\n const features: Feature[] = []\n for (let i = 0; i < 20000; i++) {\n // 더 많은 데이터 포인트 생성\n const lon = Math.random() * 360 - 180 // 전 세계 경도 (-180 ~ 180)\n const lat = Math.random() * 170 - 85 // 전 세계 위도 (-85 ~ 85)\n\n const weight = Math.random() * 1 // 가중치 (0 ~ 2)\n\n const feature = new Feature({\n geometry: new Point(fromLonLat([lon, lat])),\n weight: weight\n })\n features.push(feature)\n }\n return features\n }\n\n const vectorSource = new VectorSource({\n features: generateHeatmapData()\n })\n\n const heatmapLayer = new Heatmap({\n source: vectorSource,\n blur: 60, // 흐림 정도 증가\n radius: 60, // 점의 반경 증가\n weight: feature => feature.get('weight'), // 가중치 적용\n gradient: colors.map(c => `rgba(${c.join(',')})`), // 색상 적용\n opacity: 0.8\n })\n\n this.map = new Map({\n target: this.renderRoot.querySelector('#map') as HTMLElement,\n pixelRatio: Math.min(DEVICE_PIXEL_RATIO, 2),\n layers: [lightOSM, /*heatmapLayer, */ outlineLayer, labelOSM],\n view: new View({\n center: fromLonLat([127.5, 36.5]),\n zoom: 7.5\n })\n })\n\n this._addWeatherLayer()\n // this._addWindLayer()\n }\n\n private _addWindLayer() {\n if (this.windLayer) {\n this.map.removeLayer(this.windLayer)\n console.log('🚨 기존 날씨 레이어 제거')\n }\n\n const maxSpeed = 20\n\n const colorStops: (number | [number, number, number, number])[] = []\n for (let i = 0; i < colors.length; ++i) {\n colorStops.push((i * maxSpeed) / (colors.length - 1))\n colorStops.push(colors[i])\n }\n\n this.windLayer = new Flow({\n source: wind,\n maxSpeed,\n particles: 20000,\n style: {\n color: ['interpolate', ['linear'], ['get', 'speed'], ...colorStops]\n }\n })\n\n this.map.addLayer(this.windLayer)\n }\n\n private _addWeatherLayer() {\n if (this.weatherLayer) {\n this.map.removeLayer(this.weatherLayer)\n console.log('🚨 기존 날씨 레이어 제거')\n }\n\n // 날씨 타입별 최적 설정\n let opacity, blendMode\n\n switch (this.activeType) {\n case 'temperature':\n opacity = 0.75\n blendMode = 'normal'\n break\n case 'humidity':\n opacity = 0.65\n blendMode = 'multiply'\n break\n case 'wind':\n opacity = 0.8\n blendMode = 'screen'\n break\n case 'precipitation':\n opacity = 0.7\n blendMode = 'color-dodge'\n break\n default:\n opacity = 0.7\n blendMode = 'normal'\n }\n\n this.weatherLayer = new ImageLayer({\n source: new WebGLImageStatic({\n url: `/weather/map/${this.activeType}.png?refresh=${Date.now()}`, // 캐시 방지\n // 올바른 지구 투영 비율 사용\n imageExtent: [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892],\n interpolation: true,\n projection: 'EPSG:3857'\n // opacity: opacity,\n // blendMode: blendMode\n }),\n opacity // WebGLImageStatic에서 투명도 처리\n })\n\n this.map.getLayers().insertAt(1, this.weatherLayer)\n\n console.log('🚨 날씨 레이어 추가', this.map.getLayers())\n }\n\n private _changeWeatherType(type: string) {\n if (this.activeType === type) return\n this.activeType = type\n this._addWeatherLayer()\n }\n\n private _refreshWeatherMap() {\n this._addWeatherLayer()\n }\n}\n"]}