def _unrotate_equation(rotated_lons, rotated_lats, rotated_us, rotated_vs, pole_lon, pole_lat): # Perform a rotated-pole 'unrotate winds' transformation on arrays of # rotated-lat, rotated-lon, u and v. # This can be defined as an analytic function : cf. UMDP015 # Work out the rotation angles. lambda_angle = np.radians(pole_lon - 180.0) phi_angle = np.radians(90.0 - pole_lat) # Get the locations in true lats+lons. trueLongitude, trueLatitude = unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat) # Calculate inter-coordinate rotation coefficients. cos_rot = (np.cos(np.radians(rotated_lons)) * np.cos(np.radians(trueLongitude) - lambda_angle) + np.sin(np.radians(rotated_lons)) * np.sin(np.radians(trueLongitude) - lambda_angle) * np.cos(phi_angle)) sin_rot = -((np.sin(np.radians(trueLongitude) - lambda_angle) * np.sin(phi_angle)) / np.cos(np.radians(rotated_lats))) # Matrix-multiply to rotate the vectors. u_true = rotated_us * cos_rot - rotated_vs * sin_rot v_true = rotated_vs * cos_rot + rotated_us * sin_rot return u_true, v_true
def _unrotate_equation(rotated_lons, rotated_lats, rotated_us, rotated_vs, pole_lon, pole_lat): # Perform a rotated-pole 'unrotate winds' transformation on arrays of # rotated-lat, rotated-lon, u and v. # This can be defined as an analytic function : cf. UMDP015 # Work out the rotation angles. lambda_angle = np.radians(pole_lon - 180.0) phi_angle = np.radians(90.0 - pole_lat) # Get the locations in true lats+lons. trueLongitude, trueLatitude = unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat) # Calculate inter-coordinate rotation coefficients. cos_rot = np.cos(np.radians(rotated_lons)) * np.cos( np.radians(trueLongitude) - lambda_angle) + np.sin(np.radians(rotated_lons)) * np.sin( np.radians(trueLongitude) - lambda_angle) * np.cos(phi_angle) sin_rot = -((np.sin(np.radians(trueLongitude) - lambda_angle) * np.sin(phi_angle)) / np.cos(np.radians(rotated_lats))) # Matrix-multiply to rotate the vectors. u_true = rotated_us * cos_rot - rotated_vs * sin_rot v_true = rotated_vs * cos_rot + rotated_us * sin_rot return u_true, v_true
def unrotate_pole_update_cube(cube): lat = cube.coord('grid_latitude').points lon = cube.coord('grid_longitude').points cs = cube.coord_system('CoordSystem') if isinstance(cs, RotatedGeogCS): #print ' %s - %s - Unrotate pole %s' % (diag, experiment_id, cs) lons, lats = meshgrid(lon, lat) lons,lats = unrotate_pole(lons,lats, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) lon=lons[0] lat=lats[:,0] for i, coord in enumerate (cube.coords()): if coord.standard_name=='grid_latitude': lat_dim_coord_cube = i if coord.standard_name=='grid_longitude': lon_dim_coord_cube = i # IRIS unrotate_pole uses the default cartopy.crs.Geodetic, which is # WGS84 by default wgs84_cs = GeogCS(semi_major_axis= 6378137., inverse_flattening=298.257223563) cube.remove_coord('grid_latitude') cube.remove_coord('grid_longitude') cube.add_dim_coord(DimCoord(points=lat, standard_name='grid_latitude', units='degrees', coord_system=wgs84_cs), lat_dim_coord_cube) cube.add_dim_coord(DimCoord(points=lon, standard_name='grid_longitude', units='degrees', coord_system=wgs84_cs), lon_dim_coord_cube) return cube
def main(infile): thiscube = iris.load_cube(infile) xmin_rp = min(thiscube.coord('grid_longitude').points) - (0.036/2) xmax_rp = max(thiscube.coord('grid_longitude').points) + (0.036/2) ymin_rp = min(thiscube.coord('grid_latitude').points) - (0.036/2) ymax_rp = max(thiscube.coord('grid_latitude').points) + (0.036/2) pole_lat = thiscube.coord("grid_latitude").coord_system.grid_north_pole_latitude pole_lon = thiscube.coord("grid_latitude").coord_system.grid_north_pole_longitude bbox_ll = carto.unrotate_pole(np.array([xmin_rp,xmax_rp]), np.array([ymin_rp, ymax_rp]), pole_lon, pole_lat) latpts = np.arange(bbox_ll[1][0], bbox_ll[1][1], float(0.036)) lonpts = np.arange(bbox_ll[0][0], bbox_ll[0][1], float(0.036)) latitude = iris.coords.DimCoord(latpts, standard_name='latitude', units='degrees') longitude = iris.coords.DimCoord(lonpts, standard_name='longitude', units='degrees') ll = cs.GeogCS(semi_major_axis=6378137, semi_minor_axis=6356752.314245) llcube = iris.cube.Cube(np.zeros((latpts.size, lonpts.size), np.float32), dim_coords_and_dims=[(latitude, 0), (longitude, 1)]) llcube.coord_system = ll llcube.coord(axis='x').coord_system = ll llcube.coord(axis='y').coord_system = ll thiscube_ll = iris.experimental.regrid.regrid_bilinear_rectilinear_src_and_grid(thiscube, llcube) outfile = infile.replace('.pp','_ll.nc') iris.save(thiscube_ll, outfile) return(thiscube_ll)
def tilable(settings, data_var, query=None): file_names = get_file_names(settings["pattern"]) cubes = get_cubes(file_names[0], data_var) cube = cubes[0] # Search for lon/lat arrays lons, lats = None, None for name in dim_names(cube): if "latitude" in name: lats = cube.coord(name)[:].points if "longitude" in name: lons = cube.coord(name)[:].points # Constrain cube dims = { key: value for key, value in query.items() if key not in ["grid_latitude", "grid_longitude"] } kwargs = {} for key, value in query.items(): if key in ["grid_latitude", "grid_longitude"]: continue if "time" in key: kwargs[key] = fromisoformat(value) else: kwargs[key] = float(value) cube_slice = cube.extract(iris.Constraint(**kwargs)) coord_system = cube.coord_system() if isinstance(coord_system, RotatedGeogCS): # UKV Rotated pole support rotated_lons, rotated_lats = np.meshgrid(lons, lats) pole_lon = coord_system.grid_north_pole_longitude pole_lat = coord_system.grid_north_pole_latitude lons, lats = unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat) # # Roll input data into [-180, 180] range # if np.any(lons > 180.0): # shift_by = np.sum(lons > 180.0) # lons[lons > 180.0] -= 360. # lons = np.roll(lons, shift_by) # values = np.roll(values, shift_by, axis=1) if cube_slice.ndim != 2: raise Exception(f"unsupported ndim: {cube_slice.ndim}") values = cube_slice.data.copy() return { "latitude": lats, "longitude": lons, "values": values, "units": str(cube.units) }
def unroate_pole(resource, write_to_file=True): from numpy import reshape, repeat from iris.analysis import cartography as ct ds = Dataset(resource, mode='a') try: if 'rotated_latitude_longitude' in ds.variables: rp = ds.variables['rotated_latitude_longitude'] elif 'rotated_pole' in ds.variables: rp = ds.variables['rotated_pole'] else: logger.debug('rotated pole variable not found') pole_lat = rp.grid_north_pole_latitude pole_lon = rp.grid_north_pole_longitude except Exception as e: logger.debug('failed to find rotated_pole coorinates: %s' % e) try: if 'rlat' in ds.variables: rlats = ds.variables['rlat'] rlons = ds.variables['rlon'] if 'x' in ds.variables: rlats = ds.variables['y'] rlons = ds.variables['x'] except Exception as e: logger.debug('failed to read in rotated coordiates %s '% e) try: rlons_i = reshape(rlons,(1,len(rlons))) rlats_i = reshape(rlats,(len(rlats),1)) grid_rlats = repeat(rlats_i, (len(rlons)), axis=1) grid_rlons = repeat(rlons_i, (len(rlats)), axis=0) except Exception as e: logger.debug('failed to repeat coordinates %s'% e) lons, lats = ct.unrotate_pole(grid_rlons, grid_rlats, pole_lon, pole_lat) if write_to_file == True: lat = ds.createVariable('lat','f8',('rlat','rlon')) lon = ds.createVariable('lon','f8',('rlat','rlon')) lon.standard_name = "longitude" ; lon.long_name = "longitude coordinate" ; lon.units = 'degrees_east' lat.standard_name = "latitude" ; lat.long_name = "latitude coordinate" ; lat.units = 'degrees_north' lat[:] = lats lon[:] = lons ds.close() return lats, lons
def true_coords(cube): """Extract latitude and longitude from a cube with rotated coordinates Args: cube (iris.cube.Cube): Returns: tuple (numpy.ndarray, numpy.ndarray): N-dimensional arrays of longitude and latitude """ lon, lat = cartography.get_xy_grids(cube) cs = cube.coord_system() if cs.grid_mapping_name == 'rotated_latitude_longitude': lon, lat = cartography.unrotate_pole(lon, lat, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) return lon, lat
def test_2d_coords_contour(self): ny, nx = 4, 6 x1 = np.linspace(-20, 70, nx) y1 = np.linspace(10, 60, ny) data = np.zeros((ny, nx)) data.flat[:] = np.arange(nx * ny) % 7 cube = Cube(data, long_name='Odd data') x2, y2 = np.meshgrid(x1, y1) true_lons, true_lats = unrotate_pole(x2, y2, -130., 77.) co_x = AuxCoord(true_lons, standard_name='longitude', units='degrees') co_y = AuxCoord(true_lats, standard_name='latitude', units='degrees') cube.add_aux_coord(co_y, (0, 1)) cube.add_aux_coord(co_x, (0, 1)) ax = plt.axes(projection=ccrs.PlateCarree()) qplt.contourf(cube) ax.coastlines(color='red') ax.gridlines(draw_labels=True) ax.set_extent((0, 180, 0, 90)) self.check_graphic()
def setCubeRegularLatLon(cube, field, filename): # https://github.com/SciTools/iris/issues/448 glon = cube.coord('grid_longitude') rotated_lons = glon.points glat = cube.coord('grid_latitude') rotated_lats = glat.points pole_lat = glat.coord_system.grid_north_pole_latitude pole_lon = glat.coord_system.grid_north_pole_longitude rotated_lats, rotated_lons = meshgrid(rotated_lons, rotated_lats) lons, lats = icart.unrotate_pole(rotated_lons, rotated_lats, pole_lon, pole_lat) ## the below lat is 2D array regular2DLatitude = AuxCoord(lons, standard_name='longitude', units='degree_north') ## the below lon is 2D array regular2DLongitude = AuxCoord(lats, standard_name='latitude', units='degree_east') cube.remove_coord('grid_latitude') cube.remove_coord('grid_longitude') cube.add_aux_coord(regular2DLatitude, [0, 1]) cube.add_aux_coord(regular2DLongitude, [0, 1])
def unrotateGrid(data): ## ## ROTATE GRID BACK TO STANDARD ## import iris.analysis.cartography as ircrt ### LAM Configuration from suite u-bg610 dlat = 0.015714 dlon = 0.016334 frst_lat = -5.6112 frst_lon = 353.0345 pole_lat = 37.5000 pole_lon = 177.5000 rot_lat = data.coord('grid_latitude').points rot_lon = data.coord('grid_longitude').points rot_pole = data.coord('grid_latitude').coord_system.as_cartopy_crs() lon, lat = ircrt.unrotate_pole(rot_lon, rot_lat, frst_lon, frst_lat) # Print to check conversion print ('******') print ('Test of unrotated coordinate grid: ') print ('Rotated lon coord = ', rot_lon[0]) print ('Rotated lat coord = ', rot_lat[0]) print ('Lon = ', lon[0]) print ('Lat = ', lat[0]) print (' ') # ****** # lat/lon vertices of nest (output): 85.960219407715 80.41973098346767 49.567255645848604 -27.55740381723681 # ****** return lon, lat
def sample_2d_latlons(regional=False, rotated=False, transformed=False): """ Construct small 2d cubes with 2d X and Y coordinates. This makes cubes with 'expanded' coordinates (4 bounds per cell), analagous to ORCA data. The coordinates are always geographical, so either it has a coord system or they are "true" lats + lons. ( At present, they are always latitudes and longitudes, but maybe in a rotated system. ) The results always have fully contiguous bounds. Kwargs: * regional (bool): If False (default), results cover the whole globe, and there is implicit connectivity between rhs + lhs of the array. If True, coverage is regional and edges do not connect. * rotated (bool): If False, X and Y coordinates are true-latitudes and longitudes, with an implicit coordinate system (i.e. None). If True, the X and Y coordinates are lats+lons in a selected rotated-latlon coordinate system. * transformed (bool): Build coords from rotated coords as for 'rotated', but then replace their values with the equivalent "true" lats + lons, and no coord-system (defaults to true-latlon). In this case, the X and Y coords are no longer 'meshgrid' style, i.e. the points + bounds values vary in *both* dimensions. .. note:: 'transformed' is an alternative to 'rotated' : when 'transformed' is set, then 'rotated' has no effect. .. Some sample results printouts :: >>> print(sample_2d_latlons()) test_data / (unknown) (-- : 5; -- : 6) Auxiliary coordinates: latitude x x longitude x x >>> >>> print(sample_2d_latlons().coord(axis='x')[0, :2]) AuxCoord(array([ 37.5 , 93.75]), bounds=array([[ 0. , 65.625, 65.625, 0. ], [ 65.625, 121.875, 121.875, 65.625]]), standard_name='longitude', units=Unit('degrees')) >>> print(np.round(sample_2d_latlons().coord(axis='x').points, 3)) [[ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75]] >>> print(np.round(sample_2d_latlons().coord(axis='y').points, 3)) [[-85. -85. -85. -85. -85. -85. ] [-47.5 -47.5 -47.5 -47.5 -47.5 -47.5] [-10. -10. -10. -10. -10. -10. ] [ 27.5 27.5 27.5 27.5 27.5 27.5] [ 65. 65. 65. 65. 65. 65. ]] >>> print(np.round( sample_2d_latlons(rotated=True).coord(axis='x').points, 3)) [[ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75]] >>> print(sample_2d_latlons(rotated=True).coord(axis='y').coord_system) RotatedGeogCS(75.0, 120.0) >>> print( sample_2d_latlons(transformed=True).coord(axis='y').coord_system) None >>> print(np.round( sample_2d_latlons(transformed=True).coord(axis='x').points, 3)) [[ -50.718 -40.983 -46.74 -71.938 -79.293 -70.146] [ -29.867 17.606 77.936 157.145 -141.037 -93.172] [ -23.139 31.007 87.699 148.322 -154.639 -100.505] [ -16.054 41.218 92.761 143.837 -164.738 -108.105] [ 10.86 61.78 100.236 137.285 175.511 -135.446]] >>> print(np.round( sample_2d_latlons(transformed=True).coord(axis='y').points, 3)) [[-70.796 -74.52 -79.048 -79.26 -74.839 -70.96 ] [-34.99 -46.352 -59.721 -60.34 -47.305 -35.499] [ 1.976 -10.626 -22.859 -23.349 -11.595 1.37 ] [ 38.914 25.531 14.312 13.893 24.585 38.215] [ 74.197 60.258 51.325 51.016 59.446 73.268]] >>> """ def sample_cube(xargs, yargs): # Make a test cube with given latitude + longitude coordinates. # xargs/yargs are args for np.linspace (start, stop, N), to make the X # and Y coordinate points. x0, x1, nx = xargs y0, y1, ny = yargs # Data has cycling values, staggered a bit in successive rows. data = np.zeros((ny, nx)) data.flat[:] = np.arange(ny * nx) % (nx + 2) # Build a 2d cube with longitude + latitude coordinates. cube = Cube(data, long_name='test_data') x_pts = np.linspace(x0, x1, nx, endpoint=True) y_pts = np.linspace(y0, y1, ny, endpoint=True) co_x = DimCoord(x_pts, standard_name='longitude', units='degrees') co_y = DimCoord(y_pts, standard_name='latitude', units='degrees') cube.add_dim_coord(co_y, 0) cube.add_dim_coord(co_x, 1) return cube # Start by making a "normal" cube with separate 1-D X and Y coords. if regional: # Make a small regional cube. cube = sample_cube(xargs=(150., 243.75, 6), yargs=(-10., 40., 5)) # Add contiguous bounds. for ax in ('x', 'y'): cube.coord(axis=ax).guess_bounds() else: # Global data, but at a drastically reduced resolution. cube = sample_cube(xargs=(37.5, 318.75, 6), yargs=(-85., 65., 5)) # Make contiguous bounds and adjust outer edges to ensure it is global. for name in ('longitude', 'latitude'): coord = cube.coord(name) coord.guess_bounds() bds = coord.bounds.copy() # Make bounds global, by fixing lowest and uppermost values. if name == 'longitude': bds[0, 0] = 0.0 bds[-1, 1] = 360.0 else: bds[0, 0] = -90.0 bds[-1, 1] = 90.0 coord.bounds = bds # Now convert the 1-d coords to 2-d equivalents. # Get original 1-d coords. co_1d_x, co_1d_y = [cube.coord(axis=ax).copy() for ax in ('x', 'y')] # Calculate 2-d equivalents. co_2d_x, co_2d_y = grid_coords_2d_from_1d(co_1d_x, co_1d_y) # Remove the old grid coords. for coord in (co_1d_x, co_1d_y): cube.remove_coord(coord) # Add the new grid coords. for coord in (co_2d_x, co_2d_y): cube.add_aux_coord(coord, (0, 1)) if transformed or rotated: # Put the cube locations into a rotated coord system. pole_lat, pole_lon = 75.0, 120.0 if transformed: # Reproject coordinate values from rotated to true lat-lons. co_x, co_y = [cube.coord(axis=ax) for ax in ('x', 'y')] # Unrotate points. lons, lats = co_x.points, co_y.points lons, lats = unrotate_pole(lons, lats, pole_lon, pole_lat) co_x.points, co_y.points = lons, lats # Unrotate bounds. lons, lats = co_x.bounds, co_y.bounds # Note: save the shape, flatten + then re-apply the shape, because # "unrotate_pole" uses "cartopy.crs.CRS.transform_points", which # only works on arrays of 1 or 2 dimensions. shape = lons.shape lons, lats = unrotate_pole(lons.flatten(), lats.flatten(), pole_lon, pole_lat) co_x.bounds, co_y.bounds = lons.reshape(shape), lats.reshape(shape) else: # "Just" rotate operation : add a coord-system to each coord. cs = RotatedGeogCS(pole_lat, pole_lon) for coord in cube.coords(): coord.coord_system = cs return cube
fu = '%s%s/%s/%s%s.pp' % (pp_file_path, expmin1, experiment_id, experiment_id, diag) print experiment_id sys.stdout.flush() cube = iris.load_cube(fu, glob_tc) #cube = iris.load_cube(fu) cs = cube.coord_system('CoordSystem') lons, lats = np.meshgrid( cube.coord('grid_longitude').points, cube.coord('grid_latitude').points) unrot_lons, unrot_lats = unrotate_pole(lons, lats, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) latmin = unrot_lats.min() latmax = unrot_lats.max() lonmin = unrot_lons.min() lonmax = unrot_lons.max() lat_constraint = iris.Constraint( grid_latitude=lambda la: latmin <= la.point <= latmax) lon_constraint = iris.Constraint( grid_longitude=lambda lo: lonmin <= lo.point <= lonmax) #time_constraint= regrid_cube = regrid_cube_init.extract(lat_constraint & lon_constraint) #pdb.set_trace()
expmin1 = experiment_id[:-1] fu = '%s%s/%s/%s%s.pp' % (pp_file_path, expmin1, experiment_id, experiment_id, diag) print experiment_id sys.stdout.flush() cube = iris.load_cube(fu, glob_tc) #cube = iris.load_cube(fu) cs = cube.coord_system('CoordSystem') lons, lats = np.meshgrid(cube.coord('grid_longitude').points, cube.coord('grid_latitude').points) unrot_lons, unrot_lats = unrotate_pole(lons, lats, cs.grid_north_pole_longitude, cs.grid_north_pole_latitude) latmin = unrot_lats.min() latmax = unrot_lats.max() lonmin = unrot_lons.min() lonmax = unrot_lons.max() lat_constraint=iris.Constraint(grid_latitude= lambda la: latmin <= la.point <= latmax) lon_constraint=iris.Constraint(grid_longitude= lambda lo: lonmin <= lo.point <= lonmax) #time_constraint= regrid_cube = regrid_cube_init.extract(lat_constraint & lon_constraint) #pdb.set_trace() mean_list=[]
def unrotate_pole(resource, write_to_file=True): from numpy import reshape, repeat from iris.analysis import cartography as ct ds = Dataset(resource, mode="a") if "lat" in ds.variables.keys(): logger.info("coordinates already unrotated") lats = ds.variables["lat"][:] lons = ds.variables["lon"][:] else: try: if "rotated_latitude_longitude" in ds.variables: rp = ds.variables["rotated_latitude_longitude"] elif "rotated_pole" in ds.variables: rp = ds.variables["rotated_pole"] else: logger.debug("rotated pole variable not found") pole_lat = rp.grid_north_pole_latitude pole_lon = rp.grid_north_pole_longitude except Exception as e: logger.debug("failed to find rotated_pole coordinates: %s" % e) try: if "rlat" in ds.variables: rlats = ds.variables["rlat"] rlons = ds.variables["rlon"] if "x" in ds.variables: rlats = ds.variables["y"] rlons = ds.variables["x"] except Exception as e: logger.debug("failed to read in rotated coordiates %s " % e) try: rlons_i = reshape(rlons, (1, len(rlons))) rlats_i = reshape(rlats, (len(rlats), 1)) grid_rlats = repeat(rlats_i, (len(rlons)), axis=1) grid_rlons = repeat(rlons_i, (len(rlats)), axis=0) except Exception as e: logger.debug("failed to repeat coordinates %s" % e) lons, lats = ct.unrotate_pole(grid_rlons, grid_rlats, pole_lon, pole_lat) if write_to_file == True: lat = ds.createVariable("lat", "f8", ("rlat", "rlon")) lon = ds.createVariable("lon", "f8", ("rlat", "rlon")) lon.standard_name = "longitude" lon.long_name = "longitude coordinate" lon.units = "degrees_east" lat.standard_name = "latitude" lat.long_name = "latitude coordinate" lat.units = "degrees_north" lat[:] = lats lon[:] = lons ds.close() return lats, lons
def sample_2d_latlons(regional=False, rotated=False, transformed=False): """ Construct small 2d cubes with 2d X and Y coordinates. This makes cubes with 'expanded' coordinates (4 bounds per cell), analagous to ORCA data. The coordinates are always geographical, so either it has a coord system or they are "true" lats + lons. ( At present, they are always latitudes and longitudes, but maybe in a rotated system. ) The results always have fully contiguous bounds. Kwargs: * regional (bool): If False (default), results cover the whole globe, and there is implicit connectivity between rhs + lhs of the array. If True, coverage is regional and edges do not connect. * rotated (bool): If False, X and Y coordinates are true-latitudes and longitudes, with an implicit coordinate system (i.e. None). If True, the X and Y coordinates are lats+lons in a selected rotated-latlon coordinate system. * transformed (bool): Build coords from rotated coords as for 'rotated', but then replace their values with the equivalent "true" lats + lons, and no coord-system (defaults to true-latlon). In this case, the X and Y coords are no longer 'meshgrid' style, i.e. the points + bounds values vary in *both* dimensions. .. note:: 'transformed' is an alternative to 'rotated' : when 'transformed' is set, then 'rotated' has no effect. .. Some sample results printouts :: >>> print(sample_2d_latlons()) test_data / (unknown) (-- : 5; -- : 6) Auxiliary coordinates: latitude x x longitude x x >>> >>> print(sample_2d_latlons().coord(axis='x')[0, :2]) AuxCoord(array([ 37.5 , 93.75]), bounds=array([[ 0. , 65.625, 65.625, 0. ], [ 65.625, 121.875, 121.875, 65.625]]), standard_name='longitude', units=Unit('degrees')) >>> print(np.round(sample_2d_latlons().coord(axis='x').points, 3)) [[ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75]] >>> print(np.round(sample_2d_latlons().coord(axis='y').points, 3)) [[-85. -85. -85. -85. -85. -85. ] [-47.5 -47.5 -47.5 -47.5 -47.5 -47.5] [-10. -10. -10. -10. -10. -10. ] [ 27.5 27.5 27.5 27.5 27.5 27.5] [ 65. 65. 65. 65. 65. 65. ]] >>> print(np.round( sample_2d_latlons(rotated=True).coord(axis='x').points, 3)) [[ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75] [ 37.5 93.75 150. 206.25 262.5 318.75]] >>> print(sample_2d_latlons(rotated=True).coord(axis='y').coord_system) RotatedGeogCS(75.0, 120.0) >>> print( sample_2d_latlons(transformed=True).coord(axis='y').coord_system) None >>> print(np.round( sample_2d_latlons(transformed=True).coord(axis='x').points, 3)) [[ -50.718 -40.983 -46.74 -71.938 -79.293 -70.146] [ -29.867 17.606 77.936 157.145 -141.037 -93.172] [ -23.139 31.007 87.699 148.322 -154.639 -100.505] [ -16.054 41.218 92.761 143.837 -164.738 -108.105] [ 10.86 61.78 100.236 137.285 175.511 -135.446]] >>> print(np.round( sample_2d_latlons(transformed=True).coord(axis='y').points, 3)) [[-70.796 -74.52 -79.048 -79.26 -74.839 -70.96 ] [-34.99 -46.352 -59.721 -60.34 -47.305 -35.499] [ 1.976 -10.626 -22.859 -23.349 -11.595 1.37 ] [ 38.914 25.531 14.312 13.893 24.585 38.215] [ 74.197 60.258 51.325 51.016 59.446 73.268]] >>> """ def sample_cube(xargs, yargs): # Make a test cube with given latitude + longitude coordinates. # xargs/yargs are args for np.linspace (start, stop, N), to make the X # and Y coordinate points. x0, x1, nx = xargs y0, y1, ny = yargs # Data has cycling values, staggered a bit in successive rows. data = np.zeros((ny, nx)) data.flat[:] = np.arange(ny * nx) % (nx + 2) # Build a 2d cube with longitude + latitude coordinates. cube = Cube(data, long_name="test_data") x_pts = np.linspace(x0, x1, nx, endpoint=True) y_pts = np.linspace(y0, y1, ny, endpoint=True) co_x = DimCoord(x_pts, standard_name="longitude", units="degrees") co_y = DimCoord(y_pts, standard_name="latitude", units="degrees") cube.add_dim_coord(co_y, 0) cube.add_dim_coord(co_x, 1) return cube # Start by making a "normal" cube with separate 1-D X and Y coords. if regional: # Make a small regional cube. cube = sample_cube(xargs=(150.0, 243.75, 6), yargs=(-10.0, 40.0, 5)) # Add contiguous bounds. for ax in ("x", "y"): cube.coord(axis=ax).guess_bounds() else: # Global data, but at a drastically reduced resolution. cube = sample_cube(xargs=(37.5, 318.75, 6), yargs=(-85.0, 65.0, 5)) # Make contiguous bounds and adjust outer edges to ensure it is global. for name in ("longitude", "latitude"): coord = cube.coord(name) coord.guess_bounds() bds = coord.bounds.copy() # Make bounds global, by fixing lowest and uppermost values. if name == "longitude": bds[0, 0] = 0.0 bds[-1, 1] = 360.0 else: bds[0, 0] = -90.0 bds[-1, 1] = 90.0 coord.bounds = bds # Now convert the 1-d coords to 2-d equivalents. # Get original 1-d coords. co_1d_x, co_1d_y = [cube.coord(axis=ax).copy() for ax in ("x", "y")] # Calculate 2-d equivalents. co_2d_x, co_2d_y = grid_coords_2d_from_1d(co_1d_x, co_1d_y) # Remove the old grid coords. for coord in (co_1d_x, co_1d_y): cube.remove_coord(coord) # Add the new grid coords. for coord in (co_2d_x, co_2d_y): cube.add_aux_coord(coord, (0, 1)) if transformed or rotated: # Put the cube locations into a rotated coord system. pole_lat, pole_lon = 75.0, 120.0 if transformed: # Reproject coordinate values from rotated to true lat-lons. co_x, co_y = [cube.coord(axis=ax) for ax in ("x", "y")] # Unrotate points. lons, lats = co_x.points, co_y.points lons, lats = unrotate_pole(lons, lats, pole_lon, pole_lat) co_x.points, co_y.points = lons, lats # Unrotate bounds. lons, lats = co_x.bounds, co_y.bounds # Note: save the shape, flatten + then re-apply the shape, because # "unrotate_pole" uses "cartopy.crs.CRS.transform_points", which # only works on arrays of 1 or 2 dimensions. shape = lons.shape lons, lats = unrotate_pole(lons.flatten(), lats.flatten(), pole_lon, pole_lat) co_x.bounds, co_y.bounds = lons.reshape(shape), lats.reshape(shape) else: # "Just" rotate operation : add a coord-system to each coord. cs = RotatedGeogCS(pole_lat, pole_lon) for coord in cube.coords(): coord.coord_system = cs return cube
def unrotate_pole(resource, write_to_file=False): """ Calculates the unrotatated coordinates for a rotated pole grid :param resource: netCDF file or list of files of one datatset :param write_to_file: calculated values will be written to file if True (default=False) :return list: lats, lons """ from numpy import reshape, repeat from iris.analysis import cartography as ct if len(resource) == 1: ds = Dataset(resource[0]) else: ds = MFDataset(resource) # ds = MFDataset(resource) if 'lat' in ds.variables.keys(): LOGGER.info('file include unrotated coordinate values') lats = ds.variables['lat'][:] lons = ds.variables['lon'][:] else: try: if 'rotated_latitude_longitude' in ds.variables: rp = ds.variables['rotated_latitude_longitude'] elif 'rotated_pole' in ds.variables: rp = ds.variables['rotated_pole'] else: LOGGER.debug('rotated pole variable not found') pole_lat = rp.grid_north_pole_latitude pole_lon = rp.grid_north_pole_longitude except: LOGGER.exception('failed to find rotated_pole coordinates') try: if 'rlat' in ds.variables: rlats = ds.variables['rlat'] rlons = ds.variables['rlon'] if 'x' in ds.variables: rlats = ds.variables['y'] rlons = ds.variables['x'] except: LOGGER.exception('failed to read in rotated coordiates') try: rlons_i = reshape(rlons, (1, len(rlons))) rlats_i = reshape(rlats, (len(rlats), 1)) grid_rlats = repeat(rlats_i, (len(rlons)), axis=1) grid_rlons = repeat(rlons_i, (len(rlats)), axis=0) except: LOGGER.execption('failed to repeat coordinates') lons, lats = ct.unrotate_pole(grid_rlons, grid_rlats, pole_lon, pole_lat) if write_to_file is True: lat = ds.createVariable('lat', 'f8', ('rlat', 'rlon')) lon = ds.createVariable('lon', 'f8', ('rlat', 'rlon')) lon.standard_name = "longitude" lon.long_name = "longitude coordinate" lon.units = 'degrees_east' lat.standard_name = "latitude" lat.long_name = "latitude coordinate" lat.units = 'degrees_north' lat[:] = lats lon[:] = lons ds.close() return lats, lons
def extract_section(cube, waypoints, n_sample_points=30): """ Extract a section from the cube given lat-lon waypoints. This works on any cube with x and y dimension coordinates. If the cube also contains Z or T etc dimension coordinates then these are retained in the extracted section. :param cube: Iris cube :param waypoints: List of dictionaries, whose keys are 'latitude' and 'longitude' and whose values are the lat/lon points that should be included in the section. :param n_sample_points: Number of sample points to include in section :returns: An iris cube which no longer has X and Y as dimension coordinates but instead has an 'i_sample_point' coordinate. If latitude and longitude were not in the original cube, they are added to to the new section cube. Load sample data - a gridded surface-only, time-varying cube: >>> import config >>> sample_datadir = config.SAMPLE_DATADIR+'gridded_cube_list/' >>> cube = iris.load_cube(sample_datadir+'aqum_oper_1days.nc', ... 'mass_concentration_of_ozone_in_air') >>> print(cube) # doctest: +NORMALIZE_WHITESPACE mass_concentration_of_ozone_in_air / (ug/m3) (time: 25; grid_latitude: 182; \ grid_longitude: 146) Dimension coordinates: time x - - grid_latitude - x - grid_longitude - - x Auxiliary coordinates: forecast_period x - - surface_altitude - x x Derived coordinates: altitude - x x Scalar coordinates: atmosphere_hybrid_height_coordinate: 20.000338 m, \ bound=(0.0, 49.998882) m forecast_day: 1.0 Days model_level_number: 1 sigma: 0.9977165, bound=(1.0, 0.99429625) Attributes: Conventions: CF-1.5 STASH: m01s34i001 label: aqum_oper short_name: O3 source: Data from Met Office Unified Model Cell methods: mean: time (1 hour) Set up waypoints list: >>> waypoints = [ ... {'latitude': 50.8, 'longitude': -1.8}, ... {'latitude': 51.2, 'longitude': -1.2}, ... {'latitude': 51.4, 'longitude': -0.9}] Extract section along these waypoints: >>> section = extract_section(cube, waypoints) >>> print(section) mass_concentration_of_ozone_in_air / (ug/m3) (time: 25; i_sample_point: 30) Dimension coordinates: time x - i_sample_point - x Auxiliary coordinates: forecast_period x - grid_latitude - x grid_longitude - x latitude - x longitude - x surface_altitude - x Derived coordinates: altitude - x Scalar coordinates: atmosphere_hybrid_height_coordinate: 20.000338 m, \ bound=(0.0, 49.998882) m forecast_day: 1.0 Days model_level_number: 1 sigma: 0.9977165, bound=(1.0, 0.99429625) Attributes: Conventions: CF-1.5 STASH: m01s34i001 label: aqum_oper short_name: O3 source: Data from Met Office Unified Model Cell methods: mean: time (1 hour) """ xcoord, ycoord = guess_coord_names(cube, ['X', 'Y']) if None in [xcoord, ycoord]: raise ValueError('Can not calculate section for this cube') lat_lon_coord_system = iris.coord_systems.GeogCS( semi_major_axis=iris.fileformats.pp.EARTH_RADIUS) lons = np.array([waypoint['longitude'] for waypoint in waypoints]) lats = np.array([waypoint['latitude'] for waypoint in waypoints]) #Calculate waypoints in cube coordinates if xcoord == 'grid_longitude' and ycoord == 'grid_latitude': #Rotated pole pole_lon = cube.coord(xcoord).coord_system.grid_north_pole_longitude pole_lat = cube.coord(ycoord).coord_system.grid_north_pole_latitude #Perform rotation rot_lons, rot_lats = rotate_pole(lons, lats, pole_lon, pole_lat) #Put back into waypoints format cube_waypoints = [{ xcoord: lon, ycoord: lat } for lat, lon in zip(rot_lats, rot_lons)] elif xcoord == 'projection_x_coordinate' and \ ycoord == 'projection_y_coordinate': #Other coordinate system (note this may work for x/ycoords other than #those considered here ll_crs = lat_lon_coord_system.as_cartopy_crs() cube_crs = cube.coord(xcoord).coord_system.as_cartopy_crs() #Convert to lat/lon points cube_lonlats = ll_crs.transform_points(cube_crs, lons, lats) cube_lons = cube_lonlats[:, 0] cube_lats = cube_lonlats[:, 1] #Put back into waypoints format cube_waypoints = [{ xcoord: lon, ycoord: lat } for lat, lon in zip(cube_lats, cube_lons)] elif xcoord == 'longitude' and ycoord == 'latitude': cube_waypoints = waypoints else: raise ValueError('Unable to convert cube x/y points to lat/lon') #Specify the trajectory (straight-line) by giving the # start and end of the line and specifying how many points there # should be on the line (in this case 30) sample_points = trajectory.Trajectory(cube_waypoints, sample_count=n_sample_points) #Extract the data from the cube at the sample points #(Note this only works with iris vn2.2+) section = sample_points.interpolate(cube) #Rename new index coordinate section.coord('index').rename('i_sample_point') #Also add latitude and longitude coords to section #if not in original cube if xcoord != 'longitude' and ycoord != 'latitude': cube_lons_samplepts = np.array( [d[xcoord] for d in sample_points.sampled_points]) cube_lats_samplepts = np.array( [d[ycoord] for d in sample_points.sampled_points]) if xcoord == 'grid_longitude' and ycoord == 'grid_latitude': section_lons, section_lats = unrotate_pole(cube_lons_samplepts, cube_lats_samplepts, pole_lon, pole_lat) elif xcoord == 'projection_x_coordinate' and \ ycoord == 'projection_y_coordinate': #Other coordinate system (note this may work for x/ycoords other than #those considered here lonlats = cube_crs.transform_points(ll_crs, cube_lons_samplepts, cube_lats_samplepts) section_lons = lonlats[:, 0] section_lats = lonlats[:, 1] #Set up lat/lon coords and add as aux coords lon_coord = iris.coords.AuxCoord(section_lons, standard_name='longitude', units='degrees', coord_system=lat_lon_coord_system) lat_coord = iris.coords.AuxCoord(section_lats, standard_name='latitude', units='degrees', coord_system=lat_lon_coord_system) section.add_aux_coord(lon_coord, section.coord_dims(xcoord)) section.add_aux_coord(lat_coord, section.coord_dims(ycoord)) return section
def unrotate_pole(resource, write_to_file=True): """ Calculates the unrotatated coordinates for a rotated pole grid :param resource: netCDF file :return list: lats, lons """ from numpy import reshape, repeat from iris.analysis import cartography as ct ds = Dataset(resource, mode='a') if 'lat' in ds.variables.keys(): LOGGER.info('coordinates already unrotated') lats = ds.variables['lat'][:] lons = ds.variables['lon'][:] else: try: if 'rotated_latitude_longitude' in ds.variables: rp = ds.variables['rotated_latitude_longitude'] elif 'rotated_pole' in ds.variables: rp = ds.variables['rotated_pole'] else: LOGGER.debug('rotated pole variable not found') pole_lat = rp.grid_north_pole_latitude pole_lon = rp.grid_north_pole_longitude except Exception as e: LOGGER.debug('failed to find rotated_pole coordinates: %s' % e) try: if 'rlat' in ds.variables: rlats = ds.variables['rlat'] rlons = ds.variables['rlon'] if 'x' in ds.variables: rlats = ds.variables['y'] rlons = ds.variables['x'] except Exception as e: LOGGER.debug('failed to read in rotated coordiates %s' % e) try: rlons_i = reshape(rlons, (1, len(rlons))) rlats_i = reshape(rlats, (len(rlats), 1)) grid_rlats = repeat(rlats_i, (len(rlons)), axis=1) grid_rlons = repeat(rlons_i, (len(rlats)), axis=0) except Exception as e: LOGGER.debug('failed to repeat coordinates %s' % e) lons, lats = ct.unrotate_pole(grid_rlons, grid_rlats, pole_lon, pole_lat) if write_to_file is True: lat = ds.createVariable('lat', 'f8', ('rlat', 'rlon')) lon = ds.createVariable('lon', 'f8', ('rlat', 'rlon')) lon.standard_name = "longitude" lon.long_name = "longitude coordinate" lon.units = 'degrees_east' lat.standard_name = "latitude" lat.long_name = "latitude coordinate" lat.units = 'degrees_north' lat[:] = lats lon[:] = lons ds.close() return lats, lons