Here's my approach to the problem, although my need for owslib is very specific, so it may not be applicable to your situation. My objective was to extract weather forecast data at a specific point using the WebMapService of owslib
. As far as I know, this is the only way to achieve this with Environment Canada forecasts.
To get the data, I made a request through wms.getfeatureinfo()
. This returns a wrapper function that can be read and decoded without asyncio. However, to make it work with asyncio, I needed to use the method .geturl() from the wrapper, which gets the query URL. Then, I used the function async with session.get(url)
as resp to obtain the necessary data.
Here is basically the necessary function. A lot of it has blabla stuff to handle whatif cases.
import re
import numpy as np
import pandas as pd
from datetime import datetime
import warnings
from owslib.util import ServiceException
from owslib.wms import WebMapService
warnings.filterwarnings("ignore")
import aiohttp
import asyncio
async def request(layer: str, time: datetime.date, coor: list) -> list:
info = []
pixel_value = []
# Create an aiohttp client session for making HTTP requests
async with aiohttp.ClientSession() as session:
# Iterate through each timestep
for timestep in time:
# Get the request URL using the OWSLib library
url = wms.getfeatureinfo(layers=[layer],
srs='EPSG:4326',
bbox=tuple(coor),
size=(100, 100),
format='image/jpeg',
query_layers=[layer],
info_format='text/plain',
xy=(1, 1),
feature_count=1,
time=str(timestep.isoformat()) + 'Z'
).geturl()
try:
# Make an asynchronous GET request to the URL
async with session.get(url) as resp:
# Check if the response status is OK (200)
if resp.status == 200:
# Read the text content of the response
text = await resp.text()
# Extract the value using a regex pattern
pixel_value.append(str(re.findall(r'value_0\s+\d*.*\d+', text)))
try:
# Convert the extracted value to a float
pixel_value[-1] = float(
re.sub('value_0 = \'', '', pixel_value[-1])
.strip('[""]')
)
except ValueError:
# Handle any issues with data extraction and print a message
print(
f'Problem with the extract data (most likely empty output) at time = {timestep} and layer = {layer}')
print('Returning empty float instead')
pixel_value[-1] = [np.nan]
else:
# If the response status is not OK, print an error message
print(f'Request could not be made for some reason at time = {timestep} and layer = {layer}')
pixel_value.append(np.nan)
except ServiceException:
# Handle any ServiceException errors
print(f'Request could not be made for some reason at time = {timestep} and layer = {layer}')
pixel_value.append(np.nan)
return pixel_value
The following is a reproducible code, just in case.
Stations_info = pd.DataFrame({'ID': {0: 'COMPTN', 1: 'DUNHM'},
'Lat': {0: 45.2608, 1: 45.0959},
'Lon': {0: -71.8337, 1: -72.8126},
'Alt': {0: 245, 1: 225},
'Name': {0: 'Compton', 1: 'Dunham'},
'Lon2': {0: -71.7337, 1: -72.71260000000001},
'Lat2': {0: 45.1608, 1: 44.9959}})
address = "geo.weather.gc.ca/geomet?service=WMS" # for operational use # "http://collaboration.cmc.ec.gc.ca/rpn-wms" # for experimental use #
Version = "1.3.0"
HRDPS_varlist = ['HRDPS.CONTINENTAL_TT', 'HRDPS.CONTINENTAL_HR', "HRDPS.CONTINENTAL_PR"]
wms = WebMapService('https://geo.weather.gc.ca/geomet?SERVICE=WMS' +
'&REQUEST=GetCapabilities',
version=Version,
timeout=300)
async def process_request(arg):
info = pd.DataFrame(arg).T
print(f'Acquiring weather forecast for {arg.iloc[0]}')
coor = (info[['Lon', 'Lat2', 'Lon2', 'Lat']].iloc[0].tolist())
# Can add nb_timesteps to define how far we want to go. Must add as function argument
nb_timestep = 2
# HRDPS_df = await run_HRDPS(coor,nb_timestep)
datetime_str = '23-03-31 13:00:00' # Hardcoded for the sake of the example This should change to always be tomorrow.
time_utc = [datetime.strptime(datetime_str, '%y-%m-%d %H:%M:%S')]
time_local = time_utc.copy()
# Make async requests for each layer in GDPS_varlist and create a dictionary
pixel_value_dict_HRDPS = {
layer: await request(layer, time_utc[:nb_timestep], coor) for layer in HRDPS_varlist}
# Convert the dictionary to a pandas DataFrame
HRDPS_df = pd.DataFrame.from_dict(pixel_value_dict_HRDPS, orient='index').transpose()
HRDPS_df['Date'] = time_local[:nb_timestep]
HRDPS_df['HRDPS.ECONTINENTAL_PR'] = HRDPS_df['HRDPS.CONTINENTAL_PR'].diff()
print(HRDPS_df)
async def main():
tasks = [process_request(row) for _, row in Stations_info.iterrows()]
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())