def _salem_grid_from_dataset(ds): """Seek for coordinates that Salem might have created. Current convention: x_coord, y_coord, pyproj_srs as attribute """ # Projection try: proj = ds.pyproj_srs except AttributeError: proj = None proj = gis.check_crs(proj) if proj is None: return None # Do we have some standard names as variable? vns = ds.variables.keys() xc = utils.str_in_list(vns, utils.valid_names['x_dim']) yc = utils.str_in_list(vns, utils.valid_names['y_dim']) # Sometimes there are more than one coordinates, one of which might have # more dims (e.g. lons in WRF files): take the first one with ndim = 1: x = None for xp in xc: if len(ds.variables[xp].shape) == 1: x = xp y = None for yp in yc: if len(ds.variables[yp].shape) == 1: y = yp if (x is None) or (y is None): return None # OK, get it x = ds.variables[x][:] y = ds.variables[y][:] # Make the grid dx = x[1] - x[0] dy = y[1] - y[0] args = dict(nxny=(x.shape[0], y.shape[0]), proj=proj, dxdy=(dx, dy), x0y0=(x[0], y[0])) return gis.Grid(**args)
def _lonlat_grid_from_dataset(ds): """Seek for longitude and latitude coordinates.""" # Do we have some standard names as variable? vns = ds.variables.keys() xc = utils.str_in_list(vns, utils.valid_names['x_dim']) yc = utils.str_in_list(vns, utils.valid_names['y_dim']) # Sometimes there are more than one coordinates, one of which might have # more dims (e.g. lons in WRF files): take the first one with ndim = 1: x = None for xp in xc: if len(ds.variables[xp].shape) == 1: x = xp y = None for yp in yc: if len(ds.variables[yp].shape) == 1: y = yp if (x is None) or (y is None): return None # OK, get it lon = ds.variables[x][:] lat = ds.variables[y][:] # double check for dubious variables if not utils.str_in_list([x], utils.valid_names['lon_var']) or \ not utils.str_in_list([y], utils.valid_names['lat_var']): # name not usual. see if at least the range follows some conv if (np.max(np.abs(lon)) > 360.1) or (np.max(np.abs(lat)) > 90.1): return None # Make the grid dx = lon[1] - lon[0] dy = lat[1] - lat[0] args = dict(nxny=(lon.shape[0], lat.shape[0]), proj=wgs84, dxdy=(dx, dy), x0y0=(lon[0], lat[0])) return gis.Grid(**args)
def _wrf_grid_from_dataset(ds): """Get the WRF projection out of the file.""" pargs = dict() if hasattr(ds, 'PROJ_ENVI_STRING'): # HAR and other TU Berlin files dx = ds.GRID_DX dy = ds.GRID_DY pargs['lat_1'] = ds.PROJ_STANDARD_PAR1 pargs['lat_2'] = ds.PROJ_STANDARD_PAR2 pargs['lat_0'] = ds.PROJ_CENTRAL_LAT pargs['lon_0'] = ds.PROJ_CENTRAL_LON pargs['center_lon'] = ds.PROJ_CENTRAL_LON if ds.PROJ_NAME == 'Lambert Conformal Conic': proj_id = 1 else: proj_id = 99 # pragma: no cover else: # Normal WRF file cen_lon = ds.CEN_LON cen_lat = ds.CEN_LAT dx = ds.DX dy = ds.DY pargs['lat_1'] = ds.TRUELAT1 pargs['lat_2'] = ds.TRUELAT2 pargs['lat_0'] = ds.MOAD_CEN_LAT pargs['lon_0'] = ds.STAND_LON pargs['center_lon'] = ds.CEN_LON proj_id = ds.MAP_PROJ if proj_id == 1: # Lambert p4 = '+proj=lcc +lat_1={lat_1} +lat_2={lat_2} ' \ '+lat_0={lat_0} +lon_0={lon_0} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' p4 = p4.format(**pargs) elif proj_id == 3: # Mercator p4 = '+proj=merc +lat_ts={lat_1} ' \ '+lon_0={center_lon} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' p4 = p4.format(**pargs) else: raise NotImplementedError('WRF proj not implemented yet: ' '{}'.format(proj_id)) proj = gis.check_crs(p4) if proj is None: raise RuntimeError('WRF proj not understood: {}'.format(p4)) # Here we have to accept xarray and netCDF4 datasets try: nx = len(ds.dimensions['west_east']) ny = len(ds.dimensions['south_north']) except AttributeError: # maybe an xarray dataset nx = ds.dims['west_east'] ny = ds.dims['south_north'] if hasattr(ds, 'PROJ_ENVI_STRING'): # HAR x0 = ds.GRID_X00 y0 = ds.GRID_Y00 else: # Normal WRF file e, n = gis.transform_proj(wgs84, proj, cen_lon, cen_lat) x0 = -(nx-1) / 2. * dx + e # DL corner y0 = -(ny-1) / 2. * dy + n # DL corner grid = gis.Grid(nxny=(nx, ny), ll_corner=(x0, y0), dxdy=(dx, dy), proj=proj) if tmp_check_wrf: # Temporary asserts if 'XLONG' in ds.variables: # Normal WRF mylon, mylat = grid.ll_coordinates reflon = ds.variables['XLONG'] reflat = ds.variables['XLAT'] if len(reflon.shape) == 3: reflon = reflon[0, :, :] reflat = reflat[0, :, :] assert np.allclose(reflon, mylon, atol=1e-4) assert np.allclose(reflat, mylat, atol=1e-4) if 'lon' in ds.variables: # HAR mylon, mylat = grid.ll_coordinates reflon = ds.variables['lon'] reflat = ds.variables['lat'] if len(reflon.shape) == 3: reflon = reflon[0, :, :] reflat = reflat[0, :, :] assert np.allclose(reflon, mylon, atol=1e-4) assert np.allclose(reflat, mylat, atol=1e-4) return grid
def _wrf_grid_from_dataset(ds): """Get the WRF projection out of the file.""" pargs = dict() if hasattr(ds, 'PROJ_ENVI_STRING'): # HAR and other TU Berlin files dx = ds.GRID_DX dy = ds.GRID_DY pargs['lat_1'] = ds.PROJ_STANDARD_PAR1 pargs['lat_2'] = ds.PROJ_STANDARD_PAR2 pargs['lat_0'] = ds.PROJ_CENTRAL_LAT pargs['lon_0'] = ds.PROJ_CENTRAL_LON pargs['center_lon'] = ds.PROJ_CENTRAL_LON if ds.PROJ_NAME in [ 'Lambert Conformal Conic', 'WRF Lambert Conformal' ]: proj_id = 1 else: proj_id = 99 # pragma: no cover else: # Normal WRF file cen_lon = ds.CEN_LON cen_lat = ds.CEN_LAT dx = ds.DX dy = ds.DY pargs['lat_1'] = ds.TRUELAT1 pargs['lat_2'] = ds.TRUELAT2 pargs['lat_0'] = ds.MOAD_CEN_LAT pargs['lon_0'] = ds.STAND_LON pargs['center_lon'] = ds.CEN_LON proj_id = ds.MAP_PROJ if proj_id == 1: # Lambert p4 = '+proj=lcc +lat_1={lat_1} +lat_2={lat_2} ' \ '+lat_0={lat_0} +lon_0={lon_0} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' p4 = p4.format(**pargs) elif proj_id == 2: # Polar stereo p4 = '+proj=stere +lat_ts={lat_1} +lon_0={lon_0} +lat_0=90.0' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' p4 = p4.format(**pargs) elif proj_id == 3: # Mercator p4 = '+proj=merc +lat_ts={lat_1} ' \ '+lon_0={center_lon} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' p4 = p4.format(**pargs) else: raise NotImplementedError('WRF proj not implemented yet: ' '{}'.format(proj_id)) proj = gis.check_crs(p4) if proj is None: raise RuntimeError('WRF proj not understood: {}'.format(p4)) # Here we have to accept xarray and netCDF4 datasets try: nx = len(ds.dimensions['west_east']) ny = len(ds.dimensions['south_north']) except AttributeError: # maybe an xarray dataset nx = ds.dims['west_east'] ny = ds.dims['south_north'] if hasattr(ds, 'PROJ_ENVI_STRING'): # HAR x0 = ds['west_east'][0] y0 = ds['south_north'][0] else: # Normal WRF file e, n = gis.transform_proj(wgs84, proj, cen_lon, cen_lat) x0 = -(nx - 1) / 2. * dx + e # DL corner y0 = -(ny - 1) / 2. * dy + n # DL corner grid = gis.Grid(nxny=(nx, ny), x0y0=(x0, y0), dxdy=(dx, dy), proj=proj) if tmp_check_wrf: # Temporary asserts if 'XLONG' in ds.variables: # Normal WRF reflon = ds.variables['XLONG'] reflat = ds.variables['XLAT'] elif 'XLONG_M' in ds.variables: # geo_em reflon = ds.variables['XLONG_M'] reflat = ds.variables['XLAT_M'] elif 'lon' in ds.variables: # HAR reflon = ds.variables['lon'] reflat = ds.variables['lat'] else: raise RuntimeError("couldn't test for correct WRF lon-lat") if len(reflon.shape) == 3: reflon = reflon[0, :, :] reflat = reflat[0, :, :] mylon, mylat = grid.ll_coordinates atol = 5e-3 if proj_id == 2 else 1e-3 check = np.isclose(reflon, mylon, atol=atol) if not np.alltrue(check): n_pix = np.sum(~check) maxe = np.max(np.abs(reflon - mylon)) if maxe < (360 - atol): warnings.warn('For {} grid points, the expected accuracy ({}) ' 'of our lons did not match those of the WRF ' 'file. Max error: {}'.format(n_pix, atol, maxe)) check = np.isclose(reflat, mylat, atol=atol) if not np.alltrue(check): n_pix = np.sum(~check) maxe = np.max(np.abs(reflat - mylat)) warnings.warn('For {} grid points, the expected accuracy ({}) ' 'of our lats did not match those of the WRF file. ' 'Max error: {}'.format(n_pix, atol, maxe)) return grid
def geogrid_simulator(fpath, do_maps=True, map_kwargs=None): """Emulates geogrid.exe, which is useful when defining new WRF domains. Parameters ---------- fpath: str path to a namelist.wps file do_maps: bool if you want the simulator to return you maps of the grids as well map_kwargs: dict kwargs to pass to salem.Map() Returns ------- (grids, maps) with: - grids: a list of Grids corresponding to the domains defined in the namelist - maps: a list of maps corresponding to the grids (if do_maps==True) """ with open(fpath) as f: lines = f.readlines() pargs = dict() for l in lines: s = l.split('=') if len(s) < 2: continue s0 = s[0].strip().upper() s1 = list(filter(None, s[1].strip().replace('\n', '').split(','))) if s0 == 'PARENT_ID': parent_id = [int(s) for s in s1] if s0 == 'PARENT_GRID_RATIO': parent_ratio = [int(s) for s in s1] if s0 == 'I_PARENT_START': i_parent_start = [int(s) for s in s1] if s0 == 'J_PARENT_START': j_parent_start = [int(s) for s in s1] if s0 == 'E_WE': e_we = [int(s) for s in s1] if s0 == 'E_SN': e_sn = [int(s) for s in s1] if s0 == 'DX': dx = float(s1[0]) if s0 == 'DY': dy = float(s1[0]) if s0 == 'MAP_PROJ': map_proj = s1[0].replace("'", '').strip().upper() if s0 == 'REF_LAT': pargs['lat_0'] = float(s1[0]) if s0 == 'REF_LON': pargs['ref_lon'] = float(s1[0]) if s0 == 'TRUELAT1': pargs['lat_1'] = float(s1[0]) if s0 == 'TRUELAT2': pargs['lat_2'] = float(s1[0]) if s0 == 'STAND_LON': pargs['lon_0'] = float(s1[0]) # Sometimes files are not complete pargs.setdefault('lon_0', pargs['ref_lon']) # define projection if map_proj == 'LAMBERT': pwrf = '+proj=lcc +lat_1={lat_1} +lat_2={lat_2} ' \ '+lat_0={lat_0} +lon_0={lon_0} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' pwrf = pwrf.format(**pargs) elif map_proj == 'MERCATOR': pwrf = '+proj=merc +lat_ts={lat_1} +lon_0={lon_0} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' pwrf = pwrf.format(**pargs) elif map_proj == 'POLAR': pwrf = '+proj=stere +lat_ts={lat_1} +lat_0=90.0 +lon_0={lon_0} ' \ '+x_0=0 +y_0=0 +a=6370000 +b=6370000' pwrf = pwrf.format(**pargs) else: raise NotImplementedError('WRF proj not implemented yet: ' '{}'.format(map_proj)) pwrf = gis.check_crs(pwrf) # get easting and northings from dom center (probably unnecessary here) e, n = pyproj.transform(wgs84, pwrf, pargs['ref_lon'], pargs['lat_0']) # LL corner nx, ny = e_we[0] - 1, e_sn[0] - 1 x0 = -(nx - 1) / 2. * dx + e # -2 because of staggered grid y0 = -(ny - 1) / 2. * dy + n # parent grid grid = gis.Grid(nxny=(nx, ny), x0y0=(x0, y0), dxdy=(dx, dy), proj=pwrf) # child grids out = [grid] for ips, jps, pid, ratio, we, sn in zip(i_parent_start, j_parent_start, parent_id, parent_ratio, e_we, e_sn): if ips == 1: continue ips -= 1 jps -= 1 we -= 1 sn -= 1 nx = we / ratio ny = sn / ratio if nx != (we / ratio): raise RuntimeError('e_we and ratios are incompatible: ' '(e_we - 1) / ratio must be integer!') if ny != (sn / ratio): raise RuntimeError('e_sn and ratios are incompatible: ' '(e_sn - 1) / ratio must be integer!') prevgrid = out[pid - 1] xx, yy = prevgrid.corner_grid.x_coord, prevgrid.corner_grid.y_coord dx = prevgrid.dx / ratio dy = prevgrid.dy / ratio grid = gis.Grid(nxny=(we, sn), x0y0=(xx[ips], yy[jps]), dxdy=(dx, dy), pixel_ref='corner', proj=pwrf) out.append(grid.center_grid) maps = None if do_maps: from salem import Map import shapely.geometry as shpg if map_kwargs is None: map_kwargs = {} maps = [] for i, g in enumerate(out): m = Map(g, **map_kwargs) for j in range(i + 1, len(out)): cg = out[j] left, right, bottom, top = cg.extent s = np.array([(left, bottom), (right, bottom), (right, top), (left, top)]) l1 = shpg.LinearRing(s) m.set_geometry(l1, crs=cg.proj, linewidth=(len(out) - j), zorder=5) maps.append(m) return out, maps