{
 "openapi": "3.1.0",
 "info": {
  "title": "Swiss Sun",
  "version": "1.0.0",
  "summary": "Hours of sunshine at any address in Switzerland, mountain shade included.",
  "description": "Sunshine hours by month and year for any address or point in Switzerland and Liechtenstein: clouds and fog from MeteoSwiss climate normals 1991–2020 (1 km grid), and the shade of the mountains around the place (terrain horizon at 200 m). Also the times of direct sun on the 21st of each month, temperature and precipitation. Free, no key. Cite as \"Source: Swiss Sun (swiss-sun.app), with data from MeteoSwiss and swisstopo\" and link links.map or links.page. The same answer as a plain web page: https://swiss-sun.app/sun?q=<address>. How the numbers are made: https://swiss-sun.app/methodology/",
  "contact": { "name": "Swiss Sun", "url": "https://swiss-sun.app/" }
 },
 "servers": [{ "url": "https://swiss-sun.app" }],
 "security": [],
 "paths": {
  "/api/sunshine": {
   "get": {
    "operationId": "getSunshine",
    "summary": "Sunshine hours, direct-sun times and climate at a Swiss place",
    "description": "Give either q (an address, street, town or postcode, searched with swisstopo like the map's search box) or lat and lon. A town name lands on the town centre. If a name exists in several municipalities, the first is answered and the others are listed in place.other_matches; ask again with the canton (\"Wil SG\") or with their coordinates. Answers are cached for a day.",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "description": "Swiss address, street, town or postcode, e.g. \"Bundesplatz 3 Bern\", \"Lugano\", \"Wil SG\". Coordinates as \"lat,lon\" work too. At most 200 characters.",
      "schema": { "type": "string", "maxLength": 200 },
      "examples": { "address": { "value": "Bundesplatz 3 Bern" }, "town": { "value": "Zermatt" } }
     },
     {
      "name": "lat",
      "in": "query",
      "description": "Latitude in degrees (WGS84). Use with lon instead of q.",
      "schema": { "type": "number", "minimum": 45, "maximum": 49 },
      "example": 46.9466
     },
     {
      "name": "lon",
      "in": "query",
      "description": "Longitude in degrees (WGS84). Use with lat instead of q.",
      "schema": { "type": "number", "minimum": 5, "maximum": 11.5 },
      "example": 7.4443
     },
     {
      "name": "at",
      "in": "query",
      "description": "\"lat,lon\" in one parameter, as in the map's links (https://swiss-sun.app/#at=46.9466,7.4443).",
      "schema": { "type": "string" }
     }
    ],
    "responses": {
     "200": {
      "description": "The sunshine at the place.",
      "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Answer" } } }
     },
     "400": { "description": "No place given.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
     "404": { "description": "Nothing found, or the point is outside Switzerland and Liechtenstein.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
     "502": { "description": "The address search (swisstopo) did not answer. Try again, or give coordinates.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Answer": {
    "type": "object",
    "required": ["place", "sunshine", "months", "shade_notes", "not_included", "links", "source", "attribution"],
    "properties": {
     "place": { "$ref": "#/components/schemas/Place" },
     "sunshine": { "$ref": "#/components/schemas/Year" },
     "months": { "type": "array", "description": "January to December.", "minItems": 12, "maxItems": 12, "items": { "$ref": "#/components/schemas/Month" } },
     "shade_notes": { "type": "array", "description": "What the mountains take, in plain English: months without direct sun, and how much earlier or later than over a flat horizon the sun goes or comes (from 30 minutes). Empty for open places.", "items": { "type": "string" } },
     "not_included": { "type": "string", "description": "What the model leaves out (buildings, trees, clouds by time of day)." },
     "links": {
      "type": "object",
      "required": ["map", "page", "json", "methodology"],
      "properties": {
       "map": { "type": "string", "format": "uri", "description": "The place on the interactive sunshine map. Best link for people." },
       "page": { "type": "string", "format": "uri", "description": "This answer as a plain web page." },
       "json": { "type": "string", "format": "uri", "description": "This answer as JSON." },
       "methodology": { "type": "string", "format": "uri", "description": "How the numbers are made." }
      }
     },
     "source": { "type": "string", "description": "The data sources." },
     "attribution": { "type": "string", "description": "Short credit line to show with the numbers." }
    }
   },
   "Place": {
    "type": "object",
    "required": ["name", "lat", "lon", "elevation_m"],
    "properties": {
     "name": { "type": "string", "description": "The address or place found, or the nearest address for coordinates.", "example": "Bundesplatz 3, 3011 Bern" },
     "lat": { "type": "number", "example": 46.94684 },
     "lon": { "type": "number", "example": 7.44425 },
     "elevation_m": { "type": "integer", "description": "Metres above sea level (200 m terrain model)." },
     "other_matches": {
      "type": "array",
      "description": "Other municipalities of the same name, when the name was ambiguous. Missing otherwise.",
      "items": {
       "type": "object",
       "required": ["name", "lat", "lon"],
       "properties": { "name": { "type": "string", "example": "Wil (SG)" }, "lat": { "type": "number" }, "lon": { "type": "number" } }
      }
     }
    }
   },
   "Year": {
    "type": "object",
    "required": ["hours_per_year", "hours_per_day", "percent_of_possible", "possible_hours_per_year", "mountain_shade_hours_per_year", "sunniest_month", "least_sunny_month", "period"],
    "properties": {
     "hours_per_year": { "type": "integer", "description": "Hours of sunshine in an average year, clouds and mountain shade included.", "example": 1690 },
     "hours_per_day": { "type": "number", "description": "Average per day over the year." },
     "percent_of_possible": { "type": "integer", "description": "Share of the most the sun could shine here with the mountains around it." },
     "possible_hours_per_year": { "type": "integer", "description": "Hours the sun could shine here on cloudless days, with the mountains around it." },
     "mountain_shade_hours_per_year": { "type": "integer", "description": "Sunshine hours the surrounding terrain takes away, compared with a flat horizon (already subtracted from hours_per_year)." },
     "sunniest_month": { "type": "string", "description": "Month with the most sunshine per day." },
     "least_sunny_month": { "type": "string", "description": "Month with the least sunshine per day." },
     "period": { "type": "string", "description": "The climate normal period.", "example": "1991–2020 climate normals" }
    }
   },
   "Month": {
    "type": "object",
    "required": ["month", "sunshine_hours", "sunshine_hours_per_day", "percent_of_possible", "possible_hours", "direct_sun_on_21st", "direct_sun_on_21st_without_mountains", "temperature_c", "precipitation_mm"],
    "properties": {
     "month": { "type": "string", "example": "January" },
     "sunshine_hours": { "type": "integer", "description": "Hours of sunshine in the month, on average." },
     "sunshine_hours_per_day": { "type": "number" },
     "percent_of_possible": { "type": "integer", "description": "MeteoSwiss relative sunshine: % of the possible hours (clouds and fog)." },
     "possible_hours": { "type": "integer", "description": "Most the sun could shine in the month, with the mountains around it." },
     "direct_sun_on_21st": { "$ref": "#/components/schemas/DirectSun" },
     "direct_sun_on_21st_without_mountains": { "$ref": "#/components/schemas/DirectSun" },
     "temperature_c": {
      "type": "object",
      "required": ["mean", "daily_high", "nightly_low"],
      "properties": {
       "mean": { "type": ["number", "null"] },
       "daily_high": { "type": ["number", "null"], "description": "Average daily maximum." },
       "nightly_low": { "type": ["number", "null"], "description": "Average daily minimum." }
      }
     },
     "precipitation_mm": { "type": ["number", "null"], "description": "Rain and snow (as water)." }
    }
   },
   "DirectSun": {
    "type": ["object", "null"],
    "description": "On a clear 21st of the month: first and last minute the sun clears the low-sun threshold (and the mountains), local time (Europe/Zurich). null when there is no direct sun that day.",
    "required": ["from", "to", "hours"],
    "properties": {
     "from": { "type": "string", "pattern": "^\\d\\d:\\d\\d$", "example": "09:18" },
     "to": { "type": "string", "pattern": "^\\d\\d:\\d\\d$", "example": "15:23" },
     "hours": { "type": "number", "description": "Hours of direct sun that day (gaps behind single peaks subtracted)." }
    }
   },
   "Error": {
    "type": "object",
    "required": ["error"],
    "properties": { "error": { "type": "string", "description": "What went wrong, and what to try." } }
   }
  }
 }
}
