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