{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Building Interactive Web Apps\n", "\n", "```{contents}\n", ":local:\n", ":depth: 2\n", "```\n", "\n", "## Introduction\n", "\n", "## Technical requirements\n", "\n", "```bash\n", "conda create -n gee python\n", "conda activate gee\n", "conda install -c conda-forge mamba\n", "mamba install -c conda-forge pygis\n", "```\n", "\n", "```bash\n", "pip install \"geemap[apps]\"\n", "```\n", "\n", "```bash\n", "jupyter lab\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# %pip install pygis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "geemap.ee_initialize()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building JavaScript web apps\n", "\n", "```javascript\n", "var getNLCD = function (year) {\n", " var dataset = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\");\n", " var nlcd = dataset.filter(ee.Filter.eq(\"system:index\", year)).first();\n", " var landcover = nlcd.select(\"landcover\");\n", " return ui.Map.Layer(landcover, {}, year);\n", "};\n", "```\n", "\n", "```javascript\n", "var images = {\n", " 2001: getNLCD(\"2001\"),\n", " 2004: getNLCD(\"2004\"),\n", " 2006: getNLCD(\"2006\"),\n", " 2008: getNLCD(\"2008\"),\n", " 2011: getNLCD(\"2011\"),\n", " 2013: getNLCD(\"2013\"),\n", " 2016: getNLCD(\"2016\"),\n", " 2019: getNLCD(\"2019\"),\n", "};\n", "```\n", "\n", "```javascript\n", "var leftMap = ui.Map();\n", "leftMap.setControlVisibility(false);\n", "var leftSelector = addLayerSelector(leftMap, 0, \"top-left\");\n", "\n", "var rightMap = ui.Map();\n", "rightMap.setControlVisibility(true);\n", "var rightSelector = addLayerSelector(rightMap, 7, \"top-right\");\n", "\n", "function addLayerSelector(mapToChange, defaultValue, position) {\n", " var label = ui.Label(\"Select a year:\");\n", "\n", " function updateMap(selection) {\n", " mapToChange.layers().set(0, images[selection]);\n", " }\n", "\n", " var select = ui.Select({ items: Object.keys(images), onChange: updateMap });\n", " select.setValue(Object.keys(images)[defaultValue], true);\n", "\n", " var controlPanel = ui.Panel({\n", " widgets: [label, select],\n", " style: { position: position },\n", " });\n", "\n", " mapToChange.add(controlPanel);\n", "}\n", "```\n", "\n", "```javascript\n", "var title = \"NLCD Land Cover Classification\";\n", "var position = \"bottom-right\";\n", "var dict = {\n", " names: [\n", " \"11 Open Water\",\n", " \"12 Perennial Ice/Snow\",\n", " \"21 Developed, Open Space\",\n", " \"22 Developed, Low Intensity\",\n", " \"23 Developed, Medium Intensity\",\n", " \"24 Developed, High Intensity\",\n", " \"31 Barren Land (Rock/Sand/Clay)\",\n", " \"41 Deciduous Forest\",\n", " \"42 Evergreen Forest\",\n", " \"43 Mixed Forest\",\n", " \"51 Dwarf Scrub\",\n", " \"52 Shrub/Scrub\",\n", " \"71 Grassland/Herbaceous\",\n", " \"72 Sedge/Herbaceous\",\n", " \"73 Lichens\",\n", " \"74 Moss\",\n", " \"81 Pasture/Hay\",\n", " \"82 Cultivated Crops\",\n", " \"90 Woody Wetlands\",\n", " \"95 Emergent Herbaceous Wetlands\",\n", " ],\n", "\n", " colors: [\n", " \"#466b9f\",\n", " \"#d1def8\",\n", " \"#dec5c5\",\n", " \"#d99282\",\n", " \"#eb0000\",\n", " \"#ab0000\",\n", " \"#b3ac9f\",\n", " \"#68ab5f\",\n", " \"#1c5f2c\",\n", " \"#b5c58f\",\n", " \"#af963c\",\n", " \"#ccb879\",\n", " \"#dfdfc2\",\n", " \"#d1d182\",\n", " \"#a3cc51\",\n", " \"#82ba9e\",\n", " \"#dcd939\",\n", " \"#ab6c28\",\n", " \"#b8d9eb\",\n", " \"#6c9fb8\",\n", " ],\n", "};\n", "```\n", "\n", "```javascript\n", "var legend = ui.Panel({\n", " style: {\n", " position: position,\n", " padding: \"8px 15px\",\n", " },\n", "});\n", "\n", "function addCategoricalLegend(panel, dict, title) {\n", " var legendTitle = ui.Label({\n", " value: title,\n", " style: {\n", " fontWeight: \"bold\",\n", " fontSize: \"18px\",\n", " margin: \"0 0 4px 0\",\n", " padding: \"0\",\n", " },\n", " });\n", " panel.add(legendTitle);\n", "\n", " var loading = ui.Label(\"Loading legend...\", { margin: \"2px 0 4px 0\" });\n", " panel.add(loading);\n", "\n", " var makeRow = function (color, name) {\n", " var colorBox = ui.Label({\n", " style: {\n", " backgroundColor: color,\n", " padding: \"8px\",\n", " margin: \"0 0 4px 0\",\n", " },\n", " });\n", " var description = ui.Label({\n", " value: name,\n", " style: { margin: \"0 0 4px 6px\" },\n", " });\n", "\n", " return ui.Panel({\n", " widgets: [colorBox, description],\n", " layout: ui.Panel.Layout.Flow(\"horizontal\"),\n", " });\n", " };\n", "\n", " var palette = dict.colors;\n", " var names = dict.names;\n", " loading.style().set(\"shown\", false);\n", "\n", " for (var i = 0; i < names.length; i++) {\n", " panel.add(makeRow(palette[i], names[i]));\n", " }\n", "\n", " rightMap.add(panel);\n", "}\n", "\n", "addCategoricalLegend(legend, dict, title);\n", "```\n", "\n", "```javascript\n", "var splitPanel = ui.SplitPanel({\n", " firstPanel: leftMap,\n", " secondPanel: rightMap,\n", " wipe: true,\n", " style: { stretch: \"both\" },\n", "});\n", "\n", "ui.root.widgets().reset([splitPanel]);\n", "var linker = ui.Map.Linker([leftMap, rightMap]);\n", "leftMap.setCenter(-100, 40, 4);\n", "```\n", "\n", "## Publishing JavaScript web apps\n", "\n", "## Building Python Web Apps" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Map = geemap.Map(center=[40, -100], zoom=4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dataset = ee.ImageCollection('USGS/NLCD_RELEASES/2019_REL/NLCD')\n", "nlcd2019 = dataset.filter(ee.Filter.eq('system:index', '2019')).first()\n", "landcover = nlcd2019.select('landcover')\n", "Map.addLayer(landcover, {}, 'NLCD 2019')\n", "Map" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "title = 'NLCD Land Cover Classification'\n", "Map.add_legend(title=title, builtin_legend='NLCD')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dataset.aggregate_array(\"system:id\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "years = ['2001', '2004', '2006', '2008', '2011', '2013', '2016', '2019']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def getNLCD(year):\n", " dataset = ee.ImageCollection('USGS/NLCD_RELEASES/2019_REL/NLCD')\n", " nlcd = dataset.filter(ee.Filter.eq('system:index', year)).first()\n", " landcover = nlcd.select('landcover')\n", " return landcover" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "collection = ee.ImageCollection(ee.List(years).map(lambda year: getNLCD(year)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "labels = [f'NLCD {year}' for year in years]\n", "labels" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Map.ts_inspector(\n", " left_ts=collection,\n", " right_ts=collection,\n", " left_names=labels,\n", " right_names=labels\n", ")\n", "Map" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using Voila to deploy web apps\n", "\n", "```bash\n", "cd /path/to/ngrok/dir\n", "ngrok config add-authtoken \n", "```\n", "\n", "```bash\n", "./ngrok config add-authtoken \n", "```\n", "\n", "```bash\n", "conda activate gee\n", "voila --no-browser nlcd_app.ipynb\n", "```\n", "\n", "```bash\n", "cd /path/to/ngrok/dir\n", "ngrok http 8866\n", "```\n", "\n", "```bash\n", "voila --no-browser --strip_sources=False nlcd_app.ipynb\n", "```\n", "\n", "```bash\n", "ngrok http -auth=\"username:password\" 8866\n", "```\n", "\n", "## Building Streamlit web apps\n", "\n", "```bash\n", "streamlit hello\n", "```\n", "\n", "```bash\n", "pip install streamlit\n", "```\n", "\n", "```bash\n", "git config --global user.name \"Firstname Lastname\"\n", "git config --global user.email user@example.com\n", "```\n", "\n", "```bash\n", "git clone https://github.com/USERNAME/geemap-apps.git\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import streamlit as st\n", "import geemap.foliumap as geemap" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def getNLCD(year):\n", " dataset = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\")\n", " nlcd = dataset.filter(ee.Filter.eq(\"system:index\", year)).first()\n", " landcover = nlcd.select(\"landcover\")\n", " return landcover" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st.header(\"National Land Cover Database (NLCD)\")\n", "row1_col1, row1_col2 = st.columns([3, 1])\n", "Map = geemap.Map()\n", "years = [\"2001\", \"2004\", \"2006\", \"2008\", \"2011\", \"2013\", \"2016\", \"2019\"]\n", "with row1_col2:\n", " selected_year = st.multiselect(\"Select a year\", years)\n", " add_legend = st.checkbox(\"Show legend\")\n", "if selected_year:\n", " for year in selected_year:\n", " Map.addLayer(getNLCD(year), {}, \"NLCD \" + year)\n", "\n", " if add_legend:\n", " Map.add_legend(title=\"NLCD Land Cover\", builtin_legend=\"NLCD\")\n", " with row1_col1:\n", " Map.to_streamlit(height=600)\n", "else:\n", " with row1_col1:\n", " Map.to_streamlit(height=600)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```bash\n", "conda activate gee\n", "streamlit run app.py\n", "```\n", "\n", "## Building Solara web apps\n", "\n", "```bash\n", "pip install solara\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap\n", "import solara\n", "\n", "class Map(geemap.Map):\n", " def __init__(self, **kwargs):\n", " super().__init__(**kwargs)\n", " self.add_ee_data()\n", "\n", " def add_ee_data(self):\n", " years = ['2001', '2004', '2006', '2008', '2011', '2013', '2016', '2019']\n", " def getNLCD(year):\n", " dataset = ee.ImageCollection('USGS/NLCD_RELEASES/2019_REL/NLCD')\n", " nlcd = dataset.filter(ee.Filter.eq('system:index', year)).first()\n", " landcover = nlcd.select('landcover')\n", " return landcover\n", "\n", " collection = ee.ImageCollection(ee.List(years).map(lambda year: getNLCD(year)))\n", " labels = [f'NLCD {year}' for year in years]\n", " self.ts_inspector(\n", " left_ts=collection,\n", " right_ts=collection,\n", " left_names=labels,\n", " right_names=labels,\n", " )\n", " self.add_legend(\n", " title='NLCD Land Cover Type',\n", " builtin_legend='NLCD',\n", " height=\"460px\",\n", " add_header=False\n", " )\n", "\n", "@solara.component\n", "def Page():\n", " with solara.Column(style={\"min-width\": \"500px\"}):\n", " Map.element(\n", " center=[40, -100],\n", " zoom=4,\n", " height=\"800px\",\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```bash\n", "conda activate gee\n", "solara run ./pages\n", "```\n", "\n", "## Deploying web apps on Hugging Face" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import geemap\n", "geemap.get_ee_token()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }