@uniai-fe/uds-templates 0.5.28 → 0.6.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/README.md +3 -6
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/cctv/styles/variables.scss +1 -1
- package/src/weather/_legacy/apis/index.ts +4 -0
- package/src/weather/_legacy/data/response.ts +36 -0
- package/src/weather/_legacy/hooks/index.ts +5 -0
- package/src/weather/{hooks → _legacy/hooks}/useOpenWeatherMap.ts +1 -1
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKorea.ts +4 -7
- package/src/weather/{hooks → _legacy/hooks}/useWeatherKoreaAlert.ts +2 -4
- package/src/weather/_legacy/types/api.ts +221 -0
- package/src/weather/_legacy/types/base.ts +70 -0
- package/src/weather/_legacy/types/index.ts +4 -0
- package/src/weather/_legacy/utils/index.ts +5 -0
- package/src/weather/_legacy/utils/locale.ts +28 -0
- package/src/weather/_legacy/utils/location.ts +139 -0
- package/src/weather/_legacy/utils/weather.ts +460 -0
- package/src/weather/apis/client.ts +339 -0
- package/src/weather/apis/index.ts +2 -4
- package/src/weather/apis/server.ts +264 -0
- package/src/weather/components/icon/Address.tsx +7 -0
- package/src/weather/components/icon/Weather.tsx +7 -6
- package/src/weather/components/page-header/Address.tsx +14 -0
- package/src/weather/components/page-header/Alert.tsx +17 -13
- package/src/weather/components/page-header/Container.tsx +12 -19
- package/src/weather/components/page-header/Forecast.tsx +21 -28
- package/src/weather/components/page-header/NextDays.tsx +10 -0
- package/src/weather/components/page-header/Today.tsx +86 -158
- package/src/weather/components/page-header/index.ts +5 -0
- package/src/weather/data/response.ts +4 -23
- package/src/weather/hooks/index.ts +3 -3
- package/src/weather/hooks/useWeather.ts +52 -0
- package/src/weather/hooks/useWeatherAlert.ts +35 -0
- package/src/weather/index.tsx +2 -2
- package/src/weather/jotai/coordinate.ts +4 -0
- package/src/weather/jotai/farm-idx.ts +4 -0
- package/src/weather/types/api.ts +393 -114
- package/src/weather/types/base.ts +15 -32
- package/src/weather/types/index.ts +0 -3
- package/src/weather/types/page-header.ts +118 -68
- package/src/weather/utils/index.ts +6 -4
- package/src/weather/utils/locale.ts +7 -69
- package/src/weather/utils/location.ts +6 -141
- package/src/weather/utils/weather.ts +53 -456
- package/src/weather/data/alert-regions-meta.json +0 -1286
- package/src/weather/data/weather-regions-meta.json +0 -9833
- package/src/weather/types/provider.ts +0 -34
- package/src/weather/utils/alert.ts +0 -30
- /package/src/weather/{apis → _legacy/apis}/korea/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/korea/server.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/client.ts +0 -0
- /package/src/weather/{apis → _legacy/apis}/open-weather-map/server.ts +0 -0
- /package/src/weather/{types → _legacy/types}/korea.ts +0 -0
- /package/src/weather/{types → _legacy/types}/open-weather-map.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/date-time.ts +0 -0
- /package/src/weather/{utils → _legacy/utils}/validate.ts +0 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
API_Res_WeatherKoreaBase,
|
|
3
|
+
API_Res_WeatherKoreaForecast,
|
|
4
|
+
API_Res_WeatherKoreaNextDays,
|
|
5
|
+
API_Res_WeatherKoreaNow,
|
|
6
|
+
KMA_Res_WeatherItem,
|
|
7
|
+
KMA_Res_WeatherForecast,
|
|
8
|
+
KMA_Res_WeatherNow,
|
|
9
|
+
OWM_Res_Weather_Forecast,
|
|
10
|
+
OWM_Res_Weather_Forecast_Item,
|
|
11
|
+
} from "../types";
|
|
12
|
+
import { getMoments } from "./date-time";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 날씨 도구; 현재 날씨/예보 값 추출
|
|
16
|
+
* @param {KMA_Res_WeatherItem[]} items - 현재 날씨 아이템
|
|
17
|
+
* @param {string} category - 카테고리
|
|
18
|
+
* @return {string | null} 현재 날씨 값
|
|
19
|
+
*/
|
|
20
|
+
const getValueNow = (
|
|
21
|
+
items: KMA_Res_WeatherItem[],
|
|
22
|
+
category: string,
|
|
23
|
+
): string | null => items.find(i => i.category === category)?.fcstValue || null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 날씨 도구; 예보 값 추출
|
|
27
|
+
* @param {KMA_Res_WeatherItem[]} items - 예보 아이템
|
|
28
|
+
* @param {string} date - 날짜
|
|
29
|
+
* @param {string} category - 카테고리
|
|
30
|
+
* @return {string | null} 예보 값
|
|
31
|
+
*/
|
|
32
|
+
const getValueForecast = (
|
|
33
|
+
items: KMA_Res_WeatherItem[],
|
|
34
|
+
date: string,
|
|
35
|
+
category: string,
|
|
36
|
+
): string | null =>
|
|
37
|
+
items.find(i => i.fcstDate === date && i.category === category)?.fcstValue ||
|
|
38
|
+
null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 강수량 코드
|
|
42
|
+
* @desc
|
|
43
|
+
* - 1(약한 비); 3mm/h 미만
|
|
44
|
+
* - 2(보통 비); 3mm/h 이상 15mm/h 미만
|
|
45
|
+
* - 3(강한 비); 15mm/h 이상
|
|
46
|
+
* @param {string} props.pty - 강수형태 코드
|
|
47
|
+
* @param {string} props.pcp - 1시간 강수량
|
|
48
|
+
* @return {string | null} 강수량 코드; 1(약한비), 2(보통비), 3(강한비)
|
|
49
|
+
*/
|
|
50
|
+
const getRainAmount = ({
|
|
51
|
+
pty,
|
|
52
|
+
pcp,
|
|
53
|
+
}: {
|
|
54
|
+
pty: string | null;
|
|
55
|
+
pcp: string | null;
|
|
56
|
+
}): string | null => {
|
|
57
|
+
if (!pty || !pcp) return null;
|
|
58
|
+
|
|
59
|
+
if (Number(pcp) < 3) return "1";
|
|
60
|
+
else if (3 <= Number(pcp) && Number(pcp) < 15) return "2";
|
|
61
|
+
else return "3";
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 강설량 코드
|
|
66
|
+
* @desc
|
|
67
|
+
* - 1(보통 눈); 1cm/h 미만
|
|
68
|
+
* - 2(많은 눈); 1cm/h 이상
|
|
69
|
+
* @param {string} props.pty - 강수형태 코드
|
|
70
|
+
* @param {string} props.snow - 1시간 신적설
|
|
71
|
+
* @return {string | null} 강설량 코드; 1(보통눈), 2(많은눈)
|
|
72
|
+
*/
|
|
73
|
+
const getSnowAmount = ({
|
|
74
|
+
pty,
|
|
75
|
+
snow,
|
|
76
|
+
}: {
|
|
77
|
+
pty: string | null;
|
|
78
|
+
snow: string | null;
|
|
79
|
+
}): string | null => {
|
|
80
|
+
if (!pty || !snow) return null;
|
|
81
|
+
|
|
82
|
+
if (Number(snow) < 1) return "1";
|
|
83
|
+
else return "2";
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 풍속 코드
|
|
88
|
+
* @desc
|
|
89
|
+
* - 1(약한 바람); 4m/s 미만
|
|
90
|
+
* - 2(약간 강한 바람); 4m/s 이상 9m/s 미만
|
|
91
|
+
* - 3(강한 바람); 9m/s 이상
|
|
92
|
+
* @param {string} props.wind - 풍속
|
|
93
|
+
* @return {string | null} 풍속 코드; 1(약한바람), 2(약간강한바람), 3(강한바람)
|
|
94
|
+
*/
|
|
95
|
+
const getWindSpeed = ({ wind }: { wind: string | null }): string | null => {
|
|
96
|
+
if (!wind) return null;
|
|
97
|
+
if (Number(wind) < 4) return "1";
|
|
98
|
+
else if (4 <= Number(wind) && Number(wind) < 9) return "2";
|
|
99
|
+
else return "3";
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 날씨 상태 코드
|
|
104
|
+
* @desc
|
|
105
|
+
* - sky-1(맑음)
|
|
106
|
+
* - sky-2(구름조금)
|
|
107
|
+
* - sky-3(구름많음)
|
|
108
|
+
* - sky-4(흐림)
|
|
109
|
+
* - drop-rain-shower(소나기)
|
|
110
|
+
* - drop-rain(비)
|
|
111
|
+
* - drop-rain-1(약한 비)
|
|
112
|
+
* - drop-rain-2(보통 비)
|
|
113
|
+
* - drop-rain-3(강한 비)
|
|
114
|
+
* - drop-rain-snow(비/눈)
|
|
115
|
+
* - drop-rain-snow-1(약한 비/눈)
|
|
116
|
+
* - drop-rain-snow-2(보통 비/눈)
|
|
117
|
+
* - drop-rain-snow-3(강한 비/눈)
|
|
118
|
+
* - drop-snow(눈)
|
|
119
|
+
* - drop-snow-1(보통 눈)
|
|
120
|
+
* - drop-snow-2(많은 눈)
|
|
121
|
+
* @param {string} props.sky - 하늘상태 코드; 1(맑음), 2(구름조금), 3(구름많음), 4(흐림)
|
|
122
|
+
* @param {string} props.pty - 강수형태 코드; 0(없음), 1(비), 2(비/눈), 3(눈), 4(소나기)
|
|
123
|
+
* @param {string} props.pcp - 1시간 강수량
|
|
124
|
+
* @param {string} props.snow - 1시간 신적설
|
|
125
|
+
*/
|
|
126
|
+
const getWeatherCondition = ({
|
|
127
|
+
sky,
|
|
128
|
+
pty,
|
|
129
|
+
pcp,
|
|
130
|
+
snow,
|
|
131
|
+
}: {
|
|
132
|
+
sky: string | null;
|
|
133
|
+
pty: string | null;
|
|
134
|
+
pcp: string | null;
|
|
135
|
+
snow: string | null;
|
|
136
|
+
}): { code: string | null; name: string | null } => {
|
|
137
|
+
const dropType = () => {
|
|
138
|
+
if (!pty || isNaN(Number(pty)) || pty === "0") return null;
|
|
139
|
+
switch (Number(pty)) {
|
|
140
|
+
case 1:
|
|
141
|
+
return "rain";
|
|
142
|
+
case 2:
|
|
143
|
+
return "rain-snow";
|
|
144
|
+
case 3:
|
|
145
|
+
return "snow";
|
|
146
|
+
case 4:
|
|
147
|
+
return "shower";
|
|
148
|
+
default:
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (!dropType()) {
|
|
154
|
+
const skyCode = sky === null ? null : sky;
|
|
155
|
+
const skyName = () => {
|
|
156
|
+
switch (Number(skyCode)) {
|
|
157
|
+
case 1:
|
|
158
|
+
return "맑음";
|
|
159
|
+
case 2:
|
|
160
|
+
return "구름조금";
|
|
161
|
+
case 3:
|
|
162
|
+
return "구름많음";
|
|
163
|
+
case 4:
|
|
164
|
+
return "흐림";
|
|
165
|
+
default:
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
code: `sky-${skyCode}`,
|
|
172
|
+
name: skyName(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const rainAmount = getRainAmount({ pty, pcp });
|
|
177
|
+
const snowAmount = getSnowAmount({ pty, snow });
|
|
178
|
+
|
|
179
|
+
const dropStrength = (): string | null => {
|
|
180
|
+
const type = dropType();
|
|
181
|
+
if (type === "rain") return rainAmount;
|
|
182
|
+
else if (type === "snow") return snowAmount;
|
|
183
|
+
else if (type === "rain-snow") {
|
|
184
|
+
if (rainAmount === null) return snowAmount;
|
|
185
|
+
if (snowAmount === null) return rainAmount;
|
|
186
|
+
// rainAmount와 snowAmount 중 큰 값을 선택
|
|
187
|
+
return Math.max(Number(rainAmount), Number(snowAmount)).toString();
|
|
188
|
+
}
|
|
189
|
+
// 소나기
|
|
190
|
+
else if (type === "shower") return type;
|
|
191
|
+
return null;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const dropCode = `drop-${dropType()}${dropStrength() ? `-${dropStrength()}` : ""}`;
|
|
195
|
+
const dropName = () => {
|
|
196
|
+
const type = dropType();
|
|
197
|
+
if (type === "rain") {
|
|
198
|
+
if (dropStrength() === null) return "비";
|
|
199
|
+
if (rainAmount === "1") return "약한 비";
|
|
200
|
+
else if (rainAmount === "2") return "보통 비";
|
|
201
|
+
else if (rainAmount === "3") return "강한 비";
|
|
202
|
+
} else if (type === "snow") {
|
|
203
|
+
if (dropStrength() === null) return "눈";
|
|
204
|
+
if (snowAmount === "1") return "보통 눈";
|
|
205
|
+
else if (snowAmount === "2") return "많은 눈";
|
|
206
|
+
} else if (type === "rain-snow") {
|
|
207
|
+
if (dropStrength() === null) return "비/눈";
|
|
208
|
+
if (dropStrength() === "1") return "약한 비/눈";
|
|
209
|
+
else if (dropStrength() === "2") return "보통 비/눈";
|
|
210
|
+
else if (dropStrength() === "3") return "강한 비/눈";
|
|
211
|
+
} else if (type === "shower") {
|
|
212
|
+
return "소나기";
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
code: dropCode,
|
|
219
|
+
name: dropName(),
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 날씨 도구; OpenWeatherMap 아이콘 코드를 weather icon 코드로 변환
|
|
225
|
+
* @param {string} [icon] OpenWeatherMap icon 코드
|
|
226
|
+
* @return {string} weather icon 코드
|
|
227
|
+
*/
|
|
228
|
+
export const getOpenWeatherMapConditionCode = (icon?: string): string => {
|
|
229
|
+
switch (icon) {
|
|
230
|
+
case "01d":
|
|
231
|
+
case "01n":
|
|
232
|
+
return "sky-1";
|
|
233
|
+
case "02d":
|
|
234
|
+
case "02n":
|
|
235
|
+
case "03d":
|
|
236
|
+
case "03n":
|
|
237
|
+
case "04d":
|
|
238
|
+
case "04n":
|
|
239
|
+
return "sky-2";
|
|
240
|
+
case "09d":
|
|
241
|
+
case "09n":
|
|
242
|
+
return "drop-rain-3";
|
|
243
|
+
case "10d":
|
|
244
|
+
case "10n":
|
|
245
|
+
return "drop-rain-1";
|
|
246
|
+
case "11d":
|
|
247
|
+
case "11n":
|
|
248
|
+
return "drop-rain-thunder";
|
|
249
|
+
case "13d":
|
|
250
|
+
case "13n":
|
|
251
|
+
return "drop-snow";
|
|
252
|
+
case "50d":
|
|
253
|
+
case "50n":
|
|
254
|
+
return "sky-4";
|
|
255
|
+
default:
|
|
256
|
+
return "sky-1";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const getLocalDateKey = (date: Date): string => {
|
|
261
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
262
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
263
|
+
|
|
264
|
+
return `${date.getFullYear()}-${month}-${day}`;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const getTimeZoneDateKey = (dateTimeSeconds: number, timezoneSeconds: number) =>
|
|
268
|
+
new Date((dateTimeSeconds + timezoneSeconds) * 1000)
|
|
269
|
+
.toISOString()
|
|
270
|
+
.slice(0, 10);
|
|
271
|
+
|
|
272
|
+
const getOpenWeatherMapDateKey = (
|
|
273
|
+
forecast?: OWM_Res_Weather_Forecast,
|
|
274
|
+
): string => {
|
|
275
|
+
const timezoneSeconds = forecast?.city?.timezone;
|
|
276
|
+
|
|
277
|
+
if (typeof timezoneSeconds === "number" && Number.isFinite(timezoneSeconds)) {
|
|
278
|
+
return getTimeZoneDateKey(Math.floor(Date.now() / 1000), timezoneSeconds);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return getLocalDateKey(new Date());
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const getForecastItemDateKey = (
|
|
285
|
+
item: OWM_Res_Weather_Forecast_Item,
|
|
286
|
+
timezoneSeconds = 0,
|
|
287
|
+
): string =>
|
|
288
|
+
Number.isFinite(item.dt)
|
|
289
|
+
? getTimeZoneDateKey(item.dt, timezoneSeconds)
|
|
290
|
+
: item.dt_txt?.slice(0, 10) || getLocalDateKey(new Date(item.dt * 1000));
|
|
291
|
+
|
|
292
|
+
const toOpenWeatherMapDayForecast = (
|
|
293
|
+
items: OWM_Res_Weather_Forecast_Item[],
|
|
294
|
+
): API_Res_WeatherKoreaNextDays | undefined => {
|
|
295
|
+
if (items.length === 0) return undefined;
|
|
296
|
+
|
|
297
|
+
const conditionItem =
|
|
298
|
+
items.find(item => item.dt_txt?.includes("12:00")) ||
|
|
299
|
+
items[Math.floor(items.length / 2)];
|
|
300
|
+
const temperatures = items
|
|
301
|
+
.flatMap(item => [item.main.temp_min, item.main.temp_max, item.main.temp])
|
|
302
|
+
.filter((temperature): temperature is number =>
|
|
303
|
+
Number.isFinite(temperature),
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
sky: null,
|
|
308
|
+
drop: null,
|
|
309
|
+
rainAmount: null,
|
|
310
|
+
snowAmount: null,
|
|
311
|
+
windSpeed: null,
|
|
312
|
+
condition: getOpenWeatherMapConditionCode(
|
|
313
|
+
conditionItem?.weather?.[0]?.icon,
|
|
314
|
+
),
|
|
315
|
+
conditionName: conditionItem?.weather?.[0]?.description || null,
|
|
316
|
+
max_temperature:
|
|
317
|
+
temperatures.length > 0 ? Math.round(Math.max(...temperatures)) : null,
|
|
318
|
+
min_temperature:
|
|
319
|
+
temperatures.length > 0 ? Math.round(Math.min(...temperatures)) : null,
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 날씨 도구; OpenWeatherMap 예보 응답을 page-header next days 형태로 변환
|
|
325
|
+
* @param {OWM_Res_Weather_Forecast} [forecast] OpenWeatherMap 예보 응답
|
|
326
|
+
* @return {{ day_1?: API_Res_WeatherKoreaNextDays; day_2?: API_Res_WeatherKoreaNextDays }} 내일/모레 예보
|
|
327
|
+
*/
|
|
328
|
+
export const getOpenWeatherMapNextDays = (
|
|
329
|
+
forecast?: OWM_Res_Weather_Forecast,
|
|
330
|
+
): {
|
|
331
|
+
day_1?: API_Res_WeatherKoreaNextDays;
|
|
332
|
+
day_2?: API_Res_WeatherKoreaNextDays;
|
|
333
|
+
} => {
|
|
334
|
+
if (!Array.isArray(forecast?.list)) return {};
|
|
335
|
+
|
|
336
|
+
const timezoneSeconds = forecast.city?.timezone ?? 0;
|
|
337
|
+
const todayKey = getOpenWeatherMapDateKey(forecast);
|
|
338
|
+
const grouped = forecast.list.reduce<
|
|
339
|
+
Record<string, OWM_Res_Weather_Forecast_Item[]>
|
|
340
|
+
>((days, item) => {
|
|
341
|
+
const dateKey = getForecastItemDateKey(item, timezoneSeconds);
|
|
342
|
+
if (dateKey === todayKey) return days;
|
|
343
|
+
|
|
344
|
+
days[dateKey] = [...(days[dateKey] || []), item];
|
|
345
|
+
return days;
|
|
346
|
+
}, {});
|
|
347
|
+
const [day1Key, day2Key] = Object.keys(grouped).sort();
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
day_1: day1Key ? toOpenWeatherMapDayForecast(grouped[day1Key]) : undefined,
|
|
351
|
+
day_2: day2Key ? toOpenWeatherMapDayForecast(grouped[day2Key]) : undefined,
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* 날씨 도구; OpenWeatherMap 예보 응답을 오늘 최고/최저 형태로 변환
|
|
357
|
+
* @param {OWM_Res_Weather_Forecast} [forecast] OpenWeatherMap 예보 응답
|
|
358
|
+
* @return {API_Res_WeatherKoreaNextDays | undefined} 오늘 최고/최저 예보
|
|
359
|
+
*/
|
|
360
|
+
export const getOpenWeatherMapTodayForecast = (
|
|
361
|
+
forecast?: OWM_Res_Weather_Forecast,
|
|
362
|
+
): API_Res_WeatherKoreaNextDays | undefined => {
|
|
363
|
+
if (!Array.isArray(forecast?.list)) return undefined;
|
|
364
|
+
|
|
365
|
+
const timezoneSeconds = forecast.city?.timezone ?? 0;
|
|
366
|
+
const todayKey = getOpenWeatherMapDateKey(forecast);
|
|
367
|
+
const todayItems = forecast.list.filter(
|
|
368
|
+
item => getForecastItemDateKey(item, timezoneSeconds) === todayKey,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
return toOpenWeatherMapDayForecast(todayItems);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
export function now(
|
|
375
|
+
outputResponse: API_Res_WeatherKoreaNow,
|
|
376
|
+
apiResponse: KMA_Res_WeatherNow,
|
|
377
|
+
): API_Res_WeatherKoreaNow {
|
|
378
|
+
const res: API_Res_WeatherKoreaNow = { ...outputResponse };
|
|
379
|
+
|
|
380
|
+
const items = apiResponse?.response?.body?.items?.item;
|
|
381
|
+
if (!items || !Array.isArray(items) || items.length === 0) return res;
|
|
382
|
+
|
|
383
|
+
const sky = getValueNow(items, "SKY"); // 없음
|
|
384
|
+
const pty = getValueNow(items, "PTY"); // 강수형태
|
|
385
|
+
const pcp = getValueNow(items, "RN1"); // 1시간 강수량 PCP없음
|
|
386
|
+
const snow = getValueNow(items, "SNO"); // 없음
|
|
387
|
+
const wind = getValueNow(items, "WSD");
|
|
388
|
+
|
|
389
|
+
const condition = getWeatherCondition({ sky, pty, pcp, snow });
|
|
390
|
+
|
|
391
|
+
const getDailyState: API_Res_WeatherKoreaBase = {
|
|
392
|
+
sky,
|
|
393
|
+
drop: pty,
|
|
394
|
+
rainAmount: getRainAmount({ pty, pcp }),
|
|
395
|
+
snowAmount: getSnowAmount({ pty, snow }),
|
|
396
|
+
windSpeed: getWindSpeed({ wind }),
|
|
397
|
+
condition: condition.code,
|
|
398
|
+
conditionName: condition.name,
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
res.today.temperature = getValueNow(items, "T1H");
|
|
402
|
+
res.today.humidity = getValueNow(items, "REH");
|
|
403
|
+
|
|
404
|
+
Object.assign(res.today, getDailyState);
|
|
405
|
+
|
|
406
|
+
return res;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export function forecast(
|
|
410
|
+
outputResponse: API_Res_WeatherKoreaForecast,
|
|
411
|
+
apiResponse: KMA_Res_WeatherForecast,
|
|
412
|
+
): API_Res_WeatherKoreaForecast {
|
|
413
|
+
const res: API_Res_WeatherKoreaForecast = { ...outputResponse };
|
|
414
|
+
|
|
415
|
+
const items = apiResponse?.response?.body?.items?.item;
|
|
416
|
+
if (!items || !Array.isArray(items) || items.length === 0) return res;
|
|
417
|
+
|
|
418
|
+
const { today, tomorrow, dayAfterTomorrow } = getMoments();
|
|
419
|
+
|
|
420
|
+
const getDailyState = (date: string): API_Res_WeatherKoreaBase => {
|
|
421
|
+
const sky = getValueForecast(items, date, "SKY");
|
|
422
|
+
const pty = getValueForecast(items, date, "PTY");
|
|
423
|
+
const pcp = getValueForecast(items, date, "PCP");
|
|
424
|
+
const snow = getValueForecast(items, date, "SNO");
|
|
425
|
+
const wind = getValueForecast(items, date, "WSD");
|
|
426
|
+
|
|
427
|
+
const condition = getWeatherCondition({ sky, pty, pcp, snow });
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
sky,
|
|
431
|
+
drop: pty,
|
|
432
|
+
rainAmount: getRainAmount({ pty, pcp }),
|
|
433
|
+
snowAmount: getSnowAmount({ pty, snow }),
|
|
434
|
+
windSpeed: getWindSpeed({ wind }),
|
|
435
|
+
condition: condition.code,
|
|
436
|
+
conditionName: condition.name,
|
|
437
|
+
};
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
res.today.temperature = getValueForecast(items, today, "TMP");
|
|
441
|
+
res.today.max_temperature = getValueForecast(items, today, "TMX");
|
|
442
|
+
res.today.min_temperature = getValueForecast(items, today, "TMN");
|
|
443
|
+
res.today.humidity = getValueForecast(items, today, "REH");
|
|
444
|
+
Object.assign(res.today, getDailyState(today));
|
|
445
|
+
|
|
446
|
+
res.day_1.max_temperature = getValueForecast(items, tomorrow, "TMX");
|
|
447
|
+
res.day_1.min_temperature = getValueForecast(items, tomorrow, "TMN");
|
|
448
|
+
Object.assign(res.day_1, getDailyState(tomorrow));
|
|
449
|
+
|
|
450
|
+
res.day_2.max_temperature = getValueForecast(items, dayAfterTomorrow, "TMX");
|
|
451
|
+
res.day_2.min_temperature = getValueForecast(items, dayAfterTomorrow, "TMN");
|
|
452
|
+
Object.assign(res.day_2, getDailyState(dayAfterTomorrow));
|
|
453
|
+
|
|
454
|
+
return res;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export const extractWeatherSummary = {
|
|
458
|
+
forecast,
|
|
459
|
+
now,
|
|
460
|
+
};
|