{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# General workflow demonstration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup and configuration" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# # If you have JupyterLab, you will need to install the JupyterLab extension:\n", "# # !jupyter labextension install @jupyter-widgets/jupyterlab-manager jupyter-leaflet\n", "\n", "# On a normal notebook server, the extension needs only to be enabled\n", "# !jupyter nbextension enable --py --sys-prefix ipyleaflet" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "os.environ[\"USE_PYGEOS\"] = \"0\" # force use Shapely with GeoPandas\n", "\n", "import ipyleaflet\n", "from birdy import WPSClient\n", "from siphon.catalog import TDSCatalog\n", "\n", "# we will use these coordinates later to center the map on Canada\n", "canada_center_lat_lon = (52.4292, -93.2959)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PAVICS URL configuration" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Ouranos production server\n", "pavics_url = \"https://pavics.ouranos.ca\"\n", "\n", "finch_url = os.getenv(\"WPS_URL\")\n", "if not finch_url:\n", " finch_url = f\"{pavics_url}/twitcher/ows/proxy/finch/wps\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting user input from a map widget" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Notes about ipyleaflet\n", "\n", "ipyleaflet is a **\"A Jupyter / Leaflet bridge enabling interactive maps in the Jupyter notebook\"**\n", "\n", "This means that the interactions with graphical objects on the map and in python are synchronized. The documentation is at: https://ipyleaflet.readthedocs.io" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "381879fe6f384658bace24b116549b65", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Map(center=[52.4292, -93.2959], controls=(ZoomControl(options=['position', 'zoom_in_text', 'zoom_in_title', 'z…" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "leaflet_map = ipyleaflet.Map(\n", " center=canada_center_lat_lon,\n", " basemap=ipyleaflet.basemaps.Stamen.Terrain,\n", " zoom=4,\n", ")\n", "\n", "initial_marker_location = canada_center_lat_lon\n", "marker = ipyleaflet.Marker(location=initial_marker_location, draggable=True)\n", "leaflet_map.add_layer(marker)\n", "\n", "leaflet_map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you move the marker on the map, it will update the `marker.location` variable. Also, if you update `marker.location` manually (`marker.location = (45.44, -90.44)`) it will also move on the map." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "marker.location = (45.55, -72.44)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(52.4292, -93.2959)\n", "[45.55, -72.44]\n" ] } ], "source": [ "print(initial_marker_location)\n", "print(marker.location)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Helper function to get user input" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "import ipyleaflet\n", "import ipywidgets as widgets\n", "from IPython.display import display\n", "\n", "\n", "def get_rectangle():\n", " canada_center = (52.4292, -93.2959)\n", " m = ipyleaflet.Map(\n", " center=canada_center,\n", " basemap=ipyleaflet.basemaps.Stamen.Terrain,\n", " zoom=4,\n", " )\n", "\n", " # Create a new draw control\n", " draw_control = ipyleaflet.DrawControl()\n", "\n", " # disable some drawing inputs\n", " draw_control.polyline = {}\n", " draw_control.circlemarker = {}\n", " draw_control.polygon = {}\n", "\n", " draw_control.rectangle = {\n", " \"shapeOptions\": {\n", " \"fillColor\": \"#4ae\",\n", " \"color\": \"#4ae\",\n", " \"fillOpacity\": 0.3,\n", " }\n", " }\n", "\n", " output = widgets.Output(layout={\"border\": \"1px solid black\"})\n", "\n", " rectangle = {}\n", "\n", " # set drawing callback\n", " def callback(control, action, geo_json):\n", " if action == \"created\":\n", " # note: we can't close the map or remove it from the output\n", " # from this callback. The map keeps the focus, and the\n", " # jupyter keyboard input is messed up.\n", " # So we set it very thin to make it disappear :)\n", " m.layout = {\"max_height\": \"0\"}\n", " with output:\n", " print(\"*User selected 1 rectangle*\")\n", " rectangle.update(geo_json)\n", "\n", " draw_control.on_draw(callback)\n", "\n", " m.add_control(draw_control)\n", "\n", " with output:\n", " print(\"Select a rectangle:\")\n", " display(m)\n", "\n", " display(output)\n", "\n", " return rectangle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The user wants to select a rectangle on a map, and get a GeoJSON back" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3d7f0c9f2cfe41e487c077faa095b6da", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Output(layout=Layout(border_bottom='1px solid black', border_left='1px solid black', border_right='1px solid b…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "\n", "\n", "rectangle = get_rectangle()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'type': 'Feature',\n", " 'properties': {'style': {'stroke': True,\n", " 'color': '#4ae',\n", " 'weight': 4,\n", " 'opacity': 0.5,\n", " 'fill': True,\n", " 'fillColor': '#4ae',\n", " 'fillOpacity': 0.3,\n", " 'clickable': True}},\n", " 'geometry': {'type': 'Polygon',\n", " 'coordinates': [[[-74.511948, 45.202296],\n", " [-74.511948, 45.934852],\n", " [-72.978537, 45.934852],\n", " [-72.978537, 45.202296],\n", " [-74.511948, 45.202296]]]}}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# GeoJSON with custom style properties\n", "if not len(rectangle):\n", " # Use the default region of Greater Montreal Area\n", " rectangle = {\n", " \"type\": \"Feature\",\n", " \"properties\": {\n", " \"style\": {\n", " \"stroke\": True,\n", " \"color\": \"#4ae\",\n", " \"weight\": 4,\n", " \"opacity\": 0.5,\n", " \"fill\": True,\n", " \"fillColor\": \"#4ae\",\n", " \"fillOpacity\": 0.3,\n", " \"clickable\": True,\n", " }\n", " },\n", " \"geometry\": {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [\n", " [\n", " [-74.511948, 45.202296],\n", " [-74.511948, 45.934852],\n", " [-72.978537, 45.934852],\n", " [-72.978537, 45.202296],\n", " [-74.511948, 45.202296],\n", " ]\n", " ],\n", " },\n", " }\n", "rectangle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Get the maximum and minimum bounds" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | minx | \n", "miny | \n", "maxx | \n", "maxy | \n", "
---|---|---|---|---|
0 | \n", "-74.511948 | \n", "45.202296 | \n", "-72.978537 | \n", "45.934852 | \n", "
<xarray.Dataset>\n", "Dimensions: (lat: 510, lon: 1068, time: 24837)\n", "Coordinates:\n", " * lat (lat) float32 83.46 83.38 83.29 83.21 ... 41.29 41.21 41.12 41.04\n", " * lon (lon) float32 -141.0 -140.9 -140.8 -140.7 ... -52.21 -52.13 -52.04\n", " * time (time) datetime64[ns] 1950-01-01 1950-01-02 ... 2017-12-31\n", "Data variables:\n", " tasmin (time, lat, lon) float32 dask.array<chunksize=(3375, 69, 144), meta=np.ndarray>\n", " tasmax (time, lat, lon) float32 dask.array<chunksize=(3375, 69, 144), meta=np.ndarray>\n", " pr (time, lat, lon) float32 dask.array<chunksize=(3375, 69, 144), meta=np.ndarray>\n", "Attributes: (12/15)\n", " Conventions: CF-1.5\n", " title: NRCAN ANUSPLIN daily gridded dataset : version 2\n", " history: Fri Jan 25 14:11:15 2019 : Convert from original fo...\n", " institute_id: NRCAN\n", " frequency: day\n", " abstract: Gridded daily observational dataset produced by Nat...\n", " ... ...\n", " dataset_id: NRCAN_anusplin_daily_v2\n", " version: 2.0\n", " license_type: permissive\n", " license: https://open.canada.ca/en/open-government-licence-c...\n", " attribution: The authors provide this data under the Environment...\n", " citation: Natural Resources Canada ANUSPLIN interpolated hist...