@@ -24,11 +24,8 @@ Usage:
2424 get-observations
2525
2626Environment Variables:
27- SITE_ID: The four digit DataPoint site code identifying the weather station
28- we are to fetch results for.
29- API_KEY: The DataPoint API key, required for getting live weather data.
30- If un-specified then get-observations will fall back to archive data
31- from the workflow directory.
27+ SITE_ID: The four digit WMO (World Meteorological Organization)
28+ site code identifying the weather station we are to fetch results for.
3229
3330"""
3431
@@ -43,10 +40,6 @@ import requests
4340import util
4441
4542
46- BASE_URL = ('http://datapoint.metoffice.gov.uk/public/data/'
47- 'val/wxobs/all/json/{site_id}'
48- '?res=hourly&time={time}&key={api_key}' )
49-
5043# Compass bearings for ordinate directions.
5144# NOTE: We measure direction by where the winds have come from!
5245ORDINATES = {
@@ -68,22 +61,52 @@ ORDINATES = {
6861 'NNW' : '157.5'
6962}
7063
64+ class Meteorology :
65+ """
66+ Surface winds tend to be 20-30 degrees backed (vector anticlockwise)
67+ from the winds which steer weather systems:
68+ Friction with the ground surface tends to mean that land surface winds
69+ are as slow as half the speed of the wind at 2000ft. This fudge factor is
70+ a more conservative 1.5:
7171
72- class NoDataException (Exception ):
73- ...
72+ .. seealso::
73+
74+ [Source Book to the Forecaster's Reference Book](https://digital.nmla
75+ .metoffice.gov.uk/IO_011f7cd4-50fc-4903-b556-d24480ea883d/), section
76+ 1.2
77+ """
78+ SURFACE_BACKING = 2
79+ SURFACE_FRICTION = .66
80+ KT_TO_MPH = 1.15078
81+
82+ @staticmethod
83+ def process_direction (direction : str ) -> str :
84+ """Process raw wind direction:
85+
86+ * Convert direction from 10s of degrees to degrees.
87+ * Convert from Oceanographic (wind to) to Meteorological (wind from)
88+ convention.
89+ * Surface Friction Correction
90+ """
91+ return str (
92+ ((int (direction ) + 18 - Meteorology .SURFACE_BACKING ) % 36 ) * 10 )
93+
94+ @staticmethod
95+ def process_speed (speed : str ) -> str :
96+ """Process Raw wind speed
97+
98+ * Convert to KT to MPH
99+ * Surface Friction Correction
100+ """
101+ return str (
102+ (
103+ int (speed ) * Meteorology .KT_TO_MPH
104+ ) / Meteorology .SURFACE_FRICTION
105+ )
74106
75107
76- def get_datapoint_data (site_id , time , api_key ):
77- """Get weather data from the DataPoint service."""
78- # The URL required to get the data.
79- time = datetime .strptime (time , '%Y%m%dT%H%MZ' ).strftime ('%Y-%m-%dT%H:%MZ' )
80- url = BASE_URL .format (time = time , site_id = site_id , api_key = api_key )
81- req = requests .get (url )
82- if req .status_code != 200 :
83- raise Exception (f'{ url } returned exit code { req .status_code } ' )
84- # Get the data and parse it as JSON.
85- print ('Opening URL: %s' % url )
86- return req .json ()['SiteRep' ]['DV' ]['Location' ]
108+ class NoDataException (Exception ):
109+ ...
87110
88111
89112def get_archived_data (site_id , time ):
@@ -152,11 +175,12 @@ def synop_grab(site_id, time):
152175 raise NoDataException (
153176 f'Request for data failed, raw request was { req .text } ' )
154177
155- # Parse the direction from 10's of degrees to degrees:
156- data ['direction' ] = str (int (data ['direction' ]) * 10 )
178+ # * Parse the direction from 10's of degrees to degrees
179+ # * Convert direction from to direction it's blowing to
180+ data ['direction' ] = Meteorology .process_direction (data ['direction' ])
157181
158182 # Convert data in KT to MPH:
159- data ['speed' ] = str ( int ( data ['speed' ]) * 1.15078 )
183+ data ['speed' ] = Meteorology . process_speed ( data ['speed' ])
160184
161185 # Get lat and long from MO Data:
162186 lat , lon = reference_lat_long (site_id )
@@ -185,7 +209,7 @@ def get_nearby_site(site_id, badsites):
185209 return int (result [0 ]), dist
186210
187211
188- def main (site_id , api_key = None ):
212+ def main (site_id ):
189213 cycle_point = os .environ ['CYLC_TASK_CYCLE_POINT' ]
190214
191215 # Try to get the information from SYNOPS.
@@ -202,13 +226,8 @@ def main(site_id, api_key=None):
202226 site_id , dist = get_nearby_site (site_id , badsites )
203227
204228 if obs is None :
205- if api_key :
206- print ('Attempting to get weather data from DataPoint...' )
207- data = get_datapoint_data (site_id , cycle_point , api_key )
208- else :
209- print ('No API key provided, falling back to archived data' )
210- data = get_archived_data (site_id , cycle_point )
211-
229+ print ('Obs unavailable, falling back to archived data' )
230+ data = get_archived_data (site_id , cycle_point )
212231 obs = extract_observations (data )
213232
214233 # Write observations.
@@ -218,5 +237,4 @@ def main(site_id, api_key=None):
218237
219238if __name__ == '__main__' :
220239 util .sleep ()
221- main (os .environ ['SITE_ID' ],
222- os .environ .get ('API_KEY' ))
240+ main (os .environ ['SITE_ID' ])
0 commit comments