Services
The core of wetterdienst is to provide data but as we don't collect the data ourselves we rely on consuming data of
already existing services - mostly governmental services. To simplify the implementation of weather services we created
data models, enumerations and classes that should be used in order to adapt whatever API is offered by a service to the
general scheme of wetterdienst with some handful of attributes to define each API and streamline internal workflows. The
following paragraphs describe how we can/may/should implement a new weather service in wetterdienst as far as our own
experience goes. We'll give examples based on the DwdObservationRequest
implementation.
Step 1: Metadata
The basis for the implementation of a new service is the extensive metadata model. The metadata model is a layered
abstraction of inheritance looking roughly like resolutions -> datasets -> parameters which means that the uppermost
layer is the resolution, which contains one or more datasets, which in turn contain one or more parameters. The
following example reflects this structure for the MINUTE_1
dataset PRECIPITATION
of DwdObservationRequest
.
DwdObservationMetadata = {
"resolutions": [
{
"name": "1_minute",
"name_original": "1_minute",
"periods": ["historical", "recent", "now"],
"date_required": False,
"datasets": [
{
"name": "precipitation",
"name_original": "precipitation",
"grouped": True,
"parameters": [
{
"name": "quality",
"name_original": "qn",
"unit_type": "dimensionless",
"unit": "dimensionless",
},
{
"name": "precipitation_height",
"name_original": "rs_01",
"unit_type": "precipitation",
"unit": "millimeter",
},
{
"name": "precipitation_height_droplet",
"name_original": "rth_01",
"unit_type": "precipitation",
"unit": "millimeter",
},
{
"name": "precipitation_height_rocker",
"name_original": "rwh_01",
"unit_type": "precipitation",
"unit": "millimeter",
},
{
"name": "precipitation_index",
"name_original": "rs_ind_01",
"unit_type": "dimensionless",
"unit": "dimensionless",
},
],
}
],
}
]
}
The attributes periods
and date_required
are actually required for each dataset but if they are equal for all
datasets of a resolution they can be defined on the resolution level instead saving you some lines of code.
The grouped
attribute is used to define if data is provided eather as one for the whole dataset or for each
parameter separately. If grouped
is True
the data is provided as one for the whole dataset, if False
the data
is provided for each parameter separately. If grouped
is True
the collect_station_parameter_or_dataset
method
expects parameter_or_dataset
to be of DatasetModel
type, if False
it expects ParameterModel
type.
Given the metadata model above you can request either the whole dataset or a specific parameter of the dataset.
Requesting the whole dataset:
from wetterdienst.provider.dwd.observation import DwdObservationRequest, DwdObservationMetadata
DwdObservationRequest(
parameters=[DwdObservationMetadata.minute_1.precipitation]
)
or equally
from wetterdienst.provider.dwd.observation import DwdObservationRequest
DwdObservationRequest(
parameters=[("minute_1", "precipitation")]
)
Requesting a specific parameter of the dataset:
from wetterdienst.provider.dwd.observation import DwdObservationRequest, DwdObservationMetadata
DwdObservationRequest(
parameters=[DwdObservationMetadata.minute_1.precipitation.precipitation_height]
)
or equally
from wetterdienst.provider.dwd.observation import DwdObservationRequest
DwdObservationRequest(
parameters=[("minute_1", "precipitation", "precipitation_height")]
)
Step 2: Request class
The request class represents a request and carries all the required attributes as well as the values class that is
responsible for acquiring the data later on. The implementation is based on TimeseriesRequest
from wetterdienst.core
.
Attributes:
@property
@abstractmethod
def provider(self) -> Provider:
"""Optional enumeration for multiple resolutions"""
pass
@property
@abstractmethod
def kind(self) -> Kind:
"""Optional enumeration for multiple resolutions"""
pass
@property
@abstractmethod
def _values(self):
"""Class to get the values for a request"""
pass
@property
@abstractmethod
def metadata(self):
"""Metadata for the request"""
pass
TimeseriesRequest
has one abstract method that has to be implemented: the _all
which manages to get a listing of
stations for the requested datasets/parameters. The listing includes:
- station_id
- start_date
- end_date
- height
- name
- state
- latitude
- longitude
Step 3: Values class
The values class is based on TimeseriesValues
and manages the acquisition of actual data. The
class is also part of the TimeseriesRequest
being accessed via the _values
property. It has to implement the
_collect_station_parameter
method that takes care of getting values of a parameter/dataset for a station id.