def setUp(self): """Set up the test case.""" chunks = 10 self.dst_area = AreaDefinition( 'euro40', 'euro40', None, { 'proj': 'stere', 'lon_0': 14.0, 'lat_0': 90.0, 'lat_ts': 60.0, 'ellps': 'bessel' }, 102, 102, (-2717181.7304994687, -5571048.14031214, 1378818.2695005313, -1475048.1403121399)) self.src_area = AreaDefinition( 'omerc_otf', 'On-the-fly omerc area', None, { 'alpha': '8.99811271718795', 'ellps': 'sphere', 'gamma': '0', 'k': '1', 'lat_0': '0', 'lonc': '13.8096029486222', 'proj': 'omerc', 'units': 'm' }, 50, 100, (-1461111.3603, 3440088.0459, 1534864.0322, 9598335.0457)) self.lons, self.lats = self.src_area.get_lonlats(chunks=chunks) xrlons = xr.DataArray(self.lons.persist()) xrlats = xr.DataArray(self.lats.persist()) self.src_swath = SwathDefinition(xrlons, xrlats)
def test_dest_area_is_outside_source_area_domain(self): """Test dest area is outside the source area domain (nan coordinates).""" area_to_crop = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 100, 100, (5550000.0, 5550000.0, -5550000.0, -5550000.0)) area_to_contain = AreaDefinition('merc', 'Kasimbar, Indonesia', None, { 'proj': 'merc', 'lon_0': 120.0, 'lat_0': 0, 'ellps': 'WGS84' }, 102, 102, (-100000, -100000, 100000, 100000)) slicer = create_slicer(area_to_crop, area_to_contain) with pytest.raises(IncompatibleAreas): slicer.get_slices()
def test_source_area_does_not_cover_dest_area_at_all_2(self): """Test source area does not cover dest area at all.""" src_area = AreaDefinition('src', 'src area', None, { 'proj': 'merc', 'lon_0': -60, 'lat_0': 0, "ellps": "bessel" }, 100, 100, (-100000, -100000, 100000, 100000)) dst_area = AreaDefinition('merc', 'merc', None, { 'proj': 'merc', 'lon_0': 120.0, 'lat_0': 0, 'ellps': 'bessel' }, 102, 102, (-100000, -100000, 100000, 100000)) slicer = create_slicer(src_area, dst_area) with pytest.raises(IncompatibleAreas): slicer.get_slices()
def test_slicing_an_area_with_infinite_bounds(self): """Test slicing an area with infinite bounds.""" src_area = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'proj': 'merc' }, 100, 100, (-10000.0, -10000.0, 0.0, 0.0)) dst_area = AreaDefinition('moll', 'moll', None, { 'ellps': 'WGS84', 'lon_0': '0', 'proj': 'moll', 'units': 'm' }, 102, 102, (-100000.0, -4369712.0686, 18040096.0, 9020047.8481)) slicer = create_slicer(src_area, dst_area) with pytest.raises(IncompatibleAreas): slicer.get_slices()
def test_barely_touching_chunks_intersection(self): """Test that barely touching chunks generate slices on intersection.""" src_area = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 100, 100, (5550000.0, 5550000.0, -5550000.0, -5550000.0)) dst_area = AreaDefinition('moll', 'moll', None, { 'ellps': 'WGS84', 'lon_0': '0', 'proj': 'moll', 'units': 'm' }, 102, 102, (-1040095.6961, 4369712.0686, 1040095.6961, 9020047.8481)) slicer = create_slicer(src_area, dst_area) x_slice, y_slice = slicer.get_slices() assert x_slice.start > 0 and x_slice.stop < 100 assert y_slice.start > 0 and y_slice.stop >= 100
def test_dest_area_is_partly_outside_source_area_domain(self): """Test dest area is outside the source area domain (nan coordinates).""" area_to_crop = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 100, 100, (5550000.0, 5550000.0, -5550000.0, -5550000.0)) area_to_contain = AreaDefinition( 'afghanistan', 'Afghanistan', None, { 'proj': 'merc', 'lon_0': 67.5, 'lat_0': 35.0, 'lat_ts': 35.0, 'ellps': 'WGS84' }, 102, 102, (-1600000.0, 1600000.0, 1600000.0, 4800000.0)) slicer = create_slicer(area_to_crop, area_to_contain) assert slicer.get_slices() == (slice(1, 24, None), slice(63, 89, None))
def test_source_area_does_not_cover_dest_area_at_all(self): """Test source area does not cover dest area at all.""" src_area = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 80, 100, (5550000.0, 3330000.0, -5550000.0, -5550000.0)) slicer = create_slicer(src_area, self.dst_area) with pytest.raises(IncompatibleAreas): slicer.get_slices()
def test_source_area_covers_dest_area(self): """Test source area covers dest area.""" src_area = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 100, 100, (5550000.0, 5550000.0, -5550000.0, -5550000.0)) slicer = create_slicer(src_area, self.dst_area) x_slice, y_slice = slicer.get_slices() assert x_slice.start > 0 and x_slice.stop <= 100 assert y_slice.start > 0 and y_slice.stop <= 100
def setUp(self): """Set up the test case.""" self.dst_area = AreaDefinition( 'euro40', 'euro40', None, { 'proj': 'stere', 'lon_0': 14.0, 'lat_0': 90.0, 'lat_ts': 60.0, 'ellps': 'bessel' }, 102, 102, (-2717181.7304994687, -5571048.14031214, 1378818.2695005313, -1475048.1403121399))
def test_source_area_does_not_cover_dest_area_entirely(self): """Test source area does not cover dest area entirely.""" src_area = AreaDefinition('dst', 'dst area', None, { 'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos' }, 100, 100, (5550000.0, 4440000.0, -5550000.0, -6660000.0)) slicer = create_slicer(src_area, self.dst_area) x_slice, y_slice = slicer.get_slices() assert x_slice.start > 0 and x_slice.stop < 100 assert y_slice.start > 0 and y_slice.stop >= 100
def fix_bounds(metadata: dict, proj: AreaDefinition): """updates the global attributes with information of the new resampling grid Parameters ---------- metadata: dict target filename to save the tiff file proj: AreaDefinition AreaDefinition with grid resampling information Returns ------- attrs: dict Attributes with geospatial information updated to new grid """ lon, lat = proj.get_lonlats() metadata['geospatial_lat_min'] = lon.min() metadata['geospatial_lat_max'] = lon.max() metadata['geospatial_lon_min'] = lat.min() metadata['geospatial_lon_max'] = lat.max() if 'start_center_longitude' in metadata.keys(): metadata.pop('start_center_longitude') if 'start_center_latitude' in metadata.keys(): metadata.pop('start_center_latitude') if 'end_center_longitude' in metadata.keys(): metadata.pop('end_center_longitude') if 'end_center_latitude' in metadata.keys(): metadata.pop('end_center_latitude') metadata['center_longitude'] = lon[0, :].mean() metadata['center_latitude'] = lat[:, 0].mean() metadata['northernmost_latitude'] = lat.max() metadata['southernmost_latitude'] = lat.min() metadata['easternmost_longitude'] = lon.max() metadata['westernmost_longitude'] = lon.min() return metadata
def test_slicing_works_with_extents_of_different_units(self): """Test a problematic case.""" src_area = create_area_def("epsg4326", "EPSG:4326", 200, 200, (20., 60., 30., 70.)) area_id = 'Suomi_3067' description = 'Suomi_kansallinen, EPSG 3067' proj_id = 'Suomi_3067' projection = 'EPSG:3067' width = 116 height = 182 from pyproj import Proj pp = Proj(proj='utm', zone=35, ellps='GRS80') xx1, yy1 = pp(15.82308183, 55.93417040) # LL_lon, LL_lat xx2, yy2 = pp(43.12029189, 72.19756918) # UR_lon, UR_lat area_extent = (xx1, yy1, xx2, yy2) dst_area = AreaDefinition(area_id, description, proj_id, projection, width, height, area_extent) slicer = create_slicer(src_area, dst_area[:50, :50]) slice_x, slice_y = slicer.get_slices() assert 60 <= slice_x.stop < 65 assert 50 <= slice_y.stop < 55
def set_atcf_area_def(fields, tcyear=None, finalstormname=None, source_sector_file=None, clat=None, clon=None, num_lines=None, num_samples=None, pixel_width=None, pixel_height=None, track_type=None): ''' This is copied from geoips.sectorfile.dynamic.py, and still relies heavily on geoips.sectorfile''' if not pixel_width: pixel_width = TC_SECTOR_PIXEL_WIDTH_M if not pixel_height: pixel_height = TC_SECTOR_PIXEL_HEIGHT_M if not num_samples: num_samples = TC_SECTOR_NUM_SAMPLES if not num_lines: num_lines = TC_SECTOR_NUM_LINES if not finalstormname and 'final_storm_name' in fields: finalstormname = fields['final_storm_name'] if not source_sector_file and 'source_sector_file' in fields: source_sector_file = fields['source_sector_file'] if not tcyear: tcyear = fields['storm_year'] if clat is None: clat = fields['clat'] if clon is None: clon = fields['clon'] area_id = get_atcf_sectorname(fields, finalstormname, tcyear) long_description = '{0} synoptic_time {1}'.format(area_id, str(fields['synoptic_time'])) proj4_dict, area_extent = set_atcf_proj_info(clat=clat, clon=clon, num_samples=num_samples, num_lines=num_lines, pixel_width=pixel_width, pixel_height=pixel_height) # Create the AreaDefinition object from given fields. We are currently relying on Sector objects # for some information - need to decide how to handle this directly. # This is area_id= then name= for Python2, area_id= then description= for Python3 from pyresample import AreaDefinition try: # Backwards compatibility for Python 2 version of pyresample area_def = AreaDefinition(area_id, long_description, proj_id='{0}_{1}'.format(proj4_dict['proj'], area_id), proj_dict=proj4_dict, x_size=num_samples, y_size=num_lines, area_extent=area_extent) except TypeError: area_def = AreaDefinition(area_id, long_description, proj_id='{0}_{1}'.format(proj4_dict['proj'], area_id), projection=proj4_dict, width=num_samples, height=num_lines, area_extent=area_extent) area_def.sector_start_datetime = fields['synoptic_time'] area_def.sector_end_datetime = fields['synoptic_time'] area_def.sector_type = 'atcf' area_def.sector_info = {} # area_def.description is Python3 compatible, and area_def.name is Python2 compatible area_def.description = long_description if not hasattr(area_def, 'name'): area_def.name = long_description area_def.sector_info['source_sector_file'] = source_sector_file # area_def.sector_info['sourcetemplate'] = dynamic_templatefname # area_def.sector_info['sourcedynamicxmlpath'] = dynamic_xmlpath # FNMOC sectorfile doesn't have pressure for fieldname in fields.keys(): area_def.sector_info[fieldname] = fields[fieldname] area_def.sector_info['storm_year'] = tcyear # If storm_name is undefined in the current deck line, set it to finalstormname if area_def.sector_info['storm_name'] == '' and finalstormname: LOG.info('USING finalstormname "%s" rather than deck storm name "%s"', finalstormname, area_def.sector_info['storm_name']) area_def.sector_info['storm_name'] = finalstormname LOG.debug(' Current TC sector: %s', fields['deck_line']) return area_def
class TestSwathSlicer(unittest.TestCase): """Test the get_slice function when input is a swath.""" def setUp(self): """Set up the test case.""" chunks = 10 self.dst_area = AreaDefinition( 'euro40', 'euro40', None, { 'proj': 'stere', 'lon_0': 14.0, 'lat_0': 90.0, 'lat_ts': 60.0, 'ellps': 'bessel' }, 102, 102, (-2717181.7304994687, -5571048.14031214, 1378818.2695005313, -1475048.1403121399)) self.src_area = AreaDefinition( 'omerc_otf', 'On-the-fly omerc area', None, { 'alpha': '8.99811271718795', 'ellps': 'sphere', 'gamma': '0', 'k': '1', 'lat_0': '0', 'lonc': '13.8096029486222', 'proj': 'omerc', 'units': 'm' }, 50, 100, (-1461111.3603, 3440088.0459, 1534864.0322, 9598335.0457)) self.lons, self.lats = self.src_area.get_lonlats(chunks=chunks) xrlons = xr.DataArray(self.lons.persist()) xrlats = xr.DataArray(self.lats.persist()) self.src_swath = SwathDefinition(xrlons, xrlats) def test_slicer_init(self): """Test slicer initialization.""" slicer = create_slicer(self.src_swath, self.dst_area) assert slicer.area_to_crop == self.src_area assert slicer.area_to_contain == self.dst_area def test_source_swath_slicing_does_not_return_full_dataset(self): """Test source area covers dest area.""" slicer = create_slicer(self.src_swath, self.dst_area) y_max, x_max = self.src_swath.shape y_max -= 1 x_max -= 1 x_slice, y_slice = slicer.get_slices() assert x_slice.start > 0 or x_slice.stop < x_max assert y_slice.start > 0 or y_slice.stop < y_max def test_source_area_slicing_does_not_return_full_dataset(self): """Test source area covers dest area.""" slicer = create_slicer(self.src_area, self.dst_area) x_slice, y_slice = slicer.get_slices() assert x_slice.start == 0 assert x_slice.stop == 35 assert y_slice.start == 16 assert y_slice.stop == 94 def test_area_get_polygon_returns_a_polygon(self): """Test getting a polygon returns a polygon.""" from shapely.geometry import Polygon slicer = create_slicer(self.src_area, self.dst_area) poly = slicer.get_polygon_to_contain() assert isinstance(poly, Polygon) def test_swath_get_polygon_returns_a_polygon(self): """Test getting a polygon returns a polygon.""" from shapely.geometry import Polygon slicer = create_slicer(self.src_swath, self.dst_area) poly = slicer.get_polygon_to_contain() assert isinstance(poly, Polygon) def test_cannot_slice_a_string(self): """Test that we cannot slice a string.""" with pytest.raises(NotImplementedError): create_slicer("my_funky_area", self.dst_area)
def get_adef(pixel_resolution: float, subarea: dict): """Generates the grid projection for mapping L2 data based on input data resolution. If lonlat griding scheme is used, the grid resolution will be exact at the centre Parameters ---------- pixel_resolution: float swath pixel resolution in metres subarea: dict subarea dictionary with keys x0 float: longitude (deg E) of the lower left corner y0 float: latitude (deg N) of the lower left corner x1 float: longitude (deg E) of the upper right corner y1 float: latitude (deg N) of the upper right corner area_id str: string with subarea id area_name: string name of the subarea proj_id: string name of the projection being used (any recognised by pyproj) Returns ------- AreaDefinition: AreaDefinition area definition with pyproj information embedded """ # -------------- # subarea limits (box) # -------------- lon_box = subarea['x0'], subarea['x1'] lat_box = subarea['y0'], subarea['y1'] lon_0 = np.array(lon_box).mean() lat_0 = np.array(lat_box).mean() # --------------- # proj parameters # --------------- datum = 'WGS84' proj_id = subarea['proj_id'] area_id = subarea['area_id'] area_name = subarea['area_name'] # ----------- # pyproj proj # ----------- area_dict = dict(datum=datum, lat_0=lat_0, lon_0=lon_0, proj=proj_id, units='m') proj = pyproj.Proj(area_dict) if proj_id in ('lonlat', 'longlat'): g = pyproj.Geod(ellps=datum) # define Pixel height width based on meter resolution, keeping 7 decimals, < 1 m # L = 2・pi・r・A / 360 x_pixel_size = round( (pixel_resolution * 360.) / (2. * np.pi * g.a * np.cos(np.deg2rad(lat_0))) * 1e7) / 1e7 y_pixel_size = round( (pixel_resolution * 360.) / (2. * np.pi * g.b) * 1e7) / 1e7 x = np.arange(lon_box[0], lon_box[1], x_pixel_size, np.float32) y = np.arange(lat_box[1], lat_box[0], -y_pixel_size, np.float32) else: # use proj to get granule corners # ------------------------------- x, y = proj(lon_box, lat_box) x_pixel_size = y_pixel_size = pixel_resolution min_x, max_x = np.min(x), np.max(x) min_y, max_y = np.min(y), np.max(y) # ----------- # area extent # ----------- area_extent = min_x, min_y, max_x, max_y x_size = int((area_extent[2] - area_extent[0]) / x_pixel_size) y_size = int((area_extent[3] - area_extent[1]) / y_pixel_size) # --------------- # area definition # --------------- return AreaDefinition(area_id, area_name, proj, area_dict, x_size, y_size, area_extent)