{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "18fc356a-e860-41a5-a2b1-b145b662964f",
   "metadata": {},
   "source": [
    "# An OGC API - DGGS visualization client built with DGGAL\n",
    "This notebook illustrates the use of the [Discrete Global Grid Abstraction Library (DGGAL)](https://dggal.org) to build a basic data visualization client for [OGC API - Discrete Global Grid Systems (DGGS)](https://docs.ogc.org/DRAFTS/21-038r1.html).\n",
    "# Source: [Ecere DGGAL](https://github.com/ecere/dggal/blob/eC-core/tutorial/dggs-client.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29c89ca1-c36b-4549-98af-d0d7048a8694",
   "metadata": {},
   "source": [
    "## Imports and utility functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "41ff3c3a-fbcb-413d-b7f8-70b1b79a34fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import urllib\n",
    "from IPython.display import JSON\n",
    "from io import BytesIO\n",
    "import rasterio\n",
    "from rasterio.plot import show\n",
    "import matplotlib.colors as colors\n",
    "import matplotlib.pyplot as plt\n",
    "from shapely.geometry import shape\n",
    "import numpy as np\n",
    "\n",
    "from dggal import *\n",
    "\n",
    "from ipyleaflet import Map, basemaps, GeoJSON, Popup\n",
    "import ipywidgets as widgets\n",
    "\n",
    "app = Application(appGlobals=globals())\n",
    "pydggal_setup(app)\n",
    "\n",
    "\n",
    "def get_call(url, resource):\n",
    "    call = urllib.parse.urljoin(url, resource)\n",
    "    # print(f\"endpoint: {call}\")\n",
    "    return requests.get(call)\n",
    "\n",
    "\n",
    "def post(url, resource, workflow):\n",
    "    path = urllib.parse.urljoin(url, resource)\n",
    "    return requests.post(path, json=workflow)\n",
    "\n",
    "\n",
    "def findLink(links, rel, lType):\n",
    "    link = \"\"\n",
    "    for key in links:\n",
    "        if \"type\" in key:\n",
    "            if lType in key[\"type\"] and rel in key[\"rel\"]:\n",
    "                link = key[\"href\"]\n",
    "                break\n",
    "    return link\n",
    "\n",
    "\n",
    "def findCollection(collections, colID):\n",
    "    for key in collections:\n",
    "        if colID in key[\"id\"]:\n",
    "            return key\n",
    "\n",
    "\n",
    "def displayRGBCoverage(cov, bgcolor, title, axesNames=[\"Longitude\", \"Latitude\"]):\n",
    "    fig2, ax2 = plt.subplots(figsize=(7, 7))\n",
    "    rio = rasterio.open(BytesIO(cov))\n",
    "    rioCov = rio.read()\n",
    "    # TODO: Figure out how to set to bgcolor\n",
    "    # bgArray = np(colors.to_rgb(bgcolor)) / 255.0\n",
    "    rioCov = np.where(rioCov == -32767, 0.9, np.clip(rioCov / 3500.0, 0.0, 1.0))\n",
    "    image_hidden2 = ax2.imshow(\n",
    "        rioCov.transpose(1, 2, 0), visible=False\n",
    "    )  # ,cmap=cmap,vmin=vmin,vmax=vmax, visible=False)\n",
    "    extent = image_hidden2.get_extent()\n",
    "    aspect = abs((extent[1] - extent[0]) / (extent[3] - extent[2]))\n",
    "    ax2.set_xlabel(axesNames[0])\n",
    "    ax2.set_ylabel(axesNames[1])\n",
    "    ax2.set_title(title)\n",
    "    show(\n",
    "        rioCov, ax=ax2, transform=rio.transform, interpolation=\"bilinear\", aspect=aspect\n",
    "    )\n",
    "\n",
    "\n",
    "def displayGraduatedCoverage(\n",
    "    cov, lvls, clrs, bgcolor, title, axesNames=[\"Longitude\", \"Latitude\"]\n",
    "):\n",
    "    vmin = min(lvls)\n",
    "    vmax = max(lvls)\n",
    "    norm = colors.Normalize(vmin=vmin, vmax=vmax)\n",
    "    normed_vals = norm(lvls)\n",
    "    cmap = colors.LinearSegmentedColormap.from_list(\n",
    "        \"colormap\", list(zip(normed_vals, clrs)), N=1000\n",
    "    )\n",
    "    cmap.set_under(bgcolor)\n",
    "    fig2, ax2 = plt.subplots(figsize=(7, 7))\n",
    "    rio = rasterio.open(BytesIO(cov))\n",
    "    rioCov = rio.read(1)\n",
    "    image_hidden2 = ax2.imshow(rioCov, cmap=cmap, vmin=vmin, vmax=vmax, visible=False)\n",
    "    fig2.colorbar(image_hidden2, ax=ax2, shrink=0.4)\n",
    "    extent = image_hidden2.get_extent()\n",
    "    aspect = abs((extent[1] - extent[0]) / (extent[3] - extent[2]))\n",
    "    ax2.set_xlabel(axesNames[0])\n",
    "    ax2.set_ylabel(axesNames[1])\n",
    "    ax2.set_title(title)\n",
    "    show(\n",
    "        rioCov,\n",
    "        ax=ax2,\n",
    "        cmap=cmap,\n",
    "        transform=rio.transform,\n",
    "        norm=norm,\n",
    "        interpolation=\"bilinear\",\n",
    "        aspect=aspect,\n",
    "    )\n",
    "\n",
    "\n",
    "def calcCentroid(polygonCoordinates):\n",
    "    polygon = shape({\"type\": \"Polygon\", \"coordinates\": polygonCoordinates})\n",
    "    centroid = polygon.centroid\n",
    "    return centroid.y, centroid.x\n",
    "\n",
    "\n",
    "def setupLeafletMap(center, zoom):\n",
    "    layout = widgets.Layout(height=\"600px\")\n",
    "    return Map(\n",
    "        center=[center.y, center.x],\n",
    "        scroll_wheel_zoom=True,\n",
    "        zoom=zoom,\n",
    "        interpolation=\"bilinear\",\n",
    "        layout=layout,\n",
    "        basemap=basemaps.Esri.WorldImagery,\n",
    "    )\n",
    "\n",
    "\n",
    "def popup_zone_info(map, feature):\n",
    "    centroid = calcCentroid(feature[\"geometry\"][\"coordinates\"])\n",
    "\n",
    "    props = feature[\"properties\"]\n",
    "    zoneID = props[\"zoneID\"]\n",
    "    popupText = f\"<h3>Zone {zoneID}</h3><table><ul>\"\n",
    "    for key, value in props.items():\n",
    "        if key != \"style\" and key != \"feature::id\" and key != \"zoneID\":\n",
    "            popupText = (\n",
    "                popupText\n",
    "                + f\"<tr><td align='right'><b>{key}</b></td><td align='right'><em> {value}</em></td></tr>\"\n",
    "            )\n",
    "    popupText = popupText + \"</ul>\"\n",
    "    popup = Popup(location=centroid, child=widgets.HTML(popupText), auto_close=True)\n",
    "    map.add(popup)  # add_layer\n",
    "\n",
    "\n",
    "def build_style(color, opacity, fillOpacity, weight=1.0):\n",
    "    return {\n",
    "        \"fillColor\": colors.rgb2hex(color),\n",
    "        \"fillOpacity\": fillOpacity,\n",
    "        \"opacity\": opacity,\n",
    "        \"color\": \"#FFFFFF\",\n",
    "        \"weight\": weight,\n",
    "    }\n",
    "\n",
    "\n",
    "def color_style(feature, cmap):\n",
    "    try:\n",
    "        v = feature[\"properties\"][prop]\n",
    "        color = cmap((v - vmin) / dv)\n",
    "        opacity = 1.0\n",
    "        fillOpacity = 0.85\n",
    "    except Exception:\n",
    "        color = (0, 0, 0)\n",
    "        fillOpacity = 0.0\n",
    "        opacity = 0.0\n",
    "    return build_style(color, opacity, fillOpacity)\n",
    "\n",
    "\n",
    "def s2RGB_style(feature, rgbBands):\n",
    "    try:\n",
    "        r = feature[\"properties\"][rgbBands[0]]\n",
    "        g = feature[\"properties\"][rgbBands[1]]\n",
    "        b = feature[\"properties\"][rgbBands[2]]\n",
    "\n",
    "        r = max(0.0, min(1.0, r / 3500))\n",
    "        g = max(0.0, min(1.0, g / 3500))\n",
    "        b = max(0.0, min(1.0, b / 3500))\n",
    "        color = (r, g, b)\n",
    "        opacity = 0.0  # 1.0\n",
    "        fillOpacity = 1.0  # 0.85\n",
    "    except Exception:\n",
    "        color = (0, 0, 0)\n",
    "        fillOpacity = 0.0\n",
    "        opacity = 0.0\n",
    "    return build_style(color, opacity, fillOpacity, 0.5)\n",
    "\n",
    "\n",
    "def displayGraduatedFeatures(map, features, prop, lvls, clrs):\n",
    "    vmin = min(lvls)\n",
    "    vmax = max(lvls)\n",
    "    dv = vmax - vmin\n",
    "    norm = colors.Normalize(vmin=vmin, vmax=vmax)\n",
    "    normed_vals = norm(lvls)\n",
    "    cmap = colors.LinearSegmentedColormap.from_list(\n",
    "        \"colormap\", list(zip(normed_vals, clrs)), N=1000\n",
    "    )\n",
    "\n",
    "    def zoneStyle(feature):\n",
    "        return color_style(feature, cmap)\n",
    "\n",
    "    gjLayer = GeoJSON(data=features, style_callback=zoneStyle)\n",
    "    map.add(gjLayer)  # add_layer\n",
    "\n",
    "    def on_click_handler(event, feature, **kwargs):\n",
    "        popup_zone_info(map, feature)\n",
    "\n",
    "    gjLayer.on_click(on_click_handler)\n",
    "    return map\n",
    "\n",
    "\n",
    "def displayS2RGBFeatures(map, features, rgbBands):\n",
    "    def zoneStyle(feature):\n",
    "        return s2RGB_style(feature, rgbBands)\n",
    "\n",
    "    gjLayer = GeoJSON(data=features, style_callback=zoneStyle)\n",
    "\n",
    "    def on_click_handler(event, feature, **kwargs):\n",
    "        popup_zone_info(map, feature)\n",
    "\n",
    "    gjLayer.on_click(on_click_handler)\n",
    "    map.add(gjLayer)\n",
    "    return map\n",
    "\n",
    "\n",
    "tiff = \"image/tiff; application=geotiff\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc4d0f7a-df04-4da1-a364-f844d66ee7f3",
   "metadata": {},
   "source": [
    "## Configuration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d141496b-ef2d-47da-b9fe-fd9231becdc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "gnosisDemo = \"https://maps.gnosis.earth/ogcapi/\"  # no auth necessary\n",
    "# gnosisDemo = 'http://localhost:8080/ogcapi/' # no auth necessary\n",
    "\n",
    "sloveniaAOI = {\"Lat\": [45.4236367, 46.8639623], \"Lon\": [13.3652612, 16.5153015]}\n",
    "\n",
    "bkColor = \"#ffe9cc\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af40ee64-9a55-4372-b0bf-afdc98d9d93c",
   "metadata": {},
   "source": [
    "## OGC API - Common (discovery, description of capabilities and data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85281105-49fb-44e5-ad1b-5e74188d6a86",
   "metadata": {},
   "source": [
    "### Landing page `/`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "bb7b20c4-a0c7-48ef-9877-2134e4e19583",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "links": [
        {
         "href": "/ogcapi?f=json",
         "rel": "self",
         "title": "The JSON representation of the landing page for this OGC (geospatial) API Service providing links to the API definition, the conformance declaration and information about the data collections offered at this endpoint.",
         "type": "application/json"
        },
        {
         "href": "/ogcapi?f=econ",
         "rel": "alternate",
         "title": "The ECON representation of the landing page for this OGC (geospatial) API Service providing links to the API definition, the conformance declaration and information about the data collections offered at this endpoint.",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi?f=html",
         "rel": "alternate",
         "title": "The HTML representation of the landing page for this OGC (geospatial) API Service providing links to the API definition, the conformance declaration and information about the data collections offered at this endpoint.",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/api?f=json",
         "rel": "service-desc",
         "title": "The JSON OpenAPI 3.0 document that describes the API offered at this endpoint",
         "type": "application/vnd.oai.openapi+json;version=3.0"
        },
        {
         "href": "/ogcapi/api?f=html",
         "rel": "service-doc",
         "title": "The HTML documentation of the API offered at this endpoint",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/conformance",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/conformance",
         "title": "The JSON representation of the conformance declaration for this server listing the requirement classes implemented by this server",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/conformance",
         "rel": "conformance",
         "title": "The JSON representation of the conformance declaration for this server listing the requirement classes implemented by this server",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections?f=json",
         "rel": "data",
         "title": "The JSON representation of the list of all data collections served from this endpoint",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections?f=econ",
         "rel": "data",
         "title": "The ECON representation of the list of all data collections served from this endpoint",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections?f=html",
         "rel": "data",
         "title": "The HTML representation of the list of all data collections served from this endpoint",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/processes",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/processes",
         "title": "The JSON representation of the list of all processes available from this endpoint",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/routes",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/routes",
         "title": "Routing end-point",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/tiles",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-vector",
         "title": "Tiles end-point",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/map/tiles",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map Tiles end-point",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/map",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Map end-point",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/tileMatrixSets?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
         "title": "The list of supported tiling schemes (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/tileMatrixSets?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
         "title": "The list of supported tiling schemes (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/tileMatrixSets?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tiling-schemes",
         "title": "The list of supported tiling schemes (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/dggs",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/dggrs-list",
         "title": "Discrete Global Grid Reference Systems"
        }
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "execution_count": 10,
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def requestLandingPage(serverURL):\n",
    "    return get_call(url=serverURL, resource=\"\").json()\n",
    "\n",
    "\n",
    "lp = requestLandingPage(gnosisDemo)\n",
    "JSON(lp)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acf2195b-c436-460d-bc56-992ec01e94c5",
   "metadata": {},
   "source": [
    "### Conformance declaration `/conformance`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fe7189d8-3666-4ccf-b6e3-f06c05990c05",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "conformsTo": [
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/landing-page",
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html",
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json",
        "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30",
        "http://www.opengis.net/spec/ogcapi-common-2/0.0/conf/collections",
        "http://www.opengis.net/spec/ogcapi-common-2/0.0/conf/umd-collection",
        "http://www.opengis.net/spec/ogcapi-common-3/0.0/conf/schemas",
        "http://www.opengis.net/spec/ogcapi-common-4/0.0/conf/hierarchical-collections",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson",
        "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30",
        "http://www.opengis.net/spec/ogcapi-features-2/1.0/conf/crs",
        "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/features-filter",
        "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/filter",
        "http://www.opengis.net/spec/ogcapi-features-3/1.0/conf/queryables",
        "http://www.opengis.net/spec/ogcapi-features-5/0.0/conf/schemas",
        "http://www.opengis.net/spec/ogcapi-features-5/0.0/conf/core-roles-features",
        "http://www.opengis.net/spec/json-fg-1/0.2/conf/core",
        "https://ecere.ca/specs/cmss/0.0/conf/cmss-filter",
        "http://www.opengis.net/spec/cql2/1.0/conf/basic-cql2",
        "http://www.opengis.net/spec/cql2/1.0/conf/advanced-comparison-operators",
        "http://www.opengis.net/spec/cql2/1.0/conf/arithmetic",
        "http://www.opengis.net/spec/cql2/1.0/conf/property-property",
        "http://www.opengis.net/spec/cql2/1.0/conf/case-insensitive-comparison",
        "http://www.opengis.net/spec/cql2/1.0/conf/accent-insensitive-comparison",
        "http://www.opengis.net/spec/cql2/1.0/conf/basic-spatial-functions",
        "http://www.opengis.net/spec/cql2/1.0/conf/basic-spatial-functions-plus",
        "http://www.opengis.net/spec/cql2/1.0/conf/spatial-functions",
        "http://www.opengis.net/spec/cql2/1.0/conf/temporal-functions",
        "http://www.opengis.net/spec/cql2/1.0/conf/array-functions",
        "http://www.opengis.net/spec/cql2/1.0/conf/cql2-text",
        "http://www.opengis.net/spec/cql2/1.0/conf/cql2-json",
        "http://www.opengis.net/spec/cql2/1.0/conf/functions",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/scaling",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/subsetting",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/fieldselection",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/crs",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/tiles",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/geotiff",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/png",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/oas30",
        "http://www.opengis.net/spec/ogcapi-coverages-2/0.0/conf/filtering",
        "http://www.opengis.net/spec/ogcapi-coverages-2/0.0/conf/derivedfields",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/geodata-coverage",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-subset",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-scaling",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-tiles",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-rangesubset",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-bbox",
        "http://www.opengis.net/spec/ogcapi-coverages-1/0.0/conf/coverage-datetime",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/collections-selection",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/datetime",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/oas30",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpg",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson",
        "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt",
        "http://www.opengis.net/spec/tms/2.0/conf/tilematrixset",
        "http://www.opengis.net/spec/tms/2.0/conf/variablematrixwidth",
        "http://www.opengis.net/spec/tms/2.0/conf/tilematrixsetlimits",
        "http://www.opengis.net/spec/tms/2.0/conf/tilesetmetadata",
        "http://www.opengis.net/spec/tms/2.0/conf/json-tilematrixset",
        "http://www.opengis.net/spec/tms/2.0/conf/json-variablematrixwidth",
        "http://www.opengis.net/spec/tms/2.0/conf/json-tilematrixsetlimits",
        "http://www.opengis.net/spec/tms/2.0/conf/json-tilesetmetadata",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/collection-map",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/background",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/styled-map",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/dataset-map",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/collections-selection",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/spatial-subsetting",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/datetime",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/general-subsetting",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/tilesets",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/scaling",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/display-resolution",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/crs",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/png",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/jpeg",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/tiff",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/api-operations",
        "http://www.opengis.net/spec/ogcapi-maps-1/1.0/conf/cors",
        "http://www.opengis.net/spec/ogcapi-maps-2/0.0/conf/filtering",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/resources",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/html",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/sld-10",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/mapbox-style",
        "https://ecere.ca/specs/cmss/0.0/conf/cmss-style",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/style-info",
        "http://www.opengis.net/spec/ogcapi-styles-1/0.0/conf/queryables",
        "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description",
        "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/json",
        "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/oas30",
        "http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-input",
        "http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-output",
        "http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-collections",
        "http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/nested-processes",
        "http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-core-processes",
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/mode",
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/intermediate-waypoints",
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/height",
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/weight",
        "http://www.opengis.net/spec/ogcapi-geovolumes-1/0.0/conf/3dtiles-bvh",
        "http://www.opengis.net/spec/ogcapi-geovolumes-1/0.0/conf/referenced-models",
        "http://www.opengis.net/spec/ogcapi-geovolumes-1/0.0/conf/refpoints-tilesets",
        "http://www.opengis.net/spec/ogcapi-geovolumes-1/0.0/conf/batched-models-tilesets",
        "http://www.opengis.net/spec/ogcapi-geovolumes-1/0.0/conf/gltf",
        "https://ecere.ca/specs/e3d/0.0/conf/e3d",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/core",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/root-dggs",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/collection-dggs",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-retrieval",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-custom-depths",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-subsetting",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-cql2-filter",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-json",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-png",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-geotiff",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/data-geojson",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-query",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-query-cql2-filter",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-json",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-geojson",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-tiff",
        "http://www.opengis.net/spec/ogcapi-dggs-1/0.0/conf/zone-uint64"
       ],
       "links": [
        {
         "href": "/ogcapi/conformance?f=json",
         "rel": "self",
         "title": "This document",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/conformance?f=html",
         "rel": "alternate",
         "title": "this document as HTML",
         "type": "text/html"
        }
       ],
       "properties": {
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/core": null,
        "http://www.opengis.net/spec/ogcapi-routes-1/0.0/conf/mode": null
       },
       "type": "object"
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "execution_count": 11,
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def requestConformance(serverURL):\n",
    "    return get_call(url=serverURL, resource=\"conformance\").json()\n",
    "\n",
    "\n",
    "conformance = requestConformance(gnosisDemo)\n",
    "JSON(conformance)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "597557cd-28fe-4170-8f73-ae3fb3197a71",
   "metadata": {},
   "source": [
    "### Data `/collections`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "c464d349-3eab-4d79-90a2-cbae22c0d20c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "attribution": "<a target='_blank' href='https://sentinel.esa.int/web/sentinel/missions/sentinel-2'>sentinel-2 (ESA)</a> / <a target='_blank' href='https://registry.opendata.aws/sentinel-2-l2a-cogs/'>COGs by Element84 @ aws</a>",
       "crs": [
        "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
        "http://www.opengis.net/def/crs/EPSG/0/4326",
        "http://www.opengis.net/def/crs/EPSG/0/3857",
        "http://www.opengis.net/def/crs/EPSG/0/3395"
       ],
       "dataType": "coverage",
       "extent": {
        "spatial": {
         "bbox": [
          [
           -180,
           -90,
           180,
           90
          ]
         ],
         "grid": [
          {
           "cellsCount": 4194304,
           "resolution": 0.00008583068847656
          },
          {
           "cellsCount": 2097152,
           "resolution": 0.00008583068847656
          }
         ]
        },
        "temporal": {
         "grid": {
          "cellsCount": 188938050,
          "resolution": "PT1S"
         },
         "interval": [
          [
           "2016-11-01T08:51:12Z",
           "2022-10-28T03:38:41Z"
          ]
         ]
        }
       },
       "id": "sentinel2-l2a",
       "links": [
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=json",
         "rel": "self",
         "title": "Information about the sentinel2-l2a data (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=econ",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=mapml",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as MapML)",
         "type": "text/mapml"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=html",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/dggs",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/dggrs-list",
         "title": "Discrete Global Grid Reference Systems for sentinel2-l2a"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage?f=png",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage",
         "title": "sentinel2-l2a (as PNG; Note: requesting large extent may result in generalized data)",
         "type": "image/png"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage?f=tif",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage",
         "title": "sentinel2-l2a (as GeoTIFF; Note: requesting large extent may result in generalized data)",
         "type": "image/tiff; application=geotiff"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage/domainset?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage-domainset",
         "title": "sentinel2-l2a (domain set of the coverage for this collection)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage/rangetype?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage-rangetype",
         "title": "sentinel2-l2a (range type of the coverage for this collection)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.png",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as PNG)",
         "type": "image/png"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.jpg",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as JPG)",
         "type": "image/jpeg"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.tif",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as GeoTIFF)",
         "type": "image/tif"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=html",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=json",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=econ",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as ECON)",
         "type": "text/plain"
        }
       ],
       "minCellSize": 0.00008583068847656,
       "minScaleDenominator": 34123.6733415965,
       "storageCrs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
       "title": "sentinel2-l2a"
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "execution_count": 12,
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def requestCollections(serverURL):\n",
    "    return get_call(url=serverURL, resource=\"collections\").json()\n",
    "\n",
    "\n",
    "cols = requestCollections(gnosisDemo)\n",
    "s2Id = \"sentinel2-l2a\"\n",
    "col = findCollection(cols[\"collections\"], s2Id)\n",
    "\n",
    "# JSON(cols)\n",
    "JSON(col)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37d2d55b-a2e1-41bb-b277-4af4fa1bec9a",
   "metadata": {},
   "source": [
    "### Collection description `/collections/{collectionId}`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "523b0a3e-b876-4b16-be05-2e341db8e0a4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "attribution": "<a target='_blank' href='https://sentinel.esa.int/web/sentinel/missions/sentinel-2'>sentinel-2 (ESA)</a> / <a target='_blank' href='https://registry.opendata.aws/sentinel-2-l2a-cogs/'>COGs by Element84 @ aws</a>",
       "crs": [
        "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
        "http://www.opengis.net/def/crs/EPSG/0/4326",
        "http://www.opengis.net/def/crs/EPSG/0/3857",
        "http://www.opengis.net/def/crs/EPSG/0/3395"
       ],
       "dataType": "coverage",
       "extent": {
        "spatial": {
         "bbox": [
          [
           -180,
           -90,
           180,
           90
          ]
         ],
         "grid": [
          {
           "cellsCount": 4194304,
           "resolution": 0.00008583068847656
          },
          {
           "cellsCount": 2097152,
           "resolution": 0.00008583068847656
          }
         ]
        },
        "temporal": {
         "grid": {
          "cellsCount": 188938050,
          "resolution": "PT1S"
         },
         "interval": [
          [
           "2016-11-01T08:51:12Z",
           "2022-10-28T03:38:41Z"
          ]
         ]
        }
       },
       "id": "sentinel2-l2a",
       "links": [
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=json",
         "rel": "self",
         "title": "Information about the sentinel2-l2a data (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=econ",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=mapml",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as MapML)",
         "type": "text/mapml"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a?f=html",
         "rel": "alternate",
         "title": "Information about the sentinel2-l2a data (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/dggs",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/dggrs-list",
         "title": "Discrete Global Grid Reference Systems for sentinel2-l2a"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/schema?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/schema",
         "title": "Schema (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/queryables?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/queryables",
         "title": "Queryables (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage?f=png",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage",
         "title": "sentinel2-l2a (as PNG; Note: requesting large extent may result in generalized data)",
         "type": "image/png"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage?f=tif",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage",
         "title": "sentinel2-l2a (as GeoTIFF; Note: requesting large extent may result in generalized data)",
         "type": "image/tiff; application=geotiff"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage/domainset?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage-domainset",
         "title": "sentinel2-l2a (domain set of the coverage for this collection)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/coverage/rangetype?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/coverage-rangetype",
         "title": "sentinel2-l2a (range type of the coverage for this collection)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/tiles?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-coverage",
         "title": "Coverage tilesets available for this dataset (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.png",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as PNG)",
         "type": "image/png"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.jpg",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as JPG)",
         "type": "image/jpeg"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map.tif",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/map",
         "title": "Default map (as GeoTIFF)",
         "type": "image/tif"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=json",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=econ",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as ECON)",
         "type": "text/plain"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/map/tiles?f=html",
         "rel": "http://www.opengis.net/def/rel/ogc/1.0/tilesets-map",
         "title": "Map tilesets available for this dataset (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=html",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as HTML)",
         "type": "text/html"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=json",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as JSON)",
         "type": "application/json"
        },
        {
         "href": "/ogcapi/collections/sentinel2-l2a/styles?f=econ",
         "rel": "styles",
         "title": "Styles for sentinel2-l2a (as ECON)",
         "type": "text/plain"
        }
       ],
       "minCellSize": 0.00008583068847656,
       "minScaleDenominator": 34123.6733415965,
       "storageCrs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
       "title": "sentinel2-l2a"
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "execution_count": 13,
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def requestCollection(serverURL, id):\n",
    "    return get_call(url=serverURL, resource=f\"collections/{id}\").json()\n",
    "\n",
    "\n",
    "s2Col = requestCollection(gnosisDemo, s2Id)\n",
    "JSON(s2Col)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4033c99a-f204-4735-87ea-f2cff852be9c",
   "metadata": {},
   "source": [
    "### Logical Schema `/collections/{collectionId}/schema`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "8da4f094-fc35-4b1b-8497-2e24f4860c56",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "$id": "https://maps.gnosis.earth/ogcapi/collections/sentinel2-l2a/schema",
       "$schema": "https://json-schema.org/draft/2020-12/schema",
       "properties": {
        "AOT": {
         "title": "Aerosol Optical Thickness (AOT)",
         "type": "number",
         "x-ogc-propertySeq": 1
        },
        "B01": {
         "title": "Band 1 (coastal) - wavelength: 0.4169-0.4709 μm",
         "type": "number",
         "x-ogc-propertySeq": 2
        },
        "B02": {
         "title": "Band 2 (blue) - wavelength: 0.3986-0.5946 μm",
         "type": "number",
         "x-ogc-propertySeq": 3
        },
        "B03": {
         "title": "Band 3 (green) - wavelength: 0.515-0.605 μm",
         "type": "number",
         "x-ogc-propertySeq": 4
        },
        "B04": {
         "title": "Band 4 (red) - wavelength: 0.6265-0.7025 μm",
         "type": "number",
         "x-ogc-propertySeq": 5
        },
        "B05": {
         "title": "Band 5 - wavelength: 0.6849-0.7229 μm",
         "type": "number",
         "x-ogc-propertySeq": 6
        },
        "B06": {
         "title": "Band 6 - wavelength: 0.7222-0.7582 μm",
         "type": "number",
         "x-ogc-propertySeq": 7
        },
        "B07": {
         "title": "Band 7 - wavelength: 0.7545-0.8105 μm",
         "type": "number",
         "x-ogc-propertySeq": 8
        },
        "B08": {
         "title": "Band 8 (nir) - wavelength: 0.6901-0.9801 μm",
         "type": "number",
         "x-ogc-propertySeq": 9
        },
        "B09": {
         "title": "Band 9 - wavelength: 0.919-0.971 μm",
         "type": "number",
         "x-ogc-propertySeq": 10
        },
        "B11": {
         "title": "Band 11 (swir16) - wavelength: 1.4707-1.7567 μm",
         "type": "number",
         "x-ogc-propertySeq": 11
        },
        "B12": {
         "title": "Band 12 (swir22) - wavelength: 1.97824-2.46224 μm",
         "type": "number",
         "x-ogc-propertySeq": 12
        },
        "B8A": {
         "title": "Band 8A - wavelength: 0.8318-0.8978 μm",
         "type": "number",
         "x-ogc-propertySeq": 13
        },
        "SCL": {
         "title": "Scene Classification Map (SCL) - 0: No data; 1: Saturated / Defective; 2: Dark Area Pixels; 3: Cloud Shadows; 4: Vegetation; 5: Bare Soils; 6: Water; 7: Clouds low probability / Unclassified; 8: Clouds medium probability; 9: Clouds high probability; 10: Cirrus; 11: Snow / Ice",
         "type": "integer",
         "x-ogc-propertySeq": 14
        },
        "WVP": {
         "title": "Water Vapour (WVP)",
         "type": "number",
         "x-ogc-propertySeq": 15
        }
       },
       "title": "sentinel2-l2a",
       "type": "object"
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "execution_count": 14,
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def requestSchema(serverURL, collectionId):\n",
    "    return get_call(url=serverURL, resource=f\"collections/{collectionId}/schema\").json()\n",
    "\n",
    "\n",
    "s2Schema = requestSchema(gnosisDemo, s2Id)\n",
    "JSON(s2Schema)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c43618a-8b3d-4595-bf40-744784a5f8d4",
   "metadata": {},
   "source": [
    "## OGC API - DGGS Zone Data requests\n",
    "\n",
    "[OGC API - DGGS](https://docs.ogc.org/DRAFTS/21-038r1.html) allows to [retrieve data](https://docs.ogc.org/DRAFTS/21-038r1.html#rc_data-retrieval) for a particular DGGRS zone identifier.\n",
    "\n",
    "If [Custom Depths](https://docs.ogc.org/DRAFTS/21-038r1.html#rc_data-custom-depths) are supported, a `zone-depth` parameter can specify the depth at which data  be retrieved, which determines the number of values in the response.\n",
    "\n",
    "The examples below are for the [ISEA3H DGGRS](https://docs.ogc.org/DRAFTS/21-038r1.html#isea3h-dggrs), which defines a hierarchy of hexagonal grids with a refinement ratio of 3 on the [ISEA projection](https://proj.org/en/9.4/operations/projections/isea.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dae92c3-1f03-4017-b752-c7318f222328",
   "metadata": {},
   "source": [
    "### As GeoTIFF (relative depth 11):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "7d2bbef5-ec96-4638-9ea2-43cd8af88710",
   "metadata": {},
   "outputs": [],
   "source": [
    "def requestZoneData(\n",
    "    serverURL, collectionId, dggrs, zoneId, depth, time, crs, format, fields=None\n",
    "):\n",
    "    resource = f\"collections/{collectionId}/dggs/{dggrs}/zones/{zoneId}/data\"\n",
    "    query = f\"?f={format}&zone-depth={depth}&datetime={time}&crs={crs}\"\n",
    "    if fields is not None:\n",
    "        query = query + \"&properties=\" + fields\n",
    "    return get_call(serverURL, resource + query)\n",
    "\n",
    "\n",
    "# levels = [0.0, 20.0, 40.0, 60.0, 80.0, 100.0]\n",
    "# clrs = [\"#0B0405\", \"#382A54\", \"#414184\", \"#3497A9\", \"#62CFAC\", \"#DEF5E5\"]\n",
    "\n",
    "# zoneData = requestZoneData(\n",
    "#     gnosisDemo, s2Id, \"ISEA3H\", \"H4-A825C-A\", 11, \"2020-07\", \"[OGC:1534]\", tiff\n",
    "# ).content\n",
    "# displayRGBCoverage(\n",
    "#     zoneData,\n",
    "#     bkColor,\n",
    "#     \"sentinel-2 Level 2A Output (ISEA3H Zone H4-A825C-A @ depth=11)\",\n",
    "#     [\"X (ISEA)\", \"Y (ISEA)\"],\n",
    "# )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68e407b5-61e2-4035-aefc-719b7ded51b5",
   "metadata": {},
   "source": [
    "### As GeoJSON (relative depth 7):\n",
    "\n",
    "While inefficient, a [GeoJSON](https://docs.ogc.org/DRAFTS/21-038.html#rc_data-geojson) encoding of raster zone data including the geometry of sub-zones facilitates rendering these sub-zones in DGGS-unaware clients.\n",
    "\n",
    "With the [Jupyter lab leaflet extension](https://ipyleaflet.readthedocs.io/) installed, sub-zones can be clicked to see their identifiers and associated values for each band."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "dc392c27-23db-4e24-97c2-702fcb7bbd20",
   "metadata": {},
   "outputs": [
    {
     "ename": "ConnectionError",
     "evalue": "('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mRemoteDisconnected\u001b[39m                        Traceback (most recent call last)",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connectionpool.py:787\u001b[39m, in \u001b[36mHTTPConnectionPool.urlopen\u001b[39m\u001b[34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[39m\n\u001b[32m    786\u001b[39m \u001b[38;5;66;03m# Make the request on the HTTPConnection object\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m787\u001b[39m response = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_make_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    788\u001b[39m \u001b[43m    \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    789\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    790\u001b[39m \u001b[43m    \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    791\u001b[39m \u001b[43m    \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtimeout_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    792\u001b[39m \u001b[43m    \u001b[49m\u001b[43mbody\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    793\u001b[39m \u001b[43m    \u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m=\u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    794\u001b[39m \u001b[43m    \u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m=\u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    795\u001b[39m \u001b[43m    \u001b[49m\u001b[43mretries\u001b[49m\u001b[43m=\u001b[49m\u001b[43mretries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    796\u001b[39m \u001b[43m    \u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[43m=\u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    797\u001b[39m \u001b[43m    \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    798\u001b[39m \u001b[43m    \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    799\u001b[39m \u001b[43m    \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mresponse_kw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    800\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    802\u001b[39m \u001b[38;5;66;03m# Everything went great!\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connectionpool.py:534\u001b[39m, in \u001b[36mHTTPConnectionPool._make_request\u001b[39m\u001b[34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001b[39m\n\u001b[32m    533\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m534\u001b[39m     response = \u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    535\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (BaseSSLError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connection.py:565\u001b[39m, in \u001b[36mHTTPConnection.getresponse\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    564\u001b[39m \u001b[38;5;66;03m# Get the response from http.client.HTTPConnection\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m565\u001b[39m httplib_response = \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    567\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:1430\u001b[39m, in \u001b[36mHTTPConnection.getresponse\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m   1429\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1430\u001b[39m     \u001b[43mresponse\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbegin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m   1431\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mConnectionError\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:331\u001b[39m, in \u001b[36mHTTPResponse.begin\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    330\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m331\u001b[39m     version, status, reason = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_read_status\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    332\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m status != CONTINUE:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:300\u001b[39m, in \u001b[36mHTTPResponse._read_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    297\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m line:\n\u001b[32m    298\u001b[39m     \u001b[38;5;66;03m# Presumably, the server closed the connection before\u001b[39;00m\n\u001b[32m    299\u001b[39m     \u001b[38;5;66;03m# sending a valid response.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m300\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m RemoteDisconnected(\u001b[33m\"\u001b[39m\u001b[33mRemote end closed connection without\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    301\u001b[39m                              \u001b[33m\"\u001b[39m\u001b[33m response\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m    302\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "\u001b[31mRemoteDisconnected\u001b[39m: Remote end closed connection without response",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[31mProtocolError\u001b[39m                             Traceback (most recent call last)",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\adapters.py:667\u001b[39m, in \u001b[36mHTTPAdapter.send\u001b[39m\u001b[34m(self, request, stream, timeout, verify, cert, proxies)\u001b[39m\n\u001b[32m    666\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m667\u001b[39m     resp = \u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43murlopen\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    668\u001b[39m \u001b[43m        \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    669\u001b[39m \u001b[43m        \u001b[49m\u001b[43murl\u001b[49m\u001b[43m=\u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    670\u001b[39m \u001b[43m        \u001b[49m\u001b[43mbody\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    671\u001b[39m \u001b[43m        \u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m.\u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    672\u001b[39m \u001b[43m        \u001b[49m\u001b[43mredirect\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    673\u001b[39m \u001b[43m        \u001b[49m\u001b[43massert_same_host\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    674\u001b[39m \u001b[43m        \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    675\u001b[39m \u001b[43m        \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    676\u001b[39m \u001b[43m        \u001b[49m\u001b[43mretries\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmax_retries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    677\u001b[39m \u001b[43m        \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    678\u001b[39m \u001b[43m        \u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m=\u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    679\u001b[39m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    681\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (ProtocolError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m err:\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connectionpool.py:841\u001b[39m, in \u001b[36mHTTPConnectionPool.urlopen\u001b[39m\u001b[34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[39m\n\u001b[32m    839\u001b[39m     new_e = ProtocolError(\u001b[33m\"\u001b[39m\u001b[33mConnection aborted.\u001b[39m\u001b[33m\"\u001b[39m, new_e)\n\u001b[32m--> \u001b[39m\u001b[32m841\u001b[39m retries = \u001b[43mretries\u001b[49m\u001b[43m.\u001b[49m\u001b[43mincrement\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    842\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnew_e\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m_pool\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m_stacktrace\u001b[49m\u001b[43m=\u001b[49m\u001b[43msys\u001b[49m\u001b[43m.\u001b[49m\u001b[43mexc_info\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m2\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[32m    843\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    844\u001b[39m retries.sleep()\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\util\\retry.py:474\u001b[39m, in \u001b[36mRetry.increment\u001b[39m\u001b[34m(self, method, url, response, error, _pool, _stacktrace)\u001b[39m\n\u001b[32m    473\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m read \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m._is_method_retryable(method):\n\u001b[32m--> \u001b[39m\u001b[32m474\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[43mreraise\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mtype\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m_stacktrace\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    475\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m read \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\util\\util.py:38\u001b[39m, in \u001b[36mreraise\u001b[39m\u001b[34m(tp, value, tb)\u001b[39m\n\u001b[32m     37\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m value.__traceback__ \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m tb:\n\u001b[32m---> \u001b[39m\u001b[32m38\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m value.with_traceback(tb)\n\u001b[32m     39\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m value\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connectionpool.py:787\u001b[39m, in \u001b[36mHTTPConnectionPool.urlopen\u001b[39m\u001b[34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)\u001b[39m\n\u001b[32m    786\u001b[39m \u001b[38;5;66;03m# Make the request on the HTTPConnection object\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m787\u001b[39m response = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_make_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    788\u001b[39m \u001b[43m    \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    789\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    790\u001b[39m \u001b[43m    \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    791\u001b[39m \u001b[43m    \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtimeout_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    792\u001b[39m \u001b[43m    \u001b[49m\u001b[43mbody\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbody\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    793\u001b[39m \u001b[43m    \u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m=\u001b[49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    794\u001b[39m \u001b[43m    \u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m=\u001b[49m\u001b[43mchunked\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    795\u001b[39m \u001b[43m    \u001b[49m\u001b[43mretries\u001b[49m\u001b[43m=\u001b[49m\u001b[43mretries\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    796\u001b[39m \u001b[43m    \u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[43m=\u001b[49m\u001b[43mresponse_conn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    797\u001b[39m \u001b[43m    \u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpreload_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    798\u001b[39m \u001b[43m    \u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdecode_content\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    799\u001b[39m \u001b[43m    \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mresponse_kw\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    800\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    802\u001b[39m \u001b[38;5;66;03m# Everything went great!\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connectionpool.py:534\u001b[39m, in \u001b[36mHTTPConnectionPool._make_request\u001b[39m\u001b[34m(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)\u001b[39m\n\u001b[32m    533\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m534\u001b[39m     response = \u001b[43mconn\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    535\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (BaseSSLError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\urllib3\\connection.py:565\u001b[39m, in \u001b[36mHTTPConnection.getresponse\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    564\u001b[39m \u001b[38;5;66;03m# Get the response from http.client.HTTPConnection\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m565\u001b[39m httplib_response = \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mgetresponse\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    567\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:1430\u001b[39m, in \u001b[36mHTTPConnection.getresponse\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m   1429\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1430\u001b[39m     \u001b[43mresponse\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbegin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m   1431\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mConnectionError\u001b[39;00m:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:331\u001b[39m, in \u001b[36mHTTPResponse.begin\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    330\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m331\u001b[39m     version, status, reason = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_read_status\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    332\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m status != CONTINUE:\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\AppData\\Roaming\\uv\\python\\cpython-3.12.11-windows-x86_64-none\\Lib\\http\\client.py:300\u001b[39m, in \u001b[36mHTTPResponse._read_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m    297\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m line:\n\u001b[32m    298\u001b[39m     \u001b[38;5;66;03m# Presumably, the server closed the connection before\u001b[39;00m\n\u001b[32m    299\u001b[39m     \u001b[38;5;66;03m# sending a valid response.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m300\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m RemoteDisconnected(\u001b[33m\"\u001b[39m\u001b[33mRemote end closed connection without\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    301\u001b[39m                              \u001b[33m\"\u001b[39m\u001b[33m response\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m    302\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n",
      "\u001b[31mProtocolError\u001b[39m: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[31mConnectionError\u001b[39m                           Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 49\u001b[39m\n\u001b[32m     47\u001b[39m         printLn(\u001b[33m\"\u001b[39m\u001b[33mFetching \u001b[39m\u001b[33m\"\u001b[39m, zID, \u001b[33m\"\u001b[39m\u001b[33m (\u001b[39m\u001b[33m\"\u001b[39m, i, \u001b[33m\"\u001b[39m\u001b[33m / \u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28mlen\u001b[39m(zones), \u001b[33m\"\u001b[39m\u001b[33m)\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m     48\u001b[39m         i += \u001b[32m1\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m49\u001b[39m         geoJSONZoneData = \u001b[43mrequestZoneData\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m     50\u001b[39m \u001b[43m            \u001b[49m\u001b[43mgnosisDemo\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     51\u001b[39m \u001b[43m            \u001b[49m\u001b[43ms2Id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     52\u001b[39m \u001b[43m            \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mISEA3H\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m     53\u001b[39m \u001b[43m            \u001b[49m\u001b[43mzID\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     54\u001b[39m \u001b[43m            \u001b[49m\u001b[43mdepth\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     55\u001b[39m \u001b[43m            \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m2020-07\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m     56\u001b[39m \u001b[43m            \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43m[OGC:CRS84]\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m     57\u001b[39m \u001b[43m            \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mgeojson\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m     58\u001b[39m \u001b[43m            \u001b[49m\u001b[43mfields\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m     59\u001b[39m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m.json()\n\u001b[32m     60\u001b[39m         displayS2RGBFeatures(\u001b[38;5;28mmap\u001b[39m, geoJSONZoneData, rgbBands)\n\u001b[32m     62\u001b[39m \u001b[38;5;66;03m# This requests a single zone:\u001b[39;00m\n\u001b[32m     63\u001b[39m \u001b[38;5;66;03m# geoJSONZoneData = requestZoneData(gnosisDemo, s2Id, \"ISEA3H\", zoneId, depth, \"2020-07\", \"[OGC:CRS84]\", \"geojson\").json()\u001b[39;00m\n\u001b[32m     64\u001b[39m \u001b[38;5;66;03m# displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\u001b[39;00m\n\u001b[32m   (...)\u001b[39m\u001b[32m     69\u001b[39m \u001b[38;5;66;03m#   geoJSONZoneData = requestZoneData(gnosisDemo, s2Id, \"ISEA3H\", nID, depth, \"2020-07\", \"[OGC:CRS84]\", \"geojson\").json()\u001b[39;00m\n\u001b[32m     70\u001b[39m \u001b[38;5;66;03m#   displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\u001b[39;00m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[15]\u001b[39m\u001b[32m, line 8\u001b[39m, in \u001b[36mrequestZoneData\u001b[39m\u001b[34m(serverURL, collectionId, dggrs, zoneId, depth, time, crs, format, fields)\u001b[39m\n\u001b[32m      6\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m fields \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m      7\u001b[39m     query = query + \u001b[33m\"\u001b[39m\u001b[33m&properties=\u001b[39m\u001b[33m\"\u001b[39m + fields\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43mserverURL\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mresource\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 24\u001b[39m, in \u001b[36mget_call\u001b[39m\u001b[34m(url, resource)\u001b[39m\n\u001b[32m     22\u001b[39m call = urllib.parse.urljoin(url, resource)\n\u001b[32m     23\u001b[39m \u001b[38;5;66;03m# print(f\"endpoint: {call}\")\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m24\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mrequests\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcall\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\api.py:73\u001b[39m, in \u001b[36mget\u001b[39m\u001b[34m(url, params, **kwargs)\u001b[39m\n\u001b[32m     62\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mget\u001b[39m(url, params=\u001b[38;5;28;01mNone\u001b[39;00m, **kwargs):\n\u001b[32m     63\u001b[39m \u001b[38;5;250m    \u001b[39m\u001b[33mr\u001b[39m\u001b[33;03m\"\"\"Sends a GET request.\u001b[39;00m\n\u001b[32m     64\u001b[39m \n\u001b[32m     65\u001b[39m \u001b[33;03m    :param url: URL for the new :class:`Request` object.\u001b[39;00m\n\u001b[32m   (...)\u001b[39m\u001b[32m     70\u001b[39m \u001b[33;03m    :rtype: requests.Response\u001b[39;00m\n\u001b[32m     71\u001b[39m \u001b[33;03m    \"\"\"\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m73\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mget\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\api.py:59\u001b[39m, in \u001b[36mrequest\u001b[39m\u001b[34m(method, url, **kwargs)\u001b[39m\n\u001b[32m     55\u001b[39m \u001b[38;5;66;03m# By using the 'with' statement we are sure the session is closed, thus we\u001b[39;00m\n\u001b[32m     56\u001b[39m \u001b[38;5;66;03m# avoid leaving sockets open which can trigger a ResourceWarning in some\u001b[39;00m\n\u001b[32m     57\u001b[39m \u001b[38;5;66;03m# cases, and look like a memory leak in others.\u001b[39;00m\n\u001b[32m     58\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m sessions.Session() \u001b[38;5;28;01mas\u001b[39;00m session:\n\u001b[32m---> \u001b[39m\u001b[32m59\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43murl\u001b[49m\u001b[43m=\u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\sessions.py:589\u001b[39m, in \u001b[36mSession.request\u001b[39m\u001b[34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[39m\n\u001b[32m    584\u001b[39m send_kwargs = {\n\u001b[32m    585\u001b[39m     \u001b[33m\"\u001b[39m\u001b[33mtimeout\u001b[39m\u001b[33m\"\u001b[39m: timeout,\n\u001b[32m    586\u001b[39m     \u001b[33m\"\u001b[39m\u001b[33mallow_redirects\u001b[39m\u001b[33m\"\u001b[39m: allow_redirects,\n\u001b[32m    587\u001b[39m }\n\u001b[32m    588\u001b[39m send_kwargs.update(settings)\n\u001b[32m--> \u001b[39m\u001b[32m589\u001b[39m resp = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43msend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprep\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43msend_kwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    591\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m resp\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\sessions.py:703\u001b[39m, in \u001b[36mSession.send\u001b[39m\u001b[34m(self, request, **kwargs)\u001b[39m\n\u001b[32m    700\u001b[39m start = preferred_clock()\n\u001b[32m    702\u001b[39m \u001b[38;5;66;03m# Send the request\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m703\u001b[39m r = \u001b[43madapter\u001b[49m\u001b[43m.\u001b[49m\u001b[43msend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    705\u001b[39m \u001b[38;5;66;03m# Total elapsed time of the request (approximately)\u001b[39;00m\n\u001b[32m    706\u001b[39m elapsed = preferred_clock() - start\n",
      "\u001b[36mFile \u001b[39m\u001b[32md:\\Github\\vgrid\\.venv\\Lib\\site-packages\\requests\\adapters.py:682\u001b[39m, in \u001b[36mHTTPAdapter.send\u001b[39m\u001b[34m(self, request, stream, timeout, verify, cert, proxies)\u001b[39m\n\u001b[32m    667\u001b[39m     resp = conn.urlopen(\n\u001b[32m    668\u001b[39m         method=request.method,\n\u001b[32m    669\u001b[39m         url=url,\n\u001b[32m   (...)\u001b[39m\u001b[32m    678\u001b[39m         chunked=chunked,\n\u001b[32m    679\u001b[39m     )\n\u001b[32m    681\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (ProtocolError, \u001b[38;5;167;01mOSError\u001b[39;00m) \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m--> \u001b[39m\u001b[32m682\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mConnectionError\u001b[39;00m(err, request=request)\n\u001b[32m    684\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m MaxRetryError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m    685\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(e.reason, ConnectTimeoutError):\n\u001b[32m    686\u001b[39m         \u001b[38;5;66;03m# TODO: Remove this in 3.0.0: see #2811\u001b[39;00m\n",
      "\u001b[31mConnectionError\u001b[39m: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))"
     ]
    }
   ],
   "source": [
    "dggrs = ISEA3H()\n",
    "zoneId = \"H4-A825C-A\"\n",
    "zone = dggrs.getZoneFromTextID(zoneId)\n",
    "centroid = dggrs.getZoneWGS84Centroid(zone)\n",
    "\n",
    "mapDimensions = Point(1100, 600)\n",
    "\n",
    "neighbors = dggrs.getZoneNeighbors(zone)\n",
    "\n",
    "\n",
    "# ipyleaflet doesn't provide map.getBounds().\n",
    "# This is an experimental guess assuming a WebMercator projection\n",
    "def computeBBox(c, z):\n",
    "    dLat = 180 * 1.3 / pow(2, z) * mapDimensions.y / 256\n",
    "    dLon = 360 / pow(2, z) * mapDimensions.x / 256\n",
    "    # printLn(\"dLat: \", dLat, \", dLon: \", dLon)\n",
    "    return GeoExtent(\n",
    "        (c[0] - dLat / 2, c[1] - dLon / 2), (c[0] + dLat / 2, c[1] + dLon / 2)\n",
    "    )\n",
    "\n",
    "\n",
    "# Likely related, it doesn't seem to be possible to set up callbacks for users panning/zooming\n",
    "# Possibly Voilà (https://voila.readthedocs.io/) allows more interactivity?\n",
    "\n",
    "fields = \"B04,B03,B02,B08\"\n",
    "depth = 7\n",
    "map = setupLeafletMap(Pointd(centroid.lon, centroid.lat), 13)\n",
    "\n",
    "bbox = computeBBox(map.center, map.zoom)\n",
    "\n",
    "# printLn(centroid)\n",
    "# printLn(bbox)\n",
    "# if zones != None:\n",
    "#    print(len(zones), \"zones returned\")\n",
    "#    for z in zones:\n",
    "#        print(dggrs.getZoneTextID(z))\n",
    "\n",
    "zoneLevel = 14\n",
    "zones = dggrs.listZones(zoneLevel, bbox)\n",
    "\n",
    "rgbBands = [\"B04\", \"B03\", \"B02\"]\n",
    "\n",
    "i = 1\n",
    "if zones != None:\n",
    "    for z in zones:\n",
    "        zID = dggrs.getZoneTextID(z)\n",
    "        printLn(\"Fetching \", zID, \" (\", i, \" / \", len(zones), \")\")\n",
    "        i += 1\n",
    "        geoJSONZoneData = requestZoneData(\n",
    "            gnosisDemo,\n",
    "            s2Id,\n",
    "            \"ISEA3H\",\n",
    "            zID,\n",
    "            depth,\n",
    "            \"2020-07\",\n",
    "            \"[OGC:CRS84]\",\n",
    "            \"geojson\",\n",
    "            fields,\n",
    "        ).json()\n",
    "        displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\n",
    "\n",
    "# This requests a single zone:\n",
    "# geoJSONZoneData = requestZoneData(gnosisDemo, s2Id, \"ISEA3H\", zoneId, depth, \"2020-07\", \"[OGC:CRS84]\", \"geojson\").json()\n",
    "# displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\n",
    "\n",
    "# This requests the immediate neighbors of a zone:\n",
    "# for n in neighbors:\n",
    "#   nID = dggrs.getZoneTextID(n)\n",
    "#   geoJSONZoneData = requestZoneData(gnosisDemo, s2Id, \"ISEA3H\", nID, depth, \"2020-07\", \"[OGC:CRS84]\", \"geojson\").json()\n",
    "#   displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\n",
    "\n",
    "map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39bf656f-a7f6-4da0-9213-3862745c76f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def requestZoneInfo(serverURL, collectionId, dggrs, zoneId, format):\n",
    "    resource = f\"collections/{collectionId}/dggs/{dggrs}/zones/{zoneId}?f={format}\"\n",
    "    return get_call(serverURL, resource)\n",
    "\n",
    "\n",
    "# zoneInfo = requestZoneInfo(gnosisDemo, s2Id, \"ISEA3H\", zoneId, \"geojson\").json()\n",
    "# center = shape(zoneInfo[\"features\"][0][\"geometry\"]).centroid"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6320b78d-837b-451d-90bb-4841f6807285",
   "metadata": {},
   "source": [
    "### As DGGS-JSON (relative depth 7):\n",
    "\n",
    "[DGGS-JSON](https://docs.ogc.org/DRAFTS/21-038.html#rc_data-json) is an encoding specifically targeting DGGS-enabled clients, allowing to return data values quantized to sub-zones while relying on the client's understanding of the DGGRS and a deterministic order of sub-zones. Here we use DGGAL to automatically generate GeoJSON from the DGGS-JSON to add it as a GeoJSON layer to Leaflet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6685f789-b749-4f79-9630-3834d3d35bfb",
   "metadata": {},
   "outputs": [],
   "source": [
    "depth = 7\n",
    "zoom = 15  # 13\n",
    "map = setupLeafletMap(Pointd(centroid.lon, centroid.lat), zoom)\n",
    "\n",
    "bbox = computeBBox(map.center, map.zoom)\n",
    "\n",
    "zoneLevel = dggrs.getLevelFromPixelsAndExtent(\n",
    "    bbox, mapDimensions, dggrs.get64KDepth() + 1\n",
    ")\n",
    "# printLn(\"Selected level: \", zoneLevel)\n",
    "zones = dggrs.listZones(zoneLevel, bbox)\n",
    "\n",
    "\n",
    "def generateZoneGeometry(dggrs, zone, crs, id, centroids: bool, fc: bool):\n",
    "    coordinates = []\n",
    "    if not crs or crs == CRS(ogc, 84) or crs == CRS(epsg, 4326):\n",
    "        if centroids:\n",
    "            centroid = dggrs.getZoneWGS84Centroid(zone, centroid)\n",
    "            coordinates.append(centroid.lon.value)\n",
    "            coordinates.append(centroid.lat.value)\n",
    "        else:\n",
    "            vertices = dggrs.getZoneRefinedWGS84Vertices(zone, 0)\n",
    "            if vertices:\n",
    "                contour = []\n",
    "                for v in vertices:\n",
    "                    contour.append([v.lon.value, v.lat.value])\n",
    "                contour.append([vertices[0].lon.value, vertices[0].lat.value])\n",
    "                coordinates.append(contour)\n",
    "    else:\n",
    "        if centroids:\n",
    "            centroid = dggrs.getZoneCRSCentroid(zone, crs, centroid)\n",
    "            coordinates.append(centroid.x)\n",
    "            coordinates.append(centroid.y)\n",
    "        else:\n",
    "            vertices = dggrs.getZoneRefinedCRSVertices(zone, crs, 0)\n",
    "            if vertices:\n",
    "                count = vertices.count\n",
    "                contour = []\n",
    "                for v in vertices:\n",
    "                    contour.append([v.x, v.y])\n",
    "                contour.append([vertices[0].x, vertices[0].y])\n",
    "                coordinates.append(contour)\n",
    "    geometry = {\"type\": \"Point\" if centroids else \"Polygon\", \"coordinates\": coordinates}\n",
    "    return geometry\n",
    "\n",
    "\n",
    "def generateZoneFeature(dggrs, zone, crs, id, centroids: bool, fc: bool, props):\n",
    "    zoneID = dggrs.getZoneTextID(zone)\n",
    "\n",
    "    properties = {\"zoneID\": f\"{zoneID}\"}\n",
    "    if props:\n",
    "        for key, v in props.items():\n",
    "            properties[key] = v\n",
    "\n",
    "    features = {\n",
    "        \"type\": \"Feature\",\n",
    "        \"id\": id if id is not None else zoneID,\n",
    "        \"geometry\": generateZoneGeometry(dggrs, zone, crs, id, centroids, fc),\n",
    "        \"properties\": properties,\n",
    "    }\n",
    "    return features\n",
    "\n",
    "\n",
    "def dggsJSON2GeoJSON(dggsJSON, crs: CRS = None, centroids: bool = False):\n",
    "    result = None\n",
    "    if dggsJSON is not None:\n",
    "        dggrsClass = None\n",
    "        dggrsID = getLastDirectory(dggsJSON[\"dggrs\"])\n",
    "\n",
    "        # We could use globals()['GNOSISGlobalGrid'] to be more generic, but here we limit to DGGRSs we know\n",
    "        if not strnicmp(dggrsID, \"GNOSIS\", 6):\n",
    "            dggrsClass = GNOSISGlobalGrid\n",
    "        elif not strnicmp(dggrsID, \"ISEA3H\", 6):\n",
    "            dggrsClass = ISEA3H\n",
    "        elif not strnicmp(dggrsID, \"ISEA9R\", 6):\n",
    "            dggrsClass = ISEA9R\n",
    "        elif not strnicmp(dggrsID, \"IVEA3H\", 6):\n",
    "            dggrsClass = IVEA3H\n",
    "        elif not strnicmp(dggrsID, \"IVEA9R\", 6):\n",
    "            dggrsClass = IVEA9R\n",
    "\n",
    "        if dggrsClass:\n",
    "            zoneID = dggsJSON[\"zoneId\"]\n",
    "            dggrs = dggrsClass()\n",
    "            zone = dggrs.getZoneFromTextID(zoneID)\n",
    "\n",
    "            if zone != nullZone:\n",
    "                depths = dggsJSON[\"depths\"]\n",
    "                if depths:\n",
    "                    maxDepth = -1\n",
    "\n",
    "                    for d in range(len(depths)):\n",
    "                        depth = depths[d]\n",
    "                        if depth > maxDepth:\n",
    "                            maxDepth = depth\n",
    "                            break\n",
    "                    if d < len(depths):\n",
    "                        depth = maxDepth\n",
    "                        subZones = dggrs.getSubZones(zone, depth)\n",
    "                        if subZones:\n",
    "                            i = 0\n",
    "                            values = dggsJSON[\"values\"]\n",
    "                            features = []\n",
    "                            for z in subZones:\n",
    "                                props = {}\n",
    "                                for key, vDepths in values.items():\n",
    "                                    if key and vDepths and len(vDepths) > d:\n",
    "                                        data = vDepths[d][\"data\"]\n",
    "                                        props[key] = data[i]\n",
    "                                features.append(\n",
    "                                    generateZoneFeature(\n",
    "                                        dggrs, z, crs, i + 1, centroids, True, props\n",
    "                                    )\n",
    "                                )\n",
    "                                i += 1\n",
    "                            result = {\"type\": \"FeatureCollection\", \"features\": features}\n",
    "    return result\n",
    "\n",
    "\n",
    "if zones != None:\n",
    "    i = 1\n",
    "    for z in zones:\n",
    "        zID = dggrs.getZoneTextID(z)\n",
    "        printLn(\"Fetching \", zID, \" (\", i, \" / \", len(zones), \")\")\n",
    "        i += 1\n",
    "        dggsjsonZoneData = requestZoneData(\n",
    "            gnosisDemo,\n",
    "            s2Id,\n",
    "            \"ISEA3H\",\n",
    "            zID,\n",
    "            depth,\n",
    "            \"2020-07\",\n",
    "            \"[OGC:CRS84]\",\n",
    "            \"json\",\n",
    "            fields,\n",
    "        ).json()\n",
    "        geoJSONZoneData = dggsJSON2GeoJSON(dggsjsonZoneData)\n",
    "        # print(json.dumps(geoJSONZoneData, indent = 3))\n",
    "        displayS2RGBFeatures(map, geoJSONZoneData, rgbBands)\n",
    "\n",
    "map"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
