This is an automated email from the ASF dual-hosted git repository.

nchung pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-sdap-ningesterpy.git


The following commit(s) were added to refs/heads/master by this push:
     new 9bad704  SDAP-227 Support i,j indexed lat/lon (#12)
9bad704 is described below

commit 9bad704a416af10a941099ba03e1e7d34e7a8c66
Author: Eamon Ford <[email protected]>
AuthorDate: Thu Mar 19 14:34:21 2020 -0700

    SDAP-227 Support i,j indexed lat/lon (#12)
    
    Co-authored-by: Eamon Ford <[email protected]>
---
 .gitignore                              |   3 ++
 conda-requirements.txt                  |   7 ++--
 requirements.txt                        |   2 +-
 sdap/processors/tilereadingprocessor.py |  67 ++++++++++++++++----------------
 tests/datafiles/OBP_2017_01.nc          | Bin 0 -> 2110135 bytes
 tests/tilereadingprocessor_test.py      |  31 +++++++++++++++
 6 files changed, 72 insertions(+), 38 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6942e83..3e11168 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,6 +120,9 @@ pip-selfcheck.json
 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 
 # User-specific stuff:
+
+.vscode 
+
 .idea/**/workspace.xml
 .idea/**/tasks.xml
 .idea/dictionaries
diff --git a/conda-requirements.txt b/conda-requirements.txt
index 76b02be..7048cfd 100644
--- a/conda-requirements.txt
+++ b/conda-requirements.txt
@@ -1,3 +1,4 @@
-scipy=0.18.1
-nco=4.7.1
-netcdf4=1.3.1
\ No newline at end of file
+scipy=1.3.2
+nco=4.9.1
+netcdf4=1.5.3
+xarray=0.15.0
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index a539381..3d54f7a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ werkzeug==0.14.1
 flask==1.0.2
 flask-accept==0.0.6
 nexusproto===1.0.0
-numpy==1.12.1
+numpy
 protobuf==3.2.0
 pytz==2017.2
 PyYAML==3.12
diff --git a/sdap/processors/tilereadingprocessor.py 
b/sdap/processors/tilereadingprocessor.py
index a483907..a94260c 100644
--- a/sdap/processors/tilereadingprocessor.py
+++ b/sdap/processors/tilereadingprocessor.py
@@ -16,15 +16,16 @@
 import datetime
 from collections import OrderedDict
 from contextlib import contextmanager
-from os import sep, path, remove
+from os import path, remove, sep
 from urllib.request import urlopen
 
-from nexusproto import DataTile_pb2 as nexusproto
 import numpy
-from netCDF4 import Dataset, num2date
-from nexusproto.serialization import to_shaped_array, to_metadata
+import xarray as xr
+from cftime import num2date
 from pytz import timezone
 
+from nexusproto import DataTile_pb2 as nexusproto
+from nexusproto.serialization import to_metadata, to_shaped_array
 from sdap.processors import NexusTileProcessor
 
 EPOCH = 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)
 
 
 def get_ordered_slices(ds, variable, dimension_to_slice):
-    dimensions_for_variable = [str(dimension) for dimension in 
ds[variable].dimensions]
+    dimensions_for_variable = [str(dimension) for dimension in 
ds[variable].dims]
     ordered_slices = OrderedDict()
     for dimension in dimensions_for_variable:
         ordered_slices[dimension] = dimension_to_slice[dimension]
@@ -132,37 +133,38 @@ class TileReadingProcessor(NexusTileProcessor):
 
 
 class GridReadingProcessor(TileReadingProcessor):
+    def __init__(self, variable_to_read, latitude, longitude, **kwargs):
+        super().__init__(variable_to_read, latitude, longitude, **kwargs)
+        self.x_dim = kwargs.get('x_dim', longitude)
+        self.y_dim = kwargs.get('y_dim', latitude)
+
     def read_data(self, tile_specifications, file_path, output_tile):
         # Time is optional for Grid data
         time = self.environ['TIME']
 
-        with Dataset(file_path) as ds:
+        with xr.decode_cf(xr.open_dataset(file_path, decode_cf=False), 
decode_times=False) as ds:
             for section_spec, dimtoslice in tile_specifications:
                 tile = nexusproto.GridTile()
 
-                tile.latitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.latitude][dimtoslice[self.latitude]], 
numpy.NaN)))
-
-                tile.longitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.longitude][dimtoslice[self.longitude]], 
numpy.NaN)))
-
+                
tile.latitude.CopyFrom(to_shaped_array(numpy.ma.filled(ds[self.latitude].data[dimtoslice[self.y_dim]],
 numpy.NaN)))
+                
tile.longitude.CopyFrom(to_shaped_array(numpy.ma.filled(ds[self.longitude].data[dimtoslice[self.x_dim]],
 numpy.NaN)))
                 # Before we read the data we need to make sure the dimensions 
are in the proper order so we don't have any
                 #  indexing issues
                 ordered_slices = get_ordered_slices(ds, self.variable_to_read, 
dimtoslice)
                 # Read data using the ordered slices, replacing masked values 
with NaN
-                data_array = 
numpy.ma.filled(ds[self.variable_to_read][tuple(ordered_slices.values())], 
numpy.NaN)
+                data_array = 
numpy.ma.filled(ds[self.variable_to_read].data[tuple(ordered_slices.values())], 
numpy.NaN)
 
                 tile.variable_data.CopyFrom(to_shaped_array(data_array))
 
                 if self.metadata is not None:
                     tile.meta_data.add().CopyFrom(
-                        to_metadata(self.metadata, 
ds[self.metadata][tuple(ordered_slices.values())]))
+                        to_metadata(self.metadata, 
ds[self.metadata].data[tuple(ordered_slices.values())]))
 
                 if time is not None:
                     timevar = ds[time]
                     # Note assumption is that index of time is start value in 
dimtoslice
-                    tile.time = 
to_seconds_from_epoch(timevar[dimtoslice[time].start],
-                                                      
timeunits=timevar.getncattr('units'),
+                    tile.time = 
to_seconds_from_epoch(timevar.data[dimtoslice[time].start],
+                                                      
timeunits=timevar.attrs['units'],
                                                       
timeoffset=self.time_offset)
 
                 output_tile.tile.grid_tile.CopyFrom(tile)
@@ -178,25 +180,22 @@ class SwathReadingProcessor(TileReadingProcessor):
         self.time = time
 
     def read_data(self, tile_specifications, file_path, output_tile):
-        with Dataset(file_path) as ds:
+        with xr.decode_cf(xr.open_dataset(file_path, decode_cf=False), 
decode_times=False) as ds:
             for section_spec, dimtoslice in tile_specifications:
                 tile = nexusproto.SwathTile()
                 # Time Lat Long Data and metadata should all be indexed by the 
same dimensions, order the incoming spec once using the data variable
                 ordered_slices = get_ordered_slices(ds, self.variable_to_read, 
dimtoslice)
-                tile.latitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.latitude][tuple(ordered_slices.values())],
 numpy.NaN)))
-
-                tile.longitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.longitude][tuple(ordered_slices.values())],
 numpy.NaN)))
+                
tile.latitude.CopyFrom(to_shaped_array(numpy.ma.filled(ds[self.latitude].data[tuple(ordered_slices.values())],
 numpy.NaN)))
+                
tile.longitude.CopyFrom(to_shaped_array(numpy.ma.filled(ds[self.longitude].data[tuple(ordered_slices.values())],
 numpy.NaN)))
 
                 timetile = ds[self.time][
-                    tuple([ordered_slices[time_dim] for time_dim in 
ds[self.time].dimensions])].astype(
+                    tuple([ordered_slices[time_dim] for time_dim in 
ds[self.time].dims])].astype(
                     'float64',
                     casting='same_kind',
                     copy=False)
-                timeunits = ds[self.time].getncattr('units')
+                timeunits = ds[self.time].attrs['units']
                 try:
-                    start_of_day_date = 
datetime.datetime.strptime(ds.getncattr(self.start_of_day),
+                    start_of_day_date = 
datetime.datetime.strptime(ds.attrs[self.start_of_day],
                                                                    
self.start_of_day_pattern)
                 except Exception:
                     start_of_day_date = None
@@ -208,12 +207,12 @@ class SwathReadingProcessor(TileReadingProcessor):
                 tile.time.CopyFrom(to_shaped_array(timetile))
 
                 # Read the data converting masked values to NaN
-                data_array = 
numpy.ma.filled(ds[self.variable_to_read][tuple(ordered_slices.values())], 
numpy.NaN)
+                data_array = 
numpy.ma.filled(ds[self.variable_to_read].data[tuple(ordered_slices.values())], 
numpy.NaN)
                 tile.variable_data.CopyFrom(to_shaped_array(data_array))
 
                 if self.metadata is not None:
                     tile.meta_data.add().CopyFrom(
-                        to_metadata(self.metadata, 
ds[self.metadata][tuple(ordered_slices.values())]))
+                        to_metadata(self.metadata, 
ds[self.metadata].data[tuple(ordered_slices.values())]))
 
                 output_tile.tile.swath_tile.CopyFrom(tile)
 
@@ -228,33 +227,33 @@ class TimeSeriesReadingProcessor(TileReadingProcessor):
         self.time = time
 
     def read_data(self, tile_specifications, file_path, output_tile):
-        with Dataset(file_path) as ds:
+        with xr.decode_cf(xr.open_dataset(file_path, decode_cf=False), 
decode_times=False) as ds:
             for section_spec, dimtoslice in tile_specifications:
                 tile = nexusproto.TimeSeriesTile()
 
                 instance_dimension = next(
-                    iter([dim for dim in ds[self.variable_to_read].dimensions 
if dim != self.time]))
+                    iter([dim for dim in ds[self.variable_to_read].dims if dim 
!= self.time]))
 
                 tile.latitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.latitude][dimtoslice[instance_dimension]],
 numpy.NaN)))
+                    
to_shaped_array(numpy.ma.filled(ds[self.latitude].data[dimtoslice[instance_dimension]],
 numpy.NaN)))
 
                 tile.longitude.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.longitude][dimtoslice[instance_dimension]],
 numpy.NaN)))
+                    
to_shaped_array(numpy.ma.filled(ds[self.longitude].data[dimtoslice[instance_dimension]],
 numpy.NaN)))
 
                 # Before we read the data we need to make sure the dimensions 
are in the proper order so we don't
                 # have any indexing issues
                 ordered_slices = get_ordered_slices(ds, self.variable_to_read, 
dimtoslice)
                 # Read data using the ordered slices, replacing masked values 
with NaN
-                data_array = 
numpy.ma.filled(ds[self.variable_to_read][tuple(ordered_slices.values())], 
numpy.NaN)
+                data_array = 
numpy.ma.filled(ds[self.variable_to_read].data[tuple(ordered_slices.values())], 
numpy.NaN)
 
                 tile.variable_data.CopyFrom(to_shaped_array(data_array))
 
                 if self.metadata is not None:
                     tile.meta_data.add().CopyFrom(
-                        to_metadata(self.metadata, 
ds[self.metadata][tuple(ordered_slices.values())]))
+                        to_metadata(self.metadata, 
ds[self.metadata].data[tuple(ordered_slices.values())]))
 
                 tile.time.CopyFrom(
-                    
to_shaped_array(numpy.ma.filled(ds[self.time][dimtoslice[self.time]], 
numpy.NaN)))
+                    
to_shaped_array(numpy.ma.filled(ds[self.time].data[dimtoslice[self.time]], 
numpy.NaN)))
 
                 output_tile.tile.time_series_tile.CopyFrom(tile)
 
diff --git a/tests/datafiles/OBP_2017_01.nc b/tests/datafiles/OBP_2017_01.nc
new file mode 100644
index 0000000..8c9b584
Binary files /dev/null and b/tests/datafiles/OBP_2017_01.nc differ
diff --git a/tests/tilereadingprocessor_test.py 
b/tests/tilereadingprocessor_test.py
index 927b436..6c33184 100644
--- a/tests/tilereadingprocessor_test.py
+++ b/tests/tilereadingprocessor_test.py
@@ -311,5 +311,36 @@ class TestReadWSWMData(unittest.TestCase):
                                places=3)
 
 
+class TestReadInterpEccoData(unittest.TestCase):
+    def setUp(self):
+        self.module = sdap.processors.GridReadingProcessor('OBP', 'latitude', 
'longitude', x_dim='i', y_dim='j', time='time')
+
+    def test_read_indexed_ecco(self):
+        test_file = path.join(path.dirname(__file__), 'datafiles', 
'OBP_2017_01.nc')
+
+        input_tile = nexusproto.NexusTile()
+        tile_summary = nexusproto.TileSummary()
+        tile_summary.granule = "file:%s" % test_file
+        tile_summary.section_spec = "time:0:1,j:0:10,i:0:10"
+        input_tile.summary.CopyFrom(tile_summary)
+
+        results = list(self.module.process(input_tile))
+
+        self.assertEqual(1, len(results))
+
+        for nexus_tile in results:
+            self.assertTrue(nexus_tile.HasField('tile'))
+            self.assertTrue(nexus_tile.tile.HasField('grid_tile'))
+
+            tile = nexus_tile.tile.grid_tile
+            self.assertEqual(10, len(from_shaped_array(tile.latitude)))
+            self.assertEqual(10, len(from_shaped_array(tile.longitude)))
+
+            the_data = 
np.ma.masked_invalid(from_shaped_array(tile.variable_data))
+            self.assertEqual((1, 10, 10), the_data.shape)
+            self.assertEqual(100, np.ma.count(the_data))
+            self.assertEqual(1484568000, tile.time)
+
+
 if __name__ == '__main__':
     unittest.main()

Reply via email to