├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.md ├── conda-requirements.txt ├── docker-compose.yml ├── dockerun.sh ├── jupyter.sh ├── requirements.txt └── work ├── API_quick_search.ipynb ├── BWS_calculation.ipynb ├── Country_pages_examples.ipynb ├── Deploy_google_cloud_functions.ipynb ├── EE_as_a_tile_server.ipynb ├── GFW_climate_biomass_loss.ipynb ├── GFW_climate_biomass_tiles.ipynb ├── GFW_climate_biomass_widgets.ipynb ├── GFW_tiles.ipynb ├── GLADS_decode_tiles.ipynb ├── GLADS_test_clusters.ipynb ├── Geodescriber.ipynb ├── Geojson_services.ipynb ├── HE - Styling of biodiversity layers.ipynb ├── HE_infowindow_and_metadata.ipynb ├── HE_new_columns.ipynb ├── HE_protected_area_percentage.ipynb ├── INGESTING_EARTH_ENGINE_ASSETS.md ├── Interview_Questions.ipynb ├── Landmark_example.ipynb ├── Landsat 7 annual basemaps - Pt 1.ipynb ├── Modifying_Shapefiles.ipynb ├── MonteCarlo_Time_series_analysis.ipynb ├── Movie-tiles.ipynb ├── RW_API_CARTO_to_layers.ipynb ├── RW_API_EE_to_layers.ipynb ├── RW_API_and_Carto_Layers.ipynb ├── Rasters_part1.ipynb ├── Rasters_part2.ipynb ├── Recent_Images_Microservice_Tutorial.ipynb ├── Skydipper_acceptance_test.ipynb ├── TRASE_overlap_GADM36.ipynb ├── TRASE_widgets.ipynb ├── Tile_and_Movie_in_EEs.ipynb ├── Viirs_processing.ipynb ├── access_analysis.ipynb ├── aichi_test.ipynb ├── airtable_test.ipynb ├── authorize_notebook_server.ipynb ├── biomass_loss_widget.ipynb ├── compute_biomass_gee.ipynb ├── data ├── 360x114global_short.zip ├── RGB.byte.tif ├── TM_WORLD_BORDERS_SIMPL-0 │ ├── Readme.txt │ ├── TM_WORLD_BORDERS_SIMPL-0.3.dbf │ ├── TM_WORLD_BORDERS_SIMPL-0.3.prj │ ├── TM_WORLD_BORDERS_SIMPL-0.3.shp │ └── TM_WORLD_BORDERS_SIMPL-0.3.shx ├── eurasia.json ├── gadm28_countries.zip ├── gadm_36_gid2.json └── modis_example.nc ├── fires_context.ipynb ├── gfwclimate_updated_widgets.ipynb ├── lmipy_add_mapbiomass_version_3_1.ipynb ├── mapBiomass_tiles.ipynb ├── pics ├── Jamaica.png ├── add_im.png ├── birds_se.png ├── birds_sr.png ├── copy_im_to_ic.png ├── crs_perm.png ├── empty_ic.png ├── florida.png ├── gfw_biomass.png ├── gfwc_analysis_hover.png ├── ic_share.png ├── im_form_done.png ├── img_modal1.png ├── mammals_se.png ├── mammals_sr.png ├── old_gfw_climate_widget.png ├── perm1.png ├── perm2.png ├── ten1.png ├── ten2.png ├── tree_cover_hint.png └── web_map_eg.png ├── threejs-viz.ipynb ├── tileservers.ipynb └── using_recent_imagery_service.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | Untitled.ipynb 2 | .ipynb_checkpoints 3 | .Rproj.user 4 | .Rhistory 5 | .RData 6 | .Rapp.history 7 | .DS_Store 8 | .ipynb_checkpoints 9 | .ipynb_checkpoints/ 10 | .httr-oauth 11 | .env 12 | .Rproj 13 | __pycache__ 14 | client_secret.json 15 | google_secret.json 16 | .config 17 | rstudio 18 | .rstudio 19 | work.Rproj 20 | notes/ 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Projects/earth-dashboard"] 2 | path = Projects/earth-dashboard 3 | url = https://github.com/Vizzuality/earth-dashboard-data.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ecornejo/jupyter:testing 2 | COPY requirements.txt /home/jovyan/work 3 | COPY conda-requirements.txt /home/jovyan/work 4 | 5 | USER root 6 | RUN apk update && apk upgrade && apk add --no-cache --update g++ make 7 | 8 | RUN pip install -r requirements.txt 9 | RUN conda install --file conda-requirements.txt 10 | USER jovyan -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016, Vizzuality 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Science Tutorials 2 | 3 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/Vizzuality/sql2gee/blob/develop/LICENSE) 4 | 5 | A series of Jupyter Notebook tutorials, relating to libraries and methods relevant to Vizzuality's Data Science Group. 6 | 7 | Start docker via 8 | ```bash 9 | ./jupyter.sh develop 10 | ``` 11 | 12 | N.b. if the above does not execute you may need to `chown +x jupyter.sh`. 13 | 14 | Then open a browser to `0.0.0.0:8888` to connect to the Jupyter server. 15 | 16 | Notebooks and data for this repo are stored in `./Work`. 17 | -------------------------------------------------------------------------------- /conda-requirements.txt: -------------------------------------------------------------------------------- 1 | xarray==0.9.1 2 | colorama==0.3.7 3 | tqdm>=4.8.0 4 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | develop: 4 | image: ecornejo/jupyter:testing 5 | build: . 6 | container_name: viz-notebook 7 | command: start-notebook.sh --NotebookApp.token='' 8 | ports: 9 | - "8888:8888" 10 | volumes: 11 | - ./work:/home/jovyan/work 12 | -------------------------------------------------------------------------------- /dockerun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # --- Define variables --- 4 | 5 | pt_r='8889' 6 | 7 | # set directory 8 | folder="data_sci_tutorials/work" 9 | basedir="/Users/alejandroguizar/vizz/" 10 | projectdir=$basedir$folder 11 | 12 | # set containers names 13 | containername='viz-notebook' 14 | # name_py=$containername'-py' 15 | name_r=$containername'-r' 16 | 17 | #--- Build R container 18 | docker run -p $pt_r:8787 --name $name_r -v "$projectdir":/home/rstudio/work/ rocker/geospatial 19 | -------------------------------------------------------------------------------- /jupyter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case "$1" in 4 | develop) 5 | type docker-compose >/dev/null 2>&1 || { echo >&2 "docker-compose is required but it's not installed. Aborting."; exit 1; } 6 | docker-compose -f docker-compose.yml build && docker-compose -f docker-compose.yml up 7 | ;; 8 | *) 9 | echo "Usage: jupyter.sh {develop}" >&2 10 | exit 1 11 | ;; 12 | esac 13 | 14 | exit 0 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | hurry.filesize==0.9 2 | cartopy==0.14.2 3 | rasterstats==0.12.0 4 | pprintpp 5 | folium==0.3.0 6 | requests==2.18.4 7 | cartoframes 8 | iso3166 9 | togeojsontiles 10 | -------------------------------------------------------------------------------- /work/GFW_climate_biomass_loss.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Create context tiles\n", 8 | "\n", 9 | "Create RGB tiles in Earth Engine that have encoded year, biomass loss, and intensity at different Z levels." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 3, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import folium\n", 19 | "import ee\n", 20 | "ee.Initialize()" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 8, 26 | "metadata": { 27 | "collapsed": true 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "def tile_url(image, viz_params=None):\n", 32 | " \"\"\"Create a target url for WMS tiles of a given image.\n", 33 | " e.g.\n", 34 | " im = ee.Image(\"LE7_TOA_1YEAR/\" + year).select(\"B3\",\"B2\",\"B1\")\n", 35 | " viz = {'opacity': 1, 'gain':3.5, 'bias':4, 'gamma':1.5}\n", 36 | " url = tile_url(image=im),viz_params=viz)\n", 37 | " \"\"\"\n", 38 | " if viz_params:\n", 39 | " d = image.getMapId(viz_params)\n", 40 | " else:\n", 41 | " d = image.getMapId()\n", 42 | " base_url = 'https://earthengine.googleapis.com'\n", 43 | " url = (base_url + '/map/' + d['mapid'] + '/{z}/{x}/{y}?token=' + d['token'])\n", 44 | " return url" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 15, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# UMD HANSEN DATA 2000 - 2016\n", 54 | "umd = ee.Image('UMD/hansen/global_forest_change_2016_v1_4') # Load the Hansen et al. forest change dataset \n", 55 | "datamask = umd.select('datamask')\n", 56 | "mask = datamask.eq(1)\n", 57 | "land = mask # Make a land image out of the mask\n", 58 | "land = land.mask(land) # Mask land with itself to mask all the zeros (non-land)\n", 59 | "\n", 60 | "# Woods Hole Research Center Biomass data\n", 61 | "neotropic = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Neotropic')\n", 62 | "africa = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Africa')\n", 63 | "australia = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Australia')\n", 64 | "tropasia = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Tropical_Asia')\n", 65 | "palearctic = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Palearctic')\n", 66 | "nearctic = ee.Image('users/mfarina/Biomass_Data_MapV3/WHRC_Biomass_30m_Nearctic')\n", 67 | "\n", 68 | "# A custom WHRC palette\n", 69 | "whrcPALETTE = (\"75322B,84512A,8E6232,da8c19,ef9e0b,ffc011,ffdb2d,\"\n", 70 | "\"ffe215,f9eb46,d5e400,c9d800,becc00,b4c200,B7B95B,B2B659,AFB457,ABB156,\"\n", 71 | "\"A6AE53,A3AB52,A1AA51,9FA950,9EA850,9CA74F,9BA64E,9AA54E,99A44D,95A24C,\"\n", 72 | "\"92A04A,909E49,8C9C48,8B9A47,869745,859745,839544,839543,819443,7E9241,\"\n", 73 | "\"7A8F40,778D3E,758C3E,758B3D,728A3C,71893C,70883B,6F873B,6D863A,6A8438,\"\n", 74 | "\"678237,648036,627E37,607D34,5E7B33,5A7831,577630,53742E,50722D,4F712C,\"\n", 75 | "\"4E702C,4C6F2B,4A6D2A,496D29,486C29,486C29,476B29,466A28,426827,3E6525,\"\n", 76 | "\"3B6323,3A6223,396222,386122,355F21,345E22,315C1F,305B1E,2C591D,2B581C,\"\n", 77 | "\"28561B,27551A,255419,245319,235218,225218,225118,215118,205017,1F4F17,\"\n", 78 | "\"1C4E16,1B4D15,1A4C15,194C14,184A14,164913,154812,124711,114610,114610,\"\n", 79 | "\"114610,114610\")\n", 80 | " \n", 81 | "myVis = {\"max\":400,\"min\":0, 'palette': whrcPALETTE}\n", 82 | "\n", 83 | "# Make an image collection from the visualization images.\n", 84 | "biomass = ee.ImageCollection([\n", 85 | " land.visualize({'palette': '75322B'}),\n", 86 | " africa.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 87 | " australia.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 88 | " nearctic.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 89 | " neotropic.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 90 | " palearctic.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 91 | " tropasia.visualize({'palette': whrcPALETTE, \"max\":400, \"min\":0,}),\n", 92 | "]).mosaic()\n", 93 | "\n", 94 | "#Map.addLayer(biomass, null, \"Biomass\")" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 17, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "{'max': 400,\n", 113 | " 'min': 0,\n", 114 | " 'palette': '75322B,84512A,8E6232,da8c19,ef9e0b,ffc011,ffdb2d,ffe215,f9eb46,d5e400,c9d800,becc00,b4c200,B7B95B,B2B659,AFB457,ABB156,A6AE53,A3AB52,A1AA51,9FA950,9EA850,9CA74F,9BA64E,9AA54E,99A44D,95A24C,92A04A,909E49,8C9C48,8B9A47,869745,859745,839544,839543,819443,7E9241,7A8F40,778D3E,758C3E,758B3D,728A3C,71893C,70883B,6F873B,6D863A,6A8438,678237,648036,627E37,607D34,5E7B33,5A7831,577630,53742E,50722D,4F712C,4E702C,4C6F2B,4A6D2A,496D29,486C29,486C29,476B29,466A28,426827,3E6525,3B6323,3A6223,396222,386122,355F21,345E22,315C1F,305B1E,2C591D,2B581C,28561B,27551A,255419,245319,235218,225218,225118,215118,205017,1F4F17,1C4E16,1B4D15,1A4C15,194C14,184A14,164913,154812,124711,114610,114610,114610,114610'}" 115 | ] 116 | }, 117 | "execution_count": 17, 118 | "metadata": {}, 119 | "output_type": "execute_result" 120 | } 121 | ], 122 | "source": [ 123 | "myVis" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 18, 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "ename": "EEException", 133 | "evalue": "Image.visualize: Expected a string or list of strings for field 'bands'.", 134 | "output_type": "error", 135 | "traceback": [ 136 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 137 | "\u001b[0;31mEEException\u001b[0m Traceback (most recent call last)", 138 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtile_url\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbiomass\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 139 | "\u001b[0;32m\u001b[0m in \u001b[0;36mtile_url\u001b[0;34m(image, viz_params)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetMapId\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mviz_params\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetMapId\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 12\u001b[0m \u001b[0mbase_url\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'https://earthengine.googleapis.com'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0murl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mbase_url\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m'/map/'\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'mapid'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m'/{z}/{x}/{y}?token='\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'token'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 140 | "\u001b[0;32m/Users/Ben/anaconda/envs/py36/lib/python3.6/site-packages/ee/image.py\u001b[0m in \u001b[0;36mgetMapId\u001b[0;34m(self, vis_params)\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[0mrequest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mvis_params\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[0mrequest\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'image'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserialize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 129\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetMapId\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 130\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'image'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 141 | "\u001b[0;32m/Users/Ben/anaconda/envs/py36/lib/python3.6/site-packages/ee/data.py\u001b[0m in \u001b[0;36mgetMapId\u001b[0;34m(params)\u001b[0m\n\u001b[1;32m 204\u001b[0m \"\"\"\n\u001b[1;32m 205\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'json_format'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'v2'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 206\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msend_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'/mapid'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 207\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 208\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 142 | "\u001b[0;32m/Users/Ben/anaconda/envs/py36/lib/python3.6/site-packages/ee/data.py\u001b[0m in \u001b[0;36msend_\u001b[0;34m(path, params, opt_method, opt_raw)\u001b[0m\n\u001b[1;32m 728\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mee_exception\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mEEException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Invalid JSON: %s'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mcontent\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 729\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'error'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mjson_content\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 730\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mee_exception\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mEEException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjson_content\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'error'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'message'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 731\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m'data'\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcontent\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 732\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mee_exception\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mEEException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Malformed response: '\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcontent\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 143 | "\u001b[0;31mEEException\u001b[0m: Image.visualize: Expected a string or list of strings for field 'bands'." 144 | ] 145 | } 146 | ], 147 | "source": [ 148 | "tile_url(biomass)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": { 155 | "collapsed": true 156 | }, 157 | "outputs": [], 158 | "source": [ 159 | "basemap = 'Cartodb dark_matter'\n", 160 | "tileset=r\"https://storage.googleapis.com/landsat-cache/2016/{z}/{x}/{y}.png\"\n", 161 | "map = folium.Map(location=[28.29, -16.6], zoom_start=3, tiles=basemap)\n", 162 | "map.add_tile_layer(tiles=tileset, max_zoom=12,min_zoom=1, attr='Custom tiles')\n", 163 | "map" 164 | ] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "Python 3", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.6.2" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 2 188 | } 189 | -------------------------------------------------------------------------------- /work/GFW_climate_biomass_widgets.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# GFW climate biomass widgets" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "#!pip install progressbar2\n", 17 | "#!pip install retrying" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import geopandas as gpd\n", 27 | "import pandas as pd\n", 28 | "import numpy as np\n", 29 | "import requests\n", 30 | "import os\n", 31 | "import json\n", 32 | "import progressbar\n", 33 | "from retrying import retry\n", 34 | "%matplotlib inline" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "## Table with biomass density and total biomass" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "**GADM 3.6 admin 2**" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "df = gpd.read_file('/Users/Ben/Downloads/gadm36_shp/gadm36.shp')" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "df.head()" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "#gadm_ids = df[['GID_0', 'ID_0', 'NAME_0', 'ID_1', 'NAME_1', 'ID_2', 'NAME_2','GID_1','GID_2']]" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "#gadm_ids[gadm_ids['GID_2'] == 'AFG.2.1_1']" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "#tmp = gadm_ids[gadm_ids['GID_0']=='BRA']" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "#tmp[tmp['GID_1'] == 'BRA.2_1'].head()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "missing_df = df[df['GID_2'] == '']" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "f'{len(missing_df)/len(df) * 100:3.2f}% of rows are missing admin-2 id codes.'" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "def process_gid_2(gid_2):\n", 137 | " \"\"\"Return dict of iso (string), and admin_1 and admin_2 (ints) from gid_2 entry.\"\"\"\n", 138 | " try:\n", 139 | " iso, admin_1, tmp_admin_2 = gid_2.split('.')\n", 140 | " admin_2 = tmp_admin_2.split('_')[0]\n", 141 | " return {'iso':iso, 'admin_1':int(admin_1), 'admin_2':int(admin_2)}\n", 142 | " except:\n", 143 | " return None" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "# Create list of GIDS to process\n", 153 | "all_areas = []\n", 154 | "for x in df['GID_2'].values:\n", 155 | " tmp = process_gid_2(x)\n", 156 | " if tmp:\n", 157 | " all_areas.append(tmp)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "len(all_areas)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "# Create gadm3.6 GID_2 data list\n", 176 | "with open(\"./data/gadm_36_gid2.json\", \"w\") as f:\n", 177 | " for row in all_areas:\n", 178 | " f.write(json.dumps(row) +'\\n') " 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "# now we have all the codes for all areas I am going to de-allocate the memory of the df to save RAM\n", 188 | "df = 0" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "### Begin here if gadm 3.6 data file exists" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 2, 208 | "metadata": {}, 209 | "outputs": [ 210 | { 211 | "name": "stdout", 212 | "output_type": "stream", 213 | "text": [ 214 | "Found existing gadm-3.6 gid-2 file, restoring previous data! 🍺\n", 215 | "Loaded 338307 rows of data.\n" 216 | ] 217 | } 218 | ], 219 | "source": [ 220 | "# Restore list of GID_2 data if the file exists\n", 221 | "gid_list = \"./data/gadm_36_gid2.json\"\n", 222 | "if os.path.exists(gid_list):\n", 223 | " print(\"Found existing gadm-3.6 gid-2 file, restoring previous data! 🍺\")\n", 224 | " with open(gid_list,\"r\") as f:\n", 225 | " all_areas = []\n", 226 | " for row in f.readlines():\n", 227 | " all_areas.append(json.loads(row))\n", 228 | " print(f'Loaded {len(all_areas)} rows of data.')" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 3, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "data": { 238 | "text/plain": [ 239 | "[{'iso': 'AFG', 'admin_1': 1, 'admin_2': 1},\n", 240 | " {'iso': 'AFG', 'admin_1': 1, 'admin_2': 2},\n", 241 | " {'iso': 'AFG', 'admin_1': 1, 'admin_2': 3},\n", 242 | " {'iso': 'AFG', 'admin_1': 1, 'admin_2': 4},\n", 243 | " {'iso': 'AFG', 'admin_1': 1, 'admin_2': 5}]" 244 | ] 245 | }, 246 | "execution_count": 3, 247 | "metadata": {}, 248 | "output_type": "execute_result" 249 | } 250 | ], 251 | "source": [ 252 | "all_areas[0:5]" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "The API contains an endpoint for `whrc-biomass` to compute the total biomass and biomass density of a given municipality which uses geostore v2 endpoint for gadm geometries." 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 4, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/plain": [ 270 | "338307" 271 | ] 272 | }, 273 | "execution_count": 4, 274 | "metadata": {}, 275 | "output_type": "execute_result" 276 | } 277 | ], 278 | "source": [ 279 | "len(all_areas)" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": 5, 285 | "metadata": {}, 286 | "outputs": [], 287 | "source": [ 288 | "# Use session to persist connection between requests (for speed-up) http://docs.python-requests.org/en/master/user/advanced/\n", 289 | "s = requests.Session() \n", 290 | "\n", 291 | "@retry(stop_max_attempt_number=5, wait_fixed=2000)\n", 292 | "def make_query(area):\n", 293 | " try:\n", 294 | " r = s.get(f\"https://production-api.globalforestwatch.org/v1/whrc-biomass/admin/{area['iso']}/{area['admin_1']}/{area['admin_2']}\")\n", 295 | " if r.status_code == 200:\n", 296 | " return r.json().get('data').get('attributes')\n", 297 | " else:\n", 298 | " return None\n", 299 | " except:\n", 300 | " #print(f\"Failed on {area['iso']}/{area['admin_1']}/{area['admin_2']}\")\n", 301 | " #raise IOError(f\"EE failure: {r.status_code}\")\n", 302 | " return None\n", 303 | "\n", 304 | " \n", 305 | "def find_in_written_data(written_data, iso, admin_1, admin_2):\n", 306 | " for row in written_data:\n", 307 | " if row.get('iso') == iso and row.get('admin_1') == admin_1 and row.get('admin_2') == admin_2:\n", 308 | " return True\n", 309 | " else:\n", 310 | " pass\n", 311 | " return False\n", 312 | "\n", 313 | " \n", 314 | "def get_written_data(backup_file):\n", 315 | " '''Create or restore data from a backup file e.g ./tmp_whrc_data.json '''\n", 316 | " if os.path.exists(backup_file):\n", 317 | " #print(\"Found existing file, restoring previous data! 🍺\")\n", 318 | " written_data = []\n", 319 | " with open(backup_file, 'r') as f:\n", 320 | " for line in f.readlines():\n", 321 | " written_data.append(json.loads(line))\n", 322 | " return written_data\n", 323 | " else:\n", 324 | " #print(\"No previous data found, starting queries from scratch... 🏃‍♂️\") \n", 325 | " return []\n", 326 | " \n", 327 | "def check_writen_lenght():\n", 328 | " check_data = []\n", 329 | " with open(\"./tmp_whrc_data.json\", 'r') as f:\n", 330 | " for line in f.readlines():\n", 331 | " check_data.append(json.loads(line))\n", 332 | " print(f\"Number of records sucessfully written: {len(check_data):,g}\")" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [] 341 | }, 342 | { 343 | "cell_type": "code", 344 | "execution_count": 23, 345 | "metadata": {}, 346 | "outputs": [], 347 | "source": [] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "metadata": {}, 353 | "outputs": [], 354 | "source": [ 355 | "# Single thread process\n", 356 | "\n", 357 | "# %%time\n", 358 | "# with open(backup_file, \"a+\") as f:\n", 359 | "# with progressbar.ProgressBar(max_value=len(all_areas)) as bar:\n", 360 | "# for n, area in enumerate(all_areas[0:40]):\n", 361 | "# bar.update(n)\n", 362 | "# if not find_in_written_data(written_data, area.get('iso'), area.get('admin_1'), area.get('admin_2')):\n", 363 | "# # maybe we should try it several times if it fails....\n", 364 | "# tmp_data = make_query(area)\n", 365 | "# if tmp_data:\n", 366 | "# tmp_d = {**area, **tmp_data}\n", 367 | "# written_data.append(tmp_d)\n", 368 | "# f.write(json.dumps(tmp_d) +'\\n') # write a line to a temporary file incase the process fails and all data is lost\n", 369 | "# else:\n", 370 | "# pass" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 27, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "\n", 380 | "def process_single_thread(gid_list, backup_file=\"./tmp_whrc_data.json\"):\n", 381 | " with open(backup_file, \"a+\") as f:\n", 382 | " with progressbar.ProgressBar(max_value=len(gid_list)) as bar:\n", 383 | " for n, area in enumerate(gid_list):\n", 384 | " bar.update(n)\n", 385 | " if not find_in_written_data(written_data, area.get('iso'), area.get('admin_1'), area.get('admin_2')):\n", 386 | " # maybe we should try it several times if it fails....\n", 387 | " tmp_data = make_query(area)\n", 388 | " if tmp_data:\n", 389 | " tmp_d = {**area, **tmp_data}\n", 390 | " written_data.append(tmp_d)\n", 391 | " f.write(json.dumps(tmp_d) +'\\n') # write a line to a temporary file incase the process fails and all data is lost\n", 392 | " else:\n", 393 | " pass\n", 394 | "\n" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": null, 400 | "metadata": {}, 401 | "outputs": [], 402 | "source": [] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": null, 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 28, 414 | "metadata": {}, 415 | "outputs": [], 416 | "source": [ 417 | "def process_gid_list(gid_list, backup_file=\"./tmp_whrc_data.json\"):\n", 418 | " \"\"\"e.g. process_gid_list(all_areas[0:20])\"\"\"\n", 419 | " written_data = get_written_data(backup_file)\n", 420 | " with open(backup_file, \"a+\") as f:\n", 421 | " #with progressbar.ProgressBar(max_value=len(gid_list)) as bar:\n", 422 | " for n, area in enumerate(gid_list):\n", 423 | " #bar.update(n)\n", 424 | " #print(f\"Already processed area = {find_in_written_data(written_data, area.get('iso'), area.get('admin_1'), area.get('admin_2'))}\")\n", 425 | " if not find_in_written_data(written_data, area.get('iso'), area.get('admin_1'), area.get('admin_2')):\n", 426 | " tmp_data = make_query(area)\n", 427 | " if tmp_data:\n", 428 | " tmp_d = {**area, **tmp_data}\n", 429 | " written_data.append(tmp_d)\n", 430 | " f.write(json.dumps(tmp_d) +'\\n') # write a line to a temporary file incase the process fails and all data is lost\n", 431 | " else:\n", 432 | " pass" 433 | ] 434 | }, 435 | { 436 | "cell_type": "markdown", 437 | "metadata": {}, 438 | "source": [ 439 | "## Single thread requests" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": null, 445 | "metadata": {}, 446 | "outputs": [ 447 | { 448 | "name": "stderr", 449 | "output_type": "stream", 450 | "text": [ 451 | " 37% (3737 of 10000) |####### | Elapsed Time: 0:02:14 ETA: 3:35:16" 452 | ] 453 | } 454 | ], 455 | "source": [ 456 | "process_single_thread(all_areas[0:10000])" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 40, 462 | "metadata": {}, 463 | "outputs": [ 464 | { 465 | "name": "stdout", 466 | "output_type": "stream", 467 | "text": [ 468 | "Number of records sucessfully written: 14,436\n" 469 | ] 470 | } 471 | ], 472 | "source": [ 473 | "check_writen_lenght()" 474 | ] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "metadata": {}, 479 | "source": [ 480 | "## Multithreadded requests" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": 7, 486 | "metadata": {}, 487 | "outputs": [], 488 | "source": [ 489 | "from multiprocessing import Pool" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": 8, 495 | "metadata": {}, 496 | "outputs": [ 497 | { 498 | "data": { 499 | "text/plain": [ 500 | "338307" 501 | ] 502 | }, 503 | "execution_count": 8, 504 | "metadata": {}, 505 | "output_type": "execute_result" 506 | } 507 | ], 508 | "source": [ 509 | "len(all_areas)" 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": 24, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "step_size = 100\n", 519 | "chunked_list = [all_areas[i:i + step_size] for i in range(0, len(all_areas[200000:200200]), step_size)]" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 16, 525 | "metadata": {}, 526 | "outputs": [ 527 | { 528 | "name": "stdout", 529 | "output_type": "stream", 530 | "text": [ 531 | "10 chunks, with 10 requests per chunk\n" 532 | ] 533 | } 534 | ], 535 | "source": [ 536 | "print(f\"{len(chunked_list)} chunks, with {len(chunked_list[0])} requests per chunk\")" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 261, 542 | "metadata": {}, 543 | "outputs": [], 544 | "source": [ 545 | "#chunked_list[0]" 546 | ] 547 | }, 548 | { 549 | "cell_type": "code", 550 | "execution_count": 29, 551 | "metadata": {}, 552 | "outputs": [ 553 | { 554 | "name": "stdout", 555 | "output_type": "stream", 556 | "text": [ 557 | "CPU times: user 217 ms, sys: 417 ms, total: 634 ms\n", 558 | "Wall time: 1.87 s\n" 559 | ] 560 | } 561 | ], 562 | "source": [ 563 | "%%time\n", 564 | "\n", 565 | "with Pool(100) as p:\n", 566 | " p.map(process_gid_list, chunked_list)" 567 | ] 568 | }, 569 | { 570 | "cell_type": "code", 571 | "execution_count": 38, 572 | "metadata": {}, 573 | "outputs": [], 574 | "source": [ 575 | "check_writen_lenght()" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 207, 581 | "metadata": {}, 582 | "outputs": [], 583 | "source": [] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": null, 588 | "metadata": {}, 589 | "outputs": [], 590 | "source": [] 591 | }, 592 | { 593 | "cell_type": "code", 594 | "execution_count": 231, 595 | "metadata": {}, 596 | "outputs": [], 597 | "source": [] 598 | }, 599 | { 600 | "cell_type": "code", 601 | "execution_count": null, 602 | "metadata": {}, 603 | "outputs": [], 604 | "source": [] 605 | }, 606 | { 607 | "cell_type": "markdown", 608 | "metadata": {}, 609 | "source": [ 610 | "## Load the written data and create a final output file" 611 | ] 612 | }, 613 | { 614 | "cell_type": "code", 615 | "execution_count": 18, 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [ 619 | "# # If you need to load/restore the data from a tmp file (due to failure etc) you can do the following...\n", 620 | "written_data = []\n", 621 | "with open(\"./tmp_whrc_data.json\", 'r') as f:\n", 622 | " for line in f.readlines():\n", 623 | " written_data.append(json.loads(line))" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": null, 629 | "metadata": {}, 630 | "outputs": [], 631 | "source": [] 632 | }, 633 | { 634 | "cell_type": "code", 635 | "execution_count": null, 636 | "metadata": {}, 637 | "outputs": [], 638 | "source": [] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": null, 643 | "metadata": {}, 644 | "outputs": [], 645 | "source": [ 646 | "# Final table needs row names of 'biomassdensity','gid_0','id_1','id_2','totalbiomass','areaHa'. Use rename function below" 647 | ] 648 | }, 649 | { 650 | "cell_type": "code", 651 | "execution_count": 19, 652 | "metadata": {}, 653 | "outputs": [], 654 | "source": [ 655 | "output_df = pd.DataFrame(written_data)" 656 | ] 657 | }, 658 | { 659 | "cell_type": "code", 660 | "execution_count": 20, 661 | "metadata": {}, 662 | "outputs": [ 663 | { 664 | "data": { 665 | "text/html": [ 666 | "
\n", 667 | "\n", 680 | "\n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | " \n", 699 | " \n", 700 | " \n", 701 | " \n", 702 | " \n", 703 | " \n", 704 | " \n", 705 | " \n", 706 | " \n", 707 | " \n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | "
admin_1admin_2areaHabiomassDensityisototalBiomass
015351695.3881690.000850AFG299.115355
116199722.0987190.376596AFG75214.485758
219184545.3749010.087080AFG16070.197273
311090486.0362100.005928AFG536.407307
417297624.4050240.013252AFG3944.064107
\n", 740 | "
" 741 | ], 742 | "text/plain": [ 743 | " admin_1 admin_2 areaHa biomassDensity iso totalBiomass\n", 744 | "0 1 5 351695.388169 0.000850 AFG 299.115355\n", 745 | "1 1 6 199722.098719 0.376596 AFG 75214.485758\n", 746 | "2 1 9 184545.374901 0.087080 AFG 16070.197273\n", 747 | "3 1 10 90486.036210 0.005928 AFG 536.407307\n", 748 | "4 1 7 297624.405024 0.013252 AFG 3944.064107" 749 | ] 750 | }, 751 | "execution_count": 20, 752 | "metadata": {}, 753 | "output_type": "execute_result" 754 | } 755 | ], 756 | "source": [ 757 | "output_df.head()" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": 21, 763 | "metadata": {}, 764 | "outputs": [ 765 | { 766 | "data": { 767 | "text/plain": [ 768 | "14436" 769 | ] 770 | }, 771 | "execution_count": 21, 772 | "metadata": {}, 773 | "output_type": "execute_result" 774 | } 775 | ], 776 | "source": [ 777 | "len(output_df)" 778 | ] 779 | }, 780 | { 781 | "cell_type": "code", 782 | "execution_count": null, 783 | "metadata": {}, 784 | "outputs": [], 785 | "source": [ 786 | "output_df.keys()" 787 | ] 788 | }, 789 | { 790 | "cell_type": "code", 791 | "execution_count": null, 792 | "metadata": {}, 793 | "outputs": [], 794 | "source": [ 795 | "output_df = output_df.rename(index=str, columns={'admin_1':'id_1','admin_2':'id_2','biomassDensity':'biomassdensity','totalBiomass':'totalbiomass'})" 796 | ] 797 | }, 798 | { 799 | "cell_type": "code", 800 | "execution_count": null, 801 | "metadata": {}, 802 | "outputs": [], 803 | "source": [ 804 | "output_df.head()" 805 | ] 806 | }, 807 | { 808 | "cell_type": "code", 809 | "execution_count": null, 810 | "metadata": {}, 811 | "outputs": [], 812 | "source": [ 813 | "# Finally, save the file\n", 814 | "output_df.to_csv('./whrc_biomass.csv')" 815 | ] 816 | } 817 | ], 818 | "metadata": { 819 | "kernelspec": { 820 | "display_name": "Python 3", 821 | "language": "python", 822 | "name": "python3" 823 | }, 824 | "language_info": { 825 | "codemirror_mode": { 826 | "name": "ipython", 827 | "version": 3 828 | }, 829 | "file_extension": ".py", 830 | "mimetype": "text/x-python", 831 | "name": "python", 832 | "nbconvert_exporter": "python", 833 | "pygments_lexer": "ipython3", 834 | "version": "3.6.7" 835 | } 836 | }, 837 | "nbformat": 4, 838 | "nbformat_minor": 2 839 | } 840 | -------------------------------------------------------------------------------- /work/INGESTING_EARTH_ENGINE_ASSETS.md: -------------------------------------------------------------------------------- 1 | ## Pipeline for ingesting S3 geotiffs into an GCS and Earth Engine 2 | 3 | These are the notes documenting how to move tif data from S3 or Google cloud storage 4 | into Image Collections in Google's Earth Engine platform. 5 | 6 | ### Requirements 7 | 8 | [AWS command line interface](http://docs.aws.amazon.com/cli/latest/userguide/installing.html) 9 | 10 | [google cloud SDK](https://cloud.google.com/sdk/?utm_source=google&utm_medium=cpc&utm_campaign=2017-q1-cloud-emea-gcp-bkws-freetrial&gclid=CLK_keLP-dMCFQeeGwod0lkISw) 11 | 12 | [Google Earth Engine Python API](https://developers.google.com/earth-engine/command_line) 13 | 14 | ### Procedure 15 | 16 | S3 Bucket --> Google Cloud Storage --> to Google Earth Engine 17 | 18 | ## S3 19 | 20 | If you need to move files between buckets, for example lots of tif files: 21 | 22 | ```bash 23 | aws s3 cp s3://my_bucket/folder_path/ s3://other_bucket/ --recursive --includes "*.tif" 24 | ``` 25 | 26 | If you want to see the size of a bucket contents in S3, use the following command: 27 | `aws s3 ls s3://my_bucket/folder_path/ --human-readable --summarize` 28 | 29 | 30 | ## Google Cloud Storage 31 | 32 | (Assuming you have access to google cloud computing platform and a google cloud storage account.) 33 | 34 | The easiest way to move files between S3 and Google cloud storage is via the Google Cloud Platform interface `Transfer` under Storage. There, you can simply connect to public buckets 35 | and transfer to a target Google bucket. 36 | 37 | Note, in the transfer system, you specify the bucket root name as the source 38 | bucket, then, if your data are in a folder within that bucket, you can 39 | specify this path as a prefix. For example, imagine you wanted to transfer S3://my_bucket/sub_folder1/another_folder/my_image1.tif 40 | S3://my_bucket/sub_folder1/another_folder/my_image2.tif 41 | 42 | You would only specify `my_bucket` as the S3 target. And then `sub_folder1/another_folder` 43 | as the prefix. (Also keys if needed.) 44 | 45 | After specifying the destination Google Cloud Storage, and transfering, the files 46 | will move with exactly the same path as they had on the S3 bucket. 47 | E.g. You may specify `gs://my_google_bucket` as the destination, and start 48 | the transfer job. The files will automatically be placed at: 49 | `gs://my_google_bucket/sub_folder1/another_folder/my_image1.tif` and `gs://my_google_bucket/sub_folder1/another_folder/my_image2.tif` 50 | 51 | #### Another option is to use CLI tools: 52 | 53 | Authenticate your google cloud CLI: 54 | 55 | `gcloud auth login` 56 | 57 | Connect to your Google Cloud project 58 | 59 | e.g. `gcloud config set project gfw-apis` 60 | 61 | At this point, you should be able to access your google cloud resources, e.g. listing GCS buckets: 62 | 63 | ```bash 64 | $gsutil ls 65 | gs://asithappens/ 66 | gs://carbonloss_test/ 67 | gs://gfw-apis-analysis/ 68 | gs://gfw-apis-country/ 69 | gs://gfw-apis-truth/ 70 | gs://gfw_subscriptions_150407/ 71 | gs://gfw_subscriptions_backups/ 72 | gs://landsat-2015/ 73 | gs://landsat-cache/ 74 | gs://s3_2gee/ 75 | ``` 76 | 77 | If you need to connect to an S3 bucket that requires authentication, then 78 | create a `.boto` file in your users home directory, with the following contents: 79 | 80 | ``` 81 | [Credentials] 82 | aws_access_key_id=XXXXXXXXXXX 83 | aws_secret_access_key=XXXXXXXXXXXX 84 | 85 | ``` 86 | 87 | You should then be able to copy file(s) from S3 to GCS as follows: 88 | 89 | ``` 90 | gsutil -m cp -R s3://target_bucket/target_file gs://destination_bucket/ 91 | ``` 92 | Note, the -m flag enables parallel transfers. 93 | 94 | 95 | ## Earth Engine data ingress 96 | 97 | You will need to authenticte your earthengine command line tool: 98 | 99 | `earthengine authenticate` 100 | 101 | Once your data is in Google Cloud Storage, we can easily move it into Earth Engine. 102 | 103 | First, we need to list the files we are interested in moving: 104 | 105 | ```bash 106 | gsutil ls gs://s3_2gee/sam/carbon_budget 107 | gs://s3_2gee/sam/carbon_budget/bgc/ 108 | gs://s3_2gee/sam/carbon_budget/carbon/ 109 | gs://s3_2gee/sam/carbon_budget/deadwood/ 110 | gs://s3_2gee/sam/carbon_budget/litter/ 111 | gs://s3_2gee/sam/carbon_budget/soil/ 112 | gs://s3_2gee/sam/carbon_budget/total_carbon/ 113 | ``` 114 | 115 | e.g. imagine we wanted to create a list of all the tiff files in the soil folder: 116 | 117 | `gsutil ls gs://s3_2gee/sam/carbon_budget/soil > gcs_flist` 118 | 119 | 120 | ``` 121 | cat gcs_flist | head -n 3 122 | gs://s3_2gee/sam/carbon_budget/soil/00N_000E_soil.tif 123 | gs://s3_2gee/sam/carbon_budget/soil/00N_010E_soil.tif 124 | gs://s3_2gee/sam/carbon_budget/soil/00N_020E_soil.tif 125 | ``` 126 | 127 | Now we have a list of targets, we need to create a collection asset in Earth Engine 128 | to use as a write target. 129 | 130 | ```bash 131 | earthengine create collection users/benlaken/soil_carbon_collection 132 | ``` 133 | 134 | Next, we use the upload command to send individual images to the collection like so: 135 | 136 | ``` 137 | earthengine upload image --asset_id users/benlaken/soil_carbon_collection/00N_000E_soil gs://s3_2gee/sam/carbon_budget/soil/00N_000E_soil.tif 138 | ``` 139 | Note the name of the **destination** image must be specified after the collection name. 140 | 141 | Note, the **“projects/wri-datalab”** Earth Engine account has extended data, and 142 | should be used to ingest and hold Data Layers for WRI. Ask for write access to this 143 | account if you need to add large volumes of data to Global Forest Watch projects. 144 | 145 | To send all the image targets we can put all this together to create a bash script as follows: 146 | 147 | 148 | ```bash 149 | #!/bin/bash 150 | bucket="gs://s3_2gee/sam/carbon_budget/soil/" 151 | EE_user="users/benlaken/soil_carbon_collection" 152 | suffix=".tif" 153 | gsutil ls ${bucket} > gcs_flist 154 | cat gcs_flist | while read f 155 | do 156 | fname_short=${f#$bucket} 157 | fname_short=${fname_short%$suffix} 158 | earthengine upload image --asset_id ${EE_user}/${fname_short} ${f} 159 | done 160 | ``` 161 | -------------------------------------------------------------------------------- /work/Movie-tiles.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Creating `movie-tiles` with Earth Engine\n", 8 | "\n", 9 | "## [Tiled web map](https://en.wikipedia.org/wiki/Tiled_web_map)\n", 10 | "\n", 11 | "Most tiled web maps follow certain Google Maps conventions:\n", 12 | "\n", 13 | "- Tiles are 256x256 pixels\n", 14 | "- At the outer most zoom level, 0, the entire world can be rendered in a single map tile.\n", 15 | "- Each zoom level doubles in both dimensions, so a single tile is replaced by 4 tiles when zooming in. \n", 16 | "- The [Web Mercator](https://en.wikipedia.org/wiki/Web_Mercator_projection) projection is used, with latitude limits of around 85 degrees.\n", 17 | "\n", 18 | "OpenStreetMap, Google Maps, MapBox, etc. use the standard known as [Slippy Map Tilenames](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) or XYZ:\n", 19 | "\n", 20 | "- Tiles are 256 × 256 pixel PNG files\n", 21 | "- Each zoom level (z) is a directory, each column (x) is a subdirectory, and each tile (y) in that column is a file\n", 22 | "- Filename (url) format is `http://.../z/x/y.png`\n", 23 | "\n", 24 | "Other standards also exist such as the [Tile Map Service](https://en.wikipedia.org/wiki/Tile_Map_Service) or TMS.\n", 25 | "\n", 26 | "The only difference between XYZ and TMS is a flip in coordinates. TMS has its origin [0,0] at the bottom-left, while the origin for XYZ is located at the top-left.\n", 27 | "\n", 28 | "The conversion between tiles and coordinates has been described [here](https://www.maptiler.com/google-maps-coordinates-tile-bounds-projection/) ([source code](https://gist.github.com/maptiler/fddb5ce33ba995d5523de9afdf8ef118#file-globalmaptiles-py)). " 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 1, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "import math\n", 38 | "\n", 39 | "class GlobalMercator(object):\n", 40 | " \"\"\"\n", 41 | " TMS Global Mercator Profile\n", 42 | " ---------------------------\n", 43 | " Functions necessary for generation of tiles in Spherical Mercator projection,\n", 44 | " EPSG:900913 (EPSG:gOOglE, Google Maps Global Mercator), EPSG:3785, OSGEO:41001.\n", 45 | " Such tiles are compatible with Google Maps, Microsoft Virtual Earth, Yahoo Maps,\n", 46 | " UK Ordnance Survey OpenSpace API, ...\n", 47 | " and you can overlay them on top of base maps of those web mapping applications.\n", 48 | " \n", 49 | " Pixel and tile coordinates are in TMS notation (origin [0,0] in bottom-left).\n", 50 | " What coordinate conversions do we need for TMS Global Mercator tiles::\n", 51 | " LatLon <-> Meters <-> Pixels <-> Tile \n", 52 | " WGS84 coordinates Spherical Mercator Pixels in pyramid Tiles in pyramid\n", 53 | " lat/lon XY in metres XY pixels Z zoom XYZ from TMS \n", 54 | " EPSG:4326 EPSG:900913 \n", 55 | " .----. --------- -- TMS \n", 56 | " / \\ <-> | | <-> /----/ <-> Google \n", 57 | " \\ / | | /--------/ QuadTree \n", 58 | " ----- --------- /------------/ \n", 59 | " KML, public WebMapService Web Clients TileMapService\n", 60 | " What is the coordinate extent of Earth in EPSG:900913?\n", 61 | " [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]\n", 62 | " Constant 20037508.342789244 comes from the circumference of the Earth in meters,\n", 63 | " which is 40 thousand kilometers, the coordinate origin is in the middle of extent.\n", 64 | " In fact you can calculate the constant as: 2 * math.pi * 6378137 / 2.0\n", 65 | " $ echo 180 85 | gdaltransform -s_srs EPSG:4326 -t_srs EPSG:900913\n", 66 | " Polar areas with abs(latitude) bigger then 85.05112878 are clipped off.\n", 67 | " What are zoom level constants (pixels/meter) for pyramid with EPSG:900913?\n", 68 | " whole region is on top of pyramid (zoom=0) covered by 256x256 pixels tile,\n", 69 | " every lower zoom level resolution is always divided by two\n", 70 | " initialResolution = 20037508.342789244 * 2 / 256 = 156543.03392804062\n", 71 | " What is the difference between TMS and Google Maps/QuadTree tile name convention?\n", 72 | " The tile raster itself is the same (equal extent, projection, pixel size),\n", 73 | " there is just different identification of the same raster tile.\n", 74 | " Tiles in TMS are counted from [0,0] in the bottom-left corner, id is XYZ.\n", 75 | " Google placed the origin [0,0] to the top-left corner, reference is XYZ.\n", 76 | " Microsoft is referencing tiles by a QuadTree name, defined on the website:\n", 77 | " http://msdn2.microsoft.com/en-us/library/bb259689.aspx\n", 78 | " The lat/lon coordinates are using WGS84 datum, yeh?\n", 79 | " Yes, all lat/lon we are mentioning should use WGS84 Geodetic Datum.\n", 80 | " Well, the web clients like Google Maps are projecting those coordinates by\n", 81 | " Spherical Mercator, so in fact lat/lon coordinates on sphere are treated as if\n", 82 | " the were on the WGS84 ellipsoid.\n", 83 | " \n", 84 | " From MSDN documentation:\n", 85 | " To simplify the calculations, we use the spherical form of projection, not\n", 86 | " the ellipsoidal form. Since the projection is used only for map display,\n", 87 | " and not for displaying numeric coordinates, we don't need the extra precision\n", 88 | " of an ellipsoidal projection. The spherical projection causes approximately\n", 89 | " 0.33 percent scale distortion in the Y direction, which is not visually noticable.\n", 90 | " How do I create a raster in EPSG:900913 and convert coordinates with PROJ.4?\n", 91 | " You can use standard GIS tools like gdalwarp, cs2cs or gdaltransform.\n", 92 | " All of the tools supports -t_srs 'epsg:900913'.\n", 93 | " For other GIS programs check the exact definition of the projection:\n", 94 | " More info at http://spatialreference.org/ref/user/google-projection/\n", 95 | " The same projection is degined as EPSG:3785. WKT definition is in the official\n", 96 | " EPSG database.\n", 97 | " Proj4 Text:\n", 98 | " +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0\n", 99 | " +k=1.0 +units=m +nadgrids=@null +no_defs\n", 100 | " Human readable WKT format of EPGS:900913:\n", 101 | " PROJCS[\"Google Maps Global Mercator\",\n", 102 | " GEOGCS[\"WGS 84\",\n", 103 | " DATUM[\"WGS_1984\",\n", 104 | " SPHEROID[\"WGS 84\",6378137,298.2572235630016,\n", 105 | " AUTHORITY[\"EPSG\",\"7030\"]],\n", 106 | " AUTHORITY[\"EPSG\",\"6326\"]],\n", 107 | " PRIMEM[\"Greenwich\",0],\n", 108 | " UNIT[\"degree\",0.0174532925199433],\n", 109 | " AUTHORITY[\"EPSG\",\"4326\"]],\n", 110 | " PROJECTION[\"Mercator_1SP\"],\n", 111 | " PARAMETER[\"central_meridian\",0],\n", 112 | " PARAMETER[\"scale_factor\",1],\n", 113 | " PARAMETER[\"false_easting\",0],\n", 114 | " PARAMETER[\"false_northing\",0],\n", 115 | " UNIT[\"metre\",1,\n", 116 | " AUTHORITY[\"EPSG\",\"9001\"]]]\n", 117 | " \"\"\"\n", 118 | " \n", 119 | " def __init__(self, tileSize=256):\n", 120 | " \"Initialize the TMS Global Mercator pyramid\"\n", 121 | " self.tileSize = tileSize\n", 122 | " self.initialResolution = 2 * math.pi * 6378137 / self.tileSize\n", 123 | " # 156543.03392804062 for tileSize 256 pixels\n", 124 | " self.originShift = 2 * math.pi * 6378137 / 2.0\n", 125 | " # 20037508.342789244\n", 126 | " \n", 127 | " def MetersToLatLon(self, mx, my ):\n", 128 | " \"Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum\"\n", 129 | "\n", 130 | " lon = (mx / self.originShift) * 180.0\n", 131 | " lat = (my / self.originShift) * 180.0\n", 132 | "\n", 133 | " lat = 180 / math.pi * (2 * math.atan( math.exp( lat * math.pi / 180.0)) - math.pi / 2.0)\n", 134 | " return lat, lon\n", 135 | "\n", 136 | " def Resolution(self, zoom ):\n", 137 | " \"Resolution (meters/pixel) for given zoom level (measured at Equator)\"\n", 138 | " \n", 139 | " # return (2 * math.pi * 6378137) / (self.tileSize * 2**zoom)\n", 140 | " return self.initialResolution / (2**zoom)\n", 141 | " \n", 142 | " def PixelsToMeters(self, px, py, zoom):\n", 143 | " \"Converts pixel coordinates in given zoom level of pyramid to EPSG:900913\"\n", 144 | "\n", 145 | " res = self.Resolution( zoom )\n", 146 | " mx = px * res - self.originShift\n", 147 | " my = py * res - self.originShift\n", 148 | " return mx, my\n", 149 | " \n", 150 | "\n", 151 | " def TileBounds(self, tx, ty, zoom):\n", 152 | " \"Returns bounds of the given tile in EPSG:900913 coordinates\"\n", 153 | " \n", 154 | " minx, miny = self.PixelsToMeters( tx*self.tileSize, ty*self.tileSize, zoom )\n", 155 | " maxx, maxy = self.PixelsToMeters( (tx+1)*self.tileSize, (ty+1)*self.tileSize, zoom )\n", 156 | " return ( minx, miny, maxx, maxy )\n", 157 | " \n", 158 | " def TileLonLatBounds(self, tx, ty, zoom ):\n", 159 | " \"Returns bounds of the given tile in latutude/longitude using WGS84 datum\"\n", 160 | "\n", 161 | " bounds = self.TileBounds( tx, ty, zoom)\n", 162 | " minLat, minLon = self.MetersToLatLon(bounds[0], bounds[1])\n", 163 | " maxLat, maxLon = self.MetersToLatLon(bounds[2], bounds[3])\n", 164 | " \n", 165 | " return ( minLon, minLat, maxLon, maxLat )\n", 166 | " \n", 167 | " def GoogleTile(self, tx, ty, zoom):\n", 168 | " \"Converts TMS tile coordinates to Google Tile coordinates and vice versa\"\n", 169 | " \n", 170 | " # coordinate origin is moved from bottom-left to top-left corner of the extent\n", 171 | " return tx, (2**zoom - 1) - ty" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "## Exporting the `movie-tiles` to a Cloud Storage bucket" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 2, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "import numpy as np\n", 188 | "import math\n", 189 | "import ee\n", 190 | "\n", 191 | "GM = GlobalMercator()\n", 192 | "\n", 193 | "class ee_movie_tiles:\n", 194 | " \n", 195 | " def __init__(self, imageCollection, startDate, stopDate, minZoom, maxZoom, visParam, bucket_name, folder_path):\n", 196 | " \"\"\"\n", 197 | " Class used to get the datasets from Earth Engine\n", 198 | " Parameters\n", 199 | " ----------\n", 200 | " imageCollection: string\n", 201 | " Name of the Image Collection.\n", 202 | " startDate : string\n", 203 | " Starting data\n", 204 | " stopDate : string\n", 205 | " Stopping data\n", 206 | " minZoom : integer\n", 207 | " The minimum zoom level of the map tiles to export.\n", 208 | " maxZoom : integer\n", 209 | " The maximum zoom level of the map tiles to export.\n", 210 | " visParam: dictionary\n", 211 | " Image visualization parameters.\n", 212 | " bucket_name: string\n", 213 | " Google Cloud Bucket\n", 214 | " folder_path: string\n", 215 | " Folder path in the bucke.\n", 216 | " \"\"\"\n", 217 | " \n", 218 | " self.image_collection = imageCollection\n", 219 | " self.startDate = startDate\n", 220 | " self.stopDate = stopDate \n", 221 | " self.minZoom = minZoom\n", 222 | " self.maxZoom = maxZoom\n", 223 | " self.vis = visParam\n", 224 | " self.bucket = bucket_name\n", 225 | " self.path = folder_path\n", 226 | " \n", 227 | " def TileCoordinates (self,z):\n", 228 | " \"\"\"Tile coordinates for a given z\"\"\"\n", 229 | " return np.arange(int(np.sqrt(4**z)))\n", 230 | " \n", 231 | " def vis_param(self,image):\n", 232 | " \"\"\"Map visualization parameters\"\"\"\n", 233 | " return ee.Image(image.visualize(**self.vis))\n", 234 | " \n", 235 | " def export_movie(self):\n", 236 | " \n", 237 | " ## Define your collection\n", 238 | " self.collection = ee.ImageCollection(self.image_collection).filterDate(self.startDate,self.stopDate)\n", 239 | " \n", 240 | " ## Map visualization parameters\n", 241 | " self.collection = self.collection.map(self.vis_param)\n", 242 | " \n", 243 | " ## Get bounding box\n", 244 | " zVals = np.arange(minZoom,maxZoom+1)\n", 245 | " for z in zVals:\n", 246 | " xVals = self.TileCoordinates(z)\n", 247 | " yVals = self.TileCoordinates(z)\n", 248 | " for x in xVals:\n", 249 | " for y in yVals:\n", 250 | " # Get bounding box\n", 251 | " x_TMS, y_TMS = GM.GoogleTile(x,y,z)\n", 252 | " bbox = list(GM.TileLonLatBounds(x_TMS,y_TMS,z))\n", 253 | " \n", 254 | " # Replace 180.0 with 179.99\n", 255 | " bbox = [x if x != 180.0 else 179.99 for x in bbox]\n", 256 | " bbox = [x if x != -180.0 else -179.99 for x in bbox]\n", 257 | " \n", 258 | " ## Area of Interest\n", 259 | " self.geom = ee.Geometry.Rectangle(bbox)\n", 260 | " self.region = self.geom.bounds().getInfo()['coordinates'] \n", 261 | " \n", 262 | " ## Filter \n", 263 | " self.filtered = self.collection.filterBounds(self.geom)\n", 264 | " \n", 265 | " ## Export movie-tiles to Google Cloud Storage\n", 266 | " print('Exporting movie-tile to {}'.format(self.bucket+'/'+self.path+'/'+str(z)+'/'+str(x)+'/'+str(y)+'.mp4'))\n", 267 | " ee.batch.Export.video.toCloudStorage(\n", 268 | " collection = self.filtered,\n", 269 | " description = f'{z}{x}{y}',\n", 270 | " bucket= self.bucket,\n", 271 | " fileNamePrefix = self.path+'/'+str(z)+'/'+str(x)+'/'+str(y),\n", 272 | " dimensions = 256,\n", 273 | " framesPerSecond = 2,\n", 274 | " region = self.region).start()" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "### MODIS Nadir BRDF-Adjusted Reflectance, daily 500m ([gee](https://developers.google.com/earth-engine/datasets/catalog/MODIS_006_MCD43A4))\n", 282 | "- **Dataset Availability:**\n", 283 | " 2000-02-18T00:00:00 - Present\n", 284 | " \n", 285 | "As an example we export movi-tiles from a MODIS ImageCollection." 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 3, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "import ee\n", 295 | "ee.Initialize()\n", 296 | "\n", 297 | "# Image Collection\n", 298 | "imageCollection = 'MODIS/006/MCD43A4'\n", 299 | "# Start and stop of time series\n", 300 | "startDate = ee.Date('2017-09-01')\n", 301 | "stopDate = ee.Date('2017-10-01')\n", 302 | "# The zoom levels of the map tiles to export.\n", 303 | "minZoom = 0 \n", 304 | "maxZoom = 2\n", 305 | "# Image visualization parameters.\n", 306 | "visParam = {'bands':['Nadir_Reflectance_Band1', 'Nadir_Reflectance_Band4','Nadir_Reflectance_Band3'],'min':0,'max':4000.0, 'gamma': 1.4} \n", 307 | "# Google Cloud Bucket\n", 308 | "bucket_name = 'skydipper_materials' \n", 309 | "# Folder path in the bucket\n", 310 | "folder_path = 'movie-tiles/MODIS'" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 4, 316 | "metadata": {}, 317 | "outputs": [ 318 | { 319 | "name": "stdout", 320 | "output_type": "stream", 321 | "text": [ 322 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/0/0/0.mp4\n", 323 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/1/0/0.mp4\n", 324 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/1/0/1.mp4\n", 325 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/1/1/0.mp4\n", 326 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/1/1/1.mp4\n", 327 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/0/0.mp4\n", 328 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/0/1.mp4\n", 329 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/0/2.mp4\n", 330 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/0/3.mp4\n", 331 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/1/0.mp4\n", 332 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/1/1.mp4\n", 333 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/1/2.mp4\n", 334 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/1/3.mp4\n", 335 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/2/0.mp4\n", 336 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/2/1.mp4\n", 337 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/2/2.mp4\n", 338 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/2/3.mp4\n", 339 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/3/0.mp4\n", 340 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/3/1.mp4\n", 341 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/3/2.mp4\n", 342 | "Exporting movie-tile to skydipper_materials/movie-tiles/MODIS/2/3/3.mp4\n" 343 | ] 344 | } 345 | ], 346 | "source": [ 347 | "modis = ee_movie_tiles(imageCollection, startDate, stopDate, minZoom, maxZoom, visParam, bucket_name, folder_path)\n", 348 | "modis.export_movie()" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "## Renaming the `.mp4` files\n", 356 | "\n", 357 | "When exporting the movie-tiles to a Cloud Storage bucket Earth Engine adds a suffix (`ee-export-video` + task_number) at the end of each file name.\n", 358 | "\n", 359 | "To rename the existing files in one of our Cloud Storage buckets:" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": 5, 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "from google.cloud import storage" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 6, 374 | "metadata": {}, 375 | "outputs": [], 376 | "source": [ 377 | "def rename_blob(bucket_name, folder_path, privatekey_path):\n", 378 | " \"\"\"Renames a blob.\"\"\"\n", 379 | " storage_client = storage.Client.from_service_account_json(privatekey_path)\n", 380 | " bucket = storage_client.get_bucket(bucket_name)\n", 381 | "\n", 382 | " # List of files\n", 383 | " blobs = bucket.list_blobs(prefix='movie-tiles')\n", 384 | "\n", 385 | " # Rename '.mp4' files\n", 386 | " for blob in blobs:\n", 387 | " blob_name = blob.name\n", 388 | " \n", 389 | " if (\"ee-export-video\" in blob_name) and (blob_name[-4:] == '.mp4'):\n", 390 | "\n", 391 | " new_name = blob_name.split(\"ee-export-video\")[0]+'.mp4'\n", 392 | " \n", 393 | " blob = bucket.blob(blob_name)\n", 394 | " new_blob = bucket.rename_blob(blob, new_name)\n", 395 | " \n", 396 | " print('Blob {} has been renamed to {}'.format(\n", 397 | " blob_name, new_name))" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 7, 403 | "metadata": {}, 404 | "outputs": [ 405 | { 406 | "name": "stdout", 407 | "output_type": "stream", 408 | "text": [ 409 | "Blob movie-tiles/MODIS/1/0/0ee-export-video5923503551241692397.mp4 has been renamed to movie-tiles/MODIS/1/0/0.mp4\n", 410 | "Blob movie-tiles/MODIS/1/0/1ee-export-video5257982942738470626.mp4 has been renamed to movie-tiles/MODIS/1/0/1.mp4\n", 411 | "Blob movie-tiles/MODIS/1/1/0ee-export-video2710149886668104937.mp4 has been renamed to movie-tiles/MODIS/1/1/0.mp4\n", 412 | "Blob movie-tiles/MODIS/2/0/0ee-export-video1469477798348441596.mp4 has been renamed to movie-tiles/MODIS/2/0/0.mp4\n", 413 | "Blob movie-tiles/MODIS/2/0/1ee-export-video6312545042210566391.mp4 has been renamed to movie-tiles/MODIS/2/0/1.mp4\n", 414 | "Blob movie-tiles/MODIS/2/0/2ee-export-video310073161352993498.mp4 has been renamed to movie-tiles/MODIS/2/0/2.mp4\n", 415 | "Blob movie-tiles/MODIS/2/0/3ee-export-video7841597083449843129.mp4 has been renamed to movie-tiles/MODIS/2/0/3.mp4\n", 416 | "Blob movie-tiles/MODIS/2/1/0ee-export-video4376602904550588578.mp4 has been renamed to movie-tiles/MODIS/2/1/0.mp4\n", 417 | "Blob movie-tiles/MODIS/2/1/1ee-export-video4091285538011975658.mp4 has been renamed to movie-tiles/MODIS/2/1/1.mp4\n", 418 | "Blob movie-tiles/MODIS/2/1/2ee-export-video3724765032099689828.mp4 has been renamed to movie-tiles/MODIS/2/1/2.mp4\n", 419 | "Blob movie-tiles/MODIS/2/1/3ee-export-video5818797136314925611.mp4 has been renamed to movie-tiles/MODIS/2/1/3.mp4\n", 420 | "Blob movie-tiles/MODIS/2/2/0ee-export-video3239007720241041273.mp4 has been renamed to movie-tiles/MODIS/2/2/0.mp4\n", 421 | "Blob movie-tiles/MODIS/2/2/1ee-export-video8791341031889772600.mp4 has been renamed to movie-tiles/MODIS/2/2/1.mp4\n", 422 | "Blob movie-tiles/MODIS/2/2/2ee-export-video9165296920252802839.mp4 has been renamed to movie-tiles/MODIS/2/2/2.mp4\n", 423 | "Blob movie-tiles/MODIS/2/2/3ee-export-video97818050076747873.mp4 has been renamed to movie-tiles/MODIS/2/2/3.mp4\n", 424 | "Blob movie-tiles/MODIS/2/3/0ee-export-video3888981155064338026.mp4 has been renamed to movie-tiles/MODIS/2/3/0.mp4\n", 425 | "Blob movie-tiles/MODIS/2/3/1ee-export-video6327589092386887130.mp4 has been renamed to movie-tiles/MODIS/2/3/1.mp4\n", 426 | "Blob movie-tiles/MODIS/2/3/2ee-export-video8983421018963390180.mp4 has been renamed to movie-tiles/MODIS/2/3/2.mp4\n", 427 | "Blob movie-tiles/MODIS/2/3/3ee-export-video7917363161521161984.mp4 has been renamed to movie-tiles/MODIS/2/3/3.mp4\n" 428 | ] 429 | } 430 | ], 431 | "source": [ 432 | "bucket_name = 'skydipper_materials'\n", 433 | "folder_path = 'movie-tiles/MODIS'\n", 434 | "privatekey_path = \"/Users/keys/privatekey.json\"\n", 435 | "\n", 436 | "rename_blob(bucket_name, folder_path, privatekey_path)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "metadata": {}, 443 | "outputs": [], 444 | "source": [] 445 | } 446 | ], 447 | "metadata": { 448 | "kernelspec": { 449 | "display_name": "Python 3", 450 | "language": "python", 451 | "name": "python3" 452 | }, 453 | "language_info": { 454 | "codemirror_mode": { 455 | "name": "ipython", 456 | "version": 3 457 | }, 458 | "file_extension": ".py", 459 | "mimetype": "text/x-python", 460 | "name": "python", 461 | "nbconvert_exporter": "python", 462 | "pygments_lexer": "ipython3", 463 | "version": "3.6.8" 464 | } 465 | }, 466 | "nbformat": 4, 467 | "nbformat_minor": 2 468 | } 469 | -------------------------------------------------------------------------------- /work/RW_API_EE_to_layers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Guide to using RW to create tiled web maps\n", 8 | "\n", 9 | "This document shows how to go from a bunch of Earth Engine (EE) images, to styled webmaps using the RW API.\n", 10 | "You will need to have an authentication token for the RW API, and an EE account.\n", 11 | "\n", 12 | "\n", 13 | "#### Useful links\n", 14 | "* [RW API documentation](https://resource-watch.github.io/doc-api)\n", 15 | "* [RW example notebooks repo](https://github.com/resource-watch/notebooks)\n", 16 | "\n", 17 | "\n", 18 | "### Step 0. Check if the data is already in the API\n", 19 | "\n", 20 | "It may be that the data you want to include is already in the API. You should check first to see if this is so.\n", 21 | "E.g. You can see all of the datasets registerd for the `gfw` platform, for example with the below query:\n", 22 | "\n", 23 | "http://api.resourcewatch.org/v1/dataset?app=gfw&page[size]=100000\n", 24 | "\n", 25 | "Or, if you have an idea what the name of the dataset may be, you can search for it as follows:\n", 26 | "\n", 27 | "http://api.resourcewatch.org/v1/dataset?app=gfw&name=viirs\n", 28 | "\n", 29 | "If the dataset doesnt exist in the API, and you want to add it here are the steps to create a new dataset based on Earth Engine images.\n", 30 | "\n", 31 | "\n", 32 | "### Step 1. Earth Engine (images -> image collection)\n", 33 | "N.b. You can do the following via a script (e.g. see code [here](https://github.com/resource-watch/nrt-scripts/blob/master/utils/examples/GEE_upload_utils/src/__init__.py)), or via GUI interface on the EE JS editor.\n", 34 | "\n", 35 | "**Using the editor:**\n", 36 | "\n", 37 | "We will assume you have imported data into Earth Engine as a series of image assets.\n", 38 | "The next step is to create an empty Image Collection.\n", 39 | "\n", 40 | "![](./pics/empty_ic.png)\n", 41 | "\n", 42 | "You will then need to select your collection from the list of `Assets`, and click copy an image into this collection:\n", 43 | "\n", 44 | "![](./pics/copy_im_to_ic.png)\n", 45 | "\n", 46 | "Then specify an image asset, and select a target name for the image (if different from the original).\n", 47 | "\n", 48 | "![](./pics/add_im.png)\n", 49 | "\n", 50 | "click the new image you have added to the collection. This should open a new modal window:\n", 51 | "\n", 52 | "![](./pics/img_modal1.png)\n", 53 | "\n", 54 | "Next, you will need to add Properties to the image, so that the RW API can query the image collection. Specifically you will need to add `system:time_start` with a value in the format of `YYYY-MM-DD`. To do this, Click `+ Add property`, and add the new info. Then click `Done`.\n", 55 | "\n", 56 | "![](./pics/im_form_done.png)\n", 57 | "\n", 58 | "Repeat this process for all images required in the collection. Again, alternativley you can do this via a script as shown [here](https://github.com/resource-watch/nrt-scripts/blob/master/utils/examples/GEE_upload_utils/src/__init__.py).\n", 59 | "\n", 60 | "** N.b. You must ensure that the Image Collection Privacy is set to publicly accessible, via the `share` button on the Image Collection **\n", 61 | "\n", 62 | "![](./pics/ic_share.png)\n", 63 | "\n", 64 | "### Step 2. Register the dataset (EE image collection -> RW dataset)\n", 65 | "\n", 66 | "The next step is to register the dataset in the Resource Watch API. This is done via HTTP requests as described in the RW documentation.\n", 67 | "\n", 68 | "Register a dataset with a Post Request:\n", 69 | "\n", 70 | "```bash\n", 71 | "curl -X POST https://api.resourcewatch.org/v1/dataset \\\n", 72 | "-H \"Authorization: Bearer \" \\\n", 73 | "-H \"Content-Type: application/json\" -d \\\n", 74 | "'{\n", 75 | " \"connectorType\":\"rest\",\n", 76 | " \"provider\":\"gee\",\n", 77 | " \"tableName\": \"projects/wri-datalab/Gain_carbon_test\"\n", 78 | " \"application\":[\n", 79 | " \"gfw\"\n", 80 | " ],\n", 81 | " \"name\":\"AGB Gains\"\n", 82 | "}'\n", 83 | "```\n", 84 | "You should recieve a response of the registered dataset info, note the `status` field may say pending on initial response:\n", 85 | "\n", 86 | "```json\n", 87 | "{\n", 88 | " \"data\": {\n", 89 | " \"id\": \"b7a34457-1d8a-456e-af46-876e0b42fb96\",\n", 90 | " \"type\": \"dataset\",\n", 91 | " \"attributes\": {\n", 92 | " \"name\": \"AGB Gains\",\n", 93 | " \"slug\": \"AGB-Gains_1\",\n", 94 | " \"type\": \"raster\",\n", 95 | " \"subtitle\": null,\n", 96 | " \"application\": [\n", 97 | " \"gfw\"\n", 98 | " ],\n", 99 | " \"dataPath\": null,\n", 100 | " \"attributesPath\": null,\n", 101 | " \"connectorType\": \"rest\",\n", 102 | " \"provider\": \"gee\",\n", 103 | " \"userId\": \"5936af3802d342203186ab99\",\n", 104 | " \"connectorUrl\": null,\n", 105 | " \"tableName\": \"projects/wri-datalab/Gain_carbon_test\",\n", 106 | " \"status\": \"pending\",\n", 107 | " \"published\": false,\n", 108 | " \"overwrite\": false,\n", 109 | " \"verified\": false,\n", 110 | " \"blockchain\": {},\n", 111 | " \"mainDateField\": null,\n", 112 | " \"env\": \"production\",\n", 113 | " \"geoInfo\": false,\n", 114 | " \"protected\": false,\n", 115 | " \"legend\": {\n", 116 | " \"date\": [],\n", 117 | " \"region\": [],\n", 118 | " \"country\": [],\n", 119 | " \"nested\": []\n", 120 | " },\n", 121 | " \"clonedHost\": {},\n", 122 | " \"errorMessage\": null,\n", 123 | " \"taskId\": null,\n", 124 | " \"updatedAt\": \"2018-04-13T10:35:25.948Z\",\n", 125 | " \"widgetRelevantProps\": [],\n", 126 | " \"layerRelevantProps\": []\n", 127 | " }\n", 128 | " }\n", 129 | "}\n", 130 | "```\n", 131 | "\n", 132 | "You can use the ID (in this case `b7a34457-1d8a-456e-af46-876e0b42fb96`) together with the `dataset` endpoint to see the dataset info as needed and to check the `status` of the dataset. If successfully processed the status should read `\"status\": \"saved\"`.\n", 133 | "\n", 134 | "https://api.resourcewatch.org/v1/dataset/b7a34457-1d8a-456e-af46-876e0b42fb96\n", 135 | "\n", 136 | "Dataset objects cannot be visulised directly, since we want to make web map tiles from these data. To do that, we will need a final step, the creation of a **Layer** object in the API.\n", 137 | "\n", 138 | "### Creating a layer (Dataset --> unique visulization)\n", 139 | "\n", 140 | "To do this we will need to make another HTTP post to the API. We will need to include in it a way to iscolate an image from our colection, and to specifiy visulisation styles (using the [Styled Layer Descriptors - SLD - method in EE](https://developers.google.com/earth-engine/image_visualization)). More info on SLD can be found [here](http://docs.geoserver.org/stable/en/user/styling/sld/cookbook/).\n", 141 | "\n", 142 | "You may want to test/experiment with the visulisation in the Earth Engine playground in advance of creating a new layer. Note that in lieu of masking the data, you can set opacity on certain values (which has the same final effect).\n", 143 | "\n", 144 | "\n", 145 | "\n", 146 | "```bash\n", 147 | "curl -X POST https://api.resourcewatch.org/v1/dataset//layer \\\n", 148 | "-H \"Authorization: Bearer \" \\\n", 149 | "-H \"Content-Type: application/json\" -d \\\n", 150 | "{\"name\":\"test agb green 2018\",\n", 151 | "\"application\": [\n", 152 | "\"gfw\"\n", 153 | "],\n", 154 | "\"provider\": \"gee\",\n", 155 | "\"layerConfig\":{\t\n", 156 | "\t\"body\":{\n", 157 | "\t\t\"sldValue\": \"\",\n", 158 | "\t\t\"styleType\": \"sld\"\n", 159 | "\t\t},\n", 160 | "\t\"assetId\": \"projects/wri-datalab/Gain_carbon_test\",\n", 161 | "\t\"provider\": \"gee\",\n", 162 | "\t\"position\": \"last\",\n", 163 | "\t\"isImageCollection\":true,\n", 164 | "\t\"order\": 2018,\n", 165 | "\t\"filterDates\": [\"2008-01-01\",\"2019-01-01\"]\n", 166 | "\t\n", 167 | "}\n", 168 | "}\n", 169 | "\n", 170 | "```\n", 171 | "\n", 172 | "From which you will get back a registered layer response:\n", 173 | "\n", 174 | "```json\n", 175 | "{\n", 176 | " \"data\": {\n", 177 | " \"id\": \"c9e48a9f-2dca-4233-9400-0b5e4e07674f\",\n", 178 | " \"type\": \"layer\",\n", 179 | " \"attributes\": {\n", 180 | " \"name\": \"test agb green 2018\",\n", 181 | " \"slug\": \"test-agb-green-2018\",\n", 182 | " \"dataset\": \"b7a34457-1d8a-456e-af46-876e0b42fb96\",\n", 183 | " \"application\": [\n", 184 | " \"gfw\"\n", 185 | " ],\n", 186 | " \"iso\": [],\n", 187 | " \"provider\": \"gee\",\n", 188 | " \"userId\": \"57a0aa1071e394dd32ffe137\",\n", 189 | " \"default\": false,\n", 190 | " \"protected\": false,\n", 191 | " \"env\": \"production\",\n", 192 | " \"layerConfig\": {\n", 193 | " \"body\": {\n", 194 | " \"sldValue\": \"\",\n", 195 | " \"styleType\": \"sld\"\n", 196 | " },\n", 197 | " \"assetId\": \"projects/wri-datalab/Gain_carbon_test\",\n", 198 | " \"provider\": \"gee\",\n", 199 | " \"position\": \"last\",\n", 200 | " \"isImageCollection\": true,\n", 201 | " \"order\": 2018,\n", 202 | " \"filterDates\": [\n", 203 | " \"2008-01-01\",\n", 204 | " \"2019-01-01\"\n", 205 | " ]\n", 206 | " },\n", 207 | " \"legendConfig\": {},\n", 208 | " \"interactionConfig\": {},\n", 209 | " \"applicationConfig\": {},\n", 210 | " \"staticImageConfig\": {},\n", 211 | " \"updatedAt\": \"2018-04-13T13:20:43.963Z\"\n", 212 | " }\n", 213 | " }\n", 214 | "}\n", 215 | "```\n", 216 | "\n", 217 | "Each time you want to create a different visulisation of a dataset you will need to register a new layer object.\n", 218 | "\n", 219 | "Finally, you will be able to use the layers as a [tiled web map](https://en.wikipedia.org/wiki/Tiled_web_map), based on the layer `id`.\n", 220 | "\n", 221 | "https://api.resourcewatch.org/v1/layer/c9e48a9f-2dca-4233-9400-0b5e4e07674f/tile/gee/{z}/{x}/{y}" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 3, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "import folium" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 8, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "url =\"https://api.resourcewatch.org/v1/layer/c9e48a9f-2dca-4233-9400-0b5e4e07674f/tile/gee/{z}/{x}/{y}\"" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 10, 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "m = folium.Map(location=[-10.141931686131018, -55.283203125,], tiles='Open Street Map')\n", 249 | "m.add_tile_layer(tiles=url, max_zoom=12, min_zoom=1, attr='Custom tiles')\n", 250 | "\n", 251 | "m" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "![](./pics/web_map_eg.png)" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [] 267 | } 268 | ], 269 | "metadata": { 270 | "kernelspec": { 271 | "display_name": "Python 3", 272 | "language": "python", 273 | "name": "python3" 274 | }, 275 | "language_info": { 276 | "codemirror_mode": { 277 | "name": "ipython", 278 | "version": 3 279 | }, 280 | "file_extension": ".py", 281 | "mimetype": "text/x-python", 282 | "name": "python", 283 | "nbconvert_exporter": "python", 284 | "pygments_lexer": "ipython3", 285 | "version": "3.6.5" 286 | } 287 | }, 288 | "nbformat": 4, 289 | "nbformat_minor": 2 290 | } 291 | -------------------------------------------------------------------------------- /work/Recent_Images_Microservice_Tutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Recent Images Microservice\n", 8 | "\n", 9 | "This microservice can be used to request multiple tile urls, thumbnail urls and metadata from Google Earth Engine (GEE) in a fast, asynchronus way.\n", 10 | "\n", 11 | "### Overview\n", 12 | "\n", 13 | "On the GFW map page users can currently use the *Sentinel service* to inspect a single satellite image tile for a given area of interest when at high zoom level. The tile url of the image with the lowest ```cloud_score``` within a selected date range is requested from GEE and used on the front end to display the image.\n", 14 | "\n", 15 | "This microservice builds on this idea by requesting all images in the date range asynchronously as well as the corresponding thumbnail urls and associated metadata.\n", 16 | "\n", 17 | "***\n", 18 | "\n", 19 | "### Intended Useage\n", 20 | "\n", 21 | "The microservice is intented to be implemented in 3 stages on the front end:\n", 22 | "\n", 23 | "1) First, do a GET request for the metadata from a GEE image collection (ordered by CLOUDY_PIXEL_PERCENTAGE), as well as the tile url for the best image (i.e. the one with the lowest CLOUDY_PIXEL_PERCENTAGE score)\n", 24 | "\n", 25 | "2) Next POST a payload containing the ```'source'``` values of all images to the ```/v1/recent-tiles/tiles``` endpoint, which returns the tile urls for the associated images.\n", 26 | "\n", 27 | "3) Finally POST the same payload to the ```/v1/recent-tiles/thumbs``` endpoint, which returns all associated thumbnail urls.\n", 28 | "\n", 29 | "***\n", 30 | "\n", 31 | "\n", 32 | "### Endpoints\n", 33 | "\n", 34 | "The microservice has 3 endpoints:\n", 35 | "\n", 36 | "**/v1/recent-tiles** (GET)\n", 37 | "- *Returns tile metadata and a single tile url*\n", 38 | "- url params: ```lat, lon, start, end```\n", 39 | "\n", 40 | "**/v1/recent-tiles/tiles** (POST)\n", 41 | "- *Returns all tile urls*\n", 42 | "- payload: ```list of JSON objects containing 'source' value```\n", 43 | "\n", 44 | "**/v1/recent-tiles/thumbs** (POST)\n", 45 | "- *Returns all thumb urls*\n", 46 | "- payload: ```list of JSON objects containing 'source' value```\n", 47 | "\n", 48 | "\n", 49 | "\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 9, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "import folium\n", 59 | "import os\n", 60 | "import ee\n", 61 | "import json\n", 62 | "import requests\n", 63 | "import math\n", 64 | "from scipy import misc\n", 65 | "import shutil\n", 66 | "from pprint import pprint\n", 67 | "import requests\n", 68 | "from IPython.display import Image" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": { 74 | "collapsed": true 75 | }, 76 | "source": [ 77 | "# Request 1\n", 78 | "\n", 79 | "First query requests data for the following params, for example:\n", 80 | "\n", 81 | "```json\n", 82 | " url = f\"https://production-api.globalforestwatch.org/v1/recent-tiles\"\n", 83 | " params = {'lat':'-16.644','lon':'28.266', 'start':'2016-01-01', 'end': \"2016-01-08\"}\n", 84 | "```\n", 85 | "\n", 86 | "And returns an object of the following format:\n", 87 | "\n", 88 | "```json\n", 89 | "\"data\": [\n", 90 | " {\n", 91 | " \"attributes\": {\n", 92 | " \"boundary_tiles\": \"https://earthengine.googleapis.com/map/4b1b...\",\n", 93 | " \"cloud_score\": 13.1214,\n", 94 | " \"date_time\": \"2017-01-31 11:52:11Z\",\n", 95 | " \"instrument\": \"Sentinel-2A\",\n", 96 | " \"source\": \"COPERNICUS/S2/20170131T115211_20170131T115306_T28RCS\",\n", 97 | " \"thumbnail_url\": Null,\n", 98 | " \"tile_url\": Null\n", 99 | " },\n", 100 | " \"id\": Null,\n", 101 | " \"type\": \"recent_tiles_url\"\n", 102 | " }\n", 103 | "\n", 104 | "```\n", 105 | "\n", 106 | "There may be multiple values in the ```'data'``` list (expect one image per 8 days approx.)\n" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 10, 112 | "metadata": { 113 | "scrolled": false 114 | }, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "https://production-api.globalforestwatch.org/v1/recent-tiles?lat=-16.644&lon=28.266&start=2016-01-01&end=2016-9-01\n", 121 | "Returned 21 items. \n", 122 | "Preview of first 3...\n", 123 | "[{'attributes': {'boundary_url': 'https://earthengine.googleapis.com/map/4b1b9c6f82d50796562521502bc4d9a2/{z}/{x}/{y}?token=3b62d76749d0c096f8196be749040215',\n", 124 | " 'cloud_score': 0.4339,\n", 125 | " 'date_time': '2016-03-17 11:53:14Z',\n", 126 | " 'instrument': 'Sentinel-2A',\n", 127 | " 'source': 'COPERNICUS/S2/20160317T115314_20160317T172609_T28RCS',\n", 128 | " 'thumbnail_url': None,\n", 129 | " 'tile_url': 'https://earthengine.googleapis.com/map/daf140b2b3e97b1be86bb49893314cd0/{z}/{x}/{y}?token=8323e8319395285243f95913c69d9383'},\n", 130 | " 'id': None,\n", 131 | " 'type': 'recent_tiles_data'},\n", 132 | " {'attributes': {'boundary_url': 'https://earthengine.googleapis.com/map/4b1b9c6f82d50796562521502bc4d9a2/{z}/{x}/{y}?token=3b62d76749d0c096f8196be749040215',\n", 133 | " 'cloud_score': 3.1135,\n", 134 | " 'date_time': '2016-06-05 11:53:14Z',\n", 135 | " 'instrument': 'Sentinel-2A',\n", 136 | " 'source': 'COPERNICUS/S2/20160605T115314_20160605T200612_T28RCS',\n", 137 | " 'thumbnail_url': None,\n", 138 | " 'tile_url': None},\n", 139 | " 'id': None,\n", 140 | " 'type': 'recent_tiles_data'},\n", 141 | " {'attributes': {'boundary_url': 'https://earthengine.googleapis.com/map/4b1b9c6f82d50796562521502bc4d9a2/{z}/{x}/{y}?token=3b62d76749d0c096f8196be749040215',\n", 142 | " 'cloud_score': 5.6817,\n", 143 | " 'date_time': '2016-04-16 11:52:20Z',\n", 144 | " 'instrument': 'Sentinel-2A',\n", 145 | " 'source': 'COPERNICUS/S2/20160416T115220_20160416T172059_T28RCS',\n", 146 | " 'thumbnail_url': None,\n", 147 | " 'tile_url': None},\n", 148 | " 'id': None,\n", 149 | " 'type': 'recent_tiles_data'}]\n", 150 | "CPU times: user 22.4 ms, sys: 4.45 ms, total: 26.8 ms\n", 151 | "Wall time: 2.36 s\n" 152 | ] 153 | } 154 | ], 155 | "source": [ 156 | "%%time\n", 157 | "# Request metadata (6 month period)\n", 158 | "\n", 159 | "url = f\"https://production-api.globalforestwatch.org/v1/recent-tiles\"\n", 160 | "params= {'lat':'-16.644','lon':'28.266', 'start':'2016-01-01', 'end': \"2016-9-01\"}\n", 161 | "r = requests.get(url, params=params)\n", 162 | "print(r.url)\n", 163 | "r.status_code\n", 164 | "\n", 165 | "data = r.json().get('data')\n", 166 | "\n", 167 | "print(f\"Returned {len(data)} items. \\nPreview of first 3...\")\n", 168 | "pprint(data[0:3])" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 11, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "name": "stdout", 178 | "output_type": "stream", 179 | "text": [ 180 | "https://earthengine.googleapis.com/map/daf140b2b3e97b1be86bb49893314cd0/{z}/{x}/{y}?token=8323e8319395285243f95913c69d9383\n" 181 | ] 182 | } 183 | ], 184 | "source": [ 185 | "# In the returned data, one of the rows of data will contain a valid tile url, which\n", 186 | "# should be used to immediatley display an image...\n", 187 | "for row in data:\n", 188 | " if row.get('attributes').get('tile_url') is not None:\n", 189 | " tile_url = row.get('attributes').get('tile_url')\n", 190 | " print(tile_url)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 21, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "quick_map = folium.Map(location=[float(params['lon']), float(params['lat'])], zoom_start=9, tiles='Mapbox Bright' )\n", 200 | "quick_map.add_tile_layer(tiles=tile_url, max_zoom=19, min_zoom=6, attr=\"Live EE tiles\")\n", 201 | "#quick_map" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "![](./pics/ten1.png)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "# Creating Payload for POST\n", 216 | "\n", 217 | "On the front end, the response must be used to buld a list of objects containing the ```'source'``` values of the images in the collection.\n", 218 | "\n", 219 | "*e.g.*\n", 220 | "\n", 221 | "```json\n", 222 | "{'source_data':\n", 223 | " \n", 224 | " [\n", 225 | " {\"source\": \"COPERNICUS/S2/20170131T11...\"},\n", 226 | " {\"source\": \"COPERNICUS/S2/20170131T12...\"},\n", 227 | " {\"source\": \"COPERNICUS/S2/20170131T13...\"}, \n", 228 | " \n", 229 | " ...\n", 230 | " \n", 231 | " {\"source\": \"COPERNICUS/S2/20170131T1N...\"}\n", 232 | " ]\n", 233 | " }\n", 234 | "\n", 235 | "```" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 14, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "data": { 245 | "text/plain": [ 246 | "[{'source': 'COPERNICUS/S2/20160317T115314_20160317T172609_T28RCS'},\n", 247 | " {'source': 'COPERNICUS/S2/20160605T115314_20160605T200612_T28RCS'},\n", 248 | " {'source': 'COPERNICUS/S2/20160416T115220_20160416T172059_T28RCS'},\n", 249 | " {'source': 'COPERNICUS/S2/20160725T115222_20160725T184330_T28RCS'},\n", 250 | " {'source': 'COPERNICUS/S2/20160824T115222_20160824T200405_T28RCS'},\n", 251 | " {'source': 'COPERNICUS/S2/20160715T115224_20160715T200356_T28RCS'},\n", 252 | " {'source': 'COPERNICUS/S2/20160506T115225_20160506T184707_T28RCS'},\n", 253 | " {'source': 'COPERNICUS/S2/20160814T115222_20160814T184201_T28RCS'},\n", 254 | " {'source': 'COPERNICUS/S2/20160327T115315_20160327T172523_T28RCS'},\n", 255 | " {'source': 'COPERNICUS/S2/20160615T115223_20160615T183608_T28RCS'},\n", 256 | " {'source': 'COPERNICUS/S2/20160804T115238_20160804T200426_T28RCS'},\n", 257 | " {'source': 'COPERNICUS/S2/20160117T115842_20160117T172446_T28RCS'},\n", 258 | " {'source': 'COPERNICUS/S2/20160526T115225_20160526T184022_T28RCS'},\n", 259 | " {'source': 'COPERNICUS/S2/20160406T115217_20160406T185930_T28RCS'},\n", 260 | " {'source': 'COPERNICUS/S2/20160625T115243_20160625T200443_T28RCS'},\n", 261 | " {'source': 'COPERNICUS/S2/20160426T115223_20160426T184333_T28RCS'},\n", 262 | " {'source': 'COPERNICUS/S2/20160107T115414_20160108T134003_T28RCS'},\n", 263 | " {'source': 'COPERNICUS/S2/20160516T115226_20160516T185815_T28RCS'},\n", 264 | " {'source': 'COPERNICUS/S2/20160216T115313_20160216T173530_T28RCS'},\n", 265 | " {'source': 'COPERNICUS/S2/20160307T115313_20160307T211100_T28RCS'},\n", 266 | " {'source': 'COPERNICUS/S2/20160206T115314_20160206T190102_T28RCS'}]" 267 | ] 268 | }, 269 | "execution_count": 14, 270 | "metadata": {}, 271 | "output_type": "execute_result" 272 | } 273 | ], 274 | "source": [ 275 | "#example of how to construct the source list\n", 276 | "\n", 277 | "source_list = [{'source': d.get('attributes').get('source')} for d in data] \n", 278 | " \n", 279 | "source_list" 280 | ] 281 | }, 282 | { 283 | "cell_type": "markdown", 284 | "metadata": {}, 285 | "source": [ 286 | "### Request tiles using the payload\n", 287 | "\n", 288 | "*IMPORTANT*\n", 289 | "\n", 290 | "- The json must have the key ```'source_data'```\n", 291 | "\n", 292 | "- It must have the following header: ```headers={'Content-Type': 'application/json'}```" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 15, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "name": "stdout", 302 | "output_type": "stream", 303 | "text": [ 304 | "Returned tile urls for 21 ids.\n", 305 | "Previewing first 3 items...\n", 306 | "[{'source_id': 'COPERNICUS/S2/20160317T115314_20160317T172609_T28RCS',\n", 307 | " 'tile_url': 'https://earthengine.googleapis.com/map/daf140b2b3e97b1be86bb49893314cd0/{z}/{x}/{y}?token=772bdcd0abf69c06ccaaa27ed63f4bb5'},\n", 308 | " {'source_id': 'COPERNICUS/S2/20160605T115314_20160605T200612_T28RCS',\n", 309 | " 'tile_url': 'https://earthengine.googleapis.com/map/4a5c95e26f3561323033c9e78aeea33c/{z}/{x}/{y}?token=e9448686d8e6919c4880cfe73f3a9842'},\n", 310 | " {'source_id': 'COPERNICUS/S2/20160416T115220_20160416T172059_T28RCS',\n", 311 | " 'tile_url': 'https://earthengine.googleapis.com/map/ab0b08438d564fc4822fbb3fd50c7f9a/{z}/{x}/{y}?token=ddeef908a8c5bd5834315951f50ab8cd'}]\n", 312 | "CPU times: user 22.4 ms, sys: 3.18 ms, total: 25.6 ms\n", 313 | "Wall time: 1.2 s\n" 314 | ] 315 | } 316 | ], 317 | "source": [ 318 | "%%time\n", 319 | "\n", 320 | "url = f\"https://production-api.globalforestwatch.org/v1/recent-tiles/tiles\"\n", 321 | "payload = {'source_data': source_list}\n", 322 | "r = requests.post(url, data=json.dumps(payload), headers={'Content-Type': 'application/json'})\n", 323 | "r.status_code\n", 324 | "\n", 325 | "tile_data = r.json().get('data').get('attributes')\n", 326 | "print(f\"Returned tile urls for {len(tile_data)} ids.\\nPreviewing first 3 items...\")\n", 327 | "pprint(tile_data[0:3])" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "### Tiles Response\n", 335 | "\n", 336 | "At this stage the data now looks like:\n", 337 | "\n", 338 | "```json\n", 339 | "{'data': {\n", 340 | " 'attributes': [{\"source\": \"COPERNICUS/S2/20170131T11...\",\n", 341 | " \"tile_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 342 | " },{\n", 343 | " \"source\": \"COPERNICUS/S2/20170131T12...\",\n", 344 | " \"tiles_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 345 | " }, \n", 346 | " ...\n", 347 | " \n", 348 | " {\n", 349 | " \"source\": \"COPERNICUS/S2/20170131T1N...\",\n", 350 | " \"tiles_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 351 | " }],\n", 352 | " 'id': None,\n", 353 | " 'type': 'recent_tiles_url'}}\n", 354 | " }\n", 355 | "}\n", 356 | "```" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "### Thumbnail service\n", 364 | "\n", 365 | "The thumbnail images will be needed. We must request them seperatley." 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": 16, 371 | "metadata": {}, 372 | "outputs": [ 373 | { 374 | "name": "stdout", 375 | "output_type": "stream", 376 | "text": [ 377 | "[{'source': 'COPERNICUS/S2/20160317T115314_20160317T172609_T28RCS',\n", 378 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=f8d1d5c66a6d3e7dc30002e663242830&token=778f906027b341e1807751fb9cd7d241'},\n", 379 | " {'source': 'COPERNICUS/S2/20160605T115314_20160605T200612_T28RCS',\n", 380 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=fbcea1cc967a46d14c48c2367c0bf5a3&token=b093d5237097bb345384fe22a451435d'},\n", 381 | " {'source': 'COPERNICUS/S2/20160416T115220_20160416T172059_T28RCS',\n", 382 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=1bfbf427df20fd31c68ab9f575ff04f2&token=b8aef3b042394d922f71caf6027fd162'}]\n", 383 | "CPU times: user 20.2 ms, sys: 3.79 ms, total: 24 ms\n", 384 | "Wall time: 1.66 s\n" 385 | ] 386 | } 387 | ], 388 | "source": [ 389 | "%%time\n", 390 | "\n", 391 | "# Request thumbs using the payload\n", 392 | "\n", 393 | "url = f\"https://production-api.globalforestwatch.org/v1/recent-tiles/thumbs\"\n", 394 | "payload = {'source_data':source_list}\n", 395 | "r = requests.post(url, data=json.dumps(payload), headers={'Content-Type': 'application/json'})\n", 396 | "r.status_code\n", 397 | "\n", 398 | "thumb_data = r.json().get('data').get('attributes')\n", 399 | "\n", 400 | "pprint(thumb_data[0:3])" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": 17, 406 | "metadata": {}, 407 | "outputs": [ 408 | { 409 | "data": { 410 | "text/html": [ 411 | "" 412 | ], 413 | "text/plain": [ 414 | "" 415 | ] 416 | }, 417 | "execution_count": 17, 418 | "metadata": {}, 419 | "output_type": "execute_result" 420 | } 421 | ], 422 | "source": [ 423 | "# E.g. of a thumbnail\n", 424 | "\n", 425 | "for row in thumb_data:\n", 426 | " thumb_url = row.get('thumbnail_url')\n", 427 | " break\n", 428 | " \n", 429 | "Image(url=thumb_url) " 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "### Build a JSON object to work from\n", 437 | "\n", 438 | "At this stage you can construct a json object like the following:\n", 439 | "\n", 440 | "```json\n", 441 | "{'data': {\n", 442 | " 'attributes': [{\"source\": \"COPERNICUS/S2/20170131T11...\",\n", 443 | " \"thumbnail_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 444 | " },{\n", 445 | " \"source\": \"COPERNICUS/S2/20170131T12...\",\n", 446 | " \"thumbnail_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 447 | " }, \n", 448 | " ...\n", 449 | " \n", 450 | " {\n", 451 | " \"source\": \"COPERNICUS/S2/20170131T1N...\",\n", 452 | " \"thumbnail_url\": \"https://earthengine.googleapis.com/api/thumb?thumbid=...\"\n", 453 | " }],\n", 454 | " 'id': None,\n", 455 | " 'type': 'recent_thumbs_url'}}\n", 456 | " }\n", 457 | "}\n", 458 | "```" 459 | ] 460 | }, 461 | { 462 | "cell_type": "markdown", 463 | "metadata": {}, 464 | "source": [ 465 | "# Image Example for Map" 466 | ] 467 | }, 468 | { 469 | "cell_type": "code", 470 | "execution_count": 18, 471 | "metadata": {}, 472 | "outputs": [], 473 | "source": [ 474 | "# Returned First Image and Boundary\n", 475 | "\n", 476 | "dt = data[0].get('attributes').get('date_time')\n", 477 | "boundary = data[0].get('attributes').get('boundary_url')\n", 478 | "sentinel_image = data[0].get('attributes').get('tile_url')" 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": 22, 484 | "metadata": { 485 | "scrolled": false 486 | }, 487 | "outputs": [], 488 | "source": [ 489 | "sentinel_map = folium.Map(location=[float(params['lon']), float(params['lat'])], zoom_start=9, tiles='Mapbox Bright' )\n", 490 | "sentinel_map.add_tile_layer(tiles=sentinel_image, max_zoom=19, min_zoom=6, attr=\"Live EE tiles\")\n", 491 | "sentinel_map.add_tile_layer(tiles=boundary, max_zoom=19, min_zoom=6, attr=\"Live EE tiles\")\n", 492 | "#sentinel_map" 493 | ] 494 | }, 495 | { 496 | "cell_type": "markdown", 497 | "metadata": {}, 498 | "source": [ 499 | "![](./pics/ten2.png)" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": null, 505 | "metadata": { 506 | "collapsed": true 507 | }, 508 | "outputs": [], 509 | "source": [] 510 | } 511 | ], 512 | "metadata": { 513 | "kernelspec": { 514 | "display_name": "Python 3", 515 | "language": "python", 516 | "name": "python3" 517 | }, 518 | "language_info": { 519 | "codemirror_mode": { 520 | "name": "ipython", 521 | "version": 3 522 | }, 523 | "file_extension": ".py", 524 | "mimetype": "text/x-python", 525 | "name": "python", 526 | "nbconvert_exporter": "python", 527 | "pygments_lexer": "ipython3", 528 | "version": "3.6.4" 529 | } 530 | }, 531 | "nbformat": 4, 532 | "nbformat_minor": 2 533 | } 534 | -------------------------------------------------------------------------------- /work/authorize_notebook_server.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Authorise Notebook server to access Earth Engine\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This notebook is a reproduction of the workflow originally developed by **Datalab**, which describes how to setup a Google Datalab container in your local machine using Docker. \n", 15 | "\n", 16 | "You can check out the full tutorial by going to this link: https://developers.google.com/earth-engine/python_install-datalab-local" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": { 23 | "collapsed": false 24 | }, 25 | "outputs": [ 26 | { 27 | "name": "stdout", 28 | "output_type": "stream", 29 | "text": [ 30 | "The IPython Widgets library (version 5.2.2) is available on this server.\n" 31 | ] 32 | } 33 | ], 34 | "source": [ 35 | "# Code to check the IPython Widgets library.\n", 36 | "try:\n", 37 | " import ipywidgets\n", 38 | "except ImportError:\n", 39 | " print('The IPython Widgets library is not available on this server.\\n'\n", 40 | " 'Please see https://github.com/jupyter-widgets/ipywidgets '\n", 41 | " 'for information on installing the library.')\n", 42 | " raise\n", 43 | "print('The IPython Widgets library (version {0}) is available on this server.'.format(\n", 44 | " ipywidgets.__version__\n", 45 | "))" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "Next, check if the Earth Engine API is available on the server." 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 2, 58 | "metadata": { 59 | "collapsed": false 60 | }, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "The Earth Engine Python API (version 0.1.107) is available on this server.\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "# Code to check the Earth Engine API library.\n", 72 | "try:\n", 73 | " import ee\n", 74 | "except ImportError:\n", 75 | " print('The Earth Engine Python API library is not available on this server.\\n'\n", 76 | " 'Please see https://developers.google.com/earth-engine/python_install '\n", 77 | " 'for information on installing the library.')\n", 78 | " raise\n", 79 | "print('The Earth Engine Python API (version {0}) is available on this server.'.format(\n", 80 | " ee.__version__\n", 81 | "))" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Finally, check if the notebook server is authorized to access the Earth Engine backend servers." 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": { 95 | "collapsed": false 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "# Code to check if authorized to access Earth Engine.\n", 100 | "import io\n", 101 | "import os\n", 102 | "import urllib\n", 103 | "from IPython import display\n", 104 | "\n", 105 | "# Define layouts used by the form.\n", 106 | "row_wide_layout = ipywidgets.Layout(flex_flow=\"row nowrap\", align_items=\"center\", width=\"100%\")\n", 107 | "column_wide_layout = ipywidgets.Layout(flex_flow=\"column nowrap\", align_items=\"center\", width=\"100%\")\n", 108 | "column_auto_layout = ipywidgets.Layout(flex_flow=\"column nowrap\", align_items=\"center\", width=\"auto\")\n", 109 | "\n", 110 | "form_definition = {'form': None}\n", 111 | "response_box = ipywidgets.HTML('')\n", 112 | "\n", 113 | "def isAuthorized():\n", 114 | " try:\n", 115 | " ee.Initialize()\n", 116 | " test = ee.Image(0).getInfo()\n", 117 | " except:\n", 118 | " return False\n", 119 | " return True\n", 120 | "\n", 121 | "def ShowForm(auth_status_button, instructions):\n", 122 | " \"\"\"Show a form to the user.\"\"\"\n", 123 | " form_definition['form'] = ipywidgets.VBox([\n", 124 | " auth_status_button,\n", 125 | " instructions,\n", 126 | " ipywidgets.VBox([response_box], layout=row_wide_layout)\n", 127 | " ], layout=column_wide_layout)\n", 128 | " display.display(form_definition.get('form'))\n", 129 | "\n", 130 | "def ShowAuthorizedForm():\n", 131 | " \"\"\"Show a form for a server that is currently authorized to access Earth Engine.\"\"\"\n", 132 | " def revoke_credentials(sender):\n", 133 | " credentials = ee.oauth.get_credentials_path()\n", 134 | " if os.path.exists(credentials):\n", 135 | " os.remove(credentials)\n", 136 | " response_box.value = ''\n", 137 | " Init()\n", 138 | " \n", 139 | " auth_status_button = ipywidgets.Button(\n", 140 | " layout=column_wide_layout,\n", 141 | " disabled=True,\n", 142 | " description='The server is authorized to access Earth Engine',\n", 143 | " button_style='success',\n", 144 | " icon='check'\n", 145 | " )\n", 146 | " \n", 147 | " instructions = ipywidgets.Button(\n", 148 | " layout = row_wide_layout,\n", 149 | " description = 'Click here to revoke authorization',\n", 150 | " disabled = False,\n", 151 | " )\n", 152 | " instructions.on_click(revoke_credentials)\n", 153 | " \n", 154 | " ShowForm(auth_status_button, instructions)\n", 155 | "\n", 156 | "def ShowUnauthorizedForm():\n", 157 | " \"\"\"Show a form for a server that is not currently authorized to access Earth Engine.\"\"\"\n", 158 | "\n", 159 | " auth_status_button = ipywidgets.Button(\n", 160 | " layout=column_wide_layout,\n", 161 | " button_style='danger',\n", 162 | " description='The server is not authorized to access Earth Engine',\n", 163 | " disabled=True\n", 164 | " )\n", 165 | " \n", 166 | " auth_link = ipywidgets.HTML(\n", 167 | " 'Open Authentication Tab
'\n", 168 | " .format(url=ee.oauth.get_authorization_url()\n", 169 | " )\n", 170 | " )\n", 171 | " \n", 172 | " instructions = ipywidgets.VBox(\n", 173 | " [\n", 174 | " ipywidgets.HTML(\n", 175 | " 'Click on the link below to start the authentication and authorization process. '\n", 176 | " 'Once you have received an authorization code, use it to replace the '\n", 177 | " 'REPLACE_WITH_AUTH_CODE in the code cell below and run the cell.'\n", 178 | " ),\n", 179 | " auth_link,\n", 180 | " ],\n", 181 | " layout=column_auto_layout\n", 182 | " )\n", 183 | " \n", 184 | " ShowForm(auth_status_button, instructions)\n", 185 | " \n", 186 | "def Init():\n", 187 | " # If a form is currently displayed, close it.\n", 188 | " if form_definition.get('form'):\n", 189 | " form_definition['form'].close()\n", 190 | " # Display the appropriate form according to whether the server is authorized.\n", 191 | " if isAuthorized():\n", 192 | " ShowAuthorizedForm()\n", 193 | " else:\n", 194 | " ShowUnauthorizedForm()\n", 195 | " \n", 196 | "Init()" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "If the server **is authorized**, you do not need to run the next code cell.\n", 204 | "\n", 205 | "If the server **is not authorized**:\n", 206 | "\n", 207 | "1. Copy the authentication code generated in the previous step.\n", 208 | "2. Replace the REPLACE_WITH_AUTH_CODE string in the cell below with the authentication code.\n", 209 | "3. Run the code cell to save authentication credentials." 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 4, 215 | "metadata": { 216 | "collapsed": true 217 | }, 218 | "outputs": [], 219 | "source": [ 220 | "auth_code = 'REPLACE_WITH_AUTH_CODE'\n", 221 | "\n", 222 | "response_box = ipywidgets.HTML('')\n", 223 | "try:\n", 224 | " token = ee.oauth.request_token(auth_code.strip())\n", 225 | " ee.oauth.write_token(token)\n", 226 | " if isAuthorized():\n", 227 | " Init()\n", 228 | " else:\n", 229 | " response_box.value = '{0}'.format(\n", 230 | " 'The account was authenticated, but does not have permission to access Earth Engine.'\n", 231 | " )\n", 232 | "except Exception as e:\n", 233 | " response_box.value = '{0}'.format(e)\n", 234 | "response_box" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 5, 240 | "metadata": { 241 | "collapsed": false 242 | }, 243 | "outputs": [ 244 | { 245 | "data": { 246 | "text/html": [ 247 | "" 248 | ], 249 | "text/plain": [ 250 | "" 251 | ] 252 | }, 253 | "execution_count": 5, 254 | "metadata": {}, 255 | "output_type": "execute_result" 256 | } 257 | ], 258 | "source": [ 259 | "# Code to display an Earth Engine generated image.\n", 260 | "from IPython.display import Image\n", 261 | "\n", 262 | "url = ee.Image(\"CGIAR/SRTM90_V4\").getThumbUrl({'min':0, 'max':3000})\n", 263 | "Image(url=url)" 264 | ] 265 | } 266 | ], 267 | "metadata": { 268 | "kernelspec": { 269 | "display_name": "Python 3", 270 | "language": "python", 271 | "name": "python3" 272 | }, 273 | "language_info": { 274 | "codemirror_mode": { 275 | "name": "ipython", 276 | "version": 3 277 | }, 278 | "file_extension": ".py", 279 | "mimetype": "text/x-python", 280 | "name": "python", 281 | "nbconvert_exporter": "python", 282 | "pygments_lexer": "ipython3", 283 | "version": "3.5.3" 284 | }, 285 | "widgets": { 286 | "state": { 287 | "0054fdbcec154a209ad0f75f8a819511": { 288 | "views": [ 289 | { 290 | "cell_index": 8 291 | } 292 | ] 293 | }, 294 | "6c8eb8f265234dc69fb9e9dd4d1a2335": { 295 | "views": [ 296 | { 297 | "cell_index": 8 298 | } 299 | ] 300 | } 301 | }, 302 | "version": "1.2.0" 303 | } 304 | }, 305 | "nbformat": 4, 306 | "nbformat_minor": 2 307 | } 308 | -------------------------------------------------------------------------------- /work/biomass_loss_widget.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Remaking the GFW Climate widget on GFW flagship\n", 8 | "\n", 9 | "We need to hit the `biomass-loss` endpoint to gather data and create a new version of the old GFW climate widget. This widget will live on both the dashboards and the analysis window. In fact the main purpous of doing this is to recreate the functionality of the GFW climate analysis widget. Drawing a shape on the old gfw climate map used to produce an analysis widget like the following:\n", 10 | "\n", 11 | "\"drawing\"\n", 12 | "\n", 13 | "We only need the biomass loss bar chart, not the tree cover loss line, nor the summaries of biomass in general, as those are covered by different widgets/endpoints.\n", 14 | "\n", 15 | "Note, this widget also showed extra info on hover:\n", 16 | "\n", 17 | "\"drawing\"" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 41, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import requests\n", 27 | "from matplotlib import pyplot as plt\n", 28 | "from pprint import pprint\n", 29 | "import numpy as np\n", 30 | "%matplotlib inline" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 48, 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | "Response: 200\n", 43 | "{'data': {'attributes': {'areaHa': 1689472.3345972016,\n", 44 | " 'biomassLoss': 97116403.27015299,\n", 45 | " 'biomassLossByYear': {'2001': 8464722.879612226,\n", 46 | " '2002': 7271793.722802333,\n", 47 | " '2003': 4468985.840595964,\n", 48 | " '2004': 12457759.88843027,\n", 49 | " '2005': 11306576.812385125,\n", 50 | " '2006': 6981180.671165106,\n", 51 | " '2007': 7987787.319722255,\n", 52 | " '2008': 15049722.06189569,\n", 53 | " '2009': 2853289.312544776,\n", 54 | " '2010': 7144006.297632572,\n", 55 | " '2011': 2667643.4373683194,\n", 56 | " '2012': 3470526.750692178,\n", 57 | " '2013': 2404846.3246598886,\n", 58 | " '2014': 4587561.950646286},\n", 59 | " 'cLossByYear': {'2001': 4232361.44,\n", 60 | " '2002': 3635896.86,\n", 61 | " '2003': 2234492.92,\n", 62 | " '2004': 6228879.94,\n", 63 | " '2005': 5653288.41,\n", 64 | " '2006': 3490590.34,\n", 65 | " '2007': 3993893.66,\n", 66 | " '2008': 7524861.03,\n", 67 | " '2009': 1426644.66,\n", 68 | " '2010': 3572003.15,\n", 69 | " '2011': 1333821.72,\n", 70 | " '2012': 1735263.38,\n", 71 | " '2013': 1202423.16,\n", 72 | " '2014': 2293780.98},\n", 73 | " 'co2LossByYear': {'2001': 15532766.48,\n", 74 | " '2002': 13343741.48,\n", 75 | " '2003': 8200589.02,\n", 76 | " '2004': 22859989.38,\n", 77 | " '2005': 20747568.46,\n", 78 | " '2006': 12810466.55,\n", 79 | " '2007': 14657589.73,\n", 80 | " '2008': 27616239.98,\n", 81 | " '2009': 5235785.9,\n", 82 | " '2010': 13109251.56,\n", 83 | " '2011': 4895125.71,\n", 84 | " '2012': 6368416.6,\n", 85 | " '2013': 4412893,\n", 86 | " '2014': 8418176.2}},\n", 87 | " 'id': None,\n", 88 | " 'type': 'biomasses'}}\n" 89 | ] 90 | } 91 | ], 92 | "source": [ 93 | "# example of calling biomass loss data using the WDPA service\n", 94 | "\n", 95 | "threshold = 30 # tree canopy threshold\n", 96 | "wdpa_id = 352203 # ID of a shape\n", 97 | "start_date = '2001-01-01' # Start and end date should also be variables\n", 98 | "end_date = '2014-12-31'\n", 99 | "\n", 100 | "url = f'https://production-api.globalforestwatch.org/v1/biomass-loss/wdpa/{wdpa_id}?thresh={threshold}&period={start_date}%2C{end_date}'\n", 101 | "r = requests.get(url)\n", 102 | "print(f'Response: {r.status_code}')\n", 103 | "pprint(r.json())" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 34, 116 | "metadata": {}, 117 | "outputs": [ 118 | { 119 | "name": "stdout", 120 | "output_type": "stream", 121 | "text": [ 122 | "2001 15532766.48\n", 123 | "2002 13343741.48\n", 124 | "2003 8200589.02\n", 125 | "2004 22859989.38\n", 126 | "2005 20747568.46\n", 127 | "2006 12810466.55\n", 128 | "2007 14657589.73\n", 129 | "2008 27616239.98\n", 130 | "2009 5235785.9\n", 131 | "2010 13109251.56\n", 132 | "2011 4895125.71\n", 133 | "2012 6368416.6\n", 134 | "2013 4412893\n", 135 | "2014 8418176.2\n", 136 | "2015 12139335.52\n", 137 | "2016 20549220.86\n", 138 | "2017 31096767.46\n" 139 | ] 140 | } 141 | ], 142 | "source": [ 143 | "# Extract the year and value from the json object\n", 144 | "years = []\n", 145 | "co2_loss = []\n", 146 | "for key in r.json().get('data').get('attributes').get('co2LossByYear'):\n", 147 | " print(key, r.json().get('data').get('attributes').get('co2LossByYear')[key])\n", 148 | " years.append(key)\n", 149 | " co2_loss.append( r.json().get('data').get('attributes').get('co2LossByYear')[key])" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 42, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "co2_loss = np.array(co2_loss)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 55, 164 | "metadata": {}, 165 | "outputs": [ 166 | { 167 | "data": { 168 | "image/png": "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\n", 169 | "text/plain": [ 170 | "
" 171 | ] 172 | }, 173 | "metadata": { 174 | "needs_background": "light" 175 | }, 176 | "output_type": "display_data" 177 | } 178 | ], 179 | "source": [ 180 | "plt.figure(figsize=(10,10))\n", 181 | "plt.bar(years,co2_loss/1000000, color='#c9cace')\n", 182 | "plt.title(f\"CO₂ Loss by year for WDPA area {wdpa_id}\")\n", 183 | "plt.ylabel(\"MtCO₂\")\n", 184 | "plt.show()" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "*Note, I included a divison by 1,000,000 above to show my chart in units of MtCO₂ (million tonnes of CO₂). You should rely on the front-end library to make that transformation rather than doing it explicitly.*" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 68, 197 | "metadata": {}, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "'Over the period of 2001–2014 there were 241.99 MtCO₂ of gross carbon emissions due to tree cover loss in this region.'" 203 | ] 204 | }, 205 | "execution_count": 68, 206 | "metadata": {}, 207 | "output_type": "execute_result" 208 | } 209 | ], 210 | "source": [ 211 | "f\"Over the period of {start_date.split('-')[0]}–{end_date.split('-')[0]} there were {np.sum(co2_loss)/1000000:3.2f} MtCO₂ of gross carbon emissions due to tree cover loss in this region.\"" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "The above shows an example of a dynamic sentence you can create from these data." 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [] 227 | } 228 | ], 229 | "metadata": { 230 | "kernelspec": { 231 | "display_name": "Python 3", 232 | "language": "python", 233 | "name": "python3" 234 | }, 235 | "language_info": { 236 | "codemirror_mode": { 237 | "name": "ipython", 238 | "version": 3 239 | }, 240 | "file_extension": ".py", 241 | "mimetype": "text/x-python", 242 | "name": "python", 243 | "nbconvert_exporter": "python", 244 | "pygments_lexer": "ipython3", 245 | "version": "3.6.6" 246 | } 247 | }, 248 | "nbformat": 4, 249 | "nbformat_minor": 2 250 | } 251 | -------------------------------------------------------------------------------- /work/compute_biomass_gee.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Calculate biomass with GEE JS\n", 8 | "\n", 9 | " Documentation on how to create a microservice with GE engine. In this specific case it \n", 10 | " calculates biomass and biomass density paralelly for all admin 2 regions in the GADM table\n", 11 | " by using the map() function. It can be used as a model to calculate other attributes. \n", 12 | " We start off by getting the GADM admin 2 areas from https://gadm.org/download_world.html (click on download as six separate layers, then take only level_2). To preprocess the data, some simple python code is used to get rid of unnecessary columns.\n", 13 | " ```python \n", 14 | "df = gpd.read_file('/Users/nrigheriu/vizzuality/data/gadm36_level2_shp/gadm36_2.shp', encoding='utf-8')\n", 15 | "iso_col = ['blah'] * len(df)\n", 16 | "df['iso'] = iso_col\n", 17 | "df['admin_1'] = iso_col\n", 18 | "df['admin_2'] = iso_col\n", 19 | "def process_gid_2(gid_2):\n", 20 | " \"\"\"Return dict of iso (string), and admin_1 and admin_2 (ints) from gid_2 entry.\"\"\"\n", 21 | " try:\n", 22 | " iso, admin_1, tmp_admin_2 = gid_2.split('.')\n", 23 | " admin_2 = tmp_admin_2.split('_')[0]\n", 24 | " return {'iso':iso, 'admin_1':int(admin_1), 'admin_2':int(admin_2)}\n", 25 | " except:\n", 26 | " return {'iso':\"\", 'admin_1':\"\", 'admin_2':\"\"}\n", 27 | "gid_split = [process_gid_2(df['GID_2'][i]) for i in range(len(df['GID_2']))]\n", 28 | "df['iso'] = [gid_split[i]['iso'] for i in range(len(gid_split))] \n", 29 | "df['admin_1'] = [gid_split[i]['admin_1'] for i in range(len(gid_split))] \n", 30 | "df['admin_2'] = [gid_split[i]['admin_2'] for i in range(len(gid_split))] \n", 31 | "out_df = gpd.GeoDataFrame(df[['GID_2','iso', 'admin_1', 'admin_2']],geometry=df.geometry, crs=df.crs)\n", 32 | "!mkdir world_parsed_gid\n", 33 | "out_df.to_file(driver = 'ESRI Shapefile', filename='world_parsed_gid/world_parsed_gid.shp')\n", 34 | "```\n", 35 | "Afterwards the shapefile is inserted in http://mapshaper.org/ and the features are simplified to about 1% to make sure it will be accepted in earth engine when uploading it as a table (here it's named gadm36).\n", 36 | " Beware the script takes several hours (>4 hrs) to complete. For smaller scale experiments, try using a data set like regions of Spain first. \n", 37 | " Firstly start off by importing the gadm table containing admin level geometries and the \n", 38 | " other 2 tables whrc_carbon and forest_change to help in calculating biomass. \n", 39 | "```javascript\n", 40 | "var whrc_carbon = ee.ImageCollection(\"projects/wri-datalab/WHRC_CARBON\"),\n", 41 | " forest_change = ee.Image(\"projects/wri-datalab/HansenComposite_17\"),\n", 42 | " gadm36 = ee.FeatureCollection(\"projects/wri-datalab/gadm36\");\n", 43 | "\n", 44 | "```\n", 45 | " The function addBiomass() has a threshold parameter which corresponds to the dataset \n", 46 | " being passed (e.g. thresholdVal 10 for dataset th10) and adds a threshold property as \n", 47 | " well as biomass and biomassDensity density corresponding to this threshold value \n", 48 | " for all the entries in gadm36 (which consists of admin2 areas). \n", 49 | " After adding the threshold property, a mask with tree cover is created with help \n", 50 | " of the forest_change data. \n", 51 | " The addBiomassSum function gets the biomass_masked property and uses the Reducer function to create \n", 52 | " a new attribute called biomass. It does so with the feature.set() function. \n", 53 | " Afterwards, the biomassSum attribute is stored in a variable and used to calculate and add a new attribute\n", 54 | " called biomassDensity which is the biomassSum divided by the area. Finally the geometry of the features is \n", 55 | " deleted by setting it to null because it isn't needed anymore.\n", 56 | " \n", 57 | " NOTE: feature properties/attributes cannot be printed inside the map() function because it is executed \n", 58 | " in paralell on different machines. The code has to be written and trusted that it works or debugged in \n", 59 | " a different way. \n", 60 | "```javascript\n", 61 | "var addBiomass = function(thresholdVal) {\n", 62 | " var addThreshold = function(feature) {\n", 63 | " return feature.set({threshold: thresholdVal});\n", 64 | " };\n", 65 | " var addArea = function(feature) {\n", 66 | " return feature.set({areaHa: feature.geometry().area().divide(100 * 100)});\n", 67 | " };\n", 68 | " var dataset = gadm36.map(addThreshold);\n", 69 | " dataset = dataset.map(addArea);\n", 70 | " var tree_mask = forest_change.select('tree_' + thresholdVal).gt(0);\n", 71 | " var biomass_masked = whrc_carbon.max().multiply(ee.Image.pixelArea().divide(10000)).mask(tree_mask);\n", 72 | " var addBiomassSum = function(feature) {\n", 73 | " feature = feature.set({biomass: biomass_masked.reduceRegion({\n", 74 | " reducer: ee.Reducer.sum().unweighted(),\n", 75 | " geometry: feature.geometry(),\n", 76 | " bestEffort: true,\n", 77 | " scale:30 }).get('b1')}); //The actual calculated biomass is in the attribute b1\n", 78 | " var areaHa = ee.Number(feature.get('areaHa'));\n", 79 | " var biomassSum = ee.Number(feature.get('biomass'));\n", 80 | " feature = feature.set({biomassDensity: biomassSum.divide(areaHa)})\n", 81 | " .setGeometry(null); \n", 82 | " return feature; \n", 83 | " };\n", 84 | " dataset = dataset.map(addBiomassSum);\n", 85 | " return dataset;\n", 86 | "}; \n", 87 | "```\n", 88 | " To have a final table with each original values duplicated for each of the threshold values,\n", 89 | " multiple datasets are created and merged. Finally, the table will consist of 7 times \n", 90 | " more rows (as there are 7 different threshold values), where the extra added rows contain\n", 91 | " the same data as the original ones, except the values indicating threshold, \n", 92 | " biomass and biomassDensity. \n", 93 | "```javascript\n", 94 | "var th10 = addBiomass(10);\n", 95 | "var th15 = addBiomass(15);\n", 96 | "var th20 = addBiomass(20);\n", 97 | "var th25 = addBiomass(25);\n", 98 | "var th30 = addBiomass(30);\n", 99 | "var th50 = addBiomass(50);\n", 100 | "var th75 = addBiomass(75);\n", 101 | "var final_table = th10.merge(th15);\n", 102 | "final_table = final_table.merge(th20);\n", 103 | "final_table = final_table.merge(th25);\n", 104 | "final_table = final_table.merge(th30);\n", 105 | "final_table = final_table.merge(th50);\n", 106 | "final_table = final_table.merge(th75);\n", 107 | "```\n", 108 | "The table needs to be exported, and not printed because it is too large to be printed. \n", 109 | "```javascript\n", 110 | "//print(gadm36); //don't print or else it'll cause scaling errors\n", 111 | "Export.table.toCloudStorage({\n", 112 | " collection: final_table,\n", 113 | " description: 'gadm36BiomassTHs',\n", 114 | " fileFormat: 'CSV'\n", 115 | "});\n", 116 | "```" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [] 125 | } 126 | ], 127 | "metadata": { 128 | "kernelspec": { 129 | "display_name": "Python 3", 130 | "language": "python", 131 | "name": "python3" 132 | }, 133 | "language_info": { 134 | "codemirror_mode": { 135 | "name": "ipython", 136 | "version": 3 137 | }, 138 | "file_extension": ".py", 139 | "mimetype": "text/x-python", 140 | "name": "python", 141 | "nbconvert_exporter": "python", 142 | "pygments_lexer": "ipython3", 143 | "version": "3.6.6" 144 | } 145 | }, 146 | "nbformat": 4, 147 | "nbformat_minor": 2 148 | } 149 | -------------------------------------------------------------------------------- /work/data/360x114global_short.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/360x114global_short.zip -------------------------------------------------------------------------------- /work/data/RGB.byte.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/RGB.byte.tif -------------------------------------------------------------------------------- /work/data/TM_WORLD_BORDERS_SIMPL-0/Readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/TM_WORLD_BORDERS_SIMPL-0/Readme.txt -------------------------------------------------------------------------------- /work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.dbf -------------------------------------------------------------------------------- /work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.prj: -------------------------------------------------------------------------------- 1 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] -------------------------------------------------------------------------------- /work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.shp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.shp -------------------------------------------------------------------------------- /work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.shx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/TM_WORLD_BORDERS_SIMPL-0/TM_WORLD_BORDERS_SIMPL-0.3.shx -------------------------------------------------------------------------------- /work/data/eurasia.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | 11 | [40.08123779296875, 43.550968170166016], 12 | [40.69596862792969, 43.54301452636719], 13 | [41.59748458862305, 43.22150802612305], 14 | [42.85519790649414, 43.17776107788086], 15 | [43.207218170166016, 42.934017181396484], 16 | [43.83394241333008, 42.729225158691406], 17 | [45.165122985839844, 42.70332717895508], 18 | [45.760276794433594, 42.47791290283203], 19 | [45.63797378540039, 42.220191955566406], 20 | [45.98693084716797, 42.028602600097656], 21 | [46.77436065673828, 41.795616149902344], 22 | [47.576171875, 41.211307525634766], 23 | [47.915470123291016, 41.2249870300293], 24 | [48.37832260131836, 41.57471466064453], 25 | [48.77095031738281, 42.04534912109375], 26 | [49.760623931884766, 42.710758209228516], 27 | [49.32215881347656, 43.300235748291016], 28 | [49.03861999511719, 43.81541442871094], 29 | [48.96474075317383, 44.287540435791016], 30 | [48.6861572265625, 44.75434494018555], 31 | [49.44831466674805, 45.5303840637207], 32 | [50.03850173950195, 45.85847854614258], 33 | [49.46200180053711, 46.10921096801758], 34 | [49.22252655029297, 46.34630584716797], 35 | [48.49443054199219, 46.667205810546875], 36 | [49.02720642089844, 46.776092529296875], 37 | [48.57249069213867, 47.36554718017578], 38 | [48.037349700927734, 47.78262710571289], 39 | [47.1446418762207, 47.81214141845703], 40 | [47.121238708496094, 48.27207565307617], 41 | [46.49916076660156, 48.41749572753906], 42 | [46.77887725830078, 48.93665313720703], 43 | [47.04374694824219, 49.10360336303711], 44 | [46.802215576171875, 49.36499786376953], 45 | [46.93138122558594, 49.86582946777344], 46 | [47.34526824951172, 50.075828552246094], 47 | [47.62735366821289, 50.45735168457031], 48 | [48.446659088134766, 49.81735610961914], 49 | [48.91471481323242, 50.03276824951172], 50 | [48.697486877441406, 50.59193420410156], 51 | [49.43922805786133, 50.866451263427734], 52 | [49.419715881347656, 51.08207702636719], 53 | [50.264163970947266, 51.27777099609375], 54 | [50.62138366699219, 51.64485549926758], 55 | [51.2752685546875, 51.683876037597656], 56 | [51.711936950683594, 51.461936950683594], 57 | [51.917076110839844, 51.68651580810547], 58 | [52.365272521972656, 51.75916290283203], 59 | [52.53068923950195, 51.484153747558594], 60 | [53.423744201660156, 51.49263381958008], 61 | [53.6755485534668, 51.22929382324219], 62 | [54.647216796875, 51.03694152832031], 63 | [55.69248962402344, 50.532493591308594], 64 | [56.529991149902344, 51.07471466064453], 65 | [57.12735366821289, 51.084712982177734], 66 | [57.3559684753418, 50.903743743896484], 67 | [57.75499725341797, 51.08249282836914], 68 | [58.57291030883789, 51.0634651184082], 69 | [58.94999313354492, 50.68221664428711], 70 | [59.81440353393555, 50.5462760925293], 71 | [59.98790740966797, 50.843048095703125], 72 | [60.76999282836914, 50.66123962402344], 73 | [61.381378173828125, 50.783607482910156], 74 | [61.49082946777344, 51.42499542236328], 75 | [60.53610610961914, 51.62749099731445], 76 | [60.4627685546875, 51.815826416015625], 77 | [59.91756057739258, 52.06283950805664], 78 | [60.15325164794922, 52.279541015625], 79 | [59.85582733154297, 52.49054718017578], 80 | [58.789302825927734, 52.45068359375], 81 | [58.901241302490234, 53.05610275268555], 82 | [58.92152404785156, 53.93290710449219], 83 | [59.28013229370117, 54.16165542602539], 84 | [59.805824279785156, 54.24061584472656], 85 | [59.67610549926758, 54.48707580566406], 86 | [59.975685119628906, 54.786521911621094], 87 | [59.638328552246094, 54.91172790527344], 88 | [59.24790954589844, 54.61325454711914], 89 | [57.97026824951172, 54.388187408447266], 90 | [57.18346405029297, 54.804019927978516], 91 | [57.13527297973633, 55.08568572998047], 92 | [57.43374252319336, 55.32721710205078], 93 | [58.30499267578125, 55.17540740966797], 94 | [59.010276794433594, 55.25249481201172], 95 | [59.46776580810547, 55.462215423583984], 96 | [59.19554138183594, 56.02027130126953], 97 | [57.44776916503906, 56.13541030883789], 98 | [57.32166290283203, 56.482765197753906], 99 | [57.559715270996094, 56.888328552246094], 100 | [58.10478210449219, 57.125858306884766], 101 | [57.936241149902344, 57.32860565185547], 102 | [58.198875427246094, 57.68915557861328], 103 | [58.793609619140625, 57.714996337890625], 104 | [58.60096740722656, 58.00568771362305], 105 | [59.44912338256836, 58.48804473876953], 106 | [59.083465576171875, 58.760276794433594], 107 | [59.182281494140625, 59.163673400878906], 108 | [58.32346725463867, 59.485965728759766], 109 | [58.98249053955078, 59.94999694824219], 110 | [59.47359848022461, 60.809574127197266], 111 | [59.26277160644531, 61.213253021240234], 112 | [59.34137725830078, 61.85694122314453], 113 | [59.602352142333984, 62.37290954589844], 114 | [59.22595977783203, 63.03554916381836], 115 | [59.61554718017578, 63.951934814453125], 116 | [59.65068817138672, 64.77804565429688], 117 | [60.08694076538086, 65.04290771484375], 118 | [60.627628326416016, 64.88540649414062], 119 | [61.2359619140625, 65.1834487915039], 120 | [61.58221435546875, 65.55165100097656], 121 | [62.52638244628906, 65.84220886230469], 122 | [63.408599853515625, 66.45277404785156], 123 | [65.11151123046875, 66.89839172363281], 124 | [65.22360229492188, 67.14610290527344], 125 | [66.10887145996094, 67.48123168945312], 126 | [65.43415069580078, 67.91984558105469], 127 | [65.29512023925781, 68.2623519897461], 128 | [65.65228271484375, 68.5572738647461], 129 | [64.83256530761719, 69.15213012695312] 130 | ] 131 | } 132 | } 133 | ] 134 | } -------------------------------------------------------------------------------- /work/data/gadm28_countries.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/gadm28_countries.zip -------------------------------------------------------------------------------- /work/data/modis_example.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/data/modis_example.nc -------------------------------------------------------------------------------- /work/mapBiomass_tiles.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Brazil Biomass tiles\n", 8 | "\n", 9 | "January 2018\n", 10 | "\n", 11 | "\n", 12 | "`https://storage.googleapis.com/wri-public/mapbiomass/tiles/v4////.png`\n", 13 | "\n", 14 | "\n", 15 | "```\n", 16 | "\n", 17 | "17 bands, one for each year (2000 to 2016)\n", 18 | "Ideally, we want to create tiles for each year. Users can select them in a menu, but not animate over time\n", 19 | "We will not do any analysis on the data at this point, only visualization\n", 20 | "Here are the classes we want:\n", 21 | "Natural forest – values 3, 4, and 5, color #16633D\n", 22 | "Planted forest – value 9, color #DB84D7\n", 23 | "Non-forest wetlands – value 11, color #29D2AB\n", 24 | "Grassland – value 12, color #C3ED61\n", 25 | "Other non-forest vegetation – value 13, #6EA224\n", 26 | "Pasture – value 15, color #CD7D28\n", 27 | "Agriculture – value 18, color #FBF0AB\n", 28 | "Pasture or Agriculture – value 21, color #F4D634\n", 29 | "Non-vegetated areas – values 22, 23, 24, and 25, color #D1D3D3\n", 30 | "Water bodies – value 26, color #2784BC\n", 31 | "Unobserved – value 27. Would prefer to leave this \"blank\" in the tiles, but if needed, color #5C6060\n", 32 | "I don't think there are pixels with values 1, 2, 10, or 14, but let me know if that's not true and I'll let you know which class they belong to\n", 33 | "I made these colors up, so if possible would be great to do one year and then see how it looks before we do the rest. Would that work?\n", 34 | " ```" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 16, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "import requests\n", 44 | "import math\n", 45 | "import maya\n", 46 | "import numpy as np\n", 47 | "import imageio\n", 48 | "import matplotlib.pyplot as plt\n", 49 | "import folium\n", 50 | "%matplotlib inline" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 34, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "data": { 60 | "image/png": "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\n", 61 | "text/plain": [ 62 | "" 63 | ] 64 | }, 65 | "metadata": {}, 66 | "output_type": "display_data" 67 | } 68 | ], 69 | "source": [ 70 | "tile_url='https://storage.googleapis.com/wri-public/mapbiomass/tiles/v4/2007/4/5/7.png'\n", 71 | "im_arrays = imageio.imread(tile_url)\n", 72 | "\n", 73 | "f, (ax1, ax2) = plt.subplots(1, 2, sharey=False)\n", 74 | "f.set_size_inches(10,5)\n", 75 | "ax1.set_title((f\"max={im_arrays.max()} min={im_arrays.min()}\"\n", 76 | " f\"\\n non-zero pixels = {len(im_arrays.flatten()[(im_arrays.flatten() > 0)]) / len(im_arrays.flatten()) * 100.:3.2f}%\")\n", 77 | " )\n", 78 | "ax1.imshow(im_arrays, cmap='Oranges')\n", 79 | "ax2.set_title(f'Histogram')\n", 80 | "ax2.hist(im_arrays.flatten(), normed=True)\n", 81 | "#ax2.set_yscale('log', nonposy='clip')\n", 82 | "plt.show()\n" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 17, 95 | "metadata": {}, 96 | "outputs": [ 97 | { 98 | "data": { 99 | "text/html": [ 100 | "
" 101 | ], 102 | "text/plain": [ 103 | "" 104 | ] 105 | }, 106 | "execution_count": 17, 107 | "metadata": {}, 108 | "output_type": "execute_result" 109 | } 110 | ], 111 | "source": [ 112 | "basemap = \"Mapbox Bright\"\n", 113 | "\n", 114 | "map = folium.Map(location=[28.29, -16.6], zoom_start=3, tiles=basemap)\n", 115 | "tileset=r\"https://storage.googleapis.com/wri-public/mapbiomass/tiles/v4/2000/{z}/{x}/{y}.png\"\n", 116 | "map.add_tile_layer(tiles=tileset, max_zoom=12,min_zoom=1, attr='Custom tiles')\n", 117 | "map" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 36, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "folium.TileLayer?" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [] 135 | } 136 | ], 137 | "metadata": { 138 | "kernelspec": { 139 | "display_name": "Python 3", 140 | "language": "python", 141 | "name": "python3" 142 | }, 143 | "language_info": { 144 | "codemirror_mode": { 145 | "name": "ipython", 146 | "version": 3 147 | }, 148 | "file_extension": ".py", 149 | "mimetype": "text/x-python", 150 | "name": "python", 151 | "nbconvert_exporter": "python", 152 | "pygments_lexer": "ipython3", 153 | "version": "3.6.4" 154 | } 155 | }, 156 | "nbformat": 4, 157 | "nbformat_minor": 2 158 | } 159 | -------------------------------------------------------------------------------- /work/pics/Jamaica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/Jamaica.png -------------------------------------------------------------------------------- /work/pics/add_im.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/add_im.png -------------------------------------------------------------------------------- /work/pics/birds_se.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/birds_se.png -------------------------------------------------------------------------------- /work/pics/birds_sr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/birds_sr.png -------------------------------------------------------------------------------- /work/pics/copy_im_to_ic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/copy_im_to_ic.png -------------------------------------------------------------------------------- /work/pics/crs_perm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/crs_perm.png -------------------------------------------------------------------------------- /work/pics/empty_ic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/empty_ic.png -------------------------------------------------------------------------------- /work/pics/florida.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/florida.png -------------------------------------------------------------------------------- /work/pics/gfw_biomass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/gfw_biomass.png -------------------------------------------------------------------------------- /work/pics/gfwc_analysis_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/gfwc_analysis_hover.png -------------------------------------------------------------------------------- /work/pics/ic_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/ic_share.png -------------------------------------------------------------------------------- /work/pics/im_form_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/im_form_done.png -------------------------------------------------------------------------------- /work/pics/img_modal1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/img_modal1.png -------------------------------------------------------------------------------- /work/pics/mammals_se.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/mammals_se.png -------------------------------------------------------------------------------- /work/pics/mammals_sr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/mammals_sr.png -------------------------------------------------------------------------------- /work/pics/old_gfw_climate_widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/old_gfw_climate_widget.png -------------------------------------------------------------------------------- /work/pics/perm1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/perm1.png -------------------------------------------------------------------------------- /work/pics/perm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/perm2.png -------------------------------------------------------------------------------- /work/pics/ten1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/ten1.png -------------------------------------------------------------------------------- /work/pics/ten2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/ten2.png -------------------------------------------------------------------------------- /work/pics/tree_cover_hint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/tree_cover_hint.png -------------------------------------------------------------------------------- /work/pics/web_map_eg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vizzuality/data_sci_tutorials/0f95fe05f236651baccf190a3fd4c5b77783bb10/work/pics/web_map_eg.png -------------------------------------------------------------------------------- /work/tileservers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "deletable": true, 7 | "editable": true 8 | }, 9 | "source": [ 10 | "## Preview tile sets using folium\n", 11 | "\n", 12 | "Often we will need to work from data in tileservers and [web map tile format](https://www.mapbox.com/help/how-web-maps-work/).\n", 13 | "\n", 14 | "To preview the tiles, and create standalone html preview documents we can use the [Folium library](https://github.com/python-visualization/folium) and the following code.\n", 15 | "\n", 16 | "There are many tile servers avaiable online, e.g.:\n", 17 | "\n", 18 | "* [NASA tile server info](https://wiki.earthdata.nasa.gov/display/GIBS/GIBS+API+for+Developers#GIBSAPIforDevelopers-GoogleEarthKMLAccess) \n", 19 | "\n", 20 | "* https://gibs.earthdata.nasa.gov/wmts/epsg{EPSG:Code}/best/{ProductName}/default/{Time}/{TileMatrixSet}/{ZoomLevel}/{TileRow}/{TileCol}.png\n" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 1, 26 | "metadata": { 27 | "collapsed": true, 28 | "deletable": true, 29 | "editable": true 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "import folium\n", 34 | "import json\n", 35 | "import requests" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": { 42 | "collapsed": true, 43 | "deletable": true, 44 | "editable": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "basemap = 'Cartodb dark_matter' # \"Mapbox Bright\"\n", 49 | "\n", 50 | "map = folium.Map(location=[28.29, -16.6], zoom_start=3, tiles=basemap)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 3, 56 | "metadata": { 57 | "collapsed": true, 58 | "deletable": true, 59 | "editable": true 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "#tileset = r\"https://storage.googleapis.com/forma-public/Forma250/tiles/global_data/biweekly/forma_biweekly_2017_4/v1/{z}/{x}/{y}.png\"\n", 64 | "#tileset=\"https://storage.googleapis.com/gfw-climate-tiles/soil_carbon/{z}/{x}/{y}.png\"\n", 65 | "#tileset=\"https://storage.googleapis.com/gfw-climate-tiles/aboveground_carbon/{z}/{x}/{y}.png\"\n", 66 | "#tileset=\"https://storage.googleapis.com/gfw-climate-tiles/below_ground_carbon/{z}/{x}/{y}.png\"\n", 67 | "#tileset =r\"https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Aqua_CorrectedReflectance_TrueColor/default/2016-04-09/GoogleMapsCompatible_Level6/{level}/{row}/{col}.png\"\n", 68 | "#tileset=r\"https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Terra_CorrectedReflectance_TrueColor/default/2012-07-09/250m/{z}/{x}/{y}.png\"\n", 69 | "\n", 70 | "tileset=r\"https://storage.googleapis.com/landsat-cache/2016/{z}/{x}/{y}.png\"\n", 71 | "\n", 72 | "map.add_tile_layer(tiles=tileset, max_zoom=12,min_zoom=1, attr='Custom tiles')" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 4, 78 | "metadata": { 79 | "collapsed": false, 80 | "deletable": true, 81 | "editable": true, 82 | "scrolled": true 83 | }, 84 | "outputs": [ 85 | { 86 | "data": { 87 | "text/html": [ 88 | "
" 89 | ], 90 | "text/plain": [ 91 | "" 92 | ] 93 | }, 94 | "execution_count": 4, 95 | "metadata": {}, 96 | "output_type": "execute_result" 97 | } 98 | ], 99 | "source": [ 100 | "map" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 7, 106 | "metadata": { 107 | "collapsed": false, 108 | "deletable": true, 109 | "editable": true 110 | }, 111 | "outputs": [ 112 | { 113 | "data": { 114 | "text/html": [ 115 | "
" 116 | ], 117 | "text/plain": [ 118 | "" 119 | ] 120 | }, 121 | "execution_count": 7, 122 | "metadata": {}, 123 | "output_type": "execute_result" 124 | } 125 | ], 126 | "source": [ 127 | "# Configuration\n", 128 | "account = 'wri-rw'\n", 129 | "urlCarto = 'https://'+account+'.carto.com/api/v1/map'\n", 130 | "body = {\n", 131 | " \"layers\": [{\n", 132 | " \"type\": \"cartodb\",\n", 133 | " \"options\": {\n", 134 | " \"sql\": \"select * from countries\",\n", 135 | " \"cartocss\":\"#layer {\\n polygon-fill: #374C70;\\n polygon-opacity: 0.9;\\n polygon-gamma: 0.5;\\n line-color: #FFF;\\n line-width: 1;\\n line-opacity: 0.5;\\n line-comp-op: soft-light;\\n}\",\n", 136 | " \"cartocss_version\": \"2.1.1\"\n", 137 | " }\n", 138 | " }]\n", 139 | "}\n", 140 | "# Get layer group id\n", 141 | "r = requests.post(urlCarto, data=json.dumps(body), headers={'content-type': 'application/json; charset=UTF-8'})\n", 142 | "tileUrl = 'https://'+account+'.carto.com/api/v1/map/' + r.json()['layergroupid'] + '/{z}/{x}/{y}.png32';\n", 143 | "\n", 144 | "# Add layer to map\n", 145 | "folium.TileLayer(\n", 146 | " tiles=tileUrl,\n", 147 | " attr='text',\n", 148 | " name='text',\n", 149 | " overlay=True\n", 150 | ").add_to(map)\n", 151 | "map" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": { 158 | "collapsed": true, 159 | "deletable": true, 160 | "editable": true 161 | }, 162 | "outputs": [], 163 | "source": [ 164 | "# to save the map to a html file, which you can share, execute the below:\n", 165 | "map.save('landsat_2015.html')" 166 | ] 167 | } 168 | ], 169 | "metadata": { 170 | "anaconda-cloud": {}, 171 | "celltoolbar": "Tags", 172 | "kernelspec": { 173 | "display_name": "Python 3", 174 | "language": "python", 175 | "name": "python3" 176 | }, 177 | "language_info": { 178 | "codemirror_mode": { 179 | "name": "ipython", 180 | "version": 3 181 | }, 182 | "file_extension": ".py", 183 | "mimetype": "text/x-python", 184 | "name": "python", 185 | "nbconvert_exporter": "python", 186 | "pygments_lexer": "ipython3", 187 | "version": "3.5.3" 188 | } 189 | }, 190 | "nbformat": 4, 191 | "nbformat_minor": 2 192 | } 193 | -------------------------------------------------------------------------------- /work/using_recent_imagery_service.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import requests" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 57, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "# First call the recent-tiles service for a list of assets with lat, long, and start/end dates as arguments\n", 19 | "r = requests.get('http://localhost:4500/api/v1/recent-tiles?lat=22.18086540699005&lon=3.433895092679592&start=2018-09-10&end=2018-10-10&')" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 58, 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "data": { 29 | "text/plain": [ 30 | "{'data': {'id': None,\n", 31 | " 'tiles': [{'attributes': {'bbox': {'geometry': {'coordinates': [[21.900331539196973,\n", 32 | " 3.6186207953344973],\n", 33 | " [21.90033081142367, 3.6186094846049626],\n", 34 | " [21.89987449753785, 3.1220814530167407],\n", 35 | " [21.899485684589003, 2.62554859322443],\n", 36 | " [21.89952225520488, 2.625507119446355],\n", 37 | " [21.899553367519612, 2.625461369552561],\n", 38 | " [21.899568104931767, 2.6254585572272124],\n", 39 | " [22.886863928562665, 2.6243525330848247],\n", 40 | " [22.88690556534788, 2.6243890334768487],\n", 41 | " [22.88695129308136, 2.6244200555647166],\n", 42 | " [22.88695420220151, 2.6244348650487987],\n", 43 | " [22.887769391835658, 3.1207656637774783],\n", 44 | " [22.888726218236116, 3.6170840489743203],\n", 45 | " [22.888689672486617, 3.6171255970648164],\n", 46 | " [22.888658581325416, 3.6171713733231408],\n", 47 | " [22.88864376302305, 3.617174204698253],\n", 48 | " [21.90042114628373, 3.6186994179732817],\n", 49 | " [21.90037948126293, 3.618662877507433],\n", 50 | " [21.90033372251688, 3.618631852728597],\n", 51 | " [21.900331539196973, 3.6186207953344973]],\n", 52 | " 'type': 'Polygon'}},\n", 53 | " 'cloud_score': 8.0064,\n", 54 | " 'date_time': '2018-09-19 08:36:21Z',\n", 55 | " 'instrument': 'Sentinel-2A',\n", 56 | " 'source': 'COPERNICUS/S2/20180919T083621_20180919T085433_T34NFJ',\n", 57 | " 'thumbnail_url': None,\n", 58 | " 'tile_url': 'https://earthengine.googleapis.com/map/9b5285c265fc83c653793d0888064e45/{z}/{x}/{y}?token=66afb3dc08b062670eec87445f635f06'}},\n", 59 | " {'attributes': {'bbox': {'geometry': {'coordinates': [[23.596777580728237,\n", 60 | " 3.572528956445655],\n", 61 | " [23.59819250911282, 3.579534100906872],\n", 62 | " [23.59345348419357, 3.58060637055956],\n", 63 | " [22.728954052002127, 3.7664034029813593],\n", 64 | " [21.93292482102623, 3.9367302459714484],\n", 65 | " [21.925690172152997, 3.9381918418910504],\n", 66 | " [21.923880484289615, 3.9381938107584324],\n", 67 | " [21.739026730420992, 3.0689526150676074],\n", 68 | " [21.563103532799545, 2.2395925895986366],\n", 69 | " [21.555803185369026, 2.204570632557085],\n", 70 | " [21.555802457572423, 2.202733392478276],\n", 71 | " [21.683774400281372, 2.1750604792248858],\n", 72 | " [22.608100466898836, 1.9749428628899899],\n", 73 | " [23.16806969381938, 1.8534738074407773],\n", 74 | " [23.227117273480214, 1.8406652711529903],\n", 75 | " [23.320868265094546, 2.2805108182071394],\n", 76 | " [23.381618467639566, 2.5651493022208975],\n", 77 | " [23.44858145878842, 2.8790311788366756],\n", 78 | " [23.566775644264922, 3.4319670461422045],\n", 79 | " [23.587344241620876, 3.5282247861225557],\n", 80 | " [23.596777580728237, 3.572528956445655]],\n", 81 | " 'type': 'Polygon'}},\n", 82 | " 'cloud_score': 29.22,\n", 83 | " 'date_time': '2018-09-16 08:43:25Z',\n", 84 | " 'instrument': 'LANDSAT_8',\n", 85 | " 'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20180916',\n", 86 | " 'thumbnail_url': None,\n", 87 | " 'tile_url': None}},\n", 88 | " {'attributes': {'bbox': {'geometry': {'coordinates': [[21.900331539196973,\n", 89 | " 3.6186207953344973],\n", 90 | " [21.90033081142367, 3.6186094846049626],\n", 91 | " [21.89987449753785, 3.1220814530167407],\n", 92 | " [21.899485684589003, 2.62554859322443],\n", 93 | " [21.89952225520488, 2.625507119446355],\n", 94 | " [21.899553367519612, 2.625461369552561],\n", 95 | " [21.899568104931767, 2.6254585572272124],\n", 96 | " [22.886863928562665, 2.6243525330848247],\n", 97 | " [22.88690556534788, 2.6243890334768487],\n", 98 | " [22.88695129308136, 2.6244200555647166],\n", 99 | " [22.88695420220151, 2.6244348650487987],\n", 100 | " [22.887769391835658, 3.1207656637774783],\n", 101 | " [22.888726218236116, 3.6170840489743203],\n", 102 | " [22.888689672486617, 3.6171255970648164],\n", 103 | " [22.888658581325416, 3.6171713733231408],\n", 104 | " [22.88864376302305, 3.617174204698253],\n", 105 | " [21.90042114628373, 3.6186994179732817],\n", 106 | " [21.90037948126293, 3.618662877507433],\n", 107 | " [21.90033372251688, 3.618631852728597],\n", 108 | " [21.900331539196973, 3.6186207953344973]],\n", 109 | " 'type': 'Polygon'}},\n", 110 | " 'cloud_score': 95.7693,\n", 111 | " 'date_time': '2018-09-29 08:37:31Z',\n", 112 | " 'instrument': 'Sentinel-2A',\n", 113 | " 'source': 'COPERNICUS/S2/20180929T083731_20180929T085615_T34NFJ',\n", 114 | " 'thumbnail_url': None,\n", 115 | " 'tile_url': None}},\n", 116 | " {'attributes': {'bbox': {'geometry': {'coordinates': [[21.900331539196973,\n", 117 | " 3.6186207953344973],\n", 118 | " [21.90033081142367, 3.6186094846049626],\n", 119 | " [21.89987449753785, 3.1220814530167407],\n", 120 | " [21.899485684589003, 2.62554859322443],\n", 121 | " [21.89952225520488, 2.625507119446355],\n", 122 | " [21.899553367519612, 2.625461369552561],\n", 123 | " [21.899568104931767, 2.6254585572272124],\n", 124 | " [22.886863928562665, 2.6243525330848247],\n", 125 | " [22.88690556534788, 2.6243890334768487],\n", 126 | " [22.88695129308136, 2.6244200555647166],\n", 127 | " [22.88695420220151, 2.6244348650487987],\n", 128 | " [22.887769391835658, 3.1207656637774783],\n", 129 | " [22.888726218236116, 3.6170840489743203],\n", 130 | " [22.888689672486617, 3.6171255970648164],\n", 131 | " [22.888658581325416, 3.6171713733231408],\n", 132 | " [22.88864376302305, 3.617174204698253],\n", 133 | " [21.90042114628373, 3.6186994179732817],\n", 134 | " [21.90037948126293, 3.618662877507433],\n", 135 | " [21.90033372251688, 3.618631852728597],\n", 136 | " [21.900331539196973, 3.6186207953344973]],\n", 137 | " 'type': 'Polygon'}},\n", 138 | " 'cloud_score': 100.0,\n", 139 | " 'date_time': '2018-10-09 08:38:41Z',\n", 140 | " 'instrument': 'Sentinel-2A',\n", 141 | " 'source': 'COPERNICUS/S2/20181009T083841_20181009T085022_T34NFJ',\n", 142 | " 'thumbnail_url': None,\n", 143 | " 'tile_url': None}}],\n", 144 | " 'type': 'recent_tiles_data'}}" 145 | ] 146 | }, 147 | "execution_count": 58, 148 | "metadata": {}, 149 | "output_type": "execute_result" 150 | } 151 | ], 152 | "source": [ 153 | "r.json()" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 18, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "# Process the response from the recent service, in particular, first look for the recent tile url \n", 163 | "# and then create an array to send to the tiles and thumbs service\n", 164 | "source_data = []\n", 165 | "for row in r.json().get('data').get('tiles'):\n", 166 | " if row.get('attributes').get('tile_url', None):\n", 167 | " quick_tiles = row.get('attributes').get('tile_url', None)\n", 168 | " source_data.append({'source':row.get('attributes').get('source', None)})" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 14, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "data": { 178 | "text/plain": [ 179 | "'https://earthengine.googleapis.com/map/9b5285c265fc83c653793d0888064e45/{z}/{x}/{y}?token=f6d2c5a09f9a0e2b984a5d606f7b57dc'" 180 | ] 181 | }, 182 | "execution_count": 14, 183 | "metadata": {}, 184 | "output_type": "execute_result" 185 | } 186 | ], 187 | "source": [ 188 | "# A valid quick-look tile url with active token:\n", 189 | "quick_tiles" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 20, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "data": { 199 | "text/plain": [ 200 | "[{'source': 'COPERNICUS/S2/20180919T083621_20180919T085433_T34NFJ'},\n", 201 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181119'},\n", 202 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20180916'},\n", 203 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181205'},\n", 204 | " {'source': 'COPERNICUS/S2/20181108T084141_20181108T085427_T34NFJ'},\n", 205 | " {'source': 'COPERNICUS/S2/20181019T083951_20181019T085322_T34NFJ'},\n", 206 | " {'source': 'COPERNICUS/S2/20181128T084301_20181128T085303_T34NFJ'},\n", 207 | " {'source': 'COPERNICUS/S2/20181029T084051_20181029T085554_T34NFJ'},\n", 208 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181103'},\n", 209 | " {'source': 'COPERNICUS/S2/20181118T084231_20181118T085626_T34NFJ'},\n", 210 | " {'source': 'COPERNICUS/S2/20180929T083731_20180929T085615_T34NFJ'},\n", 211 | " {'source': 'COPERNICUS/S2/20181009T083841_20181009T085022_T34NFJ'}]" 212 | ] 213 | }, 214 | "execution_count": 20, 215 | "metadata": {}, 216 | "output_type": "execute_result" 217 | } 218 | ], 219 | "source": [ 220 | "# example of extracted source data\n", 221 | "source_data" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 72, 227 | "metadata": {}, 228 | "outputs": [ 229 | { 230 | "name": "stdout", 231 | "output_type": "stream", 232 | "text": [ 233 | "Response=200\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "# Next call the recent-tiles/thumbs service posting back a list of the source data\n", 239 | "r2 = requests.post('http://localhost:4500/api/v1/recent-tiles/thumbs', json={'source_data': source_data, 'bands': 0})\n", 240 | "print(f\"Response={r2.status_code}\")" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 73, 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "data": { 250 | "text/plain": [ 251 | "{'data': {'attributes': [{'source': 'COPERNICUS/S2/20180919T083621_20180919T085433_T34NFJ',\n", 252 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=c12a2956e5af9f2201ec7b9645505f7d&token=482387bd0930c1fb6a52db758fb4df4d'},\n", 253 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181119',\n", 254 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=6b9ee03e80723b7822a4388628f8e08c&token=be9f7fb3aef0413e5117783e2e3a180d'},\n", 255 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20180916',\n", 256 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=da2514ef685e957d3b066c768d4e148c&token=27a12748d525419132f83db82c8ff4bc'},\n", 257 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181205',\n", 258 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=0e8a57afe297d28fcd868c811137a65d&token=dfa1775ac602732ac67312938539b4db'},\n", 259 | " {'source': 'COPERNICUS/S2/20181108T084141_20181108T085427_T34NFJ',\n", 260 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=c19f30f71841f65fb5c60397591c4099&token=40728cc4f7bb64fac703e1d14a537b9b'},\n", 261 | " {'source': 'COPERNICUS/S2/20181019T083951_20181019T085322_T34NFJ',\n", 262 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=4ac0635f8a38cd9011d9e753d852557f&token=6273985facf3797abea32ce8327a370e'},\n", 263 | " {'source': 'COPERNICUS/S2/20181128T084301_20181128T085303_T34NFJ',\n", 264 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=2130c945172f4827bf377e098eeb9c66&token=ee51d516154a0dee7d02aa898055465a'},\n", 265 | " {'source': 'COPERNICUS/S2/20181029T084051_20181029T085554_T34NFJ',\n", 266 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=26edd0fca3c45eca87e413a7d5c23d8c&token=7aa5167d97da5cfac6f32aa66b3cdb93'},\n", 267 | " {'source': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181103',\n", 268 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=1cfe3880c0f1515b020fdec49bf94814&token=0f263bf4d8eb248eb697ee7a9626acc0'},\n", 269 | " {'source': 'COPERNICUS/S2/20181118T084231_20181118T085626_T34NFJ',\n", 270 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=b1855efabcc79a5691e4eb8988adbe7a&token=e8f507f8e447e9deb9b82bdaf2875e3a'},\n", 271 | " {'source': 'COPERNICUS/S2/20180929T083731_20180929T085615_T34NFJ',\n", 272 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=487d9ed5fa718467bf36163eb5d9e5c5&token=3893d040da058ebc20545bce884a3ffc'},\n", 273 | " {'source': 'COPERNICUS/S2/20181009T083841_20181009T085022_T34NFJ',\n", 274 | " 'thumbnail_url': 'https://earthengine.googleapis.com/api/thumb?thumbid=e0bc136ab3628c29b5e3089111da349e&token=3e839f9a819f3fdf74a9f1070bc79f98'}],\n", 275 | " 'id': None,\n", 276 | " 'type': 'recent_thumbs_url'}}" 277 | ] 278 | }, 279 | "execution_count": 73, 280 | "metadata": {}, 281 | "output_type": "execute_result" 282 | } 283 | ], 284 | "source": [ 285 | "r2.json()" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 74, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "# The thumbnail_urls can be used to grab the thumbnail assets as required\n", 295 | "from IPython.display import Image\n", 296 | "from IPython.core.display import HTML " 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": 80, 302 | "metadata": {}, 303 | "outputs": [ 304 | { 305 | "data": { 306 | "text/html": [ 307 | "" 308 | ], 309 | "text/plain": [ 310 | "" 311 | ] 312 | }, 313 | "execution_count": 80, 314 | "metadata": {}, 315 | "output_type": "execute_result" 316 | } 317 | ], 318 | "source": [ 319 | "Image(url=r2.json().get('data').get('attributes')[0].get('thumbnail_url'))" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": 83, 325 | "metadata": {}, 326 | "outputs": [ 327 | { 328 | "name": "stdout", 329 | "output_type": "stream", 330 | "text": [ 331 | "Response=200\n" 332 | ] 333 | }, 334 | { 335 | "data": { 336 | "text/plain": [ 337 | "{'data': {'attributes': [{'source_id': 'COPERNICUS/S2/20180919T083621_20180919T085433_T34NFJ',\n", 338 | " 'tile_url': 'https://earthengine.googleapis.com/map/9b5285c265fc83c653793d0888064e45/{z}/{x}/{y}?token=470ea03eeada3c562c564d3e162e3552'},\n", 339 | " {'source_id': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181119',\n", 340 | " 'tile_url': 'https://earthengine.googleapis.com/map/c4c9d9c5bb1758d9774ebdcb2760324a/{z}/{x}/{y}?token=66e87b38b8dd7fe405e1b766f2e33bb6'},\n", 341 | " {'source_id': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20180916',\n", 342 | " 'tile_url': 'https://earthengine.googleapis.com/map/38ac869ff3ffd50d453f828cf0968334/{z}/{x}/{y}?token=77c824660ee0fc434e69b0f4570634de'},\n", 343 | " {'source_id': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181205',\n", 344 | " 'tile_url': 'https://earthengine.googleapis.com/map/93caccef6e7cbe4c596b1bca38730fd9/{z}/{x}/{y}?token=9877c5e85b255eebbf0673ea0aa16549'},\n", 345 | " {'source_id': 'COPERNICUS/S2/20181108T084141_20181108T085427_T34NFJ',\n", 346 | " 'tile_url': 'https://earthengine.googleapis.com/map/9d288c3fbf9c726e13a82df64276b285/{z}/{x}/{y}?token=c0680bb468a18aee34dcf71ab92963d7'},\n", 347 | " {'source_id': 'COPERNICUS/S2/20181019T083951_20181019T085322_T34NFJ',\n", 348 | " 'tile_url': 'https://earthengine.googleapis.com/map/a34b932f886521e526e18b038f10ff52/{z}/{x}/{y}?token=cf575bb7d6f782845e82a50ceab1bd55'},\n", 349 | " {'source_id': 'COPERNICUS/S2/20181128T084301_20181128T085303_T34NFJ',\n", 350 | " 'tile_url': 'https://earthengine.googleapis.com/map/ba4cd70e036b07383af32886e7be7ffb/{z}/{x}/{y}?token=a2b0eb4376f0b77f53d1273ce041ea06'},\n", 351 | " {'source_id': 'COPERNICUS/S2/20181029T084051_20181029T085554_T34NFJ',\n", 352 | " 'tile_url': 'https://earthengine.googleapis.com/map/16a51a6fc6b2c675f244e1227aab2fa8/{z}/{x}/{y}?token=84b404945e96284a05cabca05970e3bd'},\n", 353 | " {'source_id': 'LANDSAT/LC08/C01/T1_RT_TOA/LC08_178058_20181103',\n", 354 | " 'tile_url': 'https://earthengine.googleapis.com/map/6ea5b16a2d4379d1731ff37413c04bce/{z}/{x}/{y}?token=05520b05640da5188817db933f7e0c2c'},\n", 355 | " {'source_id': 'COPERNICUS/S2/20181118T084231_20181118T085626_T34NFJ',\n", 356 | " 'tile_url': 'https://earthengine.googleapis.com/map/11d070eaead28462fa063615f55be0d2/{z}/{x}/{y}?token=ed2f19a22000c10e2219f985eff86b11'},\n", 357 | " {'source_id': 'COPERNICUS/S2/20180929T083731_20180929T085615_T34NFJ',\n", 358 | " 'tile_url': 'https://earthengine.googleapis.com/map/fd04eb0ed4dde97c0e9d806204534a28/{z}/{x}/{y}?token=9ae5615daa95388e7a8a23d582e290a9'},\n", 359 | " {'source_id': 'COPERNICUS/S2/20181009T083841_20181009T085022_T34NFJ',\n", 360 | " 'tile_url': 'https://earthengine.googleapis.com/map/82103b7d4be2bf73fcb009adfac21b6c/{z}/{x}/{y}?token=654c0e1e81eb4c9f68f6c1647deeb9c3'}],\n", 361 | " 'id': None,\n", 362 | " 'type': 'recent_tiles_url'}}" 363 | ] 364 | }, 365 | "execution_count": 83, 366 | "metadata": {}, 367 | "output_type": "execute_result" 368 | } 369 | ], 370 | "source": [ 371 | "# Simultaneously you will also need to call the recent-tiles/tiles service too, with the same arguments\n", 372 | "r3 = requests.post('http://localhost:4500/api/v1/recent-tiles/tiles', json={'source_data': source_data, 'bands': 0})\n", 373 | "print(f\"Response={r3.status_code}\")\n", 374 | "r3.json()" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "metadata": {}, 381 | "outputs": [], 382 | "source": [ 383 | "# Note, tokens will expire after a day or so." 384 | ] 385 | } 386 | ], 387 | "metadata": { 388 | "kernelspec": { 389 | "display_name": "Python 3", 390 | "language": "python", 391 | "name": "python3" 392 | }, 393 | "language_info": { 394 | "codemirror_mode": { 395 | "name": "ipython", 396 | "version": 3 397 | }, 398 | "file_extension": ".py", 399 | "mimetype": "text/x-python", 400 | "name": "python", 401 | "nbconvert_exporter": "python", 402 | "pygments_lexer": "ipython3", 403 | "version": "3.6.7" 404 | } 405 | }, 406 | "nbformat": 4, 407 | "nbformat_minor": 2 408 | } 409 | --------------------------------------------------------------------------------