pretrip_source_met_norway 0.2.3
pretrip_source_met_norway: ^0.2.3 copied to clipboard
MET Norway locationforecast source for the pretrip_decision_advisor contract. Maps the global compact product to a WeatherForecast, with per-slice coverage. Pure Dart.
pretrip_source_met_norway #
MET Norway locationforecast/2.0/compact hourly-forecast source for the
pretrip_decision_advisor
contract. Fetches the publisher's GLOBAL forecast product and maps the FULL
hourly timeseries into the contract's WeatherForecast — the raw
MEASUREMENT an edge developer feeds to pretrip_decision_advisor to build
their own pre-trip "Before you drive" surface.
Pure Dart. Only http and pretrip_decision_advisor runtime dependencies.
New here? Start with QUICKSTART.md — empty
flutter create→ a rendered, offline pre-trip briefing in ~10 minutes, with the mandatoryUser-Agent, the honest-null + measured-visibility pairing, and the region/language swap (Akita 日本語 / Tromsø English) all worked through with compile-verified snippets.
Status #
Phase: maintained — v0.2.3. Requires pretrip_decision_advisor >=0.5.3 <0.7.0.
The HTTP client and mapper were extracted verbatim from the SNGNav app's
MetNorwayHourlyForecastProvider at v0.1.0. That direction has since
reversed: the app now consumes this package. Later versions are developed
here, and 0.2.3's mapping-coverage API (fetchForecastWithCoverage,
MetNorwayMappingCoverage) has no counterpart in the app.
The locationforecast product is global, so this serves a Nagoya commute as
well as a Tromsø one.
Safety contract — honesty rules (BINDING, verbatim) #
This is a SAFETY-class package: its output drives a driver's decision to set out before bad weather. These rules are binding and are enforced in the source, exercised by the tests, and stated here so an integrator cannot miss them:
- Visibility is NEVER estimated. The compact product carries no
visibility and no road-surface state.
visibilityMetersandestimatedRoadConditionare mapped asnullALWAYS — absence of data is never turned into presence (or absence) of a hazard the publisher did not forecast. Sky-state is not surface-state; we do not estimate what the publisher did not forecast. - A warning NEVER produces a number. This source emits a measurement (temperature, humidity, precipitation, and honest nulls), not a warning. It never fabricates a visibility metre value, a road-grip figure, or any number the publisher did not report. Turning a measurement into a warning is the advisor's job, downstream — never this source's.
- An observation is valid for the departure hour only. Each
HourlyForecastdescribes one hour. Slices without an hourly (next_1_hours) block — the 6-hourly tail of the timeseries — are skipped, not interpolated; the forecast simply ends where hourly resolution ends and the advisor's no-data handling takes over past that horizon. A slice missing the requiredair_temperatureis skipped, never guessed. - Null is the driver's own judgement, never a fabricated hazard.
fetchForecastreturnsnullwhen the response carries no usable hourly slice, and a missingmeta.updated_atreturnsnullrather than inventing an issue time (the advisor's staleness chip depends on it being real). When you receivenull, surface "no data" and hand the decision to the driver — never substitute a default hazard or a default all-clear. All fetch/parse failures surface asMetNorwayForecastExceptionornull; nothing is fabricated.
This contract is condition-GENERAL: visibility in metres and an hourly forecast serve every weather turmoil (fog, rain, dust, snow), not snow alone.
-
An all-clear can never be earned from this product alone, and that is permanent. Because
visibilityMetersandestimatedRoadConditionare ALWAYS null here,SnowAwarePretripAdvisor.brief()will THROWPretripAssessmentIncompleteExceptionrather than report "no hazard" it did not measure — on calm forecasts too. This is not a defect and not a gap we intend to close; the compact product does not carry those fields. UsebriefOrNull()and render "not assessed", or supply the missing measurement yourself (mergeObservedVisibility,estimatedRoadCondition). A HAZARD still reports from whatever WAS measured — the asymmetry is deliberate. -
A skipped slice is countable, and you can see it. The mapper skips a timeseries slice it cannot honestly use — an unparseable time, no
next_1_hours(the 6-hourly tail), or noair_temperature— and never interpolates. BecauseWeatherForecastcarries no coverage member, a slice we skipped used to be indistinguishable from an hour the publisher never issued. UsefetchForecastWithCoverage()(ormapLocationForecastWithCoverage()if you already hold the JSON) to get aMetNorwayMappingCoverage: slices seen, hours emitted, and the cause of every drop.unexpectedDropscounts only the drops that are NOT the publisher's documented 6-hourly shape. Bound: a consumer holding only the plainWeatherForecaststill cannot see the hole — that type belongs topretrip_decision_advisor.
Measurement vs warning — this package and its sibling #
The same MET Norway publisher feeds two packages in this family. Pick by what your surface needs:
| You need… | pub add |
It emits |
|---|---|---|
| the raw hourly forecast to reason over yourself (pre-trip departure-timing) | pretrip_source_met_norway (this) |
a MEASUREMENT — WeatherForecast (full hourly timeseries) |
| a ready-made source-neutral alert to merge with other regions' alerts | condition_aggregator_met_norway |
a WARNING — Advisory (CAP-class event) |
Both speak the same publisher endpoint. This package maps the full hourly timeseries so the advisor can search for a better departure window; the sibling maps a slice into a single in-trip advisory.
Sibling sources — same contract, pick by region #
The pretrip_source_* family all feed the same
pretrip_decision_advisor
and emit the SAME typed inputs, so you can swap region without changing your UI
code:
| Region / network | pub add |
Emits |
|---|---|---|
| Japan — JMA / AMeDAS | pretrip_source_jma |
measured VisibilityObservation (metres) |
| Finland — Fintraffic Digitraffic | pretrip_source_digitraffic |
measured VisibilityObservation (metres) |
| Global — MET Norway locationforecast | pretrip_source_met_norway (this package) |
hourly WeatherForecast |
Full reference integration (Flutter) #
A standalone, edge-developer-shaped Flutter app that assembles and RENDERS a
pre-trip briefing from pretrip_decision_advisor + pretrip_source_jma — no
SNGNav app widgets — lives at
examples/edge_dev_akita_briefing/.
Service trace (driver in unexpected weather; ≤4 hops) #
MET Norway locationforecast/2.0/compact feed
→ MetNorwayHourlyForecastProvider (this package)
→ pretrip_decision_advisor advisor (the edge developer composes)
→ integrator pre-trip surface warns the driver — and her family —
before she sets out into the whiteout
This package exists so an edge developer can warn a driver before she sets out — a commuter in Nagoya, an elderly driver in rural Akita (heavy-snow), a driver in Tromsø — and her family, especially in compound-failure conditions when standard navigation infrastructure has gone away. Package count is not the success metric; an edge developer running it and a driver seeing the briefing is.
Endpoint #
https://api.met.no/weatherapi/locationforecast/2.0/compact
No authentication required. MET Norway terms require an identifying
User-Agent naming your application plus a contact point — pass userAgent
to credit yourself (an empty UA is rejected before any request is made).
Coordinates are truncated to 4 decimals before sending (publisher
cache-friendliness AND a privacy posture — the driver's sub-11 m position is
not transmitted).
Mapping #
Per hourly slice (a slice is hourly iff it carries a next_1_hours block):
HourlyForecast field |
Source |
|---|---|
hour |
timeseries[i].time (UTC → local wall-clock) |
tempCelsius |
instant.details.air_temperature (slice skipped if absent) |
humidityRH |
instant.details.relative_humidity, or null |
precipitationMmPerHour |
next_1_hours.details.precipitation_amount, or null |
visibilityMeters |
null ALWAYS (not in the compact product) |
estimatedRoadCondition |
null ALWAYS (sky-state is not surface-state) |
WeatherForecast.issuedAt ← properties.meta.updated_at; the mapper returns
null when it is absent/unparseable rather than inventing an issue time.
Usage #
import 'package:pretrip_decision_advisor/pretrip_decision_advisor.dart';
import 'package:pretrip_source_met_norway/pretrip_source_met_norway.dart';
Future<void> main() async {
final provider = MetNorwayHourlyForecastProvider(
userAgent: 'your_app/1.0 contact@example.com',
);
try {
final forecast = await provider.fetchForecast(
latitude: 35.1709, // Nagoya
longitude: 136.8815,
);
if (forecast == null) {
print('No usable hourly forecast — hand the decision to the driver.');
return;
}
const advisor = SnowAwarePretripAdvisor();
// briefOrNull(), NOT brief(). From pretrip_decision_advisor 0.5.3 the
// advisor THROWS rather than report an all-clear it did not measure, and
// it fires on the COMMON path here — including a calm forecast. This
// product carries neither visibility nor road surface (see the honesty
// rules above), so a MET-Norway-only input can never earn "no hazard".
final briefing = advisor.briefOrNull(
forecast: forecast,
commute: CommuteShape(
plannedDeparture: DateTime.now().add(const Duration(minutes: 30)),
plannedDuration: const Duration(minutes: 30),
routeIdentifiers: const ['morning-commute'],
flexibility: CommuteFlexibility.discretionary,
),
profile: const DriverProfileSpec(
profileTag: 'demo',
reactionTimeSeconds: 1.5,
),
);
if (briefing == null) {
print('Verdict: not assessed — the forecast could not earn a '
'conclusion (no visibility or road-surface measurement).');
return;
}
print('Verdict: ${briefing.verdict.name}');
} finally {
provider.close();
}
}
See example/main.dart for the runnable version.
License + attribution (binding) #
This adapter package code is licensed under BSD 3-Clause.
MET Norway forecast data is dual-licensed (unless otherwise specified)
under the Norwegian Licence for Open Government Data (NLOD) 2.0 AND the
Creative Commons Attribution 4.0 International (CC BY 4.0) licence. The
locationforecast product is published by the Norwegian Meteorological
Institute (MET Norway) under both licences; an integrator may rely on either.
Attribution is REQUIRED at the consumer-facing surface, not optional. Per MET
Norway: "Credit should be given to The Norwegian Meteorological Institute,
shortened MET Norway, as the source of data." Surface the credit wherever the
data is shown (per-forecast detail, credits screen, about panel). MET Norway's
suggested credit is:
Data from MET Norway
(or "Based on data from MET Norway" where the data has been transformed). A link to the source where the data can be downloaded is also appreciated: https://api.met.no/.
The MET Norway terms additionally require the identifying User-Agent
described under "Endpoint". This package consumes the publisher data
unmodified; the mapping to WeatherForecast (selecting hourly slices,
converting UTC to local wall-clock, carrying honest nulls) is a derivation of
representation, not a modification of the underlying forecast data.
License #
BSD 3-Clause License. See LICENSE.