diff --git a/.github/workflows/github-action-docs.yaml b/.github/workflows/github-action-docs.yaml index bebaa0cd..e5233960 100644 --- a/.github/workflows/github-action-docs.yaml +++ b/.github/workflows/github-action-docs.yaml @@ -1,8 +1,8 @@ name: Deploy docs to GitHub Pages on: - push: - branches: ["main"] + release: + types: [published] workflow_dispatch: diff --git a/docs/index.md b/docs/index.md index 8f6e2e1c..6431cf8f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,13 +8,19 @@ SMHI stands for [Swedish Meteorological and Hydrological Institute](https://www. which is a Swedish agency under its parent department: Ministry of Climate and Enterprise. -Initially only these four APIs are supported +Currently, only these four APIs are supported - Metobs - Meteorological Observations - Metfcts - Meteorological Forecasts - Mesan - Meteorological Analysis MESAN - Strang - Meteorological Analysis STRĂ…NG (sunshine) +## Examples + +You can reach example usage of the different clients + +- [example of Metobs use](/ifk-smhi/metobs-example/) + ## Install ifk-smhi can be installed as @@ -40,11 +46,15 @@ It will be expanded in future versions. Client to fetch data from meteorological observations. Use this to access data from _observations_, i.e. recorded data from weather stations scattered over north Europe. +See [example of Metobs use](/ifk-smhi/metobs-example/) +for details on how to use the client. ## Metfcts client Client to fetch data from meteorological forecasts. Use this to access data about _forecasts_, i.e. SMHI weather predictions. +See [example of Metfcts use](/ifk-smhi/metfcts-example/) +for details on how to use the client. ## Mesan client @@ -52,9 +62,15 @@ Client to fetch data from meteorological analysis. Use this to access the last 24 hour predictions of weather parameters. This API is a useful complement to Metobs because the number of weather stations are limited. +See [example of Mesan use](/ifk-smhi/mesan-example/) +for details on how to use the client. ## Strang client Client to fetch data from meteorological analysis of sunshine. Use this to access data about _sunshine_, i.e. SMHI prediction of sunshine for a given coordinate. +This API is also a useful complement to Metobs because the number of weather +stations measureing sunshine are very limited. +See [example of Strang use](/ifk-smhi/strang-example/) +for details on how to use the client. diff --git a/docs/mesan-example.md b/docs/mesan-example.md index abb84569..7c54629b 100644 --- a/docs/mesan-example.md +++ b/docs/mesan-example.md @@ -11,7 +11,14 @@ from smhi.mesan import Mesan client = Mesan() client.approved_time +# approved time object + client.valid_time +# valid time object + +vt = client.valid_time +vt.valid_time +# returns a list of valid datetimes ``` Notice that `approved_time` is the time when the MESAN analysis was updated. @@ -26,9 +33,23 @@ To list available parameters, geographic area as polygon and points do from smhi.mesan import Mesan client = Mesan() -client.parameters -client.geo_polygon -client.get_geo_multipoint(2) + +parameters = client.parameters +parameters.parameter +# list of all parameter codes and their meaning + +client.parameter_descriptions +# mapping of parameter codes from above and a more descriptive meaning + +client.parameter_descriptions[parameters.parameter[0].name] +# descriptive meaning of parameter + +geopoly = client.geo_polygon +geopoly.coordinates +# polygon of geographic area as a list + +geomultipoint = client.get_geo_multipoint(2) +# all coordinates of value points as a list ``` where `get_geo_multipoint` accepts a downsample argument. @@ -41,13 +62,25 @@ To get data, two methods are available. `leveltype`, `level`, `geo` and `downsample` arguments. See above to acquire a valid time and parameter. +Note that the date input can be in either `str` or `datetime` formats. +The API expects times in UTC but the client expects the user to input +local datetimes. The local datetimes are converted to UTC before an API request +is performed. + ```python from smhi.mesan import Mesan client = Mesan() -data = client.get_point(58, 16) -data = client.get_multipoint( - "2022-11-12T23:00:00Z", "t", "hl", 2, False, 2 + +point = client.get_point(58, 16) +point.df +# dataframe of actual data + +point.df_info +# dataframe with information about available parameters + +multipoint = client.get_multipoint( + "2024-04-1206T16:00:00Z", "t", "hl", 2, 2 ) ``` diff --git a/docs/metfcts-example.md b/docs/metfcts-example.md index 87127fab..19120ffb 100644 --- a/docs/metfcts-example.md +++ b/docs/metfcts-example.md @@ -11,7 +11,14 @@ from smhi.metfcts import Metfcts client = Metfcts() client.approved_time +# approved time object + client.valid_time +# valid time object + +vt = client.valid_time +vt.valid_time +# returns a list of valid datetimes ``` Notice that `approved_time` is the time when the Metfcts analysis was updated. @@ -26,9 +33,23 @@ To list available parameters, geographic area as polygon and points do from smhi.metfcts import Metfcts client = Metfcts() -client.parameters -client.geo_polygon -client.get_geo_multipoint(2) + +parameters = client.parameters +parameters.parameter +# list of all parameter codes and their meaning + +client.parameter_descriptions +# mapping of parameter codes from above and a more descriptive meaning + +client.parameter_descriptions[parameters.parameter[0].name] +# descriptive meaning of parameter + +geopoly = client.geo_polygon +geopoly.coordinates +# polygon of geographic area as a list + +geomultipoint = client.get_geo_multipoint(2) +# all coordinates of value points as a list ``` where `get_geo_multipoint` accepts a downsample argument. @@ -38,16 +59,28 @@ where `get_geo_multipoint` accepts a downsample argument. To get data, two methods are available. `get_point` accepts latitude and longitude arguments. `get_multipoint` accepts `validtime`, `parameter`, -`leveltype`, `level`, `downsample` arguments. +`leveltype`, `level`, `geo` and `downsample` arguments. See above to acquire a valid time and parameter. +Note that the date input can be in either `str` or `datetime` formats. +The API expects times in UTC but the client expects the user to input +local datetimes. The local datetimes are converted to UTC before an API request +is performed. + ```python from smhi.metfcts import Metfcts client = Metfcts() -data = client.get_point(58, 16) -data = client.get_multipoint( - "2022-11-12T23:00:00Z", "t", "hl", 2, 2 + +point = client.get_point(58, 16) +point.df +# dataframe of actual data + +point.df_info +# dataframe with information about available parameters + +multipoint = client.get_multipoint( + "2024-04-1206T16:00:00Z", "t", "hl", 2, 2 ) ``` diff --git a/docs/metobs-example.md b/docs/metobs-example.md index b2c94711..1b4494ff 100644 --- a/docs/metobs-example.md +++ b/docs/metobs-example.md @@ -2,7 +2,9 @@ Direct usage of `Metobs`. -The `Metobs` clinet consistes of 5 objects +## Basic chain + +The `Metobs` client consistes of 5 objects `Versions, Parameters, Stations, Periods, Data` that building up a chain to get data. @@ -13,7 +15,7 @@ versions = Versions() # defaults to type = json, this step can be skipped versions.data # show data from this endpoint -parameters = Parameters(versions) # can be called by simply Parameters() +parameters = Parameters(versions) # can be called: Parameters() parameters.data # show all available parameters in that API version @@ -30,8 +32,8 @@ data.df # show the station data dataframe ``` -Note that, the last call gives back a `MetobsDataModel` with four -dataframes when a single station is used to fetch data as above. +The `data` variable holds four dataframes when a single station +is used to fetch data as above. They are called `station`, `parameter`, `period` and `df`. It is posible to fetch a station set, that is, data from many stations for @@ -48,3 +50,219 @@ data.df ``` in which case only the `parameter` and `df` fields are populated. + +## Detailed chain + +All objects contain several useful fields, in this example we will +look at a few of them. For a full reference see +[Metobs reference](/ifk-smhi/metobs-reference/). + +### All objects + +All objects contain these fields + +- `headers` +- `key` +- `updated` +- `title` +- `summary` +- `link` +- `url` + +```python +from smhi.metobs import Versions + +versions = Versions() +versions.headers +# show response header + +versions.title +# show API title + +versions.link +# show list of links subsequent APIs will use + +versions.url +# the URL used for the request +``` + +### Parameters + +The most important fields are + +- `resource` +- `data` + +where `data` is equal to `resource`, but condensed in information. + +```python +from smhi.metobs import Parameters + +parameters = Parameters() +parameters.data +# tuple of parameters with key, title, summary and unit fields + +parameters.data +# list of parameters with all available fields +``` + +### Stations + +The most important fields are + +- `station` +- `station_set` +- `data` + +where again `data` is equal to `station`, but condensed in information. +`station_set` is entirely different here. + +It is important to note that parameter key `1` in the example below also has +name `Lufttemperatur` and this object can be called with both. + +```python +from smhi.metobs import Stations + +stations = Stations(parameters, 1) +stations = Stations(parameters, parameter_title="Lufttemperatur") # equivalent +stations.data +# tuple of stations with a tuple containing station id and name + +stations.station +# list of stations with all available fields + +stations.station_set +# list of station sets +``` + +### Periods + +The most important fields are + +- `period` +- `data` +- `position` +- `owner` +- `active` +- `time_from` +- `time_to` + +where again `data` is equal to `period`, but condensed in information. + +Note that, `Periods` can be called with a station `id=1`, `name="Akalla"` +(from the above station) or `station_set="all"` key. + +```python +from smhi.metobs import Periods + +periods = Periods(stations, 1) +periods = Periods(stations, station_name="Akalla") # equivalent + +periods.data +# tuple of available periods, e.g. "corrected-archive" etc. + +periods.period +# list of all available information for periods available + +periods.position +# list station position, height, latitude and longitude + +periods.owner +# station owner + +periods.active +# boolean whether the station is active or not + +periods.time_from +# datetime from when the station started collecting data + +periods.time_to +# datetime until when the station collected data +``` + +When `Periods` is called with `station_set` many fields are empty. + +```python +from smhi.metobs import Periods + +periods_set = Periods(stations, station_set="all") # not equivalent to above + +periods_set.data +# tuple of available periods, e.g. "corrected-archive" etc. + +periods_set.period +# list of all available information for periods available + +periods_set.position +periods_set.owner +periods_set.active +periods_set.time_from +periods_set.time_to +# all empty as data response will contain data from many stations +``` + +### Data from station + +Finally, we can fetch the actual data. The most important fields are + +- `df` +- `parameter` +- `station` +- `period` +- `time_from` +- `time_to` + +```python +from smhi.metobs import Data + +data = Data(periods, period="corrected-archive") # period can be omitted + +data.df +# dataframe of actual data + +data.parameter +# dataframe with parameter information, e.g. units and frequency + +data.station +# dataframe with station information + +data.period +# dataframe with period and location information + +data.time_from +# datetime from when data is available in response + +data.time_to +# datetime until data is available in response +``` + +### Data from station set + +Data from a station set is slighlty different and the most important fields are + +- `df` +- `station` +- `time_from` +- `time_to` + +```python +from smhi.metobs import Data + +data = Data(periods_set) + +data.df +# dataframe of actual data + +data.parameter +# dataframe with parameter information, e.g. units and frequency + +data.station +data.period +# empty, reponse contains data from many stations + +data.time_from +# datetime from when data is available in response + +data.time_to +# datetime until data is available in response +``` diff --git a/docs/strang-example.md b/docs/strang-example.md index 1c058067..ea0bcf91 100644 --- a/docs/strang-example.md +++ b/docs/strang-example.md @@ -10,10 +10,11 @@ All available parameters can be listed as from smhi.strang import Strang client = Strang() -response = client.parameters +parameters = client.parameters +# prints parameters and also returns a list with parameter numbers ``` -## Point and multipoint data +## Point data To get a point response from `Strang` do @@ -21,12 +22,54 @@ To get a point response from `Strang` do from smhi.strang import Strang client = Strang() -response = client.get_point( - 58, 16, 118, "2020-01-01", "2020-02-01", "hourly" +point = client.get_point( + latitude=58, + longitude=16, + parameter=118, + time_from="2020-01-01", + time_to="2020-02-01", + time_interval="hourly", ) ``` -and for a multi point response +Note that the date input can be in either `str` or `datetime` formats. +The API expects times in UTC but the client expects the user to input +local datetimes. The local datetimes are converted to UTC before an API request +is performed. + +That is, this will work fine + +```python +from datetime import datetime +from smhi.strang import Strang + +client = Strang() +point = client.get_point( + 58, + 16, + 118, + "2020 01 01", + datetime.strptime("2020-02-01", '%Y-%m-%d').date(), + "hourly", +) +``` + +The return object is of type `StrangPoint`. For a full list of available +fields see [strang model](/ifk-smhi/strang-model/). + +```python +response.df +``` + +while shows what the selected parameter means. + +```python +response.parameter_meaning +``` + +## Multipoint data + +For a multi point response ```python from smhi.strang import Strang @@ -35,8 +78,13 @@ client = Strang() response = client.get_multipoint(116, "2022-01-01", "monthly") ``` -The return objects are of type `StrangPointModel` and `StrangMultiPointModel`, -respectively. This will show the data in a dataframe +Also here, the datetime input can be in either `str` or `datetime` formats. + +The return objects is of type `StrangMultiPoint`. +For a full list of available fields see +[strang model](/ifk-smhi/strang-model/). + +This will show the data in a dataframe ```python response.df diff --git a/mkdocs.yml b/mkdocs.yml index 47dbce55..8515eeb9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,14 +12,16 @@ nav: - "Mesan": mesan-example.md - "Strang": strang-example.md - Reference: - - "Metobs reference": metobs-reference.md - - "Metfcts reference": metfcts-reference.md - - "Mesan reference": mesan-reference.md - - "Strang reference": strang-reference.md - - "Metobs model": metobs-model.md - - "Metfcts model": metfcts-model.md - - "Mesan model": mesan-model.md - - "Strang model": strang-model.md + - "Clients": + - "Metobs reference": metobs-reference.md + - "Metfcts reference": metfcts-reference.md + - "Mesan reference": mesan-reference.md + - "Strang reference": strang-reference.md + - "Models": + - "Metobs model": metobs-model.md + - "Metfcts model": metfcts-model.md + - "Mesan model": mesan-model.md + - "Strang model": strang-model.md watch: - src/smhi @@ -41,7 +43,12 @@ theme: plugins: - search - - mkdocstrings + - mkdocstrings: + handlers: + python: + options: + show_root_heading: true + heading_level: 2 markdown_extensions: - pymdownx.highlight: diff --git a/src/smhi/metobs.py b/src/smhi/metobs.py index 01c00209..909f5258 100644 --- a/src/smhi/metobs.py +++ b/src/smhi/metobs.py @@ -24,7 +24,7 @@ class BaseMetobs: - """Base Metobs class.""" + """BaseMetobs class.""" def __init__(self) -> None: """Initialise base class.""" @@ -105,7 +105,7 @@ def __init__( ) -> None: """Get versions. - For now, only supports `json` and version 1. + Currently, only supports `json` and version 1. Args: data_type: data_type of request diff --git a/src/smhi/models/mesan_model.py b/src/smhi/models/mesan_model.py index 7dea6f06..7f710e28 100644 --- a/src/smhi/models/mesan_model.py +++ b/src/smhi/models/mesan_model.py @@ -7,20 +7,11 @@ from pydantic import BaseModel, ConfigDict, Field -class MesanParameterItem(BaseModel): - name: str - key: str - level_type: str = Field(..., alias="levelType") - level: int - unit: str - missing_value: int = Field(..., alias="missingValue") - - -class MesanParameter(BaseModel): +class MesanValidTime(BaseModel): url: str status: int headers: Dict[str, str] - parameter: List[MesanParameterItem] + valid_time: List[datetime] class MesanApprovedTime(BaseModel): @@ -31,13 +22,6 @@ class MesanApprovedTime(BaseModel): reference_time: datetime -class MesanValidTime(BaseModel): - url: str - status: int - headers: Dict[str, str] - valid_time: List[datetime] - - class MesanGeoPolygon(BaseModel): url: str status: int @@ -54,6 +38,22 @@ class MesanGeoMultiPoint(BaseModel): coordinates: List[List[float]] +class MesanParameterItem(BaseModel): + name: str + key: str + level_type: str = Field(..., alias="levelType") + level: int + unit: str + missing_value: int = Field(..., alias="missingValue") + + +class MesanParameter(BaseModel): + url: str + status: int + headers: Dict[str, str] + parameter: List[MesanParameterItem] + + class MesanPointInfoSchema(pa.DataFrameModel): name: Index[str] = pa.Field(check_name=True, unique=True) level: Series[int] @@ -73,6 +73,8 @@ class MesanGeometry(BaseModel): class MesanPoint(BaseModel): + """Point model.""" + model_config = ConfigDict(arbitrary_types_allowed=True) longitude: float @@ -89,6 +91,8 @@ class MesanPoint(BaseModel): class MesanMultiPoint(BaseModel): + """Multi point model.""" + parameter: str parameter_meaning: str geo: bool diff --git a/src/smhi/models/metfcts_model.py b/src/smhi/models/metfcts_model.py index 8ec31814..6ac9e988 100644 --- a/src/smhi/models/metfcts_model.py +++ b/src/smhi/models/metfcts_model.py @@ -68,6 +68,8 @@ class MetfctsMultiPointSchema(pa.DataFrameModel): class MetfctsPoint(BaseModel): + """Point model.""" + model_config = ConfigDict(arbitrary_types_allowed=True) longitude: float @@ -84,6 +86,8 @@ class MetfctsPoint(BaseModel): class MetfctsMultiPoint(BaseModel): + """Multi point model.""" + parameter: str parameter_meaning: str geo: bool diff --git a/src/smhi/models/strang_model.py b/src/smhi/models/strang_model.py index e258b9bb..ddffb9ed 100644 --- a/src/smhi/models/strang_model.py +++ b/src/smhi/models/strang_model.py @@ -21,6 +21,8 @@ class StrangMultiPointSchema(pa.DataFrameModel): class StrangParameter(BaseModel): + """Strang parameter model.""" + key: Optional[int] meaning: Optional[str] time_from: Optional[datetime] = None @@ -39,6 +41,8 @@ class StrangMultiPointItem(BaseModel): class StrangPoint(BaseModel): + """Point model.""" + parameter_key: int parameter_meaning: str longitude: float @@ -53,6 +57,8 @@ class StrangPoint(BaseModel): class StrangMultiPoint(BaseModel): + """Multi point model.""" + parameter_key: int parameter_meaning: str valid_time: Optional[datetime]