meteoswiss-mcp 2.2.1 → 2.3.0-rc.2
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/dist/data/ogd-climate-data.js +140 -0
- package/dist/data/ogd-climate-data.js.map +1 -0
- package/dist/data/ogd-current-weather.js +64 -6
- package/dist/data/ogd-current-weather.js.map +1 -1
- package/dist/data/ogd-data-store.js +29 -3
- package/dist/data/ogd-data-store.js.map +1 -1
- package/dist/data/ogd-nbcn-stations.js +137 -0
- package/dist/data/ogd-nbcn-stations.js.map +1 -0
- package/dist/data/ogd-pollen-data.js +46 -15
- package/dist/data/ogd-pollen-data.js.map +1 -1
- package/dist/data/ogd-smn-stations.js +69 -24
- package/dist/data/ogd-smn-stations.js.map +1 -1
- package/dist/data/ogd-stac-client.js +4 -0
- package/dist/data/ogd-stac-client.js.map +1 -1
- package/dist/data/ogd-station-resolver.js +32 -24
- package/dist/data/ogd-station-resolver.js.map +1 -1
- package/dist/schemas/ogd-climate-data.js +39 -0
- package/dist/schemas/ogd-climate-data.js.map +1 -0
- package/dist/schemas/ogd-shared.js +2 -0
- package/dist/schemas/ogd-shared.js.map +1 -1
- package/dist/server.js +38 -1
- package/dist/server.js.map +1 -1
- package/dist/support/geocode.js +24 -0
- package/dist/support/geocode.js.map +1 -1
- package/dist/support/name-matcher.js +39 -0
- package/dist/support/name-matcher.js.map +1 -0
- package/dist/support/ogd-csv-parser.js +18 -30
- package/dist/support/ogd-csv-parser.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data layer for the meteoswissClimateData tool.
|
|
3
|
+
* Fetches homogeneous climate measurement series from NBCN stations.
|
|
4
|
+
*/
|
|
5
|
+
import { getLatin1CsvData } from './ogd-data-store.js';
|
|
6
|
+
import { resolveNbcnStation, findNearestNbcnStation } from './ogd-nbcn-stations.js';
|
|
7
|
+
import { parseNumeric } from '../support/ogd-csv-parser.js';
|
|
8
|
+
import { debugData } from '../support/logging.js';
|
|
9
|
+
import { SOURCE_ATTRIBUTION } from '../schemas/ogd-shared.js';
|
|
10
|
+
/**
|
|
11
|
+
* Map resolution names to URL suffixes.
|
|
12
|
+
* Monthly and yearly files contain all history (no _recent suffix).
|
|
13
|
+
* Daily has _recent (last ~2 years) and _historical variants.
|
|
14
|
+
*/
|
|
15
|
+
const RESOLUTION_SUFFIX = {
|
|
16
|
+
daily: 'd_recent',
|
|
17
|
+
monthly: 'm',
|
|
18
|
+
yearly: 'y',
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Build the download URL for a station's climate data.
|
|
22
|
+
*/
|
|
23
|
+
function buildDataUrl(station, resolution) {
|
|
24
|
+
const collection = station.network === 'nbcn-precip' ? 'ogd-nbcn-precip' : 'ogd-nbcn';
|
|
25
|
+
const abbrLower = station.abbr.toLowerCase();
|
|
26
|
+
const suffix = RESOLUTION_SUFFIX[resolution];
|
|
27
|
+
return `https://data.geo.admin.ch/ch.meteoschweiz.${collection}/${abbrLower}/${collection}_${abbrLower}_${suffix}.csv`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a MeteoSwiss timestamp (DD.MM.YYYY HH:MM) into an ISO-ish YYYY-MM-DD string.
|
|
31
|
+
*/
|
|
32
|
+
function parseTimestamp(ts) {
|
|
33
|
+
const match = ts.match(/^(\d{2})\.(\d{2})\.(\d{4})/);
|
|
34
|
+
if (!match)
|
|
35
|
+
return ts;
|
|
36
|
+
return `${match[3]}-${match[2]}-${match[1]}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Map CSV columns to structured measurements.
|
|
40
|
+
* Daily, monthly, and yearly CSVs have different column sets.
|
|
41
|
+
*/
|
|
42
|
+
function mapMeasurement(row) {
|
|
43
|
+
return {
|
|
44
|
+
date: parseTimestamp(row.reference_timestamp ?? ''),
|
|
45
|
+
// Temperature (available at all resolutions)
|
|
46
|
+
temperature_mean: parseNumeric(row.ths200d0 ?? row.ths200m0 ?? row.ths200y0 ?? null) ?? undefined,
|
|
47
|
+
temperature_max: parseNumeric(row.ths200dx ?? row.ths2dymx ?? row.ths2dyyx ?? null) ?? undefined,
|
|
48
|
+
temperature_min: parseNumeric(row.ths200dn ?? row.ths2dymn ?? row.ths2dyyn ?? null) ?? undefined,
|
|
49
|
+
// Precipitation (monthly/yearly)
|
|
50
|
+
precipitation: parseNumeric(row.rhs150m0 ?? row.rhs150y0 ?? null) ?? undefined,
|
|
51
|
+
// Sunshine (monthly/yearly)
|
|
52
|
+
sunshine_duration_min: parseNumeric(row.shs000m0 ?? row.shs000y0 ?? null) ?? undefined,
|
|
53
|
+
// Radiation (monthly/yearly)
|
|
54
|
+
radiation_w_m2: parseNumeric(row.ghs000m0 ?? row.ghs000y0 ?? null) ?? undefined,
|
|
55
|
+
// Wind (monthly/yearly)
|
|
56
|
+
wind_speed_m_s: parseNumeric(row.fhs010m0 ?? row.fhs010y0 ?? null) ?? undefined,
|
|
57
|
+
// Pressure (monthly/yearly)
|
|
58
|
+
pressure_hpa: parseNumeric(row.phsstam0 ?? row.phsstay0 ?? null) ?? undefined,
|
|
59
|
+
// Day counts (monthly/yearly)
|
|
60
|
+
frost_days: parseNumeric(row.ths00nm0 ?? row.ths00ny0 ?? null) ?? undefined,
|
|
61
|
+
summer_days: parseNumeric(row.ths25xm0 ?? row.ths25xy0 ?? null) ?? undefined,
|
|
62
|
+
heat_days: parseNumeric(row.ths30xm0 ?? row.ths30xy0 ?? null) ?? undefined,
|
|
63
|
+
ice_days: parseNumeric(row.ths00xm0 ?? row.ths00xy0 ?? null) ?? undefined,
|
|
64
|
+
tropical_nights: parseNumeric(row.ths20nm0 ?? row.ths20ny0 ?? null) ?? undefined,
|
|
65
|
+
rain_days: parseNumeric(row.rhs001m0 ?? row.rhs001y0 ?? null) ?? undefined,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Strip undefined values from a measurement to keep the response clean.
|
|
70
|
+
*/
|
|
71
|
+
function stripUndefined(m) {
|
|
72
|
+
const result = {};
|
|
73
|
+
for (const [key, value] of Object.entries(m)) {
|
|
74
|
+
if (value !== undefined) {
|
|
75
|
+
result[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetch climate data from MeteoSwiss NBCN stations.
|
|
82
|
+
*/
|
|
83
|
+
export async function getClimateData(params) {
|
|
84
|
+
let station;
|
|
85
|
+
let distance_km;
|
|
86
|
+
if (params.coordinates) {
|
|
87
|
+
const result = await findNearestNbcnStation(params.coordinates.lat, params.coordinates.lon);
|
|
88
|
+
station = result.station;
|
|
89
|
+
distance_km = result.distance_km;
|
|
90
|
+
debugData('[ogd-nbcn] Nearest station to coordinates: %s (%.1f km)', station.abbr, distance_km);
|
|
91
|
+
}
|
|
92
|
+
else if (params.station) {
|
|
93
|
+
station = await resolveNbcnStation(params.station);
|
|
94
|
+
debugData('[ogd-nbcn] Resolved station: %s (%s)', station.abbr, station.name);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw new Error('Either "station" or "coordinates" must be provided');
|
|
98
|
+
}
|
|
99
|
+
const resolution = params.resolution ?? 'monthly';
|
|
100
|
+
const url = buildDataUrl(station, resolution);
|
|
101
|
+
const abbrLower = station.abbr.toLowerCase();
|
|
102
|
+
const cacheKey = `climate/${station.network}-${abbrLower}-${RESOLUTION_SUFFIX[resolution]}.csv`;
|
|
103
|
+
const rows = await getLatin1CsvData(url, cacheKey, 'climate');
|
|
104
|
+
debugData('[ogd-nbcn] Fetched %d rows for %s (%s)', rows.length, station.abbr, resolution);
|
|
105
|
+
// Apply date filtering
|
|
106
|
+
let filtered = rows;
|
|
107
|
+
if (params.start_date) {
|
|
108
|
+
const start = params.start_date;
|
|
109
|
+
filtered = filtered.filter((row) => {
|
|
110
|
+
const date = parseTimestamp(row.reference_timestamp ?? '');
|
|
111
|
+
return date >= start;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (params.end_date) {
|
|
115
|
+
const end = params.end_date;
|
|
116
|
+
filtered = filtered.filter((row) => {
|
|
117
|
+
const date = parseTimestamp(row.reference_timestamp ?? '');
|
|
118
|
+
return date <= end;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Take the most recent rows (from the end), limited by params.limit
|
|
122
|
+
const limit = params.limit ?? 30;
|
|
123
|
+
const sliced = filtered.slice(-limit);
|
|
124
|
+
const data = sliced.map((row) => stripUndefined(mapMeasurement(row)));
|
|
125
|
+
return {
|
|
126
|
+
station: {
|
|
127
|
+
name: station.name,
|
|
128
|
+
abbreviation: station.abbr,
|
|
129
|
+
elevation: station.elevation,
|
|
130
|
+
coordinates: { lat: station.lat, lon: station.lon },
|
|
131
|
+
canton: station.canton,
|
|
132
|
+
distance_km,
|
|
133
|
+
network: station.network,
|
|
134
|
+
},
|
|
135
|
+
resolution,
|
|
136
|
+
data,
|
|
137
|
+
source: SOURCE_ATTRIBUTION,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=ogd-climate-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ogd-climate-data.js","sourceRoot":"","sources":["../../src/data/ogd-climate-data.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* Data layer for the getCurrentWeather tool.
|
|
3
3
|
* Fetches real-time measurements from the consolidated VQHA80.csv.
|
|
4
4
|
*/
|
|
5
|
-
import { getCsvData } from './ogd-data-store.js';
|
|
5
|
+
import { getCsvData, getLatin1CsvData } from './ogd-data-store.js';
|
|
6
6
|
import { resolveSmnStation, findNearestStation } from './ogd-smn-stations.js';
|
|
7
7
|
import { parseNumeric } from '../support/ogd-csv-parser.js';
|
|
8
8
|
import { reverseGeocodeSwiss } from '../support/reverse-geocode.js';
|
|
9
9
|
import { debugData } from '../support/logging.js';
|
|
10
10
|
import { SOURCE_ATTRIBUTION } from '../schemas/ogd-shared.js';
|
|
11
11
|
const REALTIME_CSV_URL = 'https://data.geo.admin.ch/ch.meteoschweiz.messwerte-aktuell/VQHA80.csv';
|
|
12
|
+
/** The 8 stations that have visual observations (OBS) data */
|
|
13
|
+
const OBS_STATIONS = new Set(['ALT', 'BAS', 'CHU', 'GSB', 'JUN', 'SAE', 'SIO', 'SMA']);
|
|
12
14
|
/** Cache reverse geocode results per station (coordinates are static) */
|
|
13
15
|
const reverseGeoCache = new Map();
|
|
14
16
|
async function getCachedReverseGeocode(lat, lon, stationAbbr) {
|
|
@@ -57,12 +59,23 @@ export async function getCurrentWeather(params) {
|
|
|
57
59
|
getCsvData(REALTIME_CSV_URL, 'measurements/VQHA80.csv', 'realtime', filter),
|
|
58
60
|
getCachedReverseGeocode(station.lat, station.lon, station.abbr),
|
|
59
61
|
]);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
let row = rows[0];
|
|
63
|
+
// Fallback for precipitation-only stations: VQHA80 does not include them
|
|
64
|
+
if (!row && station.network === 'smn-precip') {
|
|
65
|
+
debugData('[ogd-weather] Station %s is precip-only, fetching per-station CSV', station.abbr);
|
|
66
|
+
const abbrLower = station.abbr.toLowerCase();
|
|
67
|
+
const precipUrl = `https://data.geo.admin.ch/ch.meteoschweiz.ogd-smn-precip/${abbrLower}/ogd-smn-precip_${abbrLower}_t_recent.csv`;
|
|
68
|
+
const precipRows = await getCsvData(precipUrl, `measurements/smn-precip-${abbrLower}-t-recent.csv`, 'realtime');
|
|
69
|
+
// Use the most recent row (last in the CSV)
|
|
70
|
+
row = precipRows.length > 0 ? precipRows[precipRows.length - 1] : undefined;
|
|
62
71
|
}
|
|
63
|
-
const row = rows[0];
|
|
64
72
|
if (!row) {
|
|
65
|
-
throw new Error(`No data
|
|
73
|
+
throw new Error(`No current data available for station ${station.abbr} (${station.name})`);
|
|
74
|
+
}
|
|
75
|
+
// Enrich with visual observations for the 8 OBS stations
|
|
76
|
+
let visual_observations;
|
|
77
|
+
if (OBS_STATIONS.has(station.abbr)) {
|
|
78
|
+
visual_observations = await fetchVisualObservations(station.abbr);
|
|
66
79
|
}
|
|
67
80
|
return {
|
|
68
81
|
station: {
|
|
@@ -73,8 +86,9 @@ export async function getCurrentWeather(params) {
|
|
|
73
86
|
municipality: geo?.municipality,
|
|
74
87
|
canton: geo?.canton ?? station.canton,
|
|
75
88
|
distance_km,
|
|
89
|
+
network: station.network,
|
|
76
90
|
},
|
|
77
|
-
timestamp: row.Date ?? '',
|
|
91
|
+
timestamp: row.Date ?? row.reference_timestamp ?? '',
|
|
78
92
|
measurements: {
|
|
79
93
|
temperature: measurement(row, 'tre200s0', '\u00B0C'),
|
|
80
94
|
humidity: measurement(row, 'ure200s0', '%'),
|
|
@@ -89,7 +103,51 @@ export async function getCurrentWeather(params) {
|
|
|
89
103
|
pressure_sea_level: measurement(row, 'pp0qffs0', 'hPa'),
|
|
90
104
|
snow_depth: measurement(row, 'htoauts0', 'cm'),
|
|
91
105
|
},
|
|
106
|
+
visual_observations,
|
|
92
107
|
source: SOURCE_ATTRIBUTION,
|
|
93
108
|
};
|
|
94
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Fetch the latest visual observation for an OBS station.
|
|
112
|
+
* Returns undefined if the fetch fails (non-critical enrichment).
|
|
113
|
+
*/
|
|
114
|
+
async function fetchVisualObservations(abbr) {
|
|
115
|
+
try {
|
|
116
|
+
const abbrLower = abbr.toLowerCase();
|
|
117
|
+
const url = `https://data.geo.admin.ch/ch.meteoschweiz.ogd-obs/${abbrLower}/ogd-obs_${abbrLower}_d_recent.csv`;
|
|
118
|
+
const rows = await getLatin1CsvData(url, `observations/obs-${abbrLower}-d-recent.csv`, 'realtime');
|
|
119
|
+
if (rows.length === 0)
|
|
120
|
+
return undefined;
|
|
121
|
+
const latestRow = rows[rows.length - 1];
|
|
122
|
+
const cloudCover = parseNumeric(latestRow.nto000d0 ?? null);
|
|
123
|
+
// MeteoSwiss CSV uses '-' for "phenomenon not observed", which the CSV parser
|
|
124
|
+
// maps to null. For boolean observation flags, not-observed means false.
|
|
125
|
+
const parseFlag = (val) => {
|
|
126
|
+
if (val === null)
|
|
127
|
+
return false;
|
|
128
|
+
const num = parseNumeric(val);
|
|
129
|
+
return num === 1;
|
|
130
|
+
};
|
|
131
|
+
// Parse timestamp DD.MM.YYYY → YYYY-MM-DD
|
|
132
|
+
const ts = latestRow.reference_timestamp ?? '';
|
|
133
|
+
const match = ts.match(/^(\d{2})\.(\d{2})\.(\d{4})/);
|
|
134
|
+
const date = match ? `${match[3]}-${match[2]}-${match[1]}` : ts;
|
|
135
|
+
return {
|
|
136
|
+
date,
|
|
137
|
+
cloud_cover_percent: cloudCover ?? undefined,
|
|
138
|
+
is_clear_day: parseFlag(latestRow.nto002d0 ?? null),
|
|
139
|
+
is_overcast_day: parseFlag(latestRow.nto008d0 ?? null),
|
|
140
|
+
has_rain: parseFlag(latestRow.w1p012d0 ?? null),
|
|
141
|
+
has_rain_and_snow: parseFlag(latestRow.w2p001d0 ?? null),
|
|
142
|
+
has_snowfall: parseFlag(latestRow.w2p002d0 ?? null),
|
|
143
|
+
has_hail: parseFlag(latestRow.w3p002d0 ?? null),
|
|
144
|
+
has_fog: parseFlag(latestRow.w5p002d0 ?? null),
|
|
145
|
+
has_snow_coverage: parseFlag(latestRow.est000d0 ?? null),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
debugData('[ogd-weather] Failed to fetch visual observations for %s: %O', abbr, error);
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
95
153
|
//# sourceMappingURL=ogd-current-weather.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ogd-current-weather.js","sourceRoot":"","sources":["../../src/data/ogd-current-weather.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ogd-current-weather.js","sourceRoot":"","sources":["../../src/data/ogd-current-weather.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,14 +21,40 @@ function resolveFixturePath(url) {
|
|
|
21
21
|
// Map known URL patterns to fixture directories
|
|
22
22
|
if (url.includes('VQHA80.csv'))
|
|
23
23
|
return path.join(OGD_FIXTURES_ROOT, 'measurements', 'VQHA80.csv');
|
|
24
|
-
|
|
24
|
+
// SMN-precip metadata and data (must come BEFORE SMN to avoid 'smn' substring match)
|
|
25
|
+
if (url.includes('meta_stations.csv') && url.includes('smn-precip'))
|
|
26
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-smn-precip_meta_stations.csv');
|
|
27
|
+
if (url.includes('ogd-smn-precip') && url.includes('_t_recent.csv'))
|
|
28
|
+
return path.join(OGD_FIXTURES_ROOT, 'measurements', 'smn-precip-abe-t-recent.csv');
|
|
29
|
+
// SMN (weather station) metadata
|
|
30
|
+
if (url.includes('meta_stations.csv') && url.includes('smn'))
|
|
25
31
|
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-smn_meta_stations.csv');
|
|
32
|
+
if (url.includes('meta_parameters.csv') && url.includes('smn'))
|
|
33
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-smn_meta_parameters.csv');
|
|
34
|
+
// NBCN metadata and data (nbcn-precip must come BEFORE nbcn)
|
|
35
|
+
if (url.includes('meta_stations.csv') && url.includes('nbcn-precip'))
|
|
36
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-nbcn-precip_meta_stations.csv');
|
|
37
|
+
if (url.includes('meta_stations.csv') && url.includes('nbcn'))
|
|
38
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-nbcn_meta_stations.csv');
|
|
39
|
+
if (url.includes('ogd-nbcn') && url.includes('_m.csv'))
|
|
40
|
+
return path.join(OGD_FIXTURES_ROOT, 'climate', 'nbcn-bas-m.csv');
|
|
41
|
+
if (url.includes('ogd-nbcn') && url.includes('_d_recent.csv'))
|
|
42
|
+
return path.join(OGD_FIXTURES_ROOT, 'climate', 'nbcn-bas-d-recent.csv');
|
|
43
|
+
// OBS (visual observations) data
|
|
44
|
+
if (url.includes('ogd-obs') && url.includes('_d_recent.csv'))
|
|
45
|
+
return path.join(OGD_FIXTURES_ROOT, 'observations', 'obs-sma-d-recent.csv');
|
|
46
|
+
// Pollen metadata and data
|
|
47
|
+
if (url.includes('meta_stations.csv') && url.includes('pollen'))
|
|
48
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-pollen_meta_stations.csv');
|
|
49
|
+
if (url.includes('meta_parameters.csv') && url.includes('pollen'))
|
|
50
|
+
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-pollen_meta_parameters.csv');
|
|
51
|
+
if (url.includes('ogd-pollen') && url.includes('_d_recent.csv'))
|
|
52
|
+
return path.join(OGD_FIXTURES_ROOT, 'pollen', 'pzh-daily-recent.csv');
|
|
53
|
+
// Forecast metadata
|
|
26
54
|
if (url.includes('meta_point.csv'))
|
|
27
55
|
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-local-forecasting_meta_point.csv');
|
|
28
56
|
if (url.includes('meta_parameters.csv') && url.includes('forecasting'))
|
|
29
57
|
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-local-forecasting_meta_parameters.csv');
|
|
30
|
-
if (url.includes('meta_parameters.csv') && url.includes('smn'))
|
|
31
|
-
return path.join(OGD_FIXTURES_ROOT, 'metadata', 'ogd-smn_meta_parameters.csv');
|
|
32
58
|
// Forecast CSVs: match by parameter name
|
|
33
59
|
for (const param of [
|
|
34
60
|
'tre200dx',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ogd-data-store.js","sourceRoot":"","sources":["../../src/data/ogd-data-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAe,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEnF,MAAM,cAAc,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEjD;;;GAGG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAEpC,gDAAgD;IAChD,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ogd-data-store.js","sourceRoot":"","sources":["../../src/data/ogd-data-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Station metadata loader for MeteoSwiss NBCN (National Basic Climatic Network) stations.
|
|
3
|
+
* Loads and caches station metadata from the OGD STAC API.
|
|
4
|
+
* Merges NBCN (29 climate stations) and NBCN-precip (46 precipitation stations).
|
|
5
|
+
*/
|
|
6
|
+
import { getCollection } from './ogd-stac-client.js';
|
|
7
|
+
import { getLatin1CsvData } from './ogd-data-store.js';
|
|
8
|
+
import { parseNumeric } from '../support/ogd-csv-parser.js';
|
|
9
|
+
import { normalize } from '../support/normalize.js';
|
|
10
|
+
import { scoreNameMatch } from '../support/name-matcher.js';
|
|
11
|
+
import { findNearest } from '../support/haversine.js';
|
|
12
|
+
import { geocodeSwissLocation } from '../support/geocode.js';
|
|
13
|
+
import { debugData } from '../support/logging.js';
|
|
14
|
+
import { OGD_COLLECTIONS } from '../schemas/ogd-shared.js';
|
|
15
|
+
/** Max distance (km) for geocoding fallback — NBCN is sparser than SMN */
|
|
16
|
+
const MAX_GEOCODE_DISTANCE_KM = 80;
|
|
17
|
+
let stationCache = null;
|
|
18
|
+
let stationByAbbr = null;
|
|
19
|
+
/**
|
|
20
|
+
* Parse station metadata rows into NbcnStation objects.
|
|
21
|
+
*/
|
|
22
|
+
function parseStationRows(rows, network) {
|
|
23
|
+
return rows
|
|
24
|
+
.filter((r) => r.station_abbr)
|
|
25
|
+
.map((row) => ({
|
|
26
|
+
abbr: row.station_abbr ?? '',
|
|
27
|
+
name: row.station_name ?? '',
|
|
28
|
+
canton: row.station_canton ?? '',
|
|
29
|
+
elevation: parseNumeric(row.station_height_masl ?? null) ?? 0,
|
|
30
|
+
lat: parseNumeric(row.station_coordinates_wgs84_lat ?? null) ?? 0,
|
|
31
|
+
lon: parseNumeric(row.station_coordinates_wgs84_lon ?? null) ?? 0,
|
|
32
|
+
data_since: row.station_data_since ?? '',
|
|
33
|
+
network,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load NBCN + NBCN-precip station metadata from the OGD STAC API.
|
|
38
|
+
* Cached in memory after first load.
|
|
39
|
+
*
|
|
40
|
+
* @returns Array of all NBCN and NBCN-precip stations
|
|
41
|
+
*/
|
|
42
|
+
export async function loadNbcnStations() {
|
|
43
|
+
if (stationCache)
|
|
44
|
+
return stationCache;
|
|
45
|
+
debugData('[ogd-nbcn] Loading station metadata (NBCN + NBCN-precip)...');
|
|
46
|
+
const [nbcnCollection, precipCollection] = await Promise.all([
|
|
47
|
+
getCollection(OGD_COLLECTIONS.NBCN),
|
|
48
|
+
getCollection(OGD_COLLECTIONS.NBCN_PRECIP),
|
|
49
|
+
]);
|
|
50
|
+
const nbcnAsset = nbcnCollection.assets?.['ogd-nbcn_meta_stations.csv'];
|
|
51
|
+
if (!nbcnAsset) {
|
|
52
|
+
throw new Error('Station metadata asset not found in NBCN collection');
|
|
53
|
+
}
|
|
54
|
+
const precipAsset = precipCollection.assets?.['ogd-nbcn-precip_meta_stations.csv'];
|
|
55
|
+
if (!precipAsset) {
|
|
56
|
+
throw new Error('Station metadata asset not found in NBCN-precip collection');
|
|
57
|
+
}
|
|
58
|
+
const [nbcnRows, precipRows] = await Promise.all([
|
|
59
|
+
getLatin1CsvData(nbcnAsset.href, 'metadata/nbcn-stations.csv', 'metadata'),
|
|
60
|
+
getLatin1CsvData(precipAsset.href, 'metadata/nbcn-precip-stations.csv', 'metadata'),
|
|
61
|
+
]);
|
|
62
|
+
const nbcnStations = parseStationRows(nbcnRows, 'nbcn');
|
|
63
|
+
const precipStations = parseStationRows(precipRows, 'nbcn-precip');
|
|
64
|
+
stationCache = [...nbcnStations, ...precipStations];
|
|
65
|
+
stationByAbbr = new Map(stationCache.map((s) => [s.abbr.toLowerCase(), s]));
|
|
66
|
+
debugData('[ogd-nbcn] Loaded %d stations (%d NBCN + %d precip)', stationCache.length, nbcnStations.length, precipStations.length);
|
|
67
|
+
return stationCache;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Find the nearest NBCN station to a given WGS84 coordinate.
|
|
71
|
+
*/
|
|
72
|
+
export async function findNearestNbcnStation(lat, lon) {
|
|
73
|
+
const stations = await loadNbcnStations();
|
|
74
|
+
const result = findNearest(stations, (s) => s.lat, (s) => s.lon, lat, lon);
|
|
75
|
+
if (!result) {
|
|
76
|
+
throw new Error('No NBCN stations available');
|
|
77
|
+
}
|
|
78
|
+
return { station: result.item, distance_km: result.distance_km };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve a query to an NBCN station.
|
|
82
|
+
* Tries: exact abbreviation → fuzzy name → geocoding fallback.
|
|
83
|
+
*/
|
|
84
|
+
export async function resolveNbcnStation(query) {
|
|
85
|
+
const stations = await loadNbcnStations();
|
|
86
|
+
if (!stationByAbbr) {
|
|
87
|
+
throw new Error('NBCN station index not initialized');
|
|
88
|
+
}
|
|
89
|
+
const q = normalize(query.trim());
|
|
90
|
+
if (q === '') {
|
|
91
|
+
throw new Error('Station query must not be empty. Provide a station name (e.g., "Zurich"), ' +
|
|
92
|
+
'abbreviation (e.g., "SMA"), or address (e.g., "Bahnhofplatz 1 Bern").');
|
|
93
|
+
}
|
|
94
|
+
const exact = stationByAbbr.get(q);
|
|
95
|
+
if (exact)
|
|
96
|
+
return exact;
|
|
97
|
+
// Scored name match — word-boundary matches beat substring matches
|
|
98
|
+
let bestStation;
|
|
99
|
+
let bestScore = 0;
|
|
100
|
+
for (const s of stations) {
|
|
101
|
+
const score = scoreNameMatch(q, normalize(s.name));
|
|
102
|
+
if (score > bestScore ||
|
|
103
|
+
(score === bestScore && bestStation && s.name.length < bestStation.name.length)) {
|
|
104
|
+
bestScore = score;
|
|
105
|
+
bestStation = s;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (bestStation)
|
|
109
|
+
return bestStation;
|
|
110
|
+
debugData('[ogd-nbcn] No direct match for "%s", trying geocoding...', query);
|
|
111
|
+
const geocoded = await geocodeSwissLocation(query);
|
|
112
|
+
if (geocoded) {
|
|
113
|
+
const { station, distance_km } = await findNearestNbcnStation(geocoded.lat, geocoded.lon);
|
|
114
|
+
if (distance_km > MAX_GEOCODE_DISTANCE_KM) {
|
|
115
|
+
debugData('[ogd-nbcn] Geocoded "%s" too far from nearest station: %.1f km (limit: %d km)', query, distance_km, MAX_GEOCODE_DISTANCE_KM);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
debugData('[ogd-nbcn] Geocoded "%s" → %s, nearest station: %s (%s, %.1f km)', query, geocoded.name, station.abbr, station.name, distance_km);
|
|
119
|
+
return station;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const examples = stations
|
|
123
|
+
.filter((s) => s.network === 'nbcn')
|
|
124
|
+
.slice(0, 5)
|
|
125
|
+
.map((s) => `${s.abbr} (${s.name})`)
|
|
126
|
+
.join(', ');
|
|
127
|
+
throw new Error(`No climate station found for "${query}". ` +
|
|
128
|
+
`Try a Swiss location name, station abbreviation, or address. Examples: ${examples}`);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Clear the in-memory station cache. Useful for testing.
|
|
132
|
+
*/
|
|
133
|
+
export function clearNbcnStationCache() {
|
|
134
|
+
stationCache = null;
|
|
135
|
+
stationByAbbr = null;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=ogd-nbcn-stations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ogd-nbcn-stations.js","sourceRoot":"","sources":["../../src/data/ogd-nbcn-stations.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,31 @@ import { parseNumeric } from '../support/ogd-csv-parser.js';
|
|
|
8
8
|
import { normalize } from '../support/normalize.js';
|
|
9
9
|
import { debugData } from '../support/logging.js';
|
|
10
10
|
import { OGD_COLLECTIONS, SOURCE_ATTRIBUTION } from '../schemas/ogd-shared.js';
|
|
11
|
+
/**
|
|
12
|
+
* Map from the first 6 characters of a pollen parameter code to a short
|
|
13
|
+
* species display name. MeteoSwiss codes follow the pattern:
|
|
14
|
+
* k{a|h}{genus-4-chars}{resolution} where genus is a Latin-genus abbreviation.
|
|
15
|
+
*/
|
|
16
|
+
const POLLEN_SPECIES = {
|
|
17
|
+
kaalnu: 'Alder (Alnus)',
|
|
18
|
+
kabetu: 'Birch (Betula)',
|
|
19
|
+
kacory: 'Hazel (Corylus)',
|
|
20
|
+
kafagu: 'Beech (Fagus)',
|
|
21
|
+
kafrax: 'Ash (Fraxinus)',
|
|
22
|
+
kaquer: 'Oak (Quercus)',
|
|
23
|
+
khpoac: 'Grasses (Poaceae)',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Get a short display name for a pollen parameter code.
|
|
27
|
+
* Falls back to the raw code if the prefix is not recognized.
|
|
28
|
+
*
|
|
29
|
+
* @param code - Parameter shortname (or 6-char prefix) like 'kaalnud1'
|
|
30
|
+
* @returns Short species name like 'Alder (Alnus)', or the raw code
|
|
31
|
+
*/
|
|
32
|
+
function pollenDisplayName(code) {
|
|
33
|
+
const prefix = code.slice(0, 6);
|
|
34
|
+
return POLLEN_SPECIES[prefix] ?? code;
|
|
35
|
+
}
|
|
11
36
|
/**
|
|
12
37
|
* Fetch pollen data from MeteoSwiss OGD.
|
|
13
38
|
*
|
|
@@ -23,18 +48,6 @@ export async function getPollenData(params) {
|
|
|
23
48
|
throw new Error('Pollen station metadata asset not found');
|
|
24
49
|
}
|
|
25
50
|
const stationRows = await getLatin1CsvData(stationMetaAsset.href, 'metadata/pollen-stations.csv', 'metadata');
|
|
26
|
-
// Get parameter metadata for pollen type names
|
|
27
|
-
const paramMetaAsset = collection.assets?.['ogd-pollen_meta_parameters.csv'];
|
|
28
|
-
const paramRows = paramMetaAsset
|
|
29
|
-
? await getLatin1CsvData(paramMetaAsset.href, 'metadata/pollen-parameters.csv', 'metadata')
|
|
30
|
-
: [];
|
|
31
|
-
const paramNames = new Map();
|
|
32
|
-
for (const row of paramRows) {
|
|
33
|
-
const code = row.parameter_shortname ?? '';
|
|
34
|
-
const name = row.parameter_description_en ?? row.parameter_description_de ?? code;
|
|
35
|
-
if (code)
|
|
36
|
-
paramNames.set(code, name);
|
|
37
|
-
}
|
|
38
51
|
// Filter stations if search provided
|
|
39
52
|
let filteredStations = stationRows.filter((r) => r.station_abbr);
|
|
40
53
|
if (params.station) {
|
|
@@ -58,16 +71,34 @@ export async function getPollenData(params) {
|
|
|
58
71
|
const latestRow = rows[rows.length - 1];
|
|
59
72
|
if (!latestRow)
|
|
60
73
|
return null;
|
|
61
|
-
|
|
74
|
+
// Collect measurements, preferring d1 (calendar day 0-0 UTC) over d0 (6-6 UTC)
|
|
75
|
+
const d0Values = new Map();
|
|
76
|
+
const d1Values = new Map();
|
|
62
77
|
for (const [key, value] of Object.entries(latestRow)) {
|
|
63
78
|
if (key === 'station_abbr' || key === 'reference_timestamp' || key === 'Date')
|
|
64
79
|
continue;
|
|
65
80
|
const numVal = parseNumeric(value);
|
|
66
81
|
if (numVal === null)
|
|
67
82
|
continue;
|
|
83
|
+
const prefix = key.slice(0, 6);
|
|
84
|
+
const suffix = key.slice(6);
|
|
85
|
+
if (suffix === 'd1') {
|
|
86
|
+
d1Values.set(prefix, numVal);
|
|
87
|
+
}
|
|
88
|
+
else if (suffix === 'd0') {
|
|
89
|
+
d0Values.set(prefix, numVal);
|
|
90
|
+
}
|
|
91
|
+
// Ignore h0, y0, and other resolutions for daily pollen output
|
|
92
|
+
}
|
|
93
|
+
const pollen = [];
|
|
94
|
+
const allPrefixes = new Set([...d1Values.keys(), ...d0Values.keys()]);
|
|
95
|
+
for (const prefix of allPrefixes) {
|
|
96
|
+
const value = d1Values.get(prefix) ?? d0Values.get(prefix);
|
|
97
|
+
if (value === undefined)
|
|
98
|
+
continue;
|
|
68
99
|
pollen.push({
|
|
69
|
-
type:
|
|
70
|
-
value
|
|
100
|
+
type: pollenDisplayName(prefix),
|
|
101
|
+
value,
|
|
71
102
|
unit: 'particles/m\u00B3',
|
|
72
103
|
});
|
|
73
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ogd-pollen-data.js","sourceRoot":"","sources":["../../src/data/ogd-pollen-data.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQ/E
|
|
1
|
+
{"version":3,"file":"ogd-pollen-data.js","sourceRoot":"","sources":["../../src/data/ogd-pollen-data.ts"],"names":[],"mappings":"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"}
|