Vector 2: Interactive plots
Contents
Vector 2: Interactive plots#
UW Geospatial Data Analysis
CEE467/CEWA567
David Shean
Part 5: Interactive plots#
GeoPandas explore#
Awesome new option for simple interactive plots using folium under the hood
https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.explore.html
Returns a folium map object
Mouse over points/polygons to see interactive tooltip popup with GeoDataFrame fields
#Example for WA GLAS points
#glas_gdf_aea_wa.explore()
Create an interactive map for your states geodataframe with explore
defaults#
#Student Exercise
Create an interactive map for your states geodataframe with colors representing GLAS point count#
#Student Exercise
Explore some more!#
Below is a helper function that creates a plot for an input geodatframe and the specified column
Try for the larger CONUS dataset
See how the stride values impact performance
Can pass in different
xyz
tiles as an argument
def plotcol(gdf, col='glas_z', stride=10, tiles=xyz.Esri.WorldImagery):
#Set colorbar limits to 2-98 percentile
clim = gdf[col].quantile((0.02, 0.98)).values
#Create interactive folium plot using GeoPandas explore function
m = gdf[::stride].explore(tiles=tiles, column=col, cmap='inferno', vmin=clim[0], vmax=clim[1])
#Return plot, required to display when calling this function
return m
#plotcol(glas_gdf_aea, col='decyear', stride=100)
Explore at least two other available tiled basemaps#
https://xyzservices.readthedocs.io/en/stable/introduction.html
Check out the NASAGIBS options - you can pass an specific date as
time
to get the MODIS basemap for that day!
#List available tiles
#xyz
#Student Exercise
#Student Exercise
ipyleaflet#
ipyleaflet is similar to folium, with some nice interactive “widget” support (useful if you need to update the map or retrieve user input from the map, like clicked points):
Note that leaflet uses tiled basemaps: https://en.wikipedia.org/wiki/Tiled_web_map
Default projection for these tiled basemaps is almost always Web Mercator (EPSG:3857): https://en.wikipedia.org/wiki/Web_Mercator_projection. This works well for lower latitudes, but not the polar regions. QGIS and contextily can reproject tiled basemaps on the fly, but looks like leaflet requires some workarounds (https://kartena.github.io/Proj4Leaflet/)
hvplot#
See: https://hvplot.holoviz.org/user_guide/Introduction.html
Based on
bokeh
backend, a nice alternative tomatplotlib
that is better suited for interactive visualizationPlugs in with
datashader
which is designed to render large point datasets efficientlyStill relatively new, with multiple parallel efforts
holoviews
,geoviews
,datashader
and some bugs, outdated or limited documentation, etc.
# Use the pandas hvplot interface
import hvplot.pandas
#Using `hvplot` instead of `plot` call on our GeoDataFrame
stride=10
#glas_gdf_aea[::stride].hvplot(aspect='equal')
Explore the above plot and tools. Try some interactive zooming, hover over points, etc.#
Add some map tiles#
geoviews
has some nice tile support
from geoviews import tile_sources as gvts
#map_tiles = gvts.StamenTerrain
map_tiles = gvts.EsriImagery
kw = {'width':500, 'height':400, 'hover':False, 'data_aspect':1, 'alpha':1.0}
kw['colorbar'] = True
kw['cmap'] = 'inferno'
Overlay points on map tiles (subset of points)#
To combine layers in a single plot using
holoviews
, you use the asterisk (*) to overlay the two objectsYou can use the plus sign (+) to build a layout with two separate subplots
Currently need to use the
geo=True
option and revert back to theglas_gdf
GeoDataFrame with lat/lon geometry, not our reprojected points inglas_gdf_aea
.In principle, hvplot/geoviews should work with our projected points, but there are some residual issues, and it appears that some underlying code somewhere in the stack is assuming lat/lon
#map_tiles * glas_gdf[::stride].hvplot(geo=True, c='glas_z', title='GLAS Elevation', **kw)
Using DataShader to efficiently render points on the fly#
There are still some limitations and bugs in the current
datashader
support for GeoDataFrame objects, so we will revert toglas_df
and specify x and y columns hereZoom in and see how points are re-rendered for each view
#glas_df.hvplot.scatter(x='lon', y='lat', datashade=True, c='glas_z', title='GLAS Elevation', **kw)
folium#
We haven’t discussed as a class yet, but this is a simple, effective interactive visualization package (alternative to matplotlib)
See the example here: https://python-visualization.github.io/folium/quickstart.html
For your plot:
Isolate points to WA state for now, convert to EPSG:4326
Compute the centroid of the WA state polygon (remember GeoPandas
unary_union
) in EPSG:4326Create a map object centered on this centroid
Use the ‘Stamen Terrain’ basemap layer
Experiment with
zoom_start
level to find a good extent
Export your WSG84 GeoDataFrame using
to_json()
, then load all features usingfolium.features.GeoJson
Add the points to the map
Take a moment to explore this interactive map interface.
#Compute WA state centroid in WGS84
#wa_center_wgs84 = list(wa_gdf.to_crs('EPSG:4326').centroid.iloc[0].coords)[0][::-1]
wgs_c = wa_gdf.to_crs('EPSG:4326').unary_union.centroid
wa_center_wgs84 = (wgs_c.y, wgs_c.x)
wa_center_wgs84
#Reproject to WGS84
glas_gdf_wa = glas_gdf_aea_wa.to_crs('EPSG:4326')
#Note: some students reported issues using folium on Chrome browser with many points (n=5265)
#This is one workaround: https://github.com/python-visualization/folium/issues/812#issuecomment-437483792
#Alternatively, reduce number of points, using only every 10th point
#stride=10
stride=1
#import folium
#m = folium.Map(location=wa_center_wgs84, zoom_start=7, tiles='Stamen Terrain')
#folium.features.GeoJson(glas_gdf_wa[::stride].to_json()).add_to(m)
#m
OK, but performance isn’t great with so many points#
Use a
MarkerCluster
object here to cluster nearby points at each zoom levelThis example is likely useful: https://ocefpaf.github.io/python4oceanographers/blog/2015/12/14/geopandas_folium/
from folium.plugins import MarkerCluster
#m = folium.Map(location=wa_center_wgs84, tiles='Stamen Terrain', zoom_start=7)
#Create clustered map with popups
#locations, popups = [], []
#for idx,row in glas_gdf_wa.iterrows():
# locations.append([row['geometry'].y, row['geometry'].x])
# popups.append(idx)
#t = folium.FeatureGroup(name='GLAS')
#t.add_child(MarkerCluster(locations=locations, popups=popups))
#m.add_child(t)
#m