<jupyter_start><jupyter_text>Data prep and symbology - Xarray and GeoViewsYou can download the source GeoTIFFs for this notebook and a QGIS style file [here](https://gregorhd.github.io/assets/img/uploads/2021-12-02-nakuru-pt3/03_final.rar).As usual, we start with all the necessary imports.<jupyter_code>import glob
import xarray as xr
import pandas as pd
from cartopy import crs as ccrs
import hvplot.xarray
import geoviews as gv

# give our plot a nice dark theme courtesy of Bokeh
gv.renderer('bokeh').theme = 'dark_minimal'<jupyter_output><empty_output><jupyter_text>We then need to create an [Xarray](https://xarray.pydata.org/en/stable/) `Dataset` from our three GeoTIFF files and add in some additional metadata (kudos to [Digital Earth Australia](https://docs.dea.ga.gov.au/notebooks/Frequently_used_code/Opening_GeoTIFFs_NetCDFs.html) for a great tutorial on this).<jupyter_code># Create list of file paths for all final GeoTIFFs in our folder
tif_list = glob.glob('03_final/*.tif')

# Create a 'time' coordinate along which to later display each DataArray
time_var = xr.Variable('time', pd.to_datetime(['2002-09-13', '2011-09-14', '2020-10-08']))

# Load and concatenate all individual GeoTIFFs
da = xr.concat([xr.open_rasterio(i) for i in tif_list], dim=time_var)

# Convert to dataset
ds = da.to_dataset('band')

# rename the dataset's first (and only) data variable
ds = ds.rename({1: 'landcover'})

# This resets my no data values from 255 to nan
ds = ds.where(ds['landcover'] != 255)<jupyter_output><empty_output><jupyter_text>The approach to symbology and legend creation is similar to what we used in the *datashader* example [here](https://gregorhd.github.io/osm2bokehserver-pt1/preparing-the-script). We simply need to make sure that the order of information classes in the `color_key` `dict` matches the order of integer values in our data.<jupyter_code># define the labels and corresponding colors
color_key = {'0 - Urban': '#000000', 
             '1 - Non-urban': '#FFFF00',
             '2 - Dense vegetation': '#00CC00',
             '3 - Water': '#009EE0',
             '4 - Clouds': '#FFFFFF',
             '5 - Shadows': '#FF0000'
            }

legend = gv.NdOverlay(
    {k: gv.Points([0,0], label=str(k)).opts(color=v, apply_ranges=False) for k, v in color_key.items()},
    'Landcover').opts(legend_position='top'
    )<jupyter_output><empty_output><jupyter_text>Final output - hvPlotThe magic of *hvPlot* is its superbly succinct syntax. After defining our plot's look and features through keyword arguments all contained in a single function call, we simply *multiply* the individual *HoloViews* objects we defined to create an [Overlay](http://holoviews.org/reference/containers/bokeh/Overlay.html), in this case consisting of the plot itself and the legend - and we're done!By setting the `groupby=` keyword argument, *hvPlot* is able to automatically add a slider widget allowing us to change between the `Dataset`'s three `DataArrays` along the 'time' dimension (depending on the size of your data, there may be a slight delay of a few seconds before the plot updates in a live notebook).<jupyter_code>plot = ds.landcover.hvplot(groupby='time', frame_width=600, data_aspect=1, 
                           yaxis=None, xaxis=None, 
                           colorbar=False, cmap=[v for v in color_key.values()],
                           
                           # defining the data's CRS is only really necessary, 
                           # if you want to add a tile layer underneath
                           crs=ccrs.UTM(36), tiles=True
                          )

# We can set all of Bokeh's figure settings as part of the '.opts()' method
overlay = (plot * legend).opts(active_tools=['wheel_zoom'])

overlay<jupyter_output><empty_output><jupyter_text>Not a perfect classification by any means (my heart goes out to all those urban areas *disappearing* between 2002 and 2011!), but good enough for demonstrating what's possible with these powerful and freely available tools.Finally, if we want, we can save our plot to an HTML file which, in this case, runs to around 100Mb, a good indication that making use of hvPlot's [built-in *datashader* support](https://hvplot.holoviz.org/user_guide/Customization.htmldatashading-options) is worth considering for wider distribution.<jupyter_code>hvplot.save(overlay,'nakuru-pt3.html')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09938733832539143, 'fraction_numerical': 0.02087587928295893, 'mean_word_length': 3.8869179600886916, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '266077', 'license': 'MIT', 'n_tokens_mistral': 1419, 'n_tokens_neox': 1319, 'n_words': 469, 'path': 'assets/img/uploads/2021-12-02-nakuru-pt3/tif2hvPlot.ipynb', 'repo_name': 'gregorhd/gregorhd.github.io', 'size': '4407'}