Beispiel #1
0
    def _process_geometries_(self,itr,field,headers,value_keys,alias):
        '''
        :param sequence itr: Contains geometry dictionaries to process. If there
         are no geometries to process, this will be a sequence of one element with
         an empty dictionary.
        :param :class:`ocgis.interface.Field` field: The field object to use for
         operations.
        :param sequence headers: Sequence of strings to use as headers for the
         creation of the collection.
        :param sequence value_keys: Sequence of strings to use as headers for the
         keyed output functions.
        :param str alias: The request data alias currently being processed.
        :yields: :class:~`ocgis.SpatialCollection`
        '''
        ## loop over the iterator
        for gd in itr:
            ## always work with a new geometry dictionary
            gd = deepcopy(gd)
            ## CFRotatedPole takes special treatment. only do this if a subset
            ## geometry is available. this variable is needed to determine if 
            ## backtransforms are necessary.
            original_rotated_pole_crs = None
            if isinstance(field.spatial.crs,CFRotatedPole):
                ## only transform if there is a subset geometry
                if len(gd) > 0:
                    ## store row and column dimension metadata and names before
                    ## transforming as this information is lost w/out row and 
                    ## column dimensions on the transformations.
                    original_row_column_metadata = {'row':{'name':field.spatial.grid.row.name,
                                                           'meta':field.spatial.grid.row.meta},
                                                    'col':{'name':field.spatial.grid.col.name,
                                                           'meta':field.spatial.grid.col.meta}}
                    ## reset the geometries
                    field.spatial._geom = None
                    ## get the new grid dimension
                    field.spatial.grid = get_rotated_pole_spatial_grid_dimension(field.spatial.crs,field.spatial.grid)
                    ## update the CRS. copy the original CRS for possible later
                    ## transformation back to rotated pole.
                    original_rotated_pole_crs = deepcopy(field.spatial.crs)
                    field.spatial.crs = CFWGS84()
            
            ## initialize the collection object to store the subsetted data. if
            ## the output CRS differs from the field's CRS, adjust accordingly 
            ## when initializing.
            if self.ops.output_crs is not None and field.spatial.crs != self.ops.output_crs:
                collection_crs = self.ops.output_crs
            else:
                collection_crs = field.spatial.crs
                
            coll = SpatialCollection(crs=collection_crs,headers=headers,meta=gd.get('meta'),
                                     value_keys=value_keys)
            
            ## reference variables from the geometry dictionary
            geom = gd.get('geom')
            ## keep this around for the collection creation
            coll_geom = deepcopy(geom)
            crs = gd.get('crs')
            
            ## if there is a spatial abstraction, ensure it may be loaded.
            if self.ops.abstraction is not None:
                try:
                    getattr(field.spatial.geom,self.ops.abstraction)
                except ImproperPolygonBoundsError:
                    exc = ImproperPolygonBoundsError('A "polygon" spatial abstraction is not available without the presence of bounds.')
                    ocgis_lh(exc=exc,logger='subset')
                except Exception as e:
                    ocgis_lh(exc=e,logger='subset')
                    
            ## if there is a snippet, return the first realization, time, and level
            if self.ops.snippet:
                field = field[0,0,0,:,:]
            ## if there is a slice, use it to subset the field.
            elif self.ops.slice is not None:
                field = field.__getitem__(self.ops.slice)

            ## see if the selection crs matches the field's crs
            if crs is not None and crs != field.spatial.crs:
                geom = project_shapely_geometry(geom,crs.sr,field.spatial.crs.sr)
                crs = field.spatial.crs
            ## if the geometry is a point, we need to buffer it...
            if type(geom) in [Point,MultiPoint]:
                ocgis_lh(logger=self._subset_log,msg='buffering point geometry',level=logging.DEBUG)
                geom = geom.buffer(self.ops.search_radius_mult*field.spatial.grid.resolution)
                ## update the geometry to store in the collection
                coll_geom = deepcopy(geom)
            
            ## get the ugid following geometry manipulations
            if 'properties' in gd and 'UGID' in gd['properties']:
                ugid = gd['properties']['UGID']
            else:
                ugid = 1
                
            if geom is None:
                msg = 'No selection geometry. Returning all data. Assiging UGID as 1.'
            else:
                msg = 'Subsetting with selection geometry having UGID={0}'.format(ugid)
            ocgis_lh(msg=msg,logger=self._subset_log)
                
            ## check for unique ugids. this is an issue with point subsetting
            ## as the buffer radius changes by dataset.
            if ugid in self._ugid_unique_store and geom is not None:
                ## only update if the geometry is unique
                if not any([__.almost_equals(geom) for __ in self._geom_unique_store]):
                    prev_ugid = ugid
                    ugid = max(self._ugid_unique_store) + 1
                    self._ugid_unique_store.append(ugid)
                    msg = 'Updating UGID {0} to {1} to maintain uniqueness.'.format(prev_ugid,ugid)
                    ocgis_lh(msg,self._subset_log,level=logging.WARN,alias=alias,ugid=ugid)
                else:
                    self._geom_unique_store.append(geom)
            else:
                self._ugid_unique_store.append(ugid)
                self._geom_unique_store.append(geom)
                            
            ## try to update the properties
            try:
                gd['properties']['UGID'] = ugid
            except KeyError:
                if not isinstance(gd,dict):
                    raise
                
            ## unwrap the data if it is geographic and 360
            if geom is not None and crs == CFWGS84():
                if CFWGS84.get_is_360(field.spatial):
                    ocgis_lh('unwrapping selection geometry',self._subset_log,alias=alias,ugid=ugid,level=logging.DEBUG)
                    geom = Wrapper().unwrap(geom)
            ## perform the spatial operation
            if geom is not None:
                try:
                    if self.ops.spatial_operation == 'intersects':
                        sfield = field.get_intersects(geom, use_spatial_index=env.USE_SPATIAL_INDEX,
                                                      select_nearest=self.ops.select_nearest)
                    elif self.ops.spatial_operation == 'clip':
                        sfield = field.get_clip(geom, use_spatial_index=env.USE_SPATIAL_INDEX,
                                                select_nearest=self.ops.select_nearest)
                    else:
                        ocgis_lh(exc=NotImplementedError(self.ops.spatial_operation))
                except EmptySubsetError as e:
                    if self.ops.allow_empty:
                        ocgis_lh(alias=alias,ugid=ugid,msg='empty geometric operation but empty returns allowed',level=logging.WARN)
                        sfield = None
                    else:
                        msg = str(e) + ' This typically means the selection geometry falls outside the spatial domain of the target dataset.'
                        ocgis_lh(exc=ExtentError(message=msg),alias=alias,logger=self._subset_log)
            else:
                sfield = field
            
            ## if the base size is being requested, bypass the rest of the
            ## operations.
            if self._request_base_size_only == False:
                ## if empty returns are allowed, there be an empty field
                if sfield is not None:
                    ## aggregate if requested
                    if self.ops.aggregate:
                        ocgis_lh('executing spatial average',self._subset_log,alias=alias,ugid=ugid)
                        sfield = sfield.get_spatially_aggregated(new_spatial_uid=ugid)
                    
                    ## wrap the returned data.
                    if not env.OPTIMIZE_FOR_CALC:
                        if CFWGS84.get_is_360(sfield.spatial):
                            if self.ops.output_format != 'nc' and self.ops.vector_wrap:
                                ocgis_lh('wrapping output geometries',self._subset_log,alias=alias,ugid=ugid,
                                         level=logging.DEBUG)
                                ## modifying these values in place will change the values
                                ## in the base field. a copy is necessary.
                                sfield.spatial = deepcopy(sfield.spatial)
                                sfield.spatial.crs.wrap(sfield.spatial)
                                
                    ## check for all masked values
                    if env.OPTIMIZE_FOR_CALC is False and self.ops.file_only is False:
                        for variable in sfield.variables.itervalues():
                            ocgis_lh(msg='Fetching data for variable with alias "{0}".'.format(variable.alias),
                                     logger=self._subset_log)
                            if variable.value.mask.all():
                                ## masked data may be okay depending on other opeartional
                                ## conditions.
                                if self.ops.snippet or self.ops.allow_empty or (self.ops.output_format == 'numpy' and self.ops.allow_empty):
                                    if self.ops.snippet:
                                        ocgis_lh('all masked data encountered but allowed for snippet',
                                                 self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                                    if self.ops.allow_empty:
                                        ocgis_lh('all masked data encountered but empty returns allowed',
                                                 self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                                    if self.ops.output_format == 'numpy':
                                        ocgis_lh('all masked data encountered but numpy data being returned allowed',
                                                 logger=self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                                else:
                                    ## if the geometry is also masked, it is an empty spatial
                                    ## operation.
                                    if sfield.spatial.abstraction_geometry.value.mask.all():
                                        ocgis_lh(exc=EmptyData,logger=self._subset_log)
                                    ## if none of the other conditions are met, raise the masked data error
                                    else:
                                        ocgis_lh(logger=self._subset_log,exc=MaskedDataError(),alias=alias,ugid=ugid)
                    
                    ## transform back to rotated pole if necessary
                    if original_rotated_pole_crs is not None:
                        if self.ops.output_crs is None and not isinstance(self.ops.output_crs,CFWGS84):
                            # copy the spatial mask to the new spatial array
                            spatial_mask_before_transform = deepcopy(sfield.spatial.get_mask())
                            # need to load the values before proceeding. source indices will disappear.
                            for variable in sfield.variables.itervalues():
                                variable.value
                            # reset the geometries
                            sfield.spatial._geom = None
                            sfield.spatial.grid = get_rotated_pole_spatial_grid_dimension(
                             original_rotated_pole_crs,sfield.spatial.grid,inverse=True,
                             rc_original=original_row_column_metadata)
                            # update the grid mask with the previous spatial mask
                            sfield.spatial.grid.value.mask = spatial_mask_before_transform
                            ## update the uid mask to match the spatial mask
                            sfield.spatial.uid = np.ma.array(sfield.spatial.uid,mask=spatial_mask_before_transform)
                            sfield.spatial.crs = original_rotated_pole_crs

                    ## update the coordinate system of the data output
                    if self.ops.output_crs is not None:
                        ## if the geometry is not None, it may need to be projected to match
                        ## the output crs.
                        if geom is not None and crs != self.ops.output_crs:
                            geom = project_shapely_geometry(geom,crs.sr,self.ops.output_crs.sr)
                            coll_geom = deepcopy(geom)
                        ## update the coordinate reference system of the spatial
                        ## dimension.
                        try:
                            sfield.spatial.update_crs(self.ops.output_crs)
                        ## this is likely a rotated pole origin
                        except RuntimeError as e:
                            if isinstance(sfield.spatial.crs,CFRotatedPole):
                                assert(isinstance(self.ops.output_crs,WGS84))
                                sfield.spatial._geom = None
                                sfield.spatial.grid = get_rotated_pole_spatial_grid_dimension(
                                 sfield.spatial.crs,sfield.spatial.grid)
                                sfield.spatial.crs = self.ops.output_crs
                            else:
                                ocgis_lh(exc=e,logger=self._subset_log)
                
            ## the geometry may need to be wrapped or unwrapped depending on
            ## the vector wrap situation
            name = alias if sfield is None else None
            coll.add_field(ugid, coll_geom, sfield, properties=gd.get('properties'), name=name)

            yield(coll)
Beispiel #2
0
 def _process_geometries_(self,rds):
     ocgis_lh(msg='entering _process_geometries_',logger=self._subset_log,level=logging.DEBUG)
     
     ## select headers
     if self.ops.headers is not None:
         headers = self.ops.headers
     else:
         if self.cengine is not None:
             if self.cengine._check_calculation_members_(self.cengine.funcs,AbstractMultivariateFunction):
                 headers = constants.multi_headers
             else:
                 headers = constants.calc_headers
         else:
             headers = constants.raw_headers
             
     ## keyed output functions require appending headers regardless. there is
     ## only one keyed output function allowed in a request.
     if self.cengine is not None:
         if self.cengine._check_calculation_members_(self.cengine.funcs,AbstractKeyedOutputFunction):
             value_keys = self.cengine.funcs[0]['ref'].structure_dtype['names']
             headers = list(headers) + value_keys
             ## remove the 'value' attribute headers as this is replaced by the
             ## keyed output names.
             try:
                 headers.remove('value')
             ## it may not be in the list because of a user overload
             except ValueError:
                 pass
         else:
             value_keys = None
     else:
         value_keys = None
                 
     alias = '_'.join([r.alias for r in rds])
     ocgis_lh('processing...',self._subset_log,alias=alias)
     ## return the field object
     try:
         field = [rd.get(format_time=self.ops.format_time) for rd in rds]
         if len(field) > 1:
             field[0].variables.add_variable(field[1].variables.first())
         field = field[0]
     except EmptySubsetError as e:
         if self.ops.allow_empty:
             ocgis_lh(msg='time or level subset empty but empty returns allowed',
                      logger=self._subset_log,level=logging.WARN)
             coll = SpatialCollection(headers=headers)
             coll.add_field(1,None,rd.alias,None)
             try:
                 yield(coll)
             finally:
                 return
         else:
             ocgis_lh(exc=ExtentError(message=str(e)),alias=rd.alias,logger=self._subset_log)
             
     ## set iterator based on presence of slice. slice always overrides geometry.
     if self.ops.slice is not None:
         itr = [{}]
     else:
         itr = [{}] if self.ops.geom is None else self.ops.geom
             
     ## loop over the iterator
     for gd in itr:
         ## initialize the collection object to store the subsetted data. if
         ## the output CRS differs from the field's CRS, adjust accordingly 
         ## when initilizing.
         if self.ops.output_crs is not None and field.spatial.crs != self.ops.output_crs:
             collection_crs = self.ops.output_crs
         else:
             collection_crs = field.spatial.crs
             
         coll = SpatialCollection(crs=collection_crs,headers=headers,meta=gd.get('meta'),
                                  value_keys=value_keys)
         
         ## reference variables from the geometry dictionary
         geom = gd.get('geom')
         
         crs = gd.get('crs')
         
         if 'properties' in gd and 'UGID' in gd['properties']:
             ugid = gd['properties']['UGID']
         else:
             ## try to get lowercase ugid in case the shapefile is not perfectly
             ## formed. however, if there is no geometry accept the error and
             ## use the default geometry identifier.
             if len(gd) == 0:
                 ugid = 1
             else:
                 ugid = gd['properties']['ugid']
                 
         ocgis_lh('processing',self._subset_log,level=logging.DEBUG,alias=alias,ugid=ugid)
         
         ## if there is a spatial abstraction, ensure it may be loaded.
         if self.ops.abstraction is not None:
             try:
                 getattr(field.spatial.geom,self.ops.abstraction)
             except ImproperPolygonBoundsError:
                 exc = ImproperPolygonBoundsError('A "polygon" spatial abstraction is not available without the presence of bounds.')
                 ocgis_lh(exc=exc,logger='subset')
             except Exception as e:
                 ocgis_lh(exc=e,logger='subset')
                 
         ## if there is a snippet, return the first realization, time, and level
         if self.ops.snippet:
             field = field[0,0,0,:,:]
         ## if there is a slice, use it to subset the field.
         elif self.ops.slice is not None:
             field = field.__getitem__(self.ops.slice)
             
         ## see if the selection crs matches the field's crs
         if crs is not None and crs != field.spatial.crs:
             geom = project_shapely_geometry(geom,crs.sr,field.spatial.crs.sr)
             crs = field.spatial.crs
         ## if the geometry is a point, we need to buffer it...
         if type(geom) in [Point,MultiPoint]:
             ocgis_lh(logger=self._subset_log,msg='buffering point geometry',level=logging.DEBUG)
             geom = geom.buffer(self.ops.search_radius_mult*field.spatial.grid.resolution)
         ## unwrap the data if it is geographic and 360
         if geom is not None and crs == CFWGS84():
             if CFWGS84.get_is_360(field.spatial):
                 ocgis_lh('unwrapping selection geometry',self._subset_log,alias=alias,ugid=ugid)
                 geom = Wrapper().unwrap(geom)
         ## perform the spatial operation
         if geom is not None:
             try:
                 if self.ops.spatial_operation == 'intersects':
                     sfield = field.get_intersects(geom)
                 elif self.ops.spatial_operation == 'clip':
                     sfield = field.get_clip(geom)
                 else:
                     ocgis_lh(exc=NotImplementedError(self.ops.spatial_operation))
             except EmptySubsetError as e:
                 if self.ops.allow_empty:
                     ocgis_lh(alias=alias,ugid=ugid,msg='empty geometric operation but empty returns allowed',level=logging.WARN)
                     sfield = None
                 else:
                     ocgis_lh(exc=ExtentError(message=str(e)),alias=alias,logger=self._subset_log)
         else:
             sfield = field
         
         ## if empty returns are allowed, there be an empty field
         if sfield is not None:
             ## aggregate if requested
             if self.ops.aggregate:
                 sfield = sfield.get_spatially_aggregated(new_spatial_uid=ugid)
             
             ## wrap the returned data.
             if not env.OPTIMIZE_FOR_CALC:
                 if CFWGS84.get_is_360(sfield.spatial):
                     if self.ops.output_format != 'nc' and self.ops.vector_wrap:
                         ocgis_lh('wrapping output geometries',self._subset_log,alias=alias,ugid=ugid)
                         sfield.spatial.crs.wrap(sfield.spatial)
                         
             ## check for all masked values
             if env.OPTIMIZE_FOR_CALC is False and self.ops.file_only is False:
                 for variable in sfield.variables.itervalues():
                     if variable.value.mask.all():
                         ## masked data may be okay depending on other opeartional
                         ## conditions.
                         if self.ops.snippet or self.ops.allow_empty or (self.ops.output_format == 'numpy' and self.ops.allow_empty):
                             if self.ops.snippet:
                                 ocgis_lh('all masked data encountered but allowed for snippet',
                                          self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                             if self.ops.allow_empty:
                                 ocgis_lh('all masked data encountered but empty returns allowed',
                                          self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                             if self.ops.output_format == 'numpy':
                                 ocgis_lh('all masked data encountered but numpy data being returned allowed',
                                          logger=self._subset_log,alias=alias,ugid=ugid,level=logging.WARN)
                         else:
                             ## if the geometry is also masked, it is an empty spatial
                             ## operation.
                             if sfield.spatial.abstraction_geometry.value.mask.all():
                                 ocgis_lh(exc=EmptyData,logger=self._subset_log)
                             ## if none of the other conditions are met, raise the masked data error
                             else:
                                 ocgis_lh(logger=self._subset_log,exc=MaskedDataError(),alias=alias,ugid=ugid)
         
         ## update the coordinate system of the data output
         if self.ops.output_crs is not None:
             ## if the geometry is not None, it may need to be projected to match
             ## the output crs.
             if geom is not None and crs != self.ops.output_crs:
                 geom = project_shapely_geometry(geom,crs.sr,self.ops.output_crs.sr)
                 
             sfield.spatial.update_crs(self.ops.output_crs)
         
         ## update the spatial abstraction to match the operations value. sfield
         ## will be none if the operation returns empty and it is allowed to have
         ## empty returns.
         if sfield is not None:
             sfield.spatial.abstraction = self.ops.abstraction
         
         coll.add_field(ugid,geom,alias,sfield,properties=gd.get('properties'))
         
         yield(coll)
Beispiel #3
0
    def write(self):
        SEED = 1  #: random number seeding for missing data
        RES = 1  #: resolution of the grid
        ORIGIN = Point(-105, 40)  #: center coordinate of upper left cell
        from_sr = CoordinateReferenceSystem(epsg=4326).sr
        to_sr = CoordinateReferenceSystem(epsg=2163).sr
        ORIGIN = project_shapely_geometry(ORIGIN, from_sr, to_sr)
        DIM = [4, 4]  #: number of cells [dimx,dimy]
        VAR = 'foo'  #: name of the data variable
        ## any relevant variables for the time construction
        TIME = {'origin': datetime.datetime(2000, 3, 1, 12, 0, 0),
                'end': datetime.datetime(2000, 4, 30, 12, 0, 0),
                'calendar': 'proleptic_gregorian',
                'units': 'days since 2000-01-01 00:00:00',
                'name': 'time'}
        ## any relevant variables for the spatial construction
        SPACE = {'row_bnds': 'bounds_latitude',
                 'col_bnds': 'bounds_longitude'}
        ## any relevant variables for the level construction
        LEVEL = {'name': 'level',
                 'n': 2}
        ## variables for masked data
        MASK = {'n': 0,
                'value': float(1e20)}

        ## MANUAL VALUE SETTING ########################################################

        d1l1 = [[1, 1, 2, 2],
                [1, 1, 2, 2],
                [3, 3, 4, 4],
                [3, 3, 4, 4]]
        VAL = np.array(d1l1).reshape(1, 1, 4, 4)
        VAL_MAN = None

        ################################################################################

        ## GENERATE BASE ARRAYS ##

        ## make time vector
        delta = datetime.timedelta(1)
        start = TIME['origin']
        timevec = []
        while start <= TIME['end']:
            timevec.append(start)
            start += delta
        timevec = np.array(timevec)
        ## make vector time bounds
        timevec_bnds = np.empty((len(timevec), 2), dtype=object)
        delta = datetime.timedelta(hours=12)
        for idx, tv in iter_array(timevec, return_value=True):
            timevec_bnds[idx, 0] = tv - delta
            timevec_bnds[idx, 1] = tv + delta

        ## make the level vector
        levelvec = np.array([50, 150])
        levelvec_bounds = np.array([[0, 100], [100, 200]])

        ## make centroids
        col_coords = -np.arange(abs(ORIGIN.x) - RES * (DIM[0] - 1), abs(ORIGIN.x) + RES, RES)
        col_coords = col_coords[::-1]
        row_coords = np.arange(ORIGIN.y - RES * (DIM[1] - 1), ORIGIN.y + RES, RES)
        # row_coords = row_coords[::-1]
        # col,row = np.meshgrid(col_coords,row_coords)

        ## create bounds arrays
        col_bnds = self.make_bounds(col_coords, RES)
        row_bnds = self.make_bounds(row_coords, RES)

        ## make value array
        if VAL is not None:
            val = np.ones((len(timevec), LEVEL['n'], DIM[0], DIM[1]), dtype=float) * VAL
        else:
            val = VAL_MAN
        ## set up the mask if requested
        mask = np.zeros(val.shape, dtype=bool)
        np.random.seed(SEED)
        # set the random masked cells to None
        for ii in range(0, MASK['n']):
            rx = np.random.randint(0, DIM[0])
            ry = np.random.randint(0, DIM[1])
            mask[:, :, rx, ry] = True
        val = np.ma.array(val, dtype=float, mask=mask, fill_value=MASK['value'])

        ## WRITE THE NC FILE ###########################################################

        ## initialize the output file
        rootgrp = nc.Dataset(os.path.join(self.outdir, self.filename), 'w', format='NETCDF4')
        ## create the dimensions
        level = rootgrp.createDimension(LEVEL['name'], size=LEVEL['n'])
        time = rootgrp.createDimension(TIME['name'], size=len(timevec))
        lat = rootgrp.createDimension('lat', size=len(row_coords))
        lon = rootgrp.createDimension('lon', size=len(col_coords))
        bound = rootgrp.createDimension('bound', size=2)
        ## create the variables
        times = rootgrp.createVariable(TIME['name'], 'f8', ('time',))
        times.axis = 'T'
        bounds_times = rootgrp.createVariable('time_bnds', 'f8', ('time', 'bound'))
        levels = rootgrp.createVariable(LEVEL['name'], 'i4', ('level',))
        levels.axis = 'Z'
        bounds_levels = rootgrp.createVariable('level_bnds', 'i4', ('level', 'bound'))
        cols = rootgrp.createVariable('longitude', 'f8', ('lon',))
        cols.axis = 'X'
        cols.standard_name = 'projection_x_coordinate'
        rows = rootgrp.createVariable('latitude', 'f8', ('lat',))
        rows.axis = 'Y'
        rows.standard_name = 'projection_y_coordinate'
        bounds_col = rootgrp.createVariable(SPACE['col_bnds'], 'f8', ('lon', 'bound'))
        bounds_row = rootgrp.createVariable(SPACE['row_bnds'], 'f8', ('lat', 'bound'))
        value = rootgrp.createVariable(VAR, 'f8', ('time', 'level', 'lat', 'lon'), fill_value=1e20)
        ## fill variables
        times.units = TIME['units']
        times.calendar = TIME['calendar']
        times[:] = nc.date2num(timevec, units=times.units, calendar=times.calendar)
        bounds_times[:] = nc.date2num(timevec_bnds, units=times.units, calendar=times.calendar)
        levels[:] = levelvec
        bounds_levels[:] = levelvec_bounds
        cols[:] = col_coords
        rows[:] = row_coords
        bounds_col[:, :] = col_bnds
        bounds_row[:, :] = row_bnds
        value[:, :, :, :] = val
        value.missing_value = MASK['value']
        value.standard_name = 'foo'
        value.long_name = 'foo_foo'
        value.units = 'huge'
        value.grid_mapping = 'crs'

        grid_mapping = rootgrp.createVariable('crs', 'c')
        grid_mapping.grid_mapping_name = "lambert_conformal_conic"
        grid_mapping.standard_parallel = [30., 60.]
        grid_mapping.longitude_of_central_meridian = -97.
        grid_mapping.latitude_of_projection_origin = 47.5
        grid_mapping.false_easting = 3325000.
        grid_mapping.false_northing = 2700000.

        # add bounds attributes
        times.bounds = bounds_times._name
        rows.bounds = bounds_row._name
        cols.bounds = bounds_col._name
        levels.bounds = bounds_levels._name

        rootgrp.close()
Beispiel #4
0
    def write(self):
        SEED = 1  #: random number seeding for missing data
        RES = 1  #: resolution of the grid
        ORIGIN = Point(-105, 40)  #: center coordinate of upper left cell
        from_sr = CoordinateReferenceSystem(epsg=4326).sr
        to_sr = CoordinateReferenceSystem(epsg=2163).sr
        ORIGIN = project_shapely_geometry(ORIGIN, from_sr, to_sr)
        DIM = [4, 4]  #: number of cells [dimx,dimy]
        VAR = 'foo'  #: name of the data variable
        ## any relevant variables for the time construction
        TIME = {'origin': datetime.datetime(2000, 3, 1, 12, 0, 0),
                'end': datetime.datetime(2000, 4, 30, 12, 0, 0),
                'calendar': 'proleptic_gregorian',
                'units': 'days since 2000-01-01 00:00:00',
                'name': 'time'}
        ## any relevant variables for the spatial construction
        SPACE = {'row_bnds': 'bounds_latitude',
                 'col_bnds': 'bounds_longitude'}
        ## any relevant variables for the level construction
        LEVEL = {'name': 'level',
                 'n': 2}
        ## variables for masked data
        MASK = {'n': 0,
                'value': float(1e20)}

        ## MANUAL VALUE SETTING ########################################################

        d1l1 = [[1, 1, 2, 2],
                [1, 1, 2, 2],
                [3, 3, 4, 4],
                [3, 3, 4, 4]]
        VAL = np.array(d1l1).reshape(1, 1, 4, 4)
        VAL_MAN = None

        ################################################################################

        ## GENERATE BASE ARRAYS ##

        ## make time vector
        delta = datetime.timedelta(1)
        start = TIME['origin']
        timevec = []
        while start <= TIME['end']:
            timevec.append(start)
            start += delta
        timevec = np.array(timevec)
        ## make vector time bounds
        timevec_bnds = np.empty((len(timevec), 2), dtype=object)
        delta = datetime.timedelta(hours=12)
        for idx, tv in iter_array(timevec, return_value=True):
            timevec_bnds[idx, 0] = tv - delta
            timevec_bnds[idx, 1] = tv + delta

        ## make the level vector
        levelvec = np.array([50, 150])
        levelvec_bounds = np.array([[0, 100], [100, 200]])

        ## make centroids
        col_coords = -np.arange(abs(ORIGIN.x) - RES * (DIM[0] - 1), abs(ORIGIN.x) + RES, RES)
        col_coords = col_coords[::-1]
        row_coords = np.arange(ORIGIN.y - RES * (DIM[1] - 1), ORIGIN.y + RES, RES)
        # row_coords = row_coords[::-1]
        # col,row = np.meshgrid(col_coords,row_coords)

        ## create bounds arrays
        col_bnds = self.make_bounds(col_coords, RES)
        row_bnds = self.make_bounds(row_coords, RES)

        ## make value array
        if VAL is not None:
            val = np.ones((len(timevec), LEVEL['n'], DIM[0], DIM[1]), dtype=float) * VAL
        else:
            val = VAL_MAN
        ## set up the mask if requested
        mask = np.zeros(val.shape, dtype=bool)
        np.random.seed(SEED)
        # set the random masked cells to None
        for ii in range(0, MASK['n']):
            rx = np.random.randint(0, DIM[0])
            ry = np.random.randint(0, DIM[1])
            mask[:, :, rx, ry] = True
        val = np.ma.array(val, dtype=float, mask=mask, fill_value=MASK['value'])

        ## WRITE THE NC FILE ###########################################################

        ## initialize the output file
        rootgrp = nc.Dataset(os.path.join(self.outdir, self.filename), 'w', format='NETCDF4')
        ## create the dimensions
        level = rootgrp.createDimension(LEVEL['name'], size=LEVEL['n'])
        time = rootgrp.createDimension(TIME['name'], size=len(timevec))
        lat = rootgrp.createDimension('lat', size=len(row_coords))
        lon = rootgrp.createDimension('lon', size=len(col_coords))
        bound = rootgrp.createDimension('bound', size=2)
        ## create the variables
        times = rootgrp.createVariable(TIME['name'], 'f8', ('time',))
        times.axis = 'T'
        bounds_times = rootgrp.createVariable('time_bnds', 'f8', ('time', 'bound'))
        levels = rootgrp.createVariable(LEVEL['name'], 'i4', ('level',))
        levels.axis = 'Z'
        bounds_levels = rootgrp.createVariable('level_bnds', 'i4', ('level', 'bound'))
        cols = rootgrp.createVariable('longitude', 'f8', ('lon',))
        cols.axis = 'X'
        cols.standard_name = 'projection_x_coordinate'
        rows = rootgrp.createVariable('latitude', 'f8', ('lat',))
        rows.axis = 'Y'
        rows.standard_name = 'projection_y_coordinate'
        bounds_col = rootgrp.createVariable(SPACE['col_bnds'], 'f8', ('lon', 'bound'))
        bounds_row = rootgrp.createVariable(SPACE['row_bnds'], 'f8', ('lat', 'bound'))
        value = rootgrp.createVariable(VAR, 'f8', ('time', 'level', 'lat', 'lon'), fill_value=1e20)
        ## fill variables
        times.units = TIME['units']
        times.calendar = TIME['calendar']
        times[:] = nc.date2num(timevec, units=times.units, calendar=times.calendar)
        bounds_times[:] = nc.date2num(timevec_bnds, units=times.units, calendar=times.calendar)
        levels[:] = levelvec
        bounds_levels[:] = levelvec_bounds
        cols[:] = col_coords
        rows[:] = row_coords
        bounds_col[:, :] = col_bnds
        bounds_row[:, :] = row_bnds
        value[:, :, :, :] = val
        value.missing_value = MASK['value']
        value.standard_name = 'foo'
        value.long_name = 'foo_foo'
        value.units = 'huge'
        value.grid_mapping = 'crs'

        grid_mapping = rootgrp.createVariable('crs', 'c')
        grid_mapping.grid_mapping_name = "lambert_conformal_conic"
        grid_mapping.standard_parallel = [30., 60.]
        grid_mapping.longitude_of_central_meridian = -97.
        grid_mapping.latitude_of_projection_origin = 47.5
        grid_mapping.false_easting = 3325000.
        grid_mapping.false_northing = 2700000.

        # add bounds attributes
        times.bounds = bounds_times._name
        rows.bounds = bounds_row._name
        cols.bounds = bounds_col._name
        levels.bounds = bounds_levels._name

        rootgrp.close()