Spaces:
Sleeping
Sleeping
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 * | |
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. | |
# """) | |