streamlit-geospatial / apps /timelapse.py
giswqs's picture
Added Alibaba font type
127d654
raw
history blame
24.7 kB
import ee
import os
import datetime
import geopandas as gpd
import folium
import streamlit as st
import geemap.foliumap as geemap
from datetime import date
from .rois import *
@st.cache
def uploaded_file_to_gdf(data):
import tempfile
import os
import uuid
_, file_extension = os.path.splitext(data.name)
file_id = str(uuid.uuid4())
file_path = os.path.join(tempfile.gettempdir(), f"{file_id}{file_extension}")
with open(file_path, "wb") as file:
file.write(data.getbuffer())
if file_path.lower().endswith(".kml"):
gpd.io.file.fiona.drvsupport.supported_drivers["KML"] = "rw"
gdf = gpd.read_file(file_path, driver="KML")
else:
gdf = gpd.read_file(file_path)
return gdf
def app():
today = date.today()
st.title("Create Timelapse")
st.markdown(
"""
An interactive web app for creating [Landsat](https://developers.google.com/earth-engine/datasets/catalog/landsat)/[GOES](https://jstnbraaten.medium.com/goes-in-earth-engine-53fbc8783c16) timelapse for any location around the globe.
The app was built using [streamlit](https://streamlit.io), [geemap](https://geemap.org), and [Google Earth Engine](https://earthengine.google.com).
"""
)
row1_col1, row1_col2 = st.columns([2, 1])
if st.session_state.get("zoom_level") is None:
st.session_state["zoom_level"] = 4
with row1_col1:
m = geemap.Map(basemap="HYBRID", plugin_Draw=True, draw_export=True)
m.add_basemap("ROADMAP")
with row1_col2:
keyword = st.text_input("Search for a location:", "")
if keyword:
locations = geemap.geocode(keyword)
if locations is not None and len(locations) > 0:
str_locations = [str(g)[1:-1] for g in locations]
location = st.selectbox("Select a location:", str_locations)
loc_index = str_locations.index(location)
selected_loc = locations[loc_index]
lat, lng = selected_loc.lat, selected_loc.lng
folium.Marker(location=[lat, lng], popup=location).add_to(m)
m.set_center(lng, lat, 12)
st.session_state["zoom_level"] = 12
collection = st.selectbox(
"Select a satellite image collection: ",
[
"Landsat TM-ETM-OLI Surface Reflectance",
"Sentinel-2 MSI Surface Reflectance",
"Geostationary Operational Environmental Satellites (GOES)",
"MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km",
],
index=0,
)
if collection in [
"Landsat TM-ETM-OLI Surface Reflectance",
"Sentinel-2 MSI Surface Reflectance",
]:
roi_options = ["Uploaded GeoJSON"] + list(landsat_rois.keys())
elif collection == "Geostationary Operational Environmental Satellites (GOES)":
roi_options = ["Uploaded GeoJSON"] + list(goes_rois.keys())
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
roi_options = ["Uploaded GeoJSON"] + list(modis_rois.keys())
sample_roi = st.selectbox(
"Select a sample ROI or upload a GeoJSON file:",
roi_options,
index=0,
)
add_outline = st.checkbox(
"Overlay an administrative boundary on timelapse", False
)
if add_outline:
with st.expander("Customize administrative boundary", True):
overlay_options = {
"User-defined": None,
"Continents": "continents",
"Countries": "countries",
"US States": "us_states",
"China Administrative Boundary Level 0": "chn_admin_level0",
"China Administrative Boundary Level 1": "chn_admin_level1",
"China Administrative Boundary Level 2": "chn_admin_level2",
}
overlay = st.selectbox(
"Select an administrative boundary:",
list(overlay_options.keys()),
index=2,
)
overlay_data = overlay_options[overlay]
if overlay_data is None:
overlay_data = st.text_input(
"Enter an HTTP URL to a GeoJSON file or an ee.FeatureCollection asset id:",
"https://raw.githubusercontent.com/giswqs/geemap/master/examples/data/countries.geojson",
)
overlay_color = st.color_picker(
"Select a color for the administrative boundary:", "#000000"
)
overlay_width = st.slider(
"Select a line width for the administrative boundary:", 1, 20, 1
)
overlay_opacity = st.slider(
"Select an opacity for the administrative boundary:",
0.0,
1.0,
1.0,
0.05,
)
else:
overlay_data = None
overlay_color = "black"
overlay_width = 1
overlay_opacity = 1
with row1_col1:
with st.expander(
"Steps: Draw a rectangle on the map -> Export it as a GeoJSON -> Upload it back to the app -> Click the Submit button. Expand this tab to see a demo 👉"
):
video_empty = st.empty()
data = st.file_uploader(
"Upload a GeoJSON file to use as an ROI. Customize timelapse parameters and then click the Submit button 😇👇",
type=["geojson"],
)
crs = {"init": "epsg:4326"}
if sample_roi == "Uploaded GeoJSON":
if data is None:
# st.info(
# "Steps to create a timelapse: Draw a rectangle on the map -> Export it as a GeoJSON -> Upload it back to the app -> Click Submit button"
# )
if (
collection
== "Geostationary Operational Environmental Satellites (GOES)"
and (not keyword)
):
m.set_center(-100, 40, 3)
# else:
# m.set_center(4.20, 18.63, zoom=2)
else:
if collection in [
"Landsat TM-ETM-OLI Surface Reflectance",
"Sentinel-2 MSI Surface Reflectance",
]:
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[landsat_rois[sample_roi]]
)
elif (
collection
== "Geostationary Operational Environmental Satellites (GOES)"
):
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[goes_rois[sample_roi]["region"]]
)
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[modis_rois[sample_roi]]
)
if sample_roi != "Uploaded GeoJSON":
if collection in [
"Landsat TM-ETM-OLI Surface Reflectance",
"Sentinel-2 MSI Surface Reflectance",
]:
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[landsat_rois[sample_roi]]
)
elif (
collection
== "Geostationary Operational Environmental Satellites (GOES)"
):
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[goes_rois[sample_roi]["region"]]
)
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
gdf = gpd.GeoDataFrame(
index=[0], crs=crs, geometry=[modis_rois[sample_roi]]
)
st.session_state["roi"] = geemap.geopandas_to_ee(gdf, geodesic=False)
m.add_gdf(gdf, "ROI")
elif data:
gdf = uploaded_file_to_gdf(data)
st.session_state["roi"] = geemap.geopandas_to_ee(gdf, geodesic=False)
m.add_gdf(gdf, "ROI")
m.to_streamlit(height=600)
with row1_col2:
if collection in [
"Landsat TM-ETM-OLI Surface Reflectance",
"Sentinel-2 MSI Surface Reflectance",
]:
if collection == "Landsat TM-ETM-OLI Surface Reflectance":
sensor_start_year = 1984
timelapse_title = "Landsat Timelapse"
timelapse_speed = 10
elif collection == "Sentinel-2 MSI Surface Reflectance":
sensor_start_year = 2015
timelapse_title = "Sentinel-2 Timelapse"
timelapse_speed = 5
video_empty.video("https://youtu.be/VVRK_-dEjR4")
with st.form("submit_landsat_form"):
roi = None
if st.session_state.get("roi") is not None:
roi = st.session_state.get("roi")
out_gif = geemap.temp_file_path(".gif")
title = st.text_input(
"Enter a title to show on the timelapse: ", timelapse_title
)
RGB = st.selectbox(
"Select an RGB band combination:",
[
"Red/Green/Blue",
"NIR/Red/Green",
"SWIR2/SWIR1/NIR",
"NIR/SWIR1/Red",
"SWIR2/NIR/Red",
"SWIR2/SWIR1/Red",
"SWIR1/NIR/Blue",
"NIR/SWIR1/Blue",
"SWIR2/NIR/Green",
"SWIR1/NIR/Red",
],
index=9,
)
with st.expander("Customize timelapse"):
speed = st.slider("Frames per second:", 1, 30, timelapse_speed)
progress_bar_color = st.color_picker(
"Progress bar color:", "#0000ff"
)
years = st.slider(
"Start and end year:",
sensor_start_year,
today.year,
(sensor_start_year, today.year),
)
months = st.slider("Start and end month:", 1, 12, (5, 10))
font_size = st.slider("Font size:", 10, 50, 30)
font_color = st.color_picker("Font color:", "#ffffff")
apply_fmask = st.checkbox(
"Apply fmask (remove clouds, shadows, snow)", True
)
title_font = st.selectbox(
"Select the font type for the title:",
["arial.ttf", "alibaba.otf"],
index=0,
)
empty_text = st.empty()
empty_image = st.empty()
empty_fire_image = st.empty()
submitted = st.form_submit_button("Submit")
if submitted:
if sample_roi == "Uploaded GeoJSON" and data is None:
empty_text.warning(
"Steps to create a timelapse: Draw a rectangle on the map -> Export it as a GeoJSON -> Upload it back to the app -> Click the Submit button. Alternatively, you can select a sample ROI from the dropdown list."
)
else:
empty_text.text("Computing... Please wait...")
start_year = years[0]
end_year = years[1]
start_date = str(months[0]).zfill(2) + "-01"
end_date = str(months[1]).zfill(2) + "-30"
bands = RGB.split("/")
print(overlay_color)
out_gif = geemap.landsat_timelapse(
roi=roi,
out_gif=out_gif,
start_year=start_year,
end_year=end_year,
start_date=start_date,
end_date=end_date,
bands=bands,
apply_fmask=apply_fmask,
overlay_data=overlay_data,
overlay_color=overlay_color,
overlay_width=overlay_width,
overlay_opacity=overlay_opacity,
)
geemap.add_text_to_gif(
out_gif,
out_gif,
xy=("2%", "2%"),
text_sequence=start_year,
font_size=font_size,
font_color=font_color,
duration=int(1000 / speed),
add_progress_bar=True,
progress_bar_color=progress_bar_color,
progress_bar_height=5,
)
geemap.add_text_to_gif(
out_gif,
out_gif,
xy=("2%", "90%"),
text_sequence=title,
font_size=font_size,
font_type=title_font,
font_color=font_color,
duration=int(1000 / speed),
add_progress_bar=True,
progress_bar_color=progress_bar_color,
progress_bar_height=5,
)
geemap.reduce_gif_size(out_gif)
empty_text.text(
"Right click the GIF to save it to your computer👇"
)
empty_image.image(out_gif)
elif collection == "Geostationary Operational Environmental Satellites (GOES)":
video_empty.video("https://youtu.be/16fA2QORG4A")
with st.form("submit_goes_form"):
roi = None
if st.session_state.get("roi") is not None:
roi = st.session_state.get("roi")
out_gif = geemap.temp_file_path(".gif")
satellite = st.selectbox("Select a satellite:", ["GOES-17", "GOES-16"])
earliest_date = datetime.date(2017, 7, 10)
latest_date = datetime.date.today()
if sample_roi == "Uploaded GeoJSON":
roi_start_date = today - datetime.timedelta(days=2)
roi_end_date = today - datetime.timedelta(days=1)
roi_start_time = datetime.time(14, 00)
roi_end_time = datetime.time(1, 00)
else:
roi_start = goes_rois[sample_roi]["start_time"]
roi_end = goes_rois[sample_roi]["end_time"]
roi_start_date = datetime.datetime.strptime(
roi_start[:10], "%Y-%m-%d"
)
roi_end_date = datetime.datetime.strptime(roi_end[:10], "%Y-%m-%d")
roi_start_time = datetime.time(
int(roi_start[11:13]), int(roi_start[14:16])
)
roi_end_time = datetime.time(
int(roi_end[11:13]), int(roi_end[14:16])
)
start_date = st.date_input("Select the start date:", roi_start_date)
end_date = st.date_input("Select the end date:", roi_end_date)
with st.expander("Customize timelapse"):
add_fire = st.checkbox("Add Fire/Hotspot Characterization", False)
scan_type = st.selectbox(
"Select a scan type:", ["Full Disk", "CONUS", "Mesoscale"]
)
start_time = st.time_input(
"Select the start time of the start date:", roi_start_time
)
end_time = st.time_input(
"Select the end time of the end date:", roi_end_time
)
start = (
start_date.strftime("%Y-%m-%d")
+ "T"
+ start_time.strftime("%H:%M:%S")
)
end = (
end_date.strftime("%Y-%m-%d")
+ "T"
+ end_time.strftime("%H:%M:%S")
)
speed = st.slider("Frames per second:", 1, 30, 10)
add_progress_bar = st.checkbox("Add a progress bar", True)
progress_bar_color = st.color_picker(
"Progress bar color:", "#0000ff"
)
font_size = st.slider("Font size:", 10, 50, 20)
font_color = st.color_picker("Font color:", "#ffffff")
empty_text = st.empty()
empty_image = st.empty()
empty_fire_text = st.empty()
empty_fire_image = st.empty()
submitted = st.form_submit_button("Submit")
if submitted:
if sample_roi == "Uploaded GeoJSON" and data is None:
empty_text.warning(
"Steps to create a timelapse: Draw a rectangle on the map -> Export it as a GeoJSON -> Upload it back to the app -> Click the Submit button. Alternatively, you can select a sample ROI from the dropdown list."
)
else:
empty_text.text("Computing... Please wait...")
geemap.goes_timelapse(
out_gif,
start_date=start,
end_date=end,
data=satellite,
scan=scan_type.replace(" ", "_").lower(),
region=roi,
dimensions=768,
framesPerSecond=speed,
date_format="YYYY-MM-dd HH:mm",
xy=("3%", "3%"),
text_sequence=None,
font_type="arial.ttf",
font_size=font_size,
font_color=font_color,
add_progress_bar=add_progress_bar,
progress_bar_color=progress_bar_color,
progress_bar_height=5,
loop=0,
overlay_data=overlay_data,
overlay_color=overlay_color,
overlay_width=overlay_width,
overlay_opacity=overlay_opacity,
)
if os.path.exists(out_gif):
empty_text.text(
"Right click the GIF to save it to your computer👇"
)
empty_image.image(out_gif)
if add_fire:
out_fire_gif = geemap.temp_file_path(".gif")
empty_fire_text.text(
"Delineating Fire Hotspot... Please wait..."
)
geemap.goes_fire_timelapse(
out_fire_gif,
start_date=start,
end_date=end,
data=satellite,
scan=scan_type.replace(" ", "_").lower(),
region=roi,
dimensions=768,
framesPerSecond=speed,
date_format="YYYY-MM-dd HH:mm",
xy=("3%", "3%"),
text_sequence=None,
font_type="arial.ttf",
font_size=font_size,
font_color=font_color,
add_progress_bar=add_progress_bar,
progress_bar_color=progress_bar_color,
progress_bar_height=5,
loop=0,
)
if os.path.exists(out_fire_gif):
empty_fire_image.image(out_fire_gif)
else:
empty_text.text(
"Something went wrong, either the ROI is too big or there are no data available for the specified date range. Please try a smaller ROI or different date range."
)
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
video_empty.video("https://youtu.be/16fA2QORG4A")
satellite = st.selectbox("Select a satellite:", ["Terra", "Aqua"])
band = st.selectbox("Select a band:", ["NDVI", "EVI"])
with st.form("submit_modis_form"):
roi = None
if st.session_state.get("roi") is not None:
roi = st.session_state.get("roi")
out_gif = geemap.temp_file_path(".gif")
with st.expander("Customize timelapse"):
start = st.date_input(
"Select a start date:", datetime.date(2000, 2, 8)
)
end = st.date_input("Select an end date:", datetime.date.today())
start_date = start.strftime("%Y-%m-%d")
end_date = end.strftime("%Y-%m-%d")
speed = st.slider("Frames per second:", 1, 30, 10)
add_progress_bar = st.checkbox("Add a progress bar", True)
progress_bar_color = st.color_picker(
"Progress bar color:", "#0000ff"
)
font_size = st.slider("Font size:", 10, 50, 20)
font_color = st.color_picker("Font color:", "#ffffff")
empty_text = st.empty()
empty_image = st.empty()
submitted = st.form_submit_button("Submit")
if submitted:
if sample_roi == "Uploaded GeoJSON" and data is None:
empty_text.warning(
"Steps to create a timelapse: Draw a rectangle on the map -> Export it as a GeoJSON -> Upload it back to the app -> Click the Submit button. Alternatively, you can select a sample ROI from the dropdown list."
)
else:
empty_text.text("Computing... Please wait...")
print(roi.getInfo())
geemap.modis_ndvi_timelapse(
out_gif,
satellite,
band,
start_date,
end_date,
roi,
768,
speed,
overlay_data=overlay_data,
overlay_color=overlay_color,
overlay_width=overlay_width,
overlay_opacity=overlay_opacity,
)
geemap.reduce_gif_size(out_gif)
empty_text.text(
"Right click the GIF to save it to your computer👇"
)
empty_image.image(out_gif)
# st.error("This timelapse app is not working at the moment. See the GEE Develop Forum. Stay tuned!")
# st.markdown("""
# See the [GEE Develop Forum](https://groups.google.com/g/google-earth-engine-developers/c/jhMWdmdFoVY) for more details.
# """)