@@ -38,20 +38,20 @@ import math
3838import os
3939import shutil
4040
41+ import h5py
42+ from pathlib import Path
43+ import urllib
4144import requests
4245
43- try :
44- from PIL import Image
45- except ModuleNotFoundError :
46- # not all PIL installations are created equal
47- # sometimes we must import like this
48- import Image
4946from mercator import get_offset , get_scale , pos_to_coord
5047import util
5148
52-
53- URL = ('http://datapoint.metoffice.gov.uk/public/data/layer/wxobs/'
54- 'RADAR_UK_Composite_Highres/png?TIME={time}&key={api_key}' )
49+ S3URL = (
50+ 'https://met-office-radar-obs-data.s3-eu-west-2.amazonaws.com/radar/'
51+ '{Y}/{m}/{d}/{YYYYmmddHHMM}_ODIM_ng_radar_rainrate_composite_1km_UK.h5'
52+ )
53+ DEBUG = os .environ ['CYLC_DEBUG' ]== 'true'
54+ CYLC_TASK_LOG_DIR = os .environ ['CYLC_TASK_LOG_DIR' ]
5555
5656
5757class Rainfall (object ):
@@ -62,16 +62,6 @@ class Rainfall(object):
6262 resolution (float): The length of each grid cell in degrees.
6363
6464 """
65- VALUE_MAP = {
66- (0 , 0 , 254 , 255 ): 1 ,
67- (50 , 101 , 254 , 255 ): 2 ,
68- (127 , 127 , 0 , 255 ): 3 ,
69- (254 , 203 , 0 , 255 ): 4 ,
70- (254 , 152 , 0 , 255 ): 5 ,
71- (254 , 0 , 0 , 255 ): 6 ,
72- (254 , 0 , 254 , 255 ): 7
73- }
74-
7565 def __init__ (self , domain , resolution ):
7666 self .resolution = resolution
7767 self .domain = domain
@@ -98,10 +88,23 @@ class Rainfall(object):
9888 """
9989 itt_x , itt_y = util .get_grid_coordinates (lng , lat , self .domain ,
10090 self .resolution )
101- try :
102- self .data [itt_y ][itt_x ].append (self .VALUE_MAP [value ])
103- except KeyError :
104- pass
91+
92+ self .data [itt_y ][itt_x ].append (self .value_map (value ))
93+
94+ @staticmethod
95+ def value_map (v ):
96+ """Convert rainfall rate values into colour space values.
97+
98+ Checks if rainfall value above each threshold in turn.
99+
100+ TODO:
101+ - Unit test this
102+ """
103+ thresholds = {32 , 16 , 8 , 4 , 2 , 1 , .5 , .2 }
104+ for i , threshold in enumerate (sorted (thresholds , reverse = True )):
105+ if v > threshold :
106+ return 8 - i
107+ return 0
105108
106109 def compute_bins (self ):
107110 """Return this dataset as a 2D matrix."""
@@ -114,25 +117,6 @@ class Rainfall(object):
114117 return self .data
115118
116119
117- def get_datapoint_radar_image (filename , time , api_key ):
118- """Retrieve a png image of rainfall from the DataPoint service.
119-
120- Args:
121- filename (str): The path to write the image file to.
122- time (str): The datetime of the image to retrieve in ISO8601 format.
123- api_key (str): Datapoint API key.
124-
125- """
126- time = datetime .strptime (time , '%Y%m%dT%H%MZ' ).strftime (
127- '%Y-%m-%dT%H:%M:%SZ' )
128- url = URL .format (time = time , api_key = api_key )
129- req = requests .get (url )
130- if req .status_code != 200 :
131- raise Exception (f'{ url } returned exit code { req .status_code } ' )
132- with open (filename , 'bw' ) as png_file :
133- png_file .write (req .content )
134-
135-
136120def get_archived_radar_image (filename , time ):
137121 """Retrieve a png image from the archived data in the workflow directory.
138122
@@ -147,8 +131,23 @@ def get_archived_radar_image(filename, time):
147131 filename )
148132
149133
134+ def get_amazon_radar_data (filename , time ):
135+ time = datetime .strptime (time , '%Y%m%dT%H%MZ' )
136+ url = S3URL .format (
137+ Y = time .strftime ('%Y' ),
138+ m = time .strftime ('%m' ),
139+ d = time .strftime ('%d' ),
140+ YYYYmmddHHMM = time .strftime ('%Y%m%d%H%M' )
141+ )
142+ print (f'[INFO] Getting data from { url = } ' )
143+ data = urllib .request .urlopen (url ).read ()
144+ with open (filename , 'wb' ) as fh :
145+ fh .write (data )
146+
147+
148+ # def process_rainfall_data(filename, resolution, domain):
150149def process_rainfall_data (filename , resolution , domain ):
151- """Generate a 2D matrix of data from the rainfall data in the image.
150+ """get_amazon_radar_dataGenerate a 2D matrix of data from the rainfall data in the image.
152151
153152 Args:
154153 filename (str): Path to the png image to process.
@@ -160,37 +159,64 @@ def process_rainfall_data(filename, resolution, domain):
160159 list - A 2D matrix of rainfall data.
161160
162161 """
162+ print (f'{ util .INFO } Analysing data from { filename } ' )
163+ data = h5py .File (filename )['dataset1' ]['data1' ]['data' ]
163164 rainfall = Rainfall (domain , resolution )
164165
165- image = Image .open (filename )
166- scale = get_scale (domain , image .width )
166+ # image = Image.open(filename)
167+ height , width = data .shape
168+
169+ # TODO - work out why this isn't working
170+ scale = get_scale (domain , width )
167171 offset = get_offset (domain , scale )
172+ scale = (1672.2334443981335 , 3344.466888796267 )
173+ offset = (- 1100.8461538461539 , 1400.6953225710452 )
174+
175+ if DEBUG :
176+ from matplotlib import pyplot as plt
177+ Path ('CYLC_TASK_LOG_DIR' ).mkdir (parents = True , exist_ok = True )
178+ plt .imshow (data )
179+ plt .savefig (f'{ os .environ ['CYLC_TASK_LOG_DIR' ]} /raw.data.png' )
168180
169- for itt_x in range (image . width ):
170- for itt_y in range (image . height ):
181+ for itt_x in range (width ):
182+ for itt_y in range (height ):
171183 lng , lat = pos_to_coord (
172184 itt_x ,
173185 itt_y * (2. / 3. ), # Counter aspect ratio.
174- offset , scale )
175- rainfall .add (lng , lat , image .getpixel ((itt_x , itt_y )))
186+ offset ,
187+ scale
188+ )
189+ val = float (data [height - 1 - itt_y ][itt_x ])
190+ val = 0 if val == - 1 else val
191+ rainfall .add (lng , lat , val )
192+ data = rainfall .compute_bins ()
176193
177- return rainfall .compute_bins ()
194+ if DEBUG :
195+ plt .imshow (data )
196+ plt .savefig (f'{ CYLC_TASK_LOG_DIR } /processed.data.png' )
178197
198+ return data
179199
180200def main ():
181201 time = os .environ ['CYLC_TASK_CYCLE_POINT' ]
182202 resolution = float (os .environ ['RESOLUTION' ])
183203 domain = util .parse_domain (os .environ ['DOMAIN' ])
184- api_key = os .environ .get ('API_KEY' )
185204
186- if api_key :
187- print ('Attempting to get weather data from the DataPoint service.' )
188- get_datapoint_radar_image ('rainfall-radar.png' , time , api_key )
205+ # Acts as a switch - if a file-name is given, use that file-name
206+ # TODO - keep the nice S3 formatting with implied metadata
207+ canned_data = os .environ .get ('CANNED_DATA' , 'fetch' )
208+
209+ if canned_data == 'fetch' :
210+ print (f'{ util .INFO } Attempting to get rainfall data from S3 bucket' )
211+ get_amazon_radar_data ('radardata.h5' , time )
212+ canned_data = 'radardata.h5'
189213 else :
190- print ('No API key provided, falling back to archived data' )
191- get_archived_radar_image ('rainfall-radar.png' , time )
214+ print (
215+ f'{ util .INFO } Canned data provided: { canned_data } ' )
216+ # get_archived_radar_data(canned_data, time)
217+
218+ data = process_rainfall_data (canned_data , resolution , domain )
192219
193- data = process_rainfall_data ('rainfall-radar.png' , resolution , domain )
194220 util .write_csv ('rainfall.csv' , data )
195221
196222
0 commit comments