Wingfoilscout · Methodology
From eight data sources to three destinations: every threshold, every weighting and every exclusion that is actually in the code — and the places where the tool estimates instead of knowing.
Wingfoilscout does not rate spots, it rates hours. Every hour at every spot gets a score between 0 and 1; hard exclusions set it to 0 and record a reason. Everything else — sessions, destinations, ranking — is built from these hourly values by aggregating them. None of it is a black box: every hour knows why it is what it is.
Eight sources, all without an API key or an account. None of them is load-bearing: if one fails, the report runs without it and says in the log what is missing. That is deliberate — a tool that delivers nothing at all when someone else’s server goes down is worthless at the decisive moment.
| Source | provides | resolution | if it fails |
|---|---|---|---|
| Open-Meteo Forecast | wind, gust, direction, temperature, rain, CAPE, weather code, cloud cover, solar radiation, sunrise and sunset | 7–25 km | no report |
| Regional models | the same wind series, finer — overrides it hour by hour | 1–2.5 km | the coarse model stays |
| ICON-EPS ensemble | 40 runs of the same situation → probability | coarser | no damping, raw score only |
| Open-Meteo Marine | water temperature, wave height, water level (sea and lagoon) | 8 km | no tide, no wetsuit hint, no cold-water veto |
| OpenStreetMap / Overpass | shoreline → fetch rose per spot, protected areas | 36 directions, 25 km | sectors from the catalogue |
| OSRM | real drive time and road distance | — | straight line × 1.22 at 85 km/h, marked “(estimated)” |
| Park4Night | overnight spots, dogs must be allowed | top 8 destinations | destinations without a list of overnight spots |
| MeteoAlarm | official severe-weather warnings per region | — | no warning badges |
The prefilter runs before any network request and throws out whatever is out of the question anyway, in this order: reference spot only, disabled in the catalogue, excluded water type, heavy seagrass, standing depth throughout, dog ban and wingfoiling ban (both only if you have switched that on), out of season, outside the radius, too far to drive. It checks radius and drive time with the estimate (straight line × 1.22 at 85 km/h) plus a 35% allowance, so that a spot does not fail because of an overly pessimistic rule of thumb. Only after that are the real drive times fetched, and with them radius and upper limit apply exactly. Spots with an unknown water type deliberately stay in; the shoreline geometry later clarifies what kind of water the point lies in.
Three global models always run: dwd_icon_seamless as the primary model, with
ncep_gfs_seamless and ecmwf_ifs alongside. The primary model provides
the value, the others fill gaps — and above all, they measure the agreement.
Both requests, coarse and fine, explicitly use the nearest grid cell: otherwise
Open-Meteo would move a coordinate to a cell of similar elevation on land, and over
land the wind is systematically weaker because of surface roughness.
Before anything is scored, the spot’s wind factor (wind_factor
in the catalogue) scales wind and gusts. It is meant for spots where experience shows it blows
more or less than the models say; the logbook suggests it from the sessions you have logged.
1.15 means: it blows 15% more there. Currently it is set to 1.0 at all spots.
| Regional model | Short name | Grid | Home country |
|---|---|---|---|
| meteoswiss_icon_ch1 | CH1 | 1.0 km | Switzerland |
| meteofrance_arome_france_hd | AROME-HD | 1.5 km | France |
| italia_meteo_arpae_icon_2i | ICON-2I | 2.0 km | Italy |
| dwd_icon_d2 | ICON-D2 | 2.0 km | Germany |
| knmi_harmonie_arome_netherlands | HARMONIE | 2.0 km | Netherlands |
| dmi_harmonie_arome_europe | DMI | 2.0 km | Denmark |
| geosphere_arome_austria | AROME-AT | 2.5 km | Austria |
Which model covers which point is documented nowhere by Open-Meteo — the docs just say “Central Europe”. And a point outside the domain is answered with HTTP 400 instead of empty values, which in a batch request for twenty coordinates takes all the others down with it. Wingfoilscout therefore halves rejected groups until the outliers are pinned down, and remembers the result for each spot. This dance happens once, not on every run.
0.7 + 0.3 · agreement — you don’t plan a drive around an outlier. Since the
agreement never drops below 0.4, an hour on which the models disagree completely still keeps
82% of its score.Five sub-scores, each between 0 and 1, summed with their weights and divided by the sum of the weights. Then the model agreement as a factor. The weights are in the configuration and are an opinion, not a measurement — wind and direction together carry 58%, because without those two nothing works at all.
config.example.yaml. You can change them without having
to adjust anything anywhere else — the sum is always normalised.Two questions in one number. First: is the wind in the middle of a wing’s range or at its edge? Second: is it within the comfort band you have set? The sawtooth in the chart is not a bug, it is the quiver — with every change of wing, the centrality starts over.
A veto is not a downgrade but a stop: score 0, reason recorded, next hour. The order in the code is chosen deliberately — whatever makes the hour impossible in principle is checked first.
| # | Check | Threshold | Effect |
|---|---|---|---|
| 1 | Hour already past | time of the run | Veto |
| 2 | Water temperature — sea and lagoon only, with the “Water temperature and wave model” box ticked | < 7 °C | Veto |
| 3 | Daylight | sunrise + 30 min … sunset − 30 min | Veto |
| 4 | Tide window — only where the catalogue sets one | e.g. HW ± 2 h | Veto |
| 5 | Suitable wing in the quiver | 10 … 43 kn | Veto |
| 6 | Offshore with a lot of water downwind — only with offshore_veto_km above 0, off by default | km of open water downwind | Veto |
| 7 | Thunderstorm reported or air temperature out of range | WMO 95 / 96 / 99 · 7 … 35 °C | Veto |
| — | Close to a temperature limit | < 3 °C away | × 0.8 |
| — | Rain | from 0.6 mm/h · from 3.0 mm/h | linear … × 0.2 |
| — | Thunderstorm potential (CAPE) | from 1200 · from 2000 J/kg | × 0.7 · × 0.35 |
| — | Thunderstorm shadow | ± 2 h around a thunderstorm hour | × 0.4 |
The thunderstorm shadow is where the tool goes beyond a forecast portal: the model only flags the hour in which it calculates a thunderstorm at that grid point — out on the water, the window around it matters just as much.
Maintaining wind sectors by hand does not scale to 278 spots — currently 29 have them. Wingfoilscout therefore measures the wind angle from OpenStreetMap: from the spot’s coordinate, in 36 directions, up to 25 km each, until it hits land. That gives the fetch rose — how far the wind has travelled over water, from every direction. Hand-maintained sectors beat the geometry when both exist.
Everything else follows from two numbers of the rose: the fetch upwind and the room downwind.
The upwind fetch also yields the wave height, via the fetch-limited engineering approximation from the Shore Protection Manual:
The water scores come from the chop aversion in the configuration (default 0.7): at 0 the
water state does not matter, at 1 only flat water counts. Above 22 knots, everything except
flat loses another 10%.
This is explicitly an approximation. It assumes that the wind has been blowing from the same direction for long enough, and it uses the 10-metre wind instead of the wind stress factor. At the sea it is therefore replaced by Open-Meteo’s wave model wherever that provides a value — with swell, tide and seabed profile; the fetch is still used for the wind angle. On inland lakes the approximation remains. Where hand-maintained sectors apply, the water state comes from the sector and the wave model is shown for information only.
If a spot has neither a rose nor sectors, the direction is scored 0.6 — neutral. If it has sectors but none of them matches, it is 0.2: that is a statement, not a gap. The difference matters, because otherwise every imported spot without maintained sectors would become invisible.
Ora, Maloja wind, Breva, Maestral: at these spots the circulation is smaller than the model grid. The model sees 5 knots where there are actually 18. For 71 spots, the catalogue therefore records which thermal wind blows there, when, from which direction, how strongly and how reliably — with a source. From that, Wingfoilscout calculates whether it will blow today.
Three quantities decide that, and all three are backed by sources rather than guessed:
The third quantity is the solar energy received since sunrise, not the sunshine in that hour: the thermal lives on how much heat the morning has already put into the ground. From 1800 Wh/m² it counts as full; if the model has no radiation data, cloud cover is the fallback.
Reliability is no longer part of the assumed wind: up to 1.6.1, 16 knots at 70% gave 11 knots, a number that never occurs on any day — if the Ora blows, it is 16, otherwise 0. Reliability is a probability, and it now goes where probabilities belong: into the ranking of destinations, in place of the ensemble (section 07).
In the daily potential, the daily cycle cancels out. What remains is what actually differs today: solar radiation and opposing wind, times the spot’s reliability. Up to 1.5.1 this was the maximum over the session — and on every sunny day, that is the one hour in which all three ingredients happen to line up. In the run of 16 September, all five thermal destinations were therefore between 96 and 100%.
And one rule that is not a curve: some thermals are killed off by a particular background
flow. For the Maloja wind, according to SRF Meteo, an upper-level flow from north to south-east
is enough, and then it practically never blows. Such sectors are recorded for 28 spots in the catalogue
(suppressed_by) and set the potential hard to zero from 6 knots out of that
direction. Below 8 knots of background wind, the thermal also sets the direction; above that,
the model’s direction stays.
Wingfoilscout does not invent thermals. It only calculates them at spots where the catalogue says there is one. Where no source gives a strength — Chiemsee, Lake Constance, Lake Thun — none is assumed; the spot only appears as a candidate. Assumed wind is always marked as an assumption in the report, never as a measurement.
At the sea and in lagoons, Wingfoilscout fetches the water level from Open-Meteo’s marine model and calculates high and low tide from it to the minute: a parabola through the three hours around each turning point. The model itself is not accurate to the minute — it works on an 8 km grid, combines tide and wind set-up, and is not an official tide table. How far it deviates from a tide gauge has not yet been checked against a table.
Whether the tide applies at a spot is decided by the catalogue:
tidal: true or false, otherwise automatically — sea or lagoon with a
modelled tidal range of at least 0.5 m (tide.auto_hub_min). Currently nine spots
are set to “yes” by hand, and two lagoons behind dams to “no”, because there the 8 km grid sees
the open sea in front of them.
Without a tide window, the tide does not change any score. It is shown at the destination and in the hourly grid, and the report recalculates the tide state when you open it: “Now rising · high tide at 14:50”. Only a window in the catalogue turns it into a veto:
| tide: fahrbar | rideable during | Example |
|---|---|---|
| hochwasser | the hours around each high tide | HW ± 2 h |
| niedrigwasser | the hours around each low tide | LW ± 2 h |
| auflaufend | from low to high tide, turning point to turning point | LW → HW |
| ablaufend | from high to low tide | HW → LW |
What counts is the middle of the hour: “13:00” stands for 13:00 to 14:00 and, with high tide at 14:25 and ± 2 h, is included. Wingfoilscout does not ship any windows — which one a spot needs is known only to those who know the spot. Currently no spot has one set.
Scored hours become consecutive blocks, blocks become destinations, destinations become a ranking. Every stage throws information away — and exactly at these points arise the errors that you can no longer see in the report.
The same weather, calculated forty times with slightly perturbed initial conditions (ICON-EPS). The share of runs that reach the lower end of your quiver in at least two hours of the session is a more honest number than any single value — it tells you how stable the situation is. For this, the threshold is converted back for the regional model and the wind factor, so that the ensemble judges the same number that is shown in the report. Since 1.5.0 the share is not only shown in the report, it also has a say:
Multiplying by the raw share would empty the report whenever the weather is uncertain — it
would show nothing, even though there is something to decide. weight: 0.0 restores
the behaviour up to 1.4.3: the badge is shown and changes nothing.
There is a limitation: the probability is only fetched for the top 20 destinations. Anything ranked below that is neither rewarded nor penalised — and can therefore overtake a damped destination.
For thermal sessions, the spot’s reliability from the catalogue
(reliability, 0.6 if not specified) takes the place of the ensemble, with the same
damped factor and for all thermal sessions, not just the top twenty. There, the ensemble sees
the model wind of 5 knots and reported 0% — up to 1.6.1, that halved precisely the sessions the
tool had specifically corrected.
Finally, each destination comes with a Plan B: the best other destination
within 120 km of estimated road distance (drive.plan_b_km, straight line × 1.22)
with at least one day in common.
This list is not small print but part of the method. Each item is a place where a number in the report looks more confident than it is.
All numbers on this page come from the code and from
config.example.yaml, as of version 2.1.0; SCORING.md in the repository
says the same in text form, step by step. Anyone who changes the weights, thresholds or the
comfort band has to change this page as well — it describes the defaults, not a law of nature.
A test in the repository will remind you when a number is no longer right.