{"openapi":"3.1.0","info":{"title":"shade-engine API","description":"Public API of shade-engine: urban shade queries answered from precomputed per-city horizon artifacts. Times without a UTC offset are interpreted in the city's timezone.","version":"0.1.0"},"paths":{"/v1/cities":{"get":{"summary":"Cities with built artifacts","description":"Cities this deployment can answer for, with WGS84 bounds and attribution.","operationId":"list_cities_v1_cities_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CityOut"},"type":"array","title":"Response List Cities V1 Cities Get"}}}}}}},"/v1/cities/{city_id}":{"get":{"summary":"One city plus its artifact build metadata","description":"The city entry plus the full metadata of the loaded artifacts.","operationId":"city_detail_v1_cities__city_id__get","parameters":[{"name":"city_id","in":"path","required":true,"schema":{"type":"string","title":"City Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CityDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/shade":{"get":{"summary":"Shade verdict for a point at an instant","description":"Is this point in the sun, in shade (and cast by what), or in the night?","operationId":"shade_v1_shade_get","parameters":[{"name":"city","in":"query","required":true,"schema":{"type":"string","title":"City"}},{"name":"lat","in":"query","required":true,"schema":{"type":"number","maximum":90,"minimum":-90,"description":"Latitude, WGS84 degrees","title":"Lat"},"description":"Latitude, WGS84 degrees"},{"name":"lon","in":"query","required":true,"schema":{"type":"number","maximum":180,"minimum":-180,"description":"Longitude, WGS84 degrees","title":"Lon"},"description":"Longitude, WGS84 degrees"},{"name":"at","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space).","title":"At"},"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShadeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/shade/timeline":{"get":{"summary":"Sun/shade intervals across one local day","description":"Daylight intervals of constant state, plus shaded_until when date is today.","operationId":"timeline_v1_shade_timeline_get","parameters":[{"name":"city","in":"query","required":true,"schema":{"type":"string","title":"City"}},{"name":"lat","in":"query","required":true,"schema":{"type":"number","maximum":90,"minimum":-90,"description":"Latitude, WGS84 degrees","title":"Lat"},"description":"Latitude, WGS84 degrees"},{"name":"lon","in":"query","required":true,"schema":{"type":"number","maximum":180,"minimum":-180,"description":"Longitude, WGS84 degrees","title":"Lon"},"description":"Longitude, WGS84 degrees"},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Local calendar day; omitted means today","title":"Date"},"description":"Local calendar day; omitted means today"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimelineOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/parking/nearby":{"get":{"summary":"Parking zones near a point, with shade state","description":"Zones within ``radius`` meters, nearest first, with shade at ``at``.","operationId":"parking_nearby_v1_parking_nearby_get","parameters":[{"name":"city","in":"query","required":true,"schema":{"type":"string","title":"City"}},{"name":"lat","in":"query","required":true,"schema":{"type":"number","maximum":90,"minimum":-90,"description":"Latitude, WGS84 degrees","title":"Lat"},"description":"Latitude, WGS84 degrees"},{"name":"lon","in":"query","required":true,"schema":{"type":"number","maximum":180,"minimum":-180,"description":"Longitude, WGS84 degrees","title":"Lon"},"description":"Longitude, WGS84 degrees"},{"name":"radius","in":"query","required":false,"schema":{"type":"number","maximum":1000,"exclusiveMinimum":0,"description":"Search radius, meters","default":300.0,"title":"Radius"},"description":"Search radius, meters"},{"name":"at","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space).","title":"At"},"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParkingNearbyOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/routes/shaded":{"get":{"summary":"Shaded vs shortest walking route","operationId":"shaded_route_v1_routes_shaded_get","parameters":[{"name":"city","in":"query","required":true,"schema":{"type":"string","title":"City"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","description":"Origin as 'lat,lon', WGS84","title":"From"},"description":"Origin as 'lat,lon', WGS84"},{"name":"to","in":"query","required":true,"schema":{"type":"string","description":"Destination as 'lat,lon', WGS84","title":"To"},"description":"Destination as 'lat,lon', WGS84"},{"name":"at","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space).","title":"At"},"description":"ISO 8601 instant. Without a UTC offset it is interpreted in the city's timezone; omitted means now. URL-encode a '+' offset as %2B (a literal '+' in a query string is a space)."},{"name":"alpha","in":"query","required":false,"schema":{"type":"number","maximum":10.0,"minimum":0.0,"description":"Detour appetite: an edge fully in the sun costs (1 + alpha) times its length, so alpha = 1 accepts up to a 2x detour for full shade; 0 returns the shortest route","default":1.0,"title":"Alpha"},"description":"Detour appetite: an edge fully in the sun costs (1 + alpha) times its length, so alpha = 1 accepts up to a 2x detour for full shade; 0 returns the shortest route"},{"name":"beta","in":"query","required":false,"schema":{"type":"number","maximum":10.0,"minimum":0.0,"description":"Tree preference: shade cast by buildings or terrain costs (1 + beta) times its length while tree shade costs its bare length, so beta > 0 routes under canopy; must not exceed alpha","default":0.0,"title":"Beta"},"description":"Tree preference: shade cast by buildings or terrain costs (1 + beta) times its length while tree shade costs its bare length, so beta > 0 routes under canopy; must not exceed alpha"},{"name":"alternatives","in":"query","required":false,"schema":{"type":"boolean","description":"Also return the distinct routes an alpha sweep produces, each with its own length and sun accounting","default":false,"title":"Alternatives"},"description":"Also return the distinct routes an alpha sweep produces, each with its own length and sun accounting"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShadedRouteOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/healthz":{"get":{"summary":"Liveness probe","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthOut"}}}}}}}},"components":{"schemas":{"ArtifactInput":{"properties":{"name":{"type":"string","title":"Name"},"points":{"type":"integer","title":"Points"},"created":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Created"}},"type":"object","required":["name","points"],"title":"ArtifactInput","description":"One source file that fed the build."},"BuildMetadata":{"properties":{"schema_version":{"type":"integer","title":"Schema Version"},"city_id":{"type":"string","title":"City Id"},"artifact_version":{"type":"string","title":"Artifact Version"},"engine_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Engine Version"},"built_at":{"type":"string","format":"date-time","title":"Built At"},"crs":{"type":"string","title":"Crs"},"bbox":{"prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}],"type":"array","maxItems":4,"minItems":4,"title":"Bbox"},"resolution_m":{"type":"number","title":"Resolution M"},"horizon":{"$ref":"#/components/schemas/HorizonBuildParams"},"landcover_classes":{"additionalProperties":{"type":"integer"},"type":"object","title":"Landcover Classes"},"landcover":{"anyOf":[{"$ref":"#/components/schemas/LandcoverBuildParams"},{"type":"null"}]},"declutter":{"anyOf":[{"$ref":"#/components/schemas/DeclutterBuildParams"},{"type":"null"}]},"coverage":{"anyOf":[{"$ref":"#/components/schemas/CoverageBuildParams"},{"type":"null"}]},"tree_inventory":{"anyOf":[{"$ref":"#/components/schemas/TreeInventoryBuildParams"},{"type":"null"}]},"no_blocker_value":{"type":"integer","title":"No Blocker Value"},"software":{"additionalProperties":{"type":"string"},"type":"object","title":"Software"},"inputs":{"items":{"$ref":"#/components/schemas/ArtifactInput"},"type":"array","title":"Inputs"},"source_dates":{"anyOf":[{"$ref":"#/components/schemas/SourceDates"},{"type":"null"}]},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["schema_version","city_id","artifact_version","built_at","crs","bbox","resolution_m","horizon","landcover_classes","no_blocker_value","software","inputs","attribution"],"title":"BuildMetadata","description":"Contents of ``metadata.json``: everything needed to trust an artifact.\n\nThree of these fields are versions and they answer different questions, so\nthey move independently:\n\n- ``artifact_version`` (\"v1\") is the *layout*, and it is also a path\n  segment: ``data/cities/<id>/v1/``.\n- ``schema_version`` is *this file*: which fields it carries. Additive\n  fields do not move it -- a reader that does not know one simply drops it.\n- ``engine_version`` is *the numbers inside the rasters*. Same layout, same\n  fields, different values.\n\nOnly the last one tells you whether a city has to be rebuilt, and it is the\none that used to be missing entirely."},"CityDetail":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"country":{"type":"string","title":"Country"},"timezone":{"type":"string","title":"Timezone"},"bbox_wgs84":{"prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}],"type":"array","maxItems":4,"minItems":4,"title":"Bbox Wgs84"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"},"artifacts":{"$ref":"#/components/schemas/BuildMetadata"},"caveats":{"items":{"type":"string"},"type":"array","title":"Caveats","description":"Modelling assumptions that limit what this city's answers mean. Always present here, because they describe the engine and not any one verdict; the shade endpoints return them only when they bear on the answer given"}},"type":"object","required":["id","name","country","timezone","bbox_wgs84","attribution","artifacts"],"title":"CityDetail","description":"A city plus the build metadata of its loaded artifacts."},"CityOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"country":{"type":"string","title":"Country"},"timezone":{"type":"string","title":"Timezone"},"bbox_wgs84":{"prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"},{"type":"number"}],"type":"array","maxItems":4,"minItems":4,"title":"Bbox Wgs84"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["id","name","country","timezone","bbox_wgs84","attribution"],"title":"CityOut","description":"One city with built artifacts, ready to answer shade queries."},"CoverageBuildParams":{"properties":{"source":{"type":"string","title":"Source"},"covered_px":{"type":"integer","title":"Covered Px"},"covered_fraction":{"type":"number","title":"Covered Fraction"}},"type":"object","required":["source","covered_px","covered_fraction"],"title":"CoverageBuildParams","description":"Which pixels of the bbox the build actually computed.\n\nOptional in :class:`BuildMetadata`: a city with no ``area`` in its config\ncomputed all of them, which is what its absence means."},"DeclutterBuildParams":{"properties":{"linear_px":{"type":"integer","title":"Linear Px"},"slab_px":{"type":"integer","title":"Slab Px"},"protrusion_m":{"type":"number","title":"Protrusion M"},"opening_px":{"type":"integer","title":"Opening Px"},"max_base_m":{"type":"number","title":"Max Base M"},"slab_roughness_max_m":{"type":"number","title":"Slab Roughness Max M"}},"type":"object","required":["linear_px","slab_px","protrusion_m","opening_px","max_base_m","slab_roughness_max_m"],"title":"DeclutterBuildParams","description":"How much of the DSM the build rewrote before sweeping it.\n\nOptional in :class:`BuildMetadata`, and its absence is meaningful: an\nartifact without it holds the raw surface the LiDAR recorded, which is\nwhat every build before ADR-022 shipped."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthOut":{"properties":{"status":{"type":"string","title":"Status"},"cities":{"type":"integer","title":"Cities"}},"type":"object","required":["status","cities"],"title":"HealthOut"},"HorizonBuildParams":{"properties":{"sectors":{"type":"integer","title":"Sectors"},"max_distance_m":{"type":"number","title":"Max Distance M"},"observer_height_m":{"type":"number","title":"Observer Height M"},"angle_max_deg":{"type":"number","title":"Angle Max Deg"},"step_mode":{"type":"string","title":"Step Mode"},"tile_size":{"type":"integer","title":"Tile Size"},"height_datum_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height Datum M"}},"type":"object","required":["sectors","max_distance_m","observer_height_m","angle_max_deg","step_mode","tile_size"],"title":"HorizonBuildParams","description":"Sweep parameters recorded at build time.\n\n``height_datum_m`` is the elevation the sweep subtracted before computing\nin float32. Nothing reads it at runtime -- the cubes are angles -- but\nwithout it a cube cannot be reproduced, so it travels with the artifact.\nOptional because artifacts built before the datum existed do not carry it.\n\n``step_mode`` now only ever says ``\"exact\"``: [[ADR-028]] withdrew the\n``geometric`` schedule from the engine. The field stays because metadata is\nthe record of what was done, not a mirror of what the current code can do --\nthe cities already on disk were built by a version where the mode was a\nchoice, and dropping the field would leave them unable to say how they were\nmade."},"LandcoverBuildParams":{"properties":{"building_margin_m":{"type":"number","title":"Building Margin M"},"footprints":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Footprints"},"footprint_count":{"type":"integer","title":"Footprint Count","default":0},"footprint_relabelled":{"type":"integer","title":"Footprint Relabelled","default":0},"roof_tolerance_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Roof Tolerance M"}},"type":"object","required":["building_margin_m"],"title":"LandcoverBuildParams","description":"How the build decided which cell is a building and which a crown.\n\nOptional in :class:`BuildMetadata`: artifacts written before the\nclassification had knobs simply do not carry it."},"ParkingNearbyOut":{"properties":{"city":{"type":"string","title":"City"},"at":{"type":"string","format":"date-time","title":"At","description":"The instant answered for, in the city's timezone"},"radius_m":{"type":"number","title":"Radius M"},"zones":{"items":{"$ref":"#/components/schemas/ParkingZoneOut"},"type":"array","title":"Zones"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["city","at","radius_m","zones","attribution"],"title":"ParkingNearbyOut","description":"Parking zones within a radius, nearest first, with their shade state."},"ParkingShadeOut":{"properties":{"state":{"$ref":"#/components/schemas/ShadeState","description":"Majority verdict: 'shade' when at least half the samples are shaded"},"in_shade":{"type":"boolean","title":"In Shade","description":"True exactly when state is 'shade'"},"shade_fraction":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Shade Fraction","description":"Fraction of sampled points in shade; null at night"},"shaded_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shaded Until","description":"Only while in_shade: when the zone drops below majority shade (or daylight ends, whichever comes first)"}},"type":"object","required":["state","in_shade","shade_fraction","shaded_until"],"title":"ParkingShadeOut","description":"Aggregate shade verdict of a zone, sampled along its geometry."},"ParkingZoneOut":{"properties":{"name":{"type":"string","title":"Name"},"zone_type":{"type":"string","title":"Zone Type"},"orientation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Orientation"},"capacity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Capacity"},"schedule":{"items":{"$ref":"#/components/schemas/ScheduleEntryOut"},"type":"array","title":"Schedule"},"max_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Minutes"},"tariff_eur_hour":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tariff Eur Hour"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"last_verified":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Verified"},"distance_m":{"type":"number","title":"Distance M","description":"Distance from the query point, meters"},"geometry":{"additionalProperties":true,"type":"object","title":"Geometry","description":"GeoJSON MultiLineString, WGS84 lon-lat"},"shade":{"anyOf":[{"$ref":"#/components/schemas/ParkingShadeOut"},{"type":"null"}],"description":"Null when the zone lies outside the city's raster coverage"}},"type":"object","required":["name","zone_type","orientation","capacity","schedule","max_minutes","tariff_eur_hour","notes","source","last_verified","distance_m","geometry","shade"],"title":"ParkingZoneOut","description":"One parking zone near the query point."},"RouteAlternativeOut":{"properties":{"geometry":{"additionalProperties":true,"type":"object","title":"Geometry","description":"GeoJSON LineString, WGS84 lon-lat"},"length_m":{"type":"number","title":"Length M"},"sun_length_m":{"type":"number","title":"Sun Length M","description":"Meters of the route walked in the sun"},"sun_fraction":{"type":"number","title":"Sun Fraction","description":"Length-weighted fraction of the route in the sun; 0 at night"},"veg_shade_length_m":{"type":"number","title":"Veg Shade Length M","description":"Meters walked under tree canopy; the rest of the shaded meters (length - sun_length_m - veg_shade_length_m) is cast by buildings or terrain"},"segments":{"anyOf":[{"items":{"$ref":"#/components/schemas/RouteSegmentOut"},"type":"array"},{"type":"null"}],"title":"Segments","description":"Per-edge decomposition in walked order; only on the shaded leg. Consecutive segments share their joint vertex and their lengths sum to length_m. Fractions are the edge's, constant along the segment: this is the finest resolution the router itself used"},"alpha":{"type":"number","title":"Alpha","description":"The smallest alpha of the sweep that produced this route"}},"type":"object","required":["geometry","length_m","sun_length_m","sun_fraction","veg_shade_length_m","alpha"],"title":"RouteAlternativeOut","description":"One distinct route from the alpha sweep, with the taste that found it."},"RouteLegOut":{"properties":{"geometry":{"additionalProperties":true,"type":"object","title":"Geometry","description":"GeoJSON LineString, WGS84 lon-lat"},"length_m":{"type":"number","title":"Length M"},"sun_length_m":{"type":"number","title":"Sun Length M","description":"Meters of the route walked in the sun"},"sun_fraction":{"type":"number","title":"Sun Fraction","description":"Length-weighted fraction of the route in the sun; 0 at night"},"veg_shade_length_m":{"type":"number","title":"Veg Shade Length M","description":"Meters walked under tree canopy; the rest of the shaded meters (length - sun_length_m - veg_shade_length_m) is cast by buildings or terrain"},"segments":{"anyOf":[{"items":{"$ref":"#/components/schemas/RouteSegmentOut"},"type":"array"},{"type":"null"}],"title":"Segments","description":"Per-edge decomposition in walked order; only on the shaded leg. Consecutive segments share their joint vertex and their lengths sum to length_m. Fractions are the edge's, constant along the segment: this is the finest resolution the router itself used"}},"type":"object","required":["geometry","length_m","sun_length_m","sun_fraction","veg_shade_length_m"],"title":"RouteLegOut","description":"One walking route with its sun exposure accounting."},"RoutePointOut":{"properties":{"lat":{"type":"number","title":"Lat"},"lon":{"type":"number","title":"Lon"},"snapped_lat":{"type":"number","title":"Snapped Lat","description":"Where the point met the network, WGS84"},"snapped_lon":{"type":"number","title":"Snapped Lon","description":"Where the point met the network, WGS84"},"snap_distance_m":{"type":"number","title":"Snap Distance M","description":"Distance from the requested point to the snapped point on the network, meters"}},"type":"object","required":["lat","lon","snapped_lat","snapped_lon","snap_distance_m"],"title":"RoutePointOut","description":"A route endpoint: as requested, and where it landed on the graph."},"RouteSegmentOut":{"properties":{"geometry":{"additionalProperties":true,"type":"object","title":"Geometry","description":"GeoJSON LineString, WGS84 lon-lat"},"length_m":{"type":"number","title":"Length M","description":"Meters walked on this edge"},"sun_fraction":{"type":"number","title":"Sun Fraction","description":"This edge's share in the sun at the queried instant, 0..1"},"veg_shade_fraction":{"type":"number","title":"Veg Shade Fraction","description":"This edge's share under tree canopy; the remainder (1 - sun_fraction - veg_shade_fraction) is shade cast by buildings or terrain"}},"type":"object","required":["geometry","length_m","sun_fraction","veg_shade_fraction"],"title":"RouteSegmentOut","description":"One graph edge's stretch of a route, with that edge's shade mix."},"ScheduleEntryOut":{"properties":{"days":{"type":"string","title":"Days"},"from":{"type":"string","title":"From","description":"Local start time, HH:MM"},"to":{"type":"string","title":"To","description":"Local end time, HH:MM"}},"type":"object","required":["days","from","to"],"title":"ScheduleEntryOut","description":"One regulated stretch; days is a compact range like 'mo-fr' or 'sa'."},"ShadeOut":{"properties":{"city":{"type":"string","title":"City"},"at":{"type":"string","format":"date-time","title":"At","description":"The instant answered for, in the city's timezone"},"state":{"$ref":"#/components/schemas/ShadeState","description":"'night' when the sun is below the astronomical horizon -- a state the in_shade flag alone cannot express"},"in_shade":{"type":"boolean","title":"In Shade","description":"True exactly when state is 'shade'"},"shade_type":{"anyOf":[{"$ref":"#/components/schemas/ShadeType"},{"type":"null"}],"description":"What casts the shade: 'building', 'vegetation' when only the trees hold it, or 'both' when a crown covers the point and the skyline would shade it anyway. Null in sun, at night or when unknown"},"sun":{"$ref":"#/components/schemas/SunOut"},"caveats":{"items":{"type":"string"},"type":"array","title":"Caveats","description":"Modelling assumptions that bear on THIS verdict. Present when shade_type is 'vegetation', because crowns are modelled as opaque all year and a leafless winter tree would not shade this point. Empty under 'both', where the skyline shades it anyway"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["city","at","state","in_shade","shade_type","sun","attribution"],"title":"ShadeOut","description":"Shade verdict for one point at one instant."},"ShadeState":{"type":"string","enum":["sun","shade","night"],"title":"ShadeState"},"ShadeType":{"type":"string","enum":["building","vegetation","both"],"title":"ShadeType"},"ShadedRouteOut":{"properties":{"city":{"type":"string","title":"City"},"at":{"type":"string","format":"date-time","title":"At","description":"The instant answered for, in the city's timezone"},"status":{"type":"string","title":"Status","description":"'ok', or 'night' (no sun: both routes are the shortest)"},"alpha":{"type":"number","title":"Alpha","description":"Detour appetite used: an edge fully in the sun costs (1 + alpha) times its length"},"beta":{"type":"number","title":"Beta","description":"Tree preference used: shade cast by buildings or terrain costs (1 + beta) times its length, tree shade costs its bare length"},"sun":{"$ref":"#/components/schemas/SunOut"},"origin":{"$ref":"#/components/schemas/RoutePointOut"},"destination":{"$ref":"#/components/schemas/RoutePointOut"},"shaded":{"$ref":"#/components/schemas/RouteLegOut"},"shortest":{"$ref":"#/components/schemas/RouteLegOut","description":"The alpha = 0 route, for comparison"},"alternatives":{"anyOf":[{"items":{"$ref":"#/components/schemas/RouteAlternativeOut"},"type":"array"},{"type":"null"}],"title":"Alternatives","description":"Distinct non-dominated routes across an alpha sweep, shortest first; only present when alternatives=true"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["city","at","status","alpha","beta","sun","origin","destination","shaded","shortest","attribution"],"title":"ShadedRouteOut","description":"Shade-optimized walking route plus the shortest route as reference."},"SourceDates":{"properties":{"earliest":{"type":"string","format":"date","title":"Earliest"},"latest":{"type":"string","format":"date","title":"Latest"}},"type":"object","required":["earliest","latest"],"title":"SourceDates","description":"The span of dates the source files declare for themselves.\n\n**This is not when the LiDAR was flown**, and the difference matters enough\nto be worth a name. It is ``creation_date`` in the LAS header, which the\nprovider writes when it generates the file: PNOA's third coverage stamps\nevery tile of Montilla, Montalban and Cuesta Blanca with the same\n2024-11-23 (40 tiles, three municipalities), so it is a batch mark and not\na survey date. A flight does not cover three towns in one day.\n\nWhat it is good for is the question people actually ask -- \"how old is this\ndata\" -- answered with a lower bound the artifact can prove, instead of the\nprose in ``sources`` and ``attribution`` that nobody can query. The flight\nyear, when it matters, is in the CNIG tile names carried by\n:class:`ArtifactInput` (``PNOA-2024-AND-...``)."},"SunOut":{"properties":{"azimuth_deg":{"type":"number","title":"Azimuth Deg"},"elevation_deg":{"type":"number","title":"Elevation Deg"}},"type":"object","required":["azimuth_deg","elevation_deg"],"title":"SunOut","description":"Sun position in the local sky; azimuth 0 = North, clockwise, degrees."},"TimelineIntervalOut":{"properties":{"from":{"type":"string","title":"From","description":"Local start time, HH:MM"},"to":{"type":"string","title":"To","description":"Local end time, HH:MM"},"state":{"$ref":"#/components/schemas/ShadeState"},"in_shade":{"type":"boolean","title":"In Shade"},"shade_type":{"anyOf":[{"$ref":"#/components/schemas/ShadeType"},{"type":"null"}]}},"type":"object","required":["from","to","state","in_shade","shade_type"],"title":"TimelineIntervalOut","description":"A [from, to) stretch of constant state during daylight, local HH:MM."},"TimelineOut":{"properties":{"city":{"type":"string","title":"City"},"date":{"type":"string","format":"date","title":"Date"},"timezone":{"type":"string","title":"Timezone"},"intervals":{"items":{"$ref":"#/components/schemas/TimelineIntervalOut"},"type":"array","title":"Intervals"},"shaded_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shaded Until","description":"Only when the requested date is today and the point is currently shaded: the instant the current shaded run ends"},"caveats":{"items":{"type":"string"},"type":"array","title":"Caveats","description":"Modelling assumptions that bear on this day. Present when any interval is held by vegetation alone: those hours are the ones a leafless winter tree would not deliver"},"attribution":{"items":{"type":"string"},"type":"array","title":"Attribution"}},"type":"object","required":["city","date","timezone","intervals","shaded_until","attribution"],"title":"TimelineOut","description":"Sun/shade intervals across one local calendar day."},"TreeInventoryBuildParams":{"properties":{"source":{"type":"string","title":"Source"},"layers":{"items":{"type":"string"},"type":"array","title":"Layers"},"specimens":{"type":"integer","title":"Specimens"},"dense_radius_m":{"type":"number","title":"Dense Radius M"},"near_radius_m":{"type":"number","title":"Near Radius M"},"corroborated_px":{"type":"integer","title":"Corroborated Px"},"judged_px":{"type":"integer","title":"Judged Px"}},"type":"object","required":["source","layers","specimens","dense_radius_m","near_radius_m","corroborated_px","judged_px"],"title":"TreeInventoryBuildParams","description":"The municipal tree inventory the build audited its canopy against.\n\nOptional in :class:`BuildMetadata`: a city that publishes no inventory, or\nan artifact built before the audit existed, simply does not carry it."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}