14. Leafmap#
14.1. Overview#
This lecture offers a comprehensive introduction to Leafmap, a powerful, open-source Python package for creating, managing, and analyzing interactive geospatial maps. Leafmap simplifies working with geospatial data by providing a high-level, Pythonic interface that integrates seamlessly with Jupyter environments, such as Google Colab, Jupyter Notebook, and JupyterLab.
Built on top of several well-established libraries like folium, ipyleaflet, maplibre, bokeh, pydeck, kepler.gl, and plotly, Leafmap extends their functionalities with a unified API. This extension streamlines the process of visualizing and analyzing geospatial data interactively, minimizing the amount of coding required.
In this lecture, we will explore Leafmap’s key features, including how to create interactive maps, add and customize basemaps, visualize both vector and raster data, and edit vector layers. Each section provides practical examples and guidance essential for using Leafmap effectively in GIS programming tasks.
14.2. Learning Objectives#
By the end of this lecture, you will be able to:
Create and customize interactive maps using Leafmap.
Add and configure different basemap layers.
Visualize and style vector and raster geospatial data.
Edit vector data directly on interactive maps.
Search for open geospatial datasets and incorporate them into your maps.
14.3. Install leafmap#
To install Leafmap, uncomment the following line and run the cell.
# %pip install -U leafmap
14.4. Import libraries#
To get started, import the necessary libraries.
import leafmap
Leafmap supports multiple plotting backends, including folium, ipyleaflet, maplibre, bokeh, pydeck, keplergl, and plotly. The default plotting backend is ipyleaflet, offering the most extensive features.
To switch the plotting backend, uncomment one of the following lines and run the cell:
# import leafmap.foliumap as leafmap
# import leafmap.bokehmap as leafmap
# import leafmap.maplibregl as leafmap
# import leafmap.deck as leafmap
# import leafmap.kepler as leafmap
# import leafmap.plotlymap as leafmap
14.5. Creating interactive maps#
Leafmap provides a high-level, Pythonic interface for creating interactive maps. You can create a basic interactive map by calling the Map function. The default basemap is OpenStreetMap.
m = leafmap.Map()
m
14.5.1. Customizing the Map#
You can customize the map’s center, zoom level, and height. The center takes a tuple of latitude and longitude, zoom is an integer, and height specifies the map height in pixels. The example below centers the map on the U.S. with a zoom level of 4 and a height of 600 pixels:
m = leafmap.Map(center=(40, -100), zoom=4, height="600px")
m
14.5.2. Adding or Removing Controls#
By default, the map includes controls such as zoom, fullscreen, scale, attribution, and toolbar. You can toggle these controls using parameters like zoom_control, fullscreen_control, scale_control, attribution_control, and toolbar_control parameters to True or False. The example below disables all controls:
m = leafmap.Map(
zoom_control=False,
draw_control=False,
scale_control=False,
fullscreen_control=False,
attribution_control=False,
toolbar_control=False,
)
m
To add a search control to the map, use the m.add_search_control() method. The search control allows users to search for places and zoom to them. The example below adds a search control to the map:
url = "https://nominatim.openstreetmap.org/search?format=json&q={s}"
m.add_search_control(url, zoom=10, position="topleft")
14.5.3. Working with Layers#
You can access the layers on the map using the layers attribute:
m.layers
(TileLayer(attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', base=True, max_zoom=19, min_zoom=1, name='OpenStreetMap', options=['attribution', 'bounds', 'detect_retina', 'max_native_zoom', 'max_zoom', 'min_native_zoom', 'min_zoom', 'no_wrap', 'tile_size', 'tms', 'zoom_offset'], url='https://tile.openstreetmap.org/{z}/{x}/{y}.png'),)
To add or remove layers, use the add and remove methods. For example, to remove the last layer:
m.remove(m.layers[-1])
14.5.4. Clearing Controls and Layers#
You can clear all controls and layers from the map with clear_controls and clear_layers methods. First, create a map:
m = leafmap.Map()
m
Then, remove all controls:
m.clear_controls()
And clear all layers:
m.clear_layers()
14.6. Changing Basemaps#
You can easily change the basemap of a map. For example, the following code adds the OpenTopoMap basemap:
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m
To change basemap interactively, add the basemap control to the map with the add_basemap_gui() method:
m = leafmap.Map()
m.add_basemap_gui()
m
14.6.1. Adding an XYZ Tile Layer#
To add custom XYZ tile layers, you can use the add_tile_layer() method. The example below adds a Google Satellite layer using an XYZ URL template:
m = leafmap.Map()
m.add_tile_layer(
url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}",
name="Google Satellite",
attribution="Google",
)
m
14.6.2. Adding a WMS Tile Layer#
Web Map Service (WMS) layers can be added using the add_wms_layer() method. The following code adds a WMS layer of NAIP Imagery from the USGS, centered on the U.S. with a zoom level of 4:
m = leafmap.Map(center=[40, -100], zoom=4)
url = "https://imagery.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?"
m.add_wms_layer(
url=url,
layers="USGSNAIPImagery:FalseColorComposite",
name="NAIP",
attribution="USGS",
format="image/png",
shown=True,
)
m
14.6.3. Adding a Legend to a Map#
To provide better context for the data layers, you can add a legend to your map. In this example, we add a WMS layer displaying the 2021 NLCD land cover data and a corresponding legend to explain the land cover types:
m = leafmap.Map(center=[40, -100], zoom=4)
m.add_basemap("Esri.WorldImagery")
url = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2021_Land_Cover_L48/wms?"
m.add_wms_layer(
url=url,
layers="NLCD_2021_Land_Cover_L48",
name="NLCD 2021",
attribution="MRLC",
format="image/png",
shown=True,
)
m.add_legend(title="NLCD Land Cover Type", builtin_legend="NLCD")
m
14.6.4. Adding a Colorbar to Visualize Data#
If you need to visualize continuous data like elevation, you can add a colorbar to the map. The example below shows how to add a colorbar with a terrain colormap for elevation, ranging from 0 to 4000 meters:
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m.add_colormap(
"terrain",
label="Elevation",
orientation="horizontal",
vmin=0,
vmax=4000,
)
m
Each of these sections demonstrates different features of the Leafmap library, allowing you to customize maps by adding basemaps, XYZ tiles, WMS layers, legends, and colorbars to enhance map visualization.
14.7. Visualizing Vector Data#
Leafmap makes it easy to visualize various vector data formats such as GeoJSON, Shapefile, GeoPackage, and others supported by GeoPandas. The following examples demonstrate different ways to add vector data to your map.
14.7.1. Adding a Marker#
You can add individual markers to the map at specific locations. In this example, a draggable marker is placed at the given latitude and longitude.
m = leafmap.Map()
location = [40, -100]
m.add_marker(location, draggable=True)
m
14.7.2. Adding Multiple Markers#
To add multiple markers at once, use the add_markers() method. This example places markers at three different locations:
m = leafmap.Map()
m.add_markers(markers=[[40, -100], [45, -110], [50, -120]])
m
14.7.3. Adding Marker Clusters#
For a large number of points, you can group them into clusters. This method reduces map clutter by aggregating nearby points. The example below uses a CSV file of world cities to create marker clusters:
m = leafmap.Map()
url = "https://github.com/opengeos/datasets/releases/download/world/world_cities.csv"
m.add_marker_cluster(url, x="longitude", y="latitude", layer_name="World cities")
m
14.7.4. Customizing Markers#
Markers can be customized with colors, icons, and labels. The following example customizes points from a CSV file of U.S. cities, using different icons and adding a legend:
m = leafmap.Map(center=[40, -100], zoom=4)
cities = "https://github.com/opengeos/datasets/releases/download/us/cities.csv"
regions = "https://github.com/opengeos/datasets/releases/download/us/us_regions.geojson"
m.add_geojson(regions, layer_name="US Regions")
m.add_points_from_xy(
cities,
x="longitude",
y="latitude",
color_column="region",
icon_names=["gear", "map", "leaf", "globe"],
spin=True,
add_legend=True,
)
m
14.7.5. Visualizing Polylines#
Polyline visualization is useful for displaying linear features such as roads or pipelines. In this example, a GeoJSON file containing submarine cable lines is added to the map:
m = leafmap.Map(center=[20, 0], zoom=2)
data = "https://github.com/opengeos/datasets/releases/download/vector/cables.geojson"
m.add_vector(data, layer_name="Cable lines", info_mode="on_hover")
m
14.7.5.1. Customizing Polyline Styles#
You can further customize polylines with a style callback function. This example dynamically changes the color and weight of each polyline based on its properties:
m = leafmap.Map(center=[20, 0], zoom=2)
m.add_basemap("CartoDB.DarkMatter")
data = "https://github.com/opengeos/datasets/releases/download/vector/cables.geojson"
callback = lambda feat: {"color": feat["properties"]["color"], "weight": 1}
m.add_vector(data, layer_name="Cable lines", style_callback=callback)
m
14.7.6. Visualizing Polygons#
To visualize polygon features, you can use the add_vector() method. In this example, a GeoJSON file of New York City buildings is added and the map automatically zooms to the layer’s extent:
m = leafmap.Map()
url = "https://github.com/opengeos/datasets/releases/download/places/nyc_buildings.geojson"
m.add_vector(url, layer_name="NYC Buildings", zoom_to_layer=True)
m
14.7.7. Visualizing GeoPandas GeoDataFrames#
You can directly visualize GeoPandas GeoDataFrames on the map. In this example, a GeoDataFrame containing building footprints from Las Vegas is displayed with custom styling:
import geopandas as gpd
url = "https://github.com/opengeos/datasets/releases/download/places/las_vegas_buildings.geojson"
gdf = gpd.read_file(url)
gdf.head()
| id | height | geometry | |
|---|---|---|---|
| 0 | 08b2986b81b34fff0200120c6aabf673 | 2.539160 | POLYGON ((-115.20758 36.11913, -115.20754 36.1... |
| 1 | 08b2986b81b34fff0200ce30fa61746d | 3.209284 | POLYGON ((-115.20756 36.11929, -115.20776 36.1... |
| 2 | 08b2986b81b34fff0200b898b488ab8b | 2.691431 | POLYGON ((-115.20759 36.11932, -115.20759 36.1... |
| 3 | 08b2986b81b34fff02004b957b4cd490 | 2.795901 | POLYGON ((-115.20762 36.11953, -115.20775 36.1... |
| 4 | 08b2986b81869fff020091d66f33d09b | 2.649764 | POLYGON ((-115.20727 36.11898, -115.20727 36.1... |
You can use the GeoDataFrame.explore() method to visualize the data interactively, which utilizes the folium library. The example below displays the building footprints interactively:
gdf.explore()
To display the GeoDataFrame using Leaflet, use the add_gdf() method. The following code adds the building footprints to the map:
m = leafmap.Map()
m.add_basemap("HYBRID")
style = {"color": "red", "fillColor": "red", "fillOpacity": 0.1, "weight": 2}
m.add_gdf(gdf, style=style, layer_name="Las Vegas Buildings", zoom_to_layer=True)
m
14.8. Creating Choropleth Maps#
Choropleth maps are useful for visualizing data distributions. The add_data() method allows you to create choropleth maps from various vector formats. Below is an example that visualizes population data using the “Quantiles” classification scheme:
m = leafmap.Map()
data = "https://raw.githubusercontent.com/opengeos/leafmap/master/docs/data/countries.geojson"
m.add_data(
data, column="POP_EST", scheme="Quantiles", cmap="Blues", legend_title="Population"
)
m
You can also use different classification schemes like “EqualInterval” for different data visualizations:
m = leafmap.Map()
m.add_data(
data,
column="POP_EST",
scheme="EqualInterval",
cmap="Blues",
legend_title="Population",
)
m
14.9. Visualizing GeoParquet Data#
GeoParquet is a columnar format for geospatial data that allows efficient storage and retrieval. The following example demonstrates how to download, read, and visualize GeoParquet files using GeoPandas and Leafmap.
14.9.1. Loading and Visualizing Point Data#
First, import the necessary libraries:
import geopandas as gpd
Download and load a GeoParquet file containing city data:
url = "https://opengeos.org/data/duckdb/cities.parquet"
filename = "cities.parquet"
leafmap.download_file(url, filename, quiet=True)
'/home/runner/work/geog-312/geog-312/book/geospatial/cities.parquet'
Read the GeoParquet file into a GeoDataFrame and preview the first few rows:
gdf = gpd.read_parquet(filename)
gdf.head()
| country | geometry | id | latitude | longitude | name | population | |
|---|---|---|---|---|---|---|---|
| 0 | UGA | POINT (32.5333 0.5833) | 1.0 | 0.5833 | 32.5333 | Bombo | 75000.0 |
| 1 | UGA | POINT (30.275 0.671) | 2.0 | 0.6710 | 30.2750 | Fort Portal | 42670.0 |
| 2 | ITA | POINT (15.799 40.642) | 3.0 | 40.6420 | 15.7990 | Potenza | 69060.0 |
| 3 | ITA | POINT (14.656 41.563) | 4.0 | 41.5630 | 14.6560 | Campobasso | 50762.0 |
| 4 | ITA | POINT (7.315 45.737) | 5.0 | 45.7370 | 7.3150 | Aosta | 34062.0 |
You can use GeoDataFrame.explore() to visualize the data interactively:
gdf.explore()
Alternatively, you can add the data to a Leafmap interactive map and plot the points by specifying their latitude and longitude:
m = leafmap.Map()
m.add_points_from_xy(gdf, x="longitude", y="latitude")
m
14.9.2. Visualizing Polygon Data#
For polygon data, such as wetlands, you can follow a similar process. Start by downloading the GeoParquet file containing wetland polygons:
url = "https://data.source.coop/giswqs/nwi/wetlands/DC_Wetlands.parquet"
filename = "DC_Wetlands.parquet"
leafmap.download_file(url, filename, quiet=True)
'/home/runner/work/geog-312/geog-312/book/geospatial/DC_Wetlands.parquet'
Load the data into a GeoDataFrame and check its coordinate reference system (CRS):
gdf = gpd.read_parquet(filename)
gdf.head()
| ATTRIBUTE | WETLAND_TYPE | ACRES | Shape_Length | Shape_Area | geometry | |
|---|---|---|---|---|---|---|
| 0 | PEM5Ax | Freshwater Emergent Wetland | 0.454008 | 621.865659 | 1837.304895 | MULTIPOLYGON (((1629715.061 1937762.608, 16297... |
| 1 | PFO1A | Freshwater Forested/Shrub Wetland | 3.339719 | 586.449553 | 13515.364977 | MULTIPOLYGON (((1629117.705 1937092.842, 16291... |
| 2 | PUBHh | Freshwater Pond | 0.873177 | 335.425439 | 3533.620783 | MULTIPOLYGON (((1630250.202 1937446.102, 16302... |
| 3 | R4SBC | Riverine | 0.788316 | 1078.745304 | 3190.202659 | MULTIPOLYGON (((1630224.321 1937472.319, 16302... |
| 4 | R4SBC | Riverine | 0.069572 | 106.791860 | 281.546217 | MULTIPOLYGON (((1630340.011 1937467.028, 16303... |
gdf.crs
<Projected CRS: EPSG:5070>
Name: NAD83 / Conus Albers
Axis Info [cartesian]:
- X[east]: Easting (metre)
- Y[north]: Northing (metre)
Area of Use:
- name: United States (USA) - CONUS onshore - Alabama; Arizona; Arkansas; California; Colorado; Connecticut; Delaware; Florida; Georgia; Idaho; Illinois; Indiana; Iowa; Kansas; Kentucky; Louisiana; Maine; Maryland; Massachusetts; Michigan; Minnesota; Mississippi; Missouri; Montana; Nebraska; Nevada; New Hampshire; New Jersey; New Mexico; New York; North Carolina; North Dakota; Ohio; Oklahoma; Oregon; Pennsylvania; Rhode Island; South Carolina; South Dakota; Tennessee; Texas; Utah; Vermont; Virginia; Washington; West Virginia; Wisconsin; Wyoming.
- bounds: (-124.79, 24.41, -66.91, 49.38)
Coordinate Operation:
- name: Conus Albers
- method: Albers Equal Area
Datum: North American Datum 1983
- Ellipsoid: GRS 1980
- Prime Meridian: Greenwich
You can visualize the polygon data directly using explore() with folium:
gdf.explore()