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

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.
@@ -78,17 +78,16 @@ export async function getGlobalWeatherImage(type: WeatherType, forceRefresh: boo
78
78
  // 객체인 경우 처리 (JSON.stringify된 버퍼일 수 있음)
79
79
  try {
80
80
  // JSON 문자열로 변환했다가 다시 파싱하면 배열로 변환될 수 있음
81
- cachedData = Object.keys(cachedDataJson)
82
- .filter(key => !isNaN(Number(key)))
83
- .sort((a, b) => Number(a) - Number(b))
84
- .map(key => cachedDataJson[key])
81
+ if ((cachedDataJson.type = 'Buffer')) {
82
+ cachedData = cachedDataJson.data
83
+ }
85
84
  } catch (conversionError) {
86
85
  console.error('캐시 데이터 변환 실패:', conversionError)
87
86
  }
88
87
  }
89
88
  }
90
89
 
91
- if (cachedData) {
90
+ if (cachedData && cachedData.length > 0) {
92
91
  console.log(`전체 날씨 이미지 캐시 사용 (타입: ${type})`)
93
92
  return cachedData
94
93
  }
@@ -98,7 +97,7 @@ export async function getGlobalWeatherImage(type: WeatherType, forceRefresh: boo
98
97
 
99
98
  var { value: weatherPoints } = (await cacheService.getFromCache(`weather-points`, {})) || {}
100
99
 
101
- if (!weatherPoints) {
100
+ if (!weatherPoints || weatherPoints.length === 0) {
102
101
  const weatherData = await getGlobalWeatherData(10)
103
102
 
104
103
  const flattenedWeatherPoints = weatherData.reduce((acc, area) => {
@@ -106,10 +105,27 @@ export async function getGlobalWeatherImage(type: WeatherType, forceRefresh: boo
106
105
  }, [])
107
106
 
108
107
  weatherPoints = flattenedWeatherPoints.map(point => {
108
+ let value
109
+ switch (type) {
110
+ case 'temperature':
111
+ value = point.temperature?.current
112
+ break
113
+ case 'humidity':
114
+ value = point.humidity
115
+ break
116
+ case 'wind':
117
+ value = point.wind?.speed
118
+ break
119
+ case 'precipitation':
120
+ value = 0
121
+ break // 현재 데이터 없음
122
+ }
123
+
109
124
  return {
110
125
  lat: point.location.latitude,
111
126
  lng: point.location.longitude,
112
- value: point.temperature?.current,
127
+ value,
128
+ // value: point.temperature?.current,
113
129
  description: point.weatherDescription,
114
130
  temperature: point.temperature?.current,
115
131
  humidity: point.humidity,
@@ -100,39 +100,70 @@ export async function renderWeatherMap(
100
100
  }
101
101
  }
102
102
 
103
- const imageData = ctx.createImageData(width, height)
104
-
105
- data.forEach(point => {
106
- const pixel = latLngToPixel(point.lat, point.lng)
107
- if (!pixel) return
108
-
109
- const color = valueToRGB(type, point)
110
- const index = (pixel.y * width + pixel.x) * 4
111
-
112
- if (index >= 0 && index < imageData.data.length) {
113
- imageData.data[index] = color.r
114
- imageData.data[index + 1] = color.g
115
- imageData.data[index + 2] = color.b
116
- imageData.data[index + 3] = color.a
117
- } else {
118
- console.warn('잘못된 인덱스:', index)
103
+ function calculateColor(type, value) {
104
+ switch (type) {
105
+ case 'temperature':
106
+ return { r: Math.min(255, Math.max(0, (value + 50) * 2.55)), g: 0, b: 0, a: 255 }
107
+ case 'humidity':
108
+ return { r: 0, g: Math.min(255, Math.max(0, value * 2.55)), b: 0, a: 255 }
109
+ case 'wind':
110
+ return { r: Math.min(255, Math.max(0, value * 5)), g: 0, b: 255, a: 255 }
111
+ case 'precipitation':
112
+ return { r: 0, g: 0, b: Math.min(255, Math.max(0, value * 2.55)), a: 255 }
113
+ default:
114
+ return { r: 255, g: 255, b: 255, a: 255 }
119
115
  }
116
+ }
120
117
 
121
- /* only for test */
122
- for (let dy = -18; dy <= 18; dy++) {
123
- for (let dx = -18; dx <= 18; dx++) {
124
- const fillIndex = ((pixel.y + dy) * width + (pixel.x + dx)) * 4
125
- if (fillIndex >= 0 && fillIndex < imageData.data.length) {
126
- imageData.data[fillIndex] = color.r
127
- imageData.data[fillIndex + 1] = color.g
128
- imageData.data[fillIndex + 2] = color.b
129
- imageData.data[fillIndex + 3] = color.a
118
+ function renderInterpolated(ctx, data, type, width, height) {
119
+ const imageData = ctx.createImageData(width, height)
120
+ const maxDistance = width * 0.05 // 최대 영향 거리 (이미지 너비의 5%)
121
+
122
+ // 모든 픽셀에 대해 계산
123
+ for (let y = 0; y < height; y++) {
124
+ for (let x = 0; x < width; x++) {
125
+ // 픽셀의 위/경도 계산
126
+ const lng = (x / width) * 360 - 180
127
+ const lat = 90 - (y / height) * 180
128
+
129
+ // 역거리 가중치 보간법
130
+ let weightedSum = 0
131
+ let totalWeight = 0
132
+
133
+ data.forEach(point => {
134
+ const pixelX = ((point.lng + 180) / 360) * width
135
+ const pixelY = ((90 - point.lat) / 180) * height
136
+ const distance = Math.sqrt(Math.pow(x - pixelX, 2) + Math.pow(y - pixelY, 2))
137
+
138
+ if (distance <= maxDistance && typeof point.value === 'number') {
139
+ const weight = 1 / (Math.pow(distance, 2) + 0.1)
140
+ weightedSum += point.value * weight
141
+ totalWeight += weight
142
+ }
143
+ })
144
+
145
+ const index = (y * width + x) * 4
146
+ if (totalWeight > 0) {
147
+ const value = weightedSum / totalWeight
148
+ const color = calculateColor(type, value)
149
+
150
+ imageData.data[index] = color.r
151
+ imageData.data[index + 1] = color.g
152
+ imageData.data[index + 2] = color.b
153
+ imageData.data[index + 3] = color.a
154
+ } else {
155
+ // 영향 범위 내 데이터 없음
156
+ imageData.data[index + 3] = 0 // 투명하게
130
157
  }
131
158
  }
132
159
  }
133
- })
134
160
 
135
- ctx.putImageData(imageData, 0, 0)
161
+ ctx.putImageData(imageData, 0, 0)
162
+ }
163
+
164
+ // 보간 렌더링 호출
165
+ renderInterpolated(ctx, data, type, width, height)
166
+
136
167
  console.log('Canvas 렌더링 완료')
137
168
  ;(window as any).renderComplete = true
138
169
  } catch (err) {