def plot(self, axis=None, **kwargs): """Plot centroids scatter points over earth. Parameters ---------- axis : matplotlib.axes._subplots.AxesSubplot, optional axis to use kwargs : optional arguments for scatter matplotlib function Returns ------- axis : matplotlib.axes._subplots.AxesSubplot """ if self.meta and not self.coord.size: self.set_meta_to_lat_lon() pad = np.abs(u_coord.get_resolution(self.lat, self.lon)).min() proj_data, _ = u_plot.get_transformation(self.crs) proj_plot = proj_data if isinstance(proj_data, ccrs.PlateCarree): # use different projections for plot and data to shift the central lon in the plot xmin, ymin, xmax, ymax = u_coord.latlon_bounds(self.lat, self.lon, buffer=pad) proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax)) else: xmin, ymin, xmax, ymax = (self.lon.min() - pad, self.lat.min() - pad, self.lon.max() + pad, self.lat.max() + pad) if not axis: _, axis = u_plot.make_map(proj=proj_plot) axis.set_extent((xmin, xmax, ymin, ymax), crs=proj_data) u_plot.add_shapes(axis) axis.scatter(self.lon, self.lat, transform=proj_data, **kwargs) return axis
def test_latlon_bounds(self): """Test latlon_bounds function""" lat, lon = np.array([0, -2, 5]), np.array([-179, 175, 178]) bounds = latlon_bounds(lat, lon) self.assertEqual(bounds, (175, -2, 181, 5)) bounds = latlon_bounds(lat, lon, buffer=1) self.assertEqual(bounds, (174, -3, 182, 6)) # buffer exceeding antimeridian lat, lon = np.array([0, -2.1, 5]), np.array([-179.5, -175, -178]) bounds = latlon_bounds(lat, lon, buffer=1) self.assertEqual(bounds, (179.5, -3.1, 186, 6)) # longitude values need to be normalized before they lie between computed bounds: lon_mid = 0.5 * (bounds[0] + bounds[2]) lon = lon_normalize(lon, center=lon_mid) self.assertTrue(np.all((bounds[0] <= lon) & (lon <= bounds[2]))) # data covering almost the whole longitudinal range lat, lon = np.linspace(-90, 90, 180), np.linspace(-180.0, 179, 360) bounds = latlon_bounds(lat, lon) self.assertEqual(bounds, (-179, -90, 180, 90)) bounds = latlon_bounds(lat, lon, buffer=1) self.assertEqual(bounds, (-180, -90, 180, 90))
def plot_raster(self, res=None, raster_res=None, save_tiff=None, raster_f=lambda x: np.log10((np.fmax(x + 1, 1))), label='value (log10)', scheduler=None, axis=None, **kwargs): """Generate raster from points geometry and plot it using log10 scale: np.log10((np.fmax(raster+1, 1))). Parameters: res (float, optional): resolution of current data in units of latitude and longitude, approximated if not provided. raster_res (float, optional): desired resolution of the raster save_tiff (str, optional): file name to save the raster in tiff format, if provided raster_f (lambda function): transformation to use to data. Default: log10 adding 1. label (str): colorbar label scheduler (str): used for dask map_partitions. “threads”, “synchronous” or “processes” axis (matplotlib.axes._subplots.AxesSubplot, optional): axis to use kwargs (optional): arguments for imshow matplotlib function Returns: matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot """ if self.meta and self.meta.get('height', 0) * self.meta.get( 'height', 0) == len(self.gdf): raster = self.gdf.value.values.reshape( (self.meta['height'], self.meta['width'])) # check raster starts by upper left corner if self.gdf.latitude.values[0] < self.gdf.latitude.values[-1]: raster = np.flip(raster, axis=0) if self.gdf.longitude.values[0] > self.gdf.longitude.values[-1]: LOGGER.error( 'Points are not ordered according to meta raster.') raise ValueError else: raster, meta = u_coord.points_to_raster(self.gdf, ['value'], res, raster_res, scheduler) raster = raster.reshape((meta['height'], meta['width'])) # save tiff if save_tiff is not None: with rasterio.open(save_tiff, 'w', driver='GTiff', height=meta['height'], width=meta['width'], count=1, dtype=np.float32, crs=self.crs, transform=meta['transform']) as ras_tiff: ras_tiff.write(raster.astype(np.float32), 1) # make plot proj_data, _ = u_plot.get_transformation(self.crs) proj_plot = proj_data if isinstance(proj_data, ccrs.PlateCarree): # use different projections for plot and data to shift the central lon in the plot xmin, ymin, xmax, ymax = u_coord.latlon_bounds( self.gdf.latitude.values, self.gdf.longitude.values) proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax)) else: xmin, ymin, xmax, ymax = (self.gdf.longitude.min(), self.gdf.latitude.min(), self.gdf.longitude.max(), self.gdf.latitude.max()) if not axis: _, axis = u_plot.make_map(proj=proj_plot) cbar_ax = make_axes_locatable(axis).append_axes('right', size="6.5%", pad=0.1, axes_class=plt.Axes) axis.set_extent((xmin, xmax, ymin, ymax), crs=proj_data) u_plot.add_shapes(axis) imag = axis.imshow(raster_f(raster), **kwargs, origin='upper', extent=(xmin, xmax, ymin, ymax), transform=proj_data) plt.colorbar(imag, cax=cbar_ax, label=label) plt.draw() return axis
def _plot_scattered_data(method, array_sub, geo_coord, var_name, title, pop_name=False, buffer=BUFFER, extend='neither', proj=ccrs.PlateCarree(), shapes=True, axes=None, figsize=(9, 13), adapt_fontsize=True, **kwargs): """Function for internal use in `geo_scatter_from_array` (when called with method="scatter") and `geo_bin_from_array` (when called with method="hexbin"). See the docstrings of the respective functions for more information on the parameters.""" # Generate array of values used in each subplot num_im, list_arr = _get_collection_arrays(array_sub) list_tit = to_list(num_im, title, 'title') list_name = to_list(num_im, var_name, 'var_name') list_coord = to_list(num_im, geo_coord, 'geo_coord') if 'cmap' not in kwargs: kwargs['cmap'] = CMAP_EXPOSURES if axes is None: proj_plot = proj if isinstance(proj, ccrs.PlateCarree): # use different projections for plot and data to shift the central lon in the plot xmin, xmax = u_coord.lon_bounds( np.concatenate([c[:, 1] for c in list_coord])) proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax)) _, axes, fontsize = make_map(num_im, proj=proj_plot, figsize=figsize, adapt_fontsize=adapt_fontsize) else: fontsize = None axes_iter = axes if not isinstance(axes, np.ndarray): axes_iter = np.array([[axes]]) # Generate each subplot for array_im, axis, tit, name, coord in \ zip(list_arr, axes_iter.flatten(), list_tit, list_name, list_coord): if coord.shape[0] != array_im.size: raise ValueError("Size mismatch in input array: %s != %s." % (coord.shape[0], array_im.size)) # Binned image with coastlines if isinstance(proj, ccrs.PlateCarree): xmin, ymin, xmax, ymax = u_coord.latlon_bounds(coord[:, 0], coord[:, 1], buffer=buffer) extent = (xmin, xmax, ymin, ymax) else: extent = _get_borders(coord, buffer=buffer, proj_limits=proj.x_limits + proj.y_limits) axis.set_extent((extent), proj) if shapes: add_shapes(axis) if pop_name: add_populated_places(axis, extent, proj, fontsize) if method == "hexbin": if 'gridsize' not in kwargs: kwargs['gridsize'] = min(int(array_im.size / 2), MAX_BINS) mappable = axis.hexbin(coord[:, 1], coord[:, 0], C=array_im, transform=proj, **kwargs) else: mappable = axis.scatter(coord[:, 1], coord[:, 0], c=array_im, transform=proj, **kwargs) # Create colorbar in this axis cbax = make_axes_locatable(axis).append_axes('right', size="6.5%", pad=0.1, axes_class=plt.Axes) cbar = plt.colorbar(mappable, cax=cbax, orientation='vertical', extend=extend) cbar.set_label(name) axis.set_title("\n".join(wrap(tit))) if fontsize: cbar.ax.tick_params(labelsize=fontsize) cbar.ax.yaxis.get_offset_text().set_fontsize(fontsize) for item in [axis.title, cbar.ax.xaxis.label, cbar.ax.yaxis.label]: item.set_fontsize(fontsize) plt.tight_layout() return axes
def plot_raster(self, res=None, raster_res=None, save_tiff=None, raster_f=lambda x: np.log10((np.fmax(x + 1, 1))), label='value (log10)', scheduler=None, axis=None, figsize=(9, 13), fill=True, adapt_fontsize=True, **kwargs): """Generate raster from points geometry and plot it using log10 scale: np.log10((np.fmax(raster+1, 1))). Parameters ---------- res : float, optional resolution of current data in units of latitude and longitude, approximated if not provided. raster_res : float, optional desired resolution of the raster save_tiff : str, optional file name to save the raster in tiff format, if provided raster_f : lambda function transformation to use to data. Default: log10 adding 1. label : str colorbar label scheduler : str used for dask map_partitions. “threads”, “synchronous” or “processes” axis : matplotlib.axes._subplots.AxesSubplot, optional axis to use figsize : tuple, optional figure size for plt.subplots fill : bool, optional If false, the areas with no data will be plotted in white. If True, the areas with missing values are filled as 0s. The default is True. adapt_fontsize : bool, optional If set to true, the size of the fonts will be adapted to the size of the figure. Otherwise the default matplotlib font size is used. Default is True. kwargs : optional arguments for imshow matplotlib function Returns ------- matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot """ if self.meta and self.meta.get('height', 0) * self.meta.get( 'height', 0) == len(self.gdf): raster = self.gdf.value.values.reshape( (self.meta['height'], self.meta['width'])) # check raster starts by upper left corner if self.gdf.latitude.values[0] < self.gdf.latitude.values[-1]: raster = np.flip(raster, axis=0) if self.gdf.longitude.values[0] > self.gdf.longitude.values[-1]: raise ValueError( 'Points are not ordered according to meta raster.') else: raster, meta = u_coord.points_to_raster(self.gdf, ['value'], res, raster_res, scheduler) raster = raster.reshape((meta['height'], meta['width'])) # save tiff if save_tiff is not None: with rasterio.open(save_tiff, 'w', driver='GTiff', height=meta['height'], width=meta['width'], count=1, dtype=np.float32, crs=self.crs, transform=meta['transform']) as ras_tiff: ras_tiff.write(raster.astype(np.float32), 1) # make plot proj_data, _ = u_plot.get_transformation(self.crs) proj_plot = proj_data if isinstance(proj_data, ccrs.PlateCarree): # use different projections for plot and data to shift the central lon in the plot xmin, ymin, xmax, ymax = u_coord.latlon_bounds( self.gdf.latitude.values, self.gdf.longitude.values) proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax)) else: xmin, ymin, xmax, ymax = (self.gdf.longitude.min(), self.gdf.latitude.min(), self.gdf.longitude.max(), self.gdf.latitude.max()) if not axis: _, axis, fontsize = u_plot.make_map(proj=proj_plot, figsize=figsize, adapt_fontsize=adapt_fontsize) else: fontsize = None cbar_ax = make_axes_locatable(axis).append_axes('right', size="6.5%", pad=0.1, axes_class=plt.Axes) axis.set_extent((xmin, xmax, ymin, ymax), crs=proj_data) u_plot.add_shapes(axis) if not fill: raster = np.where(raster == 0, np.nan, raster) raster_f = lambda x: np.log10((np.maximum(x + 1, 1))) if 'cmap' not in kwargs: kwargs['cmap'] = CMAP_RASTER imag = axis.imshow(raster_f(raster), **kwargs, origin='upper', extent=(xmin, xmax, ymin, ymax), transform=proj_data) cbar = plt.colorbar(imag, cax=cbar_ax, label=label) plt.colorbar(imag, cax=cbar_ax, label=label) plt.tight_layout() plt.draw() if fontsize: cbar.ax.tick_params(labelsize=fontsize) cbar.ax.yaxis.get_offset_text().set_fontsize(fontsize) for item in [axis.title, cbar.ax.xaxis.label, cbar.ax.yaxis.label]: item.set_fontsize(fontsize) return axis
def geo_bin_from_array(array_sub, geo_coord, var_name, title, pop_name=True, buffer=BUFFER, extend='neither', proj=ccrs.PlateCarree(), axes=None, figsize=(9, 13), **kwargs): """Plot array values binned over input coordinates. Parameters ---------- array_sub : np.array(1d or 2d) or list(np.array) Each array (in a row or in the list) are values at each point in corresponding geo_coord that are binned in one subplot. geo_coord : 2d np.array or list(2d np.array) (lat, lon) for each point in a row. If one provided, the same grid is used for all subplots. Otherwise provide as many as subplots in array_sub. var_name : str or list(str) label to be shown in the colorbar. If one provided, the same is used for all subplots. Otherwise provide as many as subplots in array_sub. title : str or list(str) subplot title. If one provided, the same is used for all subplots. Otherwise provide as many as subplots in array_sub. pop_name : bool, optional add names of the populated places, by default True buffer : float, optional border to add to coordinates, by default BUFFER extend : str, optional extend border colorbar with arrows. [ 'neither' | 'both' | 'min' | 'max' ], by default 'neither' proj : ccrs, optional coordinate reference system of the given data, by default ccrs.PlateCarree() axes : Axes or ndarray(Axes), optional by default None figsize : tuple, optional figure size for plt.subplots, by default (9, 13) **kwargs arbitrary keyword arguments for hexbin matplotlib function Returns ------- cartopy.mpl.geoaxes.GeoAxesSubplot Raises ------ ValueError """ # Generate array of values used in each subplot num_im, list_arr = _get_collection_arrays(array_sub) list_tit = to_list(num_im, title, 'title') list_name = to_list(num_im, var_name, 'var_name') list_coord = to_list(num_im, geo_coord, 'geo_coord') if 'cmap' not in kwargs: kwargs['cmap'] = 'Wistia' if axes is None: proj_plot = proj if isinstance(proj, ccrs.PlateCarree): # use different projections for plot and data to shift the central lon in the plot xmin, xmax = u_coord.lon_bounds( np.concatenate([c[:, 1] for c in list_coord])) proj_plot = ccrs.PlateCarree(central_longitude=0.5 * (xmin + xmax)) _, axes = make_map(num_im, proj=proj_plot, figsize=figsize) if not isinstance(axes, np.ndarray): axes_iter = np.array([[axes]]) # Generate each subplot for array_im, axis, tit, name, coord in \ zip(list_arr, axes_iter.flatten(), list_tit, list_name, list_coord): if coord.shape[0] != array_im.size: raise ValueError("Size mismatch in input array: %s != %s." % (coord.shape[0], array_im.size)) # Binned image with coastlines if isinstance(proj, ccrs.PlateCarree): xmin, ymin, xmax, ymax = u_coord.latlon_bounds(coord[:, 0], coord[:, 1], buffer=buffer) extent = (xmin, xmax, ymin, ymax) else: extent = _get_borders(coord, buffer=buffer, proj_limits=proj.x_limits + proj.y_limits) axis.set_extent((extent), proj) add_shapes(axis) if pop_name: add_populated_places(axis, extent, proj) if 'gridsize' not in kwargs: kwargs['gridsize'] = min(int(array_im.size / 2), MAX_BINS) hex_bin = axis.hexbin(coord[:, 1], coord[:, 0], C=array_im, transform=proj, **kwargs) # Create colorbar in this axis cbax = make_axes_locatable(axis).append_axes('right', size="6.5%", pad=0.1, axes_class=plt.Axes) cbar = plt.colorbar(hex_bin, cax=cbax, orientation='vertical', extend=extend) cbar.set_label(name) axis.set_title(tit) return axes