Пример #1
0
 def test_calculation_iteration(self):
     field = self.get_field(with_value=True,month_count=2)
     field.variables.add_variable(Variable(value=field.variables['tmax'].value+5,
                                           name='tmin',alias='tmin'))
     field.temporal.name_uid = 'tid'
     field.level.name_uid = 'lid'
     field.spatial.geom.name_uid = 'gid'
     
     grouping = ['month']
     tgd = field.temporal.get_grouping(grouping)
     mu = Mean(field=field,tgd=tgd,alias='my_mean')
     ret = mu.execute()
     
     kwds = copy(field.__dict__)
     kwds.pop('_raw')
     kwds.pop('_variables')
     kwds['temporal'] = tgd
     kwds['variables'] = ret
     cfield = DerivedField(**kwds)
     cfield.temporal.name_uid = 'tid'
     cfield.temporal.name_value = 'time'
     cfield.spatial.name_uid = 'gid'
                     
     sc = ShpCabinet()
     meta = sc.get_meta('state_boundaries')
     sp = SpatialCollection(meta=meta,key='state_boundaries',headers=constants.calc_headers)
     for row in sc.iter_geoms('state_boundaries'):
         sp.add_field(row['properties']['UGID'],row['geom'],cfield.variables.keys()[0],
                      cfield,properties=row['properties'])
     for ii,row in enumerate(sp.get_iter_dict()):
         if ii == 0:
             self.assertEqual(row[0].bounds,(-100.5, 39.5, -99.5, 40.5))
             self.assertDictEqual(row[1],{'lid': 1, 'ugid': 1, 'vid': 1, 'cid': 1, 'did': 1, 'year': 2000, 'time': datetime.datetime(2000, 1, 16, 0, 0), 'calc_alias': 'my_mean_tmax', 'value': 0.44808476666433006, 'month': 1, 'alias': 'tmax', 'variable': 'tmax', 'gid': 1, 'calc_key': 'mean', 'tid': 1, 'level': 50, 'day': 16})
         self.assertEqual(len(row),2)
         self.assertEqual(len(row[1]),len(constants.calc_headers))
Пример #2
0
    def test_multivariate_iteration(self):
        field = self.get_field(with_value=True, month_count=1)
        field.variables.add_variable(Variable(value=field.variables['tmax'].value + 5,
                                              name='tmin', alias='tmin'))
        field.temporal.name_uid = 'tid'
        field.level.name_uid = 'lid'
        field.spatial.geom.name_uid = 'gid'

        div = Divide(field=field, parms={'arr1': 'tmin', 'arr2': 'tmax'}, alias='some_division',
                     dtype=np.float64)
        ret = div.execute()

        cfield = DerivedMultivariateField(variables=ret, realization=field.realization, temporal=field.temporal,
                                          level=field.level,
                                          spatial=field.spatial, meta=field.meta, uid=field.uid)
        cfield.spatial.name_uid = 'gid'

        sc = ShpCabinet()
        meta = sc.get_meta('state_boundaries')
        sp = SpatialCollection(meta=meta, key='state_boundaries', headers=constants.HEADERS_MULTI)
        for row in sc.iter_geoms('state_boundaries', as_spatial_dimension=True):
            sp.add_field(cfield, ugeom=row)

        for ii, row in enumerate(sp.get_iter_dict(melted=True)):
            if ii == 0:
                self.assertDictEqual(row[1], {'lid': 1, 'ugid': 1, 'cid': 1, 'did': None, 'year': 2000,
                                              'time': datetime.datetime(2000, 1, 1, 12, 0),
                                              'calc_alias': 'some_division', 'value': 12.989774984574424, 'month': 1,
                                              'gid': 1, 'calc_key': 'divide', 'tid': 1, 'level': 50, 'day': 1})
        self.assertEqual(ii + 1, 2 * 31 * 2 * 3 * 4 * 51)
Пример #3
0
 def get_collection(self):
     field = self.get_field(with_value=True)
     sc = ShpCabinet()
     meta = sc.get_meta('state_boundaries')
     sp = SpatialCollection(meta=meta, key='state_boundaries')
     for row in sc.iter_geoms('state_boundaries', as_spatial_dimension=True):
         sp.add_field(field, ugeom=row)
     return sp
Пример #4
0
 def write(self):
     build = True
     for coll in self:
         if build:
             ret = SpatialCollection(meta=coll.meta,key=coll.key,crs=coll.crs,headers=coll.headers)
             build = False
         for k,v in coll.iteritems():
             ret.add_field(k,coll.geoms[k],v.keys()[0],v.values()[0],properties=coll.properties[k])
     return(ret)
Пример #5
0
 def get_spatial_collection_two_geometries(self, pt=None):
     pt = pt or Point(1, 2)
     record1 = {'geom': pt, 'properties': {'ID': 4, 'NAME': 'hello'}}
     record2 = {'geom': pt, 'properties': {'ID': 5, 'NAME': 'another'}}
     sd1 = SpatialDimension.from_records([record1], uid='ID')
     sd2 = SpatialDimension.from_records([record2], uid='ID')
     sc = SpatialCollection()
     field = self.get_field()
     sc.add_field(field, ugeom=sd1)
     sc.add_field(field, ugeom=sd2)
     return sc
Пример #6
0
 def test_constructor(self):
     field = self.get_field(with_value=True)
     sc = ShpCabinet()
     meta = sc.get_meta('state_boundaries')
     sp = SpatialCollection(meta=meta,key='state_boundaries')
     for row in sc.iter_geoms('state_boundaries'):
         sp.add_field(row['properties']['UGID'],row['geom'],field.variables.keys()[0],
                      field,properties=row['properties'])
     self.assertEqual(len(sp),51)
     self.assertIsInstance(sp.geoms[25],MultiPolygon)
     self.assertIsInstance(sp.properties[25],dict)
     self.assertEqual(sp[25]['tmax'].variables['tmax'].value.shape,(2, 31, 2, 3, 4))
Пример #7
0
    def test_calculation_iteration_two_calculations(self):
        field = self.get_field(with_value=True, month_count=2)
        field.variables.add_variable(Variable(value=field.variables['tmax'].value + 5, name='tmin', alias='tmin'))
        field.temporal.name_uid = 'tid'
        field.level.name_uid = 'lid'
        field.spatial.geom.name_uid = 'gid'

        grouping = ['month']
        tgd = field.temporal.get_grouping(grouping)
        mu = Mean(field=field, tgd=tgd, alias='my_mean', dtype=np.float64, add_parents=True)
        ret = mu.execute()
        thresh = Threshold(field=field, vc=ret, tgd=tgd, alias='a_treshold', add_parents=True,
                           parms={'operation': 'gte', 'threshold': 0.5})
        ret = thresh.execute()

        kwds = copy(field.__dict__)
        kwds.pop('_raw')
        kwds.pop('_variables')
        kwds.pop('_should_regrid')
        kwds.pop('_has_assigned_coordinate_system')
        kwds.pop('_attrs')
        kwds['name'] = kwds.pop('_name')
        kwds['temporal'] = tgd
        kwds['variables'] = ret
        cfield = DerivedField(**kwds)
        cfield.temporal.name_uid = 'tid'
        cfield.temporal.name_value = 'time'
        cfield.spatial.name_uid = 'gid'

        sc = ShpCabinet()
        meta = sc.get_meta('state_boundaries')
        sp = SpatialCollection(meta=meta, key='state_boundaries', headers=constants.HEADERS_CALC)
        for row in sc.iter_geoms('state_boundaries', as_spatial_dimension=True):
            sp.add_field(cfield, ugeom=row)

        cids = set()
        for ii, row in enumerate(sp.get_iter_dict(melted=True)):
            cids.update([row[1]['cid']])
            if ii == 0:
                self.assertEqual(row[0].bounds, (-100.5, 39.5, -99.5, 40.5))
                self.assertDictEqual(row[1], {'lid': 1, 'ugid': 1, 'vid': 1, 'cid': 1, 'did': 1, 'year': 2000,
                                              'time': datetime.datetime(2000, 1, 16, 0, 0),
                                              'calc_alias': 'my_mean_tmax', 'value': 0.44808476666433006, 'month': 1,
                                              'alias': 'tmax', 'variable': 'tmax', 'gid': 1, 'calc_key': 'mean',
                                              'tid': 1, 'level': 50, 'day': 16})
            self.assertEqual(len(row), 2)
            self.assertEqual(len(row[1]), len(constants.HEADERS_CALC))
        self.assertEqual(ii + 1, 2 * 2 * 2 * 3 * 4 * 51 * 4)
        self.assertEqual(len(cids), 4)
Пример #8
0
    def write(self):
        build = True
        for coll in self:
            if build:
                ret = SpatialCollection(meta=coll.meta, key=coll.key, crs=coll.crs, headers=coll.headers)
                build = False
            for k, v in coll.iteritems():
                field = v.values()[0]
                if field is None:
                    name = v.keys()[0]
                else:
                    name = None
                ret.add_field(v.values()[0], ugeom=coll.ugeom[k], name=name)

        return ret
Пример #9
0
    def test_crs(self):
        sc = SpatialCollection()
        self.assertIsNone(sc.crs)
        field = self.get_field(crs=CFWGS84())
        sc.add_field(field)
        self.assertEqual(sc.crs, CFWGS84())
        self.assertIsNone(sc._crs)

        sc = SpatialCollection(crs=Spherical())
        self.assertEqual(sc.crs, Spherical())

        sc = SpatialCollection()
        field = self.get_field()
        field.spatial = None
        sc.add_field(field)
        self.assertIsNone(sc.crs)
Пример #10
0
Файл: base.py Проект: NCPP/ocgis
    def get_esmf_field(self, **kwargs):
        """
        :keyword field: (``=None``) The field object. If ``None``, call :meth:`~ocgis.test.base.TestBase.get_field`
        :type field: :class:`~ocgis.Field`
        :param kwargs: Other keyword arguments to :meth:`ocgis.test.base.TestBase.get_field`.
        :returns: An ESMF field object.
        :rtype: :class:`ESMF.Field`
        """

        from ocgis.conv.esmpy import ESMPyConverter

        field = kwargs.pop("field", None) or self.get_field(**kwargs)
        coll = SpatialCollection()
        coll.add_field(field)
        conv = ESMPyConverter([coll])
        efield = conv.write()
        return efield
Пример #11
0
 def test_write(self):
     records = [{'geom': Point(1, 2).buffer(1), 'properties': {'ID': 5, 'name': 'heaven'}},
                {'geom': Point(7, 8).buffer(1), 'properties': {'ID': 50, 'name': 'hell'}}]
     sdim1 = SpatialDimension.from_records([records[0]], uid='ID')
     sdim2 = SpatialDimension.from_records([records[1]], uid='ID')
     field = self.get_field(crs=WGS84())
     coll1 = SpatialCollection()
     coll1.add_field(field, ugeom=sdim1)
     coll2 = SpatialCollection()
     coll2.add_field(field, ugeom=sdim2)
     colls = [coll1, coll2]
     f = FakeAbstractCollectionConverter(colls, outdir=self.current_dir_output, prefix='me')
     ret = f.write()
     path = os.path.join(ret, 'shp', 'me_ugid.shp')
     with fiona.open(path, 'r') as source:
         records = list(source)
     self.assertEqual(len(records), 2)
     self.assertEqual([r['properties']['ID'] for r in records], [5, 50])
     self.assertEqual([r['properties']['name'] for r in records], ['heaven', 'hell'])
Пример #12
0
 def test_iteration(self):
     field = self.get_field(with_value=True)
     
     field.temporal.name_uid = 'tid'
     field.level.name_uid = 'lid'
     field.spatial.geom.name_uid = 'gid'
     field.spatial.name_uid = 'gid'
     
     sc = ShpCabinet()
     meta = sc.get_meta('state_boundaries')
     sp = SpatialCollection(meta=meta,key='state_boundaries')
     for row in sc.iter_geoms('state_boundaries'):
         sp.add_field(row['properties']['UGID'],row['geom'],field.variables.keys()[0],
                      field,properties=row['properties'])
     for ii,row in enumerate(sp.get_iter_dict()):
         if ii == 1:
             self.assertEqual(row[1],{'lid': 1, 'ugid': 1, 'vid': 1, 'alias': 'tmax', 'did': 1, 'year': 2000, 'value': 0.7203244934421581, 'month': 1, 'variable': 'tmax', 'gid': 2, 'time': datetime.datetime(2000, 1, 1, 12, 0), 'tid': 1, 'level': 50, 'day': 1})
         self.assertIsInstance(row[0],MultiPolygon)
         self.assertEqual(len(row),2)
         self.assertEqual(len(row[1]),len(constants.raw_headers))
Пример #13
0
    def test_iteration_methods(self):
        field = self.get_field(with_value=True)

        field.temporal.name_uid = 'tid'
        field.level.name_uid = 'lid'
        field.spatial.geom.name_uid = 'gid'
        field.spatial.name_uid = 'gid'

        sc = ShpCabinet()
        meta = sc.get_meta('state_boundaries')
        sp = SpatialCollection(meta=meta, key='state_boundaries', headers=constants.HEADERS_RAW)
        for row in sc.iter_geoms('state_boundaries', as_spatial_dimension=True):
            sp.add_field(field, ugeom=row)
        for ii, row in enumerate(sp.get_iter_dict(melted=True)):
            if ii == 1:
                self.assertDictEqual(row[1], {'lid': 1, 'ugid': 1, 'vid': 1, 'alias': 'tmax', 'did': 1, 'year': 2000,
                                              'value': 0.7203244934421581, 'month': 1, 'variable': 'tmax', 'gid': 2,
                                              'time': datetime.datetime(2000, 1, 1, 12, 0), 'tid': 1, 'level': 50,
                                              'day': 1})
            self.assertIsInstance(row[0], MultiPolygon)
            self.assertEqual(len(row), 2)
            self.assertEqual(len(row[1]), len(constants.HEADERS_RAW))
Пример #14
0
    def test_add_field(self):
        sc = SpatialCollection()
        field = self.get_field()
        sc.add_field(field)
        self.assertIsInstance(sc[1][field.name], Field)
        with self.assertRaises(ValueError):
            sc.add_field(field)
        field2 = deepcopy(field)
        field2.name = 'another'
        sc.add_field(field2)
        self.assertIsInstance(sc[1]['another'], Field)
        sc.add_field(field, name='hiding')
        self.assertIsInstance(sc[1]['hiding'], Field)
        self.assertIsNone(sc.ugeom[1])

        record = {'geom': Point(1, 2), 'properties': {'ID': 4, 'NAME': 'hello'}}
        sd = SpatialDimension.from_records([record], uid='ID')
        sc.add_field(field, ugeom=sd)
        self.assertEqual(sc.keys(), [1, 4])
        self.assertEqual(sc.ugeom[4].geom.point.value[0, 0], record['geom'])

        sc = SpatialCollection()
        sc.add_field(field)
        self.assertEqual(field.uid, 1)
        field.uid = 10
        sc = SpatialCollection()
        sc.add_field(field)
        self.assertEqual(field.uid, 10)
        field2 = deepcopy(field)
        field2.spatial.crs = Spherical()
        with self.assertRaises(ValueError):
            # coordinate systems must be same
            sc.add_field(field2, name='hover')

        sc = SpatialCollection()
        sc.add_field(None, name='food')
        self.assertIsNone(sc[1]['food'])
Пример #15
0
    def _process_subsettables_(self,rds):
        '''
        :param rds: Sequence of :class:~`ocgis.RequestDataset` objects.
        :type rds: sequence
        :yields: :class:~`ocgis.SpatialCollection`
        '''
        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._has_multivariate_calculations:
                    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.name for r in rds])

        ocgis_lh('processing...',self._subset_log,alias=alias,level=logging.DEBUG)
        ## return the field object
        try:
            ## look for field optimizations
            if self.ops.optimizations is not None and 'fields' in self.ops.optimizations:
                field = [self.ops.optimizations['fields'][rd.alias] for rd in rds]
            else:
                field = [rd.get(format_time=self.ops.format_time,
                                interpolate_spatial_bounds=self.ops.interpolate_spatial_bounds) for rd in rds]
            ## 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.
            for f in field:
                f.spatial.abstraction = self.ops.abstraction

            if len(field) > 1:
                try:
                    ## reset the variable uid and let the collection handle its assignment
                    variable_to_add = field[1].variables.first()
                    variable_to_add.uid = None
                    field[0].variables.add_variable(variable_to_add)
                    ## reset the field names and let these be auto-generated
                    for f in field:
                        f._name = None
                ## this will fail for optimizations as the fields are already joined
                except VariableInCollectionError:
                    if self.ops.optimizations is not None and 'fields' in self.ops.optimizations:
                        pass
                    else:
                        raise
            field = field[0]
        ## this error is related to subsetting by time or level. spatial subsetting
        ## occurs below.
        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, None, name='_'.join([rd.name for rd in rds]))
                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
        
        for coll in self._process_geometries_(itr,field,headers,value_keys,alias):
            yield(coll)
Пример #16
0
    def _process_subsettables_(self, rds):
        """
        :param rds: Sequence of :class:~`ocgis.RequestDataset` objects.
        :type rds: sequence
        :rtype: :class:`ocgis.api.collection.AbstractCollection`
        """

        ocgis_lh(msg='entering _process_geometries_', logger=self._subset_log, level=logging.DEBUG)

        # select headers and any value keys for keyed output functions
        value_keys = None
        if self.ops.headers is not None:
            headers = self.ops.headers
        else:
            if self.ops.melted:
                if self.cengine is not None:
                    if self._has_multivariate_calculations:
                        headers = constants.HEADERS_MULTI
                    else:
                        headers = constants.HEADERS_CALC
                else:
                    headers = constants.HEADERS_RAW
            else:
                headers = None

        # keyed output functions require appending headers regardless. there is only one keyed output function
        # allowed in a request.
        if headers is not None:
            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

        alias = '_'.join([r.name for r in rds])

        ocgis_lh('processing...', self._subset_log, alias=alias, level=logging.DEBUG)
        # return the field object
        try:
            # look for field optimizations
            if self.ops.optimizations is not None and 'fields' in self.ops.optimizations:
                ocgis_lh('applying optimizations', self._subset_log, level=logging.DEBUG)
                field = [self.ops.optimizations['fields'][rd.alias] for rd in rds]
            # no field optimizations, extract the target data from the dataset collection
            else:
                ocgis_lh('creating field objects', self._subset_log, level=logging.DEBUG)
                len_rds = len(rds)
                field = [None] * len_rds
                for ii in range(len_rds):
                    rds_element = rds[ii]
                    try:
                        field_object = rds_element.get(format_time=self.ops.format_time)
                    except AttributeError:
                        # likely a field object which does not need to be loaded from source
                        if not self.ops.format_time:
                            raise NotImplementedError
                        field_object = rds_element

                    # extrapolate the spatial bounds if requested
                    if self.ops.interpolate_spatial_bounds:
                        try:
                            try:
                                field_object.spatial.grid.row.set_extrapolated_bounds()
                                field_object.spatial.grid.col.set_extrapolated_bounds()
                            except AttributeError:
                                # row/col is likely none. attempt to extrapolate using the grid values
                                field_object.spatial.grid.set_extrapolated_corners()
                        except BoundsAlreadyAvailableError:
                            msg = 'Bounds/corners already on object. Ignoring "interpolate_spatial_bounds".'
                            ocgis_lh(msg=msg, logger=self._subset_log, level=logging.WARNING)

                    field[ii] = field_object

            # 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.
            for f in field:
                f.spatial.abstraction = self.ops.abstraction

            if len(field) > 1:
                try:
                    # reset the variable uid and let the collection handle its assignment
                    variable_to_add = field[1].variables.first()
                    variable_to_add.uid = None
                    field[0].variables.add_variable(variable_to_add)
                    # reset the field names and let these be auto-generated
                    for f in field:
                        f._name = None
                # this will fail for optimizations as the fields are already joined
                except VariableInCollectionError:
                    if self.ops.optimizations is not None and 'fields' in self.ops.optimizations:
                        pass
                    else:
                        raise
            field = field[0]
        # this error is related to subsetting by time or level. spatial subsetting occurs below.
        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)
                name = '_'.join([rd.name for rd in rds])
                coll.add_field(None, name=name)
                try:
                    yield coll
                finally:
                    return
            else:
                ocgis_lh(exc=ExtentError(message=str(e)), alias=str([rd.name for rd in rds]), logger=self._subset_log)

        # set iterator based on presence of slice. slice always overrides geometry.
        if self.ops.slice is not None:
            itr = [None]
        else:
            itr = [None] if self.ops.geom is None else self.ops.geom
        for coll in self._process_geometries_(itr, field, headers, value_keys, alias):
            yield (coll)
Пример #17
0
 def get_spatial_collection(self, field=None):
     rd = self.test_data.get_rd('cancm4_tas')
     field = field or rd.get()[:, 0, :, 0, 0]
     coll = SpatialCollection()
     coll.add_field(field)
     return coll
Пример #18
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)
Пример #19
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)