16. MapLibre#
The notebook demonstrates how to create 3D maps using the MapLibre Python package. The examples shown in this notebook are based on the MapLibre documentation.
16.1. Installation#
Uncomment the following line to install leafmap if needed.
# %pip install "leafmap[maplibre]"
16.2. Import libraries#
import leafmap.maplibregl as leafmap
16.3. Create maps#
Create an interactive map by specifying map center [lon, lat], zoom level, pitch, and bearing.
m = leafmap.Map(center=[-100, 40], zoom=3, pitch=0, bearing=0)
m
To customize the basemap, you can specify the style
parameter. It can be an URL or a string, such as dark-matter
, positron
, voyager
, demotiles
.
m = leafmap.Map(style="positron")
m
To create a map with a background color, use style="background-<COLOR>"
, such as background-lightgray
and background-green
.
m = leafmap.Map(style="background-lightgray")
m
Alternatively, you can provide a URL to a vector style.
style = "https://demotiles.maplibre.org/style.json"
m = leafmap.Map(style=style)
m
16.4. Add controls#
The control to add to the map. Can be one of the following: scale
, fullscreen
, geolocate
, navigation
.
m = leafmap.Map()
m.add_control("geolocate", position="top-left")
m
16.5. Add basemaps#
m = leafmap.Map()
m
m.add_basemap()
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m
m.add_basemap("Esri.WorldImagery")
16.6. XYZ tile layer#
m = leafmap.Map()
url = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
m.add_tile_layer(
url, name="OpenStreetMap", attribution="OpenStreetMap", opacity=1.0, visible=True
)
m
16.7. WMS layer#
m = leafmap.Map(center=[-100, 40], zoom=3)
m.add_basemap("Esri.WorldImagery")
url = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2021_Land_Cover_L48/wms"
layers = "NLCD_2021_Land_Cover_L48"
m.add_wms_layer(url, layers=layers, name="NLCD", opacity=0.8)
m
16.8. COG layer#
m = leafmap.Map()
url = (
"https://github.com/opengeos/datasets/releases/download/raster/Libya-2023-07-01.tif"
)
m.add_cog_layer(url, name="COG", attribution="Maxar", fit_bounds=True)
m
16.9. STAC layer#
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=["B4", "B3", "B2"], name="SPOT", vmin=0, vmax=150)
m
16.10. Local raster#
url = "https://github.com/opengeos/datasets/releases/download/raster/srtm90.tif"
filepath = "srtm90.tif"
leafmap.download_file(url, filepath)
m = leafmap.Map()
m.add_raster(filepath, colormap="terrain", name="DEM")
m
16.11. Vancouver Property Value#
m = leafmap.Map(
center=[-123.13, 49.254], zoom=11, style="dark-matter", pitch=45, bearing=0
)
url = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json"
paint_line = {
"line-color": "white",
"line-width": 2,
}
paint_fill = {
"fill-extrusion-color": {
"property": "valuePerSqm",
"stops": [
[0, "grey"],
[1000, "yellow"],
[5000, "orange"],
[10000, "darkred"],
[50000, "lightblue"],
],
},
"fill-extrusion-height": ["*", 10, ["sqrt", ["get", "valuePerSqm"]]],
"fill-extrusion-opacity": 0.9,
}
m.add_geojson(url, layer_type="line", paint=paint_line, name="blocks-line")
m.add_geojson(url, layer_type="fill-extrusion", paint=paint_fill, name="blocks-fill")
m
m.layer_interact()
16.12. Earthquake Clusters#
m = leafmap.Map(style="positron")
data = "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
source_args = {
"cluster": True,
"cluster_radius": 50,
"cluster_min_points": 2,
"cluster_max_zoom": 14,
"cluster_properties": {
"maxMag": ["max", ["get", "mag"]],
"minMag": ["min", ["get", "mag"]],
},
}
m.add_geojson(
data,
layer_type="circle",
name="earthquake-circles",
filter=["!", ["has", "point_count"]],
paint={"circle-color": "darkblue"},
source_args=source_args,
)
m.add_geojson(
data,
layer_type="circle",
name="earthquake-clusters",
filter=["has", "point_count"],
paint={
"circle-color": [
"step",
["get", "point_count"],
"#51bbd6",
100,
"#f1f075",
750,
"#f28cb1",
],
"circle-radius": ["step", ["get", "point_count"], 20, 100, 30, 750, 40],
},
source_args=source_args,
)
m.add_geojson(
data,
layer_type="symbol",
name="earthquake-labels",
filter=["has", "point_count"],
layout={
"text-field": ["get", "point_count_abbreviated"],
"text-size": 12,
},
source_args=source_args,
)
m
16.13. Airport Markers#
from maplibre.controls import Marker, MarkerOptions, Popup, PopupOptions
import pandas as pd
m = leafmap.Map(style="positron")
url = "https://github.com/visgl/deck.gl-data/raw/master/examples/line/airports.json"
data = leafmap.pandas_to_geojson(
url, "coordinates", properties=["type", "name", "abbrev"]
)
m.add_geojson(
data,
name="Airports",
layer_type="circle",
paint={
"circle-color": [
"match",
["get", "type"],
"mid",
"darkred",
"major",
"darkgreen",
"darkblue",
],
"circle_radius": 10,
"circle-opacity": 0.3,
},
)
def get_color(airport_type: str) -> str:
color = "darkblue"
if airport_type == "mid":
color = "darkred"
elif airport_type == "major":
color = "darkgreen"
return color
airports_data = pd.read_json(url)
popup_options = PopupOptions(close_button=False)
for _, r in airports_data.iterrows():
m.add_marker(
lng_lat=r["coordinates"],
options=MarkerOptions(color=get_color(r["type"])),
popup=Popup(
text=r["name"],
options=popup_options,
),
)
m
16.14. 3D Indoor Mapping#
m = leafmap.Map(
center=(-87.61694, 41.86625), zoom=17, pitch=40, bearing=20, style="positron"
)
m.add_basemap("OpenStreetMap.Mapnik")
data = "https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson"
m.add_geojson(
data,
layer_type="fill-extrusion",
name="floorplan",
paint={
"fill-extrusion-color": ["get", "color"],
"fill-extrusion-height": ["get", "height"],
"fill-extrusion-base": ["get", "base_height"],
"fill-extrusion-opacity": 0.5,
},
)
m
16.15. Custom Basemap#
import leafmap.maplibregl as leafmap
from maplibre.basemaps import construct_basemap_style
from maplibre import Layer, LayerType, Map, MapOptions
from maplibre.sources import GeoJSONSource
bg_layer = Layer(
type=LayerType.BACKGROUND,
id="background",
source=None,
paint={"background-color": "darkblue", "background-opacity": 0.8},
)
countries_source = GeoJSONSource(
data="https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_110m_admin_0_countries.geojson"
)
lines_layer = Layer(
type=LayerType.LINE,
source="countries",
paint={"line-color": "white", "line-width": 1.5},
)
polygons_layer = Layer(
type=LayerType.FILL,
source="countries",
paint={"fill-color": "darkred", "fill-opacity": 0.8},
)
custom_basemap = construct_basemap_style(
layers=[bg_layer, polygons_layer, lines_layer],
sources={"countries": countries_source},
)
m = leafmap.Map(style=custom_basemap)
data = "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
m.add_geojson(
data,
layer_type="circle",
name="earthquakes",
paint={"circle-color": "yellow", "circle-radius": 5},
)
m.add_popup("earthquakes", "mag")
m
16.16. H3 Grid UK Road Safety#
import pandas as pd
import h3
RESOLUTION = 7
COLORS = (
"lightblue",
"turquoise",
"lightgreen",
"yellow",
"orange",
"darkred",
)
road_safety = pd.read_csv(
"https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv"
).dropna()
def create_h3_grid(res=RESOLUTION) -> dict:
road_safety["h3"] = road_safety.apply(
lambda x: h3.geo_to_h3(x["lat"], x["lng"], resolution=res), axis=1
)
df = road_safety.groupby("h3").h3.agg("count").to_frame("count").reset_index()
df["hexagon"] = df.apply(
lambda x: [h3.h3_to_geo_boundary(x["h3"], geo_json=True)], axis=1
)
df["color"] = pd.cut(
df["count"],
bins=len(COLORS),
labels=COLORS,
)
return leafmap.pandas_to_geojson(
df, "hexagon", geometry_type="Polygon", properties=["count", "color"]
)
m = leafmap.Map(
center=(-1.415727, 52.232395),
zoom=7,
pitch=40,
bearing=-27,
)
data = create_h3_grid()
m.add_geojson(
data,
layer_type="fill-extrusion",
paint={
"fill-extrusion-color": ["get", "color"],
"fill-extrusion-opacity": 0.7,
"fill-extrusion-height": ["*", 100, ["get", "count"]],
},
)
m
16.17. Deck.GL Layer#
m = leafmap.Map(
style="positron",
center=(-122.4, 37.74),
zoom=12,
pitch=40,
)
deck_grid_layer = {
"@@type": "GridLayer",
"id": "GridLayer",
"data": "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json",
"extruded": True,
"getPosition": "@@=COORDINATES",
"getColorWeight": "@@=SPACES",
"getElevationWeight": "@@=SPACES",
"elevationScale": 4,
"cellSize": 200,
"pickable": True,
}
m.add_deck_layers([deck_grid_layer], tooltip="Number of points: {{ count }}")
m
16.18. Multiple Deck.GL Layers#
import requests
data = requests.get(
"https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_airports.geojson"
).json()
m = leafmap.Map(
style="positron",
center=(0.45, 51.47),
zoom=4,
pitch=30,
)
deck_geojson_layer = {
"@@type": "GeoJsonLayer",
"id": "airports",
"data": data,
"filled": True,
"pointRadiusMinPixels": 2,
"pointRadiusScale": 2000,
"getPointRadius": "@@=11 - properties.scalerank",
"getFillColor": [200, 0, 80, 180],
"autoHighlight": True,
"pickable": True,
}
deck_arc_layer = {
"@@type": "ArcLayer",
"id": "arcs",
"data": [
feature
for feature in data["features"]
if feature["properties"]["scalerank"] < 4
],
"getSourcePosition": [-0.4531566, 51.4709959], # London
"getTargetPosition": "@@=geometry.coordinates",
"getSourceColor": [0, 128, 200],
"getTargetColor": [200, 0, 80],
"getWidth": 2,
"pickable": True,
}
m.add_deck_layers(
[deck_geojson_layer, deck_arc_layer],
tooltip={
"airports": "{{ &properties.name }}",
"arcs": "gps_code: {{ properties.gps_code }}",
},
)
m