Skip to content

Commit

Permalink
Add visible_callback
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Aug 4, 2022
1 parent 15b63ca commit 90148e7
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 4 deletions.
134 changes: 134 additions & 0 deletions examples/markers_or_density.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "15eefe2f-7d1b-4814-9a5b-3b57df451fd7",
"metadata": {},
"outputs": [],
"source": [
"from ipyleaflet import GeoData, Map, basemaps, LayersControl, WidgetControl\n",
"from ipywidgets import FloatSlider\n",
"import xarray_leaflet\n",
"import geopandas as gpd\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "30114fbe-e6ef-4547-98ab-28686f8980b1",
"metadata": {},
"outputs": [],
"source": [
"!wget https://raw.githubusercontent.com/drei01/geojson-world-cities/master/cities.geojson"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab8cd637-be46-4953-96a8-edd918aeccf8",
"metadata": {},
"outputs": [],
"source": [
"df = gpd.read_file(\"cities.geojson\")\n",
"df.geometry = df.geometry.centroid\n",
"measurement = \"mask\"\n",
"df[measurement] = 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "55a78adf",
"metadata": {},
"outputs": [],
"source": [
"m = Map(center=(34.36519854648025, -47.3743535773436), zoom=3, basemap=basemaps.CartoDB.DarkMatter, interpolation='nearest')\n",
"m"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6180d7f5-05e5-4f56-b179-78cc8f581f1a",
"metadata": {},
"outputs": [],
"source": [
"class LayerManager:\n",
"\n",
" def __init__(self, df, measurement):\n",
" self.df = df\n",
" self.measurement = measurement\n",
" self.marker_layer = None\n",
"\n",
" def visible_callback(self, m, da, bbox):\n",
" # show density if there are too many points\n",
" # otherwise show individual markers\n",
" density_visible = da.sum() > 500\n",
" if self.marker_layer:\n",
" # remove markers\n",
" m.remove(self.marker_layer)\n",
" self.marker_layer = None\n",
" if not density_visible:\n",
" # only show markers that are on the map view\n",
" self.marker_layer = GeoData(geo_dataframe=self.df.clip(bbox))\n",
" m.add(self.marker_layer)\n",
" return density_visible\n",
"\n",
"layer_manager = LayerManager(df, measurement)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2ab54311-d960-4b51-9623-e8d91b97ad75",
"metadata": {},
"outputs": [],
"source": [
"l = df.leaflet.plot(m, measurement=\"mask\", colormap=plt.cm.inferno, visible_callback=layer_manager.visible_callback)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fe1ea3ac-7c1b-4443-8f82-2bffe819b27c",
"metadata": {},
"outputs": [],
"source": [
"layers_control = LayersControl(position='topright')\n",
"m.add_control(layers_control)\n",
"\n",
"opacity_slider = FloatSlider(description='Opacity:', min=0, max=1, value=1)\n",
"\n",
"def set_opacity(change):\n",
" l.opacity = change['new']\n",
"\n",
"opacity_slider.observe(set_opacity, names='value')\n",
"slider_control = WidgetControl(widget=opacity_slider, position='bottomleft')\n",
"m.add_control(slider_control)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
21 changes: 17 additions & 4 deletions xarray_leaflet/xarray_leaflet.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import asyncio
import os
import tempfile
from typing import Optional
from typing import Callable, Optional

import geopandas as gpd
import matplotlib as mpl
import mercantile
import numpy as np
import pandas as pd
import xarray as xr
from ipyleaflet import DrawControl, LocalTileLayer, WidgetControl
from ipyleaflet import DrawControl, Layer, LocalTileLayer, WidgetControl
from ipyspin import Spinner
from IPython.display import Image, display
from ipyurl import Url
Expand Down Expand Up @@ -62,6 +62,7 @@ def plot(
resampling=Resampling.nearest,
get_base_url=None,
measurement: Optional[str] = None,
visible_callback: Optional[Callable] = None,
):
"""Display an array as an interactive map.
Expand Down Expand Up @@ -114,6 +115,13 @@ def plot(
A function taking the window URL and returning the base URL to use.
measurement: str, optional
The geocube measurement.
visible_callback: callable, optional
A callable taking the following arguments:
- the ipyleaflet.Map
- the xarray.DataArray of the visible region
- the mercantile.LngLatBbox of the visible region
and returning True if the layer should be shown, False otherwise.
Returns
-------
Expand All @@ -125,6 +133,7 @@ def plot(

if self.is_vector:
# source is a GeoDataFrame (vector)
self.visible_callback = visible_callback
if measurement is None:
raise RuntimeError("You must provide a 'measurement'.")
self.measurement = measurement
Expand Down Expand Up @@ -365,6 +374,10 @@ def _get_vector_tiles(self, change=None):
if self.dynamic:
llbbox = mercantile.LngLatBbox(west, south, east, north)
da_visible = self.zvect.get_da_llbbox(llbbox, z)
# check if we must show the layer
if self.visible_callback and not self.visible_callback(self.m, da_visible, llbbox):
self.m.remove_control(self.spinner_control)
return
if da_visible is None:
self.max_value = 0
else:
Expand Down Expand Up @@ -600,7 +613,7 @@ async def async_fit_bounds(self):
class DataArrayLeaflet(Leaflet):
"""A DataArraye extension for tiled map plotting, based on (ipy)leaflet."""

def __init__(self, da: xr.DataArray = None):
def __init__(self, da: xr.DataArray):
self._da = da
self._da_selected = None
self.is_vector = False
Expand All @@ -610,6 +623,6 @@ def __init__(self, da: xr.DataArray = None):
class GeoDataFrameLeaflet(Leaflet):
"""A GeoDataFrame extension for tiled map plotting, based on (ipy)leaflet."""

def __init__(self, df: gpd.GeoDataFrame = None):
def __init__(self, df: gpd.GeoDataFrame):
self._df = df
self.is_vector = True

0 comments on commit 90148e7

Please sign in to comment.