def add_sea(exposures, sea_res, scheduler=None): """Add sea to geometry's surroundings with given resolution. region_id set to -1 and other variables to 0. Parameters ---------- exposures : Exposures the Exposures object without sea surroundings. sea_res : tuple (float,float) (sea_coast_km, sea_res_km), where first parameter is distance from coast to fill with water and second parameter is resolution between sea points scheduler : str, optional used for dask map_partitions. “threads”, “synchronous” or “processes” Returns: Exposures """ LOGGER.info( "Adding sea at %s km resolution and %s km distance from coast.", str(sea_res[1]), str(sea_res[0])) sea_res = (sea_res[0] / ONE_LAT_KM, sea_res[1] / ONE_LAT_KM) min_lat = max(-90, float(exposures.gdf.latitude.min()) - sea_res[0]) max_lat = min(90, float(exposures.gdf.latitude.max()) + sea_res[0]) min_lon = max(-180, float(exposures.gdf.longitude.min()) - sea_res[0]) max_lon = min(180, float(exposures.gdf.longitude.max()) + sea_res[0]) lat_arr = np.arange(min_lat, max_lat + sea_res[1], sea_res[1]) lon_arr = np.arange(min_lon, max_lon + sea_res[1], sea_res[1]) lon_mgrid, lat_mgrid = np.meshgrid(lon_arr, lat_arr) lon_mgrid, lat_mgrid = lon_mgrid.ravel(), lat_mgrid.ravel() on_land = ~u_coord.coord_on_land(lat_mgrid, lon_mgrid) sea_exp_gdf = GeoDataFrame() sea_exp_gdf['latitude'] = lat_mgrid[on_land] sea_exp_gdf['longitude'] = lon_mgrid[on_land] sea_exp_gdf['region_id'] = np.zeros(sea_exp_gdf.latitude.size, int) - 1 if 'geometry' in exposures.gdf.columns: u_coord.set_df_geometry_points(sea_exp_gdf, crs=exposures.crs, scheduler=scheduler) for var_name in exposures.gdf.columns: if var_name not in ('latitude', 'longitude', 'region_id', 'geometry'): sea_exp_gdf[var_name] = np.zeros(sea_exp_gdf.latitude.size, exposures.gdf[var_name].dtype) return Exposures(pd.concat([exposures.gdf, sea_exp_gdf], ignore_index=True, sort=False), crs=exposures.crs, ref_year=exposures.ref_year, value_unit=exposures.value_unit, meta=exposures.meta, tag=exposures.tag)
def test_on_land_pass(self): """check point on land with 1:50.000.000 resolution.""" lat = np.array([28.203216, 28.555994, 28.860875]) lon = np.array([-16.567489, -18.554130, -9.532476]) res = coord_on_land(lat, lon) self.assertEqual(res.size, 3) self.assertTrue(res[0]) self.assertFalse(res[1]) self.assertTrue(res[2])
def set_on_land(self, scheduler=None): """Set on_land attribute for every pixel or point Parameters: scheduler (str): used for dask map_partitions. “threads”, “synchronous” or “processes” """ ne_geom = self._ne_crs_geom(scheduler) LOGGER.debug('Setting on_land %s points.', str(self.lat.size)) self.on_land = coord_on_land(ne_geom.geometry[:].y.values, ne_geom.geometry[:].x.values)
def test_add_sea_pass(self): """Test add_sea function with fake data.""" exp = Exposures() exp.gdf['value'] = np.arange(0, 1.0e6, 1.0e5) min_lat, max_lat = 27.5, 30 min_lon, max_lon = -18, -12 exp.gdf['latitude'] = np.linspace(min_lat, max_lat, 10) exp.gdf['longitude'] = np.linspace(min_lon, max_lon, 10) exp.gdf['region_id'] = np.ones(10) exp.gdf['if_TC'] = np.ones(10) exp.ref_year = 2015 exp.value_unit = 'XSD' exp.check() sea_coast = 100 sea_res_km = 50 sea_res = (sea_coast, sea_res_km) exp_sea = add_sea(exp, sea_res) exp_sea.check() sea_coast /= ONE_LAT_KM sea_res_km /= ONE_LAT_KM min_lat = min_lat - sea_coast max_lat = max_lat + sea_coast min_lon = min_lon - sea_coast max_lon = max_lon + sea_coast self.assertEqual(np.min(exp_sea.gdf.latitude), min_lat) self.assertEqual(np.min(exp_sea.gdf.longitude), min_lon) self.assertTrue( np.array_equal(exp_sea.gdf.value.values[:10], np.arange(0, 1.0e6, 1.0e5))) self.assertEqual(exp_sea.ref_year, exp.ref_year) self.assertEqual(exp_sea.value_unit, exp.value_unit) on_sea_lat = exp_sea.gdf.latitude.values[11:] on_sea_lon = exp_sea.gdf.longitude.values[11:] res_on_sea = coord_on_land(on_sea_lat, on_sea_lon) res_on_sea = ~res_on_sea self.assertTrue(np.all(res_on_sea)) dist = DistanceMetric.get_metric('haversine') self.assertAlmostEqual( dist.pairwise([ [ exp_sea.gdf.longitude.values[-1], exp_sea.gdf.latitude.values[-1] ], [ exp_sea.gdf.longitude.values[-2], exp_sea.gdf.latitude.values[-2] ], ])[0][1], sea_res_km)
def test_calc_orig_lf(self): """ Test _calc_orig_lf for andrew tropical cyclone.""" tc_track = TCTracks() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) track = tc_track.get_track() track['on_land'] = ('time', coord_on_land(track.lat.values, track.lon.values)) sea_land_idx = np.where(np.diff(track.on_land.astype(int)) == 1)[0] orig_lf = tc._calc_orig_lf(track, sea_land_idx) self.assertEqual(orig_lf.shape, (sea_land_idx.size, 2)) self.assertTrue(np.array_equal(orig_lf[0], np.array([25.5, -80.25]))) self.assertTrue(np.array_equal(orig_lf[1], np.array([29.65, -91.5])))
def add_sea(exposures, sea_res): """ Add sea to geometry's surroundings with given resolution. region_id set to -1 and other variables to 0. Parameters: sea_res (tuple): (sea_coast_km, sea_res_km), where first parameter is distance from coast to fill with water and second parameter is resolution between sea points Returns: Exposures """ LOGGER.info( "Adding sea at %s km resolution and %s km distance from coast.", str(sea_res[1]), str(sea_res[0])) sea_res = (sea_res[0] / ONE_LAT_KM, sea_res[1] / ONE_LAT_KM) min_lat = max(-90, float(exposures.latitude.min()) - sea_res[0]) max_lat = min(90, float(exposures.latitude.max()) + sea_res[0]) min_lon = max(-180, float(exposures.longitude.min()) - sea_res[0]) max_lon = min(180, float(exposures.longitude.max()) + sea_res[0]) lat_arr = np.arange(min_lat, max_lat + sea_res[1], sea_res[1]) lon_arr = np.arange(min_lon, max_lon + sea_res[1], sea_res[1]) lon_mgrid, lat_mgrid = np.meshgrid(lon_arr, lat_arr) lon_mgrid, lat_mgrid = lon_mgrid.ravel(), lat_mgrid.ravel() on_land = np.logical_not(co.coord_on_land(lat_mgrid, lon_mgrid)) sea_exp = Exposures() sea_exp['latitude'] = lat_mgrid[on_land] sea_exp['longitude'] = lon_mgrid[on_land] sea_exp['region_id'] = np.zeros(sea_exp.latitude.size, int) - 1 if 'geometry' in exposures.columns: sea_exp.set_geometry_points() for var_name in exposures.columns: if var_name not in ('latitude', 'longitude', 'region_id', 'geometry'): sea_exp[var_name] = np.zeros(sea_exp.latitude.size, exposures[var_name].dtype) return pd.concat([exposures, sea_exp], ignore_index=True, sort=False)
def test_dist_since_lf_pass(self): """ Test _dist_since_lf for andrew tropical cyclone.""" tc_track = TCTracks() tc_track.read_processed_ibtracs_csv(TC_ANDREW_FL) track = tc_track.get_track() track['on_land'] = ('time', coord_on_land(track.lat.values, track.lon.values)) track['dist_since_lf'] = ('time', tc._dist_since_lf(track)) self.assertTrue(np.all(np.isnan(track.dist_since_lf.values[track.on_land == False]))) self.assertEqual(track.dist_since_lf.values[track.on_land == False].size, 38) self.assertTrue(track.dist_since_lf.values[-1] > dist_to_coast(track.lat.values[-1], track.lon.values[-1])/1000) self.assertEqual(1020.5431562223974, track['dist_since_lf'].values[-1]) # check distances on land always increase, in second landfall dist_on_land = track.dist_since_lf.values[track.on_land] self.assertTrue(np.all(np.diff(dist_on_land)[1:] > 0))
def set_on_land(self): """ Set on_land attribute for every pixel or point """ lon, lat = self._ne_crs_xy() LOGGER.debug('Setting on_land %s points.', str(self.lat.size)) self.on_land = coord_on_land(lat, lon)
def set_on_land(self): """ Add the on_land attribute, i.e. if a centroid is on land. """ self.on_land = coord_on_land(self.lat, self.lon)
def set_from_isimip_netcdf(self, input_dir=None, filename=None, bbox=None, yearrange=None, ag_model=None, cl_model=None, bias_corr=None, scenario=None, soc=None, co2=None, crop=None, irr=None, fn_str_var=None): """Wrapper to fill hazard from crop yield NetCDF file. Build and tested for output from ISIMIP2 and ISIMIP3, but might also work for other NetCDF containing gridded crop model output from other sources. Parameters: input_dir (Path or str): path to input data directory, default: {CONFIG.exposures.crop_production.local_data}/Input/Exposure filename (string): name of netcdf file in input_dir. If filename is given, the other parameters specifying the model run are not required! bbox (list of four floats): bounding box: [lon min, lat min, lon max, lat max] yearrange (int tuple): year range for hazard set, f.i. (1976, 2005) ag_model (str): abbrev. agricultural model (only when input_dir is selected) f.i. 'clm-crop', 'gepic','lpjml','pepic' cl_model (str): abbrev. climate model (only when input_dir is selected) f.i. ['gfdl-esm2m', 'hadgem2-es','ipsl-cm5a-lr','miroc5' bias_corr (str): bias correction of climate forcing, f.i. 'ewembi' (ISIMIP2b, default) or 'w5e5' (ISIMIP3b) scenario (str): climate change scenario (only when input_dir is selected) f.i. 'historical' or 'rcp60' or 'ISIMIP2a' soc (str): socio-economic trajectory (only when input_dir is selected) f.i. '2005soc' or 'histsoc' co2 (str): CO2 forcing scenario (only when input_dir is selected) f.i. 'co2' or '2005co2' crop (str): crop type (only when input_dir is selected) f.i. 'whe', 'mai', 'soy' or 'ric' irr (str): irrigation type (only when input_dir is selected) f.i 'noirr' or 'irr' fn_str_var (str): FileName STRing depending on VARiable and ISIMIP simuation round raises: NameError """ if not fn_str_var: fn_str_var = FN_STR_VAR if scenario is None: scenario = 'historical' if bias_corr is None: bias_corr = 'ewembi' if bbox is None: bbox = BBOX if input_dir is None: input_dir = INPUT_DIR input_dir = Path(input_dir) if not Path(input_dir).is_dir(): LOGGER.error('Input directory %s does not exist', input_dir) raise NameError # The filename is set or other variables (cl_model, scenario) are extracted of the # specified filename if filename is None: yearchunk = YEARCHUNKS[scenario] filename = '{}_{}_{}_{}_{}_{}_yield-{}-{}_{}_{}_{}.nc'.format( ag_model, cl_model, bias_corr, scenario, soc, co2, crop, irr, fn_str_var, yearchunk['startyear'], yearchunk['endyear']) elif scenario == 'ISIMIP2a': (_, _, _, _, _, _, _, crop, _, _, startyear, endyearnc) = filename.split('_') endyear, _ = endyearnc.split('.') yearchunk = dict() yearchunk = { 'yearrange': (int(startyear), int(endyear)), 'startyear': int(startyear), 'endyear': int(endyear) } elif scenario == 'test_file': yearchunk = dict() yearchunk = { 'yearrange': (1976, 2005), 'startyear': 1861, 'endyear': 2005, 'yearrange_mean': (1976, 2005) } ag_model, cl_model, _, _, soc, co2, crop_prop, *_ = filename.split( '_') _, crop, irr = crop_prop.split('-') else: # get yearchunk from filename, e.g., for rcp2.6 extended and ISIMIP3 (_, _, _, _, _, _, crop_irr, _, _, year1, year2) = filename.split('_') yearchunk = { 'yearrange': (int(year1), int(year2.split('.')[0])), 'startyear': int(year1), 'endyear': int(year2.split('.')[0]) } _, crop, irr = crop_irr.split('-') # if no yearrange is given, load full range from input file: if yearrange is None or len(yearrange) == 0: yearrange = yearchunk['yearrange'] # define indexes of the netcdf-bands to be extracted, and the # corresponding event names and dates # corrected indexes due to the bands in input starting with the index=1 id_bands = np.arange(yearrange[0] - yearchunk['startyear'] + 1, yearrange[1] - yearchunk['startyear'] + 2).tolist() # hazard setup: set attributes [lonmin, latmin, lonmax, latmax] = bbox self.set_raster([str(Path(input_dir, filename))], band=id_bands, geometry=list([ shapely.geometry.box(lonmin, latmin, lonmax, latmax) ])) self.intensity.data[np.isnan(self.intensity.data)] = 0.0 self.intensity.todense() self.crop = crop self.event_name = [ str(n) for n in range(int(yearrange[0]), int(yearrange[-1] + 1)) ] self.frequency = np.ones(len( self.event_name)) * (1 / len(self.event_name)) self.fraction = self.intensity.copy() self.fraction.data.fill(1.0) self.units = 't / y / ha' self.date = np.array( dt.str_to_date([event_ + '-01-01' for event_ in self.event_name])) self.centroids.set_meta_to_lat_lon() self.centroids.region_id = (coord.coord_on_land( self.centroids.lat, self.centroids.lon)).astype(dtype=int) self.check() return self
def _plot_warn( self, run_datetime, thresholds, decision_level, decision_dict, polygon_file, polygon_file_crs, title, proj=ccrs.PlateCarree(), figsize=(9, 13), adapt_fontsize=True, ): """plotting the warning level of each warning region based on thresholds""" # select hazard with run_datetime # pylint: disable=protected-access if run_datetime is None: run_datetime = self.run_datetime[0] haz_ind = np.argwhere(np.isin(self.run_datetime, run_datetime))[0][0] kwargs = dict() kwargs["cmap"] = CMAP_WARNPROB kwargs["s"] = 5 kwargs["marker"] = "," kwargs["norm"] = BoundaryNorm(np.linspace(0, 1, 11), CMAP_WARNPROB.N, clip=True) # Generate each subplot fig, axis, _fontsize = u_plot.make_map(1, proj=proj, figsize=figsize, adapt_fontsize=adapt_fontsize) if isinstance(axis, np.ndarray): axis = axis[0] tit = title fig.set_size_inches(9, 8) # add warning regions shp = shapereader.Reader(polygon_file) transformer = pyproj.Transformer.from_crs(polygon_file_crs, self._impact[haz_ind].crs, always_xy=True) # checking the decision dict and define the corresponding functions if not (isinstance(decision_dict["probability_aggregation"], float) & isinstance(decision_dict["area_aggregation"], float)): ValueError(" If decision_level is 'exposure_point'," + "parameters probability_aggregation and " + "area_aggregation of " + "Forecast.plot_warn_map() must both be " + "floats between [0..1]. Which each " + "specify quantiles.") decision_dict_functions = decision_dict.copy() for aggregation in decision_dict: if isinstance(decision_dict[aggregation], float): decision_dict_functions[aggregation] = np.percentile elif decision_dict[aggregation] == "sum": decision_dict_functions[aggregation] = np.sum elif decision_dict[aggregation] == "mean": decision_dict_functions[aggregation] = np.mean else: raise ValueError("Parameter area_aggregation of " + "Forecast.plot_warn_map() must eiter be " + "a float between [0..1], which " + "specifys a quantile. or 'sum' or 'mean'.") for geometry, _ in zip(shp.geometries(), shp.records()): geom2 = shapely.ops.transform(transformer.transform, geometry) in_geom = u_coord.coord_on_land( lat=self._impact[haz_ind].coord_exp[:, 0], lon=self._impact[haz_ind].coord_exp[:, 1], land_geom=geom2, ) if not in_geom.any(): continue # decide warning level warn_level = 0 for ind_i, warn_thres_i in enumerate(thresholds): if decision_level == "exposure_point": # decision at each grid_point probabilities = np.squeeze( np.asarray((self._impact[haz_ind].imp_mat >= warn_thres_i).sum(axis=0) / self._impact[haz_ind].event_id.size)) # quantiles over probability area = (probabilities[in_geom] >= decision_dict["probability_aggregation"]).sum() # quantiles over area if area >= (in_geom.sum() * decision_dict["area_aggregation"]): warn_level = ind_i + 1 elif decision_level == "polygon": # aggregation over area if isinstance(decision_dict["area_aggregation"], float): value_per_member = decision_dict_functions[ "area_aggregation"]( self._impact[haz_ind].imp_mat[:, in_geom].todense( ), decision_dict["area_aggregation"], axis=1, ) else: value_per_member = decision_dict_functions[ "area_aggregation"](self._impact[haz_ind]. imp_mat[:, in_geom].todense(), axis=1) # aggregation over members/probability if isinstance(decision_dict["probability_aggregation"], float): value_per_region = decision_dict_functions[ "probability_aggregation"]( value_per_member, decision_dict["probability_aggregation"]) else: value_per_region = decision_dict_functions[ "probability_aggregation"](value_per_member) # warn level decision if value_per_region >= warn_thres_i: warn_level = ind_i + 1 else: raise ValueError( "Parameter decision_level of " + "Forecast.plot_warn_map() must eiter be " + "'exposure_point' or 'polygon'.") # plot warn_region with specific color (dependent on warning level) axis.add_geometries( [geom2], crs=ccrs.PlateCarree(), facecolor=COLORS_WARN[warn_level, :], edgecolor="gray", ) # Create legend in this axis hazard_levels = [ "1: Minimal or no hazard", "2: Moderate hazard", "3: Significant hazard", "4: Severe hazard", "5: Very severe hazard", ] legend_elements = [ Patch(facecolor=COLORS_WARN[n, :], edgecolor="gray", label=hazard_level) for n, hazard_level in enumerate(hazard_levels) ] axis.legend( handles=legend_elements, loc="upper center", framealpha=0.5, bbox_to_anchor=(0.5, -0.02), ncol=3, ) title_position = { "model_text": [0.02, 0.91], "explain_text": [0.02, 0.87], "event_day": [0.98, 0.91], "run_start": [0.98, 0.87], } left_right = { "model_text": "left", "explain_text": "left", "event_day": "right", "run_start": "right", } color = { "model_text": "k", "explain_text": "k", "event_day": "r", "run_start": "k", } for t_i in tit: plt.figtext( title_position[t_i][0], title_position[t_i][1], tit[t_i], fontsize="xx-large", color=color[t_i], ha=left_right[t_i], ) extent = u_plot._get_borders(self._impact[haz_ind].coord_exp) axis.set_extent((extent), ccrs.PlateCarree()) fig.tight_layout() return fig, axis
def set_from_single_run(self, input_dir=None, filename=None, bbox=BBOX, yearrange=(YEARCHUNKS['historical'])['yearrange'], ag_model=None, cl_model=None, scenario='historical', soc=None, co2=None, crop=None, irr=None, fn_str_var=FN_STR_VAR): """Wrapper to fill hazard from nc_dis file from ISIMIP Parameters: input_dir (string): path to input data directory bbox (list of four floats): bounding box: [lon min, lat min, lon max, lat max] yearrange (int tuple): year range for hazard set, f.i. (1976, 2005) ag_model (str): abbrev. agricultural model (only when input_dir is selected) f.i. 'clm-crop', 'gepic','lpjml','pepic' cl_model (str): abbrev. climate model (only when input_dir is selected) f.i. ['gfdl-esm2m', 'hadgem2-es','ipsl-cm5a-lr','miroc5' scenario (str): climate change scenario (only when input_dir is selected) f.i. 'historical' or 'rcp60' or 'ISIMIP2a' soc (str): socio-economic trajectory (only when input_dir is selected) f.i. '2005soc' or 'histsoc' co2 (str): CO2 forcing scenario (only when input_dir is selected) f.i. 'co2' or '2005co2' crop (str): crop type (only when input_dir is selected) f.i. 'whe', 'mai', 'soy' or 'ric' irr (str): irrigation type (only when input_dir is selected) f.i 'noirr' or 'irr' fn_str_var (str): FileName STRing depending on VARiable and ISIMIP simuation round raises: NameError """ if input_dir is not None: if not os.path.exists(input_dir): LOGGER.error('Input directory %s does not exist', input_dir) raise NameError else: LOGGER.error('Input directory %s not set', input_dir) raise NameError # The filename is set or other variables (cl_model, scenario) are extracted of the # specified filename if filename is None: yearchunk = YEARCHUNKS[scenario] filename = os.path.join(input_dir, '%s_%s_ewembi_%s_%s_%s_yield-%s-%s_%s_%s_%s.nc' \ %(ag_model, cl_model, scenario, soc, co2, crop, irr, fn_str_var, str(yearchunk['startyear']), str(yearchunk['endyear']))) elif scenario == 'ISIMIP2a': (_, _, _, _, _, _, _, crop, _, _, startyear, endyearnc) = filename.split('_') endyear, _ = endyearnc.split('.') yearchunk = dict() yearchunk = {'yearrange': np.array([int(startyear), int(endyear)]), 'startyear': int(startyear), 'endyear': int(endyear)} filename = os.path.join(input_dir, filename) elif scenario == 'test_file': yearchunk = dict() yearchunk = {'yearrange': np.array([1976, 2005]), 'startyear': 1861, 'endyear': 2005, 'yearrange_mean': np.array([1976, 2005])} ag_model, cl_model, _, _, soc, co2, crop_prop, *_ = filename.split('_') _, crop, irr = crop_prop.split('-') filename = os.path.join(input_dir, filename) else: yearchunk = YEARCHUNKS[scenario] (_, _, _, _, _, _, crop_irr, *_) = filename.split('_') _, crop, irr = crop_irr.split('-') filename = os.path.join(input_dir, filename) # define indexes of the netcdf-bands to be extracted, and the # corresponding event names and dates # corrected indexes due to the bands in input starting with the index=1 id_bands = np.arange(yearrange[0] - yearchunk['startyear'] + 1, yearrange[1] - yearchunk['startyear'] + 2).tolist() # hazard setup: set attributes [lonmin, latmin, lonmax, latmax] = bbox self.set_raster([filename], band=id_bands, geometry=list([shapely.geometry.box(lonmin, latmin, lonmax, latmax)])) self.intensity.data[np.isnan(self.intensity.data)] = 0.0 self.intensity.todense() self.crop = crop self.event_name = [str(n) for n in range(int(yearrange[0]), int(yearrange[-1] + 1))] self.frequency = np.ones(len(self.event_name)) * (1 / len(self.event_name)) self.fraction = self.intensity.copy() self.fraction.data.fill(1.0) self.units = 't / y / ha' self.date = np.array(dt.str_to_date( [event_ + '-01-01' for event_ in self.event_name])) self.centroids.set_meta_to_lat_lon() self.centroids.region_id = ( coord.coord_on_land(self.centroids.lat, self.centroids.lon)).astype(dtype=int) self.check() return self