Skip to content

geopypi module

Main module.

ImageOverlay (ImageOverlay)

ImageOverlay class.

Parameters:

Name Type Description Default
url str

http URL or local file path to the image.

required
bounds tuple

bounding box of the image in the format of (lower_left(lat, lon), upper_right(lat, lon)), such as ((13, -130), (32, -100)).

required
Source code in geopypi/geopypi.py
class ImageOverlay(ipyleaflet.ImageOverlay):
    """ImageOverlay class.

    Args:
        url (str): http URL or local file path to the image.
        bounds (tuple): bounding box of the image in the format of (lower_left(lat, lon), upper_right(lat, lon)), such as ((13, -130), (32, -100)).
    """

    def __init__(self, **kwargs):
        from base64 import b64encode
        from PIL import Image, ImageSequence
        from io import BytesIO

        try:
            url = kwargs.get("url")
            if not url.startswith("http"):
                url = os.path.abspath(url)
                if not os.path.exists(url):
                    raise FileNotFoundError("The provided file does not exist.")

                ext = os.path.splitext(url)[1][1:]  # file extension
                image = Image.open(url)

                f = BytesIO()
                if ext.lower() == "gif":
                    frames = []
                    # Loop over each frame in the animated image
                    for frame in ImageSequence.Iterator(image):
                        frame = frame.convert("RGBA")
                        b = BytesIO()
                        frame.save(b, format="gif")
                        frame = Image.open(b)
                        frames.append(frame)
                    frames[0].save(
                        f,
                        format="GIF",
                        save_all=True,
                        append_images=frames[1:],
                        loop=0,
                    )
                else:
                    image.save(f, ext)

                data = b64encode(f.getvalue())
                data = data.decode("ascii")
                url = "data:image/{};base64,".format(ext) + data
                kwargs["url"] = url
        except Exception as e:
            raise Exception(e)

        super().__init__(**kwargs)



    def add_kml(
        self,
        in_kml,
        layer_name="Untitled",
        style={},
        hover_style={},
        style_callback=None,
        fill_colors=["black"],
        info_mode="on_hover",
    ):
        """Adds a KML file to the map.

        Args:
            in_kml (str): The input file path or HTTP URL to the KML.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

        Raises:
            FileNotFoundError: The provided KML file could not be found.
        """

        if in_kml.startswith("http") and in_kml.endswith(".kml"):
            out_dir = os.path.abspath("./cache")
            if not os.path.exists(out_dir):
                os.makedirs(out_dir)
            in_kml = download_file(in_kml)
            if not os.path.exists(in_kml):
                raise FileNotFoundError("The downloaded kml file could not be found.")
        else:
            in_kml = os.path.abspath(in_kml)
            if not os.path.exists(in_kml):
                raise FileNotFoundError("The provided KML could not be found.")

        self.add_vector(
            in_kml,
            layer_name,
            style=style,
            hover_style=hover_style,
            style_callback=style_callback,
            fill_colors=fill_colors,
            info_mode=info_mode,
        )
    def add_data(
        self,
        data,
        column,
        colors=None,
        labels=None,
        cmap=None,
        scheme="Quantiles",
        k=5,
        add_legend=True,
        legend_title=None,
        legend_position="bottomright",
        legend_kwds=None,
        classification_kwds=None,
        layer_name="Untitled",
        style=None,
        hover_style=None,
        style_callback=None,
        marker_radius=10,
        marker_args=None,
        info_mode="on_hover",
        encoding="utf-8",
        **kwargs,
    ):
        """Add vector data to the map with a variety of classification schemes.

        Args:
            data (str | pd.DataFrame | gpd.GeoDataFrame): The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.
            column (str): The column to classify.
            cmap (str, optional): The name of a colormap recognized by matplotlib. Defaults to None.
            colors (list, optional): A list of colors to use for the classification. Defaults to None.
            labels (list, optional): A list of labels to use for the legend. Defaults to None.
            scheme (str, optional): Name of a choropleth classification scheme (requires mapclassify).
                Name of a choropleth classification scheme (requires mapclassify).
                A mapclassify.MapClassifier object will be used
                under the hood. Supported are all schemes provided by mapclassify (e.g.
                'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled',
                'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced',
                'JenksCaspallSampled', 'MaxP', 'MaximumBreaks',
                'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean',
                'UserDefined'). Arguments can be passed in classification_kwds.
            k (int, optional): Number of classes (ignored if scheme is None or if column is categorical). Default to 5.
            add_legend (bool, optional): Whether to add a legend to the map. Defaults to True.
            legend_title (str, optional): The title of the legend. Defaults to None.
            legend_position (str, optional): The position of the legend. Can be 'topleft', 'topright', 'bottomleft', or 'bottomright'. Defaults to 'bottomright'.
            legend_kwds (dict, optional): Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or `matplotlib.pyplot.colorbar`. Defaults to None.
                Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or
                Additional accepted keywords when `scheme` is specified:
                fmt : string
                    A formatting specification for the bin edges of the classes in the
                    legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``.
                labels : list-like
                    A list of legend labels to override the auto-generated labblels.
                    Needs to have the same number of elements as the number of
                    classes (`k`).
                interval : boolean (default False)
                    An option to control brackets from mapclassify legend.
                    If True, open/closed interval brackets are shown in the legend.
            classification_kwds (dict, optional): Keyword arguments to pass to mapclassify. Defaults to None.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to None.
                style is a dictionary of the following form:
                    style = {
                    "stroke": False,
                    "color": "#ff0000",
                    "weight": 1,
                    "opacity": 1,
                    "fill": True,
                    "fillColor": "#ffffff",
                    "fillOpacity": 1.0,
                    "dashArray": "9"
                    "clickable": True,
                }
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
                hover_style is a dictionary of the following form:
                    hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
                style_callback is a function that takes the feature as argument and should return a dictionary of the following form:
                style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
            encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".
            **kwargs: Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

        """

        gdf, legend_dict = classify(
            data=data,
            column=column,
            cmap=cmap,
            colors=colors,
            labels=labels,
            scheme=scheme,
            k=k,
            legend_kwds=legend_kwds,
            classification_kwds=classification_kwds,
        )

        if legend_title is None:
            legend_title = column

        if style is None:
            style = {
                # "stroke": False,
                # "color": "#ff0000",
                "weight": 1,
                "opacity": 1,
                # "fill": True,
                # "fillColor": "#ffffff",
                "fillOpacity": 1.0,
                # "dashArray": "9"
                # "clickable": True,
            }
            if colors is not None:
                style["color"] = "#000000"

        if hover_style is None:
            hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

        if style_callback is None:
            style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

        if gdf.geometry.geom_type.unique().tolist()[0] == "Point":
            columns = gdf.columns.tolist()
            if "category" in columns:
                columns.remove("category")
            if "color" in columns:
                columns.remove("color")
            if marker_args is None:
                marker_args = {}
            if "fill_color" not in marker_args:
                marker_args["fill_color"] = gdf["color"].tolist()
            if "stroke" not in marker_args:
                marker_args["stroke"] = False
            if "fill_opacity" not in marker_args:
                marker_args["fill_opacity"] = 0.8

            marker_args["radius"] = marker_radius

            self.add_markers(gdf[columns], layer_name=layer_name, **marker_args)
        else:
            self.add_gdf(
                gdf,
                layer_name=layer_name,
                style=style,
                hover_style=hover_style,
                style_callback=style_callback,
                info_mode=info_mode,
                encoding=encoding,
                **kwargs,
            )
        if add_legend:
            self.add_legend(
                title=legend_title, legend_dict=legend_dict, position=legend_position
            )

add_data(self, data, column, colors=None, labels=None, cmap=None, scheme='Quantiles', k=5, add_legend=True, legend_title=None, legend_position='bottomright', legend_kwds=None, classification_kwds=None, layer_name='Untitled', style=None, hover_style=None, style_callback=None, marker_radius=10, marker_args=None, info_mode='on_hover', encoding='utf-8', **kwargs)

Add vector data to the map with a variety of classification schemes.

Parameters:

Name Type Description Default
data str | pd.DataFrame | gpd.GeoDataFrame

The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.

required
column str

The column to classify.

required
cmap str

The name of a colormap recognized by matplotlib. Defaults to None.

None
colors list

A list of colors to use for the classification. Defaults to None.

None
labels list

A list of labels to use for the legend. Defaults to None.

None
scheme str

Name of a choropleth classification scheme (requires mapclassify). Name of a choropleth classification scheme (requires mapclassify). A mapclassify.MapClassifier object will be used under the hood. Supported are all schemes provided by mapclassify (e.g. 'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled', 'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced', 'JenksCaspallSampled', 'MaxP', 'MaximumBreaks', 'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean', 'UserDefined'). Arguments can be passed in classification_kwds.

'Quantiles'
k int

Number of classes (ignored if scheme is None or if column is categorical). Default to 5.

5
add_legend bool

Whether to add a legend to the map. Defaults to True.

True
legend_title str

The title of the legend. Defaults to None.

None
legend_position str

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

'bottomright'
legend_kwds dict

Keyword arguments to pass to :func:matplotlib.pyplot.legend or matplotlib.pyplot.colorbar. Defaults to None. Keyword arguments to pass to :func:matplotlib.pyplot.legend or Additional accepted keywords when scheme is specified: fmt : string A formatting specification for the bin edges of the classes in the legend. For example, to have no decimals: {"fmt": "{:.0f}"}. labels : list-like A list of legend labels to override the auto-generated labblels. Needs to have the same number of elements as the number of classes (k). interval : boolean (default False) An option to control brackets from mapclassify legend. If True, open/closed interval brackets are shown in the legend.

None
classification_kwds dict

Keyword arguments to pass to mapclassify. Defaults to None.

None
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to None. style is a dictionary of the following form: style = { "stroke": False, "color": "#ff0000", "weight": 1, "opacity": 1, "fill": True, "fillColor": "#ffffff", "fillOpacity": 1.0, "dashArray": "9" "clickable": True, }

None
hover_style dict

Hover style dictionary. Defaults to {}. hover_style is a dictionary of the following form: hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

None
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None. style_callback is a function that takes the feature as argument and should return a dictionary of the following form: style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

None
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'
encoding str

The encoding of the GeoJSON file. Defaults to "utf-8".

'utf-8'
**kwargs

Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

{}
Source code in geopypi/geopypi.py
def add_data(
    self,
    data,
    column,
    colors=None,
    labels=None,
    cmap=None,
    scheme="Quantiles",
    k=5,
    add_legend=True,
    legend_title=None,
    legend_position="bottomright",
    legend_kwds=None,
    classification_kwds=None,
    layer_name="Untitled",
    style=None,
    hover_style=None,
    style_callback=None,
    marker_radius=10,
    marker_args=None,
    info_mode="on_hover",
    encoding="utf-8",
    **kwargs,
):
    """Add vector data to the map with a variety of classification schemes.

    Args:
        data (str | pd.DataFrame | gpd.GeoDataFrame): The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.
        column (str): The column to classify.
        cmap (str, optional): The name of a colormap recognized by matplotlib. Defaults to None.
        colors (list, optional): A list of colors to use for the classification. Defaults to None.
        labels (list, optional): A list of labels to use for the legend. Defaults to None.
        scheme (str, optional): Name of a choropleth classification scheme (requires mapclassify).
            Name of a choropleth classification scheme (requires mapclassify).
            A mapclassify.MapClassifier object will be used
            under the hood. Supported are all schemes provided by mapclassify (e.g.
            'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled',
            'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced',
            'JenksCaspallSampled', 'MaxP', 'MaximumBreaks',
            'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean',
            'UserDefined'). Arguments can be passed in classification_kwds.
        k (int, optional): Number of classes (ignored if scheme is None or if column is categorical). Default to 5.
        add_legend (bool, optional): Whether to add a legend to the map. Defaults to True.
        legend_title (str, optional): The title of the legend. Defaults to None.
        legend_position (str, optional): The position of the legend. Can be 'topleft', 'topright', 'bottomleft', or 'bottomright'. Defaults to 'bottomright'.
        legend_kwds (dict, optional): Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or `matplotlib.pyplot.colorbar`. Defaults to None.
            Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or
            Additional accepted keywords when `scheme` is specified:
            fmt : string
                A formatting specification for the bin edges of the classes in the
                legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``.
            labels : list-like
                A list of legend labels to override the auto-generated labblels.
                Needs to have the same number of elements as the number of
                classes (`k`).
            interval : boolean (default False)
                An option to control brackets from mapclassify legend.
                If True, open/closed interval brackets are shown in the legend.
        classification_kwds (dict, optional): Keyword arguments to pass to mapclassify. Defaults to None.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to None.
            style is a dictionary of the following form:
                style = {
                "stroke": False,
                "color": "#ff0000",
                "weight": 1,
                "opacity": 1,
                "fill": True,
                "fillColor": "#ffffff",
                "fillOpacity": 1.0,
                "dashArray": "9"
                "clickable": True,
            }
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            hover_style is a dictionary of the following form:
                hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            style_callback is a function that takes the feature as argument and should return a dictionary of the following form:
            style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
        encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".
        **kwargs: Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

    """

    gdf, legend_dict = classify(
        data=data,
        column=column,
        cmap=cmap,
        colors=colors,
        labels=labels,
        scheme=scheme,
        k=k,
        legend_kwds=legend_kwds,
        classification_kwds=classification_kwds,
    )

    if legend_title is None:
        legend_title = column

    if style is None:
        style = {
            # "stroke": False,
            # "color": "#ff0000",
            "weight": 1,
            "opacity": 1,
            # "fill": True,
            # "fillColor": "#ffffff",
            "fillOpacity": 1.0,
            # "dashArray": "9"
            # "clickable": True,
        }
        if colors is not None:
            style["color"] = "#000000"

    if hover_style is None:
        hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

    if style_callback is None:
        style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

    if gdf.geometry.geom_type.unique().tolist()[0] == "Point":
        columns = gdf.columns.tolist()
        if "category" in columns:
            columns.remove("category")
        if "color" in columns:
            columns.remove("color")
        if marker_args is None:
            marker_args = {}
        if "fill_color" not in marker_args:
            marker_args["fill_color"] = gdf["color"].tolist()
        if "stroke" not in marker_args:
            marker_args["stroke"] = False
        if "fill_opacity" not in marker_args:
            marker_args["fill_opacity"] = 0.8

        marker_args["radius"] = marker_radius

        self.add_markers(gdf[columns], layer_name=layer_name, **marker_args)
    else:
        self.add_gdf(
            gdf,
            layer_name=layer_name,
            style=style,
            hover_style=hover_style,
            style_callback=style_callback,
            info_mode=info_mode,
            encoding=encoding,
            **kwargs,
        )
    if add_legend:
        self.add_legend(
            title=legend_title, legend_dict=legend_dict, position=legend_position
        )

add_kml(self, in_kml, layer_name='Untitled', style={}, hover_style={}, style_callback=None, fill_colors=['black'], info_mode='on_hover')

Adds a KML file to the map.

Parameters:

Name Type Description Default
in_kml str

The input file path or HTTP URL to the KML.

required
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to {}.

{}
hover_style dict

Hover style dictionary. Defaults to {}.

{}
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.

None
fill_colors list

The random colors to use for filling polygons. Defaults to ["black"].

['black']
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'

Exceptions:

Type Description
FileNotFoundError

The provided KML file could not be found.

Source code in geopypi/geopypi.py
def add_kml(
    self,
    in_kml,
    layer_name="Untitled",
    style={},
    hover_style={},
    style_callback=None,
    fill_colors=["black"],
    info_mode="on_hover",
):
    """Adds a KML file to the map.

    Args:
        in_kml (str): The input file path or HTTP URL to the KML.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
        fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

    Raises:
        FileNotFoundError: The provided KML file could not be found.
    """

    if in_kml.startswith("http") and in_kml.endswith(".kml"):
        out_dir = os.path.abspath("./cache")
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)
        in_kml = download_file(in_kml)
        if not os.path.exists(in_kml):
            raise FileNotFoundError("The downloaded kml file could not be found.")
    else:
        in_kml = os.path.abspath(in_kml)
        if not os.path.exists(in_kml):
            raise FileNotFoundError("The provided KML could not be found.")

    self.add_vector(
        in_kml,
        layer_name,
        style=style,
        hover_style=hover_style,
        style_callback=style_callback,
        fill_colors=fill_colors,
        info_mode=info_mode,
    )

Map (Map)

This is the map class that inherits from ipyleaflet.Map.

Parameters:

Name Type Description Default
ipyleaflet Map

The ipyleaflet.Map class.

required
Source code in geopypi/geopypi.py
class Map(ipyleaflet.Map):
    """This is the map class that inherits from ipyleaflet.Map.

    Args:
        ipyleaflet (Map): The ipyleaflet.Map class.
    """    

    def __init__(self, center=[20, 0], zoom=2, **kwargs):
        """Initialize the map.

        Args:
            center (list, optional): Set the center of the map. Defaults to [20, 0].
            zoom (int, optional): Set the zoom level of the map. Defaults to 2.
        """        

        if "scroll_wheel_zoom" not in kwargs:
            kwargs["scroll_wheel_zoom"] = True

        if "add_layer_control" not in kwargs:
            layer_control_flag = True

        else:
            layer_control_flag = kwargs["add_layer_control"]
        kwargs.pop("add_layer_control", None)


        super().__init__(center=center, zoom=zoom, **kwargs)
        if layer_control_flag:
            self.add_layers_control()
        self.geojson_layers = []

    def add_tile_layer(self, url, name, **kwargs):
        layer = ipyleaflet.TileLayer(url=url, name=name, **kwargs)
        self.add(layer)   

    def add_basemap(self, name):
        """
        Adds a basemap to the current map.

        Args:
            name (str or object): The name of the basemap as a string, or an object representing the basemap.

        Raises:
            TypeError: If the name is neither a string nor an object representing a basemap.

        Returns:
            None
        """       
        if isinstance(name, str):
            url = eval(f"basemaps.{name}").build_url()
            self.add_tile_layer(url, name) 
        else:
            self.add(name)


    def add_legend(
        self,
        title="Legend",
        legend_dict=None,
        labels=None,
        colors=None,
        position="bottomright",
        builtin_legend=None,
        layer_name=None,
        **kwargs,
    ):
        """Adds a customized basemap to the map.

        Args:
            title (str, optional): Title of the legend. Defaults to 'Legend'.
            legend_dict (dict, optional): A dictionary containing legend items as keys and color as values. If provided, legend_keys and legend_colors will be ignored. Defaults to None.
            labels (list, optional): A list of legend keys. Defaults to None.
            colors (list, optional): A list of legend colors. Defaults to None.
            position (str, optional): Position of the legend. Defaults to 'bottomright'.
            builtin_legend (str, optional): Name of the builtin legend to add to the map. Defaults to None.
            layer_name (str, optional): Layer name of the legend to be associated with. Defaults to None.

        """
        import pkg_resources
        from IPython.display import display

        pkg_dir = os.path.dirname(
            pkg_resources.resource_filename("leafmap", "leafmap.py")
        )
        legend_template = os.path.join(pkg_dir, "data/template/legend.html")

        if "min_width" not in kwargs.keys():
            min_width = None
        if "max_width" not in kwargs.keys():
            max_width = None
        else:
            max_width = kwargs["max_width"]
        if "min_height" not in kwargs.keys():
            min_height = None
        else:
            min_height = kwargs["min_height"]
        if "max_height" not in kwargs.keys():
            max_height = None
        else:
            max_height = kwargs["max_height"]
        if "height" not in kwargs.keys():
            height = None
        else:
            height = kwargs["height"]
        if "width" not in kwargs.keys():
            width = None
        else:
            width = kwargs["width"]

        if width is None:
            max_width = "300px"
        if height is None:
            max_height = "400px"

        if not os.path.exists(legend_template):
            print("The legend template does not exist.")
            return

        if labels is not None:
            if not isinstance(labels, list):
                print("The legend keys must be a list.")
                return
        else:
            labels = ["One", "Two", "Three", "Four", "etc"]

        if colors is not None:
            if not isinstance(colors, list):
                print("The legend colors must be a list.")
                return
            elif all(isinstance(item, tuple) for item in colors):
                try:
                    colors = [rgb_to_hex(x) for x in colors]
                except Exception as e:
                    print(e)
            elif all((item.startswith("#") and len(item) == 7) for item in colors):
                pass
            elif all((len(item) == 6) for item in colors):
                pass
            else:
                print("The legend colors must be a list of tuples.")
                return
        else:
            colors = [
                "#8DD3C7",
                "#FFFFB3",
                "#BEBADA",
                "#FB8072",
                "#80B1D3",
            ]

        if len(labels) != len(colors):
            print("The legend keys and values must be the same length.")
            return

        allowed_builtin_legends = builtin_legends.keys()
        if builtin_legend is not None:
            if builtin_legend not in allowed_builtin_legends:
                print(
                    "The builtin legend must be one of the following: {}".format(
                        ", ".join(allowed_builtin_legends)
                    )
                )
                return
            else:
                legend_dict = builtin_legends[builtin_legend]
                labels = list(legend_dict.keys())
                colors = list(legend_dict.values())

        if legend_dict is not None:
            if not isinstance(legend_dict, dict):
                print("The legend dict must be a dictionary.")
                return
            else:
                labels = list(legend_dict.keys())
                colors = list(legend_dict.values())
                if all(isinstance(item, tuple) for item in colors):
                    try:
                        colors = [rgb_to_hex(x) for x in colors]
                    except Exception as e:
                        print(e)

        allowed_positions = [
            "topleft",
            "topright",
            "bottomleft",
            "bottomright",
        ]
        if position not in allowed_positions:
            print(
                "The position must be one of the following: {}".format(
                    ", ".join(allowed_positions)
                )
            )
            return

        header = []
        content = []
        footer = []

        with open(legend_template) as f:
            lines = f.readlines()
            lines[3] = lines[3].replace("Legend", title)
            header = lines[:6]
            footer = lines[11:]

        for index, key in enumerate(labels):
            color = colors[index]
            if not color.startswith("#"):
                color = "#" + color
            item = "      <li><span style='background:{};'></span>{}</li>\n".format(
                color, key
            )
            content.append(item)

        legend_html = header + content + footer
        legend_text = "".join(legend_html)

        try:
            legend_output_widget = widgets.Output(
                layout={
                    # "border": "1px solid black",
                    "max_width": max_width,
                    "min_width": min_width,
                    "max_height": max_height,
                    "min_height": min_height,
                    "height": height,
                    "width": width,
                    "overflow": "scroll",
                }
            )
            legend_control = ipyleaflet.WidgetControl(
                widget=legend_output_widget, position=position
            )
            legend_widget = widgets.HTML(value=legend_text)
            with legend_output_widget:
                display(legend_widget)

            self.legend_widget = legend_output_widget
            self.legend_control = legend_control
            self.add(legend_control)

        except Exception as e:
            raise Exception(e)
    def add_layers_control(self, position="topright"):
        """Adds a layers control to the map.

        Args:
            position (str, optional): The position of the layers control. Defaults to "topright".
        """
        self.add_control(ipyleaflet.LayersControl(position=position))


    def add_geojson(
        self,
        in_geojson,
        layer_name="Untitled",
        style={},
        hover_style={},
        style_callback=None,
        fill_colors=["black"],
        info_mode="on_hover",
        zoom_to_layer=False,
        encoding="utf-8",
        **kwargs,
    ):
        """Adds a GeoJSON file to the map.

        Args:
            in_geojson (str | dict): The file path or http URL to the input GeoJSON or a dictionary containing the geojson.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
            zoom_to_layer (bool, optional): Whether to zoom to the layer after adding it to the map. Defaults to False.
            encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".

        Raises:
            FileNotFoundError: The provided GeoJSON file could not be found.
        """
        import json
        import random
        import requests

        style_callback_only = False

        if len(style) == 0 and style_callback is not None:
            style_callback_only = True

        try:
            if isinstance(in_geojson, str):
                if in_geojson.startswith("http"):
                    if is_jupyterlite():
                        import pyodide

                        output = os.path.basename(in_geojson)

                        output = os.path.abspath(output)
                        obj = pyodide.http.open_url(in_geojson)
                        with open(output, "w") as fd:
                            shutil.copyfileobj(obj, fd)
                        with open(output, "r") as fd:
                            data = json.load(fd)
                    else:
                        in_geojson = github_raw_url(in_geojson)
                        data = requests.get(in_geojson).json()
                else:
                    in_geojson = os.path.abspath(in_geojson)
                    if not os.path.exists(in_geojson):
                        raise FileNotFoundError(
                            "The provided GeoJSON file could not be found."
                        )

                    with open(in_geojson, encoding=encoding) as f:
                        data = json.load(f)
            elif isinstance(in_geojson, dict):
                data = in_geojson
            else:
                raise TypeError("The input geojson must be a type of str or dict.")
        except Exception as e:
            raise Exception(e)

        geom_type = get_geometry_type(data)

        if not style:
            style = {
                # "stroke": True,
                "color": "#3388ff",
                "weight": 2,
                "opacity": 1,
                "fill": True,
                "fillColor": "#3388ff",
                "fillOpacity": 0.2,
                # "dashArray": "9"
                # "clickable": True,
            }

            if geom_type in ["LineString", "MultiLineString"]:
                style["fill"] = False

        elif "weight" not in style:
            style["weight"] = 1

        if not hover_style:
            hover_style = {"weight": style["weight"] + 2, "fillOpacity": 0}

        def random_color(feature):
            return {
                "color": "black",
                "fillColor": random.choice(fill_colors),
            }

        toolbar_button = widgets.ToggleButton(
            value=True,
            tooltip="Toolbar",
            icon="info",
            layout=widgets.Layout(
                width="28px", height="28px", padding="0px 0px 0px 4px"
            ),
        )

        close_button = widgets.ToggleButton(
            value=False,
            tooltip="Close the tool",
            icon="times",
            # button_style="primary",
            layout=widgets.Layout(
                height="28px", width="28px", padding="0px 0px 0px 4px"
            ),
        )

        html = widgets.HTML()
        html.layout.margin = "0px 10px 0px 10px"
        html.layout.max_height = "250px"
        html.layout.max_width = "250px"

        output_widget = widgets.VBox(
            [widgets.HBox([toolbar_button, close_button]), html]
        )
        info_control = ipyleaflet.WidgetControl(
            widget=output_widget, position="bottomright"
        )

        if info_mode in ["on_hover", "on_click"]:
            self.add(info_control)

        def toolbar_btn_click(change):
            if change["new"]:
                close_button.value = False
                output_widget.children = [
                    widgets.VBox([widgets.HBox([toolbar_button, close_button]), html])
                ]
            else:
                output_widget.children = [widgets.HBox([toolbar_button, close_button])]

        toolbar_button.observe(toolbar_btn_click, "value")

        def close_btn_click(change):
            if change["new"]:
                toolbar_button.value = False
                if info_control in self.controls:
                    self.remove_control(info_control)
                output_widget.close()

        close_button.observe(close_btn_click, "value")

        if "fields" in kwargs:
            fields = kwargs["fields"]
            kwargs.pop("fields")
        else:
            fields = None

        def update_html(feature, fields=fields, **kwargs):
            if fields is None:
                fields = list(feature["properties"].keys())
                if "style" in fields:
                    fields.remove("style")

            value = [
                "<b>{}: </b>{}<br>".format(prop, feature["properties"][prop])
                for prop in fields
            ]

            value = """{}""".format("".join(value))
            html.value = value

        if style_callback is None:
            style_callback = random_color

        if style_callback_only:
            geojson = ipyleaflet.GeoJSON(
                data=data,
                hover_style=hover_style,
                style_callback=style_callback,
                name=layer_name,
            )
        else:
            geojson = ipyleaflet.GeoJSON(
                data=data,
                style=style,
                hover_style=hover_style,
                style_callback=style_callback,
                name=layer_name,
            )

        if info_mode == "on_hover":
            geojson.on_hover(update_html)
        elif info_mode == "on_click":
            geojson.on_click(update_html)

        self.add(geojson)
        self.geojson_layers.append(geojson)

        if not hasattr(self, "json_layer_dict"):
            self.json_layer_dict = {}

        params = {
            "data": geojson,
            "style": style,
            "hover_style": hover_style,
            "style_callback": style_callback,
        }
        self.json_layer_dict[layer_name] = params

        if zoom_to_layer:
            try:
                import numpy as np
                import geopandas as gpd

                gdf = gpd.GeoDataFrame.from_features(data)
                if gdf.crs is None:
                    gdf.crs = "EPSG:4326"
                bounds = gdf.to_crs(epsg="4326").bounds
                west = np.min(bounds["minx"])
                south = np.min(bounds["miny"])
                east = np.max(bounds["maxx"])
                north = np.max(bounds["maxy"])
                self.fit_bounds([[south, east], [north, west]])
            except Exception as e:
                print(e)
    def add_data(
        self,
        data,
        column,
        colors=None,
        labels=None,
        cmap=None,
        scheme="Quantiles",
        k=5,
        add_legend=True,
        legend_title=None,
        legend_position="bottomright",
        legend_kwds=None,
        classification_kwds=None,
        layer_name="Untitled",
        style=None,
        hover_style=None,
        style_callback=None,
        marker_radius=10,
        marker_args=None,
        info_mode="on_hover",
        encoding="utf-8",
        **kwargs,
    ):
        """Add vector data to the map with a variety of classification schemes.

        Args:
            data (str | pd.DataFrame | gpd.GeoDataFrame): The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.
            column (str): The column to classify.
            cmap (str, optional): The name of a colormap recognized by matplotlib. Defaults to None.
            colors (list, optional): A list of colors to use for the classification. Defaults to None.
            labels (list, optional): A list of labels to use for the legend. Defaults to None.
            scheme (str, optional): Name of a choropleth classification scheme (requires mapclassify).
                Name of a choropleth classification scheme (requires mapclassify).
                A mapclassify.MapClassifier object will be used
                under the hood. Supported are all schemes provided by mapclassify (e.g.
                'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled',
                'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced',
                'JenksCaspallSampled', 'MaxP', 'MaximumBreaks',
                'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean',
                'UserDefined'). Arguments can be passed in classification_kwds.
            k (int, optional): Number of classes (ignored if scheme is None or if column is categorical). Default to 5.
            add_legend (bool, optional): Whether to add a legend to the map. Defaults to True.
            legend_title (str, optional): The title of the legend. Defaults to None.
            legend_position (str, optional): The position of the legend. Can be 'topleft', 'topright', 'bottomleft', or 'bottomright'. Defaults to 'bottomright'.
            legend_kwds (dict, optional): Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or `matplotlib.pyplot.colorbar`. Defaults to None.
                Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or
                Additional accepted keywords when `scheme` is specified:
                fmt : string
                    A formatting specification for the bin edges of the classes in the
                    legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``.
                labels : list-like
                    A list of legend labels to override the auto-generated labblels.
                    Needs to have the same number of elements as the number of
                    classes (`k`).
                interval : boolean (default False)
                    An option to control brackets from mapclassify legend.
                    If True, open/closed interval brackets are shown in the legend.
            classification_kwds (dict, optional): Keyword arguments to pass to mapclassify. Defaults to None.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to None.
                style is a dictionary of the following form:
                    style = {
                    "stroke": False,
                    "color": "#ff0000",
                    "weight": 1,
                    "opacity": 1,
                    "fill": True,
                    "fillColor": "#ffffff",
                    "fillOpacity": 1.0,
                    "dashArray": "9"
                    "clickable": True,
                }
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
                hover_style is a dictionary of the following form:
                    hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
                style_callback is a function that takes the feature as argument and should return a dictionary of the following form:
                style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
            encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".
            **kwargs: Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

        """

        gdf, legend_dict = classify(
            data=data,
            column=column,
            cmap=cmap,
            colors=colors,
            labels=labels,
            scheme=scheme,
            k=k,
            legend_kwds=legend_kwds,
            classification_kwds=classification_kwds,
        )

        if legend_title is None:
            legend_title = column

        if style is None:
            style = {
                # "stroke": False,
                # "color": "#ff0000",
                "weight": 1,
                "opacity": 1,
                # "fill": True,
                # "fillColor": "#ffffff",
                "fillOpacity": 1.0,
                # "dashArray": "9"
                # "clickable": True,
            }
            if colors is not None:
                style["color"] = "#000000"

        if hover_style is None:
            hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

        if style_callback is None:
            style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

        if gdf.geometry.geom_type.unique().tolist()[0] == "Point":
            columns = gdf.columns.tolist()
            if "category" in columns:
                columns.remove("category")
            if "color" in columns:
                columns.remove("color")
            if marker_args is None:
                marker_args = {}
            if "fill_color" not in marker_args:
                marker_args["fill_color"] = gdf["color"].tolist()
            if "stroke" not in marker_args:
                marker_args["stroke"] = False
            if "fill_opacity" not in marker_args:
                marker_args["fill_opacity"] = 0.8

            marker_args["radius"] = marker_radius

            self.add_markers(gdf[columns], layer_name=layer_name, **marker_args)
        else:
            self.add_gdf(
                gdf,
                layer_name=layer_name,
                style=style,
                hover_style=hover_style,
                style_callback=style_callback,
                info_mode=info_mode,
                encoding=encoding,
                **kwargs,
            )
        if add_legend:
            self.add_legend(
                title=legend_title, legend_dict=legend_dict, position=legend_position
            )    

    def add_shp(
        self,
        in_shp,
        layer_name="Untitled",
        style={},
        hover_style={},
        style_callback=None,
        fill_colors=["black"],
        info_mode="on_hover",
        zoom_to_layer=False,
        encoding="utf-8",
    ):
        """Adds a shapefile to the map.

        Args:
            in_shp (str): The input file path or HTTP URL (*.zip) to the shapefile.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
            zoom_to_layer (bool, optional): Whether to zoom to the layer after adding it to the map. Defaults to False.
            encoding (str, optional): The encoding of the shapefile. Defaults to "utf-8".

        Raises:
            FileNotFoundError: The provided shapefile could not be found.
        """

        import glob

        if in_shp.startswith("http") and in_shp.endswith(".zip"):
            out_dir = os.path.dirname(temp_file_path(".shp"))
            if not os.path.exists(out_dir):
                os.makedirs(out_dir)
            basename = os.path.basename(in_shp)
            filename = os.path.join(out_dir, basename)
            download_file(in_shp, filename)
            files = list(glob.glob(os.path.join(out_dir, "*.shp")))
            if len(files) > 0:
                in_shp = files[0]
            else:
                raise FileNotFoundError(
                    "The downloaded zip file does not contain any shapefile in the root directory."
                )
        else:
            in_shp = os.path.abspath(in_shp)
            if not os.path.exists(in_shp):
                raise FileNotFoundError("The provided shapefile could not be found.")

        geojson = shp_to_geojson(in_shp, encoding=encoding)
        self.add_geojson(
            geojson,
            layer_name,
            style,
            hover_style,
            style_callback,
            fill_colors,
            info_mode,
            zoom_to_layer,
            encoding,
        )



        import geopandas as gpd
        from ipyleaflet import GeoData
        from shapely.geometry import Point, LineString

    def add_vector(self, data):
        """
        Add vector data to the map.

        Args:
            data (str or geopandas.GeoDataFrame): The vector data to add. This can be a file path or a GeoDataFrame.
        """
        import geopandas as gpd
        from ipyleaflet import GeoData

        if isinstance(data, gpd.GeoDataFrame):
            vector_layer = GeoData(geo_dataframe=data)

        elif isinstance(data, str):
            vector_layer = GeoData(geo_dataframe=gpd.read_file(data))

        else:
            raise ValueError("Unsupported data format. Please provide a GeoDataFrame or a file path.")

        self.add_layer(vector_layer)



    def add_image(self, url, bounds, name="image", **kwargs):
        """
        Adds an image overlay to the map.

        Args:
            url (str): The URL of the image to add.
            bounds (list): The bounds of the image as a list of tuples.
            name (str, optional): The name of the image overlay. Defaults to "image".
        """
        layer = ipyleaflet.ImageOverlay(url=url, bounds=bounds, name=name, **kwargs)
        self.add(layer)

    def add_raster(self, data, name="raster", zoom_to_layer=True, **kwargs):
        """Adds a raster layer to the map.

        Args:
            data (str): The path to the raster file.
            name (str, optional): The name of the layer. Defaults to "raster".
        """

        try:
            from localtileserver import TileClient, get_leaflet_tile_layer
        except ImportError:
            raise ImportError("Please install the localtileserver package.")

        client = TileClient(data)
        layer = get_leaflet_tile_layer(client, name=name, **kwargs)
        self.add(layer)

        if zoom_to_layer:
            self.center = client.center()
            self.zoom = client.default_zoom


        client = TileClient(data)
        layer = get_leaflet_tile_layer(client, name=name, **kwargs)
        self.add(layer)

        if zoom_to_layer:
            self.center = client.center()
            self.zoom = client.default_zoom

    def add_zoom_slider(
            self, description="Zoom level:", min=0, max=24, value=10, position="topright"
    ):
        """Adds a zoom slider to the map.

        Args:
            position (str, optional): The position of the zoom slider. Defaults to "topright".
        """
        zoom_slider = widgets.IntSlider(
            description=description, min=min, max=max, value=value
        )

        control = ipyleaflet.WidgetControl(widget=zoom_slider, position=position)
        self.add(control)
        widgets.jslink((zoom_slider, "value"), (self, "zoom"))


    def add_widget(self, widget, position="topright"):
        """Adds a widget to the map.

        Args:
            widget (object): The widget to add.
            position (str, optional): The position of the widget. Defaults to "topright".

        Returns:
            None
        """
        control = ipyleaflet.WidgetControl(widget=widget, position=position)
        self.add(control)

    def add_gdf(
        self,
        gdf,
        layer_name="Untitled",
        style={},
        hover_style={},
        style_callback=None,
        fill_colors=["black"],
        info_mode="on_hover",
        zoom_to_layer=False,
        encoding="utf-8",
        **kwargs,
    ):
        """Adds a GeoDataFrame to the map.

        Args:
            gdf (GeoDataFrame): A GeoPandas GeoDataFrame.
            layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
            style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
            hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
            info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
            zoom_to_layer (bool, optional): Whether to zoom to the layer. Defaults to False.
            encoding (str, optional): The encoding of the GeoDataFrame. Defaults to "utf-8".
        """
        for col in gdf.columns:
            try:
                if gdf[col].dtype in ["datetime64[ns]", "datetime64[ns, UTC]"]:
                    gdf[col] = gdf[col].astype(str)
            except:
                pass

        data = gdf_to_geojson(gdf, epsg="4326")

        self.add_geojson(
            data,
            layer_name,
            style,
            hover_style,
            style_callback,
            fill_colors,
            info_mode,
            zoom_to_layer,
            encoding,
            **kwargs,
        )

        if zoom_to_layer:
            import numpy as np

            bounds = gdf.to_crs(epsg="4326").bounds
            west = np.min(bounds["minx"])
            south = np.min(bounds["miny"])
            east = np.max(bounds["maxx"])
            north = np.max(bounds["maxy"])
            self.fit_bounds([[south, east], [north, west]])

    def add_opacity_slider(
            self, layer_index=-1, description="Opacity:", position="topright"):
        """Adds an opacity slider for the specified layer.

        Args:
            layer (object): The layer for which to add the opacity slider.
            description (str, optional): The description of the opacity slider. Defaults to "Opacity:".
            position (str, optional): The position of the opacity slider. Defaults to "topright".

        Returns:
            None
        """
        layer = self.layers[layer_index]
        opacity_slider = widgets.FloatSlider(
            description=description, min=0, max=1, value=layer.opacity, style={"description_width": "initial"}
        )

        def update_opacity(change):
            """
            Updates the opacity of a layer based on the new value from a slider.

            This function is designed to be used as a callback for an ipywidgets slider. 
            It takes a dictionary with a "new" key representing the new value of the slider, 
            and sets the opacity of a global layer variable to this new value.

            Args:
            change (dict): A dictionary with a "new" key representing the new value of the slider.

            Returns:
                None
            """
            layer.opacity = change["new"]

        opacity_slider.observe(update_opacity, "value")

        control = ipyleaflet.WidgetControl(widget=opacity_slider, position=position)
        self.add(control)

        from ipywidgets import Dropdown, Button, HBox

    def add_basemap_gui(self, position="topright"):
        """Adds a basemap GUI to the map.

        Args:
            position (str, optional): The position of the basemap GUI. Defaults to "topright".

        Returns:
            None
        """
        basemap_selector = widgets.Dropdown(
            options=[
                "OpenStreetMap",
                "OpenTopoMap",
                "Esri.WorldImagery",
                "CartoDB.DarkMatter",
                "Esri.NatGeoWorldMap",
            ],
            value="OpenStreetMap",
        )

        close_button = widgets.Button(
            icon='times', 
            layout={'width': '35px'}  
        )

        def on_basemap_change(change):
            """
            Handles the event of changing the basemap on the map.

            This function is designed to be used as a callback for an ipywidgets dropdown. 
            It takes a dictionary with a "new" key representing the new value of the dropdown, 
            and calls the add_basemap method with this new value.

            Args:
                change (dict): A dictionary with a "new" key representing the new value of the dropdown.

            Returns:
                None
            """

            self.add_basemap(change['new'])


        def on_close_button_clicked(button):
            """
            Handles the event of clicking the close button on a control.

            This function is designed to be used as a callback for a button click event. 
            It takes a button instance as an argument, and calls the remove method 
            to remove a global control variable from the map.

            Args:
                button (ipywidgets.Button): The button that was clicked.

            Returns:
                None
            """
            self.remove(control)

        basemap_selector.observe(on_basemap_change, "value")
        close_button.on_click(on_close_button_clicked)

        widget_box = widgets.HBox([basemap_selector, close_button])
        control = ipyleaflet.WidgetControl(widget=widget_box, position=position)
        self.add(control)    

__init__(self, center=[20, 0], zoom=2, **kwargs) special

Initialize the map.

Parameters:

Name Type Description Default
center list

Set the center of the map. Defaults to [20, 0].

[20, 0]
zoom int

Set the zoom level of the map. Defaults to 2.

2
Source code in geopypi/geopypi.py
def __init__(self, center=[20, 0], zoom=2, **kwargs):
    """Initialize the map.

    Args:
        center (list, optional): Set the center of the map. Defaults to [20, 0].
        zoom (int, optional): Set the zoom level of the map. Defaults to 2.
    """        

    if "scroll_wheel_zoom" not in kwargs:
        kwargs["scroll_wheel_zoom"] = True

    if "add_layer_control" not in kwargs:
        layer_control_flag = True

    else:
        layer_control_flag = kwargs["add_layer_control"]
    kwargs.pop("add_layer_control", None)


    super().__init__(center=center, zoom=zoom, **kwargs)
    if layer_control_flag:
        self.add_layers_control()
    self.geojson_layers = []

add_basemap(self, name)

Adds a basemap to the current map.

Parameters:

Name Type Description Default
name str or object

The name of the basemap as a string, or an object representing the basemap.

required

Exceptions:

Type Description
TypeError

If the name is neither a string nor an object representing a basemap.

Returns:

Type Description

None

Source code in geopypi/geopypi.py
def add_basemap(self, name):
    """
    Adds a basemap to the current map.

    Args:
        name (str or object): The name of the basemap as a string, or an object representing the basemap.

    Raises:
        TypeError: If the name is neither a string nor an object representing a basemap.

    Returns:
        None
    """       
    if isinstance(name, str):
        url = eval(f"basemaps.{name}").build_url()
        self.add_tile_layer(url, name) 
    else:
        self.add(name)

add_basemap_gui(self, position='topright')

Adds a basemap GUI to the map.

Parameters:

Name Type Description Default
position str

The position of the basemap GUI. Defaults to "topright".

'topright'

Returns:

Type Description

None

Source code in geopypi/geopypi.py
def add_basemap_gui(self, position="topright"):
    """Adds a basemap GUI to the map.

    Args:
        position (str, optional): The position of the basemap GUI. Defaults to "topright".

    Returns:
        None
    """
    basemap_selector = widgets.Dropdown(
        options=[
            "OpenStreetMap",
            "OpenTopoMap",
            "Esri.WorldImagery",
            "CartoDB.DarkMatter",
            "Esri.NatGeoWorldMap",
        ],
        value="OpenStreetMap",
    )

    close_button = widgets.Button(
        icon='times', 
        layout={'width': '35px'}  
    )

    def on_basemap_change(change):
        """
        Handles the event of changing the basemap on the map.

        This function is designed to be used as a callback for an ipywidgets dropdown. 
        It takes a dictionary with a "new" key representing the new value of the dropdown, 
        and calls the add_basemap method with this new value.

        Args:
            change (dict): A dictionary with a "new" key representing the new value of the dropdown.

        Returns:
            None
        """

        self.add_basemap(change['new'])


    def on_close_button_clicked(button):
        """
        Handles the event of clicking the close button on a control.

        This function is designed to be used as a callback for a button click event. 
        It takes a button instance as an argument, and calls the remove method 
        to remove a global control variable from the map.

        Args:
            button (ipywidgets.Button): The button that was clicked.

        Returns:
            None
        """
        self.remove(control)

    basemap_selector.observe(on_basemap_change, "value")
    close_button.on_click(on_close_button_clicked)

    widget_box = widgets.HBox([basemap_selector, close_button])
    control = ipyleaflet.WidgetControl(widget=widget_box, position=position)
    self.add(control)    

add_data(self, data, column, colors=None, labels=None, cmap=None, scheme='Quantiles', k=5, add_legend=True, legend_title=None, legend_position='bottomright', legend_kwds=None, classification_kwds=None, layer_name='Untitled', style=None, hover_style=None, style_callback=None, marker_radius=10, marker_args=None, info_mode='on_hover', encoding='utf-8', **kwargs)

Add vector data to the map with a variety of classification schemes.

Parameters:

Name Type Description Default
data str | pd.DataFrame | gpd.GeoDataFrame

The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.

required
column str

The column to classify.

required
cmap str

The name of a colormap recognized by matplotlib. Defaults to None.

None
colors list

A list of colors to use for the classification. Defaults to None.

None
labels list

A list of labels to use for the legend. Defaults to None.

None
scheme str

Name of a choropleth classification scheme (requires mapclassify). Name of a choropleth classification scheme (requires mapclassify). A mapclassify.MapClassifier object will be used under the hood. Supported are all schemes provided by mapclassify (e.g. 'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled', 'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced', 'JenksCaspallSampled', 'MaxP', 'MaximumBreaks', 'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean', 'UserDefined'). Arguments can be passed in classification_kwds.

'Quantiles'
k int

Number of classes (ignored if scheme is None or if column is categorical). Default to 5.

5
add_legend bool

Whether to add a legend to the map. Defaults to True.

True
legend_title str

The title of the legend. Defaults to None.

None
legend_position str

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

'bottomright'
legend_kwds dict

Keyword arguments to pass to :func:matplotlib.pyplot.legend or matplotlib.pyplot.colorbar. Defaults to None. Keyword arguments to pass to :func:matplotlib.pyplot.legend or Additional accepted keywords when scheme is specified: fmt : string A formatting specification for the bin edges of the classes in the legend. For example, to have no decimals: {"fmt": "{:.0f}"}. labels : list-like A list of legend labels to override the auto-generated labblels. Needs to have the same number of elements as the number of classes (k). interval : boolean (default False) An option to control brackets from mapclassify legend. If True, open/closed interval brackets are shown in the legend.

None
classification_kwds dict

Keyword arguments to pass to mapclassify. Defaults to None.

None
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to None. style is a dictionary of the following form: style = { "stroke": False, "color": "#ff0000", "weight": 1, "opacity": 1, "fill": True, "fillColor": "#ffffff", "fillOpacity": 1.0, "dashArray": "9" "clickable": True, }

None
hover_style dict

Hover style dictionary. Defaults to {}. hover_style is a dictionary of the following form: hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

None
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None. style_callback is a function that takes the feature as argument and should return a dictionary of the following form: style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

None
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'
encoding str

The encoding of the GeoJSON file. Defaults to "utf-8".

'utf-8'
**kwargs

Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

{}
Source code in geopypi/geopypi.py
def add_data(
    self,
    data,
    column,
    colors=None,
    labels=None,
    cmap=None,
    scheme="Quantiles",
    k=5,
    add_legend=True,
    legend_title=None,
    legend_position="bottomright",
    legend_kwds=None,
    classification_kwds=None,
    layer_name="Untitled",
    style=None,
    hover_style=None,
    style_callback=None,
    marker_radius=10,
    marker_args=None,
    info_mode="on_hover",
    encoding="utf-8",
    **kwargs,
):
    """Add vector data to the map with a variety of classification schemes.

    Args:
        data (str | pd.DataFrame | gpd.GeoDataFrame): The data to classify. It can be a filepath to a vector dataset, a pandas dataframe, or a geopandas geodataframe.
        column (str): The column to classify.
        cmap (str, optional): The name of a colormap recognized by matplotlib. Defaults to None.
        colors (list, optional): A list of colors to use for the classification. Defaults to None.
        labels (list, optional): A list of labels to use for the legend. Defaults to None.
        scheme (str, optional): Name of a choropleth classification scheme (requires mapclassify).
            Name of a choropleth classification scheme (requires mapclassify).
            A mapclassify.MapClassifier object will be used
            under the hood. Supported are all schemes provided by mapclassify (e.g.
            'BoxPlot', 'EqualInterval', 'FisherJenks', 'FisherJenksSampled',
            'HeadTailBreaks', 'JenksCaspall', 'JenksCaspallForced',
            'JenksCaspallSampled', 'MaxP', 'MaximumBreaks',
            'NaturalBreaks', 'Quantiles', 'Percentiles', 'StdMean',
            'UserDefined'). Arguments can be passed in classification_kwds.
        k (int, optional): Number of classes (ignored if scheme is None or if column is categorical). Default to 5.
        add_legend (bool, optional): Whether to add a legend to the map. Defaults to True.
        legend_title (str, optional): The title of the legend. Defaults to None.
        legend_position (str, optional): The position of the legend. Can be 'topleft', 'topright', 'bottomleft', or 'bottomright'. Defaults to 'bottomright'.
        legend_kwds (dict, optional): Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or `matplotlib.pyplot.colorbar`. Defaults to None.
            Keyword arguments to pass to :func:`matplotlib.pyplot.legend` or
            Additional accepted keywords when `scheme` is specified:
            fmt : string
                A formatting specification for the bin edges of the classes in the
                legend. For example, to have no decimals: ``{"fmt": "{:.0f}"}``.
            labels : list-like
                A list of legend labels to override the auto-generated labblels.
                Needs to have the same number of elements as the number of
                classes (`k`).
            interval : boolean (default False)
                An option to control brackets from mapclassify legend.
                If True, open/closed interval brackets are shown in the legend.
        classification_kwds (dict, optional): Keyword arguments to pass to mapclassify. Defaults to None.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to None.
            style is a dictionary of the following form:
                style = {
                "stroke": False,
                "color": "#ff0000",
                "weight": 1,
                "opacity": 1,
                "fill": True,
                "fillColor": "#ffffff",
                "fillOpacity": 1.0,
                "dashArray": "9"
                "clickable": True,
            }
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
            hover_style is a dictionary of the following form:
                hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
            style_callback is a function that takes the feature as argument and should return a dictionary of the following form:
            style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
        encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".
        **kwargs: Additional keyword arguments to pass to the GeoJSON class, such as fields, which can be a list of column names to be included in the popup.

    """

    gdf, legend_dict = classify(
        data=data,
        column=column,
        cmap=cmap,
        colors=colors,
        labels=labels,
        scheme=scheme,
        k=k,
        legend_kwds=legend_kwds,
        classification_kwds=classification_kwds,
    )

    if legend_title is None:
        legend_title = column

    if style is None:
        style = {
            # "stroke": False,
            # "color": "#ff0000",
            "weight": 1,
            "opacity": 1,
            # "fill": True,
            # "fillColor": "#ffffff",
            "fillOpacity": 1.0,
            # "dashArray": "9"
            # "clickable": True,
        }
        if colors is not None:
            style["color"] = "#000000"

    if hover_style is None:
        hover_style = {"weight": style["weight"] + 1, "fillOpacity": 0.5}

    if style_callback is None:
        style_callback = lambda feat: {"fillColor": feat["properties"]["color"]}

    if gdf.geometry.geom_type.unique().tolist()[0] == "Point":
        columns = gdf.columns.tolist()
        if "category" in columns:
            columns.remove("category")
        if "color" in columns:
            columns.remove("color")
        if marker_args is None:
            marker_args = {}
        if "fill_color" not in marker_args:
            marker_args["fill_color"] = gdf["color"].tolist()
        if "stroke" not in marker_args:
            marker_args["stroke"] = False
        if "fill_opacity" not in marker_args:
            marker_args["fill_opacity"] = 0.8

        marker_args["radius"] = marker_radius

        self.add_markers(gdf[columns], layer_name=layer_name, **marker_args)
    else:
        self.add_gdf(
            gdf,
            layer_name=layer_name,
            style=style,
            hover_style=hover_style,
            style_callback=style_callback,
            info_mode=info_mode,
            encoding=encoding,
            **kwargs,
        )
    if add_legend:
        self.add_legend(
            title=legend_title, legend_dict=legend_dict, position=legend_position
        )    

add_gdf(self, gdf, layer_name='Untitled', style={}, hover_style={}, style_callback=None, fill_colors=['black'], info_mode='on_hover', zoom_to_layer=False, encoding='utf-8', **kwargs)

Adds a GeoDataFrame to the map.

Parameters:

Name Type Description Default
gdf GeoDataFrame

A GeoPandas GeoDataFrame.

required
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to {}.

{}
hover_style dict

Hover style dictionary. Defaults to {}.

{}
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.

None
fill_colors list

The random colors to use for filling polygons. Defaults to ["black"].

['black']
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'
zoom_to_layer bool

Whether to zoom to the layer. Defaults to False.

False
encoding str

The encoding of the GeoDataFrame. Defaults to "utf-8".

'utf-8'
Source code in geopypi/geopypi.py
def add_gdf(
    self,
    gdf,
    layer_name="Untitled",
    style={},
    hover_style={},
    style_callback=None,
    fill_colors=["black"],
    info_mode="on_hover",
    zoom_to_layer=False,
    encoding="utf-8",
    **kwargs,
):
    """Adds a GeoDataFrame to the map.

    Args:
        gdf (GeoDataFrame): A GeoPandas GeoDataFrame.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
        fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
        zoom_to_layer (bool, optional): Whether to zoom to the layer. Defaults to False.
        encoding (str, optional): The encoding of the GeoDataFrame. Defaults to "utf-8".
    """
    for col in gdf.columns:
        try:
            if gdf[col].dtype in ["datetime64[ns]", "datetime64[ns, UTC]"]:
                gdf[col] = gdf[col].astype(str)
        except:
            pass

    data = gdf_to_geojson(gdf, epsg="4326")

    self.add_geojson(
        data,
        layer_name,
        style,
        hover_style,
        style_callback,
        fill_colors,
        info_mode,
        zoom_to_layer,
        encoding,
        **kwargs,
    )

    if zoom_to_layer:
        import numpy as np

        bounds = gdf.to_crs(epsg="4326").bounds
        west = np.min(bounds["minx"])
        south = np.min(bounds["miny"])
        east = np.max(bounds["maxx"])
        north = np.max(bounds["maxy"])
        self.fit_bounds([[south, east], [north, west]])

add_geojson(self, in_geojson, layer_name='Untitled', style={}, hover_style={}, style_callback=None, fill_colors=['black'], info_mode='on_hover', zoom_to_layer=False, encoding='utf-8', **kwargs)

Adds a GeoJSON file to the map.

Parameters:

Name Type Description Default
in_geojson str | dict

The file path or http URL to the input GeoJSON or a dictionary containing the geojson.

required
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to {}.

{}
hover_style dict

Hover style dictionary. Defaults to {}.

{}
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.

None
fill_colors list

The random colors to use for filling polygons. Defaults to ["black"].

['black']
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'
zoom_to_layer bool

Whether to zoom to the layer after adding it to the map. Defaults to False.

False
encoding str

The encoding of the GeoJSON file. Defaults to "utf-8".

'utf-8'

Exceptions:

Type Description
FileNotFoundError

The provided GeoJSON file could not be found.

Source code in geopypi/geopypi.py
def add_geojson(
    self,
    in_geojson,
    layer_name="Untitled",
    style={},
    hover_style={},
    style_callback=None,
    fill_colors=["black"],
    info_mode="on_hover",
    zoom_to_layer=False,
    encoding="utf-8",
    **kwargs,
):
    """Adds a GeoJSON file to the map.

    Args:
        in_geojson (str | dict): The file path or http URL to the input GeoJSON or a dictionary containing the geojson.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
        fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
        zoom_to_layer (bool, optional): Whether to zoom to the layer after adding it to the map. Defaults to False.
        encoding (str, optional): The encoding of the GeoJSON file. Defaults to "utf-8".

    Raises:
        FileNotFoundError: The provided GeoJSON file could not be found.
    """
    import json
    import random
    import requests

    style_callback_only = False

    if len(style) == 0 and style_callback is not None:
        style_callback_only = True

    try:
        if isinstance(in_geojson, str):
            if in_geojson.startswith("http"):
                if is_jupyterlite():
                    import pyodide

                    output = os.path.basename(in_geojson)

                    output = os.path.abspath(output)
                    obj = pyodide.http.open_url(in_geojson)
                    with open(output, "w") as fd:
                        shutil.copyfileobj(obj, fd)
                    with open(output, "r") as fd:
                        data = json.load(fd)
                else:
                    in_geojson = github_raw_url(in_geojson)
                    data = requests.get(in_geojson).json()
            else:
                in_geojson = os.path.abspath(in_geojson)
                if not os.path.exists(in_geojson):
                    raise FileNotFoundError(
                        "The provided GeoJSON file could not be found."
                    )

                with open(in_geojson, encoding=encoding) as f:
                    data = json.load(f)
        elif isinstance(in_geojson, dict):
            data = in_geojson
        else:
            raise TypeError("The input geojson must be a type of str or dict.")
    except Exception as e:
        raise Exception(e)

    geom_type = get_geometry_type(data)

    if not style:
        style = {
            # "stroke": True,
            "color": "#3388ff",
            "weight": 2,
            "opacity": 1,
            "fill": True,
            "fillColor": "#3388ff",
            "fillOpacity": 0.2,
            # "dashArray": "9"
            # "clickable": True,
        }

        if geom_type in ["LineString", "MultiLineString"]:
            style["fill"] = False

    elif "weight" not in style:
        style["weight"] = 1

    if not hover_style:
        hover_style = {"weight": style["weight"] + 2, "fillOpacity": 0}

    def random_color(feature):
        return {
            "color": "black",
            "fillColor": random.choice(fill_colors),
        }

    toolbar_button = widgets.ToggleButton(
        value=True,
        tooltip="Toolbar",
        icon="info",
        layout=widgets.Layout(
            width="28px", height="28px", padding="0px 0px 0px 4px"
        ),
    )

    close_button = widgets.ToggleButton(
        value=False,
        tooltip="Close the tool",
        icon="times",
        # button_style="primary",
        layout=widgets.Layout(
            height="28px", width="28px", padding="0px 0px 0px 4px"
        ),
    )

    html = widgets.HTML()
    html.layout.margin = "0px 10px 0px 10px"
    html.layout.max_height = "250px"
    html.layout.max_width = "250px"

    output_widget = widgets.VBox(
        [widgets.HBox([toolbar_button, close_button]), html]
    )
    info_control = ipyleaflet.WidgetControl(
        widget=output_widget, position="bottomright"
    )

    if info_mode in ["on_hover", "on_click"]:
        self.add(info_control)

    def toolbar_btn_click(change):
        if change["new"]:
            close_button.value = False
            output_widget.children = [
                widgets.VBox([widgets.HBox([toolbar_button, close_button]), html])
            ]
        else:
            output_widget.children = [widgets.HBox([toolbar_button, close_button])]

    toolbar_button.observe(toolbar_btn_click, "value")

    def close_btn_click(change):
        if change["new"]:
            toolbar_button.value = False
            if info_control in self.controls:
                self.remove_control(info_control)
            output_widget.close()

    close_button.observe(close_btn_click, "value")

    if "fields" in kwargs:
        fields = kwargs["fields"]
        kwargs.pop("fields")
    else:
        fields = None

    def update_html(feature, fields=fields, **kwargs):
        if fields is None:
            fields = list(feature["properties"].keys())
            if "style" in fields:
                fields.remove("style")

        value = [
            "<b>{}: </b>{}<br>".format(prop, feature["properties"][prop])
            for prop in fields
        ]

        value = """{}""".format("".join(value))
        html.value = value

    if style_callback is None:
        style_callback = random_color

    if style_callback_only:
        geojson = ipyleaflet.GeoJSON(
            data=data,
            hover_style=hover_style,
            style_callback=style_callback,
            name=layer_name,
        )
    else:
        geojson = ipyleaflet.GeoJSON(
            data=data,
            style=style,
            hover_style=hover_style,
            style_callback=style_callback,
            name=layer_name,
        )

    if info_mode == "on_hover":
        geojson.on_hover(update_html)
    elif info_mode == "on_click":
        geojson.on_click(update_html)

    self.add(geojson)
    self.geojson_layers.append(geojson)

    if not hasattr(self, "json_layer_dict"):
        self.json_layer_dict = {}

    params = {
        "data": geojson,
        "style": style,
        "hover_style": hover_style,
        "style_callback": style_callback,
    }
    self.json_layer_dict[layer_name] = params

    if zoom_to_layer:
        try:
            import numpy as np
            import geopandas as gpd

            gdf = gpd.GeoDataFrame.from_features(data)
            if gdf.crs is None:
                gdf.crs = "EPSG:4326"
            bounds = gdf.to_crs(epsg="4326").bounds
            west = np.min(bounds["minx"])
            south = np.min(bounds["miny"])
            east = np.max(bounds["maxx"])
            north = np.max(bounds["maxy"])
            self.fit_bounds([[south, east], [north, west]])
        except Exception as e:
            print(e)

add_image(self, url, bounds, name='image', **kwargs)

Adds an image overlay to the map.

Parameters:

Name Type Description Default
url str

The URL of the image to add.

required
bounds list

The bounds of the image as a list of tuples.

required
name str

The name of the image overlay. Defaults to "image".

'image'
Source code in geopypi/geopypi.py
def add_image(self, url, bounds, name="image", **kwargs):
    """
    Adds an image overlay to the map.

    Args:
        url (str): The URL of the image to add.
        bounds (list): The bounds of the image as a list of tuples.
        name (str, optional): The name of the image overlay. Defaults to "image".
    """
    layer = ipyleaflet.ImageOverlay(url=url, bounds=bounds, name=name, **kwargs)
    self.add(layer)

add_layers_control(self, position='topright')

Adds a layers control to the map.

Parameters:

Name Type Description Default
position str

The position of the layers control. Defaults to "topright".

'topright'
Source code in geopypi/geopypi.py
def add_layers_control(self, position="topright"):
    """Adds a layers control to the map.

    Args:
        position (str, optional): The position of the layers control. Defaults to "topright".
    """
    self.add_control(ipyleaflet.LayersControl(position=position))

add_legend(self, title='Legend', legend_dict=None, labels=None, colors=None, position='bottomright', builtin_legend=None, layer_name=None, **kwargs)

Adds a customized basemap to the map.

Parameters:

Name Type Description Default
title str

Title of the legend. Defaults to 'Legend'.

'Legend'
legend_dict dict

A dictionary containing legend items as keys and color as values. If provided, legend_keys and legend_colors will be ignored. Defaults to None.

None
labels list

A list of legend keys. Defaults to None.

None
colors list

A list of legend colors. Defaults to None.

None
position str

Position of the legend. Defaults to 'bottomright'.

'bottomright'
builtin_legend str

Name of the builtin legend to add to the map. Defaults to None.

None
layer_name str

Layer name of the legend to be associated with. Defaults to None.

None
Source code in geopypi/geopypi.py
def add_legend(
    self,
    title="Legend",
    legend_dict=None,
    labels=None,
    colors=None,
    position="bottomright",
    builtin_legend=None,
    layer_name=None,
    **kwargs,
):
    """Adds a customized basemap to the map.

    Args:
        title (str, optional): Title of the legend. Defaults to 'Legend'.
        legend_dict (dict, optional): A dictionary containing legend items as keys and color as values. If provided, legend_keys and legend_colors will be ignored. Defaults to None.
        labels (list, optional): A list of legend keys. Defaults to None.
        colors (list, optional): A list of legend colors. Defaults to None.
        position (str, optional): Position of the legend. Defaults to 'bottomright'.
        builtin_legend (str, optional): Name of the builtin legend to add to the map. Defaults to None.
        layer_name (str, optional): Layer name of the legend to be associated with. Defaults to None.

    """
    import pkg_resources
    from IPython.display import display

    pkg_dir = os.path.dirname(
        pkg_resources.resource_filename("leafmap", "leafmap.py")
    )
    legend_template = os.path.join(pkg_dir, "data/template/legend.html")

    if "min_width" not in kwargs.keys():
        min_width = None
    if "max_width" not in kwargs.keys():
        max_width = None
    else:
        max_width = kwargs["max_width"]
    if "min_height" not in kwargs.keys():
        min_height = None
    else:
        min_height = kwargs["min_height"]
    if "max_height" not in kwargs.keys():
        max_height = None
    else:
        max_height = kwargs["max_height"]
    if "height" not in kwargs.keys():
        height = None
    else:
        height = kwargs["height"]
    if "width" not in kwargs.keys():
        width = None
    else:
        width = kwargs["width"]

    if width is None:
        max_width = "300px"
    if height is None:
        max_height = "400px"

    if not os.path.exists(legend_template):
        print("The legend template does not exist.")
        return

    if labels is not None:
        if not isinstance(labels, list):
            print("The legend keys must be a list.")
            return
    else:
        labels = ["One", "Two", "Three", "Four", "etc"]

    if colors is not None:
        if not isinstance(colors, list):
            print("The legend colors must be a list.")
            return
        elif all(isinstance(item, tuple) for item in colors):
            try:
                colors = [rgb_to_hex(x) for x in colors]
            except Exception as e:
                print(e)
        elif all((item.startswith("#") and len(item) == 7) for item in colors):
            pass
        elif all((len(item) == 6) for item in colors):
            pass
        else:
            print("The legend colors must be a list of tuples.")
            return
    else:
        colors = [
            "#8DD3C7",
            "#FFFFB3",
            "#BEBADA",
            "#FB8072",
            "#80B1D3",
        ]

    if len(labels) != len(colors):
        print("The legend keys and values must be the same length.")
        return

    allowed_builtin_legends = builtin_legends.keys()
    if builtin_legend is not None:
        if builtin_legend not in allowed_builtin_legends:
            print(
                "The builtin legend must be one of the following: {}".format(
                    ", ".join(allowed_builtin_legends)
                )
            )
            return
        else:
            legend_dict = builtin_legends[builtin_legend]
            labels = list(legend_dict.keys())
            colors = list(legend_dict.values())

    if legend_dict is not None:
        if not isinstance(legend_dict, dict):
            print("The legend dict must be a dictionary.")
            return
        else:
            labels = list(legend_dict.keys())
            colors = list(legend_dict.values())
            if all(isinstance(item, tuple) for item in colors):
                try:
                    colors = [rgb_to_hex(x) for x in colors]
                except Exception as e:
                    print(e)

    allowed_positions = [
        "topleft",
        "topright",
        "bottomleft",
        "bottomright",
    ]
    if position not in allowed_positions:
        print(
            "The position must be one of the following: {}".format(
                ", ".join(allowed_positions)
            )
        )
        return

    header = []
    content = []
    footer = []

    with open(legend_template) as f:
        lines = f.readlines()
        lines[3] = lines[3].replace("Legend", title)
        header = lines[:6]
        footer = lines[11:]

    for index, key in enumerate(labels):
        color = colors[index]
        if not color.startswith("#"):
            color = "#" + color
        item = "      <li><span style='background:{};'></span>{}</li>\n".format(
            color, key
        )
        content.append(item)

    legend_html = header + content + footer
    legend_text = "".join(legend_html)

    try:
        legend_output_widget = widgets.Output(
            layout={
                # "border": "1px solid black",
                "max_width": max_width,
                "min_width": min_width,
                "max_height": max_height,
                "min_height": min_height,
                "height": height,
                "width": width,
                "overflow": "scroll",
            }
        )
        legend_control = ipyleaflet.WidgetControl(
            widget=legend_output_widget, position=position
        )
        legend_widget = widgets.HTML(value=legend_text)
        with legend_output_widget:
            display(legend_widget)

        self.legend_widget = legend_output_widget
        self.legend_control = legend_control
        self.add(legend_control)

    except Exception as e:
        raise Exception(e)

add_opacity_slider(self, layer_index=-1, description='Opacity:', position='topright')

Adds an opacity slider for the specified layer.

Parameters:

Name Type Description Default
layer object

The layer for which to add the opacity slider.

required
description str

The description of the opacity slider. Defaults to "Opacity:".

'Opacity:'
position str

The position of the opacity slider. Defaults to "topright".

'topright'

Returns:

Type Description

None

Source code in geopypi/geopypi.py
def add_opacity_slider(
        self, layer_index=-1, description="Opacity:", position="topright"):
    """Adds an opacity slider for the specified layer.

    Args:
        layer (object): The layer for which to add the opacity slider.
        description (str, optional): The description of the opacity slider. Defaults to "Opacity:".
        position (str, optional): The position of the opacity slider. Defaults to "topright".

    Returns:
        None
    """
    layer = self.layers[layer_index]
    opacity_slider = widgets.FloatSlider(
        description=description, min=0, max=1, value=layer.opacity, style={"description_width": "initial"}
    )

    def update_opacity(change):
        """
        Updates the opacity of a layer based on the new value from a slider.

        This function is designed to be used as a callback for an ipywidgets slider. 
        It takes a dictionary with a "new" key representing the new value of the slider, 
        and sets the opacity of a global layer variable to this new value.

        Args:
        change (dict): A dictionary with a "new" key representing the new value of the slider.

        Returns:
            None
        """
        layer.opacity = change["new"]

    opacity_slider.observe(update_opacity, "value")

    control = ipyleaflet.WidgetControl(widget=opacity_slider, position=position)
    self.add(control)

    from ipywidgets import Dropdown, Button, HBox

add_raster(self, data, name='raster', zoom_to_layer=True, **kwargs)

Adds a raster layer to the map.

Parameters:

Name Type Description Default
data str

The path to the raster file.

required
name str

The name of the layer. Defaults to "raster".

'raster'
Source code in geopypi/geopypi.py
def add_raster(self, data, name="raster", zoom_to_layer=True, **kwargs):
    """Adds a raster layer to the map.

    Args:
        data (str): The path to the raster file.
        name (str, optional): The name of the layer. Defaults to "raster".
    """

    try:
        from localtileserver import TileClient, get_leaflet_tile_layer
    except ImportError:
        raise ImportError("Please install the localtileserver package.")

    client = TileClient(data)
    layer = get_leaflet_tile_layer(client, name=name, **kwargs)
    self.add(layer)

    if zoom_to_layer:
        self.center = client.center()
        self.zoom = client.default_zoom


    client = TileClient(data)
    layer = get_leaflet_tile_layer(client, name=name, **kwargs)
    self.add(layer)

    if zoom_to_layer:
        self.center = client.center()
        self.zoom = client.default_zoom

add_shp(self, in_shp, layer_name='Untitled', style={}, hover_style={}, style_callback=None, fill_colors=['black'], info_mode='on_hover', zoom_to_layer=False, encoding='utf-8')

Adds a shapefile to the map.

Parameters:

Name Type Description Default
in_shp str

The input file path or HTTP URL (*.zip) to the shapefile.

required
layer_name str

The layer name to be used.. Defaults to "Untitled".

'Untitled'
style dict

A dictionary specifying the style to be used. Defaults to {}.

{}
hover_style dict

Hover style dictionary. Defaults to {}.

{}
style_callback function

Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.

None
fill_colors list

The random colors to use for filling polygons. Defaults to ["black"].

['black']
info_mode str

Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".

'on_hover'
zoom_to_layer bool

Whether to zoom to the layer after adding it to the map. Defaults to False.

False
encoding str

The encoding of the shapefile. Defaults to "utf-8".

'utf-8'

Exceptions:

Type Description
FileNotFoundError

The provided shapefile could not be found.

Source code in geopypi/geopypi.py
def add_shp(
    self,
    in_shp,
    layer_name="Untitled",
    style={},
    hover_style={},
    style_callback=None,
    fill_colors=["black"],
    info_mode="on_hover",
    zoom_to_layer=False,
    encoding="utf-8",
):
    """Adds a shapefile to the map.

    Args:
        in_shp (str): The input file path or HTTP URL (*.zip) to the shapefile.
        layer_name (str, optional): The layer name to be used.. Defaults to "Untitled".
        style (dict, optional): A dictionary specifying the style to be used. Defaults to {}.
        hover_style (dict, optional): Hover style dictionary. Defaults to {}.
        style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None.
        fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"].
        info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover".
        zoom_to_layer (bool, optional): Whether to zoom to the layer after adding it to the map. Defaults to False.
        encoding (str, optional): The encoding of the shapefile. Defaults to "utf-8".

    Raises:
        FileNotFoundError: The provided shapefile could not be found.
    """

    import glob

    if in_shp.startswith("http") and in_shp.endswith(".zip"):
        out_dir = os.path.dirname(temp_file_path(".shp"))
        if not os.path.exists(out_dir):
            os.makedirs(out_dir)
        basename = os.path.basename(in_shp)
        filename = os.path.join(out_dir, basename)
        download_file(in_shp, filename)
        files = list(glob.glob(os.path.join(out_dir, "*.shp")))
        if len(files) > 0:
            in_shp = files[0]
        else:
            raise FileNotFoundError(
                "The downloaded zip file does not contain any shapefile in the root directory."
            )
    else:
        in_shp = os.path.abspath(in_shp)
        if not os.path.exists(in_shp):
            raise FileNotFoundError("The provided shapefile could not be found.")

    geojson = shp_to_geojson(in_shp, encoding=encoding)
    self.add_geojson(
        geojson,
        layer_name,
        style,
        hover_style,
        style_callback,
        fill_colors,
        info_mode,
        zoom_to_layer,
        encoding,
    )



    import geopandas as gpd
    from ipyleaflet import GeoData
    from shapely.geometry import Point, LineString

add_vector(self, data)

Add vector data to the map.

Parameters:

Name Type Description Default
data str or geopandas.GeoDataFrame

The vector data to add. This can be a file path or a GeoDataFrame.

required
Source code in geopypi/geopypi.py
def add_vector(self, data):
    """
    Add vector data to the map.

    Args:
        data (str or geopandas.GeoDataFrame): The vector data to add. This can be a file path or a GeoDataFrame.
    """
    import geopandas as gpd
    from ipyleaflet import GeoData

    if isinstance(data, gpd.GeoDataFrame):
        vector_layer = GeoData(geo_dataframe=data)

    elif isinstance(data, str):
        vector_layer = GeoData(geo_dataframe=gpd.read_file(data))

    else:
        raise ValueError("Unsupported data format. Please provide a GeoDataFrame or a file path.")

    self.add_layer(vector_layer)

add_widget(self, widget, position='topright')

Adds a widget to the map.

Parameters:

Name Type Description Default
widget object

The widget to add.

required
position str

The position of the widget. Defaults to "topright".

'topright'

Returns:

Type Description

None

Source code in geopypi/geopypi.py
def add_widget(self, widget, position="topright"):
    """Adds a widget to the map.

    Args:
        widget (object): The widget to add.
        position (str, optional): The position of the widget. Defaults to "topright".

    Returns:
        None
    """
    control = ipyleaflet.WidgetControl(widget=widget, position=position)
    self.add(control)

add_zoom_slider(self, description='Zoom level:', min=0, max=24, value=10, position='topright')

Adds a zoom slider to the map.

Parameters:

Name Type Description Default
position str

The position of the zoom slider. Defaults to "topright".

'topright'
Source code in geopypi/geopypi.py
def add_zoom_slider(
        self, description="Zoom level:", min=0, max=24, value=10, position="topright"
):
    """Adds a zoom slider to the map.

    Args:
        position (str, optional): The position of the zoom slider. Defaults to "topright".
    """
    zoom_slider = widgets.IntSlider(
        description=description, min=min, max=max, value=value
    )

    control = ipyleaflet.WidgetControl(widget=zoom_slider, position=position)
    self.add(control)
    widgets.jslink((zoom_slider, "value"), (self, "zoom"))