geog510_lab_utils main module¶
Extension of ipyleaflet class Map for custom use in geog510_lab_utils
Map (Map)
¶
Source code in geog510_lab_utils/geog510_lab_utils.py
class Map(ipyleaflet.Map):
def __init__(self, center=[20, 0], zoom=2, height="600px", **kwargs):
"""Initialize class Map based on ipyleaflet class Map
Args:
center (list, optional): Initial center of map. Defaults to [20, 0].
zoom (int, optional): Initial zoom level of map. Defaults to 2.
height (str, optional): Initial height of map (in pixels). Defaults to "600px".
"""
super().__init__(center=center, zoom=zoom, **kwargs)
self.layout.height = height
self.add_basemap("OpenStreetMap")
def add_basemap(self, basemap="OpenTopoMap"):
"""Adds basemap to Map based on user input.
Args:
basemap (str, optional): Basemap. Defaults to "OpenTopoMap".
"""
import ipyleaflet
# 1. Start at the top-level basemaps module
bm_object = ipyleaflet.basemaps
# 2. Safely traverse nested names (e.g., "Esri.WorldImagery" -> ["Esri", "WorldImagery"])
for part in basemap.split("."):
if hasattr(bm_object, part):
bm_object = getattr(bm_object, part)
else:
raise ValueError(
f"Basemap component '{part}' from '{basemap}' not found."
)
# 3. Determine how to pull the URL depending on what kind of object it is
if hasattr(bm_object, "build_url"):
# It's a direct basemap layer (e.g., Esri.WorldImagery or OpenTopoMap)
url = bm_object.build_url()
elif hasattr(bm_object, "Mapnik") and hasattr(bm_object.Mapnik, "build_url"):
# It's a bundle like OpenStreetMap, fallback to its default style
url = bm_object.Mapnik.build_url()
else:
# Fallback: grab the first available sub-style in the bundle
try:
first_style = list(bm_object.values())[0]
url = first_style.build_url()
except (AttributeError, IndexError):
raise AttributeError(
f"Could not extract a valid URL from basemap '{basemap}'."
)
# 4. Create and add the layer to the map
layer = ipyleaflet.TileLayer(url=url, name=basemap)
self.add_layer(layer)
def add_geojson(self, data, hover_style=None, **kwargs):
"""_summary_Adds GeoJson to map.
Args:
data (_type_): Data dictionary representing a GeoJson file
hover_style (_type_, optional): Initial hover style of Map. Defaults to None.
"""
import geopandas as gpd
if hover_style is None:
hover_style = {"color": "green", "fillOpacity": 0.2}
if isinstance(data, str):
gdf = gpd.read_file(data)
geojson = gdf.__geo_interface__
elif isinstance(data, dict):
geojson = data
layer = ipyleaflet.GeoJSON(data=geojson, hover_style=hover_style, **kwargs)
self.add_layer(layer)
def add_gdf(self, gdf, **kwargs):
"""Adds GeoDataFrame to Map.
Args:
gdf (_type_): Represents GeoDataFrame.
"""
gdf = gdf.to_crs(epsg=4326)
geojson = gdf.__geo_interface__
self.add_geojson(geojson, **kwargs)
def add_vector(self, data, **kwargs):
"""Adds any vector data type supported by GeoPandas to the Map.
Args:
data (_type_): Any supported data type represented by a file path or a URL.
Raises:
ValueError: Provides error if data type is not supported.
Returns:
_type_: Breaks from if/elif if GeoJson is provided upon initialization.
"""
import geopandas as gpd
if isinstance(data, str):
gdf = gpd.read_file(data)
elif isinstance(data, gpd.GeoDataFrame):
gdf = data
elif isinstance(data, dict):
return self.add_geojson(data, **kwargs)
else:
raise ValueError("Invalid data type.")
self.add_gdf(gdf, **kwargs)
def add_layer_control(self):
"""Add layer control to Map"""
control = ipyleaflet.LayersControl(position="topright")
self.add_control(control)
def add_raster(
self, data_path, name="Raster Layer", colormap=None, opacity=1, **kwargs
):
"""Adds a Cloud Optimized GeoTIFF (COG) or local raster to the map.
Args:
data_path (str): The local path or remote URL to the raster file.
name (str, optional): The name to display in the Layer Control.
Defaults to 'Raster Layer'.
colormap (str or dict, optional): The rio-tiler registered colormap
name (e.g., 'viridis', 'terrain') or a dictionary mapping
values to colors. Defaults to None.
opacity (int, optional):The transparency of the layer from 0 to 1.
Defaults to 1
"""
from localtileserver import TileClient, get_leaflet_tile_layer
client = TileClient(data_path)
tile_layer = get_leaflet_tile_layer(
client, name=name, colormap=colormap, opacity=opacity, **kwargs
)
self.add(tile_layer)
self.center = client.center()
self.zoom = client.default_zoom
def add_image(self, image, bounds, **kwargs):
"""Add static image to Map
Args:
image (str): URL or filename of image
bounds (tuple): A tuple of two coordinate tuples:
((south_lat, west_lon), (north_lat, east_lon)).
opacity (float): Opacity value
"""
from ipyleaflet import ImageOverlay
overlay = ImageOverlay(url=image, bounds=bounds, **kwargs)
self.add(overlay)
def add_video(self, video, bounds, **kwargs):
"""Add video to Map
Args:
video (str): URL or filename of video
bounds (list): A list of two coordinate lists
[[south_lat, west_lon], [north_lat, east_lon]].
"""
from ipyleaflet import VideoOverlay
overlay = VideoOverlay(url=video, bounds=bounds, **kwargs)
self.add(overlay)
def add_wms_layer(
self, url, layers, format="image/png", transparent=True, **kwargs
):
"""Adds a WMS layer to the map.
Args:
url (str): The WMS service URL.
layers (str): The layers to display.
**kwargs: Additional keyword arguments for the ipyleaflet.WMSLayer layer.
"""
layer = ipyleaflet.WMSLayer(
url=url, layers=layers, format=format, transparent=transparent, **kwargs
)
self.add(layer)
def add_basemap_gui(self, position="topright"):
"""Adds a basemap chooser to the map.
Args:
position (str, optional): Defaults to "topright".
"""
import ipywidgets as widgets
from ipyleaflet import WidgetControl
from IPython.display import display
# Initialize widget components
# button
btn = widgets.Button(icon="map", button_style="success")
btn.layout.width = "35px"
# dropdown
dropdown = widgets.Dropdown(
options=[
"OpenStreetMap",
"OpenTopoMap",
"Esri.WorldImagery",
"Esri.NatGeoWorldMap",
"CartoDB.Positron",
"CartoDB.DarkMatter",
],
value="OpenStreetMap",
description="Basemap:",
style={"description_width": "initial"},
)
dropdown.layout.width = "350px"
# Create a container (HBox) for the widget components
hbox = widgets.HBox([btn])
def on_button_clicked(b):
# Check the current state
if b.button_style == "success":
b.button_style = "danger"
b.icon = "times"
hbox.children = [dropdown, btn]
else:
b.button_style = "success"
b.icon = "map"
hbox.children = [btn]
btn.on_click(on_button_clicked)
def on_dropdown_change(change):
if change["new"]:
basemap_names = dropdown.options
layers_to_remove = [
layer
for layer in self.layers
if getattr(layer, "name", None) in basemap_names
]
for layer in layers_to_remove:
self.remove_layer(layer)
# 4. Add the new one
self.add_basemap(change["new"])
dropdown.observe(on_dropdown_change, names="value")
# Create a control, position it and add to map
control = ipyleaflet.WidgetControl(widget=hbox, position=position)
self.add(control)
__init__(self, center=[20, 0], zoom=2, height='600px', **kwargs)
special
¶
Initialize class Map based on ipyleaflet class Map
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center |
list |
Initial center of map. Defaults to [20, 0]. |
[20, 0] |
zoom |
int |
Initial zoom level of map. Defaults to 2. |
2 |
height |
str |
Initial height of map (in pixels). Defaults to "600px". |
'600px' |
Source code in geog510_lab_utils/geog510_lab_utils.py
def __init__(self, center=[20, 0], zoom=2, height="600px", **kwargs):
"""Initialize class Map based on ipyleaflet class Map
Args:
center (list, optional): Initial center of map. Defaults to [20, 0].
zoom (int, optional): Initial zoom level of map. Defaults to 2.
height (str, optional): Initial height of map (in pixels). Defaults to "600px".
"""
super().__init__(center=center, zoom=zoom, **kwargs)
self.layout.height = height
self.add_basemap("OpenStreetMap")
add_basemap(self, basemap='OpenTopoMap')
¶
Adds basemap to Map based on user input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
basemap |
str |
Basemap. Defaults to "OpenTopoMap". |
'OpenTopoMap' |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_basemap(self, basemap="OpenTopoMap"):
"""Adds basemap to Map based on user input.
Args:
basemap (str, optional): Basemap. Defaults to "OpenTopoMap".
"""
import ipyleaflet
# 1. Start at the top-level basemaps module
bm_object = ipyleaflet.basemaps
# 2. Safely traverse nested names (e.g., "Esri.WorldImagery" -> ["Esri", "WorldImagery"])
for part in basemap.split("."):
if hasattr(bm_object, part):
bm_object = getattr(bm_object, part)
else:
raise ValueError(
f"Basemap component '{part}' from '{basemap}' not found."
)
# 3. Determine how to pull the URL depending on what kind of object it is
if hasattr(bm_object, "build_url"):
# It's a direct basemap layer (e.g., Esri.WorldImagery or OpenTopoMap)
url = bm_object.build_url()
elif hasattr(bm_object, "Mapnik") and hasattr(bm_object.Mapnik, "build_url"):
# It's a bundle like OpenStreetMap, fallback to its default style
url = bm_object.Mapnik.build_url()
else:
# Fallback: grab the first available sub-style in the bundle
try:
first_style = list(bm_object.values())[0]
url = first_style.build_url()
except (AttributeError, IndexError):
raise AttributeError(
f"Could not extract a valid URL from basemap '{basemap}'."
)
# 4. Create and add the layer to the map
layer = ipyleaflet.TileLayer(url=url, name=basemap)
self.add_layer(layer)
add_basemap_gui(self, position='topright')
¶
Adds a basemap chooser to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
position |
str |
Defaults to "topright". |
'topright' |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_basemap_gui(self, position="topright"):
"""Adds a basemap chooser to the map.
Args:
position (str, optional): Defaults to "topright".
"""
import ipywidgets as widgets
from ipyleaflet import WidgetControl
from IPython.display import display
# Initialize widget components
# button
btn = widgets.Button(icon="map", button_style="success")
btn.layout.width = "35px"
# dropdown
dropdown = widgets.Dropdown(
options=[
"OpenStreetMap",
"OpenTopoMap",
"Esri.WorldImagery",
"Esri.NatGeoWorldMap",
"CartoDB.Positron",
"CartoDB.DarkMatter",
],
value="OpenStreetMap",
description="Basemap:",
style={"description_width": "initial"},
)
dropdown.layout.width = "350px"
# Create a container (HBox) for the widget components
hbox = widgets.HBox([btn])
def on_button_clicked(b):
# Check the current state
if b.button_style == "success":
b.button_style = "danger"
b.icon = "times"
hbox.children = [dropdown, btn]
else:
b.button_style = "success"
b.icon = "map"
hbox.children = [btn]
btn.on_click(on_button_clicked)
def on_dropdown_change(change):
if change["new"]:
basemap_names = dropdown.options
layers_to_remove = [
layer
for layer in self.layers
if getattr(layer, "name", None) in basemap_names
]
for layer in layers_to_remove:
self.remove_layer(layer)
# 4. Add the new one
self.add_basemap(change["new"])
dropdown.observe(on_dropdown_change, names="value")
# Create a control, position it and add to map
control = ipyleaflet.WidgetControl(widget=hbox, position=position)
self.add(control)
add_gdf(self, gdf, **kwargs)
¶
Adds GeoDataFrame to Map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gdf |
_type_ |
Represents GeoDataFrame. |
required |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_gdf(self, gdf, **kwargs):
"""Adds GeoDataFrame to Map.
Args:
gdf (_type_): Represents GeoDataFrame.
"""
gdf = gdf.to_crs(epsg=4326)
geojson = gdf.__geo_interface__
self.add_geojson(geojson, **kwargs)
add_geojson(self, data, hover_style=None, **kwargs)
¶
_summary_Adds GeoJson to map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
_type_ |
Data dictionary representing a GeoJson file |
required |
hover_style |
_type_ |
Initial hover style of Map. Defaults to None. |
None |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_geojson(self, data, hover_style=None, **kwargs):
"""_summary_Adds GeoJson to map.
Args:
data (_type_): Data dictionary representing a GeoJson file
hover_style (_type_, optional): Initial hover style of Map. Defaults to None.
"""
import geopandas as gpd
if hover_style is None:
hover_style = {"color": "green", "fillOpacity": 0.2}
if isinstance(data, str):
gdf = gpd.read_file(data)
geojson = gdf.__geo_interface__
elif isinstance(data, dict):
geojson = data
layer = ipyleaflet.GeoJSON(data=geojson, hover_style=hover_style, **kwargs)
self.add_layer(layer)
add_image(self, image, bounds, **kwargs)
¶
Add static image to Map
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image |
str |
URL or filename of image |
required |
bounds |
tuple |
A tuple of two coordinate tuples: ((south_lat, west_lon), (north_lat, east_lon)). |
required |
opacity |
float |
Opacity value |
required |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_image(self, image, bounds, **kwargs):
"""Add static image to Map
Args:
image (str): URL or filename of image
bounds (tuple): A tuple of two coordinate tuples:
((south_lat, west_lon), (north_lat, east_lon)).
opacity (float): Opacity value
"""
from ipyleaflet import ImageOverlay
overlay = ImageOverlay(url=image, bounds=bounds, **kwargs)
self.add(overlay)
add_layer_control(self)
¶
Add layer control to Map
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_layer_control(self):
"""Add layer control to Map"""
control = ipyleaflet.LayersControl(position="topright")
self.add_control(control)
add_raster(self, data_path, name='Raster Layer', colormap=None, opacity=1, **kwargs)
¶
Adds a Cloud Optimized GeoTIFF (COG) or local raster to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_path |
str |
The local path or remote URL to the raster file. |
required |
name |
str |
The name to display in the Layer Control. Defaults to 'Raster Layer'. |
'Raster Layer' |
colormap |
str or dict |
The rio-tiler registered colormap name (e.g., 'viridis', 'terrain') or a dictionary mapping values to colors. Defaults to None. |
None |
opacity |
int |
The transparency of the layer from 0 to 1. Defaults to 1 |
1 |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_raster(
self, data_path, name="Raster Layer", colormap=None, opacity=1, **kwargs
):
"""Adds a Cloud Optimized GeoTIFF (COG) or local raster to the map.
Args:
data_path (str): The local path or remote URL to the raster file.
name (str, optional): The name to display in the Layer Control.
Defaults to 'Raster Layer'.
colormap (str or dict, optional): The rio-tiler registered colormap
name (e.g., 'viridis', 'terrain') or a dictionary mapping
values to colors. Defaults to None.
opacity (int, optional):The transparency of the layer from 0 to 1.
Defaults to 1
"""
from localtileserver import TileClient, get_leaflet_tile_layer
client = TileClient(data_path)
tile_layer = get_leaflet_tile_layer(
client, name=name, colormap=colormap, opacity=opacity, **kwargs
)
self.add(tile_layer)
self.center = client.center()
self.zoom = client.default_zoom
add_vector(self, data, **kwargs)
¶
Adds any vector data type supported by GeoPandas to the Map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data |
_type_ |
Any supported data type represented by a file path or a URL. |
required |
Exceptions:
| Type | Description |
|---|---|
ValueError |
Provides error if data type is not supported. |
Returns:
| Type | Description |
|---|---|
_type_ |
Breaks from if/elif if GeoJson is provided upon initialization. |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_vector(self, data, **kwargs):
"""Adds any vector data type supported by GeoPandas to the Map.
Args:
data (_type_): Any supported data type represented by a file path or a URL.
Raises:
ValueError: Provides error if data type is not supported.
Returns:
_type_: Breaks from if/elif if GeoJson is provided upon initialization.
"""
import geopandas as gpd
if isinstance(data, str):
gdf = gpd.read_file(data)
elif isinstance(data, gpd.GeoDataFrame):
gdf = data
elif isinstance(data, dict):
return self.add_geojson(data, **kwargs)
else:
raise ValueError("Invalid data type.")
self.add_gdf(gdf, **kwargs)
add_video(self, video, bounds, **kwargs)
¶
Add video to Map
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
video |
str |
URL or filename of video |
required |
bounds |
list |
A list of two coordinate lists [[south_lat, west_lon], [north_lat, east_lon]]. |
required |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_video(self, video, bounds, **kwargs):
"""Add video to Map
Args:
video (str): URL or filename of video
bounds (list): A list of two coordinate lists
[[south_lat, west_lon], [north_lat, east_lon]].
"""
from ipyleaflet import VideoOverlay
overlay = VideoOverlay(url=video, bounds=bounds, **kwargs)
self.add(overlay)
add_wms_layer(self, url, layers, format='image/png', transparent=True, **kwargs)
¶
Adds a WMS layer to the map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url |
str |
The WMS service URL. |
required |
layers |
str |
The layers to display. |
required |
**kwargs |
Additional keyword arguments for the ipyleaflet.WMSLayer layer. |
{} |
Source code in geog510_lab_utils/geog510_lab_utils.py
def add_wms_layer(
self, url, layers, format="image/png", transparent=True, **kwargs
):
"""Adds a WMS layer to the map.
Args:
url (str): The WMS service URL.
layers (str): The layers to display.
**kwargs: Additional keyword arguments for the ipyleaflet.WMSLayer layer.
"""
layer = ipyleaflet.WMSLayer(
url=url, layers=layers, format=format, transparent=transparent, **kwargs
)
self.add(layer)