def test_unwrap(self): """Test different geometry types are appropriately unwrapped.""" wrapper = Wrapper() path = tempfile.mkdtemp() for desc, geom in self.possible.iteritems(): unwrapped = wrapper.unwrap(geom) if desc in self.actual_unwrapped: self.assertTrue(self.actual_unwrapped[desc].almost_equals(unwrapped, decimal=5)) try: self.assertEqual(type(geom), type(unwrapped)) except AssertionError: if desc == 'axis_polygon': # by necessity of being split on the axis, this will come out as a multipolygon self.assertIsInstance(unwrapped, MultiPolygon) else: raise self.assertFalse(np.any(np.array(unwrapped) < 0.0)) if isinstance(unwrapped, (MultiPolygon, Polygon)): it = get_iter(unwrapped) for polygon in it: self.assertFalse(np.any(np.array(polygon.exterior) > 360.0)) else: self.assertFalse(np.any(np.array(unwrapped) > 360.0))
def test_unwrap(self): sd = ShpDataset('state_boundaries') for axis in self.axes: w = Wrapper(axis=axis) for geom in sd.spatial.geom: new_geom = w.unwrap(geom) bounds = np.array(new_geom.bounds) self.assertFalse(np.any(bounds < axis))
def test_wrap(self): sd = ShpDataset('state_boundaries') for axis in self.axes: w = Wrapper(axis=axis) unwrapped = [w.unwrap(geom) for geom in sd.spatial.geom] for idx,unwrapped_geom in enumerate(unwrapped): new_geom = w.wrap(unwrapped_geom) self.assertFalse(unwrapped_geom.equals(new_geom)) self.assertTrue(sd.spatial.geom[idx].almost_equals(new_geom))
def test_wrap(self): """Test different geometry types are appropriately wrapped.""" wrapper = Wrapper() for desc, geom in self.possible.iteritems(): unwrapped = wrapper.unwrap(geom) wrapped = wrapper.wrap(unwrapped) try: self.assertTrue(geom.almost_equals(wrapped)) except AssertionError: # the axis multipolygon when wrapped will have an extra polygon as the split portion on the axis will # be in two parts if desc == 'axis_multipolygon': self.assertNumpyAllClose(np.array(wrapped.bounds), np.array(geom.bounds)) self.assertEqual(len(wrapped), 4) # polygon will also be split... elif desc == 'axis_polygon': self.assertNumpyAllClose(np.array(wrapped.bounds), np.array(geom.bounds)) self.assertEqual(len(wrapped), 2) else: raise
def get_collection((so, geom)): ''' :type so: SubsetOperation :type geom: None, GeometryDataset, ShpDataset :rtype: AbstractCollection ''' ## initialize the collection object to store the subsetted data. coll = RawCollection(ugeom=geom) ## perform the operations on each request dataset if env.VERBOSE: print('{0} request dataset(s) to process.'.format(len(so.ops.dataset))) for request_dataset in so.ops.dataset: if env.VERBOSE: if geom is None: msg = 'processing: alias={0}'.format(request_dataset.alias) else: msg = 'processing: ugid={0}, alias={1}'.format( geom.spatial.uid[0], request_dataset.alias) print(msg) ## copy the geometry copy_geom = deepcopy(geom) ## reference the dataset object ods = request_dataset.ds ## return a slice or do the other operations if so.ops.slice is not None: ods = ods.__getitem__(so.ops.slice) ## other subsetting operations else: ## if a geometry is passed and the target dataset is 360 longitude, ## unwrap the passed geometry to match the spatial domain of the target ## dataset. if copy_geom is None: igeom = None else: ## check projections adjusting projection the selection geometry ## if necessary if type(ods.spatial.projection) != type( copy_geom.spatial.projection): copy_geom.project(ods.spatial.projection) ## unwrap the data if it is geographic and 360 if type(ods.spatial.projection ) == WGS84 and ods.spatial.is_360: w = Wrapper(axis=ods.spatial.pm) copy_geom.spatial.geom[0] = w.unwrap( deepcopy(copy_geom.spatial.geom[0])) igeom = copy_geom.spatial.geom[0] ## perform the data subset try: ods = ods.get_subset( spatial_operation=so.ops.spatial_operation, igeom=igeom, temporal=request_dataset.time_range, level=request_dataset.level_range) ## aggregate the geometries and data if requested if so.ops.aggregate: ## the new geometry will have the same id as the passed ## geometry. if it does not have one, simple give it a value ## of 1 as it is the only geometry requested for subsetting. try: new_geom_id = copy_geom.spatial.uid[0] except AttributeError: new_geom_id = 1 ## do the aggregation in place. clip_geom = None if copy_geom is None else copy_geom.spatial.geom[ 0] ods.aggregate(new_geom_id=new_geom_id, clip_geom=clip_geom) ## wrap the returned data depending on the conditions of the ## operations. if not env.OPTIMIZE_FOR_CALC: if type(ods.spatial.projection) == WGS84 and \ ods.spatial.is_360 and \ so.ops.output_format != 'nc' and \ so.ops.vector_wrap: ods.spatial.vector.wrap() ## check for all masked values if not so.ops.file_only and ods.value.mask.all(): if so.ops.snippet or so.ops.allow_empty: if env.VERBOSE: if so.ops.snippet: print( 'all masked data encountered but allowed for snippet.' ) if so.ops.allow_empty: print( 'all masked data encountered but empty returns are allowed.' ) pass else: raise (MaskedDataError) ## there may be no data returned - this may be real or could be an ## error. by default, empty returns are not allowed except EmptyData: if so.ops.allow_empty: if env.VERBOSE: print( 'the geometric operations returned empty but empty returns are allowed' ) continue else: raise (ExtentError(request_dataset)) coll.variables.update({request_dataset.alias: ods}) ## if there are calculations, do those now and return a new type of collection if so.cengine is not None: coll = so.cengine.execute(coll, file_only=so.ops.file_only) ## conversion of groups. if so.ops.output_grouping is not None: raise (NotImplementedError) else: return (coll)
def wrap(self): wrap = Wrapper().wrap geom = self.geom for (ii, jj), to_wrap in iter_array(geom, return_value=True): geom[ii, jj] = wrap(to_wrap)
def unwrap_geoms(self, axis=0.0): axis = float(axis) w = Wrapper(axis=axis) geom = self.geom for idx in range(geom.shape[0]): geom[idx] = w.unwrap(geom[idx])
def get_collection((so,geom)): ''' :type so: SubsetOperation :type geom: None, GeometryDataset, ShpDataset :rtype: AbstractCollection ''' ## initialize the collection object to store the subsetted data. coll = RawCollection(ugeom=geom) ## perform the operations on each request dataset if env.VERBOSE: print('{0} request dataset(s) to process.'.format(len(so.ops.dataset))) for request_dataset in so.ops.dataset: if env.VERBOSE: if geom is None: msg = 'processing: alias={0}'.format(request_dataset.alias) else: msg = 'processing: ugid={0}, alias={1}'.format(geom.spatial.uid[0],request_dataset.alias) print(msg) ## copy the geometry copy_geom = deepcopy(geom) ## reference the dataset object ods = request_dataset.ds ## return a slice or do the other operations if so.ops.slice is not None: ods = ods.__getitem__(so.ops.slice) ## other subsetting operations else: ## if a geometry is passed and the target dataset is 360 longitude, ## unwrap the passed geometry to match the spatial domain of the target ## dataset. if copy_geom is None: igeom = None else: ## check projections adjusting projection the selection geometry ## if necessary if type(ods.spatial.projection) != type(copy_geom.spatial.projection): copy_geom.project(ods.spatial.projection) ## unwrap the data if it is geographic and 360 if type(ods.spatial.projection) == WGS84 and ods.spatial.is_360: w = Wrapper(axis=ods.spatial.pm) copy_geom.spatial.geom[0] = w.unwrap(deepcopy(copy_geom.spatial.geom[0])) igeom = copy_geom.spatial.geom[0] ## perform the data subset try: ods = ods.get_subset(spatial_operation=so.ops.spatial_operation, igeom=igeom, temporal=request_dataset.time_range, level=request_dataset.level_range) ## aggregate the geometries and data if requested if so.ops.aggregate: ## the new geometry will have the same id as the passed ## geometry. if it does not have one, simple give it a value ## of 1 as it is the only geometry requested for subsetting. try: new_geom_id = copy_geom.spatial.uid[0] except AttributeError: new_geom_id = 1 ## do the aggregation in place. clip_geom = None if copy_geom is None else copy_geom.spatial.geom[0] ods.aggregate(new_geom_id=new_geom_id, clip_geom=clip_geom) ## wrap the returned data depending on the conditions of the ## operations. if not env.OPTIMIZE_FOR_CALC: if type(ods.spatial.projection) == WGS84 and \ ods.spatial.is_360 and \ so.ops.output_format != 'nc' and \ so.ops.vector_wrap: ods.spatial.vector.wrap() ## check for all masked values if not so.ops.file_only and ods.value.mask.all(): if so.ops.snippet or so.ops.allow_empty: if env.VERBOSE: if so.ops.snippet: print('all masked data encountered but allowed for snippet.') if so.ops.allow_empty: print('all masked data encountered but empty returns are allowed.') pass else: raise(MaskedDataError) ## there may be no data returned - this may be real or could be an ## error. by default, empty returns are not allowed except EmptyData: if so.ops.allow_empty: if env.VERBOSE: print('the geometric operations returned empty but empty returns are allowed') continue else: raise(ExtentError(request_dataset)) coll.variables.update({request_dataset.alias:ods}) ## if there are calculations, do those now and return a new type of collection if so.cengine is not None: coll = so.cengine.execute(coll,file_only=so.ops.file_only) ## conversion of groups. if so.ops.output_grouping is not None: raise(NotImplementedError) else: return(coll)
def get_collection((so,geom,logger)): ''' :type so: SubsetOperation :type geom: None, GeometryDataset, ShpDataset :rtype: AbstractCollection ''' ## initialize the collection object to store the subsetted data. coll = RawCollection(ugeom=geom,ops=so.ops) ## perform the operations on each request dataset ocgis_lh('{0} request dataset(s) to process'.format(len(so.ops.dataset)),logger) ## reference the geometry ugid ugid = None if geom is None else geom.spatial.uid[0] for request_dataset in so.ops.dataset: ## reference the request dataset alias alias = request_dataset.alias ocgis_lh('processing',logger,level=logging.INFO,alias=alias,ugid=ugid) ## copy the geometry copy_geom = deepcopy(geom) ## reference the dataset object ods = request_dataset.ds ## return a slice or do the other operations if so.ops.slice is not None: ods = ods.__getitem__(so.ops.slice) ## other subsetting operations else: ## if a geometry is passed and the target dataset is 360 longitude, ## unwrap the passed geometry to match the spatial domain of the target ## dataset. if copy_geom is None: igeom = None else: ## check projections adjusting projection the selection geometry ## if necessary if type(ods.spatial.projection) != type(copy_geom.spatial.projection): msg = 'projecting selection geometry to match input projection: {0} to {1}' msg = msg.format(copy_geom.spatial.projection.__class__.__name__, ods.spatial.projection.__class__.__name__) ocgis_lh(msg,logger,alias=alias,ugid=ugid) copy_geom.project(ods.spatial.projection) else: ocgis_lh('projections match',logger,alias=alias,ugid=ugid) ## unwrap the data if it is geographic and 360 if type(ods.spatial.projection) == WGS84 and ods.spatial.is_360: ocgis_lh('unwrapping selection geometry with axis={0}'.format(ods.spatial.pm), logger,alias=alias,ugid=ugid) w = Wrapper(axis=ods.spatial.pm) copy_geom.spatial.geom[0] = w.unwrap(deepcopy(copy_geom.spatial.geom[0])) igeom = copy_geom.spatial.geom[0] ## perform the data subset try: ## pull the temporal subset which may be a range or region. if ## it is a snippet operation, set the temporal subset to None ## as a slice has already been applied. however, if a calculation ## is present leave the temporal subset alone. if so.ops.snippet and so.ops.calc is None: temporal = None else: temporal = request_dataset.time_range or request_dataset.time_region ocgis_lh('executing get_subset',logger,level=logging.DEBUG) ods = ods.get_subset(spatial_operation=so.ops.spatial_operation, igeom=igeom, temporal=temporal, level=request_dataset.level_range) ## for the case of time range and time region subset, apply the ## time region subset following the time range subset. if request_dataset.time_range is not None and request_dataset.time_region is not None: ods._temporal = ods.temporal.subset(request_dataset.time_region) ## aggregate the geometries and data if requested if so.ops.aggregate: ocgis_lh('aggregating target geometries and area-weighting values', logger,alias=alias,ugid=ugid) ## the new geometry will have the same id as the passed ## geometry. if it does not have one, simple give it a value ## of 1 as it is the only geometry requested for subsetting. try: new_geom_id = copy_geom.spatial.uid[0] except AttributeError: new_geom_id = 1 ## do the aggregation in place. clip_geom = None if copy_geom is None else copy_geom.spatial.geom[0] ods.aggregate(new_geom_id=new_geom_id, clip_geom=clip_geom) ## wrap the returned data depending on the conditions of the ## operations. if not env.OPTIMIZE_FOR_CALC: if type(ods.spatial.projection) == WGS84 and \ ods.spatial.is_360 and \ so.ops.output_format != 'nc' and \ so.ops.vector_wrap: ocgis_lh('wrapping output geometries',logger,alias=alias, ugid=ugid) ods.spatial.vector.wrap() ocgis_lh('geometries wrapped',logger,alias=alias, ugid=ugid,level=logging.DEBUG) ## check for all masked values if env.OPTIMIZE_FOR_CALC is False and so.ops.file_only is False: if ods.value.mask.all(): ## masked data may be okay depending on other opeartional ## conditions. if so.ops.snippet or so.ops.allow_empty: if so.ops.snippet: ocgis_lh('all masked data encountered but allowed for snippet', logger,alias=alias,ugid=ugid,level=logging.WARN) if so.ops.allow_empty: ocgis_lh('all masked data encountered but empty returns allowed', logger,alias=alias,ugid=ugid,level=logging.WARN) pass else: ## if the geometry is also masked, it is an empty spatial ## operation. if ods.spatial.vector.geom.mask.all(): raise(EmptyData) else: ocgis_lh(None,logger,exc=MaskedDataError(),alias=alias,ugid=ugid) ## there may be no data returned - this may be real or could be an ## error. by default, empty returns are not allowed except EmptyData as ed: if so.ops.allow_empty: if ed.origin == 'time': msg = 'the time subset returned empty but empty returns are allowed' else: msg = 'the geometric operations returned empty but empty returns are allowed' ocgis_lh(msg,logger,alias=alias,ugid=ugid) continue else: if ed.origin == 'time': msg = 'empty temporal subset operation' else: msg = 'empty geometric operation' ocgis_lh(msg,logger,exc=ExtentError(msg),alias=alias,ugid=ugid) ods.spatial._ugid = ugid coll.variables.update({request_dataset.alias:ods}) ## if there are calculations, do those now and return a new type of collection if so.cengine is not None: ocgis_lh('performing computations',logger,alias=alias,ugid=ugid) coll = so.cengine.execute(coll,file_only=so.ops.file_only) ## conversion of groups. if so.ops.output_grouping is not None: raise(NotImplementedError) else: ocgis_lh('subset returning',logger,level=logging.INFO) return(coll)
def unwrap_geoms(self,axis=0.0): axis = float(axis) w = Wrapper(axis=axis) geom = self.geom for idx in range(geom.shape[0]): geom[idx] = w.unwrap(geom[idx])
def get_collection((so, geom, logger)): ''' :type so: SubsetOperation :type geom: None, GeometryDataset, ShpDataset :rtype: AbstractCollection ''' ## initialize the collection object to store the subsetted data. coll = RawCollection(ugeom=geom, ops=so.ops) ## perform the operations on each request dataset ocgis_lh('{0} request dataset(s) to process'.format(len(so.ops.dataset)), logger) ## reference the geometry ugid ugid = None if geom is None else geom.spatial.uid[0] for request_dataset in so.ops.dataset: ## reference the request dataset alias alias = request_dataset.alias ocgis_lh('processing', logger, level=logging.INFO, alias=alias, ugid=ugid) ## copy the geometry copy_geom = deepcopy(geom) ## reference the dataset object ods = request_dataset.ds ## return a slice or do the other operations if so.ops.slice is not None: ods = ods.__getitem__(so.ops.slice) ## other subsetting operations else: ## if a geometry is passed and the target dataset is 360 longitude, ## unwrap the passed geometry to match the spatial domain of the target ## dataset. if copy_geom is None: igeom = None else: ## check projections adjusting projection the selection geometry ## if necessary if type(ods.spatial.projection) != type( copy_geom.spatial.projection): msg = 'projecting selection geometry to match input projection: {0} to {1}' msg = msg.format( copy_geom.spatial.projection.__class__.__name__, ods.spatial.projection.__class__.__name__) ocgis_lh(msg, logger, alias=alias, ugid=ugid) copy_geom.project(ods.spatial.projection) else: ocgis_lh('projections match', logger, alias=alias, ugid=ugid) ## unwrap the data if it is geographic and 360 if type(ods.spatial.projection ) == WGS84 and ods.spatial.is_360: ocgis_lh( 'unwrapping selection geometry with axis={0}'.format( ods.spatial.pm), logger, alias=alias, ugid=ugid) w = Wrapper(axis=ods.spatial.pm) copy_geom.spatial.geom[0] = w.unwrap( deepcopy(copy_geom.spatial.geom[0])) igeom = copy_geom.spatial.geom[0] ## perform the data subset try: ## pull the temporal subset which may be a range or region. if ## it is a snippet operation, set the temporal subset to None ## as a slice has already been applied. however, if a calculation ## is present leave the temporal subset alone. if so.ops.snippet and so.ops.calc is None: temporal = None else: temporal = request_dataset.time_range or request_dataset.time_region ocgis_lh('executing get_subset', logger, level=logging.DEBUG) ods = ods.get_subset( spatial_operation=so.ops.spatial_operation, igeom=igeom, temporal=temporal, level=request_dataset.level_range) ## for the case of time range and time region subset, apply the ## time region subset following the time range subset. if request_dataset.time_range is not None and request_dataset.time_region is not None: ods._temporal = ods.temporal.subset( request_dataset.time_region) ## aggregate the geometries and data if requested if so.ops.aggregate: ocgis_lh( 'aggregating target geometries and area-weighting values', logger, alias=alias, ugid=ugid) ## the new geometry will have the same id as the passed ## geometry. if it does not have one, simple give it a value ## of 1 as it is the only geometry requested for subsetting. try: new_geom_id = copy_geom.spatial.uid[0] except AttributeError: new_geom_id = 1 ## do the aggregation in place. clip_geom = None if copy_geom is None else copy_geom.spatial.geom[ 0] ods.aggregate(new_geom_id=new_geom_id, clip_geom=clip_geom) ## wrap the returned data depending on the conditions of the ## operations. if not env.OPTIMIZE_FOR_CALC: if type(ods.spatial.projection) == WGS84 and \ ods.spatial.is_360 and \ so.ops.output_format != 'nc' and \ so.ops.vector_wrap: ocgis_lh('wrapping output geometries', logger, alias=alias, ugid=ugid) ods.spatial.vector.wrap() ocgis_lh('geometries wrapped', logger, alias=alias, ugid=ugid, level=logging.DEBUG) ## check for all masked values if env.OPTIMIZE_FOR_CALC is False and so.ops.file_only is False: if ods.value.mask.all(): ## masked data may be okay depending on other opeartional ## conditions. if so.ops.snippet or so.ops.allow_empty: if so.ops.snippet: ocgis_lh( 'all masked data encountered but allowed for snippet', logger, alias=alias, ugid=ugid, level=logging.WARN) if so.ops.allow_empty: ocgis_lh( 'all masked data encountered but empty returns allowed', logger, alias=alias, ugid=ugid, level=logging.WARN) pass else: ## if the geometry is also masked, it is an empty spatial ## operation. if ods.spatial.vector.geom.mask.all(): raise (EmptyData) else: ocgis_lh(None, logger, exc=MaskedDataError(), alias=alias, ugid=ugid) ## there may be no data returned - this may be real or could be an ## error. by default, empty returns are not allowed except EmptyData as ed: if so.ops.allow_empty: if ed.origin == 'time': msg = 'the time subset returned empty but empty returns are allowed' else: msg = 'the geometric operations returned empty but empty returns are allowed' ocgis_lh(msg, logger, alias=alias, ugid=ugid) continue else: if ed.origin == 'time': msg = 'empty temporal subset operation' else: msg = 'empty geometric operation' ocgis_lh(msg, logger, exc=ExtentError(msg), alias=alias, ugid=ugid) ods.spatial._ugid = ugid coll.variables.update({request_dataset.alias: ods}) ## if there are calculations, do those now and return a new type of collection if so.cengine is not None: ocgis_lh('performing computations', logger, alias=alias, ugid=ugid) coll = so.cengine.execute(coll, file_only=so.ops.file_only) ## conversion of groups. if so.ops.output_grouping is not None: raise (NotImplementedError) else: ocgis_lh('subset returning', logger, level=logging.INFO) return (coll)