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.
0. TL;DR
Section titled “0. TL;DR”- The app’s picker is server-driven: it lists whatever
GET /api/layers?region_id=…returns (+ a syntheticsatellite). Only the grouping/ordering is hard-coded (§2). The website no longer calls/api/regionsor/api/layersat all — its catalogue is compiled into the bundle (regions) and a page globalparams(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, buttccis worth checking in the app: the site renders “Cloud Cover” fromCFRACL. - New on the server, reachable as vector tiles today (drop-in for the
existing fill-layer path):
uv_10(surface wind, replacessfcwind0),zsfclclmask_low,sigwx(significant weather),potfd_as(airspace-aware XC speed),oktas_cu,CFRACL(also carriesCFRACM/CFRACHas 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-altitudeuv_{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.iovector tiles requireOrigin: https://skysight.io(400 without). TheURLSessionswizzle 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 excludesICON/ICONEUby id, soICOND2would 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)”| Step | Code | Notes |
|---|---|---|
| Sign in | POST https://skysight.io/api/auth, header X-API-KEY: Vulture, body {username, password} | SkySightAuthenticationRequest; response SkySightUserAuthInfo { key, allowed_regions } |
| Regions | GET /api/regions with X-API-KEY: <key> | [SkySightRegion { id, name, tz }] |
| Layers | GET /api/layers?region_id=<id> | [SkySightLayer { id, name, description?, legend? }], legend = { unit_type, colors: [{ name, value, color:[r,g,b] }] } |
| Synthetic | SkySightLayer.satellite | appended 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.swiftinjects theOriginheader.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()):
| Section | Layer ids, in display order | Server status 2026-08-26 |
|---|---|---|
| Thermals | pfdtot, potfd, wstar_bsratio, hwcrit, dwcrit, stars | all live |
| Clouds | satellite, tcc, zsfclcldif, zsfclclmask, zblclmask, cape, accrain, CFRACM, CFRACH, sunlight, sfctemp, sfcdewpt | tcc, sunlight, sfctemp dead; sfcdewpt composite-only |
| Wind | ridge, sfcwind0, blwind, wblmaxmin | ridge composite-only; sfcwind0 still served but superseded by uv_10 |
| Wave | w_1000 … w_9000 | w_1000–w_7000 live; w_8000, w_9000 dead |
| Experimental | freezing, cceil | composite-only |
| Others | anything 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 behaviour | Live finding |
|---|---|---|
satellite | Raster 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.current | Site uses the identical tiles and radar builders, plus a new /lightning/{z}/{x}/{y}@2x?date=… vector source (source-layer lightning, z0–4). |
pfdtot | Period 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_rain | Spread markers from cloudfunctions.net/tile?query=spread®ion=GFS&hour=1500 | Site 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_bsratio | Companion 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 rescaled | Site: 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 layers | Vector edge.skysight.io/{region}/{YYYYMMDD}/{HHmm}/{layer}/{z}/{x}/{y}, source-layer = period, colour = legend.colors[idx], zoom 0–5 | Same 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 arrows | cloudfunctions.net/tile?query=wind®ion&date&hour&alt=0…9, label alt × 600 m | Still 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.
3. Regions — verified
Section titled “3. Regions — verified”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.
| id | name | model_family | tz (tile hour base) | category | notes |
|---|---|---|---|---|---|
GFS | Global - NOAA | noaa | UTC | Global | no polygon, global bbox |
ICON | DWD-ICON Global | dwd | UTC | Global | composite selector ICONCH1; vector maxzoom 4 |
ICONEU | Europe-ICONEU | dwd | UTC | Europe | |
ICOND2 | ICOND2 | dwd | UTC | Europe | new; polygon ≈ D-A-CH-NL-BE-DK |
EUROPE | Central Europe | ss | Europe/Warsaw | Europe | |
ALPS | Alps | ss | Europe/Berlin | Europe | vector maxzoom 6 |
MIDDLE_EAST | Middle East | ss | Asia/Jerusalem | Asia | |
INDOCHINA | Indochina | ss | Asia/Bangkok | Asia | |
JAPAN | Japan | ss | Asia/Tokyo | Asia | |
SANEW | South Africa | ss | Africa/Johannesburg | Africa | |
EAST_AUS | East Australia | ss | Australia/NSW | Oceania | obs stations |
WA | West Australia | ss | Australia/Perth | Oceania | obs stations |
NZ | New Zealand | ss | Pacific/Auckland | Oceania | |
BRAZIL | Brazil | ss | America/Sao_Paulo | South America | |
ARGENTINA_CHILE | Argentina/Chile | ss | America/Argentina/Buenos_Aires | South America | wave range −3.25…5.75 |
EAST_US | US/Canada East | ss | America/New_York | North America | |
WEST_US | US/Canada West | ss | America/Los_Angeles | North America | |
HRRR | HRRR | hrrr | America/Los_Angeles | North America | composite for b7 layers |
HRRRAK | Alaska | hrrr | Pacific/Samoa | North America |
Region facts that affect the app:
- Hourly regions:
ICON,ICONEU,ICOND2,HRRR,HRRRAKstep 3600 s; everyss/noaaregion 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
HHmmin the region’stz(soICOND2paths are in UTC,EUROPEin Europe/Warsaw). The app passes the picked period through unchanged and labels it with the region tz — consistent, as long as the API’stzmatches 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/ICONEUdoes not coverICOND2.
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 group | Item → id | type / unit / range | models |
|---|---|---|---|
| Thermals | Potential Flight Distance → pfdtot (+ spread uncertainty) | dist km 50–950/100 | ss, hrrr, dwd |
XC Speed → potfd (potfd_as when airspace on) | xcspd kph 40–160/10 | ss, hrrr, dwd, noaa | |
Thermal Strength & B/S → wstar_bsratio (+ bsratio source-layer) | vert m/s 0.5–4/0.5 | ss, noaa, hrrr, dwd | |
Height of Thermals → hwcrit | hgt m 300–4800/300 | ss, noaa, hrrr, dwd | |
Depth of Thermals (AGL) → dwcrit | hgt m 300–3600/300 | ss, noaa, hrrr, dwd | |
Significant Weather → dwcrit + sigwx | — | — | |
| Clouds | Cu Overview ✱ → cucloudfrac (rendered from the hwcrit tile) | hgt m 300–4800/300 | ss, noaa, hrrr, dwd |
Cu Depth → zsfclcldif | hgt m −1000–700/100 | ss, noaa, hrrr, dwd | |
Cu Cloudbase → zsfclclmask + zsfclclmask_low | hgt m 0–4800/300 | ss, noaa, hrrr, dwd | |
Cu Coverage ✱ → oktas_cu | % 0–100/12.5 | ss, noaa, hrrr, dwd | |
Cloud Cover → CFRACL tile, source-layers {HHmm}, CFRACM, CFRACH | % 5–95/15 | ss, noaa, hrrr, dwd | |
CAPE / Storms → cape | J 100–3100/300 | ss, noaa, hrrr, dwd | |
Rain → accrain | mm 0–5/0.5 (last half hour) | ss, noaa, hrrr, dwd | |
| Wind | Off / Barbs / Arrows / Particles (overlay on any layer) | — | — |
Wind at {alt} → uv_{alt} composite, alt ∈ §6 list | spd | — | |
Surface Wind → uv_10 | spd m/s 0–12/1 | ss, noaa, hrrr, dwd | |
Thermal Layer Wind (Avg) → blwind | spd m/s 1–16/1 | ss, noaa, hrrr, dwd | |
Thermal Top Wind → bltopwind | spd m/s 1–19/1 | ss, noaa, hrrr, dwd | |
Convergence → wblmaxmin | vert m/s −0.25–0.75/0.1 | ss, hrrr, cosmo | |
Ridge Lift → ridge | vert m/s 0.1–1.2/0.1 | ss, 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 Wave | vert m/s −2.6–4.6/0.4 | ss, hrrr, cosmo |
| General | Glide Range (tool), Terrain Relief (basemap) | — | — |
Last 72h Rainfall → bom_rain | mm 0–45/3 | (Australia only in practice) | |
Surface Temperature → t2c | temp °C −6–48/2 | ss, noaa, hrrr, dwd | |
Surface Dewpoint → sfcdewpt | temp °C −16–20/1 | ss, noaa, hrrr, dwd | |
MSL Pressure → mslpress | mBar 984–1034/2 | ss, noaa, hrrr, dwd | |
Low/Mid/High level cloud cover → CFRACL/CFRACM/CFRACH | % 5–95/15 | ss, noaa, hrrr, dwd | |
Thermal Hotspots → thermals (kk7 data) | — | — | |
Freezing Level ✱ → freezing | hgt m 300–5700/300 | ss, hrrr | |
Density Altitude (Surface) → densityaltitude | hgt m 300–4800/300 | ss, noaa, hrrr, dwd | |
Overdevelopment → zblclmask | % 5–95/10 | ss, hrrr, dwd | |
Smoke → smoke | mg/m² 25–350/25 | hrrr | |
| 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.
5. Tile endpoints — verified
Section titled “5. Tile endpoints — verified”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) otherwisePFD (pfdtot) composite on the ICON/HRRR targets, vector elsewhereURL shapes (all verified 200 with curl):
| Kind | Template | Auth | Zoom |
|---|---|---|---|
| Vector | https://edge.skysight.io/{target}/{YYYYMMDD}/{HHmm region-local}/{id}/{z}/{x}/{y} | needs Origin: https://skysight.io (400 otherwise); gzip MVT | 3–5 (ALPS 6; PFD on ICON/GFS 4; wave on ICONEU/ICOND2 6) |
| Composite | https://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) | none | 3–7 (ICON 4; ridge/uv_10 on ss: 9) |
| Composite, legacy form | https://cf.skysight.io/tile/{target}/{YYYYMMDD}/{HHmm}/{id}/{z}/{x}/{y}.webp | none | |
| Spread | https://edge.skysight.io/GFS/{YYYYMMDD}/{HHmm UTC}/spread/{z}/{x}/{y} | Origin | 1–6 |
| Freshness | https://cf.skysight.io/tile/updated/{target}/{YYYYMMDD}/{HHmm} → {"unix":"1787744469","init":""} | none | |
| Satellite | https://satellite.skysight.io/tiles/{z}/{x}/{y}@2x?date=YYYY/MM/DD/HHmm&mtg=true (raster, max 8) | none | |
| Radar | https://satellite.skysight.io/radar/{z}/{x}/{y}@2x?date=… (raster 512) | none | |
| Lightning | https://satellite.skysight.io/lightning/{z}/{x}/{y}@2x?date=… (vector, source-layer lightning) | none | 0–4 |
| Observations | https://edge.skysight.io/stations/{YYYYMMDD/HHmm}/{z}/{x}/{y} (vector, source-layer stations) | Origin | 4–12 |
| Legacy wind | https://us-central1-skysight-1260.cloudfunctions.net/tile?query=wind®ion&date&hour&alt | none | still 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 D2pfdtot(1800) 200 404 200 | CFRACL 200 200 200 | w_1000..w_7000 200 200 200potfd 200 200 200 | CFRACM/H 200 200 200 | w_8000, w_9000 204 404 404potfd_as 200 200 200 | sunlight 204 404 404 | freezing 204 200 404wstar_bsratio 200 200 200 | sfctemp 204 404 404 | cceil, ctops 204 200 404hwcrit 200 200 200 | t2c 204 200 200 | vfr, visibility 204 200 404dwcrit 200 200 200 | sfcdewpt 204 200 200 | turbulence_* 204 200 404stars 200 200 200 | ridge 204 404 200 | densityaltitude 204 200 404tcc 204 404 404 | sfcwind0 200 200 404 | ice_base/top 204 200 404zsfclcldif 200 200 200 | uv_10 200 200 200 | carb_icing 204 200 404zsfclclmask 200 200 200 | blwind 200 200 200 | oktas_cu 200 200 200zsfclclmask_low 200 404 404 | bltopwind 204 200 200 | cucloudfrac 204 404 404zblclmask 200 200 200 | wblmaxmin 200 200 200 | smoke 204 404 404cape, accrain 200 200 200 | thermalwave 204 200 200 | bsratio (URL) 204 404 404mslpress 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/noaaregions, 3600 s onICON*/HRRR*. Tiles are actually served from 06:00 to 20:00 (EUROPEhwcrit: 0600–2000 → 200; 2030+ → 204). - Horizon: EUROPE
pfdtotserved 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. updatedendpoint 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
ssregions: 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: compositeuv_{alt}webp (uv_600observed) — not the cloudfunctionsalt=0…9index the app uses, although that endpoint still answers. - Wave:
w_1000…w_7000for 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.
7. Legends — verified
Section titled “7. Legends — verified”pfdtoton the site: labels50, 150 … 850, >950, coloursrgb(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 (commit8be1e4a13) uses a different, muted palette ([110,100,190] … [155,55,140]). Decide which is wanted; the ten-stop structure matches.hwcriton EUROPE: labels200, 400 … 2400, >2600, coloursrgb(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:
typefromparams(hgt/spd/vert/temp/dist/xcspd) with the user’s units; matches the app’sunit_typehandling for the three it knows, but the app has notemp,dist,xcspdcases (falls back to the stop name). - Layers with no numeric legend on the site (
ACset):carb_icing,turbulence_*,vfr(categorical),thermalwave(Likely…Nil).
8. Gap analysis for the app
Section titled “8. Gap analysis for the app”A. Fix in the current vector path (small, no new rendering):
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),starsalready there.- B/S ratio stipple: read source-layer
bsratiofrom the existingwstar_bsratiosource instead of the dead/bsratio/request. _extrasymbol layer (condition markers, iconcc7wherez ≤ zoom) — optional parity item; needs thecc7sprite.- Cloud Cover: if the API still returns
tcc, its tiles are empty; render “Cloud Cover” fromCFRACLwith the three source-layers, or droptccfrom the sections (it will still be listed if the API sends it). - 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. - Spread markers: switch to
edge.skysight.io/GFS/…/spreadat the selected UTC hour (the app hard-codes 1500 and the cloudfunctions host) and tie them to an “Uncertainty” toggle as the site does. - Drop
w_8000/w_9000,sunlight,sfctemp,pfdtotclub,bom_rainfrom the tables (or leave — harmless if the API stops sending them). reorder()comparator is not a strict weak ordering (unknown ids sort unpredictably); satellite period → UTC usesTimeZone.currentinstead 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)”CFRACLmissing fromsectionKey(§2).reorder()comparator is not a strict weak ordering (§8.A.8).- Satellite period → UTC uses
TimeZone.currentinstead 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:)excludesICON/ICONEUbut notICOND2.
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/layersgives every live tile URL),params(layer catalogue),user(profile; contains the backend JWT — never copy it). Pinia stores viadocument.querySelector('#app').__vue_app__.config.globalProperties.$pinia(region,clockstep/min/max,wind-at-altitudealtitudeOptions). - Bundle:
https://skysight.io/secure/assets/main-D0tYFImc.js(served gzip;curl --compressed). Region catalogueconst Mr={…}; layer setsy7/b7/A7/C7; URL buildersmn(vector),L1/L7(composite),O1(spread),D9(updated),IR/LR/kR(satellite/radar/lightning). - Tiles:
curl -H 'Origin: https://skysight.io' <edge url>; composite andupdatedneed no headers.204= no data for that id/time/tile. - Still unverified: the live
/api/regionsand/api/layerspayloads and this account’sallowed_regions. Capture them from the app (a breakpoint inSkySightManager.fetchRegions/fetchLayers, or Charles/Proxyman on the device) and diff the ids against §4/§5.
