Skip to content

SkySight layers & regions — integration investigation

Moved from vulture-ios/Docs/skysight-layers-investigation.md

Status: verified against the live service on 2026-08-26 (the owner’s account, region EUROPE/ICOND2, via the logged-in web app at https://skysight.io/secure/, its JS bundle assets/main-D0tYFImc.js, the page global window.params, and unauthenticated curl probes of the tile endpoints). Written as the hand-off for the implementation pass on the SkySight layer picker.

  • The app’s picker is server-driven: it lists whatever GET /api/layers?region_id=… returns (+ a synthetic satellite). Only the grouping/ordering is hard-coded (§2). The website no longer calls /api/regions or /api/layers at all — its catalogue is compiled into the bundle (regions) and a page global params (layers) — so the app-facing API and the website can drift; the tile probes below are the ground truth.
  • Dead on the server (still in the app’s tables): tcc, sunlight, sfctemp, w_8000, w_9000, pfdtotclub, bom_rain (EUROPE), and the separate …/bsratio/ tile URL. The picker only shows what the API returns, so these matter mainly for the special-case code, but tcc is worth checking in the app: the site renders “Cloud Cover” from CFRACL.
  • New on the server, reachable as vector tiles today (drop-in for the existing fill-layer path): uv_10 (surface wind, replaces sfcwind0), zsfclclmask_low, sigwx (significant weather), potfd_as (airspace-aware XC speed), oktas_cu, CFRACL (also carries CFRACM/ CFRACH as source-layers).
  • New, composite (webp raster) only: t2c, sfcdewpt, bltopwind, thermalwave, mslpress, freezing, cceil, ctops, vfr, visibility, turbulence_low/mid/high, densityaltitude, ice_base, ice_top, carb_icing, ridge, wind-at-altitude uv_{alt}. These are blocked on Mapbox v11: the tiles carry raw values in the red channel, not colours, and v6 cannot run a colour ramp over a raster (§5).
  • edge.skysight.io vector tiles require Origin: https://skysight.io (400 without). The URLSession swizzle in the app is load-bearing.
  • Regions: 19 in the catalogue; the ICON family is now ICON, ICONEU, ICOND2 (new, high-res Europe). The app excludes ICON/ICONEU by id, so ICOND2 would pass through — its layers are composite-only (§5), which the current vector-fill code cannot draw.

1. Where the app’s layers come from (unchanged)

Section titled “1. Where the app’s layers come from (unchanged)”
StepCodeNotes
Sign inPOST https://skysight.io/api/auth, header X-API-KEY: Vulture, body {username, password}SkySightAuthenticationRequest; response SkySightUserAuthInfo { key, allowed_regions }
RegionsGET /api/regions with X-API-KEY: <key>[SkySightRegion { id, name, tz }]
LayersGET /api/layers?region_id=<id>[SkySightLayer { id, name, description?, legend? }], legend = { unit_type, colors: [{ name, value, color:[r,g,b] }] }
SyntheticSkySightLayer.satelliteappended by SkySightLayersViewModel to every region

Both endpoints answer 401 {"message": "Bad API Key"} without the key, so their live payloads could not be captured in this session (the website does not call them; see §10 for how to capture them from the app). Regions and per-region layers are disk-cached for 7 days (SkySightManager, skysight-regions.cache, skysight-layers.cache); a server-side change is invisible until the weekly invalidation.

Key files:

  • Modules/App/Sources/Components/SkySight/SkySightManager.swift — auth, regions, layers, caches, allowedRegions(for:).
  • Modules/App/Sources/Components/SkySight/Request/* — the three requests; URLSession+Origin.swift injects the Origin header.
  • Packages/Scenes/Sources/Scenes/Screens/SkySight/Models/* — value types; SkySightLayer.swift — sections + ordering.
  • Modules/App/Sources/Components/SkySight/Extensions/SkySightOverlay+Tile.swift — every tile URL and per-layer special case.
  • Modules/App/Sources/Modules/Detail Map/MapViewDataSource+Weather.swift — how the overlay is put on the map.

2. What the app hard-codes (grouping, ordering, special cases)

Section titled “2. What the app hard-codes (grouping, ordering, special cases)”

Grouping/ordering (SkySightLayer.sectionKey, [SkySightLayer].reorder()):

SectionLayer ids, in display orderServer status 2026-08-26
Thermalspfdtot, potfd, wstar_bsratio, hwcrit, dwcrit, starsall live
Cloudssatellite, tcc, zsfclcldif, zsfclclmask, zblclmask, cape, accrain, CFRACM, CFRACH, sunlight, sfctemp, sfcdewpttcc, sunlight, sfctemp dead; sfcdewpt composite-only
Windridge, sfcwind0, blwind, wblmaxminridge composite-only; sfcwind0 still served but superseded by uv_10
Wavew_1000 … w_9000w_1000–w_7000 live; w_8000, w_9000 dead
Experimentalfreezing, cceilcomposite-only
Othersanything else, unsorted—

Ids referenced elsewhere in the app but missing from the section table: CFRACL (in the ordering list only — lands in Others while CFRACM/CFRACH sit in Clouds), pfdtotclub and bom_rain (spread/legend special cases; both dead on EUROPE), bsratio (companion overlay, see §5).

Per-layer special behaviour in SkySightOverlay+Tile.swift / MapViewDataSource+Weather.swift:

Layer id(s)App behaviourLive finding
satelliteRaster satellite.skysight.io/tiles/{z}/{x}/{y}@2x?date=YYYY/MM/DD/HHmm&mtg=true (10-min steps, max zoom 7) + rain radar /radar/… at 0.6 opacity; date capped at today; period → UTC using TimeZone.currentSite uses the identical tiles and radar builders, plus a new /lightning/{z}/{x}/{y}@2x?date=… vector source (source-layer lightning, z0–4).
pfdtotPeriod forced to 1800; period stepping disabled; hard-coded 10-stop legend (50–950)Confirmed: pfdtot only exists at 1800 (204 at other hours); site legend is 10 stops 50 … >950. The site palette differs from the app’s (§7).
pfdtot, pfdtotclub, bom_rainSpread markers from cloudfunctions.net/tile?query=spread&region=GFS&hour=1500Site fetches spread from https://edge.skysight.io/GFS/{YYYYMMDD}/{HHmm UTC}/spread/{z}/{x}/{y} (z1–6, source-layer spread, question icon by spread attribute), only when the PFD “Uncertainty” toggle is on, and at the selected hour (not fixed 1500). The cloudfunctions URL still answers 200.
wstar_bsratioCompanion fill from edge…/{period}/bsratio/{z}/{x}/{y}, patterns bs_{idx}That URL returns 204 (empty). The stipple lives inside the wstar_bsratio tile as source-layer bsratio (verified in the tile bytes: layers 1400, 1400_extra, bsratio; keys idx, conditions, src). Same tile, second style layer.
hwcrit, dwcrit, zsfclclmask on EUROPE/ICON*Legend cut at value 2400 and colours rescaledSite: for those regions it sets min = step = 200 for hwcrit/dwcrit/cucloudfrac and step = 200 for zsfclclmask, i.e. legend 200 … >2600 (13 stops) and everything above clamps to the top colour — no rescaling (§7).
all other layersVector edge.skysight.io/{region}/{YYYYMMDD}/{HHmm}/{layer}/{z}/{x}/{y}, source-layer = period, colour = legend.colors[idx], zoom 0–5Same URL shape; site uses minzoom 3, maxzoom 5 (ALPS 6), and additionally draws the tile’s {HHmm}_extra source-layer as a symbol layer (icon cc7 where feature z ≤ zoom: condition markers).
wind arrowscloudfunctions.net/tile?query=wind&region&date&hour&alt=0…9, label alt × 600 mStill answers 200, but the site no longer uses it: barbs/arrows are GPU-rendered from composite uv_{alt} webp tiles (§6).

Periods are hard-coded 0800–2000 half-hourly (String.rawPeriods), default 1400; forecast dates unrestricted.

The website ships a static catalogue (const Mr in the bundle). The user never picks a region: the map auto-selects from the viewport (setRegionFromBounds), with only a “SkySight ⇄ ICON” toggle (Europe: EUROPE /ALPS vs ICONEU/ICOND2, chosen by bounds + zoom; ALPS only when zoomed past 5.5 within a day). Per-account entitlement is not visible on the website (the JWT carries region: EUROPE, paid, aircraft only); the app’s allowed_regions from /api/auth remains the only entitlement source and was not re-verified.

idnamemodel_familytz (tile hour base)categorynotes
GFSGlobal - NOAAnoaaUTCGlobalno polygon, global bbox
ICONDWD-ICON GlobaldwdUTCGlobalcomposite selector ICONCH1; vector maxzoom 4
ICONEUEurope-ICONEUdwdUTCEurope
ICOND2ICOND2dwdUTCEuropenew; polygon ≈ D-A-CH-NL-BE-DK
EUROPECentral EuropessEurope/WarsawEurope
ALPSAlpsssEurope/BerlinEuropevector maxzoom 6
MIDDLE_EASTMiddle EastssAsia/JerusalemAsia
INDOCHINAIndochinassAsia/BangkokAsia
JAPANJapanssAsia/TokyoAsia
SANEWSouth AfricassAfrica/JohannesburgAfrica
EAST_AUSEast AustraliassAustralia/NSWOceaniaobs stations
WAWest AustraliassAustralia/PerthOceaniaobs stations
NZNew ZealandssPacific/AucklandOceania
BRAZILBrazilssAmerica/Sao_PauloSouth America
ARGENTINA_CHILEArgentina/ChilessAmerica/Argentina/Buenos_AiresSouth Americawave range −3.25…5.75
EAST_USUS/Canada EastssAmerica/New_YorkNorth America
WEST_USUS/Canada WestssAmerica/Los_AngelesNorth America
HRRRHRRRhrrrAmerica/Los_AngelesNorth Americacomposite for b7 layers
HRRRAKAlaskahrrrPacific/SamoaNorth America

Region facts that affect the app:

  • Hourly regions: ICON, ICONEU, ICOND2, HRRR, HRRRAK step 3600 s; every ss/noaa region steps 1800 s. The app’s half-hourly period wheel would request non-existent half hours on DWD/HRRR regions.
  • Region time zone drives the tile hour: the site formats the vector path’s HHmm in the region’s tz (so ICOND2 paths are in UTC, EUROPE in Europe/Warsaw). The app passes the picked period through unchanged and labels it with the region tz — consistent, as long as the API’s tz matches the catalogue above.
  • Composite (webp) paths use UTC for both date and hour, unlike the vector paths.
  • The app’s hard-coded exclusion of ICON/ICONEU does not cover ICOND2.

4. Layer catalogue — verified (window.params + sidebar)

Section titled “4. Layer catalogue — verified (window.params + sidebar)”

params[id] = { name, type, unit, min, max, step, aircraft, models }. type ∈ vert | hgt | spd | temp | dist | xcspd | "" drives unit conversion (the app’s SkySightLegend.unitType uses the same spd/vert/ hgt vocabulary). models lists the model families the layer exists for (ss, noaa, hrrr, dwd, cosmo), which is the per-region availability rule (region → model_family in §3). aircraft: G glider, P paraglider, H hang glider, B balloon, A plane, M model aircraft.

Glider sidebar as rendered for EUROPE (Dutch UI → id; ✱ = hidden on ICON*/HRRR* regions, † = only on NZ, JAPAN, ARGENTINA_CHILE, ICON*, EUROPE, ALPS):

Site groupItem → idtype / unit / rangemodels
ThermalsPotential Flight Distance → pfdtot (+ spread uncertainty)dist km 50–950/100ss, hrrr, dwd
XC Speed → potfd (potfd_as when airspace on)xcspd kph 40–160/10ss, hrrr, dwd, noaa
Thermal Strength & B/S → wstar_bsratio (+ bsratio source-layer)vert m/s 0.5–4/0.5ss, noaa, hrrr, dwd
Height of Thermals → hwcrithgt m 300–4800/300ss, noaa, hrrr, dwd
Depth of Thermals (AGL) → dwcrithgt m 300–3600/300ss, noaa, hrrr, dwd
Significant Weather → dwcrit + sigwx——
CloudsCu Overview ✱ → cucloudfrac (rendered from the hwcrit tile)hgt m 300–4800/300ss, noaa, hrrr, dwd
Cu Depth → zsfclcldifhgt m −1000–700/100ss, noaa, hrrr, dwd
Cu Cloudbase → zsfclclmask + zsfclclmask_lowhgt m 0–4800/300ss, noaa, hrrr, dwd
Cu Coverage ✱ → oktas_cu% 0–100/12.5ss, noaa, hrrr, dwd
Cloud Cover → CFRACL tile, source-layers {HHmm}, CFRACM, CFRACH% 5–95/15ss, noaa, hrrr, dwd
CAPE / Storms → capeJ 100–3100/300ss, noaa, hrrr, dwd
Rain → accrainmm 0–5/0.5 (last half hour)ss, noaa, hrrr, dwd
WindOff / Barbs / Arrows / Particles (overlay on any layer)——
Wind at {alt} → uv_{alt} composite, alt ∈ §6 listspd—
Surface Wind → uv_10spd m/s 0–12/1ss, noaa, hrrr, dwd
Thermal Layer Wind (Avg) → blwindspd m/s 1–16/1ss, noaa, hrrr, dwd
Thermal Top Wind → bltopwindspd m/s 1–19/1ss, noaa, hrrr, dwd
Convergence → wblmaxminvert m/s −0.25–0.75/0.1ss, hrrr, cosmo
Ridge Lift → ridgevert m/s 0.1–1.2/0.1ss, hrrr, cosmo
Valley Winds † → uv_10 filtered——
Wave (dropdown)Wave X-section, Thermal Wave → thermalwave, Vertical Velocity 1000…7000 m → w_1000…w_7000, 3D Wavevert m/s −2.6–4.6/0.4ss, hrrr, cosmo
GeneralGlide Range (tool), Terrain Relief (basemap)——
Last 72h Rainfall → bom_rainmm 0–45/3(Australia only in practice)
Surface Temperature → t2ctemp °C −6–48/2ss, noaa, hrrr, dwd
Surface Dewpoint → sfcdewpttemp °C −16–20/1ss, noaa, hrrr, dwd
MSL Pressure → mslpressmBar 984–1034/2ss, noaa, hrrr, dwd
Low/Mid/High level cloud cover → CFRACL/CFRACM/CFRACH% 5–95/15ss, noaa, hrrr, dwd
Thermal Hotspots → thermals (kk7 data)——
Freezing Level ✱ → freezinghgt m 300–5700/300ss, hrrr
Density Altitude (Surface) → densityaltitudehgt m 300–4800/300ss, noaa, hrrr, dwd
Overdevelopment → zblclmask% 5–95/10ss, hrrr, dwd
Smoke → smokemg/m² 25–350/25hrrr
Plane-only (not in glider menu)vfr, turbulence_low/mid/high, visibility, cceil, ctops, ice_base, ice_top, carb_icing—ss (icing also hrrr)

Also in params but not in a glider menu: stars (0.5–5.5, PG/HG/model star rating), pfdtothg/potfd_hg (PG/HG variants), w_*_combo, w_9000 (listed but 404 on every endpoint), U/V/blwindU/blwindV (wind components), windataltitude.

Rendering rule from the bundle (op(id, region)):

composite (webp raster) if id starts with "w_"
or id ∈ y7 = {carb_icing, cceil, ctops, densityaltitude, freezing,
ice_base, ice_top, t2c, sfcdewpt, sfcwind0,
turbulence_low/mid/high, uv_10, vfr, visibility,
blwind, bltopwind, wblmaxmin, ridge, mslpress, thermalwave}
or (region.target ∈ {ICON, ICONEU, ICOND2, HRRR}
and id ∈ b7 = {CFRACL, CFRACM, CFRACH, hwcrit, oktas_cu, pfdtothg,
potfd, potfd_as, potfd_hg, potfd_hg_as, smoke,
stars, wstar_bsratio, zblclmask})
vector (edge MVT) otherwise
PFD (pfdtot) composite on the ICON/HRRR targets, vector elsewhere

URL shapes (all verified 200 with curl):

KindTemplateAuthZoom
Vectorhttps://edge.skysight.io/{target}/{YYYYMMDD}/{HHmm region-local}/{id}/{z}/{x}/{y}needs Origin: https://skysight.io (400 otherwise); gzip MVT3–5 (ALPS 6; PFD on ICON/GFS 4; wave on ICONEU/ICOND2 6)
Compositehttps://cf.skysight.io/{composite|composite-ss}/{YYYYMMDD UTC}/{HHmm UTC}/{id}/{z}/{x}/{y}.webp (composite-ss for ss regions, composite for others, ICONCH1 for global ICON)none3–7 (ICON 4; ridge/uv_10 on ss: 9)
Composite, legacy formhttps://cf.skysight.io/tile/{target}/{YYYYMMDD}/{HHmm}/{id}/{z}/{x}/{y}.webpnone
Spreadhttps://edge.skysight.io/GFS/{YYYYMMDD}/{HHmm UTC}/spread/{z}/{x}/{y}Origin1–6
Freshnesshttps://cf.skysight.io/tile/updated/{target}/{YYYYMMDD}/{HHmm} → {"unix":"1787744469","init":""}none
Satellitehttps://satellite.skysight.io/tiles/{z}/{x}/{y}@2x?date=YYYY/MM/DD/HHmm&mtg=true (raster, max 8)none
Radarhttps://satellite.skysight.io/radar/{z}/{x}/{y}@2x?date=… (raster 512)none
Lightninghttps://satellite.skysight.io/lightning/{z}/{x}/{y}@2x?date=… (vector, source-layer lightning)none0–4
Observationshttps://edge.skysight.io/stations/{YYYYMMDD/HHmm}/{z}/{x}/{y} (vector, source-layer stations)Origin4–12
Legacy windhttps://us-central1-skysight-1260.cloudfunctions.net/tile?query=wind&region&date&hour&altnonestill 200

Composite tiles are data, not pictures — and that blocks them on Mapbox v6

Section titled “Composite tiles are data, not pictures — and that blocks them on Mapbox v6”

Verified by decoding the tiles (2026-08-26). A composite webp carries the forecast value in the pixel’s red channel, scaled linearly across the layer’s [min, max] from params; green and blue are zero and alpha is the mask. Decoding composite-ss/20260826/1400/t2c/4/8/5.webp against t2c’s range of −6…48 °C gives a median of 24 °C and a 1–99 % span of 15–32 °C — a European August afternoon. The same for freezing over 300…5700 m gives a median of 3646 m. Layers with two components (thermalwave) also use blue and a graded alpha.

So the site is not downloading coloured maps: it downloads numbers and colours them in a shader (dataType: "scalar", scale, valueChannel, maskAbove/maskBelow). Dropping a composite URL into an MGLRasterTileSource would paint a black-to-red gradient, not a forecast.

Consequence: every composite-only layer is blocked on the Mapbox v11 migration. Mapbox v6 has no way to run a colour ramp over a raster. v11 does, declaratively — raster-color with a colour-ramp expression, raster-color-mix to select the red channel ([1, 0, 0, 0]), and raster-color-range set to the layer’s [min, max]. That is the same three pieces of information params already gives us, so the work at v11 time is a table of ranges plus one style layer, not a shader project.

Until then the app can only draw the vector layers, which is why the DWD (ICON) family is excluded from the region list (§8.D): most of its layers are composite-only.

Serving matrix, tile 4/8/5 (EUROPE) at 1400 on 2026-08-26 (200 served, 204/404 nothing). edge = vector EUROPE, ss = composite-ss, D2 = composite for ICOND2:

id edge ss D2 | id edge ss D2 | id edge ss D2
pfdtot(1800) 200 404 200 | CFRACL 200 200 200 | w_1000..w_7000 200 200 200
potfd 200 200 200 | CFRACM/H 200 200 200 | w_8000, w_9000 204 404 404
potfd_as 200 200 200 | sunlight 204 404 404 | freezing 204 200 404
wstar_bsratio 200 200 200 | sfctemp 204 404 404 | cceil, ctops 204 200 404
hwcrit 200 200 200 | t2c 204 200 200 | vfr, visibility 204 200 404
dwcrit 200 200 200 | sfcdewpt 204 200 200 | turbulence_* 204 200 404
stars 200 200 200 | ridge 204 404 200 | densityaltitude 204 200 404
tcc 204 404 404 | sfcwind0 200 200 404 | ice_base/top 204 200 404
zsfclcldif 200 200 200 | uv_10 200 200 200 | carb_icing 204 200 404
zsfclclmask 200 200 200 | blwind 200 200 200 | oktas_cu 200 200 200
zsfclclmask_low 200 404 404 | bltopwind 204 200 200 | cucloudfrac 204 404 404
zblclmask 200 200 200 | wblmaxmin 200 200 200 | smoke 204 404 404
cape, accrain 200 200 200 | thermalwave 204 200 200 | bsratio (URL) 204 404 404
mslpress 204 200 200 | pfdtotclub 204 404 404 | bom_rain 204 404 404

(ridge 404 on composite-ss at z4 may just be zoom — the site gives it maxzoom 9; re-probe at z6+ before concluding.)

6. Time, horizon, wind and wave — verified

Section titled “6. Time, horizon, wind and wave — verified”
  • Slider: 08:00–20:00 in the user’s zone (06:00 start for balloons), step 1800 s on ss/noaa regions, 3600 s on ICON*/HRRR*. Tiles are actually served from 06:00 to 20:00 (EUROPE hwcrit: 0600–2000 → 200; 2030+ → 204).
  • Horizon: EUROPE pfdtot served for today + 5 days (0826–0831; 0901 → 204) and past days are still served (0825, 0820 → 200). ICOND2 composite reached only +1 day at probe time.
  • updated endpoint gives the model run timestamp per target/date/hour — useful for an “Updated: 13:38” chip like the site’s.
  • Wind overlay (any layer): types off / barbs / arrows / particles. Altitude options for ss regions: 120, 240, 360, 480, 600, 720, 840, 960, 1080, 1200, 1320, 1440, 1560, 1680, 1800, 2100, 2400, 2700, 3000, 3300, 3600, 4200, 4800 m (others: 600 → 4800 by 300). Data: composite uv_{alt} webp (uv_600 observed) — not the cloudfunctions alt=0…9 index the app uses, although that endpoint still answers.
  • Wave: w_1000 … w_7000 for gliders (ss/hrrr/cosmo models only — none on DWD regions); vector and composite exist, the site draws composite. The ICON toggle hides the Wave group entirely.
  • pfdtot on the site: labels 50, 150 … 850, >950, colours rgb(0,0,143) (0,68,255) (0,248,255) (5,203,92) (104,208,9) (255,247,0) (255,180,0) (255,75,0) (213,0,35) (94,0,131). The app’s hard-coded override (commit 8be1e4a13) uses a different, muted palette ([110,100,190] … [155,55,140]). Decide which is wanted; the ten-stop structure matches.
  • hwcrit on EUROPE: labels 200, 400 … 2400, >2600, colours rgb(0,0,143) (0,23,255) (0,158,255) (0,244,219) (5,203,92) (60,195,12) (191,236,4) (255,230,0) (255,180,0) (255,104,0) (255,17,0) (183,0,59) (94,0,131), with indices ≥ 12 clamped to the last colour. The app instead truncates the API legend at "2400" and rescales the colours — a different look; consider mirroring the site (clamp, don’t rescale).
  • Unit conversion on the site: type from params (hgt/spd/vert/ temp/dist/xcspd) with the user’s units; matches the app’s unit_type handling for the three it knows, but the app has no temp, dist, xcspd cases (falls back to the stop name).
  • Layers with no numeric legend on the site (AC set): carb_icing, turbulence_*, vfr (categorical), thermalwave (Likely…Nil).

A. Fix in the current vector path (small, no new rendering):

  1. CFRACL → Clouds section (sectionKey), and add the new vector ids to the section table so they don’t land in Others: uv_10 (Wind), potfd_as (Thermals), oktas_cu (Clouds), zsfclclmask_low (Clouds), sigwx (Thermals/Experimental), stars already there.
  2. B/S ratio stipple: read source-layer bsratio from the existing wstar_bsratio source instead of the dead /bsratio/ request.
  3. _extra symbol layer (condition markers, icon cc7 where z ≤ zoom) — optional parity item; needs the cc7 sprite.
  4. Cloud Cover: if the API still returns tcc, its tiles are empty; render “Cloud Cover” from CFRACL with the three source-layers, or drop tcc from the sections (it will still be listed if the API sends it).
  5. Half-hour periods on DWD/HRRR regions request non-existent tiles — gate the period list on the region (hourly for ICON*/HRRR*), and consider exposing 06:00–07:30 which the server serves.
  6. Spread markers: switch to edge.skysight.io/GFS/…/spread at the selected UTC hour (the app hard-codes 1500 and the cloudfunctions host) and tie them to an “Uncertainty” toggle as the site does.
  7. Drop w_8000/w_9000, sunlight, sfctemp, pfdtotclub, bom_rain from the tables (or leave — harmless if the API stops sending them).
  8. reorder() comparator is not a strict weak ordering (unknown ids sort unpredictably); satellite period → UTC uses TimeZone.current instead of the region tz.

B. Blocked on Mapbox v11 — the composite layers: t2c, sfcdewpt, bltopwind, thermalwave, mslpress, freezing, cceil, ctops, densityaltitude, ridge, the plane-only set, and most layers on the DWD regions. The tiles are value-encoded (§5), so they need a raster colour ramp, which arrives with v11: raster-color over the layer’s range, raster-color-mix [1, 0, 0, 0] to read the red channel, and raster-color-range from params’s min/max. Carry the ranges from §4 into a table at that point. Nothing to do before the migration.

C. Wind overlay: keep the cloudfunctions barbs (still served) or move to uv_{alt} composites — the latter needs value decoding, so it is a v11-era item, not a picker change.

D. Region handling: ICOND2 is now excluded alongside ICON/ICONEU (done 2026-08-26) — most of the family’s layers are composite-only, so the region came up half blank. Lift the exclusion when B lands. Read per-region availability from the /api/layers response rather than assuming EUROPE’s list.

Everything added must also extend the Docs/mapbox-v11-feature-checklist.md row “SkySight overlay + rain/wind/spread/conditions layers, legend”.

9. Known issues found while reading (independent of the live check)

Section titled “9. Known issues found while reading (independent of the live check)”
  • CFRACL missing from sectionKey (§2).
  • reorder() comparator is not a strict weak ordering (§8.A.8).
  • Satellite period → UTC uses TimeZone.current instead of the region’s zone.
  • Spread markers always query hour 1500 (§8.A.6).
  • Layer/region caches live 7 days with no manual refresh.
  • allowedRegions(for:) excludes ICON/ICONEU but not ICOND2.

10. How this was verified / how to re-verify

Section titled “10. How this was verified / how to re-verify”
  • Website: https://skysight.io/secure/ (Vue 3 + Pinia + MapLibre). Useful page globals: skysightMap (MapLibre map — getStyle().sources/layers gives every live tile URL), params (layer catalogue), user (profile; contains the backend JWT — never copy it). Pinia stores via document.querySelector('#app').__vue_app__.config.globalProperties.$pinia (region, clock step/min/max, wind-at-altitude altitudeOptions).
  • Bundle: https://skysight.io/secure/assets/main-D0tYFImc.js (served gzip; curl --compressed). Region catalogue const Mr={…}; layer sets y7/b7/A7/C7; URL builders mn (vector), L1/L7 (composite), O1 (spread), D9 (updated), IR/LR/kR (satellite/radar/lightning).
  • Tiles: curl -H 'Origin: https://skysight.io' <edge url>; composite and updated need no headers. 204 = no data for that id/time/tile.
  • Still unverified: the live /api/regions and /api/layers payloads and this account’s allowed_regions. Capture them from the app (a breakpoint in SkySightManager.fetchRegions/fetchLayers, or Charles/Proxyman on the device) and diff the ids against §4/§5.