One location catalog for hourly and daily forecasts
Define a PointLocation with coordinates and elevation, or a RegionBand for a
regional elevation summary. Both hourly and daily calculations use these same
location definitions and surface defaults. Stable id values identify locations;
label supplies display text.
The repository installation includes the example catalogs.
Publish from a saved grid
For daily JSON, the file API selects relevant cells, calculates once, hashes the source file and applies issuance freshness checks:
from icon_uv import PointLocation, RegionBand, load_locations, export_daily_file
locations = load_locations([
PointLocation("zermatt", 46.017536, 7.746568, 1617, label="Zermatt"),
RegionBand("valais-3000", (7.0, 45.9, 8.4, 46.4), 3000),
])
# Or: locations = load_locations("examples/shared_locations.json")
payload = export_daily_file(
"work/uv.nc", locations, issued_at="2026-09-07T06:00:00Z",
dates=["2026-09-07", "2026-09-08"], output="work/daily.json",
)
Choose dates matching your forecast. The returned payload is also written
atomically when output is supplied. days=N or days="all" can replace
dates; omitting both selects two dates from the local issuance date.
days and dates are mutually exclusive in Python and the CLI.
Calculate in memory
Use this workflow when you need calculated values before publication:
import xarray as xr
from icon_uv import compute_points, compute_daily, write_daily_json
from icon_uv.data import file_sha256
with xr.open_dataset("work/uv.nc") as grid:
hourly = compute_points(grid, locations.points)
daily = compute_daily(grid, locations, dates=["2026-09-07", "2026-09-08"])
# Publish the existing result without recalculating it.
payload = write_daily_json(
daily, "work/daily.json", issued_at="2026-09-07T06:00:00Z",
input_sha256=file_sha256("work/uv.nc"),
)
compute_points returns hourly means in an xarray Dataset and retains ensemble
members. Pass locations.points when your catalog also contains regions.
compute_daily returns a DailyResult with entries, dates and provenance. It
reconstructs five-minute samples and finds the maximum 30-minute mean for each
Swiss local date, including regional products. Calculation requires explicit
dates, but no issuance timestamp or file hash. Publication adds those checks;
see daily products.
Location defaults
| Location | Calculation |
|---|---|
Point (default treatment="adjusted") |
Nearest cell within 10 km supplies cloud, ozone, aerosol and surface state; recompute solar geometry and pressure at the requested coordinates/elevation |
| Region band | Native cells inside the box and within ±200 m of 1000, 2000 or 3000 m; daily spatial P90 within each member |
Point with treatment="native" |
Nearest cell within 5 km and 300 m of the target; retain that cell's coordinates, elevation and surface |
Coordinates are WGS84 degrees, elevations are metres above sea level and boxes
use (west, south, east, north). maximum_distance_km overrides a point's distance
limit. Region bands need at least five selected cells and 95% spatial coverage
within each contributing member.
An omitted uv_albedo inherits the selected cell's saved UV albedo, retaining its
time and ensemble variation. Grids made by compute_grid estimate it as
0.05 + 0.75 × ICON snow_fraction, from SNOWC. This is an experimental snow
proxy, not a measured UV albedo. It is distinct from broadband ALB_RAD, used
only for fitting the shortwave cloud response. Missing source values remain
missing. For a specific site, uv_albedo=... overrides the estimate with a finite
constant between 0 and 0.85.
Points and regions default to ambient horizontal UV: no local horizon is applied. A town reference point therefore does not impose valley-floor shade on a surrounding mountain destination. It still has one target elevation and one source cloud/snow column; regional elevation bands supply broader mountain context. Elevation correction does not locate the point above or below a cloud layer.
One example with a supplied horizon
The Davos catalog contains the project's explicit site example. Its 72 horizon angles derive from swisstopo terrain profiles; UV albedo 0.05 is an assumption, not a measurement. See point outputs for provenance and limitations.
site = load_locations("examples/davos.json")
with xr.open_dataset("work/uv.nc") as grid:
hourly = compute_points(grid, site.points)
screened_daily = compute_daily(
grid, site, dates=["2026-09-07"], terrain_screened=True,
)
horizon_degrees contains at least four equally spaced elevation angles from
north clockwise, between 0° and 90°. With a horizon, hourly output includes
terrain_screened_uvi alongside ambient uvi; without one, the screened field
is unavailable. Screened daily output requires a catalog containing only adjusted
points with explicit horizons. uv_geometry identifies the chosen daily geometry.
The package accepts supplied horizon arrays and does not generate them or depend
on HORAYZON.
Catalog and CLI
uv run --no-sync icon-uv points --grid work/uv.nc \
--locations examples/shared_locations.json --output work/points.nc
uv run --no-sync icon-uv daily --grid work/uv.nc \
--locations examples/shared_locations.json --days 3 \
--issued-at "YYYY-MM-DDT06:00:00Z" --output work/daily.json
A JSON catalog is an object with an entries array. Entries use
kind: "point" or kind: "region_altitude" and the same fields as Python:
{"entries": [{"id": "bern", "kind": "point", "latitude": 46.95,
"longitude": 7.44, "altitude_m": 540}]}
There is one authoritative catalog format with no version selector. Point entries
may omit treatment, albedo and horizon. Set treatment: "native" explicitly to
retain native-cell matching. Native points do not accept local surface overrides.
Top-level and entry metadata such as coordinate sources are retained in the
normalized catalog; IDs must be unique. load_locations also accepts a list of
entry objects or Python location definitions.
All catalogs publish the same daily JSON contract, including catalogs containing only regions, CTRL runs and ensembles.
daily accepts positive --days N, --days all, or explicit --dates (mutually
exclusive with --days). --terrain-screened selects screened daily output.
preflight accepts the same grid, locations, issuance and date selection to check
support and coverage before calculation; it prints JSON and exits one when not
ready. Put --debug before a command for a traceback.