@angelrove/forecast-utils 1.1.1

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.
Files changed (64) hide show
  1. package/README.md +1046 -0
  2. package/dist/types/OpenMeteo/conf.d.ts +13 -0
  3. package/dist/types/OpenMeteo/current/fetchParams.d.ts +1 -0
  4. package/dist/types/OpenMeteo/current/transformer.d.ts +13 -0
  5. package/dist/types/OpenMeteo/current/useForecastCurrent.d.ts +14 -0
  6. package/dist/types/OpenMeteo/daily/fetchParams.d.ts +1 -0
  7. package/dist/types/OpenMeteo/daily/transformer.d.ts +9 -0
  8. package/dist/types/OpenMeteo/daily/useForecastDaily.d.ts +14 -0
  9. package/dist/types/OpenMeteo/helpers.d.ts +10 -0
  10. package/dist/types/OpenMeteo/hourly/fetchParams.d.ts +1 -0
  11. package/dist/types/OpenMeteo/hourly/useForecastHourly.d.ts +15 -0
  12. package/dist/types/OpenMeteo/weatherSymbol/lib/WeatherCodesEn.d.ts +43 -0
  13. package/dist/types/OpenMeteo/weatherSymbol/lib/WeatherCodesEs.d.ts +43 -0
  14. package/dist/types/OpenMeteo/weatherSymbol/lib/getWeatherCodeEntry.d.ts +7 -0
  15. package/dist/types/OpenMeteo/weatherSymbol/weatherSymbol.d.ts +13 -0
  16. package/dist/types/astronomy/moon/MoonCalc.d.ts +63 -0
  17. package/dist/types/astronomy/moon/parseBasicData.d.ts +8 -0
  18. package/dist/types/astronomy/sun/SunCalc.d.ts +73 -0
  19. package/dist/types/astronomy/sun/helpers.d.ts +0 -0
  20. package/dist/types/astronomy/timeZoneInfo.d.ts +10 -0
  21. package/dist/types/astronomy/timehelpers.d.ts +27 -0
  22. package/dist/types/astronomy/types.d.ts +43 -0
  23. package/dist/types/geolocation/getGeolocation.d.ts +12 -0
  24. package/dist/types/geolocation/lib/geolocation.d.ts +10 -0
  25. package/dist/types/geolocation/lib/geolocationCapacitor.d.ts +10 -0
  26. package/dist/types/geolocation/lib/reversegeocoding.d.ts +19 -0
  27. package/dist/types/index.d.ts +14 -0
  28. package/dist/types/utils/degreesToCompass.d.ts +11 -0
  29. package/dist/types/utils/warning.d.ts +46 -0
  30. package/dist/types/utils/wind/WindArrow.d.ts +32 -0
  31. package/dist/types/utils/wind/windArrowTx.d.ts +9 -0
  32. package/dist/types/utils/wind/windLevel.d.ts +29 -0
  33. package/package.json +40 -0
  34. package/src/OpenMeteo/conf.js +47 -0
  35. package/src/OpenMeteo/current/fetchParams.js +21 -0
  36. package/src/OpenMeteo/current/transformer.js +37 -0
  37. package/src/OpenMeteo/current/useForecastCurrent.js +34 -0
  38. package/src/OpenMeteo/daily/fetchParams.js +9 -0
  39. package/src/OpenMeteo/daily/transformer.js +21 -0
  40. package/src/OpenMeteo/daily/useForecastDaily.js +38 -0
  41. package/src/OpenMeteo/helpers.js +19 -0
  42. package/src/OpenMeteo/hourly/fetchParams.js +12 -0
  43. package/src/OpenMeteo/hourly/useForecastHourly.js +42 -0
  44. package/src/OpenMeteo/weatherSymbol/lib/WeatherCodesEn.js +89 -0
  45. package/src/OpenMeteo/weatherSymbol/lib/WeatherCodesEs.js +81 -0
  46. package/src/OpenMeteo/weatherSymbol/lib/getWeatherCodeEntry.js +12 -0
  47. package/src/OpenMeteo/weatherSymbol/weatherSymbol.js +60 -0
  48. package/src/astronomy/moon/MoonCalc.js +171 -0
  49. package/src/astronomy/moon/parseBasicData.js +38 -0
  50. package/src/astronomy/sun/SunCalc.js +167 -0
  51. package/src/astronomy/sun/helpers.js +0 -0
  52. package/src/astronomy/timeZoneInfo.js +89 -0
  53. package/src/astronomy/timehelpers.js +69 -0
  54. package/src/astronomy/types.js +30 -0
  55. package/src/geolocation/getGeolocation.js +49 -0
  56. package/src/geolocation/lib/geolocation.js +44 -0
  57. package/src/geolocation/lib/geolocationCapacitor.js +21 -0
  58. package/src/geolocation/lib/reversegeocoding.js +109 -0
  59. package/src/index.js +38 -0
  60. package/src/utils/degreesToCompass.js +51 -0
  61. package/src/utils/warning.js +130 -0
  62. package/src/utils/wind/WindArrow.jsx +53 -0
  63. package/src/utils/wind/windArrowTx.js +24 -0
  64. package/src/utils/wind/windLevel.js +49 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Reverse Geocoding with GoogleMaps API
3
+ *
4
+ * @see https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding?hl=es-419
5
+ * @see https://maps.googleapis.com/maps/api/geocode/json?latlng=36.7248,-4.541&key=[api_key]&result_type=sublocality
6
+ *
7
+ * @param {number} latitude - Latitude of the location.
8
+ * @param {number} longitude - Longitude of the location.
9
+ * @param {string} api_key - Google Maps API key.
10
+ * @returns {Promise<{ sublocality: string; locality: string; country: string; country_short: string; formatted_address: string; }>} - The address components.
11
+ * @throws {Error} - If the API key is invalid or the request fails.
12
+ */
13
+ export default async function reverseGeocoding(latitude, longitude, api_key) {
14
+ // Validate --
15
+ if (!api_key) {
16
+ console.error("ReverseGeocoding: invalid api key");
17
+ throw new Error("ReverseGeocoding: invalid api key");
18
+ }
19
+ if (!latitude || !longitude) {
20
+ console.error("ReverseGeocoding: invalid coordinates");
21
+ throw new Error("ReverseGeocoding: invalid coordinates");
22
+ }
23
+
24
+ // API URL --
25
+ const host =
26
+ "https://maps.googleapis.com/maps/api/geocode/json?key=" +
27
+ api_key +
28
+ "&latlng=" +
29
+ latitude +
30
+ "," +
31
+ longitude;
32
+
33
+ /*
34
+ * 'result_type' param:
35
+ * street_address,
36
+ * colloquial_area,
37
+ * locality,
38
+ * sublocality,
39
+ * administrative_area_level_1,
40
+ * administrative_area_level_2
41
+ */
42
+ const urlApi = host + "&result_type=sublocality";
43
+
44
+ // Fetch data --
45
+ const response = await fetch(urlApi);
46
+ devLog("Geocoding", urlApi);
47
+
48
+ // Check response --
49
+ if (!response.ok) {
50
+ const message =
51
+ "Failed to Reverse Geocoding: " +
52
+ response.status +
53
+ " " +
54
+ response.statusText;
55
+ console.error(message);
56
+ throw new Error(message);
57
+ }
58
+ const data = await response.json();
59
+
60
+ // No results --
61
+ if (data.status === "ZERO_RESULTS") {
62
+ // plus_code: { compound_code: 'G4F9+72P Marbella, Spain', }
63
+ const country = data.plus_code.compound_code.split(",")[1];
64
+ const address = data.plus_code.compound_code.split(" ").slice(1).join(" ");
65
+ // throw new Error('Failed to Reverse Geocoding');
66
+
67
+ return {
68
+ sublocality: "",
69
+ locality: "",
70
+ country: country,
71
+ country_short: country,
72
+ formatted_address: address,
73
+ };
74
+ }
75
+
76
+ if (data.status !== "OK") {
77
+ let message = "Failed to Reverse Geocoding: " + data.status;
78
+ if (data.error_message) {
79
+ message += " " + data.error_message;
80
+ }
81
+ console.error(message);
82
+ throw new Error(message);
83
+ }
84
+
85
+ // Formatted address --
86
+ let formatted_address = data.results[0].formatted_address.split(",");
87
+ formatted_address.pop();
88
+ formatted_address = formatted_address.join(",");
89
+
90
+ // Return --
91
+ return {
92
+ sublocality: data.results[0].address_components[0].long_name,
93
+ locality: data.results[0].address_components[1].long_name,
94
+ country: data.results[0].address_components[4].long_name,
95
+ country_short: data.results[0].address_components[4].short_name,
96
+ formatted_address: formatted_address,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * @param {string} title
102
+ * @param {any} url
103
+ */
104
+ function devLog(title, url) {
105
+ // @ts-ignore
106
+ if (import.meta.env.MODE === "development") {
107
+ console.log(`> fetch [${title}]:`, url);
108
+ }
109
+ }
package/src/index.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @author: Jose Angel Romero Vegas
3
+ * @license: MIT
4
+ * @description:
5
+ * Several utility libraries for the Tierracolora project:
6
+ * - Forecast API (OpenWeather)
7
+ * - Astronomy utilities
8
+ *
9
+ * I use public libraries for the following:
10
+ * - Astronomy calculations: suncalc3
11
+ */
12
+
13
+ // Astronomy
14
+ export { default as MoonCalc } from "./astronomy/moon/MoonCalc.js";
15
+ export { default as SunCalc } from "./astronomy/sun/SunCalc.js";
16
+ export {
17
+ dateFormat,
18
+ getLocalTimeFromTz,
19
+ nowString,
20
+ timeString,
21
+ } from "./astronomy/timehelpers.js";
22
+ export { getLocalTimeInfo as getLocalTime } from "./astronomy/timeZoneInfo.js";
23
+
24
+ // Geolocation
25
+ export { getGeolocation } from "./geolocation/getGeolocation.js";
26
+
27
+ // OpenMeteo
28
+ export { useForecastCurrent } from "./OpenMeteo/current/useForecastCurrent.js";
29
+ export { useForecastDaily } from "./OpenMeteo/daily/useForecastDaily.js";
30
+ export { useForecastHourly } from "./OpenMeteo/hourly/useForecastHourly.js";
31
+ export { weatherSymbol } from "./OpenMeteo/weatherSymbol/weatherSymbol.js";
32
+
33
+ // utils
34
+ export { degreesToCompass } from "./utils/degreesToCompass.js";
35
+ export { getWarning, getWarningByDays } from "./utils/warning.js";
36
+ export { WindArrow } from "./utils/wind/WindArrow.jsx";
37
+ export { windArrowTx } from "./utils/wind/windArrowTx.js";
38
+ export { getWindLevel } from "./utils/wind/windLevel.js";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+
5
+ const directions = [
6
+ { short: "N", full: "North" },
7
+ { short: "NE", full: "Northeast" },
8
+ { short: "E", full: "East" },
9
+ { short: "SE", full: "Southeast" },
10
+ { short: "S", full: "South" },
11
+ { short: "SW", full: "Southwest" },
12
+ { short: "W", full: "West" },
13
+ { short: "NW", full: "Northwest" },
14
+ ];
15
+
16
+ const directionsEs = [
17
+ { short: "N", full: "Norte" },
18
+ { short: "NE", full: "Noreste" },
19
+ { short: "E", full: "Este" },
20
+ { short: "SE", full: "Sureste" },
21
+ { short: "S", full: "Sur" },
22
+ { short: "SO", full: "Suroeste" },
23
+ { short: "O", full: "Oeste" },
24
+ { short: "NO", full: "Noroeste" },
25
+ ];
26
+
27
+ /**
28
+ * Convert degrees to compass designation
29
+ *
30
+ * @param {number} degrees
31
+ * @param {string} language - Language code ("en-US", "es-ES", "auto")
32
+ * @returns {{short: string, full: string}}
33
+ */
34
+ export function degreesToCompass(degrees, language = "auto") {
35
+ if (degrees == null) return { short: "?", full: "?" };
36
+
37
+ const index = Math.round(degrees / 45) % 8;
38
+
39
+ // auto detect language
40
+ let lang = language;
41
+ if (language === "auto") {
42
+ lang = navigator.language;
43
+ }
44
+
45
+ // Parse the language
46
+ if (lang.startsWith("es")) {
47
+ return directionsEs[index];
48
+ }
49
+
50
+ return directions[index];
51
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+
5
+ // AEMET (12 horas)
6
+
7
+ /**
8
+ * @typedef {Object} AlertLevel
9
+ * @property {number} levelNum - The alert level number.
10
+ * @property {string} level - The alert level string (e.g., "red", "orange", "yellow").
11
+ * @property {number} precipitation - The precipitation threshold for the alert level.
12
+ * @property {number} showers - The showers threshold for the alert level.
13
+ */
14
+
15
+ /**
16
+ * Alert levels for rain and showers
17
+ *
18
+ * @type {AlertLevel[]}
19
+ */
20
+ const ALERT_LEVEL = [
21
+ { levelNum: 3, level: "red", precipitation: 120, showers: 60 },
22
+ { levelNum: 2, level: "orange", precipitation: 80, showers: 30 },
23
+ { levelNum: 1, level: "yellow", precipitation: 50, showers: 18 },
24
+ ];
25
+
26
+ /**
27
+ * Get warning by today and tomorrow
28
+ *
29
+ * @param {number} precipitationSumToday
30
+ * @param {number} precipitationSumTomorrow
31
+ * @param {number} showersSumToday
32
+ * @param {number} showersSumTomorrow
33
+ * @returns {{ levelNum: number, level: string, message: string, day: number } | null}
34
+ */
35
+ export function getWarningByDays(
36
+ precipitationSumToday,
37
+ precipitationSumTomorrow,
38
+ showersSumToday,
39
+ showersSumTomorrow,
40
+ ) {
41
+ // Today
42
+ const wToday = getWarning(precipitationSumToday, showersSumToday);
43
+ if (wToday) {
44
+ return {
45
+ ...wToday,
46
+ day: 0,
47
+ };
48
+ }
49
+
50
+ // Tomorrow
51
+ const wTomorrow = getWarning(precipitationSumTomorrow, showersSumTomorrow);
52
+ if (wTomorrow) {
53
+ return {
54
+ ...wTomorrow,
55
+ day: 1,
56
+ };
57
+ }
58
+
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Get warning by precipitation and showers
64
+ *
65
+ * @export
66
+ * @param {number} precipitation
67
+ * @param {number} showers
68
+ * @returns {{ levelNum: number, level: string, message: string } | null}
69
+ */
70
+ export function getWarning(precipitation, showers) {
71
+ const warningRain = getWarningRain(precipitation);
72
+ const warningShowers = getWarningShowers(showers);
73
+ // console.log(warningRain);
74
+ // console.log(warningShowers);
75
+
76
+ if (warningRain && warningShowers) {
77
+ return warningRain.levelNum > warningShowers.levelNum
78
+ ? warningRain
79
+ : warningShowers;
80
+ }
81
+ if (warningRain) {
82
+ return warningRain;
83
+ }
84
+ if (warningShowers) {
85
+ return warningShowers;
86
+ }
87
+
88
+ return null;
89
+ }
90
+
91
+ //----------------------------------------------------------------
92
+ // PRIVATE FUNCTIONS
93
+ //----------------------------------------------------------------
94
+ /**
95
+ * Get warning by rain
96
+ *
97
+ * @param {number} precipitation
98
+ * @returns {{ levelNum: number, level: string, message: string } | null}
99
+ */
100
+ function getWarningRain(precipitation) {
101
+ for (let i = 0; i < ALERT_LEVEL.length; i++) {
102
+ if (precipitation >= ALERT_LEVEL[i].precipitation) {
103
+ return {
104
+ levelNum: ALERT_LEVEL[i].levelNum,
105
+ level: ALERT_LEVEL[i].level,
106
+ message: "Por precipitacion acumulada en 24 horas.",
107
+ };
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * Get warning by showers
115
+ *
116
+ * @param {number} showers
117
+ * @returns {{ levelNum: number, level: string, message: string } | null}
118
+ */
119
+ function getWarningShowers(showers) {
120
+ for (let i = 0; i < ALERT_LEVEL.length; i++) {
121
+ if (showers >= ALERT_LEVEL[i].showers) {
122
+ return {
123
+ levelNum: ALERT_LEVEL[i].levelNum,
124
+ level: ALERT_LEVEL[i].level,
125
+ message: "Chubascos intensos.",
126
+ };
127
+ }
128
+ }
129
+ return null;
130
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @module utils:WindArrow
3
+ */
4
+
5
+ /**
6
+ * @typedef {Object} WindArrowProps
7
+ * @property {number} props.deg - Wind direction in degrees
8
+ * @property {string} [props.size] - Size of the arrow (Tailwind CSS size: 'size-10')
9
+ * @property {number} [props.strokeWidth] - Stroke width of the arrow (1 to 6)
10
+ * @property {string} [props.className]
11
+ */
12
+
13
+ /**
14
+ * Wind direction arrow component (svg image).
15
+ *
16
+ * @component
17
+ * @param {WindArrowProps} props
18
+ */
19
+ export function WindArrow({
20
+ deg,
21
+ size = "size-5",
22
+ strokeWidth = 1.9,
23
+ className = "",
24
+ }) {
25
+ if (deg == null) {
26
+ // console.warn("WindArrow: deg is undefined");
27
+ /* @ts-ignore */
28
+ return <div>deg?</div>;
29
+ }
30
+
31
+ // Render arrow ---
32
+ return (
33
+ /* @ts-ignore */
34
+ <svg
35
+ className={`${size} ${className}`}
36
+ style={{ transform: `rotate(${deg}deg)` }}
37
+ fill="none"
38
+ stroke="currentColor"
39
+ strokeWidth={strokeWidth}
40
+ viewBox="0 0 24 24"
41
+ xmlns="http://www.w3.org/2000/svg"
42
+ aria-hidden="true"
43
+ >
44
+ {/* @ts-ignore */}
45
+ <path
46
+ strokeLinecap="round"
47
+ strokeLinejoin="round"
48
+ d="M19.5 13.5L12 21m0 0l-7.5-7.5M12 21V3"
49
+ />
50
+ {/* @ts-ignore */}
51
+ </svg>
52
+ );
53
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+
5
+ /**
6
+ * Convert wind direction in degrees to an arrow representation.
7
+ *
8
+ * @param {number} deg - Wind direction in degrees.
9
+ */
10
+ export function windArrowTx(deg) {
11
+ if (deg == null) {
12
+ console.warn("WindDegToArrow.jsx: deg is undefined");
13
+ return "deg?";
14
+ }
15
+
16
+ if (deg < 22.5) return "↓";
17
+ if (deg < 67.5) return "↙";
18
+ if (deg < 112.5) return "←";
19
+ if (deg < 157.5) return "↖";
20
+ if (deg < 202.5) return "↑";
21
+ if (deg < 247.5) return "↗";
22
+ if (deg < 292.5) return "→";
23
+ if (deg < 337.5) return "↘";
24
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @module utils
3
+ */
4
+
5
+ /**
6
+ * @typedef {Object} WindLevel
7
+ * @property {number} id - Level ID
8
+ * @property {number} speed - Minimum wind speed for this level
9
+ * @property {string} color - Color representing this level
10
+ * @property {string} tx - Text representing this level
11
+ * @property {string} txEn - Text representing this level
12
+ */
13
+
14
+ /**
15
+ * Ordered list of wind levels.
16
+ *
17
+ * @type {WindLevel[]}
18
+ */
19
+ const WIND_LEVELS = [
20
+ { id: 1, speed: 8, color: "green", tx: "Brisa", txEn: "Breeze" },
21
+ { id: 2, speed: 16, color: "orange", tx: "Ligero", txEn: "Light" },
22
+ { id: 3, speed: 23, color: "red", tx: "Moderado", txEn: "Moderate" },
23
+ { id: 4, speed: 30, color: "red", tx: "Fuerte", txEn: "Strong" },
24
+ { id: 5, speed: 50, color: "fuchsia", tx: "Muy fuerte", txEn: "Very strong" },
25
+ {
26
+ id: 6,
27
+ speed: Number.POSITIVE_INFINITY,
28
+ color: "fuchsia",
29
+ tx: "Peligroso",
30
+ txEn: "Dangerous",
31
+ },
32
+ ];
33
+
34
+ /**
35
+ * Return the wind level based on the speed.
36
+ *
37
+ * @param {number} speed Wind speed in km/h
38
+ * @return {WindLevel | null} Wind level object or null if speed is null
39
+ */
40
+ export function getWindLevel(speed) {
41
+ if (typeof speed !== "number") return null;
42
+
43
+ const theSpeed = Math.round(speed);
44
+
45
+ for (const level of WIND_LEVELS) {
46
+ if (theSpeed < level.speed) return level;
47
+ }
48
+ return WIND_LEVELS[WIND_LEVELS.length - 1]; // fallback defensivo
49
+ }