def test_unwrap(self): geom = box(195, -40, 225, -30) gvar = GeometryVariable(name='geoms', value=geom, crs=Spherical(), dimensions='geoms') gvar.wrap() self.assertEqual(gvar.get_value()[0].bounds, (-165.0, -40.0, -135.0, -30.0)) gvar.unwrap() self.assertEqual(gvar.get_value()[0].bounds, (195.0, -40.0, 225.0, -30.0))
def test_update_crs_to_cartesian(self): """Test a spherical to cartesian CRS update.""" bbox = box(-170., 40., 150., 80.) original_bounds = deepcopy(bbox.bounds) geom = GeometryVariable(name='geom', value=[bbox], dimensions='geom', crs=Spherical()) other_crs = Cartesian() geom.update_crs(other_crs) actual = geom.get_value()[0].bounds desired = (-0.7544065067354889, -0.13302222155948895, -0.15038373318043535, 0.38302222155948895) self.assertNumpyAllClose(np.array(actual), np.array(desired)) self.assertIsInstance(geom.crs, Cartesian) other_crs = Spherical() geom.update_crs(other_crs) self.assertEqual(geom.crs, Spherical()) actual = geom.get_value()[0].bounds self.assertNumpyAllClose(np.array(original_bounds), np.array(actual)) # Test data may not be wrapped. bbox = box(0, 40, 270, 80) geom = GeometryVariable(name='geom', value=[bbox], dimensions='geom', crs=Spherical()) other_crs = Cartesian() with self.assertRaises(ValueError): geom.update_crs(other_crs)
def test_update_crs_to_cartesian(self): """Test a spherical to cartesian CRS update.""" bbox = box(-170., 40., 150., 80.) original_bounds = deepcopy(bbox.bounds) geom = GeometryVariable(name='geom', value=[bbox], dimensions='geom', crs=Spherical()) other_crs = Cartesian() geom.update_crs(other_crs) actual = geom.get_value()[0].bounds desired = (-0.7544065067354889, -0.13302222155948895, -0.15038373318043535, 0.38302222155948895) self.assertNumpyAllClose(np.array(actual), np.array(desired)) self.assertIsInstance(geom.crs, Cartesian) other_crs = Spherical() geom.update_crs(other_crs) self.assertEqual(geom.crs, Spherical()) actual = geom.get_value()[0].bounds self.assertNumpyAllClose(np.array(original_bounds), np.array(actual)) # Test data may not be wrapped. bbox = box(0, 40, 270, 80) geom = GeometryVariable(name='geom', value=[bbox], dimensions='geom', crs=Spherical()) other_crs = Cartesian() with self.assertRaises(ValueError): geom.update_crs(other_crs)
def test_init(self): # Test empty. gvar = GeometryVariable() self.assertEqual(gvar.dtype, object) gvar = self.get_geometryvariable() self.assertIsInstance(gvar.get_masked_value(), MaskedArray) self.assertEqual(gvar.ndim, 1) # Test passing a "crs". gvar = self.get_geometryvariable(crs=WGS84(), name='my_geom', dimensions='ngeom') self.assertEqual(gvar.crs, WGS84()) # Test using lines. line1 = LineString([(0, 0), (1, 1)]) line2 = LineString([(1, 1), (2, 2)]) gvar = GeometryVariable(value=[line1, line2], dimensions='two') self.assertTrue(gvar.get_value()[1].almost_equals(line2)) self.assertEqual(gvar.geom_type, line1.geom_type) lines = MultiLineString([line1, line2]) lines2 = [lines, lines] for actual in [lines, lines2, lines]: gvar2 = GeometryVariable(value=actual, dimensions='ngeom') self.assertTrue(gvar2.get_value()[0].almost_equals(lines)) self.assertEqual(gvar2.geom_type, lines.geom_type) self.assertTrue(gvar2.shape[0] > 0) self.assertIsNone(gvar2.get_mask())
def test_prepare_geometry(self): for geometry_record in self.get_subset_geometries(): for ss, k in self: gvar = GeometryVariable(value=geometry_record['geom'], dimensions='dim', crs=WGS84()) self.assertIsNotNone(gvar.crs) prepared = ss._prepare_geometry_(gvar) self.assertNotEqual(gvar.get_value()[0].bounds, prepared.get_value()[0].bounds) self.assertFalse( np.may_share_memory(gvar.get_value(), prepared.get_value())) try: self.assertEqual(prepared.crs, ss.field.crs) except AssertionError: # Rotated pole on the fields become spherical. self.assertEqual(prepared.crs, CFSpherical()) self.assertIsInstance(ss.field.crs, CFRotatedPole) # Test nebraska against an unwrapped dataset. nebraska = GeometryVariable(value=self.nebraska['geom'], dimensions='d', crs=WGS84()) field = self.test_data.get_rd('cancm4_tas').get() ss = SpatialSubsetOperation(field) prepared = ss._prepare_geometry_(nebraska) self.assertEqual(prepared.wrapped_state, WrappedState.UNWRAPPED)
def test_unwrap(self): geom = box(195, -40, 225, -30) gvar = GeometryVariable(name='geoms', value=geom, crs=Spherical(), dimensions='geoms') gvar.wrap() self.assertEqual(gvar.get_value()[0].bounds, (-165.0, -40.0, -135.0, -30.0)) gvar.unwrap() self.assertEqual(gvar.get_value()[0].bounds, (195.0, -40.0, 225.0, -30.0))
def test_get_buffered_geometry(self): proj4 = '+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs' buffer_crs_list = [None, CoordinateReferenceSystem(proj4=proj4)] poly = make_poly((36, 44), (-104, -95)) for buffer_crs in buffer_crs_list: gvar = GeometryVariable(value=poly, name='geoms', dimensions='dim', crs=WGS84()) self.assertEqual(gvar.crs, WGS84()) if buffer_crs is None: buffer_value = 1 else: buffer_value = 10 ret = SpatialSubsetOperation._get_buffered_geometry_(gvar, buffer_value, buffer_crs=buffer_crs) ref = ret.get_value()[0] if buffer_crs is None: self.assertEqual(ref.bounds, (-105.0, 35.0, -94.0, 45.0)) else: self.assertNumpyAllClose(np.array(ref.bounds), np.array( (-104.00013263459613, 35.9999147913708, -94.99986736540386, 44.00008450528758))) self.assertEqual(gvar.crs, ret.crs) # check deepcopy ret.get_value()[0] = make_poly((1, 2), (3, 4)) ref_buffered = ret.get_value()[0] ref_original = gvar.get_value()[0] with self.assertRaises(AssertionError): self.assertNumpyAllClose(np.array(ref_buffered.bounds), np.array(ref_original.bounds))
def test_deepcopy(self): gvar = GeometryVariable(value=Point(1, 2), crs=Spherical(), dimensions='d') self.assertEqual(gvar.crs, Spherical()) d = gvar.deepcopy() d.crs = WGS84() self.assertEqual(gvar.crs, Spherical()) self.assertFalse(np.may_share_memory(gvar.get_value(), d.get_value())) self.assertIsNotNone(d.crs)
def test_deepcopy(self): gvar = GeometryVariable(value=Point(1, 2), crs=Spherical(), dimensions='d') self.assertEqual(gvar.crs, Spherical()) d = gvar.deepcopy() d.crs = WGS84() self.assertEqual(gvar.crs, Spherical()) self.assertFalse(np.may_share_memory(gvar.get_value(), d.get_value())) self.assertIsNotNone(d.crs)
def test_init(self): # Test empty. gvar = GeometryVariable() self.assertEqual(gvar.dtype, object) gvar = self.get_geometryvariable() self.assertIsInstance(gvar.get_masked_value(), MaskedArray) self.assertEqual(gvar.ndim, 1) # The geometry variable should set itself as the representative geometry on its parent field if that parent does # not have a representative geometry set. self.assertIsNotNone(gvar.parent.geom) # Test with a parent that already has a geometry. field = Field() field.set_geom(GeometryVariable(name='empty')) gvar = self.get_geometryvariable(parent=field) self.assertEqual(field.geom.name, 'empty') self.assertIn(gvar.name, field) # Test passing a "crs". gvar = self.get_geometryvariable(crs=WGS84(), name='my_geom', dimensions='ngeom') self.assertEqual(gvar.crs, WGS84()) # Test using lines. line1 = LineString([(0, 0), (1, 1)]) line2 = LineString([(1, 1), (2, 2)]) gvar = GeometryVariable(value=[line1, line2], dimensions='two') self.assertTrue(gvar.get_value()[1].almost_equals(line2)) self.assertEqual(gvar.geom_type, line1.geom_type) lines = MultiLineString([line1, line2]) lines2 = [lines, lines] for actual in [lines, lines2, lines]: gvar2 = GeometryVariable(value=actual, dimensions='ngeom') self.assertTrue(gvar2.get_value()[0].almost_equals(lines)) self.assertEqual(gvar2.geom_type, lines.geom_type) self.assertTrue(gvar2.shape[0] > 0) self.assertIsNone(gvar2.get_mask())
def test_init(self): # Test empty. gvar = GeometryVariable() self.assertEqual(gvar.dtype, object) gvar = self.get_geometryvariable() self.assertIsInstance(gvar.get_masked_value(), MaskedArray) self.assertEqual(gvar.ndim, 1) # The geometry variable should set itself as the representative geometry on its parent field if that parent does # not have a representative geometry set. self.assertIsNotNone(gvar.parent.geom) # Test with a parent that already has a geometry. field = Field() field.set_geom(GeometryVariable(name='empty')) gvar = self.get_geometryvariable(parent=field) self.assertEqual(field.geom.name, 'empty') self.assertIn(gvar.name, field) # Test passing a "crs". gvar = self.get_geometryvariable(crs=WGS84(), name='my_geom', dimensions='ngeom') self.assertEqual(gvar.crs, WGS84()) # Test using lines. line1 = LineString([(0, 0), (1, 1)]) line2 = LineString([(1, 1), (2, 2)]) gvar = GeometryVariable(value=[line1, line2], dimensions='two') self.assertTrue(gvar.get_value()[1].almost_equals(line2)) self.assertEqual(gvar.geom_type, line1.geom_type) lines = MultiLineString([line1, line2]) lines2 = [lines, lines] for actual in [lines, lines2, lines]: gvar2 = GeometryVariable(value=actual, dimensions='ngeom') self.assertTrue(gvar2.get_value()[0].almost_equals(lines)) self.assertEqual(gvar2.geom_type, lines.geom_type) self.assertTrue(gvar2.shape[0] > 0) self.assertIsNone(gvar2.get_mask())
def get_spatial_subset(self, operation, geom, use_spatial_index=env.USE_SPATIAL_INDEX, buffer_value=None, buffer_crs=None, geom_crs=None, select_nearest=False, optimized_bbox_subset=False): """ Perform a spatial subset operation on ``target``. :param str operation: Either ``'intersects'`` or ``'clip'``. :param geom: The input geometry object to use for subsetting of ``target``. :type geom: :class:`shapely.geometry.base.BaseGeometry` :param bool use_spatial_index: If ``True``, use an ``rtree`` spatial index. :param bool select_nearest: If ``True``, select the geometry nearest ``polygon`` using :meth:`shapely.geometry.base.BaseGeometry.distance`. :rtype: Same as ``target``. If ``target`` is a :class:`ocgis.RequestDataset`, then a :class:`ocgis.interface.base.field.Field` will be returned. :param float buffer_value: The buffer radius to use in units of the coordinate system of ``subset_sdim``. :param buffer_crs: If provided, then ``buffer_value`` are not in units of the coordinate system of ``subset_sdim`` but in units of ``buffer_crs``. :param bool select_nearest: If ``True``, following the spatial subset operation, select the nearest geometry in the subset data to ``geom``. Centroid-based distance is used. :type buffer_crs: :class:`ocgis.interface.base.crs.CoordinateReferenceSystem` :param bool optimized_bbox_subset: If ``True``, only do a bounding box subset and do not perform more complext GIS subset operations such as constructing a spatial index. :raises: ValueError """ if not isinstance(geom, GeometryVariable): geom = GeometryVariable(value=geom, name='geom', dimensions='one', crs=geom_crs) if geom.get_value().flatten().shape != (1, ): msg = 'Only one subset geometry allowed. The shape of the geometry variable is {}.'.format( geom.shape) raise ValueError(msg) if optimized_bbox_subset: if self.field.grid is None: msg = 'Subset operation must be performed on a grid when "optimized_bbox_subset=True".' raise ValueError(msg) if operation != 'intersects': msg = 'Only "intersects" spatial operations when "optimized_bbox_subset=True".' raise ValueError(msg) # Buffer the subset if a buffer value is provided. if buffer_value is not None: geom = self._get_buffered_geometry_(geom, buffer_value, buffer_crs=buffer_crs) self._prepare_target_() prepared = self._prepare_geometry_(geom) base_geometry = prepared.get_value().flatten()[0] # execute the spatial operation if operation == 'intersects': if self.field.grid is None: ret = self.field.geom.get_intersects( base_geometry, use_spatial_index=use_spatial_index, cascade=True).parent else: ret = self.field.grid.get_intersects( base_geometry, cascade=True, optimized_bbox_subset=optimized_bbox_subset).parent elif operation in ('clip', 'intersection'): if self.field.grid is None: ret = self.field.geom.get_intersection( base_geometry, use_spatial_index=use_spatial_index, cascade=True).parent else: ret = self.field.grid.get_intersection(base_geometry, cascade=True) # An intersection with a grid returns a geometry variable. Set this on the field. ret.parent.set_geom(ret) ret = ret.parent else: msg = 'The spatial operation "{0}" is not supported.'.format( operation) raise ValueError(msg) with vm.scoped_by_emptyable('return finalize', ret): if not vm.is_null: # Select the nearest geometry if requested. if select_nearest: ret.set_abstraction_geom() ret = ret.geom.get_nearest(base_geometry).parent # check for rotated pole and convert back to default CRS if self._original_rotated_pole_state is not None and self.output_crs == 'input': ret.update_crs(self._original_rotated_pole_state) # wrap the data... if self._get_should_wrap_(ret): ret.wrap() # convert the coordinate system if requested... if self.should_update_crs: ret.update_crs(self.output_crs) return ret
def get_spatial_subset(self, operation, geom, use_spatial_index=env.USE_SPATIAL_INDEX, buffer_value=None, buffer_crs=None, geom_crs=None, select_nearest=False, optimized_bbox_subset=False): """ Perform a spatial subset operation on ``target``. :param str operation: Either ``'intersects'`` or ``'clip'``. :param geom: The input geometry object to use for subsetting of ``target``. :type geom: :class:`shapely.geometry.base.BaseGeometry` | :class:`ocgis.GeometryVariable` :param bool use_spatial_index: If ``True``, use an ``rtree`` spatial index. :param bool select_nearest: If ``True``, select the geometry nearest ``polygon`` using :meth:`shapely.geometry.base.BaseGeometry.distance`. :rtype: Same as ``target``. If ``target`` is a :class:`ocgis.RequestDataset`, then a :class:`ocgis.interface.base.field.Field` will be returned. :param float buffer_value: The buffer radius to use in units of the coordinate system of ``subset_sdim``. :param buffer_crs: If provided, then ``buffer_value`` are not in units of the coordinate system of ``subset_sdim`` but in units of ``buffer_crs``. :param geom_crs: The coordinate reference system for the subset geometry. :type geom_crs: :class:`ocgis.crs.CRS` :param bool select_nearest: If ``True``, following the spatial subset operation, select the nearest geometry in the subset data to ``geom``. Centroid-based distance is used. :type buffer_crs: :class:`ocgis.interface.base.crs.CoordinateReferenceSystem` :param bool optimized_bbox_subset: If ``True``, only do a bounding box subset and do not perform more complext GIS subset operations such as constructing a spatial index. :raises: ValueError """ if not isinstance(geom, GeometryVariable): geom = GeometryVariable(value=geom, name='geom', dimensions='one', crs=geom_crs) if geom.get_value().flatten().shape != (1,): msg = 'Only one subset geometry allowed. The shape of the geometry variable is {}.'.format(geom.shape) raise ValueError(msg) if optimized_bbox_subset: if self.field.grid is None: msg = 'Subset operation must be performed on a grid when "optimized_bbox_subset=True".' raise ValueError(msg) if operation != 'intersects': msg = 'Only "intersects" spatial operations when "optimized_bbox_subset=True".' raise ValueError(msg) # Buffer the subset if a buffer value is provided. if buffer_value is not None: geom = self._get_buffered_geometry_(geom, buffer_value, buffer_crs=buffer_crs) prepared = self._prepare_geometry_(geom) base_geometry = prepared.get_value().flatten()[0] # Prepare the target field. self._prepare_target_() # execute the spatial operation if operation == 'intersects': if self.field.grid is None: ret = self.field.geom.get_intersects(base_geometry, use_spatial_index=use_spatial_index, cascade=True).parent else: ret = self.field.grid.get_intersects(base_geometry, cascade=True, optimized_bbox_subset=optimized_bbox_subset).parent elif operation in ('clip', 'intersection'): if self.field.grid is None: ret = self.field.geom.get_intersection(base_geometry, use_spatial_index=use_spatial_index, cascade=True).parent else: ret = self.field.grid.get_intersection(base_geometry, cascade=True) # An intersection with a grid returns a geometry variable. Set this on the field. ret.parent.set_geom(ret) ret = ret.parent else: msg = 'The spatial operation "{0}" is not supported.'.format(operation) raise ValueError(msg) with vm.scoped_by_emptyable('return finalize', ret): if not vm.is_null: # Select the nearest geometry if requested. if select_nearest: ret.set_abstraction_geom() ret = ret.geom.get_nearest(base_geometry).parent # check for rotated pole and convert back to default CRS if self._original_rotated_pole_state is not None and self.output_crs == 'input': ret.update_crs(self._original_rotated_pole_state) # wrap the data... if self._get_should_wrap_(ret): ret.wrap() # convert the coordinate system if requested... if self.should_update_crs: ret.update_crs(self.output_crs) return ret