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

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.
@@ -8,25 +8,11 @@ import TileLayer from 'ol/layer/Tile.js';
8
8
  import { fromLonLat } from 'ol/proj.js';
9
9
  import 'ol/ol.css';
10
10
  import { MVT } from 'ol/format';
11
- import { DEVICE_PIXEL_RATIO } from 'ol/has.js';
12
- import Flow from 'ol/layer/Flow.js';
13
- import ImageLayer from 'ol/layer/Image';
14
- import WebGLImageStatic from '../ol-components/webgl-image-static';
15
11
  import { VectorTile, XYZ } from 'ol/source';
16
- import VectorSource from 'ol/source/Vector';
17
- import colormap from 'colormap';
18
- import Heatmap from 'ol/layer/Heatmap.js';
19
- import Feature from 'ol/Feature.js';
20
- import Point from 'ol/geom/Point.js';
21
- import { Stroke, Style } from 'ol/style.js';
22
- import wind from '../ol-components/wind.js';
23
12
  import VectorTileLayer from 'ol/layer/VectorTile';
24
- const colors = colormap({
25
- colormap: 'jet',
26
- nshades: 10,
27
- alpha: 0.3,
28
- format: 'rgba'
29
- });
13
+ import { Style, Stroke } from 'ol/style';
14
+ import { TemperatureLayer, PrecipitationLayer, initializeWorkerSystem } from '@hyperviz/weather';
15
+ import { WindDataTileLayer } from '../ol-components/wind-data-tile-layer.js';
30
16
  let WeatherPage = class WeatherPage extends PageView {
31
17
  static get styles() {
32
18
  return css `
@@ -98,6 +84,8 @@ let WeatherPage = class WeatherPage extends PageView {
98
84
  constructor() {
99
85
  super();
100
86
  this.activeType = 'temperature';
87
+ this.map = null;
88
+ this.weatherLayer = null;
101
89
  }
102
90
  render() {
103
91
  return html `
@@ -111,12 +99,6 @@ let WeatherPage = class WeatherPage extends PageView {
111
99
  >
112
100
  온도
113
101
  </button>
114
- <button
115
- class="weather-btn ${this.activeType === 'humidity' ? 'active' : ''}"
116
- @click=${() => this._changeWeatherType('humidity')}
117
- >
118
- 습도
119
- </button>
120
102
  <button
121
103
  class="weather-btn ${this.activeType === 'wind' ? 'active' : ''}"
122
104
  @click=${() => this._changeWeatherType('wind')}
@@ -136,28 +118,32 @@ let WeatherPage = class WeatherPage extends PageView {
136
118
  }
137
119
  connectedCallback() {
138
120
  super.connectedCallback();
121
+ initializeWorkerSystem();
139
122
  requestAnimationFrame(() => {
140
123
  this._initMap();
141
124
  });
142
125
  }
143
126
  disconnectedCallback() {
144
127
  super.disconnectedCallback();
145
- this.map.removeLayer(this.weatherLayer);
146
- this.weatherLayer.dispose();
147
- this.map.dispose();
128
+ if (this.weatherLayer) {
129
+ try {
130
+ this.weatherLayer.removeFromMap();
131
+ this.weatherLayer.dispose();
132
+ }
133
+ catch (e) {
134
+ console.error('Error disposing weather layer:', e);
135
+ }
136
+ }
137
+ if (this.map) {
138
+ this.map.dispose();
139
+ }
148
140
  this.map = null;
149
141
  this.weatherLayer = null;
150
142
  }
151
143
  _initMap() {
152
144
  const lightOSM = new TileLayer({
153
145
  source: new XYZ({
154
- // url: 'https://{a-c}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png',
155
- // url: 'https://{a-c}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',
156
146
  url: 'https://{a-c}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png',
157
- // url: 'https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
158
- // url: 'https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
159
- // url: 'https://api.maptiler.com/maps/toner-lines/{z}/{x}/{y}.png?key=uHikv5CybbRm7cDLjRca'
160
- // attributions: '© MapTiler'
161
147
  attributions: '© OpenStreetMap contributors, © CartoDB'
162
148
  }),
163
149
  background: 'transparent',
@@ -166,7 +152,6 @@ let WeatherPage = class WeatherPage extends PageView {
166
152
  const labelOSM = new TileLayer({
167
153
  source: new XYZ({
168
154
  url: 'https://{a-c}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}{r}.png'
169
- // url: 'https://{a-c}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png'
170
155
  }),
171
156
  background: 'transparent',
172
157
  opacity: 1
@@ -190,118 +175,157 @@ let WeatherPage = class WeatherPage extends PageView {
190
175
  })
191
176
  })
192
177
  });
193
- console.log('🛠 벡터 타일 레이어:', outlineLayer, outlineLayer.getSource());
194
- /* heatmap */
195
- const generateHeatmapData = () => {
196
- const features = [];
197
- for (let i = 0; i < 20000; i++) {
198
- // 더 많은 데이터 포인트 생성
199
- const lon = Math.random() * 360 - 180; // 전 세계 경도 (-180 ~ 180)
200
- const lat = Math.random() * 170 - 85; // 전 세계 위도 (-85 ~ 85)
201
- const weight = Math.random() * 1; // 가중치 (0 ~ 2)
202
- const feature = new Feature({
203
- geometry: new Point(fromLonLat([lon, lat])),
204
- weight: weight
205
- });
206
- features.push(feature);
207
- }
208
- return features;
209
- };
210
- const vectorSource = new VectorSource({
211
- features: generateHeatmapData()
212
- });
213
- const heatmapLayer = new Heatmap({
214
- source: vectorSource,
215
- blur: 60, // 흐림 정도 증가
216
- radius: 60, // 점의 반경 증가
217
- weight: feature => feature.get('weight'), // 가중치 적용
218
- gradient: colors.map(c => `rgba(${c.join(',')})`), // 색상 적용
219
- opacity: 0.8
220
- });
221
178
  this.map = new Map({
222
179
  target: this.renderRoot.querySelector('#map'),
223
- pixelRatio: Math.min(DEVICE_PIXEL_RATIO, 2),
224
- layers: [lightOSM, /*heatmapLayer, */ outlineLayer, labelOSM],
180
+ pixelRatio: Math.min(window.devicePixelRatio, 2),
181
+ layers: [lightOSM, outlineLayer, labelOSM],
225
182
  view: new View({
226
- center: fromLonLat([127.5, 36.5]),
227
- zoom: 7.5
183
+ center: fromLonLat([126.9779692, 37.5662952]),
184
+ zoom: 5
228
185
  })
229
186
  });
230
187
  this._addWeatherLayer();
231
- // this._addWindLayer()
232
- }
233
- _addWindLayer() {
234
- if (this.windLayer) {
235
- this.map.removeLayer(this.windLayer);
236
- console.log('🚨 기존 날씨 레이어 제거');
237
- }
238
- const maxSpeed = 20;
239
- const colorStops = [];
240
- for (let i = 0; i < colors.length; ++i) {
241
- colorStops.push((i * maxSpeed) / (colors.length - 1));
242
- colorStops.push(colors[i]);
243
- }
244
- this.windLayer = new Flow({
245
- source: wind,
246
- maxSpeed,
247
- particles: 20000,
248
- style: {
249
- color: ['interpolate', ['linear'], ['get', 'speed'], ...colorStops]
250
- }
251
- });
252
- this.map.addLayer(this.windLayer);
253
188
  }
254
189
  _addWeatherLayer() {
255
- if (this.weatherLayer) {
256
- this.map.removeLayer(this.weatherLayer);
257
- console.log('🚨 기존 날씨 레이어 제거');
258
- }
259
- // 날씨 타입별 최적 설정
260
- let opacity, blendMode;
190
+ if (!this.map)
191
+ return;
261
192
  switch (this.activeType) {
262
193
  case 'temperature':
263
- opacity = 0.75;
264
- blendMode = 'normal';
265
- break;
266
- case 'humidity':
267
- opacity = 0.65;
268
- blendMode = 'multiply';
194
+ this.weatherLayer = new TemperatureLayer();
269
195
  break;
270
196
  case 'wind':
271
- opacity = 0.8;
272
- blendMode = 'screen';
197
+ this.weatherLayer = new WindDataTileLayer();
273
198
  break;
274
199
  case 'precipitation':
275
- opacity = 0.7;
276
- blendMode = 'color-dodge';
200
+ this.weatherLayer = new PrecipitationLayer();
277
201
  break;
278
202
  default:
279
- opacity = 0.7;
280
- blendMode = 'normal';
203
+ this.weatherLayer = new TemperatureLayer();
204
+ }
205
+ if (this.weatherLayer) {
206
+ this.weatherLayer.addToMap(this.map);
281
207
  }
282
- this.weatherLayer = new ImageLayer({
283
- source: new WebGLImageStatic({
284
- url: `/weather/map/${this.activeType}.png?refresh=${Date.now()}`, // 캐시 방지
285
- // 올바른 지구 투영 비율 사용
286
- imageExtent: [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892],
287
- interpolation: true,
288
- projection: 'EPSG:3857'
289
- // opacity: opacity,
290
- // blendMode: blendMode
291
- }),
292
- opacity // WebGLImageStatic에서 투명도 처리
293
- });
294
- this.map.getLayers().insertAt(1, this.weatherLayer);
295
- console.log('🚨 날씨 레이어 추가', this.map.getLayers());
296
208
  }
209
+ /**
210
+ * 날씨 레이어를 변경하고 새 레이어를 맵에 추가
211
+ */
297
212
  _changeWeatherType(type) {
298
213
  if (this.activeType === type)
299
214
  return;
215
+ // 현재 활성 타입 저장
216
+ const previousType = this.activeType;
217
+ // 새로운 타입으로 변경
300
218
  this.activeType = type;
301
- this._addWeatherLayer();
219
+ console.log(`날씨 타입 변경 시작: ${previousType} -> ${type}`);
220
+ // 이전 레이어가 있을 경우 처리
221
+ if (this.map && this.weatherLayer) {
222
+ // 맵 참조 저장
223
+ const mapRef = this.map;
224
+ // 이전 레이어 참조 저장
225
+ const oldLayer = this.weatherLayer;
226
+ // 렌더링 참조 제거
227
+ this.weatherLayer = null;
228
+ // 이전 레이어 제거 처리
229
+ try {
230
+ console.log(`이전 레이어(${previousType}) 제거 시작`);
231
+ // 1. 레이어 시각적으로 즉시 숨김 (특히 WindDataTileLayer 경우 중요)
232
+ if (previousType === 'wind') {
233
+ if (oldLayer['layer'] && typeof oldLayer['layer'].setVisible === 'function') {
234
+ oldLayer['layer'].setVisible(false);
235
+ }
236
+ }
237
+ // 2. 컴포넌트에서 참조 제거 (렌더링 사이클에서 제외)
238
+ this.weatherLayer = null;
239
+ // 3. 비동기 처리: 렌더링 사이클이 완료된 후 실행
240
+ requestAnimationFrame(() => {
241
+ setTimeout(() => {
242
+ try {
243
+ // 레이어 참조가 남아있을 경우에만 제거 시도
244
+ if (oldLayer) {
245
+ oldLayer.removeFromMap();
246
+ // 약간의 지연 후 자원 해제
247
+ setTimeout(() => {
248
+ try {
249
+ oldLayer.dispose();
250
+ console.log(`이전 레이어(${previousType}) 완전히 제거됨`);
251
+ }
252
+ catch (e) {
253
+ console.warn('레이어 리소스 해제 중 오류:', e);
254
+ }
255
+ // 새 레이어 생성 시작
256
+ this._addNewLayer(type, mapRef);
257
+ }, 50); // dispose 지연
258
+ }
259
+ else {
260
+ // 레이어 참조가 없는 경우 즉시 새 레이어 생성
261
+ this._addNewLayer(type, mapRef);
262
+ }
263
+ }
264
+ catch (e) {
265
+ console.error('레이어 제거 중 오류 발생:', e);
266
+ // 오류 발생해도 새 레이어 생성 시도
267
+ this._addNewLayer(type, mapRef);
268
+ }
269
+ }, 100); // 레이어 제거 지연
270
+ });
271
+ }
272
+ catch (e) {
273
+ console.error('레이어 전환 중 오류:', e);
274
+ // 에러 상태에서도 새 레이어 추가 시도
275
+ this._addNewLayer(type, mapRef);
276
+ }
277
+ }
278
+ else {
279
+ // 기존 레이어가 없으면 바로 새 레이어 추가
280
+ this._addWeatherLayer();
281
+ }
282
+ }
283
+ /**
284
+ * 새 레이어를 안전하게 추가하는 헬퍼 메소드
285
+ */
286
+ _addNewLayer(type, mapRef) {
287
+ // 타입이 바뀌지 않았는지 확인
288
+ if (this.activeType !== type) {
289
+ console.log(`타입이 변경되어 레이어 생성 취소: ${this.activeType}로 변경됨`);
290
+ return;
291
+ }
292
+ console.log(`새 레이어(${type}) 생성 시작`);
293
+ try {
294
+ // 맵 참조 확인
295
+ if (!this.map && mapRef) {
296
+ this.map = mapRef;
297
+ }
298
+ if (this.map) {
299
+ // 새 레이어 생성 및 맵에 추가
300
+ this._addWeatherLayer();
301
+ console.log(`새 레이어(${type}) 생성 완료`);
302
+ }
303
+ else {
304
+ console.error('맵 참조 없음: 레이어를 추가할 수 없습니다');
305
+ }
306
+ }
307
+ catch (e) {
308
+ console.error('새 레이어 생성 중 오류:', e);
309
+ }
302
310
  }
303
311
  _refreshWeatherMap() {
304
- this._addWeatherLayer();
312
+ if (this.weatherLayer) {
313
+ try {
314
+ // 타입에 따라 적절한 메서드 호출
315
+ if (this.activeType === 'wind') {
316
+ ;
317
+ this.weatherLayer.updateLayer();
318
+ }
319
+ else {
320
+ // @hyperviz/weather 레이어
321
+ ;
322
+ this.weatherLayer.setWeatherData([]);
323
+ }
324
+ }
325
+ catch (e) {
326
+ console.error('Error refreshing weather layer:', e);
327
+ }
328
+ }
305
329
  }
306
330
  };
307
331
  __decorate([
@@ -316,10 +340,6 @@ __decorate([
316
340
  state(),
317
341
  __metadata("design:type", Object)
318
342
  ], WeatherPage.prototype, "weatherLayer", void 0);
319
- __decorate([
320
- state(),
321
- __metadata("design:type", Object)
322
- ], WeatherPage.prototype, "windLayer", void 0);
323
343
  WeatherPage = __decorate([
324
344
  customElement('weather-page'),
325
345
  __metadata("design:paramtypes", [])
@@ -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: '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"]}
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 { VectorTile, XYZ } from 'ol/source'\nimport VectorTileLayer from 'ol/layer/VectorTile'\nimport { Style, Stroke } from 'ol/style'\n\nimport { BaseWeatherLayer, TemperatureLayer, PrecipitationLayer, initializeWorkerSystem } from '@hyperviz/weather'\n\nimport { WindDataTileLayer } from '../ol-components/wind-data-tile-layer.js'\n\n// 통합 날씨 레이어 타입 (BaseWeatherLayer와 WindDataTileLayer 모두 포함)\ntype WeatherLayerType = BaseWeatherLayer | WindDataTileLayer\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: Map | null = null\n @state() private weatherLayer: WeatherLayerType | null = null\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 === '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 initializeWorkerSystem()\n requestAnimationFrame(() => {\n this._initMap()\n })\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback()\n if (this.weatherLayer) {\n try {\n this.weatherLayer.removeFromMap()\n this.weatherLayer.dispose()\n } catch (e) {\n console.error('Error disposing weather layer:', e)\n }\n }\n if (this.map) {\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/dark_nolabels/{z}/{x}/{y}{r}.png',\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 }),\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 this.map = new Map({\n target: this.renderRoot.querySelector('#map') as HTMLElement,\n pixelRatio: Math.min(window.devicePixelRatio, 2),\n layers: [lightOSM, outlineLayer, labelOSM],\n view: new View({\n center: fromLonLat([126.9779692, 37.5662952]),\n zoom: 5\n })\n })\n\n this._addWeatherLayer()\n }\n\n private _addWeatherLayer() {\n if (!this.map) return\n\n switch (this.activeType) {\n case 'temperature':\n this.weatherLayer = new TemperatureLayer()\n break\n case 'wind':\n this.weatherLayer = new WindDataTileLayer()\n break\n case 'precipitation':\n this.weatherLayer = new PrecipitationLayer()\n break\n default:\n this.weatherLayer = new TemperatureLayer()\n }\n\n if (this.weatherLayer) {\n this.weatherLayer.addToMap(this.map)\n }\n }\n\n /**\n * 날씨 레이어를 변경하고 새 레이어를 맵에 추가\n */\n private _changeWeatherType(type: string) {\n if (this.activeType === type) return\n\n // 현재 활성 타입 저장\n const previousType = this.activeType\n // 새로운 타입으로 변경\n this.activeType = type\n\n console.log(`날씨 타입 변경 시작: ${previousType} -> ${type}`)\n\n // 이전 레이어가 있을 경우 처리\n if (this.map && this.weatherLayer) {\n // 맵 참조 저장\n const mapRef = this.map\n\n // 이전 레이어 참조 저장\n const oldLayer = this.weatherLayer\n // 렌더링 참조 제거\n this.weatherLayer = null\n\n // 이전 레이어 제거 처리\n try {\n console.log(`이전 레이어(${previousType}) 제거 시작`)\n\n // 1. 레이어 시각적으로 즉시 숨김 (특히 WindDataTileLayer 경우 중요)\n if (previousType === 'wind') {\n if (oldLayer['layer'] && typeof oldLayer['layer'].setVisible === 'function') {\n oldLayer['layer'].setVisible(false)\n }\n }\n\n // 2. 컴포넌트에서 참조 제거 (렌더링 사이클에서 제외)\n this.weatherLayer = null\n\n // 3. 비동기 처리: 렌더링 사이클이 완료된 후 실행\n requestAnimationFrame(() => {\n setTimeout(() => {\n try {\n // 레이어 참조가 남아있을 경우에만 제거 시도\n if (oldLayer) {\n oldLayer.removeFromMap()\n\n // 약간의 지연 후 자원 해제\n setTimeout(() => {\n try {\n oldLayer.dispose()\n console.log(`이전 레이어(${previousType}) 완전히 제거됨`)\n } catch (e) {\n console.warn('레이어 리소스 해제 중 오류:', e)\n }\n\n // 새 레이어 생성 시작\n this._addNewLayer(type, mapRef)\n }, 50) // dispose 지연\n } else {\n // 레이어 참조가 없는 경우 즉시 새 레이어 생성\n this._addNewLayer(type, mapRef)\n }\n } catch (e) {\n console.error('레이어 제거 중 오류 발생:', e)\n // 오류 발생해도 새 레이어 생성 시도\n this._addNewLayer(type, mapRef)\n }\n }, 100) // 레이어 제거 지연\n })\n } catch (e) {\n console.error('레이어 전환 중 오류:', e)\n // 에러 상태에서도 새 레이어 추가 시도\n this._addNewLayer(type, mapRef)\n }\n } else {\n // 기존 레이어가 없으면 바로 새 레이어 추가\n this._addWeatherLayer()\n }\n }\n\n /**\n * 새 레이어를 안전하게 추가하는 헬퍼 메소드\n */\n private _addNewLayer(type: string, mapRef: Map | null): void {\n // 타입이 바뀌지 않았는지 확인\n if (this.activeType !== type) {\n console.log(`타입이 변경되어 레이어 생성 취소: ${this.activeType}로 변경됨`)\n return\n }\n\n console.log(`새 레이어(${type}) 생성 시작`)\n\n try {\n // 맵 참조 확인\n if (!this.map && mapRef) {\n this.map = mapRef\n }\n\n if (this.map) {\n // 새 레이어 생성 및 맵에 추가\n this._addWeatherLayer()\n console.log(`새 레이어(${type}) 생성 완료`)\n } else {\n console.error('맵 참조 없음: 레이어를 추가할 수 없습니다')\n }\n } catch (e) {\n console.error('새 레이어 생성 중 오류:', e)\n }\n }\n\n private _refreshWeatherMap() {\n if (this.weatherLayer) {\n try {\n // 타입에 따라 적절한 메서드 호출\n if (this.activeType === 'wind') {\n ;(this.weatherLayer as WindDataTileLayer).updateLayer()\n } else {\n // @hyperviz/weather 레이어\n ;(this.weatherLayer as BaseWeatherLayer).setWeatherData([])\n }\n } catch (e) {\n console.error('Error refreshing weather layer:', e)\n }\n }\n }\n}\n"]}