Add RIO IFDM client

This commit is contained in:
Jules 2024-06-30 17:57:24 +02:00
parent c878bf8f81
commit 3b2e88213e
Signed by: jdejaegh
GPG key ID: 99D6D184CA66933A
11 changed files with 921 additions and 64 deletions

View file

@ -1,6 +1,6 @@
from .api import IrcelineApiError from .api import IrcelineApiError
from .rio import IrcelineRioClient from .rio import IrcelineRioClient
from .forecast import IrcelineForecastClient from .forecast import IrcelineForecastClient
from .data import RioFeature, ForecastFeature, FeatureValue, BelAqiIndex from .data import RioFeature, ForecastFeature, FeatureValue
__version__ = '2.0.0' __version__ = '2.0.0'

View file

@ -2,17 +2,18 @@ import asyncio
import socket import socket
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Tuple, List, Set from typing import Tuple, List, Set
from xml.etree import ElementTree
import aiohttp import aiohttp
import async_timeout import async_timeout
from aiohttp import ClientResponse
from .data import IrcelineFeature from .data import IrcelineFeature
from .utils import SizedDict from .utils import SizedDict
_rio_wfs_base_url = 'https://geo.irceline.be/wfs' _rio_wfs_base_url = 'https://geo.irceline.be/wfs'
_forecast_wms_base_url = 'https://geo.irceline.be/forecast/wms' _forecast_wms_base_url = 'https://geo.irceline.be/forecast/wms'
# noinspection HttpUrlsUsage _rio_ifdm_wms_base_url = 'https://geobelair.irceline.be/rioifdm/wms'
# There is not HTTPS version of this endpoint
_user_agent = 'github.com/jdejaegh/python-irceline' _user_agent = 'github.com/jdejaegh/python-irceline'
@ -65,3 +66,38 @@ class IrcelineBaseClient(ABC):
raise IrcelineApiError(f"Something really wrong happened! {exception}") from exception raise IrcelineApiError(f"Something really wrong happened! {exception}") from exception
class IrcelineBaseWmsClient(IrcelineBaseClient, ABC):
_default_querystring = {"service": "WMS",
"version": "1.1.1",
"request": "GetFeatureInfo",
"info_format": "application/json",
"width": "1",
"height": "1",
"srs": "EPSG:4326",
"X": "1",
"Y": "1"}
_epsilon = 0.00001
_base_url = None
@staticmethod
def _parse_capabilities(xml_string: str) -> Set[str]:
try:
root = ElementTree.fromstring(xml_string)
except ElementTree.ParseError:
return set()
path = './/Capability/Layer/Layer/Name'
feature_type_names = {t.text for t in root.findall(path)}
return feature_type_names
async def get_capabilities(self) -> Set[str]:
"""
Fetch the list of possible features from the WMS server
:return: set of features available on the WMS server
"""
querystring = {"service": "WMS",
"version": "1.1.1",
"request": "GetCapabilities"}
r: ClientResponse = await self._api_wrapper(self._base_url, querystring)
return self._parse_capabilities(await r.text())

View file

@ -1,5 +1,5 @@
from datetime import datetime, date from datetime import datetime, date
from enum import StrEnum, Enum from enum import StrEnum
from typing import TypedDict from typing import TypedDict
@ -30,6 +30,14 @@ class RioFeature(IrcelineFeature):
SO2_HMEAN = 'rio:so2_hmean' SO2_HMEAN = 'rio:so2_hmean'
class RioIfdmFeature(IrcelineFeature):
PM25_HMEAN = 'rioifdm:pm25_hmean'
NO2_HMEAN = 'rioifdm:no2_hmean'
PM10_HMEAN = 'rioifdm:pm10_hmean'
O3_HMEAN = 'rioifdm:o3_hmean'
BELAQI = 'rioifdm:belaqi'
class ForecastFeature(IrcelineFeature): class ForecastFeature(IrcelineFeature):
NO2_MAXHMEAN = 'forecast:no2_maxhmean' NO2_MAXHMEAN = 'forecast:no2_maxhmean'
NO2_DMEAN = 'forecast:no2_dmean' NO2_DMEAN = 'forecast:no2_dmean'
@ -40,20 +48,7 @@ class ForecastFeature(IrcelineFeature):
BELAQI = 'forecast:belaqi' BELAQI = 'forecast:belaqi'
class BelAqiIndex(Enum):
EXCELLENT = 1
VERY_GOOD = 2
GOOD = 3
FAIRLY_GOOD = 4
MODERATE = 5
POOR = 6
VERY_POOR = 7
BAD = 8
VERY_BAD = 9
HORRIBLE = 10
class FeatureValue(TypedDict): class FeatureValue(TypedDict):
# Timestamp at which the value was computed # Timestamp at which the value was computed
timestamp: datetime | date | None timestamp: datetime | date | None
value: int | float | BelAqiIndex | None value: int | float | None

View file

@ -1,16 +1,15 @@
from datetime import date, timedelta, datetime from datetime import date, timedelta, datetime
from itertools import product from itertools import product
from typing import List, Tuple, Dict, Set from typing import List, Tuple, Dict
from xml.etree import ElementTree
from aiohttp import ClientResponse, ClientResponseError from aiohttp import ClientResponse, ClientResponseError
from .api import IrcelineBaseClient, _forecast_wms_base_url, IrcelineApiError from .api import IrcelineApiError, IrcelineBaseWmsClient, _forecast_wms_base_url
from .data import ForecastFeature, FeatureValue from .data import ForecastFeature, FeatureValue
class IrcelineForecastClient(IrcelineBaseClient): class IrcelineForecastClient(IrcelineBaseWmsClient):
_epsilon = 0.00001 _base_url = _forecast_wms_base_url
async def get_data(self, async def get_data(self,
features: List[ForecastFeature], features: List[ForecastFeature],
@ -26,22 +25,14 @@ class IrcelineForecastClient(IrcelineBaseClient):
timestamp = date.today() timestamp = date.today()
result = dict() result = dict()
lat, lon = position lat, lon = position
base_querystring = {"service": "WMS", base_querystring = (self._default_querystring |
"version": "1.1.1", {"bbox": f"{lon},{lat},{lon + self._epsilon},{lat + self._epsilon}"})
"request": "GetFeatureInfo",
"info_format": "application/json",
"width": "1",
"height": "1",
"srs": "EPSG:4326",
"bbox": f"{lon},{lat},{lon + self._epsilon},{lat + self._epsilon}",
"X": "1",
"Y": "1"}
for feature, d in product(features, range(4)): for feature, d in product(features, range(4)):
querystring = base_querystring | {"layers": f"{feature}_d{d}", querystring = base_querystring | {"layers": f"{feature}_d{d}",
"query_layers": f"{feature}_d{d}"} "query_layers": f"{feature}_d{d}"}
try: try:
r: ClientResponse = await self._api_wrapper(_forecast_wms_base_url, querystring) r: ClientResponse = await self._api_wrapper(self._base_url, querystring)
r: dict = await r.json() r: dict = await r.json()
result[(feature, timestamp + timedelta(days=d))] = FeatureValue( result[(feature, timestamp + timedelta(days=d))] = FeatureValue(
value=r.get('features', [{}])[0].get('properties', {}).get('GRAY_INDEX'), value=r.get('features', [{}])[0].get('properties', {}).get('GRAY_INDEX'),
@ -50,26 +41,3 @@ class IrcelineForecastClient(IrcelineBaseClient):
result[(feature, timestamp + timedelta(days=d))] = FeatureValue(value=None, timestamp=None) result[(feature, timestamp + timedelta(days=d))] = FeatureValue(value=None, timestamp=None)
return result return result
async def get_capabilities(self) -> Set[str]:
"""
Fetch the list of possible features from the WMS server
:return: set of features available on the WMS server
"""
querystring = {"service": "WMS",
"version": "1.1.1",
"request": "GetCapabilities"}
r: ClientResponse = await self._api_wrapper(_forecast_wms_base_url, querystring)
return self._parse_capabilities(await r.text())
@staticmethod
def _parse_capabilities(xml_string: str) -> Set[str]:
try:
root = ElementTree.fromstring(xml_string)
except ElementTree.ParseError:
return set()
path = './/Capability/Layer/Layer/Name'
feature_type_names = {t.text for t in root.findall(path)}
return feature_type_names

View file

@ -2,15 +2,19 @@ from datetime import datetime, date, UTC, timedelta
from typing import List, Tuple, Dict, Set from typing import List, Tuple, Dict, Set
from xml.etree import ElementTree from xml.etree import ElementTree
from aiohttp import ClientResponse from aiohttp import ClientResponse, ClientResponseError
from .api import IrcelineBaseClient, _rio_wfs_base_url, IrcelineApiError from .api import IrcelineBaseClient, _rio_wfs_base_url, IrcelineApiError, _rio_ifdm_wms_base_url, IrcelineBaseWmsClient
from .data import RioFeature, FeatureValue from .data import RioFeature, FeatureValue, RioIfdmFeature
from .utils import epsg_transform from .utils import epsg_transform
class IrcelineRioClient(IrcelineBaseClient): class IrcelineRioClient(IrcelineBaseClient):
"""API client for RIO interpolated IRCEL - CELINE open data""" """
API client for RIO interpolated IRCEL - CELINE open data
RIO is more coarse grained for interpolation than RIO IFDM and allows to request multiple features in the same
request, which may be faster.
"""
async def get_data(self, async def get_data(self,
features: List[RioFeature], features: List[RioFeature],
@ -124,3 +128,41 @@ class IrcelineRioClient(IrcelineBaseClient):
result[name] = FeatureValue(timestamp=timestamp, value=value) result[name] = FeatureValue(timestamp=timestamp, value=value)
return result return result
class IrcelineRioIfdmClient(IrcelineBaseWmsClient):
"""
API client for RIO IFDM interpolated IRCEL - CELINE open data
RIO IFDM is more fine-grained for interpolation than RIO but only allows one feature to be request at a time, which
may be slower
"""
_base_url = _rio_ifdm_wms_base_url
async def get_data(self,
features: List[RioIfdmFeature],
position: Tuple[float, float]
) -> Dict[RioIfdmFeature, FeatureValue]:
"""
Get interpolated concentrations for the given features at the given position.
:param features: pollutants to get the forecasts for
:param position: (lat, long)
:return: dict where key is RioIfdmFeature and value is a FeatureValue
"""
result = dict()
lat, lon = position
base_querystring = (self._default_querystring |
{"bbox": f"{lon},{lat},{lon + self._epsilon},{lat + self._epsilon}"})
print({"bbox": f"{lon},{lat},{lon + self._epsilon},{lat + self._epsilon}"})
for feature in features:
querystring = base_querystring | {"layers": f"{feature}", "query_layers": f"{feature}"}
try:
r: ClientResponse = await self._api_wrapper(self._base_url, querystring)
r: dict = await r.json()
result[feature] = FeatureValue(
value=r.get('features', [{}])[0].get('properties', {}).get('GRAY_INDEX'),
timestamp=datetime.fromisoformat(r.get('timeStamp')) if 'timeStamp' in r else None)
except (IrcelineApiError, ClientResponseError, IndexError):
result[feature] = FeatureValue(value=None, timestamp=None)
return result

View file

@ -0,0 +1,17 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "",
"geometry": null,
"properties": {
"GRAY_INDEX": 84.33950805664062
}
}
],
"totalFeatures": "unknown",
"numberReturned": 1,
"timeStamp": "2024-06-30T15:43:07.222Z",
"crs": null
}

694
tests/fixtures/rio_ifdm_capabilities.xml vendored Normal file
View file

@ -0,0 +1,694 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE WMT_MS_Capabilities SYSTEM "https://geobelair.irceline.be/schemas/wms/1.1.1/WMS_MS_Capabilities.dtd">
<WMT_MS_Capabilities version="1.1.1" updateSequence="4129">
<Service>
<Name>OGC:WMS</Name>
<Title>IRCEL - CELINE - Web Map Service</Title>
<Abstract>A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also generate
PDF, SVG, KML, GeoRSS
</Abstract>
<KeywordList>
<Keyword>WFS</Keyword>
<Keyword>WMS</Keyword>
<Keyword>GEOSERVER</Keyword>
</KeywordList>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="http://geo.irceline.be/rioifdm/wms"/>
<ContactInformation>
<ContactPersonPrimary>
<ContactPerson>IRCEL - CELINE</ContactPerson>
<ContactOrganization>Belgian Interregional Environment Agency</ContactOrganization>
</ContactPersonPrimary>
<ContactPosition/>
<ContactAddress>
<AddressType/>
<Address>Gaucheretstraat 92-94 Rue Gaucheret</Address>
<City>Brussels</City>
<StateOrProvince/>
<PostCode>1030</PostCode>
<Country>Belgium</Country>
</ContactAddress>
<ContactVoiceTelephone>+(32)(0)2 227 57 01</ContactVoiceTelephone>
<ContactFacsimileTelephone/>
<ContactElectronicMailAddress>info@irceline.be</ContactElectronicMailAddress>
</ContactInformation>
<Fees>NONE</Fees>
<AccessConstraints>NONE</AccessConstraints>
</Service>
<Capability>
<Request>
<GetCapabilities>
<Format>application/vnd.ogc.wms_xml</Format>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
<Post>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Post>
</HTTP>
</DCPType>
</GetCapabilities>
<GetMap>
<Format>image/png</Format>
<Format>application/atom xml</Format>
<Format>application/atom+xml</Format>
<Format>application/json;type=utfgrid</Format>
<Format>application/openlayers</Format>
<Format>application/openlayers2</Format>
<Format>application/openlayers3</Format>
<Format>application/pdf</Format>
<Format>application/rss xml</Format>
<Format>application/rss+xml</Format>
<Format>application/vnd.google-earth.kml</Format>
<Format>application/vnd.google-earth.kml xml</Format>
<Format>application/vnd.google-earth.kml+xml</Format>
<Format>application/vnd.google-earth.kml+xml;mode=networklink</Format>
<Format>application/vnd.google-earth.kmz</Format>
<Format>application/vnd.google-earth.kmz xml</Format>
<Format>application/vnd.google-earth.kmz+xml</Format>
<Format>application/vnd.google-earth.kmz;mode=networklink</Format>
<Format>atom</Format>
<Format>image/geotiff</Format>
<Format>image/geotiff8</Format>
<Format>image/gif</Format>
<Format>image/gif;subtype=animated</Format>
<Format>image/jpeg</Format>
<Format>image/png8</Format>
<Format>image/png; mode=8bit</Format>
<Format>image/svg</Format>
<Format>image/svg xml</Format>
<Format>image/svg+xml</Format>
<Format>image/tiff</Format>
<Format>image/tiff8</Format>
<Format>image/vnd.jpeg-png</Format>
<Format>image/vnd.jpeg-png8</Format>
<Format>kml</Format>
<Format>kmz</Format>
<Format>openlayers</Format>
<Format>rss</Format>
<Format>text/html; subtype=openlayers</Format>
<Format>text/html; subtype=openlayers2</Format>
<Format>text/html; subtype=openlayers3</Format>
<Format>utfgrid</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetMap>
<GetFeatureInfo>
<Format>text/plain</Format>
<Format>application/vnd.ogc.gml</Format>
<Format>text/xml</Format>
<Format>application/vnd.ogc.gml/3.1.1</Format>
<Format>text/xml; subtype=gml/3.1.1</Format>
<Format>text/html</Format>
<Format>application/json</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
<Post>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Post>
</HTTP>
</DCPType>
</GetFeatureInfo>
<DescribeLayer>
<Format>application/vnd.ogc.wms_xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
</HTTP>
</DCPType>
</DescribeLayer>
<GetLegendGraphic>
<Format>image/png</Format>
<Format>image/jpeg</Format>
<Format>application/json</Format>
<Format>image/gif</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetLegendGraphic>
<GetStyles>
<Format>application/vnd.ogc.sld+xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?SERVICE=WMS&amp;"/>
</Get>
</HTTP>
</DCPType>
</GetStyles>
</Request>
<Exception>
<Format>application/vnd.ogc.se_xml</Format>
<Format>application/vnd.ogc.se_inimage</Format>
<Format>application/vnd.ogc.se_blank</Format>
<Format>application/json</Format>
</Exception>
<UserDefinedSymbolization SupportSLD="1" UserLayer="1" UserStyle="1" RemoteWFS="1"/>
<Layer>
<Title>IRCEL - CELINE - Web Map Service</Title>
<Abstract>A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also
generate PDF, SVG, KML, GeoRSS
</Abstract>
<!--Limited list of EPSG projections:-->
<SRS>EPSG:3857</SRS>
<SRS>EPSG:4258</SRS>
<SRS>EPSG:4326</SRS>
<SRS>EPSG:31370</SRS>
<SRS>EPSG:900913</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<AuthorityURL name="IRCEL - CELINE">
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://geo.irceline.be"/>
</AuthorityURL>
<Identifier authority="IRCEL - CELINE">http://geo.irceline.be</Identifier>
<Layer queryable="1" opaque="0">
<Name>belaqi</Name>
<Title>belaqi</Title>
<Abstract/>
<KeywordList>
<Keyword>belaqi</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>belaqi_raster_discrete_belair</Name>
<Title>AQ index for raster (discrete colour schale)</Title>
<Abstract>BelAQI index colour scale. Classic concentration color progression.</Abstract>
<LegendURL width="36" height="250">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=belaqi"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>belaqi_dm1</Name>
<Title>belaqi_dm1</Title>
<Abstract/>
<KeywordList>
<Keyword>belaqi_dmean_dm1</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>belaqi_raster_discrete_belair</Name>
<Title>AQ index for raster (discrete colour schale)</Title>
<Abstract>BelAQI index colour scale. Classic concentration color progression.</Abstract>
<LegendURL width="36" height="250">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=belaqi_dm1"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>belaqi_dm2</Name>
<Title>belaqi_dm2</Title>
<Abstract/>
<KeywordList>
<Keyword>belaqi_dmean_dm2</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>belaqi_raster_discrete_belair</Name>
<Title>AQ index for raster (discrete colour schale)</Title>
<Abstract>BelAQI index colour scale. Classic concentration color progression.</Abstract>
<LegendURL width="36" height="250">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=belaqi_dm2"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>belaqi_dm3</Name>
<Title>belaqi_dm3</Title>
<Abstract/>
<KeywordList>
<Keyword>belaqi_dmean_dm3</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>belaqi_raster_discrete_belair</Name>
<Title>AQ index for raster (discrete colour schale)</Title>
<Abstract>BelAQI index colour scale. Classic concentration color progression.</Abstract>
<LegendURL width="36" height="250">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=belaqi_dm3"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>no2_dm1</Name>
<Title>no2_dm1</Title>
<Abstract/>
<KeywordList>
<Keyword>no2_dmean_dm1</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>no2_dmean_raster_discrete_belair</Name>
<Title>Nitrogen dioxide (NO2) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for Nitrogen dioxide (NO2).
</Abstract>
<LegendURL width="80" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=no2_dm1"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>no2_dm2</Name>
<Title>no2_dm2</Title>
<Abstract/>
<KeywordList>
<Keyword>no2_dmean_dm2</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>no2_dmean_raster_discrete_belair</Name>
<Title>Nitrogen dioxide (NO2) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for Nitrogen dioxide (NO2).
</Abstract>
<LegendURL width="80" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=no2_dm2"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>no2_dm3</Name>
<Title>no2_dm3</Title>
<Abstract/>
<KeywordList>
<Keyword>no2_dmean_dm3</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>no2_dmean_raster_discrete_belair</Name>
<Title>Nitrogen dioxide (NO2) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for Nitrogen dioxide (NO2).
</Abstract>
<LegendURL width="80" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=no2_dm3"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>no2_hmean</Name>
<Title>no2_hmean</Title>
<Abstract/>
<KeywordList>
<Keyword>no2_hmean</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>no2_hmean_raster_discrete_belair</Name>
<Title>Nitrogen dioxide (NO2) hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Hourly mean concentration classes for Nitrogen dioxide (NO2).
</Abstract>
<LegendURL width="80" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=no2_hmean"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>o3_dm1</Name>
<Title>o3_dm1</Title>
<Abstract/>
<KeywordList>
<Keyword>o3_max8hmean_dm1</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>o3_max8hmean_raster_discrete_belair</Name>
<Title>Ozone (O3) daily max 8-hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily max 8-hourly mean concentration classes for Ozone (O3).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=o3_dm1"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>o3_dm2</Name>
<Title>o3_dm2</Title>
<Abstract/>
<KeywordList>
<Keyword>o3_max8hmean_dm2</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>o3_max8hmean_raster_discrete_belair</Name>
<Title>Ozone (O3) daily max 8-hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily max 8-hourly mean concentration classes for Ozone (O3).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=o3_dm2"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>o3_dm3</Name>
<Title>o3_dm3</Title>
<Abstract/>
<KeywordList>
<Keyword>o3_max8hmean_dm3</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>o3_max8hmean_raster_discrete_belair</Name>
<Title>Ozone (O3) daily max 8-hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily max 8-hourly mean concentration classes for Ozone (O3).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=o3_dm3"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>o3_hmean</Name>
<Title>o3_hmean</Title>
<Abstract/>
<KeywordList>
<Keyword>o3_hmean</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>o3_hmean_raster_discrete_belair</Name>
<Title>Ozone (O3) hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Annual mean concentration classes for Ozone (O3).</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=o3_hmean"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm10_dm1</Name>
<Title>pm10_dm1</Title>
<Abstract/>
<KeywordList>
<Keyword>pm10_dmean_dm1</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm10_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM10) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM10).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm10_dm1"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm10_dm2</Name>
<Title>pm10_dm2</Title>
<Abstract/>
<KeywordList>
<Keyword>pm10_dmean_dm2</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm10_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM10) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM10).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm10_dm2"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm10_dm3</Name>
<Title>pm10_dm3</Title>
<Abstract/>
<KeywordList>
<Keyword>pm10_dmean_dm3</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm10_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM10) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM10).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm10_dm3"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm10_hmean</Name>
<Title>pm10_hmean</Title>
<Abstract/>
<KeywordList>
<Keyword>pm10_hmean</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm10_hmean_raster_discrete_belair</Name>
<Title>particulate matter (PM10) hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Hourly mean concentration classes for particulate matter
(PM10).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm10_hmean"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm25_dm1</Name>
<Title>pm25_dm1</Title>
<Abstract/>
<KeywordList>
<Keyword>pm25_dmean_dm1</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm25_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM25) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM25).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm25_dm1"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm25_dm2</Name>
<Title>pm25_dm2</Title>
<Abstract/>
<KeywordList>
<Keyword>pm25_dmean_dm2</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm25_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM25) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM25).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm25_dm2"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm25_dm3</Name>
<Title>pm25_dm3</Title>
<Abstract/>
<KeywordList>
<Keyword>pm25_dmean_dm3</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm25_dmean_raster_discrete_belair</Name>
<Title>particulate matter (PM25) daily mean concentrations</Title>
<Abstract>BelAQI index colour scale. Daily mean concentration classes for particulate matter
(PM25).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm25_dm3"/>
</LegendURL>
</Style>
</Layer>
<Layer queryable="1" opaque="0">
<Name>pm25_hmean</Name>
<Title>pm25_hmean</Title>
<Abstract/>
<KeywordList>
<Keyword>pm25_hmean</Keyword>
<Keyword>WCS</Keyword>
<Keyword>GeoTIFF</Keyword>
</KeywordList>
<SRS>EPSG:31370</SRS>
<LatLonBoundingBox minx="2.4804079470216474" miny="49.461523892203324" maxx="6.494748595696256"
maxy="51.54189845090732"/>
<BoundingBox SRS="EPSG:31370" minx="18950.0" miny="18650.0" maxx="297550.0" maxy="248050.0"/>
<Style>
<Name>pm25_hmean_raster_discrete_belair</Name>
<Title>particulate matter (PM25) hourly mean concentrations</Title>
<Abstract>BelAQI index colour scale. Hourly mean concentration classes for particulate matter
(PM25).
</Abstract>
<LegendURL width="87" height="252">
<Format>image/png</Format>
<OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:href="https://geobelair.irceline.be/rioifdm/wms?request=GetLegendGraphic&amp;version=1.1.1&amp;format=image%2Fpng&amp;width=20&amp;height=20&amp;layer=pm25_hmean"/>
</LegendURL>
</Style>
</Layer>
</Layer>
</Capability>
</WMT_MS_Capabilities>

View file

@ -95,7 +95,7 @@ async def test_api_forecast():
client = IrcelineForecastClient(session) client = IrcelineForecastClient(session)
features = [ForecastFeature.NO2_DMEAN, ForecastFeature.O3_MAXHMEAN] features = [ForecastFeature.NO2_DMEAN, ForecastFeature.O3_MAXHMEAN]
_ = await client.get_data(features, pos) result = await client.get_data(features, pos)
base = {"service": "WMS", base = {"service": "WMS",
"version": "1.1.1", "version": "1.1.1",
@ -119,6 +119,9 @@ async def test_api_forecast():
session.request.assert_has_calls(calls, any_order=True) session.request.assert_has_calls(calls, any_order=True)
for k, v in result.items():
assert v['value'] == 10.853286743164062
def test_parse_capabilities_with_error(): def test_parse_capabilities_with_error():
result = IrcelineForecastClient._parse_capabilities("wow there no valid XML") result = IrcelineForecastClient._parse_capabilities("wow there no valid XML")

View file

@ -64,7 +64,7 @@ async def test_format_result_dmean():
def test_parse_capabilities(): def test_parse_capabilities():
data = get_api_data('capabilities.xml', plain=True) data = get_api_data('rio_capabilities.xml', plain=True)
result = IrcelineRioClient._parse_capabilities(data) result = IrcelineRioClient._parse_capabilities(data)
expected = {'rio:so2_anmean_be', 'rio:o3_hmean', 'rio:bc_anmean_vl', 'rio:o3_anmean_be', 'rio:pm10_hmean_vl', expected = {'rio:so2_anmean_be', 'rio:o3_hmean', 'rio:bc_anmean_vl', 'rio:o3_anmean_be', 'rio:pm10_hmean_vl',
@ -117,7 +117,7 @@ async def test_api_rio():
async def test_api_rio_get_capabilities(): async def test_api_rio_get_capabilities():
session = get_mock_session(text_file='capabilities.xml') session = get_mock_session(text_file='rio_capabilities.xml')
client = IrcelineRioClient(session) client = IrcelineRioClient(session)
_ = await client.get_capabilities() _ = await client.get_capabilities()

102
tests/test_api_rio_ifdm.py Normal file
View file

@ -0,0 +1,102 @@
from datetime import datetime
from unittest.mock import call
from freezegun import freeze_time
from src.open_irceline.api import _rio_ifdm_wms_base_url, _user_agent
from src.open_irceline.data import RioIfdmFeature, FeatureValue
from src.open_irceline.rio import IrcelineRioIfdmClient
from tests.conftest import get_api_data, get_mock_session
def test_parse_capabilities():
data = get_api_data('rio_ifdm_capabilities.xml', plain=True)
result = IrcelineRioIfdmClient._parse_capabilities(data)
expected = {'no2_dm3', 'belaqi_dm2', 'pm10_hmean', 'belaqi_dm1', 'pm25_dm3', 'pm25_dm2', 'pm10_dm1', 'o3_dm3',
'no2_dm1', 'pm10_dm3', 'pm25_dm1', 'belaqi', 'belaqi_dm3', 'pm10_dm2', 'o3_dm2', 'pm25_hmean', 'o3_dm1',
'o3_hmean', 'no2_dm2', 'no2_hmean'}
assert result == expected
for f in RioIfdmFeature:
assert f"{f.split(':')[1]}" in result
async def test_aget_capabilities():
session = get_mock_session(text_file='rio_ifdm_capabilities.xml')
client = IrcelineRioIfdmClient(session)
_ = await client.get_capabilities()
session.request.assert_called_once_with(
method='GET',
url=_rio_ifdm_wms_base_url,
params={"service": "WMS",
"version": "1.1.1",
"request": "GetCapabilities"},
headers={'User-Agent': _user_agent}
)
@freeze_time(datetime.fromisoformat("2024-06-30T13:00:21.520Z"))
async def test_api_forecast_error():
pos = (50.4657, 4.8647)
session = get_mock_session('forecast_wms_feature_info_invalid.json')
client = IrcelineRioIfdmClient(session)
features = [RioIfdmFeature.NO2_HMEAN, RioIfdmFeature.O3_HMEAN]
result = await client.get_data(features, pos)
for k, v in result.items():
assert v == FeatureValue(timestamp=datetime.fromisoformat("2024-06-30T13:00:21.520Z"), value=None)
async def test_api_forecast():
pos = (50.4657, 4.8647)
lat, lon = pos
session = get_mock_session('forecast_wms_feature_info.json')
client = IrcelineRioIfdmClient(session)
features = [RioIfdmFeature.NO2_HMEAN, RioIfdmFeature.O3_HMEAN]
result = await client.get_data(features, pos)
base = {"service": "WMS",
"version": "1.1.1",
"request": "GetFeatureInfo",
"info_format": "application/json",
"width": "1",
"height": "1",
"srs": "EPSG:4326",
"bbox": f"{lon},{lat},{lon + 0.00001},{lat + 0.00001}",
"X": "1",
"Y": "1"}
calls = [call(
method='GET',
url=_rio_ifdm_wms_base_url,
params=base | {"layers": f"{feature}",
"query_layers": f"{feature}"},
headers={'User-Agent': _user_agent},
)
for feature in features]
session.request.assert_has_calls(calls, any_order=True)
for k, v in result.items():
assert v['value'] == 10.853286743164062
async def test_api_forecast_no_field():
pos = (50.4657, 4.8647)
session = get_mock_session('forecast_wms_feature_info_no_field.json')
client = IrcelineRioIfdmClient(session)
features = [RioIfdmFeature.NO2_HMEAN, RioIfdmFeature.O3_HMEAN]
result = await client.get_data(features, pos)
for k, v in result.items():
assert v == FeatureValue(timestamp=None, value=None)