ipyleaflet: Interactive Maps in the Jupyter Notebook

Repository: https://github.com/jupyter-widgets/ipyleaflet

Installation:

conda install -c conda-forge ipyleaflet

Base map


In [ ]:
from ipyleaflet import Map, basemaps, basemap_to_tiles

In [ ]:
import ipyleaflet

In [ ]:
center = (52.204793, 360.121558)

m = Map(
    layers=(basemap_to_tiles(basemaps.NASAGIBS.ModisTerraTrueColorCR, "2018-11-12"), ),
    center=center,
    zoom=4
)

m

In [ ]:
m.zoom

In [ ]:
m.zoom = 5

In [ ]:
from ipywidgets import IntSlider, link

zoom_slider = IntSlider(m.zoom, min=m.layers[0].min_zoom, max=m.layers[0].max_zoom)
link((zoom_slider, 'value'), (m, 'zoom'))
zoom_slider

Layers

ipyleaflet provides a set of Layers and Controls that can be dynamically created and added/removed from the Map. You can find more details in the documentation: https://ipyleaflet.readthedocs.io

Marker Layer


In [ ]:
from ipyleaflet import Marker, Icon

icon = Icon(icon_url='https://leafletjs.com/examples/custom-icons/leaf-red.png', icon_size=[38, 95], icon_anchor=[22,94])
mark = Marker(location=center, icon=icon, rotation_origin='22px 94px')

m.add_layer(mark)

In [ ]:
import time

for _ in range(500):
    mark.rotation_angle += 1
    time.sleep(0.01)

JupyterLab Sidecar

Repository: https://github.com/jupyter-widgets/jupyterlab-sidecar

Installation:

pip install sidecar


In [ ]:
from sidecar import Sidecar
from IPython.display import display

In [ ]:
sc = Sidecar(title='Map widget')

with sc:
    display(m)

Heatmap Layer


In [ ]:
from ipywidgets import Button, IntSlider, link
from ipyleaflet import Heatmap
from random import gauss
import time

In [ ]:
center = (37.09, -103.66)
zoom = 5

In [ ]:
def create_random_data(length):
    "Return a list of some random lat/lon/value triples."
    return [[gauss(center[0], 2), 
             gauss(center[1], 4),
             gauss(700, 300)] for i in range(length)]

In [ ]:
m.center = center
m.zoom = zoom

In [ ]:
heat = Heatmap(locations=create_random_data(1000), radius=20, blur=10)
m.add_layer(heat)

In [ ]:
def generate(_):
    heat.locations = create_random_data(1000)

button = Button(description='Generate data', button_style='success')
button.on_click(generate)
button

In [ ]:
slider = IntSlider(min=10, max=30, value=heat.radius)
link((slider, 'value'), (heat, 'radius'))
slider

GeoJSON Layer with mouse events


In [ ]:
from ipywidgets import Text, HTML, HBox
from ipyleaflet import GeoJSON, WidgetControl, Map 
import json

In [ ]:
m = Map(center = (43,-100), zoom = 4)

geo_json_data = json.load(open('src/us-states-density-colored.json'))
geojson = GeoJSON(data=geo_json_data, hover_style={'color': 'black', 'dashArray': '5, 5', 'weight': 2})
m.add_layer(geojson)

html = HTML('''
    <h4>US population density</h4>
    Hover over a state
''')
html.layout.margin = '0px 20px 20px 20px'
control = WidgetControl(widget=html, position='topright')
m.add_control(control)

def update_html(properties, **kwargs):
    html.value = '''
        <h4>US population density</h4>
        <h2><b>{}</b></h2>
        {} people / mi^2
    '''.format(properties['name'], properties['density'])

geojson.on_hover(update_html)

m

Choropleth Layer


In [ ]:
from ipyleaflet import Choropleth, Map 
import json 
import pandas as pd
from ipywidgets import link, FloatSlider
from branca.colormap import linear

geo_json_data = json.load(open('src/us-states.json')) 
m = Map(center = (43,-100), zoom = 4)
unemployment = pd.read_csv('src/US_Unemployment_Oct2012.csv')

In [ ]:
unemployment =  dict(zip(unemployment['State'].tolist(), unemployment['Unemployment'].tolist()))

In [ ]:
layer = Choropleth(
    geo_data=geo_json_data,
    choro_data=unemployment,
    colormap=linear.YlOrRd_04, 
    border_color='black',
    style={'fillOpacity': 0.8, 'dashArray': '5, 5'}
)

In [ ]:
m.add_layer(layer)
m

In [ ]:
slider = FloatSlider(min=layer.value_min, value=layer.value_min, max=layer.value_max, continuous_update=False)

link((slider, 'value'), (layer, 'value_min'))

slider

Velocity Layer


In [ ]:
from ipyleaflet import Velocity
import xarray as xr

In [ ]:
center = (0, 0)
zoom = 4

m2 = Map(center=center, zoom=zoom, interpolation='nearest', basemap=basemaps.CartoDB.DarkMatter)

sc2 = Sidecar(title='Map Velocity')

with sc2:
    display(m2)

In [ ]:
ds = xr.open_dataset('src/wind-global.nc')
display_options = {
    'velocityType': 'Global Wind',
    'displayPosition': 'bottomleft',
    'displayEmptyString': 'No wind data'
}
wind = Velocity(data=ds,
                zonal_speed='u_wind',
                meridional_speed='v_wind',
                latitude_dimension='lat',
                longitude_dimension='lon',
                velocity_scale=0.01,
                max_velocity=20,
                display_options=display_options)
m2.add_layer(wind)

Controls

SlipMap Control


In [ ]:
from ipyleaflet import Map, basemaps, basemap_to_tiles, SplitMapControl

m = Map(center=(42.6824, 365.581), zoom=5)

right_layer = basemap_to_tiles(basemaps.NASAGIBS.ModisTerraTrueColorCR, "2017-11-11")
left_layer = basemap_to_tiles(basemaps.NASAGIBS.ModisAquaBands721CR, "2017-11-11")

control = SplitMapControl(left_layer=left_layer, right_layer=right_layer)
m.add_control(control)

m

JupyterLab Themes Support

Widget Control


In [ ]:
from ipywidgets import IntSlider, ColorPicker, Button, link
from ipyleaflet import Map, basemaps, WidgetControl

m = Map(center=(46.01, 6.16), zoom=12, basemap=basemaps.Stamen.Terrain)
m

In [ ]:
zoom_slider = IntSlider(description='Zoom level:', min=0, max=15, value=7)

link((zoom_slider, 'value'), (m, 'zoom'))
widget_control1 = WidgetControl(widget=zoom_slider, position='topright')

m.add_control(widget_control1)

In [ ]:
color_picker = ColorPicker(description='Pick a color:')

widget_control2 = WidgetControl(widget=color_picker, position='bottomright')

m.add_control(widget_control2)

In [ ]:
minimap = Map(
    zoom_control=False, attribution_control=False, 
    zoom=5, center=m.center, basemap=basemaps.Stamen.Terrain
)

minimap.layout.width = '150px'
minimap.layout.height = '150px'

link((minimap, 'center'), (m, 'center'))
minimap_control = WidgetControl(widget=minimap, position='bottomleft')

m.add_control(minimap_control)

Fullscreen Control


In [ ]:
from ipyleaflet import Map, FullScreenControl

control = FullScreenControl()
m.add_control(control)

ipyleaflet combined with other interactive widgets libraries


In [ ]:
import numpy as np
import bqplot.pyplot as plt
from bqplot import *
from traitlets import observe
from sidecar import Sidecar
from ipywidgets import VBox, Button
from ipyleaflet import Map, Marker, Popup

In [ ]:
axes_options = {'x': {'label': 'x'}, 'y': {'label': 'y'}}

x = np.arange(40)
y = np.cumsum(np.random.randn(2, 40), axis=1)

fig = plt.figure(animation_duration=1000)
lines = plt.plot(x=x, y=y, colors=['red', 'green'], axes_options=axes_options)

def generate(_):
    lines.y = np.cumsum(np.random.randn(2, 40), axis=1)

button = Button(description='Generate data', button_style='success')
button.on_click(generate)

box_plot = VBox([fig, button])

fig.layout.min_width = '270px'
fig.layout.width = '99%'
fig.layout.height = '320px'

fig

In [ ]:
center = (52.204793, 360.121558)

m = Map(center=center, zoom=9, close_popup_on_click=False)

marker = Marker(location=(52.1, 359.9))
m.add_layer(marker)

marker.popup = box_plot

sc = Sidecar(title='Map and bqplot')

with sc:
    display(m)

Clean


In [ ]:
from ipywidgets import Widget
Widget.close_all()

Example: ipyleaflet application using qgrid and bqplot