FOSS4G 2021
Using Leafmap for Geospatial Analysis and Data Visualization
This notebook was developed for the leafmap workshop taking place on September 27, 2021 at the FOSS4G 2021 Conference.
Author: Qiusheng Wu
Launch this notebook to execute code interactively using:
- Google Colab: https://gishub.org/foss4g-colab
- Pangeo Binder JupyterLab: https://gishub.org/foss4g-binder
- Pangeo Binder Jupyter Notebook: https://gishub.org/foss4g-binder-nb
Introduction¶
Workshop description¶
Leafmap is a Python package for interactive mapping and geospatial analysis with minimal coding in a Jupyter environment. It is built upon a number of open-source packages, such as folium and ipyleaflet (for creating interactive maps), WhiteboxTools and whiteboxgui (for analyzing geospatial data), and ipywidgets (for designing interactive graphical user interface). The WhiteboxTools library currently contains 480+ tools for advanced geospatial analysis. Leafmap provides many convenient functions for loading and visualizing geospatial data with only one line of code. Users can also use the interactive user interface to load geospatial data without coding. Anyone with a web browser and Internet connection can use leafmap to perform geospatial analysis and data visualization in the cloud with minimal coding. The topics that will be covered in this workshop include:
- Creating interactive maps
- Changing basemaps
- Loading and visualizing vector/raster data
- Using Cloud Optimized GeoTIFF (COG) and SpatialTemporal Asset Catalog (STAC)
- Downloading OpenStreetMap data
- Loading data from a PostGIS database
- Creating custom legends and colorbars
- Creating split-panel maps and linked maps
- Visualizing Planet global monthly/quarterly mosaic
- Designing and publishing interactive web apps
- Performing geospatial analysis (e.g., hydrological analysis and LiDAR data analysis) using whiteboxgui.
This workshop is intended for scientific programmers, data scientists, geospatial analysts, and concerned citizens of Earth. The attendees are expected to have a basic understanding of Python and the Jupyter ecosystem. Familiarity with Earth science and geospatial datasets is useful but not required. More information about leafmap can be found at https://leafmap.org
Jupyter keyboard shortcuts¶
- Shift+Enter: run cell, select below
- Ctrl+Enter: : run selected cells
- Alt+Enter: run cell and insert below
- Tab: code completion or indent
- Shift+Tab: tooltip
- Ctrl+/: comment out code
Set up environment¶
Required Python packages:¶
- leafmap - A Python package for interactive mapping and geospatial analysis with minimal coding in a Jupyter environment
- geopandas - An open source project to make working with geospatial data in python easier.
- keplergl - A high-performance web-based application for visual exploration of large-scale geolocation data sets
- datapane - A Python library for building interactive reports in seconds
- xarray-leaflet - An xarray extension for tiled map plotting
Required API keys¶
Use Google Colab¶
Click the button below to open this notebook in Google Colab and execute code interactively.
# !pip install leafmap
import os
import subprocess
import sys
import warnings
warnings.filterwarnings("ignore")
A function for installing Python packages.
def install(package):
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
Install required Python packages in Google Colab.
pkgs = [
"leafmap",
"geopandas",
"keplergl",
"datapane",
"xarray_leaflet",
"osmnx",
"pygeos",
"imageio",
"tifffile",
]
if "google.colab" in sys.modules:
for pkg in pkgs:
install(pkg)
Use Pangeo Binder¶
Click the buttons below to open this notebook in JupyterLab (first button) or Jupyter Notebook (second button) and execute code interactively.
- JupyterLab: https://gishub.org/foss4g-binder
- Jupyter Notebook: https://gishub.org/foss4g-binder-nb
Use Miniconda/Anaconda¶
If you have Anaconda or Miniconda installed on your computer, you can install leafmap using the following commands. Leafmap has an optional dependency - geopandas, which can be challenging to install on some computers, especially Windows. It is highly recommended that you create a fresh conda environment to install geopandas and leafmap. Follow the commands below to set up a conda env and install geopandas, leafmap, datapane, keplergl, and xarray_leaflet.
conda create -n geo python=3.9
conda activate geo
conda install geopandas
conda install mamba -c conda-forge
mamba install leafmap datapane keplergl xarray_leaflet -c conda-forge
mamba install osmnx pygeos imageio tifffile -c conda-forge
jupyter lab
try:
import leafmap
except ImportError:
install("leafmap")
Create an interactive map¶
leafmap
has four plotting backends: folium, ipyleaflet, here-map, and kepler.gl. Note that the backends do not offer equal functionality. Some interactive functionality in ipyleaflet
might not be available in other plotting backends. To use a specific plotting backend, use one of the following:
import leafmap.leafmap as leafmap
import leafmap.foliumap as leafmap
import leafmap.heremap as leafmap
import leafmap.kepler as leafmap
Use ipyleaflet¶
import leafmap
m = leafmap.Map()
m
m = leafmap.Map(center=(40, -100), zoom=4) # center=[lat, lon]
m
m = leafmap.Map(center=(51.5, -0.15), zoom=17)
m
Change map size¶
m = leafmap.Map(height="400px", width="800px")
m
Set control visibility¶
When creating a map, set the following controls to either True
or False
as appropriate.
- attribution_control
- draw_control
- fullscreen_control
- layers_control
- measure_control
- scale_control
- toolbar_control
m = leafmap.Map(
draw_control=False,
measure_control=False,
fullscreen_control=False,
attribution_control=False,
)
m
Remove all controls from the map.
m = leafmap.Map()
m.clear_controls()
m
Change basemaps¶
Specify a Google basemap to use, can be one of ["ROADMAP", "TERRAIN", "SATELLITE", "HYBRID"].
m = leafmap.Map(google_map="TERRAIN") # HYBRID, ROADMAP, SATELLITE, TERRAIN
m
Add a basemap using the add_basemap()
function.
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m
Print out the list of available basemaps.
for basemap in leafmap.basemaps:
print(basemap[:5])
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
Add WMS tile layer¶
More WMS basemaps can be found at the following websites:
- USGS National Map: https://viewer.nationalmap.gov/services
- MRLC NLCD Land Cover data: https://www.mrlc.gov/data-services-page
- FWS NWI Wetlands data: https://www.fws.gov/wetlands/Data/Web-Map-Services.html
m = leafmap.Map()
naip_url = "https://services.nationalmap.gov/arcgis/services/USGSNAIPImagery/ImageServer/WMSServer?"
m.add_wms_layer(
url=naip_url, layers="0", name="NAIP Imagery", format="image/png", shown=True
)
m
Add xyzservices provider¶
Add a layer from xyzservices provider object.
import xyzservices.providers as xyz
basemap = xyz.CartoDB.DarkMatter
basemap
m = leafmap.Map()
m.add_basemap(basemap)
m
Add vector tile layer¶
m = leafmap.Map()
The URL to the vector tile.
url = "https://tile.nextzen.org/tilezen/vector/v1/512/all/{z}/{x}/{y}.mvt?api_key=gCZXZglvRQa6sB2z7JzL1w"
Attribution of the vector tile.
attribution = "Nextzen"
One can customize the vector tile layer style if needed. More info can be found at https://ipyleaflet.readthedocs.io/en/latest/api_reference/vector_tile.html
vector_tile_layer_styles = {}
Add the vector tile layer to the map.
m.add_vector_tile_layer(url, attribution, vector_tile_layer_styles)
m
Add COG/STAC layers¶
A Cloud Optimized GeoTIFF (COG) is a regular GeoTIFF file, aimed at being hosted on a HTTP file server, with an internal organization that enables more efficient workflows on the cloud. It does this by leveraging the ability of clients issuing HTTP GET range requests to ask for just the parts of a file they need.
More information about COG can be found at https://www.cogeo.org/in-depth.html
Some publicly available Cloud Optimized GeoTIFFs:
- https://stacindex.org/
- https://cloud.google.com/storage/docs/public-datasets/landsat
- https://www.digitalglobe.com/ecosystem/open-data
- https://earthexplorer.usgs.gov/
For this demo, we will use data from https://www.maxar.com/open-data/california-colorado-fires for mapping California and Colorado fires. A list of COGs can be found here.
Add COG layer¶
m = leafmap.Map()
url = "https://github.com/opengeos/data/releases/download/raster/Libya-2023-07-01.tif"
url2 = "https://github.com/opengeos/data/releases/download/raster/Libya-2023-09-13.tif"
m.add_cog_layer(url, name="Fire (pre-event)")
m.add_cog_layer(url2, name="Fire (post-event)")
m
Retrieve the bounding box coordinates of the COG file.
leafmap.cog_bounds(url)
Retrieve the centroid coordinates of the COG file.
leafmap.cog_center(url)
Retrieves the tile layer URL of the COG file.
leafmap.cog_tile(url)
Add STAC layer¶
The SpatioTemporal Asset Catalog (STAC) specification provides a common language to describe a range of geospatial information, so it can more easily be indexed and discovered. A 'spatiotemporal asset' is any file that represents information about the earth captured in a certain space and time. The initial focus is primarily remotely-sensed imagery (from satellites, but also planes, drones, balloons, etc), but the core is designed to be extensible to SAR, full motion video, point clouds, hyperspectral, LiDAR and derived data like NDVI, Digital Elevation Models, mosaics, etc. More information about STAC can be found at https://stacspec.org/
Some publicly available SpatioTemporal Asset Catalog (STAC):
For this demo, we will use STAC assets from https://stacindex.org/catalogs/spot-orthoimages-canada-2005#/?t=catalogs
m = leafmap.Map()
url = "https://canada-spot-ortho.s3.amazonaws.com/canada_spot_orthoimages/canada_spot5_orthoimages/S5_2007/S5_11055_6057_20070622/S5_11055_6057_20070622.json"
m.add_stac_layer(url, bands=["B3", "B2", "B1"], name="False color")
m
Retrieve the bounding box coordinates of the STAC file.
leafmap.stac_bounds(url)
Retrieve the centroid coordinates of the STAC file.
leafmap.stac_center(url)
Retrieve the band names of the STAC file.
leafmap.stac_bands(url)
Retrieve the tile layer URL of the STAC file.
leafmap.stac_tile(url, bands=["B3", "B2", "B1"])
Download samples raster datasets
More datasets can be downloaded from https://viewer.nationalmap.gov/basic/
landsat = "landsat.tif"
dem = "dem.tif"
Download a small Landsat imagery.
landsat_url = (
"https://drive.google.com/file/d/1EV38RjNxdwEozjc9m0FcO3LFgAoAX1Uw/view?usp=sharing"
)
leafmap.download_file(landsat_url, "landsat.tif", unzip=False)
Download a small DEM dataset.
dem_url = (
"https://drive.google.com/file/d/1vRkAWQYsLWCi6vcTMk8vLxoXMFbdMFn8/view?usp=sharing"
)
leafmap.download_file(dem_url, "dem.tif", unzip=False)
m = leafmap.Map()
Add local raster datasets to the map
More colormap can be found at https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html
m.add_raster(dem, colormap="terrain", layer_name="DEM")
m.add_raster(landsat, bands=[5, 4, 3], layer_name="Landsat")
m
List all available built-in legends.
legends = leafmap.builtin_legends
for legend in legends:
print(legend)
Add a WMS layer and built-in legend to the map.
m = leafmap.Map()
url = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2019_Land_Cover_L48/wms?"
m.add_wms_layer(
url,
layers="NLCD_2019_Land_Cover_L48",
name="NLCD 2019 CONUS Land Cover",
format="image/png",
transparent=True,
)
m.add_legend(builtin_legend="NLCD")
m
Add U.S. National Wetlands Inventory (NWI). More info at https://www.fws.gov/wetlands.
m = leafmap.Map(google_map="HYBRID")
url1 = "https://www.fws.gov/wetlands/arcgis/services/Wetlands/MapServer/WMSServer?"
m.add_wms_layer(
url1, layers="1", format="image/png", transparent=True, name="NWI Wetlands Vector"
)
url2 = "https://www.fws.gov/wetlands/arcgis/services/Wetlands_Raster/ImageServer/WMSServer?"
m.add_wms_layer(
url2, layers="0", format="image/png", transparent=True, name="NWI Wetlands Raster"
)
m.add_legend(builtin_legend="NWI")
m
Add custom legend¶
There are two ways you can add custom legends:
- Define legend labels and colors
- Define legend dictionary
Define legend keys and colors
m = leafmap.Map()
labels = ["One", "Two", "Three", "Four", "etc"]
# color can be defined using either hex code or RGB (0-255, 0-255, 0-255)
colors = ["#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3"]
# colors = [(255, 0, 0), (127, 255, 0), (127, 18, 25), (36, 70, 180), (96, 68, 123)]
m.add_legend(title="Legend", labels=labels, colors=colors)
m
Define a legend dictionary.
m = leafmap.Map()
url = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2019_Land_Cover_L48/wms?"
m.add_wms_layer(
url,
layers="NLCD_2019_Land_Cover_L48",
name="NLCD 2019 CONUS Land Cover",
format="image/png",
transparent=True,
)
legend_dict = {
"11 Open Water": "466b9f",
"12 Perennial Ice/Snow": "d1def8",
"21 Developed, Open Space": "dec5c5",
"22 Developed, Low Intensity": "d99282",
"23 Developed, Medium Intensity": "eb0000",
"24 Developed High Intensity": "ab0000",
"31 Barren Land (Rock/Sand/Clay)": "b3ac9f",
"41 Deciduous Forest": "68ab5f",
"42 Evergreen Forest": "1c5f2c",
"43 Mixed Forest": "b5c58f",
"51 Dwarf Scrub": "af963c",
"52 Shrub/Scrub": "ccb879",
"71 Grassland/Herbaceous": "dfdfc2",
"72 Sedge/Herbaceous": "d1d182",
"73 Lichens": "a3cc51",
"74 Moss": "82ba9e",
"81 Pasture/Hay": "dcd939",
"82 Cultivated Crops": "ab6c28",
"90 Woody Wetlands": "b8d9eb",
"95 Emergent Herbaceous Wetlands": "6c9fb8",
}
m.add_legend(legend_title="NLCD Land Cover Classification", legend_dict=legend_dict)
m
m = leafmap.Map()
m.add_basemap("USGS 3DEP Elevation")
colors = ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"]
m.add_colorbar(colors=colors, vmin=0, vmax=4000)
m
Categorical colorbar¶
m = leafmap.Map()
url = "https://elevation.nationalmap.gov/arcgis/services/3DEPElevation/ImageServer/WMSServer?"
m.add_wms_layer(
url,
layers="3DEPElevation:Hillshade Elevation Tinted",
name="USGS 3DEP Elevation",
format="image/png",
transparent=True,
)
colors = ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"]
m.add_colorbar(colors=colors, vmin=0, vmax=4000, categorical=True, step=4)
m
Add colormap¶
The colormap functionality requires the ipyleaflet plotting backend. Folium is not supported.
import leafmap.colormaps as cm
Common colormaps¶
Color palette for DEM data.
cm.palettes.dem
Show the DEM palette.
cm.plot_colormap(colors=cm.palettes.dem, axis_off=True)
Color palette for NDVI data.
cm.palettes.ndvi
Show the NDVI palette.
cm.plot_colormap(colors=cm.palettes.ndvi)
Custom colormaps¶
Specify the number of classes for a palette.
cm.get_palette("terrain", n_class=8)
Show the terrain palette with 8 classes.
cm.plot_colormap(colors=cm.get_palette("terrain", n_class=8))
Create a palette with custom colors, label, and font size.
cm.plot_colormap(colors=["red", "green", "blue"], label="Temperature", font_size=12)
Create a discrete color palette.
cm.plot_colormap(
colors=["red", "green", "blue"], discrete=True, label="Temperature", font_size=12
)
Specify the width and height for the palette.
cm.plot_colormap(
"terrain",
label="Elevation",
width=8.0,
height=0.4,
orientation="horizontal",
vmin=0,
vmax=1000,
)
Change the orentation of the colormap to be vertical.
cm.plot_colormap(
"terrain",
label="Elevation",
width=0.4,
height=4,
orientation="vertical",
vmin=0,
vmax=1000,
)
Horizontal colormap¶
Add a horizontal colorbar to an interactive map.
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m.add_colormap(
"terrain",
label="Elevation",
width=8.0,
height=0.4,
orientation="horizontal",
vmin=0,
vmax=4000,
)
m
Vertical colormap¶
Add a vertical colorbar to an interactive map.
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m.add_colormap(
"terrain",
label="Elevation",
width=0.4,
height=4,
orientation="vertical",
vmin=0,
vmax=4000,
)
m
List of available colormaps¶
cm.plot_colormaps(width=12, height=0.4)
in_csv = "https://raw.githubusercontent.com/opengeos/data/main/world/world_cities.csv"
df = leafmap.csv_to_df(in_csv)
df
Create a point layer from a CSV file containing lat/long information.
m = leafmap.Map()
m.add_xy_data(in_csv, x="longitude", y="latitude", layer_name="World Cities")
m
Set the output directory.
out_dir = os.getcwd()
out_shp = os.path.join(out_dir, "world_cities.shp")
Convert a CSV file containing lat/long information to a shapefile.
leafmap.csv_to_shp(in_csv, out_shp)
Convert a CSV file containing lat/long information to a GeoJSON.
out_geojson = os.path.join(out_dir, "world_cities.geojson")
leafmap.csv_to_geojson(in_csv, out_geojson)
Convert a CSV file containing lat/long information to a GeoPandas GeoDataFrame.
gdf = leafmap.csv_to_gdf(in_csv)
gdf
Add GeoJSON¶
Add a GeoJSON to the map.
m = leafmap.Map(center=[0, 0], zoom=2)
in_geojson = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/cable_geo.geojson"
m.add_geojson(in_geojson, layer_name="Cable lines", info_mode="on_hover")
m
Add a GeoJSON with random filled color to the map.
m = leafmap.Map(center=[0, 0], zoom=2)
url = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/countries.geojson"
m.add_geojson(
url, layer_name="Countries", fill_colors=["red", "yellow", "green", "orange"]
)
m
Use the style_callback
function for assigning a random color to each polygon.
import random
m = leafmap.Map(center=[0, 0], zoom=2)
url = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/countries.geojson"
def random_color(feature):
return {
"color": "black",
"fillColor": random.choice(["red", "yellow", "green", "orange"]),
}
m.add_geojson(url, layer_name="Countries", style_callback=random_color)
m
Use custom style
and hover_style
functions.
m = leafmap.Map(center=[0, 0], zoom=2)
url = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/countries.geojson"
style = {
"stroke": True,
"color": "#0000ff",
"weight": 2,
"opacity": 1,
"fill": True,
"fillColor": "#0000ff",
"fillOpacity": 0.1,
}
hover_style = {"fillOpacity": 0.7}
m.add_geojson(url, layer_name="Countries", style=style, hover_style=hover_style)
m
Add shapefile¶
m = leafmap.Map(center=[0, 0], zoom=2)
in_shp = "https://github.com/opengeos/leafmap/raw/master/examples/data/countries.zip"
m.add_shp(in_shp, layer_name="Countries")
m
Add KML¶
m = leafmap.Map()
in_kml = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/us_states.kml"
m.add_kml(in_kml, layer_name="US States KML")
m
Add GeoDataFrame¶
import geopandas as gpd
m = leafmap.Map()
gdf = gpd.read_file(
"https://github.com/opengeos/leafmap/raw/master/examples/data/cable_geo.geojson"
)
m.add_gdf(gdf, layer_name="Cable lines")
m
Read the GeoPandas sample dataset as a GeoDataFrame.
path_to_data = (
"https://github.com/opengeos/datasets/releases/download/vector/nybb.geojson"
)
gdf = gpd.read_file(path_to_data)
gdf
m = leafmap.Map()
m.add_gdf(gdf, layer_name="New York boroughs", fill_colors=["red", "green", "blue"])
m
Add point layer¶
Add a point layer using the interactive GUI.
m = leafmap.Map()
m
Add a point layer programmatically.
m = leafmap.Map()
url = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/us_cities.geojson"
m.add_point_layer(url, popup=["name", "pop_max"], layer_name="US Cities")
m
Add vector¶
The add_vector
function supports any vector data format supported by GeoPandas.
m = leafmap.Map(center=[0, 0], zoom=2)
url = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/countries.geojson"
m.add_vector(
url, layer_name="Countries", fill_colors=["red", "yellow", "green", "orange"]
)
m
m = leafmap.Map(toolbar_control=False, layers_control=True)
m.add_osm_from_geocode("New York City", layer_name="NYC")
m
m = leafmap.Map(toolbar_control=False, layers_control=True)
m.add_osm_from_geocode("Chicago, Illinois", layer_name="Chicago, IL")
m
OSM from place¶
Add OSM entities within boundaries of geocodable place(s) to the map.
Show OSM feature tags. https://wiki.openstreetmap.org/wiki/Map_features
# leafmap.osm_tags_list()
OSM from address¶
m = leafmap.Map(toolbar_control=False, layers_control=True)
m.add_osm_from_address(
address="New York City", tags={"amenity": "bar"}, dist=1500, layer_name="NYC bars"
)
m
m = leafmap.Map(toolbar_control=False, layers_control=True)
m.add_osm_from_address(
address="New York City",
tags={"landuse": ["retail", "commercial"], "building": True},
dist=1000,
layer_name="NYC buildings",
)
m
OSM from bbox¶
m = leafmap.Map(toolbar_control=False, layers_control=True)
north, south, east, west = 40.7551, 40.7454, -73.9738, -73.9965
m.add_osm_from_bbox(
north, south, east, west, tags={"amenity": "bar"}, layer_name="NYC bars"
)
m
OSM from point¶
Add OSM entities within some distance N, S, E, W of a point to the map.
m = leafmap.Map(
center=[46.7808, -96.0156], zoom=12, toolbar_control=False, layers_control=True
)
m.add_osm_from_point(
center_point=(46.7808, -96.0156),
tags={"natural": "water"},
dist=10000,
layer_name="Lakes",
)
m
m = leafmap.Map(
center=[39.9170, 116.3908], zoom=15, toolbar_control=False, layers_control=True
)
m.add_osm_from_point(
center_point=(39.9170, 116.3908),
tags={"building": True, "natural": "water"},
dist=1000,
layer_name="Beijing",
)
m
OSM from view¶
Add OSM entities within the current map view to the map.
m = leafmap.Map(toolbar_control=False, layers_control=True)
m.set_center(-73.9854, 40.7500, 16)
m
m.add_osm_from_view(tags={"amenity": "bar", "building": True}, layer_name="New York")
Create a GeoPandas GeoDataFrame from place.
gdf = leafmap.osm_gdf_from_place("New York City", tags={"amenity": "bar"})
gdf
Use WhiteboxTools¶
Use the built-in toolbox to perform geospatial analysis. For example, you can perform depression filling using the sample DEM dataset downloaded in the above step.
Download a sample DEM dataset.
url = "https://github.com/opengeos/whitebox-python/raw/master/whitebox/testdata/DEM.tif"
leafmap.download_file(url, "dem.tif")
m = leafmap.Map()
m
Display the toolbox using the default mode.
leafmap.whiteboxgui()
Display the toolbox using the collapsible tree mode. Note that the tree mode does not support Google Colab.
leafmap.whiteboxgui(tree=True)
Perform geospatial analysis using the whitebox package.
import whitebox
wbt = whitebox.WhiteboxTools()
wbt.verbose = False
wbt.version()
data_dir = os.getcwd()
wbt.set_working_dir(data_dir)
wbt.feature_preserving_smoothing("dem.tif", "smoothed.tif", filter=9)
wbt.breach_depressions("smoothed.tif", "breached.tif")
wbt.d_inf_flow_accumulation("breached.tif", "flow_accum.tif")
import matplotlib.pyplot as plt
import imageio
%matplotlib inline
original = imageio.imread(os.path.join(data_dir, "dem.tif"))
smoothed = imageio.imread(os.path.join(data_dir, "smoothed.tif"))
breached = imageio.imread(os.path.join(data_dir, "breached.tif"))
flow_accum = imageio.imread(os.path.join(data_dir, "flow_accum.tif"))
fig = plt.figure(figsize=(16, 11))
ax1 = fig.add_subplot(2, 2, 1)
ax1.set_title("Original DEM")
plt.imshow(original)
ax2 = fig.add_subplot(2, 2, 2)
ax2.set_title("Smoothed DEM")
plt.imshow(smoothed)
ax3 = fig.add_subplot(2, 2, 3)
ax3.set_title("Breached DEM")
plt.imshow(breached)
ax4 = fig.add_subplot(2, 2, 4)
ax4.set_title("Flow Accumulation")
plt.imshow(flow_accum)
plt.show()
Create basemap gallery¶
for basemap in leafmap.basemaps:
print(basemap)
layers = list(leafmap.basemaps.keys())[17:117]
leafmap.linked_maps(rows=20, cols=5, height="200px", layers=layers, labels=layers)
Create linked map¶
leafmap.basemaps.keys()
layers = ["ROADMAP", "HYBRID"]
leafmap.linked_maps(rows=1, cols=2, height="400px", layers=layers)
layers = ["Esri.WorldTopoMap", "OpenTopoMap"]
leafmap.linked_maps(rows=1, cols=2, height="400px", layers=layers)
Create a 2 * 2 linked map to visualize land cover change. Specify the center
and zoom
parameters to change the default map center and zoom level.
layers = [str(f"NLCD {year} CONUS Land Cover") for year in [2001, 2006, 2011, 2016]]
labels = [str(f"NLCD {year}") for year in [2001, 2006, 2011, 2016]]
leafmap.linked_maps(
rows=2,
cols=2,
height="300px",
layers=layers,
labels=labels,
center=[36.1, -115.2],
zoom=9,
)
Create split-panel map¶
Create a split-panel map by specifying the left_layer
and right_layer
, which can be chosen from the basemap names, or any custom XYZ tile layer.
leafmap.split_map(left_layer="ROADMAP", right_layer="HYBRID")
Hide the zoom control from the map.
leafmap.split_map(
left_layer="Esri.WorldTopoMap", right_layer="OpenTopoMap", zoom_control=False
)
Add labels to the map and change the default map center and zoom level.
leafmap.split_map(
left_layer="NLCD 2001 CONUS Land Cover",
right_layer="NLCD 2019 CONUS Land Cover",
left_label="2001",
right_label="2019",
label_position="bottom",
center=[36.1, -114.9],
zoom=10,
)
Create heat map¶
Specify the file path to the CSV. It can either be a file locally or on the Internet.
m = leafmap.Map(layers_control=True)
in_csv = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/world_cities.csv"
m.add_heatmap(
in_csv,
latitude="latitude",
longitude="longitude",
value="pop_max",
name="Heat map",
radius=20,
)
m
Use the folium plotting backend.
import leafmap.foliumap as leafmap
m = leafmap.Map()
in_csv = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/world_cities.csv"
m.add_heatmap(
in_csv,
latitude="latitude",
longitude="longitude",
value="pop_max",
name="Heat map",
radius=20,
)
colors = ["blue", "lime", "red"]
m.add_colorbar(colors=colors, vmin=0, vmax=10000)
m.add_title("World Population Heat Map", font_size="20px", align="center")
m
Save map to HTML¶
import leafmap
m = leafmap.Map()
m.add_basemap("Esri.NatGeoWorldMap")
m
Specify the output HTML file name to save the map as a web page.
m.to_html("mymap.html")
If the output HTML file name is not provided, the function will return a string containing contain the source code of the HTML file.
html = m.to_html()
# print(html)
Publish maps¶
To follow this tutorial, you will need to sign up for an account with https://datapane.com, then install and authenticate the datapane
Python package. More information can be found here.
pip install datapane
datapane login
datapane ping
If you encounter folium version errors, please uncomment the following line to update folium and restart the kernel.
import datapane as dp
import leafmap.foliumap as leafmap
os.environ.get("DP_TOKEN")
if os.environ.get("DP_TOKEN") is None:
os.environ["DP_TOKEN"] = "your-api-key"
dp.login(token=os.environ["DP_TOKEN"])
Elevation map¶
m = leafmap.Map()
m.add_basemap("USGS 3DEP Elevation")
colors = ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"]
vmin = 0
vmax = 4000
m.add_colorbar(colors=colors, vmin=vmin, vmax=vmax)
m
m.publish(name="Elevation Map of North America")
Land cover map¶
m = leafmap.Map()
m.add_basemap("NLCD 2019 CONUS Land Cover")
m.add_legend(builtin_legend="NLCD")
m
m.publish(name="National Land Cover Database (NLCD) 2019")
Population heat map¶
m = leafmap.Map()
in_csv = "https://raw.githubusercontent.com/opengeos/leafmap/master/examples/data/world_cities.csv"
m.add_heatmap(
in_csv,
latitude="latitude",
longitude="longitude",
value="pop_max",
name="Heat map",
radius=20,
)
colors = ["blue", "lime", "red"]
vmin = 0
vmax = 10000
m.add_colorbar(colors=colors, vmin=vmin, vmax=vmax)
m
m.publish(name="World Population Heat Map")
Use planet imagery¶
First, you need to sign up a Planet account and get an API key. See https://www.planet.com/nicfi & https://developers.planet.com/quickstart/apis. Uncomment the following line to pass in your API key.
if os.environ.get("PLANET_API_KEY") is None:
os.environ["PLANET_API_KEY"] = "your-api-key"
biannual_tiles = leafmap.planet_biannual_tiles_tropical()
for tile in biannual_tiles:
print(tile)
monthly_tiles = leafmap.planet_monthly_tiles_tropical()
for tile in monthly_tiles:
print(tile)
Add a Planet monthly mosaic by specifying year and month.
m = leafmap.Map()
layer = monthly_tiles["Planet_2021-08"]
m.add_layer(layer)
m
m = leafmap.Map()
layer = biannual_tiles["Planet_2020-06_2020-08"]
m.add_layer(layer)
m
Use timeseries inspector¶
if os.environ.get("PLANET_API_KEY") is None:
os.environ["PLANET_API_KEY"] = "your-api-key"
biannual_tiles = leafmap.planet_biannual_tiles_tropical()
leafmap.ts_inspector(biannual_tiles, center=[8.5, -80], zoom=5)
monthly_tiles = leafmap.planet_monthly_tiles_tropical()
leafmap.ts_inspector(monthly_tiles, center=[8.5, -80], zoom=5)
tiles = leafmap.planet_tiles_tropical()
leafmap.ts_inspector(tiles, center=[8.5, -80], zoom=5)
Use time slider¶
Use the time slider to visualize Planet quarterly mosaic.
if os.environ.get("PLANET_API_KEY") is None:
os.environ["PLANET_API_KEY"] = "your-api-key"
m = leafmap.Map(center=[8.5, -80], zoom=5)
layers_dict = leafmap.planet_monthly_tiles_tropical()
m.add_time_slider(layers_dict, time_interval=1)
m
m = leafmap.Map(center=[8.5, -80], zoom=5)
layers_dict = leafmap.planet_biannual_tiles_tropical()
m.add_time_slider(layers_dict, time_interval=1)
m
Use the time slider to visualize basemaps.
m = leafmap.Map()
m.clear_layers()
layers_dict = leafmap.basemap_xyz_tiles()
m.add_time_slider(layers_dict, time_interval=1)
m
Use PostGIS¶
Setting up the conda env:
conda create -n geo python=3.9
conda activate geo
conda install geopandas
conda install mamba -c conda-forge
mamba install leafmap sqlalchemy psycopg2 -c conda-forge
Sample dataset:
- nyc_data.zip (Watch this video to load data into PostGIS)
Connect to the database¶
You can directly pass in the user name and password to access the database. Alternative, you can define environment variables. The default environment variables for user and password are SQL_USER
and SQL_PASSWORD
, respectively.
The try...except...
statements are only used for building the documentation website (https://leafmap.org) because the PostGIS database is not available on GitHub. If you are running the notebook with Jupyter installed locally and PostGIS set up properly, you don't need these try...except...
statements.
try:
con = leafmap.connect_postgis(
database="nyc", host="localhost", user=None, password=None, use_env_var=True
)
except:
pass
Perform SQL queries¶
Create a GeoDataFrame from a sql query.
sql = "SELECT * FROM nyc_neighborhoods"
try:
gdf = leafmap.read_postgis(sql, con)
display(gdf)
except:
pass
Display data on the map¶
try:
m = leafmap.Map()
m.add_gdf_from_postgis(
sql, con, layer_name="NYC Neighborhoods", fill_colors=["red", "green", "blue"]
)
display(m)
except:
pass
Add widget to the map¶
import leafmap
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import Output
from ipyleaflet import WidgetControl
m = leafmap.Map()
# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
fig, ax = plt.subplots()
ax.plot(t, s)
ax.set(
xlabel="time (s)", ylabel="voltage (mV)", title="About as simple as it gets, folks"
)
ax.grid()
# Create an output widget to host the plot
output_widget = Output()
# Show the plot on the widget
with output_widget:
output_widget.clear_output()
plt.show()
# Add the widget as a control to the map
output_control = WidgetControl(widget=output_widget, position="bottomright")
m.add_control(output_control)
m
Develop custom widgets¶
import leafmap
import ipywidgets as widgets
from ipyleaflet import WidgetControl
Create a toolbar button¶
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="gears",
layout=widgets.Layout(width="28px", height="28px", padding=padding),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding=padding),
)
toolbar = widgets.HBox([toolbar_button])
toolbar
Add toolbar event¶
def toolbar_click(change):
if change["new"]:
toolbar.children = [toolbar_button, close_button]
else:
toolbar.children = [toolbar_button]
toolbar_button.observe(toolbar_click, "value")
def close_click(change):
if change["new"]:
toolbar_button.close()
close_button.close()
toolbar.close()
close_button.observe(close_click, "value")
toolbar
Add a toolbar grid¶
rows = 2
cols = 2
grid = widgets.GridspecLayout(
rows, cols, grid_gap="0px", layout=widgets.Layout(width="65px")
)
icons = ["folder-open", "map", "info", "question"]
for i in range(rows):
for j in range(cols):
grid[i, j] = widgets.Button(
description="",
button_style="primary",
icon=icons[i * rows + j],
layout=widgets.Layout(width="28px", padding="0px"),
)
grid
toolbar = widgets.VBox([toolbar_button])
def toolbar_click(change):
if change["new"]:
toolbar.children = [widgets.HBox([close_button, toolbar_button]), grid]
else:
toolbar.children = [toolbar_button]
toolbar_button.observe(toolbar_click, "value")
toolbar
Add toolbar to leafmap¶
toolbar_ctrl = WidgetControl(widget=toolbar, position="topright")
m = leafmap.Map()
m.add_control(toolbar_ctrl)
m
output = widgets.Output()
output_ctrl = WidgetControl(widget=output, position="bottomright")
m.add_control(output_ctrl)
def tool_click(b):
with output:
output.clear_output()
print(f"You clicked the {b.icon} button")
for i in range(rows):
for j in range(cols):
tool = grid[i, j]
tool.on_click(tool_click)