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: |
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: |
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"))