@aeroswarm/weather 0.1.3 → 0.1.5
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/GRID_STANDARD.md +68 -0
- package/README.md +8 -0
- package/dist/grid.d.ts +12 -0
- package/dist/grid.d.ts.map +1 -1
- package/dist/gridSeries.d.ts +59 -0
- package/dist/gridSeries.d.ts.map +1 -0
- package/dist/gridSeries.test.d.ts +2 -0
- package/dist/gridSeries.test.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +301 -21
- package/dist/openMeteo.d.ts +40 -0
- package/dist/openMeteo.d.ts.map +1 -0
- package/dist/store.d.ts +21 -1
- package/dist/store.d.ts.map +1 -1
- package/package.json +3 -2
package/GRID_STANDARD.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# AeroSwarm Weather Grid (`aeroswarm.weather-grid`) v1
|
|
2
|
+
|
|
3
|
+
One JSON format for gridded weather forecasts, whatever the source (a model API, a converted GRIB
|
|
4
|
+
file, your own service). A source only has to produce this document; the renderer and the timeline
|
|
5
|
+
never need to know where the data came from.
|
|
6
|
+
|
|
7
|
+
- Read: `parseWeatherGridSeries(json)` (strict, throws a message naming the bad part).
|
|
8
|
+
- Write: `serializeWeatherGridSeries(series)`.
|
|
9
|
+
- Use: `seriesFieldsAt(series, time)` blends the two nearest frames; `null` outside the time range.
|
|
10
|
+
- Open-Meteo: `latticePoints(lattice)` gives the points to request, `weatherGridFromOpenMeteo(response,
|
|
11
|
+
lattice)` turns the answer into this format. Nothing here calls the network.
|
|
12
|
+
|
|
13
|
+
## Document
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"standard": "aeroswarm.weather-grid",
|
|
18
|
+
"version": 1,
|
|
19
|
+
"source": {
|
|
20
|
+
"name": "open-meteo",
|
|
21
|
+
"model": "icon_seamless",
|
|
22
|
+
"runAt": "2026-11-02T00:00:00Z",
|
|
23
|
+
"generatedAt": "2026-11-02T04:12:00Z"
|
|
24
|
+
},
|
|
25
|
+
"grid": { "west": 105, "south": -11, "east": 118, "north": -5, "nx": 14, "ny": 13 },
|
|
26
|
+
"times": ["2026-11-02T04:00:00Z", "2026-11-02T05:00:00Z"],
|
|
27
|
+
"fields": {
|
|
28
|
+
"windU": [[/* nx*ny values for time 0 */], [/* time 1 */]],
|
|
29
|
+
"windV": [[...], [...]],
|
|
30
|
+
"cloud": [[...], [...]],
|
|
31
|
+
"rain": [[...], [...]],
|
|
32
|
+
"temperature": [[...], [...]]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Rules
|
|
38
|
+
|
|
39
|
+
| Part | Rule |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `standard`, `version` | Exactly `"aeroswarm.weather-grid"` and `1`. |
|
|
42
|
+
| `source.name` | Required. `model`, `runAt`, `generatedAt` are optional (ISO 8601). |
|
|
43
|
+
| `grid` | `west < east`, `south < north`, `nx` and `ny` integers >= 2. Cells sit on the lattice points: column 0 is `west`, the last column is `east`; row 0 is `north`, the last row is `south`. |
|
|
44
|
+
| `times` | At least one ISO 8601 time in UTC, strictly increasing. |
|
|
45
|
+
| `fields` | At least one quantity. Each present quantity has exactly one array per time, and each array has exactly `nx * ny` values in row-major order (row 0 first). `null` means no data. |
|
|
46
|
+
| `windU` / `windV` | Both or neither. m/s. `windU` blows towards the east, `windV` towards the north. |
|
|
47
|
+
| `cloud` | Total cloud cover, percent 0-100. |
|
|
48
|
+
| `rain` | Precipitation rate, mm per hour. |
|
|
49
|
+
| `temperature` | Degrees Celsius. |
|
|
50
|
+
|
|
51
|
+
Wind is stored as components, not speed and direction, so frames can be blended and interpolated
|
|
52
|
+
without wrapping problems. To convert: `u = -speed * sin(from)`, `v = -speed * cos(from)`, where
|
|
53
|
+
`from` is the direction the wind comes FROM (meteorological, degrees from north).
|
|
54
|
+
|
|
55
|
+
## Versioning
|
|
56
|
+
|
|
57
|
+
A change that breaks readers (a different layout, a changed unit) raises `version`. Adding an optional
|
|
58
|
+
quantity or an optional `source` field does not. A reader must reject a `version` it does not know
|
|
59
|
+
instead of guessing.
|
|
60
|
+
|
|
61
|
+
## Producing it
|
|
62
|
+
|
|
63
|
+
- **Open-Meteo (in an app, not in this package):** request the lattice points with
|
|
64
|
+
`hourly=wind_speed_10m,wind_direction_10m,cloud_cover,precipitation,temperature_2m`,
|
|
65
|
+
`wind_speed_unit=ms`, `timezone=GMT`, then call `weatherGridFromOpenMeteo`.
|
|
66
|
+
- **GRIB:** convert to JSON on the server (`grib2json` or `wgrib2`), then build the document from the
|
|
67
|
+
two wind records and any scalar fields.
|
|
68
|
+
- **Your own service:** return this document directly; the app loads it from a URL or a file.
|
package/README.md
CHANGED
|
@@ -38,3 +38,11 @@ representation around the airport, not the real position of clouds or rain.
|
|
|
38
38
|
- `sampleWind`, `sampleScalar` (bilinear) and `blendWindGrids` (between two forecast hours).
|
|
39
39
|
|
|
40
40
|
Row 0 is north, wind is in m/s (`u` east, `v` north), cells without data are NaN.
|
|
41
|
+
|
|
42
|
+
## Gridded forecast standard
|
|
43
|
+
|
|
44
|
+
`GRID_STANDARD.md` defines `aeroswarm.weather-grid` v1: one JSON format (grid + times + wind/cloud/rain/
|
|
45
|
+
temperature) for any model source. `parseWeatherGridSeries`, `serializeWeatherGridSeries`,
|
|
46
|
+
`seriesFieldsAt`, and the Open-Meteo adapter (`latticePoints`, `weatherGridFromOpenMeteo`) are in the
|
|
47
|
+
package; the network call stays in the app.
|
|
48
|
+
|
package/dist/grid.d.ts
CHANGED
|
@@ -69,6 +69,18 @@ export declare function sampleWind(grid: WindGrid, lon: number, lat: number): {
|
|
|
69
69
|
u: number;
|
|
70
70
|
v: number;
|
|
71
71
|
} | null;
|
|
72
|
+
export interface NudgeOptions {
|
|
73
|
+
/** Jarak pengaruh sebuah stasiun (km). Di luar jarak ini grid tidak berubah. Bawaan 80. */
|
|
74
|
+
radiusKm?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Sesuaikan grid model dengan pengamatan di stasiun: di dekat stasiun nilai grid ditarik ke nilai
|
|
78
|
+
* pengamatan (persis di stasiun), makin jauh makin kecil, dan di luar `radiusKm` tidak berubah. Stasiun
|
|
79
|
+
* di luar grid atau di sel tanpa data dilewati.
|
|
80
|
+
*/
|
|
81
|
+
export declare function nudgeScalar(grid: ScalarGrid, points: readonly StationValue[], options?: NudgeOptions): ScalarGrid;
|
|
82
|
+
/** Sesuaikan grid angin model dengan angin di stasiun (pada komponen u dan v). */
|
|
83
|
+
export declare function nudgeWind(grid: WindGrid, stations: readonly StationWind[], options?: NudgeOptions): WindGrid;
|
|
72
84
|
/** Campur dua grid angin yang bentuknya sama (mis. dua jam prakiraan), `t` 0-1. */
|
|
73
85
|
export declare function blendWindGrids(a: WindGrid, b: WindGrid, t: number): WindGrid;
|
|
74
86
|
/**
|
package/dist/grid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../src/grid.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../src/grid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standar data medan cuaca AeroSwarm (`aeroswarm.weather-grid` versi 1).
|
|
3
|
+
*
|
|
4
|
+
* Satu format untuk semua sumber model (Open-Meteo, hasil konversi GRIB, layanan sendiri): grid
|
|
5
|
+
* beraturan, beberapa waktu, dan beberapa besaran. Sumber apa pun cukup menghasilkan format ini; renderer
|
|
6
|
+
* dan timeline tidak perlu tahu asalnya. Lihat GRID_STANDARD.md untuk bentuk JSON-nya.
|
|
7
|
+
*
|
|
8
|
+
* Konvensi: baris 0 = utara, kolom 0 = barat. Angin m/s (`windU` ke timur, `windV` ke utara), awan %,
|
|
9
|
+
* hujan mm/jam, suhu °C. Nilai `null` = tanpa data. Waktu UTC.
|
|
10
|
+
*/
|
|
11
|
+
import { type GridBounds, type ScalarGrid, type WindGrid } from "./grid.js";
|
|
12
|
+
export declare const WEATHER_GRID_STANDARD = "aeroswarm.weather-grid";
|
|
13
|
+
export declare const WEATHER_GRID_VERSION = 1;
|
|
14
|
+
export type GridQuantity = "windU" | "windV" | "cloud" | "rain" | "temperature";
|
|
15
|
+
export interface WeatherGridSource {
|
|
16
|
+
/** Nama sumber, mis. `open-meteo`. */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Nama model, mis. `icon_seamless`. */
|
|
19
|
+
model?: string;
|
|
20
|
+
/** Waktu model dijalankan (run), bila diketahui. */
|
|
21
|
+
runAt?: Date;
|
|
22
|
+
/** Waktu data diambil/dibuat. */
|
|
23
|
+
generatedAt?: Date;
|
|
24
|
+
}
|
|
25
|
+
export interface WeatherGridSeries {
|
|
26
|
+
source: WeatherGridSource;
|
|
27
|
+
bounds: GridBounds;
|
|
28
|
+
nx: number;
|
|
29
|
+
ny: number;
|
|
30
|
+
/** Waktu tiap bingkai, naik. */
|
|
31
|
+
times: Date[];
|
|
32
|
+
/** Per besaran: satu larik nx*ny per waktu. Besaran yang tidak ada dibiarkan kosong. */
|
|
33
|
+
fields: Partial<Record<GridQuantity, Float32Array[]>>;
|
|
34
|
+
}
|
|
35
|
+
export interface SeriesFields {
|
|
36
|
+
at: Date;
|
|
37
|
+
wind: WindGrid | null;
|
|
38
|
+
cloud: ScalarGrid | null;
|
|
39
|
+
rain: ScalarGrid | null;
|
|
40
|
+
temperature: ScalarGrid | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Baca dan periksa JSON berstandar `aeroswarm.weather-grid` v1. Melempar galat yang menyebut bagian
|
|
44
|
+
* mana yang salah. Nilai di luar angka (null) menjadi NaN (tanpa data).
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseWeatherGridSeries(input: unknown): WeatherGridSeries;
|
|
47
|
+
/** Bentuk JSON berstandar dari seri (kebalikan `parseWeatherGridSeries`); NaN menjadi null. */
|
|
48
|
+
export declare function serializeWeatherGridSeries(series: WeatherGridSeries): Record<string, unknown>;
|
|
49
|
+
/** Rentang waktu seri. */
|
|
50
|
+
export declare function seriesRange(series: WeatherGridSeries): {
|
|
51
|
+
from: Date;
|
|
52
|
+
to: Date;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Medan pada waktu `at`, dicampur linear antar dua bingkai terdekat. Null di luar rentang waktu seri.
|
|
56
|
+
* Besaran yang tidak ada di seri dikembalikan null.
|
|
57
|
+
*/
|
|
58
|
+
export declare function seriesFieldsAt(series: WeatherGridSeries, at: Date): SeriesFields | null;
|
|
59
|
+
//# sourceMappingURL=gridSeries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gridSeries.d.ts","sourceRoot":"","sources":["../src/gridSeries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAC9D,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC;AAEhF,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,iCAAiC;IACjC,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,wFAAwF;IACxF,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAaD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAgExE;AAED,+FAA+F;AAC/F,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsB7F;AAED,0BAA0B;AAC1B,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAK/E;AAqBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,EAAE,EAAE,IAAI,GAAG,YAAY,GAAG,IAAI,CAwBvF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gridSeries.test.d.ts","sourceRoot":"","sources":["../src/gridSeries.test.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./aftn.js";
|
|
|
3
3
|
export * from "./animation.js";
|
|
4
4
|
export * from "./conditions.js";
|
|
5
5
|
export * from "./grid.js";
|
|
6
|
+
export * from "./gridSeries.js";
|
|
7
|
+
export * from "./openMeteo.js";
|
|
6
8
|
export * from "./metar.js";
|
|
7
9
|
export * from "./taf.js";
|
|
8
10
|
export * from "./store.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAE3B;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,IAAiB,GAC3B,MAAM,EAAE,CAUV"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAE3B;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,IAAiB,GAC3B,MAAM,EAAE,CAUV"}
|
package/dist/index.js
CHANGED
|
@@ -643,6 +643,52 @@ function sampleWind(grid, lon, lat) {
|
|
|
643
643
|
const v = bilinear(grid.v, grid.bounds, grid.nx, grid.ny, lon, lat);
|
|
644
644
|
return u === null || v === null ? null : { u, v };
|
|
645
645
|
}
|
|
646
|
+
var influence = (distance, radius) => distance >= radius ? 0 : (radius * radius - distance * distance) / (radius * radius + distance * distance);
|
|
647
|
+
function nudgeValues(values, bounds, nx, ny, deltas, radius) {
|
|
648
|
+
const out = Float32Array.from(values);
|
|
649
|
+
if (deltas.length === 0) return out;
|
|
650
|
+
for (let row = 0; row < ny; row++) {
|
|
651
|
+
for (let col = 0; col < nx; col++) {
|
|
652
|
+
const index = row * nx + col;
|
|
653
|
+
if (Number.isNaN(out[index])) continue;
|
|
654
|
+
const cell = pointOf(bounds, nx, ny, col, row);
|
|
655
|
+
let weights = 0;
|
|
656
|
+
let weighted = 0;
|
|
657
|
+
for (const d of deltas) {
|
|
658
|
+
const w = influence(distanceKm(cell, d), radius);
|
|
659
|
+
weights += w;
|
|
660
|
+
weighted += w * d.delta;
|
|
661
|
+
}
|
|
662
|
+
if (weights > 0) out[index] = out[index] + weighted / weights * Math.min(1, weights);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return out;
|
|
666
|
+
}
|
|
667
|
+
function nudgeScalar(grid, points, options = {}) {
|
|
668
|
+
const deltas = [];
|
|
669
|
+
for (const p of points) {
|
|
670
|
+
const model = sampleScalar(grid, p.lon, p.lat);
|
|
671
|
+
if (model !== null) deltas.push({ lat: p.lat, lon: p.lon, delta: p.value - model });
|
|
672
|
+
}
|
|
673
|
+
return { ...grid, values: nudgeValues(grid.values, grid.bounds, grid.nx, grid.ny, deltas, options.radiusKm ?? 80) };
|
|
674
|
+
}
|
|
675
|
+
function nudgeWind(grid, stations, options = {}) {
|
|
676
|
+
const du = [];
|
|
677
|
+
const dv = [];
|
|
678
|
+
for (const s of stations) {
|
|
679
|
+
const model = sampleWind(grid, s.lon, s.lat);
|
|
680
|
+
if (!model) continue;
|
|
681
|
+
const obs = s.directionDeg === null ? { u: 0, v: 0 } : uvFromSpeedDirection(s.speedKt * KT_TO_MS, s.directionDeg);
|
|
682
|
+
du.push({ lat: s.lat, lon: s.lon, delta: obs.u - model.u });
|
|
683
|
+
dv.push({ lat: s.lat, lon: s.lon, delta: obs.v - model.v });
|
|
684
|
+
}
|
|
685
|
+
const radius = options.radiusKm ?? 80;
|
|
686
|
+
return {
|
|
687
|
+
...grid,
|
|
688
|
+
u: nudgeValues(grid.u, grid.bounds, grid.nx, grid.ny, du, radius),
|
|
689
|
+
v: nudgeValues(grid.v, grid.bounds, grid.nx, grid.ny, dv, radius)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
646
692
|
function blendWindGrids(a, b, t) {
|
|
647
693
|
if (a.nx !== b.nx || a.ny !== b.ny) throw new Error("Grid angin harus berukuran sama");
|
|
648
694
|
const mix = (x, y) => Float32Array.from(x, (value, i) => value + (y[i] - value) * t);
|
|
@@ -688,10 +734,10 @@ function parseGrib2Json(input) {
|
|
|
688
734
|
v: orient(vRecord.data)
|
|
689
735
|
};
|
|
690
736
|
}
|
|
691
|
-
function stationWindsFromMetars(reports,
|
|
737
|
+
function stationWindsFromMetars(reports, locate2) {
|
|
692
738
|
const out = [];
|
|
693
739
|
for (const report of reports) {
|
|
694
|
-
const place =
|
|
740
|
+
const place = locate2(report.station);
|
|
695
741
|
if (!place) continue;
|
|
696
742
|
const { wind } = report.conditions;
|
|
697
743
|
out.push({
|
|
@@ -766,14 +812,222 @@ function decodeGridJson(input, fallback) {
|
|
|
766
812
|
throw new Error("Format medan tidak dikenal: harus grib2json, JSON grid {bounds,nx,ny,u,v|values}, atau respons banyak lokasi");
|
|
767
813
|
}
|
|
768
814
|
|
|
815
|
+
// src/gridSeries.ts
|
|
816
|
+
var WEATHER_GRID_STANDARD = "aeroswarm.weather-grid";
|
|
817
|
+
var WEATHER_GRID_VERSION = 1;
|
|
818
|
+
var QUANTITIES = ["windU", "windV", "cloud", "rain", "temperature"];
|
|
819
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
820
|
+
function parseDate(value, where) {
|
|
821
|
+
const date = typeof value === "string" ? new Date(value) : new Date(Number.NaN);
|
|
822
|
+
if (Number.isNaN(date.getTime())) throw new Error(`${where}: bukan tanggal ISO 8601 yang valid`);
|
|
823
|
+
return date;
|
|
824
|
+
}
|
|
825
|
+
function parseWeatherGridSeries(input) {
|
|
826
|
+
if (!isRecord2(input)) throw new Error("Bukan objek JSON");
|
|
827
|
+
if (input.standard !== WEATHER_GRID_STANDARD) {
|
|
828
|
+
throw new Error(`standard harus "${WEATHER_GRID_STANDARD}"`);
|
|
829
|
+
}
|
|
830
|
+
if (input.version !== WEATHER_GRID_VERSION) {
|
|
831
|
+
throw new Error(`version ${String(input.version)} tidak didukung (didukung: ${WEATHER_GRID_VERSION})`);
|
|
832
|
+
}
|
|
833
|
+
const src = isRecord2(input.source) ? input.source : null;
|
|
834
|
+
if (!src || typeof src.name !== "string" || src.name === "") throw new Error("source.name wajib diisi");
|
|
835
|
+
const source = {
|
|
836
|
+
name: src.name,
|
|
837
|
+
model: typeof src.model === "string" ? src.model : void 0,
|
|
838
|
+
runAt: src.runAt === void 0 ? void 0 : parseDate(src.runAt, "source.runAt"),
|
|
839
|
+
generatedAt: src.generatedAt === void 0 ? void 0 : parseDate(src.generatedAt, "source.generatedAt")
|
|
840
|
+
};
|
|
841
|
+
const grid = isRecord2(input.grid) ? input.grid : null;
|
|
842
|
+
if (!grid) throw new Error("grid wajib diisi");
|
|
843
|
+
const { west, south, east, north } = grid;
|
|
844
|
+
const nx = Number(grid.nx);
|
|
845
|
+
const ny = Number(grid.ny);
|
|
846
|
+
if (![west, south, east, north].every((n) => typeof n === "number" && Number.isFinite(n))) {
|
|
847
|
+
throw new Error("grid.west/south/east/north harus angka");
|
|
848
|
+
}
|
|
849
|
+
if (!Number.isInteger(nx) || !Number.isInteger(ny) || nx < 2 || ny < 2) {
|
|
850
|
+
throw new Error("grid.nx dan grid.ny harus bilangan bulat >= 2");
|
|
851
|
+
}
|
|
852
|
+
const bounds = { west, south, east, north };
|
|
853
|
+
if (bounds.west >= bounds.east || bounds.south >= bounds.north) {
|
|
854
|
+
throw new Error("grid: west < east dan south < north");
|
|
855
|
+
}
|
|
856
|
+
if (!Array.isArray(input.times) || input.times.length === 0) throw new Error("times wajib berisi minimal satu waktu");
|
|
857
|
+
const times = input.times.map((t, i) => parseDate(t, `times[${i}]`));
|
|
858
|
+
for (let i = 1; i < times.length; i++) {
|
|
859
|
+
if (times[i].getTime() <= times[i - 1].getTime()) {
|
|
860
|
+
throw new Error(`times[${i}] harus lebih baru dari times[${i - 1}]`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const rawFields = isRecord2(input.fields) ? input.fields : null;
|
|
864
|
+
if (!rawFields) throw new Error("fields wajib diisi");
|
|
865
|
+
const fields = {};
|
|
866
|
+
for (const quantity of QUANTITIES) {
|
|
867
|
+
const frames = rawFields[quantity];
|
|
868
|
+
if (frames === void 0) continue;
|
|
869
|
+
if (!Array.isArray(frames) || frames.length !== times.length) {
|
|
870
|
+
throw new Error(`fields.${quantity} harus berisi ${times.length} bingkai (satu per waktu)`);
|
|
871
|
+
}
|
|
872
|
+
fields[quantity] = frames.map((frame, t) => {
|
|
873
|
+
if (!Array.isArray(frame) || frame.length !== nx * ny) {
|
|
874
|
+
throw new Error(`fields.${quantity}[${t}] harus berisi ${nx * ny} nilai (nx * ny)`);
|
|
875
|
+
}
|
|
876
|
+
return Float32Array.from(frame, (v) => typeof v === "number" && Number.isFinite(v) ? v : Number.NaN);
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
if (fields.windU === void 0 !== (fields.windV === void 0)) {
|
|
880
|
+
throw new Error("windU dan windV harus ada berdua");
|
|
881
|
+
}
|
|
882
|
+
if (Object.keys(fields).length === 0) throw new Error("fields tidak berisi satu besaran pun");
|
|
883
|
+
return { source, bounds, nx, ny, times, fields };
|
|
884
|
+
}
|
|
885
|
+
function serializeWeatherGridSeries(series) {
|
|
886
|
+
const fields = {};
|
|
887
|
+
for (const quantity of QUANTITIES) {
|
|
888
|
+
const frames = series.fields[quantity];
|
|
889
|
+
if (!frames) continue;
|
|
890
|
+
fields[quantity] = frames.map(
|
|
891
|
+
(frame) => Array.from(frame, (v) => Number.isNaN(v) ? null : Math.round(v * 1e3) / 1e3)
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
standard: WEATHER_GRID_STANDARD,
|
|
896
|
+
version: WEATHER_GRID_VERSION,
|
|
897
|
+
source: {
|
|
898
|
+
name: series.source.name,
|
|
899
|
+
...series.source.model ? { model: series.source.model } : {},
|
|
900
|
+
...series.source.runAt ? { runAt: series.source.runAt.toISOString() } : {},
|
|
901
|
+
...series.source.generatedAt ? { generatedAt: series.source.generatedAt.toISOString() } : {}
|
|
902
|
+
},
|
|
903
|
+
grid: { ...series.bounds, nx: series.nx, ny: series.ny },
|
|
904
|
+
times: series.times.map((t) => t.toISOString()),
|
|
905
|
+
fields
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
function seriesRange(series) {
|
|
909
|
+
return {
|
|
910
|
+
from: series.times[0],
|
|
911
|
+
to: series.times[series.times.length - 1]
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
function locate(times, at) {
|
|
915
|
+
const ms = at.getTime();
|
|
916
|
+
const first = times[0].getTime();
|
|
917
|
+
const last = times[times.length - 1].getTime();
|
|
918
|
+
if (ms < first || ms > last) return null;
|
|
919
|
+
for (let i = 1; i < times.length; i++) {
|
|
920
|
+
const hi = times[i].getTime();
|
|
921
|
+
if (ms <= hi) {
|
|
922
|
+
const lo = times[i - 1].getTime();
|
|
923
|
+
return { a: i - 1, b: i, t: (ms - lo) / (hi - lo || 1) };
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return { a: times.length - 1, b: times.length - 1, t: 0 };
|
|
927
|
+
}
|
|
928
|
+
var mixFrames = (frames, a, b, t) => Float32Array.from(frames[a], (value, i) => value + (frames[b][i] - value) * t);
|
|
929
|
+
function seriesFieldsAt(series, at) {
|
|
930
|
+
const where = locate(series.times, at);
|
|
931
|
+
if (!where) return null;
|
|
932
|
+
const { bounds, nx, ny } = series;
|
|
933
|
+
const scalar = (quantity) => {
|
|
934
|
+
const frames = series.fields[quantity];
|
|
935
|
+
return frames ? { bounds, nx, ny, values: mixFrames(frames, where.a, where.b, where.t) } : null;
|
|
936
|
+
};
|
|
937
|
+
const u = series.fields.windU;
|
|
938
|
+
const v = series.fields.windV;
|
|
939
|
+
let wind = null;
|
|
940
|
+
if (u && v) {
|
|
941
|
+
const from = { bounds, nx, ny, u: u[where.a], v: v[where.a] };
|
|
942
|
+
const to = { bounds, nx, ny, u: u[where.b], v: v[where.b] };
|
|
943
|
+
wind = where.a === where.b ? from : blendWindGrids(from, to, where.t);
|
|
944
|
+
}
|
|
945
|
+
return {
|
|
946
|
+
at,
|
|
947
|
+
wind,
|
|
948
|
+
cloud: scalar("cloud"),
|
|
949
|
+
rain: scalar("rain"),
|
|
950
|
+
temperature: scalar("temperature")
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/openMeteo.ts
|
|
955
|
+
function latticePoints(lattice) {
|
|
956
|
+
const latitude = [];
|
|
957
|
+
const longitude = [];
|
|
958
|
+
for (let row = 0; row < lattice.ny; row++) {
|
|
959
|
+
const lat = lattice.north - (lattice.north - lattice.south) * row / (lattice.ny - 1);
|
|
960
|
+
for (let col = 0; col < lattice.nx; col++) {
|
|
961
|
+
latitude.push(round(lat));
|
|
962
|
+
longitude.push(round(lattice.west + (lattice.east - lattice.west) * col / (lattice.nx - 1)));
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return { latitude, longitude };
|
|
966
|
+
}
|
|
967
|
+
var round = (value) => Math.round(value * 1e4) / 1e4;
|
|
968
|
+
var SPEED_TO_MS = { "m/s": 1, "km/h": 1 / 3.6, kn: 0.514444, mph: 0.44704 };
|
|
969
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
970
|
+
var toUtc = (iso) => new Date(/[zZ]|[+-]\d\d:?\d\d$/.test(iso) ? iso : `${iso}Z`);
|
|
971
|
+
function weatherGridFromOpenMeteo(response, lattice, options = {}) {
|
|
972
|
+
const items = Array.isArray(response) ? response : [response];
|
|
973
|
+
const expected = lattice.nx * lattice.ny;
|
|
974
|
+
if (items.length !== expected) {
|
|
975
|
+
throw new Error(`Respons berisi ${items.length} titik, kisi memerlukan ${expected} (${lattice.nx} x ${lattice.ny})`);
|
|
976
|
+
}
|
|
977
|
+
const hourly = items.map((item, i) => {
|
|
978
|
+
const h = isRecord3(item) && isRecord3(item.hourly) ? item.hourly : null;
|
|
979
|
+
if (!h || !Array.isArray(h.time)) throw new Error(`Titik ${i}: tidak ada data hourly.time`);
|
|
980
|
+
return h;
|
|
981
|
+
});
|
|
982
|
+
const timesIso = hourly[0].time;
|
|
983
|
+
if (timesIso.length === 0) throw new Error("hourly.time kosong");
|
|
984
|
+
for (const [i, h] of hourly.entries()) {
|
|
985
|
+
if (h.time.length !== timesIso.length) throw new Error(`Titik ${i}: jumlah waktu berbeda dari titik pertama`);
|
|
986
|
+
}
|
|
987
|
+
const times = timesIso.map(toUtc);
|
|
988
|
+
const toMs = SPEED_TO_MS[options.speedUnit ?? "m/s"] ?? 1;
|
|
989
|
+
const frame = (pick) => times.map((_, t) => Float32Array.from(hourly, (h) => pick(h, t)));
|
|
990
|
+
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : Number.NaN;
|
|
991
|
+
const fields = {};
|
|
992
|
+
const has = (key) => hourly.every((h) => Array.isArray(h[key]));
|
|
993
|
+
if (has("wind_speed_10m") && has("wind_direction_10m")) {
|
|
994
|
+
const uv = (h, t) => {
|
|
995
|
+
const speed = num(h.wind_speed_10m?.[t]) * toMs;
|
|
996
|
+
const dir = num(h.wind_direction_10m?.[t]);
|
|
997
|
+
return Number.isNaN(speed) || Number.isNaN(dir) ? { u: Number.NaN, v: Number.NaN } : uvFromSpeedDirection(speed, dir);
|
|
998
|
+
};
|
|
999
|
+
fields.windU = frame((h, t) => uv(h, t).u);
|
|
1000
|
+
fields.windV = frame((h, t) => uv(h, t).v);
|
|
1001
|
+
}
|
|
1002
|
+
if (has("cloud_cover")) fields.cloud = frame((h, t) => num(h.cloud_cover?.[t]));
|
|
1003
|
+
if (has("precipitation")) fields.rain = frame((h, t) => num(h.precipitation?.[t]));
|
|
1004
|
+
if (has("temperature_2m")) fields.temperature = frame((h, t) => num(h.temperature_2m?.[t]));
|
|
1005
|
+
if (Object.keys(fields).length === 0) {
|
|
1006
|
+
throw new Error("Respons tidak memuat wind_speed_10m, wind_direction_10m, cloud_cover, precipitation, atau temperature_2m");
|
|
1007
|
+
}
|
|
1008
|
+
return {
|
|
1009
|
+
source: {
|
|
1010
|
+
name: "open-meteo",
|
|
1011
|
+
model: options.model,
|
|
1012
|
+
runAt: options.runAt,
|
|
1013
|
+
generatedAt: options.generatedAt ?? /* @__PURE__ */ new Date()
|
|
1014
|
+
},
|
|
1015
|
+
bounds: { west: lattice.west, south: lattice.south, east: lattice.east, north: lattice.north },
|
|
1016
|
+
nx: lattice.nx,
|
|
1017
|
+
ny: lattice.ny,
|
|
1018
|
+
times,
|
|
1019
|
+
fields
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
|
|
769
1023
|
// src/store.ts
|
|
770
1024
|
var MAX_OBSERVATIONS_PER_STATION = 48;
|
|
771
1025
|
var MAX_FORECASTS_PER_STATION = 8;
|
|
772
1026
|
var rainRate = (intensity) => intensity * intensity * 15;
|
|
773
1027
|
var hhmm = (d) => `${String(d.getUTCHours()).padStart(2, "0")}:${String(d.getUTCMinutes()).padStart(2, "0")}`;
|
|
774
1028
|
var WeatherStore = class {
|
|
775
|
-
constructor(
|
|
776
|
-
this.locate =
|
|
1029
|
+
constructor(locate2) {
|
|
1030
|
+
this.locate = locate2;
|
|
777
1031
|
}
|
|
778
1032
|
locate;
|
|
779
1033
|
observations = /* @__PURE__ */ new Map();
|
|
@@ -891,24 +1145,48 @@ var WeatherStore = class {
|
|
|
891
1145
|
* koordinat, diinterpolasi ke grid. Mengembalikan null bila tidak ada stasiun yang bisa dipakai.
|
|
892
1146
|
*/
|
|
893
1147
|
fieldsAt(at, grid, options = {}) {
|
|
1148
|
+
const points = this.pointsAt(at, options);
|
|
1149
|
+
if (points.stations.length === 0) return null;
|
|
1150
|
+
return {
|
|
1151
|
+
at,
|
|
1152
|
+
stations: points.stations,
|
|
1153
|
+
wind: windGridFromStations(points.winds, grid),
|
|
1154
|
+
cloud: interpolateScalar(points.clouds, grid),
|
|
1155
|
+
rain: interpolateScalar(points.rains, grid),
|
|
1156
|
+
temperature: points.temperatures.length > 0 ? interpolateScalar(points.temperatures, grid) : null
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Gabungkan medan model dengan laporan: grid model disesuaikan ke pengamatan (METAR/SPECI) atau
|
|
1161
|
+
* prakiraan (TAF) di stasiun yang punya koordinat, dalam jarak `radiusKm` dari tiap stasiun. Tanpa
|
|
1162
|
+
* stasiun yang bisa dipakai, medan model dikembalikan apa adanya.
|
|
1163
|
+
*/
|
|
1164
|
+
blendInto(model, modelName, at, options = {}) {
|
|
1165
|
+
const points = this.pointsAt(at, options);
|
|
1166
|
+
return {
|
|
1167
|
+
at,
|
|
1168
|
+
stations: [modelName, ...points.stations],
|
|
1169
|
+
wind: model.wind ? nudgeWind(model.wind, points.winds, options) : null,
|
|
1170
|
+
cloud: model.cloud ? nudgeScalar(model.cloud, points.clouds, options) : null,
|
|
1171
|
+
rain: model.rain ? nudgeScalar(model.rain, points.rains, options) : null,
|
|
1172
|
+
temperature: model.temperature ? nudgeScalar(model.temperature, points.temperatures, options) : null
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
/** Nilai tiap stasiun yang punya koordinat dan data pada waktu `at`. */
|
|
1176
|
+
pointsAt(at, options) {
|
|
894
1177
|
const winds = [];
|
|
895
1178
|
const clouds = [];
|
|
896
1179
|
const rains = [];
|
|
897
1180
|
const temperatures = [];
|
|
898
|
-
const
|
|
1181
|
+
const stations = [];
|
|
899
1182
|
for (const station of this.stations()) {
|
|
900
1183
|
const place = this.locate(station);
|
|
901
1184
|
if (!place) continue;
|
|
902
1185
|
const snapshot = this.snapshotAt(station, at, options);
|
|
903
1186
|
if (!snapshot) continue;
|
|
904
|
-
|
|
1187
|
+
stations.push(station);
|
|
905
1188
|
const { state } = snapshot;
|
|
906
|
-
winds.push({
|
|
907
|
-
lat: place.lat,
|
|
908
|
-
lon: place.lon,
|
|
909
|
-
speedKt: state.wind.speedKt,
|
|
910
|
-
directionDeg: state.wind.fromDeg
|
|
911
|
-
});
|
|
1189
|
+
winds.push({ lat: place.lat, lon: place.lon, speedKt: state.wind.speedKt, directionDeg: state.wind.fromDeg });
|
|
912
1190
|
clouds.push({ lat: place.lat, lon: place.lon, value: state.clouds.total * 100 });
|
|
913
1191
|
rains.push({
|
|
914
1192
|
lat: place.lat,
|
|
@@ -920,15 +1198,7 @@ var WeatherStore = class {
|
|
|
920
1198
|
temperatures.push({ lat: place.lat, lon: place.lon, value: observed.temperatureC });
|
|
921
1199
|
}
|
|
922
1200
|
}
|
|
923
|
-
|
|
924
|
-
return {
|
|
925
|
-
at,
|
|
926
|
-
stations: used,
|
|
927
|
-
wind: windGridFromStations(winds, grid),
|
|
928
|
-
cloud: interpolateScalar(clouds, grid),
|
|
929
|
-
rain: interpolateScalar(rains, grid),
|
|
930
|
-
temperature: temperatures.length > 0 ? interpolateScalar(temperatures, grid) : null
|
|
931
|
-
};
|
|
1201
|
+
return { stations, winds, clouds, rains, temperatures };
|
|
932
1202
|
}
|
|
933
1203
|
// ---- internal -------------------------------------------------------------
|
|
934
1204
|
changed() {
|
|
@@ -990,6 +1260,8 @@ function decodeWeatherMessage(message, reference = /* @__PURE__ */ new Date()) {
|
|
|
990
1260
|
return reports;
|
|
991
1261
|
}
|
|
992
1262
|
export {
|
|
1263
|
+
WEATHER_GRID_STANDARD,
|
|
1264
|
+
WEATHER_GRID_VERSION,
|
|
993
1265
|
WeatherStore,
|
|
994
1266
|
animationFromConditions,
|
|
995
1267
|
animationSnapshot,
|
|
@@ -1002,19 +1274,27 @@ export {
|
|
|
1002
1274
|
extractReports,
|
|
1003
1275
|
forecastAt,
|
|
1004
1276
|
interpolateScalar,
|
|
1277
|
+
latticePoints,
|
|
1005
1278
|
mergeConditions,
|
|
1279
|
+
nudgeScalar,
|
|
1280
|
+
nudgeWind,
|
|
1006
1281
|
parseConditionTokens,
|
|
1007
1282
|
parseGrib2Json,
|
|
1008
1283
|
parseGridJson,
|
|
1009
1284
|
parseMetar,
|
|
1010
1285
|
parseTaf,
|
|
1286
|
+
parseWeatherGridSeries,
|
|
1011
1287
|
pointOf,
|
|
1012
1288
|
resolveDayTime,
|
|
1013
1289
|
sampleScalar,
|
|
1014
1290
|
sampleWind,
|
|
1291
|
+
serializeWeatherGridSeries,
|
|
1292
|
+
seriesFieldsAt,
|
|
1293
|
+
seriesRange,
|
|
1015
1294
|
speedOf,
|
|
1016
1295
|
stationWindsFromMetars,
|
|
1017
1296
|
stationWindsFromModelPoints,
|
|
1018
1297
|
uvFromSpeedDirection,
|
|
1298
|
+
weatherGridFromOpenMeteo,
|
|
1019
1299
|
windGridFromStations
|
|
1020
1300
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type WeatherGridSeries } from "./gridSeries.js";
|
|
2
|
+
/**
|
|
3
|
+
* Adaptor respons layanan model bergaya Open-Meteo menjadi standar `aeroswarm.weather-grid`. Murni:
|
|
4
|
+
* tidak memanggil jaringan. Pemanggilnya (aplikasi) yang mengambil datanya.
|
|
5
|
+
*
|
|
6
|
+
* Titik diminta sebagai kisi beraturan (lihat `latticePoints`), jadi responsnya langsung menjadi
|
|
7
|
+
* grid tanpa interpolasi.
|
|
8
|
+
*/
|
|
9
|
+
export interface Lattice {
|
|
10
|
+
west: number;
|
|
11
|
+
south: number;
|
|
12
|
+
east: number;
|
|
13
|
+
north: number;
|
|
14
|
+
nx: number;
|
|
15
|
+
ny: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Titik-titik kisi, baris demi baris dari utara ke selatan, kolom dari barat ke timur. Urutan ini
|
|
19
|
+
* sama dengan urutan respons banyak lokasi, dan dengan urutan sel pada grid standar.
|
|
20
|
+
*/
|
|
21
|
+
export declare function latticePoints(lattice: Lattice): {
|
|
22
|
+
latitude: number[];
|
|
23
|
+
longitude: number[];
|
|
24
|
+
};
|
|
25
|
+
declare const SPEED_TO_MS: Record<string, number>;
|
|
26
|
+
export interface OpenMeteoOptions {
|
|
27
|
+
/** Satuan kecepatan angin di respons. Bawaan `m/s` (minta dengan `wind_speed_unit=ms`). */
|
|
28
|
+
speedUnit?: keyof typeof SPEED_TO_MS;
|
|
29
|
+
model?: string;
|
|
30
|
+
runAt?: Date;
|
|
31
|
+
generatedAt?: Date;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Susun seri standar dari respons banyak lokasi (satu elemen per titik kisi, urutan `latticePoints`).
|
|
35
|
+
* Meminta `hourly=wind_speed_10m,wind_direction_10m,cloud_cover,precipitation,temperature_2m`,
|
|
36
|
+
* `timezone=GMT`, dan `wind_speed_unit=ms`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function weatherGridFromOpenMeteo(response: unknown, lattice: Lattice, options?: OpenMeteoOptions): WeatherGridSeries;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=openMeteo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openMeteo.d.ts","sourceRoot":"","sources":["../src/openMeteo.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;GAMG;AAEH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAW3F;AAID,QAAA,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAA6D,CAAC;AAEtG,MAAM,WAAW,gBAAgB;IAC/B,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,OAAO,WAAW,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAiBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,gBAAqB,GAC7B,iBAAiB,CAyDnB"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AnimationSnapshot } from "./animation.js";
|
|
2
|
-
import { type GeoPoint, type GridBounds, type ScalarGrid, type WindGrid } from "./grid.js";
|
|
2
|
+
import { type GeoPoint, type GridBounds, type NudgeOptions, type ScalarGrid, type WindGrid } from "./grid.js";
|
|
3
3
|
import type { TafChange } from "./types.js";
|
|
4
4
|
export type Locate = (icao: string) => GeoPoint | undefined;
|
|
5
5
|
export interface IngestOptions {
|
|
@@ -41,6 +41,16 @@ export interface WeatherFields {
|
|
|
41
41
|
/** Suhu (°C); null bila tidak ada pengamatan bersuhu. */
|
|
42
42
|
temperature: ScalarGrid | null;
|
|
43
43
|
}
|
|
44
|
+
/** Medan dengan besaran yang boleh tidak ada (mis. seri model tanpa data suhu). */
|
|
45
|
+
export interface FieldSet {
|
|
46
|
+
at: Date;
|
|
47
|
+
/** Sumber yang dipakai: stasiun, atau nama model diikuti stasiun yang menyesuaikannya. */
|
|
48
|
+
stations: string[];
|
|
49
|
+
wind: WindGrid | null;
|
|
50
|
+
cloud: ScalarGrid | null;
|
|
51
|
+
rain: ScalarGrid | null;
|
|
52
|
+
temperature: ScalarGrid | null;
|
|
53
|
+
}
|
|
44
54
|
export interface FieldGridOptions {
|
|
45
55
|
bounds: GridBounds;
|
|
46
56
|
nx: number;
|
|
@@ -82,6 +92,16 @@ export declare class WeatherStore {
|
|
|
82
92
|
fieldsAt(at: Date, grid: FieldGridOptions, options?: {
|
|
83
93
|
includeProbable?: boolean;
|
|
84
94
|
}): WeatherFields | null;
|
|
95
|
+
/**
|
|
96
|
+
* Gabungkan medan model dengan laporan: grid model disesuaikan ke pengamatan (METAR/SPECI) atau
|
|
97
|
+
* prakiraan (TAF) di stasiun yang punya koordinat, dalam jarak `radiusKm` dari tiap stasiun. Tanpa
|
|
98
|
+
* stasiun yang bisa dipakai, medan model dikembalikan apa adanya.
|
|
99
|
+
*/
|
|
100
|
+
blendInto(model: Omit<FieldSet, "stations" | "at">, modelName: string, at: Date, options?: NudgeOptions & {
|
|
101
|
+
includeProbable?: boolean;
|
|
102
|
+
}): FieldSet;
|
|
103
|
+
/** Nilai tiap stasiun yang punya koordinat dan data pada waktu `at`. */
|
|
104
|
+
private pointsAt;
|
|
85
105
|
private changed;
|
|
86
106
|
private add;
|
|
87
107
|
private latest;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeroswarm/weather",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Decode raw AFTN/METAR/SPECI/TAF messages and turn them into animation parameters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"GRID_STANDARD.md"
|
|
17
18
|
],
|
|
18
19
|
"publishConfig": {
|
|
19
20
|
"access": "public",
|