Skip to content

hypercoast module

Main module.

Map (Map)

A class that extends leafmap.Map to provide additional functionality for hypercoast.

Methods

Any methods inherited from leafmap.Map.

Source code in hypercoast/hypercoast.py
class Map(leafmap.Map):
    """
    A class that extends leafmap.Map to provide additional functionality for
        hypercoast.

    Attributes:
        Any attributes inherited from leafmap.Map.

    Methods:
        Any methods inherited from leafmap.Map.
    """

    def __init__(self, **kwargs):
        """
        Initializes a new instance of the Map class.

        Args:
            **kwargs: Arbitrary keyword arguments that are passed to the parent
                class's constructor.
        """
        super().__init__(**kwargs)
        self._spectral_data = {}
        self._plot_options = None
        self._plot_marker_cluster = ipyleaflet.MarkerCluster(name="Marker Cluster")

    def add(self, obj, position="topright", xlim=None, ylim=None, **kwargs):
        """Add a layer to the map.

        Args:
            obj (str or object): The name of the layer or a layer object.
            position (str, optional): The position of the layer widget. Can be
                'topright', 'topleft', 'bottomright', or 'bottomleft'. Defaults
                to 'topright'.
            xlim (tuple, optional): The x-axis limits of the plot. Defaults to None.
            ylim (tuple, optional): The y-axis limits of the plot. Defaults to None.
            **kwargs: Arbitrary keyword arguments that are passed to the parent
                class's add_layer method.
        """

        if isinstance(obj, str):
            if obj == "spectral":

                SpectralWidget(self, position=position, xlim=xlim, ylim=ylim, **kwargs)
                self.set_plot_options(add_marker_cluster=True)
            else:
                super().add(obj, **kwargs)

        else:
            super().add(obj, **kwargs)

    def search_emit(self, default_dataset="EMITL2ARFL"):
        """
        Adds a NASA Earth Data search tool to the map with a default dataset for
            EMIT.

        Args:
            default_dataset (str, optional): The default dataset to search for.
                Defaults to "EMITL2ARFL".
        """
        self.add("nasa_earth_data", default_dataset=default_dataset)

    def search_pace(self, default_dataset="PACE_OCI_L2_AOP_NRT"):
        """
        Adds a NASA Earth Data search tool to the map with a default dataset for
            PACE.

        Args:
            default_dataset (str, optional): The default dataset to search for.
                Defaults to "PACE_OCI_L2_AOP_NRT".
        """
        self.add("nasa_earth_data", default_dataset=default_dataset)

    def search_ecostress(self, default_dataset="ECO_L2T_LSTE"):
        """
        Adds a NASA Earth Data search tool to the map with a default dataset for
            ECOSTRESS.

        Args:
            default_dataset (str, optional): The default dataset to search for.
                Defaults to "ECO_L2T_LSTE".
        """
        self.add("nasa_earth_data", default_dataset=default_dataset)

    def add_raster(
        self,
        source,
        indexes=None,
        colormap=None,
        vmin=None,
        vmax=None,
        nodata=None,
        attribution=None,
        layer_name="Raster",
        layer_index=None,
        zoom_to_layer=True,
        visible=True,
        opacity=1.0,
        array_args=None,
        client_args={"cors_all": False},
        open_args=None,
        **kwargs,
    ):
        """Add a local raster dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the GeoTIFF file or the URL of the Cloud
                Optimized GeoTIFF.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band. See
                https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is greyscale.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            nodata (float, optional): The value from the band to use to interpret
                as not valid data. Defaults to None.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'Raster'.
            layer_index (int, optional): The index of the layer. Defaults to None.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults to
                True.
            opacity (float, optional): The opacity of the layer. Defaults to 1.0.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
            client_args (dict, optional): Additional arguments to pass to
                localtileserver.TileClient. Defaults to { "cors_all": False }.
            open_args (dict, optional): Additional arguments to pass to
                rioxarray.open_rasterio.

        """

        import rioxarray as rxr

        if array_args is None:
            array_args = {}
        if open_args is None:
            open_args = {}

        if nodata is None:
            nodata = np.nan
        super().add_raster(
            source,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            layer_index=layer_index,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            opacity=opacity,
            array_args=array_args,
            client_args=client_args,
            **kwargs,
        )

        if isinstance(source, str):
            da = rxr.open_rasterio(source, **open_args)
            dims = da.dims
            da = da.transpose(dims[1], dims[2], dims[0])

            xds = da.to_dataset(name="data")
            self.cog_layer_dict[layer_name]["xds"] = xds
            # if self.cog_layer_dict[layer_name].get("hyper") is None:
            #     self.cog_layer_dict[layer_name]["hyper"] = "COG"

    def add_dataset(
        self,
        source,
        indexes=None,
        colormap=None,
        vmin=None,
        vmax=None,
        nodata=None,
        attribution=None,
        layer_name="Raster",
        zoom_to_layer=True,
        visible=True,
        array_args=None,
        open_args=None,
        **kwargs,
    ):
        import rioxarray as rxr
        from leafmap import array_to_image

        if array_args is None:
            array_args = {}

        if open_args is None:
            open_args = {}

        if isinstance(source, str):
            da = rxr.open_rasterio(source, **open_args)
            dims = da.dims
            da = da.transpose(dims[1], dims[2], dims[0])
            xds = da.to_dataset(name="data")

        elif not isinstance(source, xr.Dataset):
            raise ValueError(
                "source must be a path to a raster file or an xarray.Dataset object."
            )
        else:
            xds = source

        if indexes is None:
            if xds.sizes[dims[2]] < 3:
                indexes = [1]
            elif xds.sizes[dims[2]] < 4:
                indexes = [1, 2, 3]
            else:
                indexes = [3, 2, 1]

        bands = [i - 1 for i in indexes]
        da = xds.isel(band=bands)["data"]
        image = array_to_image(da, transpose=False)

        self.add_raster(
            image,
            indexes=None,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = xds
        self.cog_layer_dict[layer_name]["type"] = "XARRAY"
        self.cog_layer_dict[layer_name]["hyper"] = "XARRAY"
        self.cog_layer_dict[layer_name]["band_names"] = [
            "b" + str(i) for i in xds.coords["band"].values.tolist()
        ]
        self.cog_layer_dict[layer_name]["indexes"] = indexes
        self.cog_layer_dict[layer_name]["vis_bands"] = ["b" + str(i) for i in indexes]

    def add_emit(
        self,
        source,
        wavelengths=None,
        indexes=None,
        colormap=None,
        vmin=None,
        vmax=None,
        nodata=np.nan,
        attribution=None,
        layer_name="EMIT",
        zoom_to_layer=True,
        visible=True,
        array_args=None,
        **kwargs,
    ):
        """Add an EMIT dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the GeoTIFF file or the URL of the Cloud
                Optimized GeoTIFF.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band.
                    See https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                    Default is greyscale.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            nodata (float, optional): The value from the band to use to
                interpret as not valid data. Defaults to None.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults to
                True.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
        """

        if array_args is None:
            array_args = {}

        xds = None
        if isinstance(source, str):

            xds = read_emit(source)
            source = emit_to_image(xds, wavelengths=wavelengths)
        elif isinstance(source, xr.Dataset):
            xds = source
            source = emit_to_image(xds, wavelengths=wavelengths)

        self.add_raster(
            source,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = xds
        self.cog_layer_dict[layer_name]["hyper"] = "EMIT"
        self._update_band_names(layer_name, wavelengths)

    def add_pace(
        self,
        source,
        wavelengths=None,
        indexes=None,
        colormap="jet",
        vmin=None,
        vmax=None,
        nodata=np.nan,
        attribution=None,
        layer_name="PACE",
        zoom_to_layer=True,
        visible=True,
        method="nearest",
        gridded=False,
        array_args=None,
        **kwargs,
    ):
        """Add a PACE dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the GeoTIFF file or the URL of the Cloud
                Optimized GeoTIFF.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band. See
                    https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                    Default is greyscale.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            nodata (float, optional): The value from the band to use to interpret
                as not valid data. Defaults to None.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults to True.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
        """

        if array_args is None:
            array_args = {}

        if isinstance(source, str):

            source = read_pace(source)

        try:
            image = pace_to_image(
                source, wavelengths=wavelengths, method=method, gridded=gridded
            )

            if isinstance(wavelengths, list) and len(wavelengths) > 1:
                colormap = None

            self.add_raster(
                image,
                indexes=indexes,
                colormap=colormap,
                vmin=vmin,
                vmax=vmax,
                nodata=nodata,
                attribution=attribution,
                layer_name=layer_name,
                zoom_to_layer=zoom_to_layer,
                visible=visible,
                array_args=array_args,
                **kwargs,
            )

            self.cog_layer_dict[layer_name]["xds"] = source
            self.cog_layer_dict[layer_name]["hyper"] = "PACE"
            self._update_band_names(layer_name, wavelengths)
        except Exception as e:
            print(e)

    def add_desis(
        self,
        source,
        wavelengths=[900, 650, 525],
        indexes=None,
        colormap="jet",
        vmin=None,
        vmax=None,
        nodata=np.nan,
        attribution=None,
        layer_name="DESIS",
        zoom_to_layer=True,
        visible=True,
        method="nearest",
        array_args=None,
        **kwargs,
    ):
        """Add a DESIS dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the GeoTIFF file or the URL of the Cloud
                Optimized GeoTIFF.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band. See
                https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is 'jet'.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to None.
            nodata (float, optional): The value from the band to use to interpret
                as not valid data. Defaults to None.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults to True.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
        """
        if array_args is None:
            array_args = {}

        if isinstance(source, str):

            source = read_desis(source)

        image = desis_to_image(source, wavelengths=wavelengths, method=method)

        if isinstance(wavelengths, list) and len(wavelengths) > 1:
            colormap = None

        if isinstance(wavelengths, int):
            wavelengths = [wavelengths]

        if indexes is None:
            if isinstance(wavelengths, list) and len(wavelengths) == 1:
                indexes = [1]
            else:
                indexes = [1, 2, 3]

        self.add_raster(
            image,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = source
        self.cog_layer_dict[layer_name]["hyper"] = "DESIS"
        self._update_band_names(layer_name, wavelengths)

    def add_neon(
        self,
        source,
        wavelengths=None,
        indexes=None,
        colormap=None,
        vmin=0,
        vmax=0.5,
        nodata=np.nan,
        attribution=None,
        layer_name="NEON",
        zoom_to_layer=True,
        visible=True,
        array_args=None,
        method="nearest",
        **kwargs,
    ):
        """Add an NEON AOP dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the NEON AOP HDF5 file.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band. See
                    https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                    Default is greyscale.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to 0.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to 0.5.
            nodata (float, optional): The value from the band to use to
                interpret as not valid data. Defaults to np.nan.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'NEON'.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults
                to True.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
            method (str, optional): The method to use for data interpolation.
                Defaults to "nearest".
        """

        if array_args is None:
            array_args = {}
        xds = None
        if isinstance(source, str):

            xds = read_neon(source)
            source = neon_to_image(xds, wavelengths=wavelengths, method=method)
        elif isinstance(source, xr.Dataset):
            xds = source
            source = neon_to_image(xds, wavelengths=wavelengths, method=method)

        self.add_raster(
            source,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = xds
        self.cog_layer_dict[layer_name]["hyper"] = "NEON"
        self._update_band_names(layer_name, wavelengths)

    def add_aviris(
        self,
        source,
        wavelengths=None,
        indexes=None,
        colormap=None,
        vmin=0,
        vmax=0.5,
        nodata=np.nan,
        attribution=None,
        layer_name="AVIRIS",
        zoom_to_layer=True,
        visible=True,
        array_args=None,
        method="nearest",
        **kwargs,
    ):
        """Add an AVIRIS dataset to the map.
            If you are using this function in JupyterHub on a remote server
                (e.g., Binder, Microsoft Planetary Computer) and
            if the raster does not render properly, try installing
                jupyter-server-proxy using `pip install jupyter-server-proxy`,
            then running the following code before calling this function. For
                more info, see https://bit.ly/3JbmF93.

            import os
            os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

        Args:
            source (str): The path to the AVIRIS file.
            indexes (int, optional): The band(s) to use. Band indexing starts
                at 1. Defaults to None.
            colormap (str, optional): The name of the colormap from `matplotlib`
                to use when plotting a single band. See
                    https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                    Default is greyscale.
            vmin (float, optional): The minimum value to use when colormapping
                the palette when plotting a single band. Defaults to 0.
            vmax (float, optional): The maximum value to use when colormapping
                the palette when plotting a single band. Defaults to 0.5.
            nodata (float, optional): The value from the band to use to
                interpret as not valid data. Defaults to np.nan.
            attribution (str, optional): Attribution for the source raster. This
                defaults to a message about it being a local file.. Defaults to None.
            layer_name (str, optional): The layer name to use. Defaults to 'NEON'.
            zoom_to_layer (bool, optional): Whether to zoom to the extent of the
                layer. Defaults to True.
            visible (bool, optional): Whether the layer is visible. Defaults
                to True.
            array_args (dict, optional): Additional arguments to pass to
                `array_to_memory_file` when reading the raster. Defaults to {}.
            method (str, optional): The method to use for data interpolation.
                Defaults to "nearest".
        """
        if array_args is None:
            array_args = {}

        xds = None
        if isinstance(source, str):

            xds = read_aviris(source)
            source = neon_to_image(xds, wavelengths=wavelengths, method=method)
        elif isinstance(source, xr.Dataset):
            xds = source
            source = aviris_to_image(xds, wavelengths=wavelengths, method=method)

        self.add_raster(
            source,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = xds
        self.cog_layer_dict[layer_name]["hyper"] = "AVIRIS"
        self._update_band_names(layer_name, wavelengths)

    def add_hyper(self, xds, dtype, wvl_indexes=None, **kwargs):
        """Add a hyperspectral dataset to the map.

        Args:
            xds (str): The Xarray dataset containing the hyperspectral data.
            dtype (str): The type of the hyperspectral dataset. Can be one of
                "EMIT", "PACE", "DESIS", "NEON", "AVIRIS".
            **kwargs: Additional keyword arguments to pass to the corresponding
                add function.
        """

        if wvl_indexes is not None:
            if dtype == "XARRAY":
                kwargs["indexes"] = [i + 1 for i in wvl_indexes]
            else:

                kwargs["wavelengths"] = (
                    xds.isel(wavelength=wvl_indexes)
                    .coords["wavelength"]
                    .values.tolist()
                )

        if dtype == "EMIT":
            self.add_emit(xds, **kwargs)
        elif dtype == "PACE":
            self.add_pace(xds, **kwargs)
        elif dtype == "DESIS":
            self.add_desis(xds, **kwargs)
        elif dtype == "NEON":
            self.add_neon(xds, **kwargs)
        elif dtype == "AVIRIS":
            self.add_aviris(xds, **kwargs)
        elif dtype == "XARRAY":
            kwargs.pop("wavelengths", None)
            self.add_dataset(xds, **kwargs)

    def set_plot_options(
        self,
        add_marker_cluster=False,
        plot_type=None,
        overlay=False,
        position="bottomright",
        min_width=None,
        max_width=None,
        min_height=None,
        max_height=None,
        **kwargs,
    ):
        """Sets plotting options.

        Args:
            add_marker_cluster (bool, optional): Whether to add a marker cluster.
                Defaults to False.
            sample_scale (float, optional):  A nominal scale in meters of the
                projection to sample in . Defaults to None.
            plot_type (str, optional): The plot type can be one of "None", "bar",
                "scatter" or "hist". Defaults to None.
            overlay (bool, optional): Whether to overlay plotted lines on the
                figure. Defaults to False.
            position (str, optional): Position of the control, can be
                ‘bottomleft’, ‘bottomright’, ‘topleft’, or ‘topright’. Defaults
                to 'bottomright'.
            min_width (int, optional): Min width of the widget (in pixels), if
                None it will respect the content size. Defaults to None.
            max_width (int, optional): Max width of the widget (in pixels), if
                None it will respect the content size. Defaults to None.
            min_height (int, optional): Min height of the widget (in pixels), if
                None it will respect the content size. Defaults to None.
            max_height (int, optional): Max height of the widget (in pixels), if
                None it will respect the content size. Defaults to None.

        """
        plot_options_dict = {}
        plot_options_dict["add_marker_cluster"] = add_marker_cluster
        plot_options_dict["plot_type"] = plot_type
        plot_options_dict["overlay"] = overlay
        plot_options_dict["position"] = position
        plot_options_dict["min_width"] = min_width
        plot_options_dict["max_width"] = max_width
        plot_options_dict["min_height"] = min_height
        plot_options_dict["max_height"] = max_height

        for key in kwargs:
            plot_options_dict[key] = kwargs[key]

        self._plot_options = plot_options_dict

        if not hasattr(self, "_plot_marker_cluster"):
            self._plot_marker_cluster = ipyleaflet.MarkerCluster(name="Marker Cluster")

        if add_marker_cluster and (self._plot_marker_cluster not in self.layers):
            self.add(self._plot_marker_cluster)

    def spectral_to_df(self, **kwargs):
        """Converts the spectral data to a pandas DataFrame.

        Returns:
            pd.DataFrame: The spectral data as a pandas DataFrame.
        """
        import pandas as pd

        df = pd.DataFrame(self._spectral_data, **kwargs)
        return df

    def spectral_to_gdf(self, **kwargs):
        """Converts the spectral data to a GeoPandas GeoDataFrame.

        Returns:
            gpd.DataFrame: The spectral data as a pandas DataFrame.
        """
        import geopandas as gpd
        from shapely.geometry import Point

        df = self.spectral_to_df()

        if len(df) == 0:
            return df

        # Step 1: Extract the coordinates from the columns
        if "wavelength" in df.columns:
            df = df.rename(columns={"wavelength": "latlon"})
        elif "wavelengths" in df.columns:
            df = df.rename(columns={"wavelengths": "latlon"})
        coordinates = [col.strip("()").split() for col in df.columns[1:]]
        coords = [(float(lat), float(lon)) for lat, lon in coordinates]

        # Step 2: Create Point geometries for each coordinate
        points = [Point(lon, lat) for lat, lon in coords]

        # Step 3: Create a GeoDataFrame
        df_transposed = df.set_index("latlon").T

        # Convert the column names to strings to ensure compatibility with GeoJSON
        df_transposed.columns = df_transposed.columns.astype(str)

        # Create the GeoDataFrame
        gdf = gpd.GeoDataFrame(df_transposed, geometry=points, **kwargs)

        # Set the coordinate reference system (CRS)
        gdf = gdf.set_geometry("geometry").set_crs("EPSG:4326")

        return gdf

    def spectral_to_csv(self, filename, index=True, **kwargs):
        """Saves the spectral data to a CSV file.

        Args:
            filename (str): The output CSV file.
            index (bool, optional): Whether to write the index. Defaults to True.
        """
        df = self.spectral_to_df()
        df = df.rename_axis("band")
        df.to_csv(filename, index=index, **kwargs)

    def _update_band_names(self, layer_name, wavelengths):

        # Function to find the nearest indices
        def find_nearest_indices(
            dataarray, selected_wavelengths, dim_name="wavelength"
        ):
            indices = []
            for wavelength in selected_wavelengths:
                if dim_name == "band":
                    nearest_wavelength = dataarray.sel(
                        band=wavelength, method="nearest"
                    )
                else:
                    nearest_wavelength = dataarray.sel(
                        wavelength=wavelength, method="nearest"
                    )
                nearest_wavelength_index = nearest_wavelength[dim_name].item()
                nearest_index = (
                    dataarray[dim_name].values.tolist().index(nearest_wavelength_index)
                )
                indices.append(nearest_index + 1)
            return indices

        if "xds" in self.cog_layer_dict[layer_name]:
            xds = self.cog_layer_dict[layer_name]["xds"]
            dim_name = "wavelength"

            if "band" in xds:
                dim_name = "band"

            band_count = xds.sizes[dim_name]
            band_names = ["b" + str(band) for band in range(1, band_count + 1)]
            self.cog_layer_dict[layer_name]["band_names"] = band_names

            try:
                indexes = find_nearest_indices(xds, wavelengths, dim_name=dim_name)
                vis_bands = ["b" + str(index) for index in indexes]
                self.cog_layer_dict[layer_name]["indexes"] = indexes
                self.cog_layer_dict[layer_name]["vis_bands"] = vis_bands
            except Exception as e:
                print(e)

    def add_field_data(
        self,
        data: Union[str],
        x_col: str = "wavelength",
        y_col_prefix: str = "(",
        x_label: str = "Wavelengths (nm)",
        y_label: str = "Reflectance",
        use_marker_cluster: bool = True,
        min_width: int = 400,
        max_width: int = 600,
        min_height: int = 200,
        max_height: int = 250,
        layer_name: str = "Marker Cluster",
        **kwargs,
    ):
        """
        Displays field data on a map with interactive markers and popups showing time series data.

        Args:
            data (Union[str, pd.DataFrame]): Path to the CSV file or a pandas DataFrame containing the data.
            x_col (str): Column name to use for the x-axis of the charts. Default is "wavelength".
            y_col_prefix (str): Prefix to identify the columns that contain the location-specific data. Default is "(".
            x_label (str): Label for the x-axis of the charts. Default is "Wavelengths (nm)".
            y_label (str): Label for the y-axis of the charts. Default is "Reflectance".
            use_marker_cluster (bool): Whether to use marker clustering. Default is True.
            min_width (int): Minimum width of the popup. Default is 400.
            max_width (int): Maximum width of the popup. Default is 600.
            min_height (int): Minimum height of the popup. Default is 200.
            max_height (int): Maximum height of the popup. Default is 250.
            layer_name (str): Name of the marker cluster layer. Default is "Marker Cluster".

        Returns:
            Map: An ipyleaflet Map with the added markers and popups.
        """
        show_field_data(
            data,
            x_col,
            y_col_prefix,
            x_label=x_label,
            y_label=y_label,
            use_marker_cluster=use_marker_cluster,
            min_width=min_width,
            max_width=max_width,
            min_height=min_height,
            max_height=max_height,
            layer_name=layer_name,
            m=self,
            **kwargs,
        )

__init__(self, **kwargs) special

Initializes a new instance of the Map class.

Parameters:

Name Type Description Default
**kwargs

Arbitrary keyword arguments that are passed to the parent class's constructor.

{}
Source code in hypercoast/hypercoast.py
def __init__(self, **kwargs):
    """
    Initializes a new instance of the Map class.

    Args:
        **kwargs: Arbitrary keyword arguments that are passed to the parent
            class's constructor.
    """
    super().__init__(**kwargs)
    self._spectral_data = {}
    self._plot_options = None
    self._plot_marker_cluster = ipyleaflet.MarkerCluster(name="Marker Cluster")

add(self, obj, position='topright', xlim=None, ylim=None, **kwargs)

Add a layer to the map.

Parameters:

Name Type Description Default
obj str or object

The name of the layer or a layer object.

required
position str

The position of the layer widget. Can be 'topright', 'topleft', 'bottomright', or 'bottomleft'. Defaults to 'topright'.

'topright'
xlim tuple

The x-axis limits of the plot. Defaults to None.

None
ylim tuple

The y-axis limits of the plot. Defaults to None.

None
**kwargs

Arbitrary keyword arguments that are passed to the parent class's add_layer method.

{}
Source code in hypercoast/hypercoast.py
def add(self, obj, position="topright", xlim=None, ylim=None, **kwargs):
    """Add a layer to the map.

    Args:
        obj (str or object): The name of the layer or a layer object.
        position (str, optional): The position of the layer widget. Can be
            'topright', 'topleft', 'bottomright', or 'bottomleft'. Defaults
            to 'topright'.
        xlim (tuple, optional): The x-axis limits of the plot. Defaults to None.
        ylim (tuple, optional): The y-axis limits of the plot. Defaults to None.
        **kwargs: Arbitrary keyword arguments that are passed to the parent
            class's add_layer method.
    """

    if isinstance(obj, str):
        if obj == "spectral":

            SpectralWidget(self, position=position, xlim=xlim, ylim=ylim, **kwargs)
            self.set_plot_options(add_marker_cluster=True)
        else:
            super().add(obj, **kwargs)

    else:
        super().add(obj, **kwargs)

add_aviris(self, source, wavelengths=None, indexes=None, colormap=None, vmin=0, vmax=0.5, nodata=nan, attribution=None, layer_name='AVIRIS', zoom_to_layer=True, visible=True, array_args=None, method='nearest', **kwargs)

Add an AVIRIS dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the AVIRIS file.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is greyscale.

None
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to 0.

0
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to 0.5.

0.5
nodata float

The value from the band to use to interpret as not valid data. Defaults to np.nan.

nan
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'NEON'.

'AVIRIS'
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
method str

The method to use for data interpolation. Defaults to "nearest".

'nearest'
Source code in hypercoast/hypercoast.py
def add_aviris(
    self,
    source,
    wavelengths=None,
    indexes=None,
    colormap=None,
    vmin=0,
    vmax=0.5,
    nodata=np.nan,
    attribution=None,
    layer_name="AVIRIS",
    zoom_to_layer=True,
    visible=True,
    array_args=None,
    method="nearest",
    **kwargs,
):
    """Add an AVIRIS dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the AVIRIS file.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band. See
                https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is greyscale.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to 0.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to 0.5.
        nodata (float, optional): The value from the band to use to
            interpret as not valid data. Defaults to np.nan.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'NEON'.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults
            to True.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
        method (str, optional): The method to use for data interpolation.
            Defaults to "nearest".
    """
    if array_args is None:
        array_args = {}

    xds = None
    if isinstance(source, str):

        xds = read_aviris(source)
        source = neon_to_image(xds, wavelengths=wavelengths, method=method)
    elif isinstance(source, xr.Dataset):
        xds = source
        source = aviris_to_image(xds, wavelengths=wavelengths, method=method)

    self.add_raster(
        source,
        indexes=indexes,
        colormap=colormap,
        vmin=vmin,
        vmax=vmax,
        nodata=nodata,
        attribution=attribution,
        layer_name=layer_name,
        zoom_to_layer=zoom_to_layer,
        visible=visible,
        array_args=array_args,
        **kwargs,
    )

    self.cog_layer_dict[layer_name]["xds"] = xds
    self.cog_layer_dict[layer_name]["hyper"] = "AVIRIS"
    self._update_band_names(layer_name, wavelengths)

add_desis(self, source, wavelengths=[900, 650, 525], indexes=None, colormap='jet', vmin=None, vmax=None, nodata=nan, attribution=None, layer_name='DESIS', zoom_to_layer=True, visible=True, method='nearest', array_args=None, **kwargs)

Add a DESIS dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the GeoTIFF file or the URL of the Cloud Optimized GeoTIFF.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is 'jet'.

'jet'
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
nodata float

The value from the band to use to interpret as not valid data. Defaults to None.

nan
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'EMIT'.

'DESIS'
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
Source code in hypercoast/hypercoast.py
def add_desis(
    self,
    source,
    wavelengths=[900, 650, 525],
    indexes=None,
    colormap="jet",
    vmin=None,
    vmax=None,
    nodata=np.nan,
    attribution=None,
    layer_name="DESIS",
    zoom_to_layer=True,
    visible=True,
    method="nearest",
    array_args=None,
    **kwargs,
):
    """Add a DESIS dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the GeoTIFF file or the URL of the Cloud
            Optimized GeoTIFF.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band. See
            https://matplotlib.org/stable/gallery/color/colormap_reference.html.
            Default is 'jet'.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        nodata (float, optional): The value from the band to use to interpret
            as not valid data. Defaults to None.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults to True.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
    """
    if array_args is None:
        array_args = {}

    if isinstance(source, str):

        source = read_desis(source)

    image = desis_to_image(source, wavelengths=wavelengths, method=method)

    if isinstance(wavelengths, list) and len(wavelengths) > 1:
        colormap = None

    if isinstance(wavelengths, int):
        wavelengths = [wavelengths]

    if indexes is None:
        if isinstance(wavelengths, list) and len(wavelengths) == 1:
            indexes = [1]
        else:
            indexes = [1, 2, 3]

    self.add_raster(
        image,
        indexes=indexes,
        colormap=colormap,
        vmin=vmin,
        vmax=vmax,
        nodata=nodata,
        attribution=attribution,
        layer_name=layer_name,
        zoom_to_layer=zoom_to_layer,
        visible=visible,
        array_args=array_args,
        **kwargs,
    )

    self.cog_layer_dict[layer_name]["xds"] = source
    self.cog_layer_dict[layer_name]["hyper"] = "DESIS"
    self._update_band_names(layer_name, wavelengths)

add_emit(self, source, wavelengths=None, indexes=None, colormap=None, vmin=None, vmax=None, nodata=nan, attribution=None, layer_name='EMIT', zoom_to_layer=True, visible=True, array_args=None, **kwargs)

Add an EMIT dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the GeoTIFF file or the URL of the Cloud Optimized GeoTIFF.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is greyscale.

None
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
nodata float

The value from the band to use to interpret as not valid data. Defaults to None.

nan
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'EMIT'.

'EMIT'
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
Source code in hypercoast/hypercoast.py
def add_emit(
    self,
    source,
    wavelengths=None,
    indexes=None,
    colormap=None,
    vmin=None,
    vmax=None,
    nodata=np.nan,
    attribution=None,
    layer_name="EMIT",
    zoom_to_layer=True,
    visible=True,
    array_args=None,
    **kwargs,
):
    """Add an EMIT dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the GeoTIFF file or the URL of the Cloud
            Optimized GeoTIFF.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band.
                See https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is greyscale.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        nodata (float, optional): The value from the band to use to
            interpret as not valid data. Defaults to None.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults to
            True.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
    """

    if array_args is None:
        array_args = {}

    xds = None
    if isinstance(source, str):

        xds = read_emit(source)
        source = emit_to_image(xds, wavelengths=wavelengths)
    elif isinstance(source, xr.Dataset):
        xds = source
        source = emit_to_image(xds, wavelengths=wavelengths)

    self.add_raster(
        source,
        indexes=indexes,
        colormap=colormap,
        vmin=vmin,
        vmax=vmax,
        nodata=nodata,
        attribution=attribution,
        layer_name=layer_name,
        zoom_to_layer=zoom_to_layer,
        visible=visible,
        array_args=array_args,
        **kwargs,
    )

    self.cog_layer_dict[layer_name]["xds"] = xds
    self.cog_layer_dict[layer_name]["hyper"] = "EMIT"
    self._update_band_names(layer_name, wavelengths)

add_field_data(self, data, x_col='wavelength', y_col_prefix='(', x_label='Wavelengths (nm)', y_label='Reflectance', use_marker_cluster=True, min_width=400, max_width=600, min_height=200, max_height=250, layer_name='Marker Cluster', **kwargs)

Displays field data on a map with interactive markers and popups showing time series data.

Parameters:

Name Type Description Default
data Union[str, pd.DataFrame]

Path to the CSV file or a pandas DataFrame containing the data.

required
x_col str

Column name to use for the x-axis of the charts. Default is "wavelength".

'wavelength'
y_col_prefix str

Prefix to identify the columns that contain the location-specific data. Default is "(".

'('
x_label str

Label for the x-axis of the charts. Default is "Wavelengths (nm)".

'Wavelengths (nm)'
y_label str

Label for the y-axis of the charts. Default is "Reflectance".

'Reflectance'
use_marker_cluster bool

Whether to use marker clustering. Default is True.

True
min_width int

Minimum width of the popup. Default is 400.

400
max_width int

Maximum width of the popup. Default is 600.

600
min_height int

Minimum height of the popup. Default is 200.

200
max_height int

Maximum height of the popup. Default is 250.

250
layer_name str

Name of the marker cluster layer. Default is "Marker Cluster".

'Marker Cluster'

Returns:

Type Description
Map

An ipyleaflet Map with the added markers and popups.

Source code in hypercoast/hypercoast.py
def add_field_data(
    self,
    data: Union[str],
    x_col: str = "wavelength",
    y_col_prefix: str = "(",
    x_label: str = "Wavelengths (nm)",
    y_label: str = "Reflectance",
    use_marker_cluster: bool = True,
    min_width: int = 400,
    max_width: int = 600,
    min_height: int = 200,
    max_height: int = 250,
    layer_name: str = "Marker Cluster",
    **kwargs,
):
    """
    Displays field data on a map with interactive markers and popups showing time series data.

    Args:
        data (Union[str, pd.DataFrame]): Path to the CSV file or a pandas DataFrame containing the data.
        x_col (str): Column name to use for the x-axis of the charts. Default is "wavelength".
        y_col_prefix (str): Prefix to identify the columns that contain the location-specific data. Default is "(".
        x_label (str): Label for the x-axis of the charts. Default is "Wavelengths (nm)".
        y_label (str): Label for the y-axis of the charts. Default is "Reflectance".
        use_marker_cluster (bool): Whether to use marker clustering. Default is True.
        min_width (int): Minimum width of the popup. Default is 400.
        max_width (int): Maximum width of the popup. Default is 600.
        min_height (int): Minimum height of the popup. Default is 200.
        max_height (int): Maximum height of the popup. Default is 250.
        layer_name (str): Name of the marker cluster layer. Default is "Marker Cluster".

    Returns:
        Map: An ipyleaflet Map with the added markers and popups.
    """
    show_field_data(
        data,
        x_col,
        y_col_prefix,
        x_label=x_label,
        y_label=y_label,
        use_marker_cluster=use_marker_cluster,
        min_width=min_width,
        max_width=max_width,
        min_height=min_height,
        max_height=max_height,
        layer_name=layer_name,
        m=self,
        **kwargs,
    )

add_hyper(self, xds, dtype, wvl_indexes=None, **kwargs)

Add a hyperspectral dataset to the map.

Parameters:

Name Type Description Default
xds str

The Xarray dataset containing the hyperspectral data.

required
dtype str

The type of the hyperspectral dataset. Can be one of "EMIT", "PACE", "DESIS", "NEON", "AVIRIS".

required
**kwargs

Additional keyword arguments to pass to the corresponding add function.

{}
Source code in hypercoast/hypercoast.py
def add_hyper(self, xds, dtype, wvl_indexes=None, **kwargs):
    """Add a hyperspectral dataset to the map.

    Args:
        xds (str): The Xarray dataset containing the hyperspectral data.
        dtype (str): The type of the hyperspectral dataset. Can be one of
            "EMIT", "PACE", "DESIS", "NEON", "AVIRIS".
        **kwargs: Additional keyword arguments to pass to the corresponding
            add function.
    """

    if wvl_indexes is not None:
        if dtype == "XARRAY":
            kwargs["indexes"] = [i + 1 for i in wvl_indexes]
        else:

            kwargs["wavelengths"] = (
                xds.isel(wavelength=wvl_indexes)
                .coords["wavelength"]
                .values.tolist()
            )

    if dtype == "EMIT":
        self.add_emit(xds, **kwargs)
    elif dtype == "PACE":
        self.add_pace(xds, **kwargs)
    elif dtype == "DESIS":
        self.add_desis(xds, **kwargs)
    elif dtype == "NEON":
        self.add_neon(xds, **kwargs)
    elif dtype == "AVIRIS":
        self.add_aviris(xds, **kwargs)
    elif dtype == "XARRAY":
        kwargs.pop("wavelengths", None)
        self.add_dataset(xds, **kwargs)

add_neon(self, source, wavelengths=None, indexes=None, colormap=None, vmin=0, vmax=0.5, nodata=nan, attribution=None, layer_name='NEON', zoom_to_layer=True, visible=True, array_args=None, method='nearest', **kwargs)

Add an NEON AOP dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the NEON AOP HDF5 file.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is greyscale.

None
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to 0.

0
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to 0.5.

0.5
nodata float

The value from the band to use to interpret as not valid data. Defaults to np.nan.

nan
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'NEON'.

'NEON'
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
method str

The method to use for data interpolation. Defaults to "nearest".

'nearest'
Source code in hypercoast/hypercoast.py
def add_neon(
    self,
    source,
    wavelengths=None,
    indexes=None,
    colormap=None,
    vmin=0,
    vmax=0.5,
    nodata=np.nan,
    attribution=None,
    layer_name="NEON",
    zoom_to_layer=True,
    visible=True,
    array_args=None,
    method="nearest",
    **kwargs,
):
    """Add an NEON AOP dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the NEON AOP HDF5 file.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band. See
                https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is greyscale.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to 0.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to 0.5.
        nodata (float, optional): The value from the band to use to
            interpret as not valid data. Defaults to np.nan.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'NEON'.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults
            to True.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
        method (str, optional): The method to use for data interpolation.
            Defaults to "nearest".
    """

    if array_args is None:
        array_args = {}
    xds = None
    if isinstance(source, str):

        xds = read_neon(source)
        source = neon_to_image(xds, wavelengths=wavelengths, method=method)
    elif isinstance(source, xr.Dataset):
        xds = source
        source = neon_to_image(xds, wavelengths=wavelengths, method=method)

    self.add_raster(
        source,
        indexes=indexes,
        colormap=colormap,
        vmin=vmin,
        vmax=vmax,
        nodata=nodata,
        attribution=attribution,
        layer_name=layer_name,
        zoom_to_layer=zoom_to_layer,
        visible=visible,
        array_args=array_args,
        **kwargs,
    )

    self.cog_layer_dict[layer_name]["xds"] = xds
    self.cog_layer_dict[layer_name]["hyper"] = "NEON"
    self._update_band_names(layer_name, wavelengths)

add_pace(self, source, wavelengths=None, indexes=None, colormap='jet', vmin=None, vmax=None, nodata=nan, attribution=None, layer_name='PACE', zoom_to_layer=True, visible=True, method='nearest', gridded=False, array_args=None, **kwargs)

Add a PACE dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the GeoTIFF file or the URL of the Cloud Optimized GeoTIFF.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is greyscale.

'jet'
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
nodata float

The value from the band to use to interpret as not valid data. Defaults to None.

nan
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'EMIT'.

'PACE'
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
Source code in hypercoast/hypercoast.py
def add_pace(
    self,
    source,
    wavelengths=None,
    indexes=None,
    colormap="jet",
    vmin=None,
    vmax=None,
    nodata=np.nan,
    attribution=None,
    layer_name="PACE",
    zoom_to_layer=True,
    visible=True,
    method="nearest",
    gridded=False,
    array_args=None,
    **kwargs,
):
    """Add a PACE dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the GeoTIFF file or the URL of the Cloud
            Optimized GeoTIFF.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band. See
                https://matplotlib.org/stable/gallery/color/colormap_reference.html.
                Default is greyscale.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        nodata (float, optional): The value from the band to use to interpret
            as not valid data. Defaults to None.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'EMIT'.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults to True.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
    """

    if array_args is None:
        array_args = {}

    if isinstance(source, str):

        source = read_pace(source)

    try:
        image = pace_to_image(
            source, wavelengths=wavelengths, method=method, gridded=gridded
        )

        if isinstance(wavelengths, list) and len(wavelengths) > 1:
            colormap = None

        self.add_raster(
            image,
            indexes=indexes,
            colormap=colormap,
            vmin=vmin,
            vmax=vmax,
            nodata=nodata,
            attribution=attribution,
            layer_name=layer_name,
            zoom_to_layer=zoom_to_layer,
            visible=visible,
            array_args=array_args,
            **kwargs,
        )

        self.cog_layer_dict[layer_name]["xds"] = source
        self.cog_layer_dict[layer_name]["hyper"] = "PACE"
        self._update_band_names(layer_name, wavelengths)
    except Exception as e:
        print(e)

add_raster(self, source, indexes=None, colormap=None, vmin=None, vmax=None, nodata=None, attribution=None, layer_name='Raster', layer_index=None, zoom_to_layer=True, visible=True, opacity=1.0, array_args=None, client_args={'cors_all': False}, open_args=None, **kwargs)

Add a local raster dataset to the map. If you are using this function in JupyterHub on a remote server (e.g., Binder, Microsoft Planetary Computer) and if the raster does not render properly, try installing jupyter-server-proxy using pip install jupyter-server-proxy, then running the following code before calling this function. For more info, see https://bit.ly/3JbmF93.

1
2
import os
os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

Parameters:

Name Type Description Default
source str

The path to the GeoTIFF file or the URL of the Cloud Optimized GeoTIFF.

required
indexes int

The band(s) to use. Band indexing starts at 1. Defaults to None.

None
colormap str

The name of the colormap from matplotlib to use when plotting a single band. See https://matplotlib.org/stable/gallery/color/colormap_reference.html. Default is greyscale.

None
vmin float

The minimum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
vmax float

The maximum value to use when colormapping the palette when plotting a single band. Defaults to None.

None
nodata float

The value from the band to use to interpret as not valid data. Defaults to None.

None
attribution str

Attribution for the source raster. This defaults to a message about it being a local file.. Defaults to None.

None
layer_name str

The layer name to use. Defaults to 'Raster'.

'Raster'
layer_index int

The index of the layer. Defaults to None.

None
zoom_to_layer bool

Whether to zoom to the extent of the layer. Defaults to True.

True
visible bool

Whether the layer is visible. Defaults to True.

True
opacity float

The opacity of the layer. Defaults to 1.0.

1.0
array_args dict

Additional arguments to pass to array_to_memory_file when reading the raster. Defaults to {}.

None
client_args dict

Additional arguments to pass to localtileserver.TileClient. Defaults to { "cors_all": False }.

{'cors_all': False}
open_args dict

Additional arguments to pass to rioxarray.open_rasterio.

None
Source code in hypercoast/hypercoast.py
def add_raster(
    self,
    source,
    indexes=None,
    colormap=None,
    vmin=None,
    vmax=None,
    nodata=None,
    attribution=None,
    layer_name="Raster",
    layer_index=None,
    zoom_to_layer=True,
    visible=True,
    opacity=1.0,
    array_args=None,
    client_args={"cors_all": False},
    open_args=None,
    **kwargs,
):
    """Add a local raster dataset to the map.
        If you are using this function in JupyterHub on a remote server
            (e.g., Binder, Microsoft Planetary Computer) and
        if the raster does not render properly, try installing
            jupyter-server-proxy using `pip install jupyter-server-proxy`,
        then running the following code before calling this function. For
            more info, see https://bit.ly/3JbmF93.

        import os
        os.environ['LOCALTILESERVER_CLIENT_PREFIX'] = 'proxy/{port}'

    Args:
        source (str): The path to the GeoTIFF file or the URL of the Cloud
            Optimized GeoTIFF.
        indexes (int, optional): The band(s) to use. Band indexing starts
            at 1. Defaults to None.
        colormap (str, optional): The name of the colormap from `matplotlib`
            to use when plotting a single band. See
            https://matplotlib.org/stable/gallery/color/colormap_reference.html.
            Default is greyscale.
        vmin (float, optional): The minimum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        vmax (float, optional): The maximum value to use when colormapping
            the palette when plotting a single band. Defaults to None.
        nodata (float, optional): The value from the band to use to interpret
            as not valid data. Defaults to None.
        attribution (str, optional): Attribution for the source raster. This
            defaults to a message about it being a local file.. Defaults to None.
        layer_name (str, optional): The layer name to use. Defaults to 'Raster'.
        layer_index (int, optional): The index of the layer. Defaults to None.
        zoom_to_layer (bool, optional): Whether to zoom to the extent of the
            layer. Defaults to True.
        visible (bool, optional): Whether the layer is visible. Defaults to
            True.
        opacity (float, optional): The opacity of the layer. Defaults to 1.0.
        array_args (dict, optional): Additional arguments to pass to
            `array_to_memory_file` when reading the raster. Defaults to {}.
        client_args (dict, optional): Additional arguments to pass to
            localtileserver.TileClient. Defaults to { "cors_all": False }.
        open_args (dict, optional): Additional arguments to pass to
            rioxarray.open_rasterio.

    """

    import rioxarray as rxr

    if array_args is None:
        array_args = {}
    if open_args is None:
        open_args = {}

    if nodata is None:
        nodata = np.nan
    super().add_raster(
        source,
        indexes=indexes,
        colormap=colormap,
        vmin=vmin,
        vmax=vmax,
        nodata=nodata,
        attribution=attribution,
        layer_name=layer_name,
        layer_index=layer_index,
        zoom_to_layer=zoom_to_layer,
        visible=visible,
        opacity=opacity,
        array_args=array_args,
        client_args=client_args,
        **kwargs,
    )

    if isinstance(source, str):
        da = rxr.open_rasterio(source, **open_args)
        dims = da.dims
        da = da.transpose(dims[1], dims[2], dims[0])

        xds = da.to_dataset(name="data")
        self.cog_layer_dict[layer_name]["xds"] = xds
        # if self.cog_layer_dict[layer_name].get("hyper") is None:
        #     self.cog_layer_dict[layer_name]["hyper"] = "COG"

search_ecostress(self, default_dataset='ECO_L2T_LSTE')

Adds a NASA Earth Data search tool to the map with a default dataset for ECOSTRESS.

Parameters:

Name Type Description Default
default_dataset str

The default dataset to search for. Defaults to "ECO_L2T_LSTE".

'ECO_L2T_LSTE'
Source code in hypercoast/hypercoast.py
def search_ecostress(self, default_dataset="ECO_L2T_LSTE"):
    """
    Adds a NASA Earth Data search tool to the map with a default dataset for
        ECOSTRESS.

    Args:
        default_dataset (str, optional): The default dataset to search for.
            Defaults to "ECO_L2T_LSTE".
    """
    self.add("nasa_earth_data", default_dataset=default_dataset)

search_emit(self, default_dataset='EMITL2ARFL')

Adds a NASA Earth Data search tool to the map with a default dataset for EMIT.

Parameters:

Name Type Description Default
default_dataset str

The default dataset to search for. Defaults to "EMITL2ARFL".

'EMITL2ARFL'
Source code in hypercoast/hypercoast.py
def search_emit(self, default_dataset="EMITL2ARFL"):
    """
    Adds a NASA Earth Data search tool to the map with a default dataset for
        EMIT.

    Args:
        default_dataset (str, optional): The default dataset to search for.
            Defaults to "EMITL2ARFL".
    """
    self.add("nasa_earth_data", default_dataset=default_dataset)

search_pace(self, default_dataset='PACE_OCI_L2_AOP_NRT')

Adds a NASA Earth Data search tool to the map with a default dataset for PACE.

Parameters:

Name Type Description Default
default_dataset str

The default dataset to search for. Defaults to "PACE_OCI_L2_AOP_NRT".

'PACE_OCI_L2_AOP_NRT'
Source code in hypercoast/hypercoast.py
def search_pace(self, default_dataset="PACE_OCI_L2_AOP_NRT"):
    """
    Adds a NASA Earth Data search tool to the map with a default dataset for
        PACE.

    Args:
        default_dataset (str, optional): The default dataset to search for.
            Defaults to "PACE_OCI_L2_AOP_NRT".
    """
    self.add("nasa_earth_data", default_dataset=default_dataset)

set_plot_options(self, add_marker_cluster=False, plot_type=None, overlay=False, position='bottomright', min_width=None, max_width=None, min_height=None, max_height=None, **kwargs)

Sets plotting options.

Parameters:

Name Type Description Default
add_marker_cluster bool

Whether to add a marker cluster. Defaults to False.

False
sample_scale float

A nominal scale in meters of the projection to sample in . Defaults to None.

required
plot_type str

The plot type can be one of "None", "bar", "scatter" or "hist". Defaults to None.

None
overlay bool

Whether to overlay plotted lines on the figure. Defaults to False.

False
position str

Position of the control, can be ‘bottomleft’, ‘bottomright’, ‘topleft’, or ‘topright’. Defaults to 'bottomright'.

'bottomright'
min_width int

Min width of the widget (in pixels), if None it will respect the content size. Defaults to None.

None
max_width int

Max width of the widget (in pixels), if None it will respect the content size. Defaults to None.

None
min_height int

Min height of the widget (in pixels), if None it will respect the content size. Defaults to None.

None
max_height int

Max height of the widget (in pixels), if None it will respect the content size. Defaults to None.

None
Source code in hypercoast/hypercoast.py
def set_plot_options(
    self,
    add_marker_cluster=False,
    plot_type=None,
    overlay=False,
    position="bottomright",
    min_width=None,
    max_width=None,
    min_height=None,
    max_height=None,
    **kwargs,
):
    """Sets plotting options.

    Args:
        add_marker_cluster (bool, optional): Whether to add a marker cluster.
            Defaults to False.
        sample_scale (float, optional):  A nominal scale in meters of the
            projection to sample in . Defaults to None.
        plot_type (str, optional): The plot type can be one of "None", "bar",
            "scatter" or "hist". Defaults to None.
        overlay (bool, optional): Whether to overlay plotted lines on the
            figure. Defaults to False.
        position (str, optional): Position of the control, can be
            ‘bottomleft’, ‘bottomright’, ‘topleft’, or ‘topright’. Defaults
            to 'bottomright'.
        min_width (int, optional): Min width of the widget (in pixels), if
            None it will respect the content size. Defaults to None.
        max_width (int, optional): Max width of the widget (in pixels), if
            None it will respect the content size. Defaults to None.
        min_height (int, optional): Min height of the widget (in pixels), if
            None it will respect the content size. Defaults to None.
        max_height (int, optional): Max height of the widget (in pixels), if
            None it will respect the content size. Defaults to None.

    """
    plot_options_dict = {}
    plot_options_dict["add_marker_cluster"] = add_marker_cluster
    plot_options_dict["plot_type"] = plot_type
    plot_options_dict["overlay"] = overlay
    plot_options_dict["position"] = position
    plot_options_dict["min_width"] = min_width
    plot_options_dict["max_width"] = max_width
    plot_options_dict["min_height"] = min_height
    plot_options_dict["max_height"] = max_height

    for key in kwargs:
        plot_options_dict[key] = kwargs[key]

    self._plot_options = plot_options_dict

    if not hasattr(self, "_plot_marker_cluster"):
        self._plot_marker_cluster = ipyleaflet.MarkerCluster(name="Marker Cluster")

    if add_marker_cluster and (self._plot_marker_cluster not in self.layers):
        self.add(self._plot_marker_cluster)

spectral_to_csv(self, filename, index=True, **kwargs)

Saves the spectral data to a CSV file.

Parameters:

Name Type Description Default
filename str

The output CSV file.

required
index bool

Whether to write the index. Defaults to True.

True
Source code in hypercoast/hypercoast.py
def spectral_to_csv(self, filename, index=True, **kwargs):
    """Saves the spectral data to a CSV file.

    Args:
        filename (str): The output CSV file.
        index (bool, optional): Whether to write the index. Defaults to True.
    """
    df = self.spectral_to_df()
    df = df.rename_axis("band")
    df.to_csv(filename, index=index, **kwargs)

spectral_to_df(self, **kwargs)

Converts the spectral data to a pandas DataFrame.

Returns:

Type Description
pd.DataFrame

The spectral data as a pandas DataFrame.

Source code in hypercoast/hypercoast.py
def spectral_to_df(self, **kwargs):
    """Converts the spectral data to a pandas DataFrame.

    Returns:
        pd.DataFrame: The spectral data as a pandas DataFrame.
    """
    import pandas as pd

    df = pd.DataFrame(self._spectral_data, **kwargs)
    return df

spectral_to_gdf(self, **kwargs)

Converts the spectral data to a GeoPandas GeoDataFrame.

Returns:

Type Description
gpd.DataFrame

The spectral data as a pandas DataFrame.

Source code in hypercoast/hypercoast.py
def spectral_to_gdf(self, **kwargs):
    """Converts the spectral data to a GeoPandas GeoDataFrame.

    Returns:
        gpd.DataFrame: The spectral data as a pandas DataFrame.
    """
    import geopandas as gpd
    from shapely.geometry import Point

    df = self.spectral_to_df()

    if len(df) == 0:
        return df

    # Step 1: Extract the coordinates from the columns
    if "wavelength" in df.columns:
        df = df.rename(columns={"wavelength": "latlon"})
    elif "wavelengths" in df.columns:
        df = df.rename(columns={"wavelengths": "latlon"})
    coordinates = [col.strip("()").split() for col in df.columns[1:]]
    coords = [(float(lat), float(lon)) for lat, lon in coordinates]

    # Step 2: Create Point geometries for each coordinate
    points = [Point(lon, lat) for lat, lon in coords]

    # Step 3: Create a GeoDataFrame
    df_transposed = df.set_index("latlon").T

    # Convert the column names to strings to ensure compatibility with GeoJSON
    df_transposed.columns = df_transposed.columns.astype(str)

    # Create the GeoDataFrame
    gdf = gpd.GeoDataFrame(df_transposed, geometry=points, **kwargs)

    # Set the coordinate reference system (CRS)
    gdf = gdf.set_geometry("geometry").set_crs("EPSG:4326")

    return gdf