diff --git a/mapshader/core.py b/mapshader/core.py index 75d8074..ef97c4f 100644 --- a/mapshader/core.py +++ b/mapshader/core.py @@ -34,6 +34,35 @@ def create_agg(source: MapSource, x: float = None, y: float = None, z: float = None, height: int = 256, width: int = 256): + """ + Instantiate an abstract canvas representing the space and compute + a reduction by pixel according to the geometry type applying the + aggregation function defined in source. + + Parameters + ---------- + source : mapshader.sources.MapSource + The map source object. + xmin : float + X-axis minimum range. + ymin : float + Y-axis minimum range. + xmax :float + X-axis maximum range. + ymax : float + Y-axis maximum range. + x, y, z : float + The coordinates to be used to get the bounds inclusive space along the axis. + height : int, default=256 + Height of the output aggregate in pixels. + width : int, default=256 + Width of the output aggregate in pixels. + + Returns + ------- + agg : xarray.DataArray + The transformed datasource. + """ if x is not None and y is not None and z is not None: xmin, ymin, xmax, ymax = tile_def.get_tile_meters(x, y, z) @@ -72,6 +101,25 @@ def create_agg(source: MapSource, def point_aggregation(cvs, data, xfield, yfield, zfield, agg_func): + """ + Compute a reduction by pixel, mapping data to pixels as points. + + Parameters + ---------- + cvs : datashader.Canvas + The input canvas. + data : pandas.DataFrame, dask.DataFrame, or xarray.DataArray/Dataset + The input datasource. + xfield, yfield, zfield : str + Column names for the x, y, and z coordinates of each point. + agg_func : Reduction, optional + Reduction to compute. Default is ``count()``. + + Returns + ------- + agg : xarray.DataArray + The transformed datasource. + """ if zfield: return cvs.points(data, xfield, yfield, getattr(ds, agg_func)(zfield)) else: @@ -79,6 +127,25 @@ def point_aggregation(cvs, data, xfield, yfield, zfield, agg_func): def line_aggregation(cvs, data, zfield, agg_func): + """ + Compute a reduction by pixel, mapping data to pixels as one or more lines. + + Parameters + ---------- + cvs : datashader.Canvas + The input canvas. + data : pandas.DataFrame, dask.DataFrame, or xarray.DataArray/Dataset + The input datasource. + zfield : str + Column names for z coordinate of each point. + agg_func : Reduction, optional + Reduction to compute. Default is ``any()``. + + Returns + ------- + agg : xarray.DataArray + The transformed datasource. + """ if zfield: return cvs.line(data, geometry='geometry', @@ -88,6 +155,26 @@ def line_aggregation(cvs, data, zfield, agg_func): def polygon_aggregation(cvs, data, zfield, agg_func): + """ + Compute a reduction by pixel, mapping data to pixels as one or + more filled polygons. + + Parameters + ---------- + cvs : datashader.Canvas + The input canvas. + data : pandas.DataFrame, dask.DataFrame, or xarray.DataArray/Dataset + The input datasource. + zfield : str + Column names for z coordinate of each point. + agg_func : Reduction, optional + Reduction to compute. Default is ``any()``. + + Returns + ------- + agg : xarray.DataArray + The transformed datasource. + """ if zfield: return cvs.polygons(data, 'geometry', @@ -104,6 +191,35 @@ def get_data_array_extent(dataarray): def raster_aggregation(cvs, data, interpolate='linear', padding=0, agg_method=rd.max()): + """ + Sample a raster dataset by canvas size and bounds. + + Parameters + ---------- + cvs : datashader.Canvas + The input canvas. + data : xarray.DataArray, xr.Dataset or dask.Array + The input datasource. + interpolate : str, default=linear + Resampling mode when upsampling raster. + Options include: nearest, linear. + padding : int, default=0 + The padding to be added over the coordinates bounds range. + agg_method : Reduction, default=datashader.reductions.max() + Resampling mode when downsampling raster. The supported + options include: first, last, mean, mode, var, std, min, + The agg can be specified as either a string name or as a + reduction function, but note that the function object will + be used only to extract the agg type (mean, max, etc.) and + the optional column name; the hardcoded raster code + supports only a fixed set of reductions and ignores the + actual code of the provided agg. + + Returns + ------- + agg : xarray.DataArray + The transformed datasource. + """ xmin, xmax = cvs.x_range ymin, ymax = cvs.y_range xdrange = (xmax - xmin) * (1 + 2 * padding) @@ -142,6 +258,24 @@ def raster_aggregation(cvs, data, interpolate='linear', padding=0, agg_method=rd 'quantile': quantile} def apply_additional_transforms(source: MapSource, agg: xr.DataArray): + """ + Apply additional transforms over the data, which options could be + ``hillshade`` or ``quantile``. + + Parameters + ---------- + source : mapshader.sources.MapSource + The map source object. + agg : xarray.DataArray + The transformed datasource. + + Returns + ------- + source : mapshader.sources.MapSource + The map source object. + agg : xarray.DataArray + The newly transformed datasource. + """ agg = agg.astype('float64') agg.data[agg.data == 0] = np.nan for e in source.extras: @@ -156,7 +290,30 @@ def apply_additional_transforms(source: MapSource, agg: xr.DataArray): def shade_discrete(agg, color_key, name='shaded', alpha=255, nodata=0): - + """ + Convert a DataArray to an image by choosing an RGBA pixel color + for each value by discrete approach. + + Parameters + ---------- + agg : xarray.DataArray + The input datasource. + color_key : dict + Categories colors. + name : str, default=shaded + Name of the datasource array. + alpha : int, default=255 + Value between 0 - 255 representing the alpha value to use for + colormapped pixels that contain data. + nodata : int, default=0 + The maximum data value, all the values less than this will be + replaced with 0. + + Returns + ------- + img : xarray.DataArray + A DataArray representing an image. + """ if not agg.ndim == 2: raise ValueError("agg must be 2D") @@ -201,6 +358,30 @@ def shade_discrete(agg, color_key, name='shaded', alpha=255, nodata=0): def shade_agg(source: MapSource, agg: xr.DataArray, xmin, ymin, xmax, ymax): + """ + Convert a DataArray to an image by choosing an RGBA pixel color + for each value. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + agg : xarray.DataArray + The input datasource. + xmin : float + X-axis minimum range. + ymin : float + Y-axis minimum range. + xmax : float + X-axis maximum range. + ymax : float + Y-axis maximum range. + + Returns + ------- + img : xarray.DataArray + A DataArray representing an image. + """ df = source.data zfield = source.zfield geometry_type = source.geometry_type @@ -240,7 +421,26 @@ def to_raster(source: MapSource, xmin: float = None, ymin: float = None, xmax: float = None, ymax: float = None, height: int = None, width: int = None): - + """ + Export a MapSource object to a raster object. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + xmin : float + X-axis minimum range. + ymin : float + Y-axis minimum range. + xmax : float + X-axis maximum range. + ymax : float + Y-axis maximum range. + height : int + Height of the output aggregate in pixels. + width : int + Width of the output aggregate in pixels. + """ if height is None and width is None: width = 1000 @@ -284,7 +484,28 @@ def render_map(source: MapSource, x: float = None, y: float = None, z: float = None, height: int = None, width: int = None, ): - + """ + Export a MapSource object to a map object. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + xmin : float + X-axis minimum range. + ymin : float + Y-axis minimum range. + xmax : float + X-axis maximum range. + ymax : float + Y-axis maximum range. + x, y, z : float + The coordinates to be used to get the bounds inclusive space along the axis. + height : int + Height of the output aggregate in pixels. + width : int + Width of the output aggregate in pixels. + """ if x is not None and y is not None and z is not None: xmin, ymin, xmax, ymax = tile_def.get_tile_meters(x, y, z) @@ -339,8 +560,23 @@ def render_map(source: MapSource, return img -def get_geojson(source: MapSource, simplify=None): +def get_source_data(source: MapSource, simplify=None): + """ + Get MapSource data and return as dict or GeoDataFrame depending on the geometry type. + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + simplify : int, default=None + Get the simplified representation of each geometry according + to the toleranced distance. + + Returns + ------- + gdf : GeoDataFrame or dict + The Mapsource data + """ if isinstance(source.data, spatialpandas.GeoDataFrame): gdf = source.data.to_geopandas() @@ -361,19 +597,58 @@ def get_geojson(source: MapSource, simplify=None): def get_legend(source: MapSource): + """ + Get the MapSource legend. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + + Returns + ------- + legend : list of dict + """ if source.legend is not None: return source.legend return [] def render_geojson(source: MapSource, simplify=None): - geojson = get_geojson(source, simplify) + """ + Export a MapSource object to a geojson object. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + simplify : int, default=None + Get the simplified representation of each geometry according + to the toleranced distance. + + Returns + ------- + geojson : string + """ + data = get_source_data(source, simplify) - if isinstance(geojson, dict): - return json.dumps(geojson) + if isinstance(data, dict): + return json.dumps(data) - return geojson.to_json() + return data.to_json() def render_legend(source: MapSource): - return json.dumps(render_legend(source)) + """ + Get the MapSource legend and return as a JSON string. + + Parameters + ---------- + source : mapshader.sources.MapSource + The input datasource. + + Returns + ------- + geojson : string + """ + return json.dumps(get_legend(source)) diff --git a/mapshader/io.py b/mapshader/io.py index 92ae6d3..6627158 100644 --- a/mapshader/io.py +++ b/mapshader/io.py @@ -12,6 +12,29 @@ def load_raster(file_path, xmin=None, ymin=None, xmax=None, ymax=None, chunks=None, layername='data'): + """ + Load raster data. + + Parameters + ---------- + file_path : str + Relative path to the file. + xmin : float + X-axis minimum range. + ymin : float + Y-axis minimum range. + xmax : float + X-axis maximum range. + ymax : float + Y-axis maximum range. + layername : str, default=data + Data layer name. + + Returns + ------- + arr : xarray.DataArray + The loaded data. + """ if file_path.endswith('.tif'): @@ -40,4 +63,17 @@ def load_raster(file_path, xmin=None, ymin=None, def load_vector(filepath: str): + """ + Load vector data. + + Parameters + ---------- + filepath : str + Relative path to the file. + + Returns + ------- + gpd : geopandas.DataFrame + The loaded data. + """ return gpd.read_file(filepath) diff --git a/mapshader/mercator.py b/mapshader/mercator.py index 0f2e7b4..672b14c 100644 --- a/mapshader/mercator.py +++ b/mapshader/mercator.py @@ -1,40 +1,47 @@ import math def invert_y_tile(y, z): - # Convert from TMS to Google tile y coordinate, and vice versa + """ + Convert from TMS to Google tile y coordinate, and vice versa + """ return (2 ** z) - 1 - y # TODO: change name from source to definition class MercatorTileDefinition(object): - ''' Implementation of mercator tile source - In general, tile sources are used as a required input for ``TileRenderer``. + """ + Implementation of mercator tile source. In general, tile sources + are used as a required input for ``TileRenderer``. + Parameters ---------- x_range : tuple - full extent of x dimension in data units + Full extent of x dimension in data units. y_range : tuple - full extent of y dimension in data units + Full extent of y dimension in data units. max_zoom : int - A maximum zoom level for the tile layer. This is the most zoomed-in level. + A maximum zoom level for the tile layer. This is the most + zoomed-in level. min_zoom : int - A minimum zoom level for the tile layer. This is the most zoomed-out level. + A minimum zoom level for the tile layer. This is the most + zoomed-out level. max_zoom : int - A maximum zoom level for the tile layer. This is the most zoomed-in level. + A maximum zoom level for the tile layer. This is the most + zoomed-in level. x_origin_offset : int - An x-offset in plot coordinates. + An x-offset in plot coordinates. y_origin_offset : int - An y-offset in plot coordinates. + An y-offset in plot coordinates. initial_resolution : int - Resolution (plot_units / pixels) of minimum zoom level of tileset - projection. None to auto-compute. + Resolution (plot_units / pixels) of minimum zoom level of + tileset projection. None to auto-compute. format : int - An y-offset in plot coordinates. - Output - ------ - tileScheme: MercatorTileSource - ''' + An y-offset in plot coordinates. + Returns + ------- + tileScheme: MercatorTileDefinition + """ def __init__(self, x_range, y_range, tile_size=256, min_zoom=0, max_zoom=30, x_origin_offset=20037508.34, y_origin_offset=20037508.34, initial_resolution=156543.03392804097): @@ -49,16 +56,16 @@ def __init__(self, x_range, y_range, tile_size=256, min_zoom=0, max_zoom=30, self._resolutions = [self._get_resolution(z) for z in range(self.min_zoom, self.max_zoom+1)] def to_ogc_tile_metadata(self, output_file_path): - ''' + """ Create OGC tile metadata XML - ''' + """ pass def to_esri_tile_metadata(self, output_file_path): - ''' + """ Create ESRI tile metadata JSON - ''' + """ pass diff --git a/mapshader/sources.py b/mapshader/sources.py index 1a3ac7f..0f46e00 100644 --- a/mapshader/sources.py +++ b/mapshader/sources.py @@ -15,6 +15,86 @@ class MapSource(object): + """ + This class represents a map source object. + + Parameters + ---------- + name : str + Service name. + description : str + Service description. + filepath : str + Relative path to the data file. + legend : list of dict + Service legend, which could be defined the name, color, value, + and category. + config_path : str + Relative path to the config file. + data : geopandas.GeoDataFrame + Service source data. + geometry_type : str + Geometry type. + key : str + Service route root. + text : str + The service introduction text. + fields : list of str + The geometry fields. + span : str or tuple of int; + Min and max data values to use for colormap/alpha interpolation + when wishing to override autoranging. + geometry_field : str, default=geometry + The geometry field name. + xfield : str, default=geometry + The x field name. + yfield : str, default=geometry + The y field name. + zfield : str + The z field name. + agg_func : str + Reduction to compute. + raster_interpolate : str, default=linear + Resampling mode when upsampling raster. + Options include: nearest, linear. + shade_how : str, default=linear + The interpolation method to use. Valid strings are 'eq_hist', + 'cbrt', 'log', and 'linear'. + cmap : list of colors or matplotlib.colors.Colormap, default=viridis + The colormap to use for 2D agg arrays. + color_key : dict or iterable + The colors to use for a 3D (categorical) agg array. + dynspread : int + The maximum number of pixels to spread on all shape sides. + extras : list of str + The additional transforms over the data, which options could be + 'hillshade' or 'quantile'. + raster_padding : int, default=0 + The padding to be added over the coordinates bounds range. + service_types : list of str + The service types, which options could be 'tile', 'image', + 'wms', and 'geojson'. + full_extent : tuple of int + The coordinate of the lower left corner and the coordinate of + the upper right corner in map units. + default_extent : list of int + The service starting extent. + default_height : int, default=256 + Height of the output aggregate in pixels. + default_width : int, default=256 + Width of the output aggregate in pixels. + overviews : dict + The factors and values to be used when reducing the data + resolution. + transforms : list of dict + The transforms to be applied over the data, which options could + include: 'reproject_raster', 'reproject_vector', 'orient_array', + 'cast', 'flip_coords', 'build_raster_overviews', 'build_vector_overviews', + 'squeeze', 'to_spatialpandas', 'add_xy_fields', 'select_by_attributes', + 'polygon_to_line', and 'raster_to_categorical_points'. + preload : bool, default=False + Preload the data after the service started. + """ def __init__(self, name=None, @@ -134,7 +214,9 @@ def get_full_extent(self): raise NotImplementedError() def load(self): - + """ + Load the service data. + """ if self.is_loaded: return self @@ -220,6 +302,14 @@ def from_obj(obj: dict): class RasterSource(MapSource): + """ + This class represents a raster source object. + + Parameters + ---------- + MapSource : mapshader.sources.MapSource + The map source object. + """ @property def load_func(self): @@ -235,6 +325,14 @@ def full_extent(self): class VectorSource(MapSource): + """ + This class represents a vector source object. + + Parameters + ---------- + MapSource : mapshader.sources.MapSource + The map source object. + """ @property def load_func(self): @@ -250,6 +348,14 @@ def full_extent(self): class MapService(): + """ + This class represents a map service object. + + Parameters + ---------- + MapSource : mapshader.sources.MapSource + The map source object. + """ def __init__(self, source: MapSource, renderers=[]): self.source = source @@ -257,38 +363,65 @@ def __init__(self, source: MapSource, renderers=[]): @property def key(self): + """ + Get the route before the coordinates. + """ return f'{self.source.key}-{self.service_type}' @property def name(self): + """ + Get the source name and service type. + """ return f'{self.source.name} {self.service_type}' @property def legend_name(self): + """ + Get the legend name. + """ return f'{self.name}-legend' @property def default_extent(self): + """ + Get the default extent. + """ return self.source.default_extent @property def default_width(self): + """ + Get the default width. + """ return self.source.default_width @property def default_height(self): + """ + Get the default height. + """ return self.source.default_height @property def service_page_url(self): + """ + Get the service page url. + """ return f'/{self.key}' @property def legend_url(self): + """ + Get the legend url. + """ return f'/{self.key}/legend' @property def service_page_name(self): + """ + Get the service page name. + """ return f'/{self.key}-{self.service_type}' @property @@ -309,6 +442,9 @@ def service_type(self): class TileService(MapService): + """ + This class represents a tile service object. + """ @property def service_url(self): @@ -328,6 +464,9 @@ def service_type(self): class ImageService(MapService): + """ + This class represents a image service object. + """ @property def service_url(self): @@ -356,6 +495,9 @@ def service_type(self): return 'image' class WMSService(MapService): + """ + This class represents a WMS service object. + """ @property def service_url(self): @@ -385,6 +527,9 @@ def service_type(self): class GeoJSONService(MapService): + """ + This class represents a GeoJSON service object. + """ @property def service_url(self): @@ -622,7 +767,19 @@ def elevation_source_netcdf(): def parse_sources(source_objs, config_path=None, contains=None): - + """ + Parse ``mapshader.sources.MapSource`` and instantiate a + ``mapshader.sources.MapService``. + + Parameters + ---------- + source_objs : list of ``mapshader.sources.MapSource`` + The map source objects. + config_path : str + Relative path to the config file. + contains : str + Skip the service type creation that contains this route. + """ service_classes = { 'tile': TileService, 'wms': WMSService, @@ -648,6 +805,20 @@ def parse_sources(source_objs, config_path=None, contains=None): def get_services(config_path=None, include_default=True, contains=None, sources=None): + """ + Get the map services. + + Parameters + ---------- + config_path : str + Relative path to the config file. + include_default : bool, default=True + Include demo services. + contains : str + Skip the service type creation that contains this route. + sources : list of ``mapshader.sources.MapSource`` + The map source objects. + """ source_objs = None diff --git a/mapshader/transforms.py b/mapshader/transforms.py index e153895..c184dc5 100644 --- a/mapshader/transforms.py +++ b/mapshader/transforms.py @@ -22,6 +22,21 @@ } def reproject_raster(arr: xr.DataArray, epsg=3857): + """ + Reproject raster data. + + Parameters + ---------- + arr : xarray.DataArray + The raster data. + epsg : int + The coordinate systems code. + + Returns + ------- + reproj_data : xarray.DataArray + The reprojected data. + """ global projections try: @@ -32,39 +47,154 @@ def reproject_raster(arr: xr.DataArray, epsg=3857): def reproject_vector(gdf: gpd.GeoDataFrame, epsg=3857): + """ + Reproject vector data. + + Parameters + ---------- + gdf : geopandas.GeoDataFrame + The vector data. + epsg : int + The coordinate systems code. + + Returns + ------- + reproj_data : geopandas.GeoDataFrame + The reprojected data. + """ return gdf.to_crs(epsg=epsg) def flip_coords(arr, dim): + """ + Flip the geometry coordinates. + + Parameters + ---------- + arr : xarray.DataArray + The data source. + dim : str + The coordinate field name. + + Returns + ------- + flipped_data : xarray.DataArray + The flipped coordinates data. + """ args = {dim: list(reversed(arr.coords[dim]))} arr = arr.assign_coords(**args) return arr def to_spatialpandas(gdf: gpd.GeoDataFrame, geometry_field='geometry'): + """ + Convert a ``geopandas.GeoDataFrame`` to ``spatialpandas.GeoDataFrame``. + + Parameters + ---------- + gdf : geopandas.GeoDataFrame + The data source. + geometry_field : str + Geometry field on GeoDataFrame + + Returns + ------- + spatial_gdf : spatialpandas.GeoDataFrame + """ return spatialpandas.GeoDataFrame(gdf, geometry=geometry_field) def squeeze(arr, dim): + """ + Return a new ``xarray.DataArray`` with squeezed data. + + Parameters + ---------- + arr : xarray.DataArray + The data source. + dim : str + Drop a specific field name. + + Returns + ------- + squeezed_data : xarray.DataArray + The squeezed data. + """ return arr.squeeze().drop(dim) def cast(arr, dtype): + """ + Cast the data to a specific data type. + + Parameters + ---------- + arr : xarray.DataArray + The data source. + dtype : str + Data type. + + Returns + ------- + casted_data : xarray.DataArray + The casted data. + """ arr.data = arr.data.astype(dtype) return arr def orient_array(arr): + """ + Reorients the array to a canonical orientation depending on + whether the x and y-resolution values are positive or negative. + + Parameters + ---------- + arr : xarray.DataArray + Data to be reoriented + + Returns + ------- + reoriented_data : numpy.ndarray + Reoriented 2d NumPy ndarray + """ arr.data = ds.utils.orient_array(arr) return arr def get_data_array_extent(dataarray): + """ + Get the coordinate of the lower left corner and the coordinate of + the upper right corner in map units. + + Parameters + ---------- + dataarray : xarray.DataArray + The input datasource. + + Returns + ------- + extent : tuple of integers + A tuple of ``(xmin, ymin, xmax, ymax)`` values. + """ return (dataarray.coords['x'].min().item(), dataarray.coords['y'].min().item(), dataarray.coords['x'].max().item(), dataarray.coords['y'].max().item()) def canvas_like(dataarray): + """ + Get a ``datashader.Canvas`` according to a ``xarray.DataArray`` features. + + Parameters + ---------- + dataarray : xarray.DataArray + The input datasource. + + Returns + ------- + canvas : datashader.Canvas + An abstract canvas representing the space in which to bin. + """ if isinstance(dataarray, xr.DataArray): extent = get_data_array_extent(dataarray) @@ -81,6 +211,24 @@ def canvas_like(dataarray): def build_vector_overviews(gdf, levels, geometry_field='geometry'): + """ + Reduce the vector data resolution. + + Parameters + ---------- + gdf : geopandas.GeoDataFrame + The vector data. + levels : dict + The factors and values to be used when reducing the data + resolution. + geometry_field : str, default=geometry + The geometry field name. + + Returns + ------- + overviews : geopandas.GeoDataFrame + The reduced resolution vector data. + """ values = {} overviews = {} for level, simplify_tol in levels.items(): @@ -100,6 +248,25 @@ def build_vector_overviews(gdf, levels, geometry_field='geometry'): def build_raster_overviews(arr, levels, interpolate='linear'): + """ + Reduce the raster data resolution. + + Parameters + ---------- + arr : xarray.DataArray + The raster data. + levels : dict + The factors and values to be used when reducing the data + resolution. + interpolate : str, default=linear + Resampling mode when upsampling raster. + Options include: nearest, linear. + + Returns + ------- + overviews : xarray.DataArray + The reduced resolution raster data. + """ values = {} overviews = {} for level, resolution in levels.items(): @@ -127,12 +294,36 @@ def build_raster_overviews(arr, levels, interpolate='linear'): def add_xy_fields(gdf, geometry_field='geometry'): + """ + Extract x and y fields from geometry and create new columns with them. + """ gdf['X'] = gdf[geometry_field].apply(lambda p: p.x) gdf['Y'] = gdf[geometry_field].apply(lambda p: p.y) return gdf def select_by_attributes(gdf, field, value, operator='IN'): + """ + Filter a ``geopandas.GeoDataFrame`` data. + + Parameters + ---------- + gdf : geopandas.GeoDataFrame + The vector data. + field : str + Column to be filtered. + value : int or float + Value to compare when filtering. + operator : str, default=IN + Arithmetic operator to be used when filtering. + Options include: IN, NOT IN, EQUALS, NOT EQUALS, LT, GT, LTE, + and GTE. + + Returns + ------- + filtered_data : geopandas.GeoDataFrame + The filtered data. + """ if operator == 'IN': return gdf[gdf[field].isin(value)] @@ -160,11 +351,43 @@ def select_by_attributes(gdf, field, value, operator='IN'): def polygon_to_line(gdf, geometry_field='geometry'): + """ + Convert geometry type from polygon to line. + + Parameters + ---------- + gdf : geopandas.GeoDataFrame + The vector data. + geometry_field : str, default=geometry + The geometry field name. + + Returns + ------- + gdf : geopandas.GeoDataFrame + The converted data. + """ gdf[geometry_field] = gdf[geometry_field].boundary return gdf def raster_to_categorical_points(arr, cats: dict, dim: str = 'data'): + """ + Convert raster data to categorical points. + + Parameters + ---------- + arr : xarray.DataArray + The raster data. + cats : dict + Categories colors. + dim : str, default=data + The categorical column name. + + Returns + ------- + df : pandas.DataFrame + The converted data. + """ df = None if isinstance(arr, da.Array): df = arr.compute().to_dataframe() @@ -197,4 +420,12 @@ def raster_to_categorical_points(arr, cats: dict, dim: str = 'data'): def get_transform_by_name(name: str): + """ + Get transform function by their name. + + Parameters + ---------- + name : str + The transform function name. + """ return _transforms[name]