1616import datetime
1717from collections import OrderedDict
1818from contextlib import contextmanager
19- from os import sep , path , remove
19+ from os import path , remove , sep
2020from urllib .request import urlopen
2121
22- from nexusproto import DataTile_pb2 as nexusproto
2322import numpy
24- from netCDF4 import Dataset , num2date
25- from nexusproto . serialization import to_shaped_array , to_metadata
23+ import xarray as xr
24+ from cftime import num2date
2625from pytz import timezone
2726
27+ from nexusproto import DataTile_pb2 as nexusproto
28+ from nexusproto .serialization import to_metadata , to_shaped_array
2829from sdap .processors import NexusTileProcessor
2930
3031EPOCH = timezone ('UTC' ).localize (datetime .datetime (1970 , 1 , 1 ))
@@ -91,7 +92,7 @@ def to_seconds_from_epoch(date, timeunits=None, start_day=None, timeoffset=None)
9192
9293
9394def get_ordered_slices (ds , variable , dimension_to_slice ):
94- dimensions_for_variable = [str (dimension ) for dimension in ds [variable ].dimensions ]
95+ dimensions_for_variable = [str (dimension ) for dimension in ds [variable ].dims ]
9596 ordered_slices = OrderedDict ()
9697 for dimension in dimensions_for_variable :
9798 ordered_slices [dimension ] = dimension_to_slice [dimension ]
@@ -132,37 +133,38 @@ def read_data(self, tile_specifications, file_path, output_tile):
132133
133134
134135class GridReadingProcessor (TileReadingProcessor ):
136+ def __init__ (self , variable_to_read , latitude , longitude , ** kwargs ):
137+ super ().__init__ (variable_to_read , latitude , longitude , ** kwargs )
138+ self .x_dim = kwargs .get ('x_dim' , longitude )
139+ self .y_dim = kwargs .get ('y_dim' , latitude )
140+
135141 def read_data (self , tile_specifications , file_path , output_tile ):
136142 # Time is optional for Grid data
137143 time = self .environ ['TIME' ]
138144
139- with Dataset ( file_path ) as ds :
145+ with xr . decode_cf ( xr . open_dataset ( file_path , decode_cf = False ), decode_times = False ) as ds :
140146 for section_spec , dimtoslice in tile_specifications :
141147 tile = nexusproto .GridTile ()
142148
143- tile .latitude .CopyFrom (
144- to_shaped_array (numpy .ma .filled (ds [self .latitude ][dimtoslice [self .latitude ]], numpy .NaN )))
145-
146- tile .longitude .CopyFrom (
147- to_shaped_array (numpy .ma .filled (ds [self .longitude ][dimtoslice [self .longitude ]], numpy .NaN )))
148-
149+ tile .latitude .CopyFrom (to_shaped_array (numpy .ma .filled (ds [self .latitude ].data [dimtoslice [self .y_dim ]], numpy .NaN )))
150+ tile .longitude .CopyFrom (to_shaped_array (numpy .ma .filled (ds [self .longitude ].data [dimtoslice [self .x_dim ]], numpy .NaN )))
149151 # Before we read the data we need to make sure the dimensions are in the proper order so we don't have any
150152 # indexing issues
151153 ordered_slices = get_ordered_slices (ds , self .variable_to_read , dimtoslice )
152154 # Read data using the ordered slices, replacing masked values with NaN
153- data_array = numpy .ma .filled (ds [self .variable_to_read ][tuple (ordered_slices .values ())], numpy .NaN )
155+ data_array = numpy .ma .filled (ds [self .variable_to_read ]. data [tuple (ordered_slices .values ())], numpy .NaN )
154156
155157 tile .variable_data .CopyFrom (to_shaped_array (data_array ))
156158
157159 if self .metadata is not None :
158160 tile .meta_data .add ().CopyFrom (
159- to_metadata (self .metadata , ds [self .metadata ][tuple (ordered_slices .values ())]))
161+ to_metadata (self .metadata , ds [self .metadata ]. data [tuple (ordered_slices .values ())]))
160162
161163 if time is not None :
162164 timevar = ds [time ]
163165 # Note assumption is that index of time is start value in dimtoslice
164- tile .time = to_seconds_from_epoch (timevar [dimtoslice [time ].start ],
165- timeunits = timevar .getncattr ( 'units' ) ,
166+ tile .time = to_seconds_from_epoch (timevar . data [dimtoslice [time ].start ],
167+ timeunits = timevar .attrs [ 'units' ] ,
166168 timeoffset = self .time_offset )
167169
168170 output_tile .tile .grid_tile .CopyFrom (tile )
@@ -178,25 +180,22 @@ def __init__(self, variable_to_read, latitude, longitude, time, **kwargs):
178180 self .time = time
179181
180182 def read_data (self , tile_specifications , file_path , output_tile ):
181- with Dataset ( file_path ) as ds :
183+ with xr . decode_cf ( xr . open_dataset ( file_path , decode_cf = False ), decode_times = False ) as ds :
182184 for section_spec , dimtoslice in tile_specifications :
183185 tile = nexusproto .SwathTile ()
184186 # Time Lat Long Data and metadata should all be indexed by the same dimensions, order the incoming spec once using the data variable
185187 ordered_slices = get_ordered_slices (ds , self .variable_to_read , dimtoslice )
186- tile .latitude .CopyFrom (
187- to_shaped_array (numpy .ma .filled (ds [self .latitude ][tuple (ordered_slices .values ())], numpy .NaN )))
188-
189- tile .longitude .CopyFrom (
190- to_shaped_array (numpy .ma .filled (ds [self .longitude ][tuple (ordered_slices .values ())], numpy .NaN )))
188+ tile .latitude .CopyFrom (to_shaped_array (numpy .ma .filled (ds [self .latitude ].data [tuple (ordered_slices .values ())], numpy .NaN )))
189+ tile .longitude .CopyFrom (to_shaped_array (numpy .ma .filled (ds [self .longitude ].data [tuple (ordered_slices .values ())], numpy .NaN )))
191190
192191 timetile = ds [self .time ][
193- tuple ([ordered_slices [time_dim ] for time_dim in ds [self .time ].dimensions ])].astype (
192+ tuple ([ordered_slices [time_dim ] for time_dim in ds [self .time ].dims ])].astype (
194193 'float64' ,
195194 casting = 'same_kind' ,
196195 copy = False )
197- timeunits = ds [self .time ].getncattr ( 'units' )
196+ timeunits = ds [self .time ].attrs [ 'units' ]
198197 try :
199- start_of_day_date = datetime .datetime .strptime (ds .getncattr ( self .start_of_day ) ,
198+ start_of_day_date = datetime .datetime .strptime (ds .attrs [ self .start_of_day ] ,
200199 self .start_of_day_pattern )
201200 except Exception :
202201 start_of_day_date = None
@@ -208,12 +207,12 @@ def read_data(self, tile_specifications, file_path, output_tile):
208207 tile .time .CopyFrom (to_shaped_array (timetile ))
209208
210209 # Read the data converting masked values to NaN
211- data_array = numpy .ma .filled (ds [self .variable_to_read ][tuple (ordered_slices .values ())], numpy .NaN )
210+ data_array = numpy .ma .filled (ds [self .variable_to_read ]. data [tuple (ordered_slices .values ())], numpy .NaN )
212211 tile .variable_data .CopyFrom (to_shaped_array (data_array ))
213212
214213 if self .metadata is not None :
215214 tile .meta_data .add ().CopyFrom (
216- to_metadata (self .metadata , ds [self .metadata ][tuple (ordered_slices .values ())]))
215+ to_metadata (self .metadata , ds [self .metadata ]. data [tuple (ordered_slices .values ())]))
217216
218217 output_tile .tile .swath_tile .CopyFrom (tile )
219218
@@ -228,33 +227,33 @@ def __init__(self, variable_to_read, latitude, longitude, time, **kwargs):
228227 self .time = time
229228
230229 def read_data (self , tile_specifications , file_path , output_tile ):
231- with Dataset ( file_path ) as ds :
230+ with xr . decode_cf ( xr . open_dataset ( file_path , decode_cf = False ), decode_times = False ) as ds :
232231 for section_spec , dimtoslice in tile_specifications :
233232 tile = nexusproto .TimeSeriesTile ()
234233
235234 instance_dimension = next (
236- iter ([dim for dim in ds [self .variable_to_read ].dimensions if dim != self .time ]))
235+ iter ([dim for dim in ds [self .variable_to_read ].dims if dim != self .time ]))
237236
238237 tile .latitude .CopyFrom (
239- to_shaped_array (numpy .ma .filled (ds [self .latitude ][dimtoslice [instance_dimension ]], numpy .NaN )))
238+ to_shaped_array (numpy .ma .filled (ds [self .latitude ]. data [dimtoslice [instance_dimension ]], numpy .NaN )))
240239
241240 tile .longitude .CopyFrom (
242- to_shaped_array (numpy .ma .filled (ds [self .longitude ][dimtoslice [instance_dimension ]], numpy .NaN )))
241+ to_shaped_array (numpy .ma .filled (ds [self .longitude ]. data [dimtoslice [instance_dimension ]], numpy .NaN )))
243242
244243 # Before we read the data we need to make sure the dimensions are in the proper order so we don't
245244 # have any indexing issues
246245 ordered_slices = get_ordered_slices (ds , self .variable_to_read , dimtoslice )
247246 # Read data using the ordered slices, replacing masked values with NaN
248- data_array = numpy .ma .filled (ds [self .variable_to_read ][tuple (ordered_slices .values ())], numpy .NaN )
247+ data_array = numpy .ma .filled (ds [self .variable_to_read ]. data [tuple (ordered_slices .values ())], numpy .NaN )
249248
250249 tile .variable_data .CopyFrom (to_shaped_array (data_array ))
251250
252251 if self .metadata is not None :
253252 tile .meta_data .add ().CopyFrom (
254- to_metadata (self .metadata , ds [self .metadata ][tuple (ordered_slices .values ())]))
253+ to_metadata (self .metadata , ds [self .metadata ]. data [tuple (ordered_slices .values ())]))
255254
256255 tile .time .CopyFrom (
257- to_shaped_array (numpy .ma .filled (ds [self .time ][dimtoslice [self .time ]], numpy .NaN )))
256+ to_shaped_array (numpy .ma .filled (ds [self .time ]. data [dimtoslice [self .time ]], numpy .NaN )))
258257
259258 output_tile .tile .time_series_tile .CopyFrom (tile )
260259
0 commit comments