Spaces:
Sleeping
Sleeping
Yunus Serhat Bıçakçı
commited on
Commit
·
3580ede
1
Parent(s):
4602598
update
Browse files- Home.py +0 -58
- LICENSE +1 -1
- Procfile +0 -1
- app-bk.py +0 -49
- apps/basemaps.py +0 -44
- apps/census.py +0 -35
- apps/cesium.py +0 -8
- apps/deck.py +0 -178
- apps/device_loc.py +0 -43
- apps/gee.py +0 -123
- apps/gee_datasets.py +0 -186
- apps/heatmap.py +0 -19
- apps/home.py +0 -34
- apps/housing.py +0 -457
- apps/hurricane.py +0 -52
- apps/plotly_maps.py +0 -17
- apps/raster.py +0 -77
- apps/rois.py +0 -174
- apps/timelapse.py +0 -1314
- apps/vector.py +0 -98
- apps/wms.py +0 -68
- apps/xy.py +0 -65
- data/cog_files.txt +0 -77
- data/html/sfo_buildings.html +0 -34
- data/london_boroughs.geojson +0 -0
- data/realtor_data_dict.csv +0 -37
- data/scotland_xyz.tsv +0 -51
- data/us_counties.geojson +0 -0
- data/us_metro_areas.geojson +0 -0
- data/us_nation.geojson +0 -0
- data/us_regions.geojson +0 -0
- data/us_states.geojson +0 -0
- environment-bk.yml +0 -17
- index.html +0 -39
- llama/__init__.py +0 -3
- llama/generation.py +0 -82
- llama/model.py +0 -424
- llama/tokenizer.py +0 -37
- multiapp.py +0 -81
- packages.txt +0 -9
- streamlit_app.py +0 -43
- streamlit_call.py +0 -14
Home.py
DELETED
@@ -1,58 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
st.set_page_config(layout="wide")
|
5 |
-
|
6 |
-
st.sidebar.title("About")
|
7 |
-
st.sidebar.info(
|
8 |
-
"""
|
9 |
-
- Web App URL: <https://streamlit.gishub.org>
|
10 |
-
- GitHub repository: <https://github.com/giswqs/streamlit-geospatial>
|
11 |
-
"""
|
12 |
-
)
|
13 |
-
|
14 |
-
st.sidebar.title("Contact")
|
15 |
-
st.sidebar.info(
|
16 |
-
"""
|
17 |
-
Qiusheng Wu at [wetlands.io](https://wetlands.io) | [GitHub](https://github.com/giswqs) | [Twitter](https://twitter.com/giswqs) | [YouTube](https://www.youtube.com/c/QiushengWu) | [LinkedIn](https://www.linkedin.com/in/qiushengwu)
|
18 |
-
"""
|
19 |
-
)
|
20 |
-
|
21 |
-
st.sidebar.title("Support")
|
22 |
-
st.sidebar.info(
|
23 |
-
"""
|
24 |
-
If you want to reward my work, I'd love a cup of coffee from you. Thanks!
|
25 |
-
[buymeacoffee.com/giswqs](http://buymeacoffee.com/giswqs)
|
26 |
-
"""
|
27 |
-
)
|
28 |
-
|
29 |
-
|
30 |
-
st.title("Streamlit for Geospatial Applications")
|
31 |
-
|
32 |
-
st.markdown(
|
33 |
-
"""
|
34 |
-
This multi-page web app demonstrates various interactive web apps created using [streamlit](https://streamlit.io) and open-source mapping libraries,
|
35 |
-
such as [leafmap](https://leafmap.org), [geemap](https://geemap.org), [pydeck](https://deckgl.readthedocs.io), and [kepler.gl](https://docs.kepler.gl/docs/keplergl-jupyter).
|
36 |
-
This is an open-source project and you are very welcome to contribute your comments, questions, resources, and apps as [issues](https://github.com/giswqs/streamlit-geospatial/issues) or
|
37 |
-
[pull requests](https://github.com/giswqs/streamlit-geospatial/pulls) to the [GitHub repository](https://github.com/giswqs/streamlit-geospatial).
|
38 |
-
|
39 |
-
"""
|
40 |
-
)
|
41 |
-
|
42 |
-
st.info("Click on the left sidebar menu to navigate to the different apps.")
|
43 |
-
|
44 |
-
st.subheader("Timelapse of Satellite Imagery")
|
45 |
-
st.markdown(
|
46 |
-
"""
|
47 |
-
The following timelapse animations were created using the Timelapse web app. Click `Timelapse` on the left sidebar menu to create your own timelapse for any location around the globe.
|
48 |
-
"""
|
49 |
-
)
|
50 |
-
|
51 |
-
row1_col1, row1_col2 = st.columns(2)
|
52 |
-
with row1_col1:
|
53 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/spain.gif")
|
54 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/las_vegas.gif")
|
55 |
-
|
56 |
-
with row1_col2:
|
57 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/goes.gif")
|
58 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/fire.gif")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
MIT License
|
2 |
|
3 |
-
Copyright (c)
|
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
|
|
|
1 |
MIT License
|
2 |
|
3 |
+
Copyright (c) 2023 Yunus Serhat Bicakci
|
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
|
Procfile
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
web: sh setup.sh && streamlit run Home.py
|
|
|
|
app-bk.py
DELETED
@@ -1,49 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from multiapp import MultiApp
|
3 |
-
from apps import (
|
4 |
-
basemaps,
|
5 |
-
census,
|
6 |
-
cesium,
|
7 |
-
deck,
|
8 |
-
device_loc,
|
9 |
-
gee,
|
10 |
-
gee_datasets,
|
11 |
-
heatmap,
|
12 |
-
home,
|
13 |
-
housing,
|
14 |
-
# hurricane,
|
15 |
-
plotly_maps,
|
16 |
-
raster,
|
17 |
-
timelapse,
|
18 |
-
vector,
|
19 |
-
wms,
|
20 |
-
xy,
|
21 |
-
)
|
22 |
-
|
23 |
-
st.set_page_config(layout="wide")
|
24 |
-
|
25 |
-
|
26 |
-
apps = MultiApp()
|
27 |
-
|
28 |
-
# Add all your application here
|
29 |
-
|
30 |
-
apps.add_app("Home", home.app)
|
31 |
-
apps.add_app("Create Timelapse", timelapse.app)
|
32 |
-
# apps.add_app("Hurricane Mapping", hurricane.app)
|
33 |
-
apps.add_app("U.S. Real Estate Data", housing.app)
|
34 |
-
apps.add_app("U.S. Census Data", census.app)
|
35 |
-
apps.add_app("Visualize Raster Data", raster.app)
|
36 |
-
apps.add_app("Visualize Vector Data", vector.app)
|
37 |
-
apps.add_app("Search Basemaps", basemaps.app)
|
38 |
-
apps.add_app("Pydeck Gallery", deck.app)
|
39 |
-
apps.add_app("Heatmaps", heatmap.app)
|
40 |
-
apps.add_app("Add Points from XY", xy.app)
|
41 |
-
apps.add_app("Add Web Map Service (WMS)", wms.app)
|
42 |
-
apps.add_app("Google Earth Engine (GEE)", gee.app)
|
43 |
-
apps.add_app("Awesome GEE Community Datasets", gee_datasets.app)
|
44 |
-
apps.add_app("Geolocation", device_loc.app)
|
45 |
-
apps.add_app("Cesium 3D Map", cesium.app)
|
46 |
-
apps.add_app("Plotly", plotly_maps.app)
|
47 |
-
|
48 |
-
# The main app
|
49 |
-
apps.run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/basemaps.py
DELETED
@@ -1,44 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
st.title("Searching Basemaps")
|
7 |
-
st.markdown(
|
8 |
-
"""
|
9 |
-
This app is a demonstration of searching and loading basemaps from [xyzservices](https://github.com/geopandas/xyzservices) and [Quick Map Services (QMS)](https://github.com/nextgis/quickmapservices). Selecting from 1000+ basemaps with a few clicks.
|
10 |
-
"""
|
11 |
-
)
|
12 |
-
|
13 |
-
with st.expander("See demo"):
|
14 |
-
st.image("https://i.imgur.com/0SkUhZh.gif")
|
15 |
-
|
16 |
-
row1_col1, row1_col2 = st.columns([3, 1])
|
17 |
-
width = 800
|
18 |
-
height = 600
|
19 |
-
tiles = None
|
20 |
-
|
21 |
-
with row1_col2:
|
22 |
-
|
23 |
-
checkbox = st.checkbox("Search Quick Map Services (QMS)")
|
24 |
-
keyword = st.text_input("Enter a keyword to search and press Enter:")
|
25 |
-
empty = st.empty()
|
26 |
-
|
27 |
-
if keyword:
|
28 |
-
options = leafmap.search_xyz_services(keyword=keyword)
|
29 |
-
if checkbox:
|
30 |
-
qms = leafmap.search_qms(keyword=keyword)
|
31 |
-
if qms is not None:
|
32 |
-
options = options + qms
|
33 |
-
|
34 |
-
tiles = empty.multiselect(
|
35 |
-
"Select XYZ tiles to add to the map:", options)
|
36 |
-
|
37 |
-
with row1_col1:
|
38 |
-
m = leafmap.Map()
|
39 |
-
|
40 |
-
if tiles is not None:
|
41 |
-
for tile in tiles:
|
42 |
-
m.add_xyz_service(tile)
|
43 |
-
|
44 |
-
m.to_streamlit(width, height)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/census.py
DELETED
@@ -1,35 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
st.title("Using U.S. Census Data")
|
7 |
-
st.markdown(
|
8 |
-
"""
|
9 |
-
This app is a demonstration of using the [U.S. Census Bureau](https://www.census.gov/) TIGERweb Web Map Service (WMS). A complete list of WMS layers can be found [here](https://tigerweb.geo.census.gov/tigerwebmain/TIGERweb_wms.html).
|
10 |
-
"""
|
11 |
-
)
|
12 |
-
|
13 |
-
if "first_index" not in st.session_state:
|
14 |
-
st.session_state["first_index"] = 60
|
15 |
-
else:
|
16 |
-
st.session_state["first_index"] = 0
|
17 |
-
|
18 |
-
row1_col1, row1_col2 = st.columns([3, 1])
|
19 |
-
width = 800
|
20 |
-
height = 600
|
21 |
-
|
22 |
-
census_dict = leafmap.get_census_dict()
|
23 |
-
with row1_col2:
|
24 |
-
|
25 |
-
wms = st.selectbox("Select a WMS", list(census_dict.keys()), index=11)
|
26 |
-
layer = st.selectbox(
|
27 |
-
"Select a layer",
|
28 |
-
census_dict[wms]["layers"],
|
29 |
-
index=st.session_state["first_index"],
|
30 |
-
)
|
31 |
-
|
32 |
-
with row1_col1:
|
33 |
-
m = leafmap.Map()
|
34 |
-
m.add_census_data(wms, layer)
|
35 |
-
m.to_streamlit(width, height)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/cesium.py
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
import leafmap
|
2 |
-
import streamlit as st
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
st.title("Cesium 3D Map")
|
7 |
-
html = "data/html/sfo_buildings.html"
|
8 |
-
leafmap.cesium_to_streamlit(html, height=800)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/deck.py
DELETED
@@ -1,178 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import streamlit as st
|
3 |
-
import pydeck as pdk
|
4 |
-
import pandas as pd
|
5 |
-
|
6 |
-
|
7 |
-
def globe_view():
|
8 |
-
|
9 |
-
"""
|
10 |
-
GlobeView
|
11 |
-
=========
|
12 |
-
|
13 |
-
Over 33,000 power plants of the world plotted by their production capacity (given by height)
|
14 |
-
and fuel type (green if renewable) on an experimental deck.gl GlobeView.
|
15 |
-
"""
|
16 |
-
|
17 |
-
COUNTRIES = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_scale_rank.geojson"
|
18 |
-
POWER_PLANTS = "https://raw.githubusercontent.com/ajduberstein/geo_datasets/master/global_power_plant_database.csv"
|
19 |
-
|
20 |
-
df = pd.read_csv(POWER_PLANTS)
|
21 |
-
|
22 |
-
def is_green(fuel_type):
|
23 |
-
"""Return a green RGB value if a facility uses a renewable fuel type"""
|
24 |
-
if fuel_type.lower() in (
|
25 |
-
"nuclear",
|
26 |
-
"water",
|
27 |
-
"wind",
|
28 |
-
"hydro",
|
29 |
-
"biomass",
|
30 |
-
"solar",
|
31 |
-
"geothermal",
|
32 |
-
):
|
33 |
-
return [10, 230, 120]
|
34 |
-
return [230, 158, 10]
|
35 |
-
|
36 |
-
df["color"] = df["primary_fuel"].apply(is_green)
|
37 |
-
|
38 |
-
view_state = pdk.ViewState(latitude=51.47, longitude=0.45, zoom=2, min_zoom=2)
|
39 |
-
|
40 |
-
# Set height and width variables
|
41 |
-
view = pdk.View(type="_GlobeView", controller=True, width=1000, height=700)
|
42 |
-
|
43 |
-
layers = [
|
44 |
-
pdk.Layer(
|
45 |
-
"GeoJsonLayer",
|
46 |
-
id="base-map",
|
47 |
-
data=COUNTRIES,
|
48 |
-
stroked=False,
|
49 |
-
filled=True,
|
50 |
-
get_fill_color=[200, 200, 200],
|
51 |
-
),
|
52 |
-
pdk.Layer(
|
53 |
-
"ColumnLayer",
|
54 |
-
id="power-plant",
|
55 |
-
data=df,
|
56 |
-
get_elevation="capacity_mw",
|
57 |
-
get_position=["longitude", "latitude"],
|
58 |
-
elevation_scale=100,
|
59 |
-
pickable=True,
|
60 |
-
auto_highlight=True,
|
61 |
-
radius=20000,
|
62 |
-
get_fill_color="color",
|
63 |
-
),
|
64 |
-
]
|
65 |
-
|
66 |
-
r = pdk.Deck(
|
67 |
-
views=[view],
|
68 |
-
initial_view_state=view_state,
|
69 |
-
tooltip={"text": "{name}, {primary_fuel} plant, {country}"},
|
70 |
-
layers=layers,
|
71 |
-
# Note that this must be set for the globe to be opaque
|
72 |
-
parameters={"cull": True},
|
73 |
-
)
|
74 |
-
|
75 |
-
return r
|
76 |
-
|
77 |
-
|
78 |
-
def geojson_layer():
|
79 |
-
|
80 |
-
"""
|
81 |
-
GeoJsonLayer
|
82 |
-
===========
|
83 |
-
|
84 |
-
Property values in Vancouver, Canada, adapted from the deck.gl example pages. Input data is in a GeoJSON format.
|
85 |
-
"""
|
86 |
-
|
87 |
-
DATA_URL = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/geojson/vancouver-blocks.json"
|
88 |
-
LAND_COVER = [
|
89 |
-
[[-123.0, 49.196], [-123.0, 49.324], [-123.306, 49.324], [-123.306, 49.196]]
|
90 |
-
]
|
91 |
-
|
92 |
-
INITIAL_VIEW_STATE = pdk.ViewState(
|
93 |
-
latitude=49.254, longitude=-123.13, zoom=11, max_zoom=16, pitch=45, bearing=0
|
94 |
-
)
|
95 |
-
|
96 |
-
polygon = pdk.Layer(
|
97 |
-
"PolygonLayer",
|
98 |
-
LAND_COVER,
|
99 |
-
stroked=False,
|
100 |
-
# processes the data as a flat longitude-latitude pair
|
101 |
-
get_polygon="-",
|
102 |
-
get_fill_color=[0, 0, 0, 20],
|
103 |
-
)
|
104 |
-
|
105 |
-
geojson = pdk.Layer(
|
106 |
-
"GeoJsonLayer",
|
107 |
-
DATA_URL,
|
108 |
-
opacity=0.8,
|
109 |
-
stroked=False,
|
110 |
-
filled=True,
|
111 |
-
extruded=True,
|
112 |
-
wireframe=True,
|
113 |
-
get_elevation="properties.valuePerSqm / 20",
|
114 |
-
get_fill_color="[255, 255, properties.growth * 255]",
|
115 |
-
get_line_color=[255, 255, 255],
|
116 |
-
)
|
117 |
-
|
118 |
-
r = pdk.Deck(layers=[polygon, geojson], initial_view_state=INITIAL_VIEW_STATE)
|
119 |
-
return r
|
120 |
-
|
121 |
-
|
122 |
-
def terrain():
|
123 |
-
|
124 |
-
"""
|
125 |
-
TerrainLayer
|
126 |
-
===========
|
127 |
-
|
128 |
-
Extruded terrain using AWS Open Data Terrain Tiles and Mapbox Satellite imagery
|
129 |
-
"""
|
130 |
-
|
131 |
-
# Import Mapbox API Key from environment
|
132 |
-
MAPBOX_API_KEY = os.environ["MAPBOX_API_KEY"]
|
133 |
-
|
134 |
-
# AWS Open Data Terrain Tiles
|
135 |
-
TERRAIN_IMAGE = (
|
136 |
-
"https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"
|
137 |
-
)
|
138 |
-
|
139 |
-
# Define how to parse elevation tiles
|
140 |
-
ELEVATION_DECODER = {
|
141 |
-
"rScaler": 256,
|
142 |
-
"gScaler": 1,
|
143 |
-
"bScaler": 1 / 256,
|
144 |
-
"offset": -32768,
|
145 |
-
}
|
146 |
-
|
147 |
-
SURFACE_IMAGE = f"https://api.mapbox.com/v4/mapbox.satellite/{{z}}/{{x}}/{{y}}@2x.png?access_token={MAPBOX_API_KEY}"
|
148 |
-
|
149 |
-
terrain_layer = pdk.Layer(
|
150 |
-
"TerrainLayer",
|
151 |
-
elevation_decoder=ELEVATION_DECODER,
|
152 |
-
texture=SURFACE_IMAGE,
|
153 |
-
elevation_data=TERRAIN_IMAGE,
|
154 |
-
)
|
155 |
-
|
156 |
-
view_state = pdk.ViewState(
|
157 |
-
latitude=46.24, longitude=-122.18, zoom=11.5, bearing=140, pitch=60
|
158 |
-
)
|
159 |
-
|
160 |
-
r = pdk.Deck(terrain_layer, initial_view_state=view_state)
|
161 |
-
return r
|
162 |
-
|
163 |
-
|
164 |
-
def app():
|
165 |
-
|
166 |
-
st.title("Pydeck Gallery")
|
167 |
-
|
168 |
-
options = ["GeoJsonLayer", "GlobeView", "TerrainLayer"]
|
169 |
-
|
170 |
-
option = st.selectbox("Select a pydeck layer type", options)
|
171 |
-
|
172 |
-
if option == "GeoJsonLayer":
|
173 |
-
st.header("Property values in Vancouver, Canada")
|
174 |
-
st.pydeck_chart(geojson_layer())
|
175 |
-
# elif option == "GlobeView":
|
176 |
-
# st.pydeck_chart(globe_view())
|
177 |
-
elif option == "TerrainLayer":
|
178 |
-
st.pydeck_chart(terrain())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/device_loc.py
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from bokeh.models.widgets import Button
|
3 |
-
from bokeh.models import CustomJS
|
4 |
-
from streamlit_bokeh_events import streamlit_bokeh_events
|
5 |
-
import leafmap.foliumap as leafmap
|
6 |
-
|
7 |
-
|
8 |
-
def app():
|
9 |
-
|
10 |
-
loc_button = Button(label="Get Device Location", max_width=150)
|
11 |
-
loc_button.js_on_event(
|
12 |
-
"button_click",
|
13 |
-
CustomJS(
|
14 |
-
code="""
|
15 |
-
navigator.geolocation.getCurrentPosition(
|
16 |
-
(loc) => {
|
17 |
-
document.dispatchEvent(new CustomEvent("GET_LOCATION", {detail: {lat: loc.coords.latitude, lon: loc.coords.longitude}}))
|
18 |
-
}
|
19 |
-
)
|
20 |
-
"""
|
21 |
-
),
|
22 |
-
)
|
23 |
-
result = streamlit_bokeh_events(
|
24 |
-
loc_button,
|
25 |
-
events="GET_LOCATION",
|
26 |
-
key="get_location",
|
27 |
-
refresh_on_update=False,
|
28 |
-
override_height=75,
|
29 |
-
debounce_time=0,
|
30 |
-
)
|
31 |
-
|
32 |
-
if result:
|
33 |
-
if "GET_LOCATION" in result:
|
34 |
-
loc = result.get("GET_LOCATION")
|
35 |
-
lat = loc.get("lat")
|
36 |
-
lon = loc.get("lon")
|
37 |
-
st.write(f"Lat, Lon: {lat}, {lon}")
|
38 |
-
|
39 |
-
m = leafmap.Map(center=(lat, lon), zoom=16)
|
40 |
-
m.add_basemap("ROADMAP")
|
41 |
-
popup = f"lat, lon: {lat}, {lon}"
|
42 |
-
m.add_marker(location=(lat, lon), popup=popup)
|
43 |
-
m.to_streamlit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/gee.py
DELETED
@@ -1,123 +0,0 @@
|
|
1 |
-
import ee
|
2 |
-
import streamlit as st
|
3 |
-
import geemap.foliumap as geemap
|
4 |
-
|
5 |
-
|
6 |
-
def nlcd():
|
7 |
-
|
8 |
-
st.header("National Land Cover Database (NLCD)")
|
9 |
-
|
10 |
-
row1_col1, row1_col2 = st.columns([3, 1])
|
11 |
-
width = 950
|
12 |
-
height = 600
|
13 |
-
|
14 |
-
Map = geemap.Map()
|
15 |
-
|
16 |
-
# Select the seven NLCD epoches after 2000.
|
17 |
-
years = ["2001", "2004", "2006", "2008", "2011", "2013", "2016"]
|
18 |
-
|
19 |
-
# Get an NLCD image by year.
|
20 |
-
def getNLCD(year):
|
21 |
-
# Import the NLCD collection.
|
22 |
-
dataset = ee.ImageCollection("USGS/NLCD_RELEASES/2016_REL")
|
23 |
-
|
24 |
-
# Filter the collection by year.
|
25 |
-
nlcd = dataset.filter(ee.Filter.eq("system:index", year)).first()
|
26 |
-
|
27 |
-
# Select the land cover band.
|
28 |
-
landcover = nlcd.select("landcover")
|
29 |
-
return landcover
|
30 |
-
|
31 |
-
with row1_col2:
|
32 |
-
selected_year = st.multiselect("Select a year", years)
|
33 |
-
add_legend = st.checkbox("Show legend")
|
34 |
-
|
35 |
-
if selected_year:
|
36 |
-
for year in selected_year:
|
37 |
-
Map.addLayer(getNLCD(year), {}, "NLCD " + year)
|
38 |
-
|
39 |
-
if add_legend:
|
40 |
-
Map.add_legend(
|
41 |
-
legend_title="NLCD Land Cover Classification", builtin_legend="NLCD"
|
42 |
-
)
|
43 |
-
with row1_col1:
|
44 |
-
Map.to_streamlit(width=width, height=height)
|
45 |
-
|
46 |
-
else:
|
47 |
-
with row1_col1:
|
48 |
-
Map.to_streamlit(width=width, height=height)
|
49 |
-
|
50 |
-
|
51 |
-
def search_data():
|
52 |
-
|
53 |
-
st.header("Search Earth Engine Data Catalog")
|
54 |
-
|
55 |
-
Map = geemap.Map()
|
56 |
-
|
57 |
-
if "ee_assets" not in st.session_state:
|
58 |
-
st.session_state["ee_assets"] = None
|
59 |
-
if "asset_titles" not in st.session_state:
|
60 |
-
st.session_state["asset_titles"] = None
|
61 |
-
|
62 |
-
col1, col2 = st.columns([2, 1])
|
63 |
-
|
64 |
-
dataset = None
|
65 |
-
with col2:
|
66 |
-
keyword = st.text_input("Enter a keyword to search (e.g., elevation)", "")
|
67 |
-
if keyword:
|
68 |
-
ee_assets = geemap.search_ee_data(keyword)
|
69 |
-
asset_titles = [x["title"] for x in ee_assets]
|
70 |
-
dataset = st.selectbox("Select a dataset", asset_titles)
|
71 |
-
if len(ee_assets) > 0:
|
72 |
-
st.session_state["ee_assets"] = ee_assets
|
73 |
-
st.session_state["asset_titles"] = asset_titles
|
74 |
-
|
75 |
-
if dataset is not None:
|
76 |
-
with st.expander("Show dataset details", True):
|
77 |
-
index = asset_titles.index(dataset)
|
78 |
-
html = geemap.ee_data_html(st.session_state["ee_assets"][index])
|
79 |
-
st.markdown(html, True)
|
80 |
-
|
81 |
-
ee_id = ee_assets[index]["ee_id_snippet"]
|
82 |
-
uid = ee_assets[index]["uid"]
|
83 |
-
st.markdown(f"""**Earth Engine Snippet:** `{ee_id}`""")
|
84 |
-
|
85 |
-
vis_params = st.text_input(
|
86 |
-
"Enter visualization parameters as a dictionary", {}
|
87 |
-
)
|
88 |
-
layer_name = st.text_input("Enter a layer name", uid)
|
89 |
-
button = st.button("Add dataset to map")
|
90 |
-
if button:
|
91 |
-
vis = {}
|
92 |
-
try:
|
93 |
-
if vis_params.strip() == "":
|
94 |
-
# st.error("Please enter visualization parameters")
|
95 |
-
vis_params = "{}"
|
96 |
-
vis = eval(vis_params)
|
97 |
-
if not isinstance(vis, dict):
|
98 |
-
st.error("Visualization parameters must be a dictionary")
|
99 |
-
try:
|
100 |
-
Map.addLayer(eval(ee_id), vis, layer_name)
|
101 |
-
except Exception as e:
|
102 |
-
st.error(f"Error adding layer: {e}")
|
103 |
-
except Exception as e:
|
104 |
-
st.error(f"Invalid visualization parameters: {e}")
|
105 |
-
|
106 |
-
with col1:
|
107 |
-
Map.to_streamlit()
|
108 |
-
else:
|
109 |
-
with col1:
|
110 |
-
Map.to_streamlit()
|
111 |
-
|
112 |
-
|
113 |
-
def app():
|
114 |
-
st.title("Google Earth Engine Applications")
|
115 |
-
|
116 |
-
apps = ["National Land Cover Database (NLCD)", "Search Earth Engine Data Catalog"]
|
117 |
-
|
118 |
-
selected_app = st.selectbox("Select an app", apps)
|
119 |
-
|
120 |
-
if selected_app == "National Land Cover Database (NLCD)":
|
121 |
-
nlcd()
|
122 |
-
elif selected_app == "Search Earth Engine Data Catalog":
|
123 |
-
search_data()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/gee_datasets.py
DELETED
@@ -1,186 +0,0 @@
|
|
1 |
-
import ee
|
2 |
-
import streamlit as st
|
3 |
-
import geemap.foliumap as geemap
|
4 |
-
|
5 |
-
WIDTH = 1060
|
6 |
-
HEIGHT = 600
|
7 |
-
|
8 |
-
|
9 |
-
def function():
|
10 |
-
st.write("Not implemented yet.")
|
11 |
-
Map = geemap.Map()
|
12 |
-
Map.to_streamlit(WIDTH, HEIGHT)
|
13 |
-
|
14 |
-
|
15 |
-
def lulc_mrb_floodplain():
|
16 |
-
|
17 |
-
Map = geemap.Map()
|
18 |
-
|
19 |
-
State_boundaries = ee.FeatureCollection('users/giswqs/MRB/State_Boundaries')
|
20 |
-
State_style = State_boundaries.style(
|
21 |
-
**{'color': '808080', 'width': 1, 'fillColor': '00000000'}
|
22 |
-
)
|
23 |
-
|
24 |
-
MRB_boundary = ee.FeatureCollection('users/giswqs/MRB/MRB_Boundary')
|
25 |
-
MRB_style = MRB_boundary.style(
|
26 |
-
**{'color': '000000', 'width': 2, 'fillColor': '00000000'}
|
27 |
-
)
|
28 |
-
|
29 |
-
floodplain = ee.Image('users/giswqs/MRB/USGS_Floodplain')
|
30 |
-
|
31 |
-
class_values = [34, 38, 46, 50, 62]
|
32 |
-
class_palette = ['c500ff', '00ffc5', '00a9e6', '73004d', '004d73']
|
33 |
-
|
34 |
-
img_1950 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_1950')
|
35 |
-
img_1950 = img_1950.set('b1_class_values', class_values)
|
36 |
-
img_1950 = img_1950.set('b1_class_palette', class_palette)
|
37 |
-
|
38 |
-
img_1960 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_1960')
|
39 |
-
img_1960 = img_1960.set('b1_class_values', class_values)
|
40 |
-
img_1960 = img_1960.set('b1_class_palette', class_palette)
|
41 |
-
|
42 |
-
img_1970 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_1970')
|
43 |
-
img_1970 = img_1970.set('b1_class_values', class_values)
|
44 |
-
img_1970 = img_1970.set('b1_class_palette', class_palette)
|
45 |
-
|
46 |
-
img_1980 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_1980')
|
47 |
-
img_1980 = img_1980.set('b1_class_values', class_values)
|
48 |
-
img_1980 = img_1980.set('b1_class_palette', class_palette)
|
49 |
-
|
50 |
-
img_1990 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_1990')
|
51 |
-
img_1990 = img_1990.set('b1_class_values', class_values)
|
52 |
-
img_1990 = img_1990.set('b1_class_palette', class_palette)
|
53 |
-
|
54 |
-
img_2000 = ee.Image('users/giswqs/MRB/Major_Transitions_1941_2000')
|
55 |
-
img_2000 = img_2000.set('b1_class_values', class_values)
|
56 |
-
img_2000 = img_2000.set('b1_class_palette', class_palette)
|
57 |
-
|
58 |
-
Map.addLayer(floodplain, {'palette': ['cccccc']}, 'Floodplain', True, 0.5)
|
59 |
-
Map.addLayer(img_2000, {}, 'Major Transitions 1941-2000')
|
60 |
-
Map.addLayer(img_1990, {}, 'Major Transitions 1941-1990')
|
61 |
-
Map.addLayer(img_1980, {}, 'Major Transitions 1941-1980')
|
62 |
-
Map.addLayer(img_1970, {}, 'Major Transitions 1941-1970')
|
63 |
-
Map.addLayer(img_1960, {}, 'Major Transitions 1941-1960')
|
64 |
-
Map.addLayer(img_1950, {}, 'Major Transitions 1941-1950')
|
65 |
-
|
66 |
-
Map.addLayer(State_style, {}, 'State Boundaries')
|
67 |
-
Map.addLayer(MRB_style, {}, 'MRB Boundary')
|
68 |
-
|
69 |
-
Map.to_streamlit(WIDTH, HEIGHT)
|
70 |
-
|
71 |
-
|
72 |
-
def global_mangrove_watch():
|
73 |
-
"""https://samapriya.github.io/awesome-gee-community-datasets/projects/mangrove/"""
|
74 |
-
Map = geemap.Map()
|
75 |
-
gmw2007 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2007_v2")
|
76 |
-
gmw2008 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2008_v2")
|
77 |
-
gmw2009 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2009_v2")
|
78 |
-
gmw2010 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2010_v2")
|
79 |
-
gmw2015 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2015_v2")
|
80 |
-
gmw2016 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_2016_v2")
|
81 |
-
gmw1996 = ee.FeatureCollection("projects/sat-io/open-datasets/GMW/GMW_1996_v2")
|
82 |
-
|
83 |
-
Map.addLayer(
|
84 |
-
ee.Image().paint(gmw1996, 0, 3),
|
85 |
-
{"palette": ["228B22"]},
|
86 |
-
'Global Mangrove Watch 1996',
|
87 |
-
)
|
88 |
-
Map.addLayer(
|
89 |
-
ee.Image().paint(gmw2007, 0, 3),
|
90 |
-
{"palette": ["228B22"]},
|
91 |
-
'Global Mangrove Watch 2007',
|
92 |
-
)
|
93 |
-
Map.addLayer(
|
94 |
-
ee.Image().paint(gmw2008, 0, 3),
|
95 |
-
{"palette": ["228B22"]},
|
96 |
-
'Global Mangrove Watch 2008',
|
97 |
-
)
|
98 |
-
Map.addLayer(
|
99 |
-
ee.Image().paint(gmw2009, 0, 3),
|
100 |
-
{"palette": ["228B22"]},
|
101 |
-
'Global Mangrove Watch 2009',
|
102 |
-
)
|
103 |
-
Map.addLayer(
|
104 |
-
ee.Image().paint(gmw2010, 0, 3),
|
105 |
-
{"palette": ["228B22"]},
|
106 |
-
'Global Mangrove Watch 2010',
|
107 |
-
)
|
108 |
-
Map.addLayer(
|
109 |
-
ee.Image().paint(gmw2015, 0, 3),
|
110 |
-
{"palette": ["228B22"]},
|
111 |
-
'Global Mangrove Watch 2015',
|
112 |
-
)
|
113 |
-
Map.addLayer(
|
114 |
-
ee.Image().paint(gmw2016, 0, 3),
|
115 |
-
{"palette": ["228B22"]},
|
116 |
-
'Global Mangrove Watch 2015',
|
117 |
-
)
|
118 |
-
|
119 |
-
Map.to_streamlit(WIDTH, HEIGHT)
|
120 |
-
|
121 |
-
|
122 |
-
def app():
|
123 |
-
|
124 |
-
st.title("Awesome GEE Community Datasets")
|
125 |
-
|
126 |
-
st.markdown(
|
127 |
-
"""
|
128 |
-
|
129 |
-
This app is for exploring the [Awesome GEE Community Datasets](https://samapriya.github.io/awesome-gee-community-datasets). Work in progress.
|
130 |
-
|
131 |
-
"""
|
132 |
-
)
|
133 |
-
|
134 |
-
datasets = {
|
135 |
-
"Population & Socioeconomic": {
|
136 |
-
"High Resolution Settlement Layer": "function()",
|
137 |
-
"World Settlement Footprint (2015)": "function()",
|
138 |
-
"Gridded Population of the World": "function()",
|
139 |
-
"geoBoundaries Global Database": "function()",
|
140 |
-
"West Africa Coastal Vulnerability Mapping": "function()",
|
141 |
-
"Relative Wealth Index (RWI)": "function()",
|
142 |
-
"Social Connectedness Index (SCI)": "function()",
|
143 |
-
"Native Land (Indigenous Land Maps)": "function()",
|
144 |
-
},
|
145 |
-
"Geophysical, Biological & Biogeochemical": {
|
146 |
-
"Geomorpho90m Geomorphometric Layers": "function()",
|
147 |
-
},
|
148 |
-
"Land Use and Land Cover": {
|
149 |
-
"Global Mangrove Watch": "global_mangrove_watch()",
|
150 |
-
"Mississippi River Basin Floodplain Land Use Change (1941-2000)": "lulc_mrb_floodplain()",
|
151 |
-
},
|
152 |
-
"Hydrology": {
|
153 |
-
"Global Shoreline Dataset": "function()",
|
154 |
-
},
|
155 |
-
"Agriculture, Vegetation and Forestry": {
|
156 |
-
"Landfire Mosaics LF v2.0.0": "function()",
|
157 |
-
},
|
158 |
-
"Global Utilities, Assets and Amenities Layers": {
|
159 |
-
"Global Power": "function()",
|
160 |
-
},
|
161 |
-
"EarthEnv Biodiversity ecosystems & climate Layers": {
|
162 |
-
"Global Consensus Landcover": "function()",
|
163 |
-
},
|
164 |
-
"Weather and Climate Layers": {
|
165 |
-
"Global Reference Evapotranspiration Layers": "function()",
|
166 |
-
},
|
167 |
-
"Global Events Layers": {
|
168 |
-
"Global Fire Atlas (2003-2016)": "function()",
|
169 |
-
},
|
170 |
-
}
|
171 |
-
|
172 |
-
row1_col1, row1_col2, _ = st.columns([1.2, 1.8, 1])
|
173 |
-
|
174 |
-
with row1_col1:
|
175 |
-
category = st.selectbox("Select a category", datasets.keys(), index=2)
|
176 |
-
with row1_col2:
|
177 |
-
dataset = st.selectbox("Select a dataset", datasets[category].keys())
|
178 |
-
|
179 |
-
Map = geemap.Map()
|
180 |
-
|
181 |
-
if dataset:
|
182 |
-
eval(datasets[category][dataset])
|
183 |
-
|
184 |
-
else:
|
185 |
-
Map = geemap.Map()
|
186 |
-
Map.to_streamlit(WIDTH, HEIGHT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/heatmap.py
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
|
7 |
-
st.title('Heatmaps')
|
8 |
-
|
9 |
-
filepath = "https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/us_cities.csv"
|
10 |
-
m = leafmap.Map(tiles="stamentoner")
|
11 |
-
m.add_heatmap(
|
12 |
-
filepath,
|
13 |
-
latitude="latitude",
|
14 |
-
longitude="longitude",
|
15 |
-
value="pop_max",
|
16 |
-
name="Heat map",
|
17 |
-
radius=20,
|
18 |
-
)
|
19 |
-
m.to_streamlit(width=700, height=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/home.py
DELETED
@@ -1,34 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
st.title("Streamlit for Geospatial Applications")
|
7 |
-
|
8 |
-
st.markdown(
|
9 |
-
"""
|
10 |
-
This multi-page web app demonstrates various interactive web apps created using [streamlit](https://streamlit.io) and open-source mapping libraries,
|
11 |
-
such as [leafmap](https://leafmap.org), [geemap](https://geemap.org), [pydeck](https://deckgl.readthedocs.io), and [kepler.gl](https://docs.kepler.gl/docs/keplergl-jupyter).
|
12 |
-
This is an open-source project and you are very welcome to contribute your comments, questions, resources, and apps as [issues](https://github.com/giswqs/streamlit-geospatial/issues) or
|
13 |
-
[pull requests](https://github.com/giswqs/streamlit-geospatial/pulls) to the [GitHub repository](https://github.com/giswqs/streamlit-geospatial).
|
14 |
-
|
15 |
-
"""
|
16 |
-
)
|
17 |
-
|
18 |
-
st.info("Click on the left sidebar menu to navigate to the different apps.")
|
19 |
-
|
20 |
-
st.subheader("Timelapse of Satellite Imagery")
|
21 |
-
st.markdown(
|
22 |
-
"""
|
23 |
-
The following timelapse animations were created using the Timelapse web app. Click `Create Timelapse` on the left sidebar menu to create your own timelapse for any location around the globe.
|
24 |
-
"""
|
25 |
-
)
|
26 |
-
|
27 |
-
row1_col1, row1_col2 = st.columns(2)
|
28 |
-
with row1_col1:
|
29 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/spain.gif")
|
30 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/las_vegas.gif")
|
31 |
-
|
32 |
-
with row1_col2:
|
33 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/goes.gif")
|
34 |
-
st.image("https://github.com/giswqs/data/raw/main/timelapse/fire.gif")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/housing.py
DELETED
@@ -1,457 +0,0 @@
|
|
1 |
-
import datetime
|
2 |
-
import os
|
3 |
-
import pathlib
|
4 |
-
import requests
|
5 |
-
import zipfile
|
6 |
-
import pandas as pd
|
7 |
-
import pydeck as pdk
|
8 |
-
import geopandas as gpd
|
9 |
-
import streamlit as st
|
10 |
-
import leafmap.colormaps as cm
|
11 |
-
from leafmap.common import hex_to_rgb
|
12 |
-
|
13 |
-
|
14 |
-
STREAMLIT_STATIC_PATH = pathlib.Path(st.__path__[0]) / "static"
|
15 |
-
# We create a downloads directory within the streamlit static asset directory
|
16 |
-
# and we write output files to it
|
17 |
-
DOWNLOADS_PATH = STREAMLIT_STATIC_PATH / "downloads"
|
18 |
-
if not DOWNLOADS_PATH.is_dir():
|
19 |
-
DOWNLOADS_PATH.mkdir()
|
20 |
-
|
21 |
-
# Data source: https://www.realtor.com/research/data/
|
22 |
-
# link_prefix = "https://econdata.s3-us-west-2.amazonaws.com/Reports/"
|
23 |
-
link_prefix = "https://raw.githubusercontent.com/giswqs/data/main/housing/"
|
24 |
-
|
25 |
-
data_links = {
|
26 |
-
"weekly": {
|
27 |
-
"national": link_prefix + "Core/listing_weekly_core_aggregate_by_country.csv",
|
28 |
-
"metro": link_prefix + "Core/listing_weekly_core_aggregate_by_metro.csv",
|
29 |
-
},
|
30 |
-
"monthly_current": {
|
31 |
-
"national": link_prefix + "Core/RDC_Inventory_Core_Metrics_Country.csv",
|
32 |
-
"state": link_prefix + "Core/RDC_Inventory_Core_Metrics_State.csv",
|
33 |
-
"metro": link_prefix + "Core/RDC_Inventory_Core_Metrics_Metro.csv",
|
34 |
-
"county": link_prefix + "Core/RDC_Inventory_Core_Metrics_County.csv",
|
35 |
-
"zip": link_prefix + "Core/RDC_Inventory_Core_Metrics_Zip.csv",
|
36 |
-
},
|
37 |
-
"monthly_historical": {
|
38 |
-
"national": link_prefix + "Core/RDC_Inventory_Core_Metrics_Country_History.csv",
|
39 |
-
"state": link_prefix + "Core/RDC_Inventory_Core_Metrics_State_History.csv",
|
40 |
-
"metro": link_prefix + "Core/RDC_Inventory_Core_Metrics_Metro_History.csv",
|
41 |
-
"county": link_prefix + "Core/RDC_Inventory_Core_Metrics_County_History.csv",
|
42 |
-
"zip": link_prefix + "Core/RDC_Inventory_Core_Metrics_Zip_History.csv",
|
43 |
-
},
|
44 |
-
"hotness": {
|
45 |
-
"metro": link_prefix
|
46 |
-
+ "Hotness/RDC_Inventory_Hotness_Metrics_Metro_History.csv",
|
47 |
-
"county": link_prefix
|
48 |
-
+ "Hotness/RDC_Inventory_Hotness_Metrics_County_History.csv",
|
49 |
-
"zip": link_prefix + "Hotness/RDC_Inventory_Hotness_Metrics_Zip_History.csv",
|
50 |
-
},
|
51 |
-
}
|
52 |
-
|
53 |
-
|
54 |
-
def get_data_columns(df, category, frequency="monthly"):
|
55 |
-
if frequency == "monthly":
|
56 |
-
if category.lower() == "county":
|
57 |
-
del_cols = ["month_date_yyyymm", "county_fips", "county_name"]
|
58 |
-
elif category.lower() == "state":
|
59 |
-
del_cols = ["month_date_yyyymm", "state", "state_id"]
|
60 |
-
elif category.lower() == "national":
|
61 |
-
del_cols = ["month_date_yyyymm", "country"]
|
62 |
-
elif category.lower() == "metro":
|
63 |
-
del_cols = ["month_date_yyyymm", "cbsa_code", "cbsa_title", "HouseholdRank"]
|
64 |
-
elif category.lower() == "zip":
|
65 |
-
del_cols = ["month_date_yyyymm", "postal_code", "zip_name", "flag"]
|
66 |
-
elif frequency == "weekly":
|
67 |
-
if category.lower() == "national":
|
68 |
-
del_cols = ["week_end_date", "geo_country"]
|
69 |
-
elif category.lower() == "metro":
|
70 |
-
del_cols = ["week_end_date", "cbsa_code", "cbsa_title", "hh_rank"]
|
71 |
-
|
72 |
-
cols = df.columns.values.tolist()
|
73 |
-
|
74 |
-
for col in cols:
|
75 |
-
if col.strip() in del_cols:
|
76 |
-
cols.remove(col)
|
77 |
-
if category.lower() == "metro":
|
78 |
-
return cols[2:]
|
79 |
-
else:
|
80 |
-
return cols[1:]
|
81 |
-
|
82 |
-
|
83 |
-
@st.cache(allow_output_mutation=True)
|
84 |
-
def get_inventory_data(url):
|
85 |
-
df = pd.read_csv(url)
|
86 |
-
url = url.lower()
|
87 |
-
if "county" in url:
|
88 |
-
df["county_fips"] = df["county_fips"].map(str)
|
89 |
-
df["county_fips"] = df["county_fips"].str.zfill(5)
|
90 |
-
elif "state" in url:
|
91 |
-
df["STUSPS"] = df["state_id"].str.upper()
|
92 |
-
elif "metro" in url:
|
93 |
-
df["cbsa_code"] = df["cbsa_code"].map(str)
|
94 |
-
elif "zip" in url:
|
95 |
-
df["postal_code"] = df["postal_code"].map(str)
|
96 |
-
df["postal_code"] = df["postal_code"].str.zfill(5)
|
97 |
-
|
98 |
-
if "listing_weekly_core_aggregate_by_country" in url:
|
99 |
-
columns = get_data_columns(df, "national", "weekly")
|
100 |
-
for column in columns:
|
101 |
-
if column != "median_days_on_market_by_day_yy":
|
102 |
-
df[column] = df[column].str.rstrip("%").astype(float) / 100
|
103 |
-
if "listing_weekly_core_aggregate_by_metro" in url:
|
104 |
-
columns = get_data_columns(df, "metro", "weekly")
|
105 |
-
for column in columns:
|
106 |
-
if column != "median_days_on_market_by_day_yy":
|
107 |
-
df[column] = df[column].str.rstrip("%").astype(float) / 100
|
108 |
-
df["cbsa_code"] = df["cbsa_code"].str[:5]
|
109 |
-
return df
|
110 |
-
|
111 |
-
|
112 |
-
def filter_weekly_inventory(df, week):
|
113 |
-
df = df[df["week_end_date"] == week]
|
114 |
-
return df
|
115 |
-
|
116 |
-
|
117 |
-
def get_start_end_year(df):
|
118 |
-
start_year = int(str(df["month_date_yyyymm"].min())[:4])
|
119 |
-
end_year = int(str(df["month_date_yyyymm"].max())[:4])
|
120 |
-
return start_year, end_year
|
121 |
-
|
122 |
-
|
123 |
-
def get_periods(df):
|
124 |
-
return [str(d) for d in list(set(df["month_date_yyyymm"].tolist()))]
|
125 |
-
|
126 |
-
|
127 |
-
@st.cache(allow_output_mutation=True)
|
128 |
-
def get_geom_data(category):
|
129 |
-
|
130 |
-
prefix = (
|
131 |
-
"https://raw.githubusercontent.com/giswqs/streamlit-geospatial/master/data/"
|
132 |
-
)
|
133 |
-
links = {
|
134 |
-
"national": prefix + "us_nation.geojson",
|
135 |
-
"state": prefix + "us_states.geojson",
|
136 |
-
"county": prefix + "us_counties.geojson",
|
137 |
-
"metro": prefix + "us_metro_areas.geojson",
|
138 |
-
"zip": "https://www2.census.gov/geo/tiger/GENZ2018/shp/cb_2018_us_zcta510_500k.zip",
|
139 |
-
}
|
140 |
-
|
141 |
-
if category.lower() == "zip":
|
142 |
-
r = requests.get(links[category])
|
143 |
-
out_zip = os.path.join(DOWNLOADS_PATH, "cb_2018_us_zcta510_500k.zip")
|
144 |
-
with open(out_zip, "wb") as code:
|
145 |
-
code.write(r.content)
|
146 |
-
zip_ref = zipfile.ZipFile(out_zip, "r")
|
147 |
-
zip_ref.extractall(DOWNLOADS_PATH)
|
148 |
-
gdf = gpd.read_file(out_zip.replace("zip", "shp"))
|
149 |
-
else:
|
150 |
-
gdf = gpd.read_file(links[category])
|
151 |
-
return gdf
|
152 |
-
|
153 |
-
|
154 |
-
def join_attributes(gdf, df, category):
|
155 |
-
|
156 |
-
new_gdf = None
|
157 |
-
if category == "county":
|
158 |
-
new_gdf = gdf.merge(df, left_on="GEOID", right_on="county_fips", how="outer")
|
159 |
-
elif category == "state":
|
160 |
-
new_gdf = gdf.merge(df, left_on="STUSPS", right_on="STUSPS", how="outer")
|
161 |
-
elif category == "national":
|
162 |
-
if "geo_country" in df.columns.values.tolist():
|
163 |
-
df["country"] = None
|
164 |
-
df.loc[0, "country"] = "United States"
|
165 |
-
new_gdf = gdf.merge(df, left_on="NAME", right_on="country", how="outer")
|
166 |
-
elif category == "metro":
|
167 |
-
new_gdf = gdf.merge(df, left_on="CBSAFP", right_on="cbsa_code", how="outer")
|
168 |
-
elif category == "zip":
|
169 |
-
new_gdf = gdf.merge(df, left_on="GEOID10", right_on="postal_code", how="outer")
|
170 |
-
return new_gdf
|
171 |
-
|
172 |
-
|
173 |
-
def select_non_null(gdf, col_name):
|
174 |
-
new_gdf = gdf[~gdf[col_name].isna()]
|
175 |
-
return new_gdf
|
176 |
-
|
177 |
-
|
178 |
-
def select_null(gdf, col_name):
|
179 |
-
new_gdf = gdf[gdf[col_name].isna()]
|
180 |
-
return new_gdf
|
181 |
-
|
182 |
-
|
183 |
-
def get_data_dict(name):
|
184 |
-
in_csv = os.path.join(os.getcwd(), "data/realtor_data_dict.csv")
|
185 |
-
df = pd.read_csv(in_csv)
|
186 |
-
label = list(df[df["Name"] == name]["Label"])[0]
|
187 |
-
desc = list(df[df["Name"] == name]["Description"])[0]
|
188 |
-
return label, desc
|
189 |
-
|
190 |
-
|
191 |
-
def get_weeks(df):
|
192 |
-
seq = list(set(df[~df["week_end_date"].isnull()]["week_end_date"].tolist()))
|
193 |
-
weeks = [
|
194 |
-
datetime.date(int(d.split("/")[2]), int(d.split("/")[0]), int(d.split("/")[1]))
|
195 |
-
for d in seq
|
196 |
-
]
|
197 |
-
weeks.sort()
|
198 |
-
return weeks
|
199 |
-
|
200 |
-
|
201 |
-
def get_saturday(in_date):
|
202 |
-
idx = (in_date.weekday() + 1) % 7
|
203 |
-
sat = in_date + datetime.timedelta(6 - idx)
|
204 |
-
return sat
|
205 |
-
|
206 |
-
|
207 |
-
def app():
|
208 |
-
|
209 |
-
st.title("U.S. Real Estate Data and Market Trends")
|
210 |
-
st.markdown(
|
211 |
-
"""**Introduction:** This interactive dashboard is designed for visualizing U.S. real estate data and market trends at multiple levels (i.e., national,
|
212 |
-
state, county, and metro). The data sources include [Real Estate Data](https://www.realtor.com/research/data) from realtor.com and
|
213 |
-
[Cartographic Boundary Files](https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html) from U.S. Census Bureau.
|
214 |
-
Several open-source packages are used to process the data and generate the visualizations, e.g., [streamlit](https://streamlit.io),
|
215 |
-
[geopandas](https://geopandas.org), [leafmap](https://leafmap.org), and [pydeck](https://deckgl.readthedocs.io).
|
216 |
-
"""
|
217 |
-
)
|
218 |
-
|
219 |
-
with st.expander("See a demo"):
|
220 |
-
st.image("https://i.imgur.com/Z3dk6Tr.gif")
|
221 |
-
|
222 |
-
row1_col1, row1_col2, row1_col3, row1_col4, row1_col5 = st.columns(
|
223 |
-
[0.6, 0.8, 0.6, 1.4, 2]
|
224 |
-
)
|
225 |
-
with row1_col1:
|
226 |
-
frequency = st.selectbox("Monthly/weekly data", ["Monthly", "Weekly"])
|
227 |
-
with row1_col2:
|
228 |
-
types = ["Current month data", "Historical data"]
|
229 |
-
if frequency == "Weekly":
|
230 |
-
types.remove("Current month data")
|
231 |
-
cur_hist = st.selectbox(
|
232 |
-
"Current/historical data",
|
233 |
-
types,
|
234 |
-
)
|
235 |
-
with row1_col3:
|
236 |
-
if frequency == "Monthly":
|
237 |
-
scale = st.selectbox(
|
238 |
-
"Scale", ["National", "State", "Metro", "County"], index=3
|
239 |
-
)
|
240 |
-
else:
|
241 |
-
scale = st.selectbox("Scale", ["National", "Metro"], index=1)
|
242 |
-
|
243 |
-
gdf = get_geom_data(scale.lower())
|
244 |
-
|
245 |
-
if frequency == "Weekly":
|
246 |
-
inventory_df = get_inventory_data(data_links["weekly"][scale.lower()])
|
247 |
-
weeks = get_weeks(inventory_df)
|
248 |
-
with row1_col1:
|
249 |
-
selected_date = st.date_input("Select a date", value=weeks[-1])
|
250 |
-
saturday = get_saturday(selected_date)
|
251 |
-
selected_period = saturday.strftime("%-m/%-d/%Y")
|
252 |
-
if saturday not in weeks:
|
253 |
-
st.error(
|
254 |
-
"The selected date is not available in the data. Please select a date between {} and {}".format(
|
255 |
-
weeks[0], weeks[-1]
|
256 |
-
)
|
257 |
-
)
|
258 |
-
selected_period = weeks[-1].strftime("%-m/%-d/%Y")
|
259 |
-
inventory_df = get_inventory_data(data_links["weekly"][scale.lower()])
|
260 |
-
inventory_df = filter_weekly_inventory(inventory_df, selected_period)
|
261 |
-
|
262 |
-
if frequency == "Monthly":
|
263 |
-
if cur_hist == "Current month data":
|
264 |
-
inventory_df = get_inventory_data(
|
265 |
-
data_links["monthly_current"][scale.lower()]
|
266 |
-
)
|
267 |
-
selected_period = get_periods(inventory_df)[0]
|
268 |
-
else:
|
269 |
-
with row1_col2:
|
270 |
-
inventory_df = get_inventory_data(
|
271 |
-
data_links["monthly_historical"][scale.lower()]
|
272 |
-
)
|
273 |
-
start_year, end_year = get_start_end_year(inventory_df)
|
274 |
-
periods = get_periods(inventory_df)
|
275 |
-
with st.expander("Select year and month", True):
|
276 |
-
selected_year = st.slider(
|
277 |
-
"Year",
|
278 |
-
start_year,
|
279 |
-
end_year,
|
280 |
-
value=start_year,
|
281 |
-
step=1,
|
282 |
-
)
|
283 |
-
selected_month = st.slider(
|
284 |
-
"Month",
|
285 |
-
min_value=1,
|
286 |
-
max_value=12,
|
287 |
-
value=int(periods[0][-2:]),
|
288 |
-
step=1,
|
289 |
-
)
|
290 |
-
selected_period = str(selected_year) + str(selected_month).zfill(2)
|
291 |
-
if selected_period not in periods:
|
292 |
-
st.error("Data not available for selected year and month")
|
293 |
-
selected_period = periods[0]
|
294 |
-
inventory_df = inventory_df[
|
295 |
-
inventory_df["month_date_yyyymm"] == int(selected_period)
|
296 |
-
]
|
297 |
-
|
298 |
-
data_cols = get_data_columns(inventory_df, scale.lower(), frequency.lower())
|
299 |
-
|
300 |
-
with row1_col4:
|
301 |
-
selected_col = st.selectbox("Attribute", data_cols)
|
302 |
-
with row1_col5:
|
303 |
-
show_desc = st.checkbox("Show attribute description")
|
304 |
-
if show_desc:
|
305 |
-
try:
|
306 |
-
label, desc = get_data_dict(selected_col.strip())
|
307 |
-
markdown = f"""
|
308 |
-
**{label}**: {desc}
|
309 |
-
"""
|
310 |
-
st.markdown(markdown)
|
311 |
-
except:
|
312 |
-
st.warning("No description available for selected attribute")
|
313 |
-
|
314 |
-
row2_col1, row2_col2, row2_col3, row2_col4, row2_col5, row2_col6 = st.columns(
|
315 |
-
[0.6, 0.68, 0.7, 0.7, 1.5, 0.8]
|
316 |
-
)
|
317 |
-
|
318 |
-
palettes = cm.list_colormaps()
|
319 |
-
with row2_col1:
|
320 |
-
palette = st.selectbox("Color palette", palettes, index=palettes.index("Blues"))
|
321 |
-
with row2_col2:
|
322 |
-
n_colors = st.slider("Number of colors", min_value=2, max_value=20, value=8)
|
323 |
-
with row2_col3:
|
324 |
-
show_nodata = st.checkbox("Show nodata areas", value=True)
|
325 |
-
with row2_col4:
|
326 |
-
show_3d = st.checkbox("Show 3D view", value=False)
|
327 |
-
with row2_col5:
|
328 |
-
if show_3d:
|
329 |
-
elev_scale = st.slider(
|
330 |
-
"Elevation scale", min_value=1, max_value=1000000, value=1, step=10
|
331 |
-
)
|
332 |
-
with row2_col6:
|
333 |
-
st.info("Press Ctrl and move the left mouse button.")
|
334 |
-
else:
|
335 |
-
elev_scale = 1
|
336 |
-
|
337 |
-
gdf = join_attributes(gdf, inventory_df, scale.lower())
|
338 |
-
gdf_null = select_null(gdf, selected_col)
|
339 |
-
gdf = select_non_null(gdf, selected_col)
|
340 |
-
gdf = gdf.sort_values(by=selected_col, ascending=True)
|
341 |
-
|
342 |
-
colors = cm.get_palette(palette, n_colors)
|
343 |
-
colors = [hex_to_rgb(c) for c in colors]
|
344 |
-
|
345 |
-
for i, ind in enumerate(gdf.index):
|
346 |
-
index = int(i / (len(gdf) / len(colors)))
|
347 |
-
if index >= len(colors):
|
348 |
-
index = len(colors) - 1
|
349 |
-
gdf.loc[ind, "R"] = colors[index][0]
|
350 |
-
gdf.loc[ind, "G"] = colors[index][1]
|
351 |
-
gdf.loc[ind, "B"] = colors[index][2]
|
352 |
-
|
353 |
-
initial_view_state = pdk.ViewState(
|
354 |
-
latitude=40, longitude=-100, zoom=3, max_zoom=16, pitch=0, bearing=0
|
355 |
-
)
|
356 |
-
|
357 |
-
min_value = gdf[selected_col].min()
|
358 |
-
max_value = gdf[selected_col].max()
|
359 |
-
color = "color"
|
360 |
-
# color_exp = f"[({selected_col}-{min_value})/({max_value}-{min_value})*255, 0, 0]"
|
361 |
-
color_exp = f"[R, G, B]"
|
362 |
-
|
363 |
-
geojson = pdk.Layer(
|
364 |
-
"GeoJsonLayer",
|
365 |
-
gdf,
|
366 |
-
pickable=True,
|
367 |
-
opacity=0.5,
|
368 |
-
stroked=True,
|
369 |
-
filled=True,
|
370 |
-
extruded=show_3d,
|
371 |
-
wireframe=True,
|
372 |
-
get_elevation=f"{selected_col}",
|
373 |
-
elevation_scale=elev_scale,
|
374 |
-
# get_fill_color="color",
|
375 |
-
get_fill_color=color_exp,
|
376 |
-
get_line_color=[0, 0, 0],
|
377 |
-
get_line_width=2,
|
378 |
-
line_width_min_pixels=1,
|
379 |
-
)
|
380 |
-
|
381 |
-
geojson_null = pdk.Layer(
|
382 |
-
"GeoJsonLayer",
|
383 |
-
gdf_null,
|
384 |
-
pickable=True,
|
385 |
-
opacity=0.2,
|
386 |
-
stroked=True,
|
387 |
-
filled=True,
|
388 |
-
extruded=False,
|
389 |
-
wireframe=True,
|
390 |
-
# get_elevation="properties.ALAND/100000",
|
391 |
-
# get_fill_color="color",
|
392 |
-
get_fill_color=[200, 200, 200],
|
393 |
-
get_line_color=[0, 0, 0],
|
394 |
-
get_line_width=2,
|
395 |
-
line_width_min_pixels=1,
|
396 |
-
)
|
397 |
-
|
398 |
-
# tooltip = {"text": "Name: {NAME}"}
|
399 |
-
|
400 |
-
# tooltip_value = f"<b>Value:</b> {median_listing_price}""
|
401 |
-
tooltip = {
|
402 |
-
"html": "<b>Name:</b> {NAME}<br><b>Value:</b> {"
|
403 |
-
+ selected_col
|
404 |
-
+ "}<br><b>Date:</b> "
|
405 |
-
+ selected_period
|
406 |
-
+ "",
|
407 |
-
"style": {"backgroundColor": "steelblue", "color": "white"},
|
408 |
-
}
|
409 |
-
|
410 |
-
layers = [geojson]
|
411 |
-
if show_nodata:
|
412 |
-
layers.append(geojson_null)
|
413 |
-
|
414 |
-
r = pdk.Deck(
|
415 |
-
layers=layers,
|
416 |
-
initial_view_state=initial_view_state,
|
417 |
-
map_style="light",
|
418 |
-
tooltip=tooltip,
|
419 |
-
)
|
420 |
-
|
421 |
-
row3_col1, row3_col2 = st.columns([6, 1])
|
422 |
-
|
423 |
-
with row3_col1:
|
424 |
-
st.pydeck_chart(r)
|
425 |
-
with row3_col2:
|
426 |
-
st.write(
|
427 |
-
cm.create_colormap(
|
428 |
-
palette,
|
429 |
-
label=selected_col.replace("_", " ").title(),
|
430 |
-
width=0.2,
|
431 |
-
height=3,
|
432 |
-
orientation="vertical",
|
433 |
-
vmin=min_value,
|
434 |
-
vmax=max_value,
|
435 |
-
font_size=10,
|
436 |
-
)
|
437 |
-
)
|
438 |
-
row4_col1, row4_col2, row4_col3 = st.columns([1, 2, 3])
|
439 |
-
with row4_col1:
|
440 |
-
show_data = st.checkbox("Show raw data")
|
441 |
-
with row4_col2:
|
442 |
-
show_cols = st.multiselect("Select columns", data_cols)
|
443 |
-
with row4_col3:
|
444 |
-
show_colormaps = st.checkbox("Preview all color palettes")
|
445 |
-
if show_colormaps:
|
446 |
-
st.write(cm.plot_colormaps(return_fig=True))
|
447 |
-
if show_data:
|
448 |
-
if scale == "National":
|
449 |
-
st.dataframe(gdf[["NAME", "GEOID"] + show_cols])
|
450 |
-
elif scale == "State":
|
451 |
-
st.dataframe(gdf[["NAME", "STUSPS"] + show_cols])
|
452 |
-
elif scale == "County":
|
453 |
-
st.dataframe(gdf[["NAME", "STATEFP", "COUNTYFP"] + show_cols])
|
454 |
-
elif scale == "Metro":
|
455 |
-
st.dataframe(gdf[["NAME", "CBSAFP"] + show_cols])
|
456 |
-
elif scale == "Zip":
|
457 |
-
st.dataframe(gdf[["GEOID10"] + show_cols])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/hurricane.py
DELETED
@@ -1,52 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import tropycal.tracks as tracks
|
3 |
-
|
4 |
-
|
5 |
-
@st.cache(allow_output_mutation=True)
|
6 |
-
def read_data(basin='north_atlantic', source='hurdat', include_btk=False):
|
7 |
-
return tracks.TrackDataset(basin=basin, source=source, include_btk=include_btk)
|
8 |
-
|
9 |
-
|
10 |
-
def app():
|
11 |
-
|
12 |
-
st.title("Hurricane Mapping")
|
13 |
-
|
14 |
-
row1_col1, row1_col2 = st.columns([3, 1])
|
15 |
-
|
16 |
-
with row1_col1:
|
17 |
-
empty = st.empty()
|
18 |
-
empty.image("https://i.imgur.com/Ec7qsR0.png")
|
19 |
-
|
20 |
-
with row1_col2:
|
21 |
-
|
22 |
-
checkbox = st.checkbox("Select from a list of hurricanes", value=False)
|
23 |
-
if checkbox:
|
24 |
-
if st.session_state.get('hurricane') is None:
|
25 |
-
st.session_state['hurricane'] = read_data()
|
26 |
-
|
27 |
-
years = st.slider(
|
28 |
-
'Select a year', min_value=1950, max_value=2022, value=(2000, 2010)
|
29 |
-
)
|
30 |
-
storms = st.session_state['hurricane'].filter_storms(year_range=years)
|
31 |
-
selected = st.selectbox('Select a storm', storms)
|
32 |
-
storm = st.session_state['hurricane'].get_storm(selected)
|
33 |
-
ax = storm.plot()
|
34 |
-
fig = ax.get_figure()
|
35 |
-
empty.pyplot(fig)
|
36 |
-
else:
|
37 |
-
|
38 |
-
name = st.text_input("Or enter a storm Name", "michael")
|
39 |
-
if name:
|
40 |
-
if st.session_state.get('hurricane') is None:
|
41 |
-
st.session_state['hurricane'] = read_data()
|
42 |
-
basin = st.session_state['hurricane']
|
43 |
-
years = basin.search_name(name)
|
44 |
-
if len(years) > 0:
|
45 |
-
year = st.selectbox("Select a year", years)
|
46 |
-
storm = basin.get_storm((name, year))
|
47 |
-
ax = storm.plot()
|
48 |
-
fig = ax.get_figure()
|
49 |
-
empty.pyplot(fig)
|
50 |
-
else:
|
51 |
-
empty.text("No storms found")
|
52 |
-
st.write("No storms found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/plotly_maps.py
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.plotlymap as leafmap
|
3 |
-
|
4 |
-
|
5 |
-
def app():
|
6 |
-
|
7 |
-
st.title("Plotly Maps")
|
8 |
-
m = leafmap.Map(basemap="street", height=650)
|
9 |
-
m.add_mapbox_layer(style="streets")
|
10 |
-
|
11 |
-
basemaps = list(leafmap.basemaps.keys())
|
12 |
-
basemap = st.selectbox(
|
13 |
-
"Select a basemap", basemaps, basemaps.index("Stamen.Terrain")
|
14 |
-
)
|
15 |
-
m.add_basemap(basemap)
|
16 |
-
|
17 |
-
st.plotly_chart(m, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/raster.py
DELETED
@@ -1,77 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
import streamlit as st
|
4 |
-
import palettable
|
5 |
-
|
6 |
-
|
7 |
-
@st.cache(allow_output_mutation=True)
|
8 |
-
def load_cog_list():
|
9 |
-
print(os.getcwd())
|
10 |
-
in_txt = os.path.join(os.getcwd(), "data/cog_files.txt")
|
11 |
-
with open(in_txt) as f:
|
12 |
-
return [line.strip() for line in f.readlines()[1:]]
|
13 |
-
|
14 |
-
|
15 |
-
@st.cache(allow_output_mutation=True)
|
16 |
-
def get_palettes():
|
17 |
-
palettes = dir(palettable.matplotlib)[:-16]
|
18 |
-
return ["matplotlib." + p for p in palettes]
|
19 |
-
|
20 |
-
|
21 |
-
def app():
|
22 |
-
|
23 |
-
st.title("Visualize Raster Datasets")
|
24 |
-
st.markdown(
|
25 |
-
"""
|
26 |
-
An interactive web app for visualizing local raster datasets and Cloud Optimized GeoTIFF ([COG](https://www.cogeo.org)). The app was built using [streamlit](https://streamlit.io), [leafmap](https://leafmap.org), and [localtileserver](https://github.com/banesullivan/localtileserver).
|
27 |
-
|
28 |
-
|
29 |
-
"""
|
30 |
-
)
|
31 |
-
|
32 |
-
row1_col1, row1_col2 = st.columns([2, 1])
|
33 |
-
|
34 |
-
with row1_col1:
|
35 |
-
cog_list = load_cog_list()
|
36 |
-
cog = st.selectbox("Select a sample Cloud Opitmized GeoTIFF (COG)", cog_list)
|
37 |
-
|
38 |
-
with row1_col2:
|
39 |
-
empty = st.empty()
|
40 |
-
|
41 |
-
url = empty.text_input(
|
42 |
-
"Enter a HTTP URL to a Cloud Optimized GeoTIFF (COG)",
|
43 |
-
cog,
|
44 |
-
)
|
45 |
-
|
46 |
-
data = st.file_uploader("Upload a raster dataset", type=["tif", "img"])
|
47 |
-
|
48 |
-
if data:
|
49 |
-
url = empty.text_input(
|
50 |
-
"Enter a URL to a Cloud Optimized GeoTIFF (COG)",
|
51 |
-
"",
|
52 |
-
)
|
53 |
-
|
54 |
-
add_palette = st.checkbox("Add a color palette")
|
55 |
-
if add_palette:
|
56 |
-
palette = st.selectbox("Select a color palette", get_palettes())
|
57 |
-
else:
|
58 |
-
palette = None
|
59 |
-
|
60 |
-
submit = st.button("Submit")
|
61 |
-
|
62 |
-
m = leafmap.Map(latlon_control=False)
|
63 |
-
|
64 |
-
if submit:
|
65 |
-
if data or url:
|
66 |
-
try:
|
67 |
-
if data:
|
68 |
-
file_path = leafmap.save_data(data)
|
69 |
-
m.add_local_tile(file_path, palette=palette, debug=True)
|
70 |
-
elif url:
|
71 |
-
m.add_remote_tile(url, palette=palette, debug=True)
|
72 |
-
except Exception as e:
|
73 |
-
with row1_col2:
|
74 |
-
st.error("Work in progress. Try it again later.")
|
75 |
-
|
76 |
-
with row1_col1:
|
77 |
-
m.to_streamlit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/rois.py
DELETED
@@ -1,174 +0,0 @@
|
|
1 |
-
""" A module for storing some sample ROIs for creating Landsat/GOES timelapse.
|
2 |
-
"""
|
3 |
-
|
4 |
-
from shapely.geometry import Polygon
|
5 |
-
|
6 |
-
goes_rois = {
|
7 |
-
"Creek Fire, CA (2020-09-05)": {
|
8 |
-
"region": Polygon(
|
9 |
-
[
|
10 |
-
[-121.003418, 36.848857],
|
11 |
-
[-121.003418, 39.049052],
|
12 |
-
[-117.905273, 39.049052],
|
13 |
-
[-117.905273, 36.848857],
|
14 |
-
[-121.003418, 36.848857],
|
15 |
-
]
|
16 |
-
),
|
17 |
-
"start_time": "2020-09-05T15:00:00",
|
18 |
-
"end_time": "2020-09-06T02:00:00",
|
19 |
-
},
|
20 |
-
"Bomb Cyclone (2021-10-24)": {
|
21 |
-
"region": Polygon(
|
22 |
-
[
|
23 |
-
[-159.5954, 60.4088],
|
24 |
-
[-159.5954, 24.5178],
|
25 |
-
[-114.2438, 24.5178],
|
26 |
-
[-114.2438, 60.4088],
|
27 |
-
]
|
28 |
-
),
|
29 |
-
"start_time": "2021-10-24T14:00:00",
|
30 |
-
"end_time": "2021-10-25T01:00:00",
|
31 |
-
},
|
32 |
-
"Hunga Tonga Volcanic Eruption (2022-01-15)": {
|
33 |
-
"region": Polygon(
|
34 |
-
[
|
35 |
-
[-192.480469, -32.546813],
|
36 |
-
[-192.480469, -8.754795],
|
37 |
-
[-157.587891, -8.754795],
|
38 |
-
[-157.587891, -32.546813],
|
39 |
-
[-192.480469, -32.546813],
|
40 |
-
]
|
41 |
-
),
|
42 |
-
"start_time": "2022-01-15T03:00:00",
|
43 |
-
"end_time": "2022-01-15T07:00:00",
|
44 |
-
},
|
45 |
-
"Hunga Tonga Volcanic Eruption Closer Look (2022-01-15)": {
|
46 |
-
"region": Polygon(
|
47 |
-
[
|
48 |
-
[-178.901367, -22.958393],
|
49 |
-
[-178.901367, -17.85329],
|
50 |
-
[-171.452637, -17.85329],
|
51 |
-
[-171.452637, -22.958393],
|
52 |
-
[-178.901367, -22.958393],
|
53 |
-
]
|
54 |
-
),
|
55 |
-
"start_time": "2022-01-15T03:00:00",
|
56 |
-
"end_time": "2022-01-15T07:00:00",
|
57 |
-
},
|
58 |
-
}
|
59 |
-
|
60 |
-
|
61 |
-
landsat_rois = {
|
62 |
-
"Aral Sea": Polygon(
|
63 |
-
[
|
64 |
-
[57.667236, 43.834527],
|
65 |
-
[57.667236, 45.996962],
|
66 |
-
[61.12793, 45.996962],
|
67 |
-
[61.12793, 43.834527],
|
68 |
-
[57.667236, 43.834527],
|
69 |
-
]
|
70 |
-
),
|
71 |
-
"Dubai": Polygon(
|
72 |
-
[
|
73 |
-
[54.541626, 24.763044],
|
74 |
-
[54.541626, 25.427152],
|
75 |
-
[55.632019, 25.427152],
|
76 |
-
[55.632019, 24.763044],
|
77 |
-
[54.541626, 24.763044],
|
78 |
-
]
|
79 |
-
),
|
80 |
-
"Hong Kong International Airport": Polygon(
|
81 |
-
[
|
82 |
-
[113.825226, 22.198849],
|
83 |
-
[113.825226, 22.349758],
|
84 |
-
[114.085121, 22.349758],
|
85 |
-
[114.085121, 22.198849],
|
86 |
-
[113.825226, 22.198849],
|
87 |
-
]
|
88 |
-
),
|
89 |
-
"Las Vegas, NV": Polygon(
|
90 |
-
[
|
91 |
-
[-115.554199, 35.804449],
|
92 |
-
[-115.554199, 36.558188],
|
93 |
-
[-113.903503, 36.558188],
|
94 |
-
[-113.903503, 35.804449],
|
95 |
-
[-115.554199, 35.804449],
|
96 |
-
]
|
97 |
-
),
|
98 |
-
"Pucallpa, Peru": Polygon(
|
99 |
-
[
|
100 |
-
[-74.672699, -8.600032],
|
101 |
-
[-74.672699, -8.254983],
|
102 |
-
[-74.279938, -8.254983],
|
103 |
-
[-74.279938, -8.600032],
|
104 |
-
]
|
105 |
-
),
|
106 |
-
"Sierra Gorda, Chile": Polygon(
|
107 |
-
[
|
108 |
-
[-69.315491, -22.837104],
|
109 |
-
[-69.315491, -22.751488],
|
110 |
-
[-69.190006, -22.751488],
|
111 |
-
[-69.190006, -22.837104],
|
112 |
-
[-69.315491, -22.837104],
|
113 |
-
]
|
114 |
-
),
|
115 |
-
}
|
116 |
-
|
117 |
-
modis_rois = {
|
118 |
-
"World": Polygon(
|
119 |
-
[
|
120 |
-
[-171.210938, -57.136239],
|
121 |
-
[-171.210938, 79.997168],
|
122 |
-
[177.539063, 79.997168],
|
123 |
-
[177.539063, -57.136239],
|
124 |
-
[-171.210938, -57.136239],
|
125 |
-
]
|
126 |
-
),
|
127 |
-
"Africa": Polygon(
|
128 |
-
[
|
129 |
-
[-18.6983, 38.1446],
|
130 |
-
[-18.6983, -36.1630],
|
131 |
-
[52.2293, -36.1630],
|
132 |
-
[52.2293, 38.1446],
|
133 |
-
]
|
134 |
-
),
|
135 |
-
"USA": Polygon(
|
136 |
-
[
|
137 |
-
[-127.177734, 23.725012],
|
138 |
-
[-127.177734, 50.792047],
|
139 |
-
[-66.269531, 50.792047],
|
140 |
-
[-66.269531, 23.725012],
|
141 |
-
[-127.177734, 23.725012],
|
142 |
-
]
|
143 |
-
),
|
144 |
-
}
|
145 |
-
|
146 |
-
ocean_rois = {
|
147 |
-
"Gulf of Mexico": Polygon(
|
148 |
-
[
|
149 |
-
[-101.206055, 15.496032],
|
150 |
-
[-101.206055, 32.361403],
|
151 |
-
[-75.673828, 32.361403],
|
152 |
-
[-75.673828, 15.496032],
|
153 |
-
[-101.206055, 15.496032],
|
154 |
-
]
|
155 |
-
),
|
156 |
-
"North Atlantic Ocean": Polygon(
|
157 |
-
[
|
158 |
-
[-85.341797, 24.046464],
|
159 |
-
[-85.341797, 45.02695],
|
160 |
-
[-55.810547, 45.02695],
|
161 |
-
[-55.810547, 24.046464],
|
162 |
-
[-85.341797, 24.046464],
|
163 |
-
]
|
164 |
-
),
|
165 |
-
"World": Polygon(
|
166 |
-
[
|
167 |
-
[-171.210938, -57.136239],
|
168 |
-
[-171.210938, 79.997168],
|
169 |
-
[177.539063, 79.997168],
|
170 |
-
[177.539063, -57.136239],
|
171 |
-
[-171.210938, -57.136239],
|
172 |
-
]
|
173 |
-
),
|
174 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/timelapse.py
DELETED
@@ -1,1314 +0,0 @@
|
|
1 |
-
import ee
|
2 |
-
import os
|
3 |
-
import datetime
|
4 |
-
import fiona
|
5 |
-
import geopandas as gpd
|
6 |
-
import folium
|
7 |
-
import streamlit as st
|
8 |
-
import geemap.colormaps as cm
|
9 |
-
import geemap.foliumap as geemap
|
10 |
-
from datetime import date
|
11 |
-
from .rois import *
|
12 |
-
|
13 |
-
|
14 |
-
@st.cache(allow_output_mutation=True)
|
15 |
-
def uploaded_file_to_gdf(data):
|
16 |
-
import tempfile
|
17 |
-
import os
|
18 |
-
import uuid
|
19 |
-
|
20 |
-
_, file_extension = os.path.splitext(data.name)
|
21 |
-
file_id = str(uuid.uuid4())
|
22 |
-
file_path = os.path.join(tempfile.gettempdir(), f"{file_id}{file_extension}")
|
23 |
-
|
24 |
-
with open(file_path, "wb") as file:
|
25 |
-
file.write(data.getbuffer())
|
26 |
-
|
27 |
-
if file_path.lower().endswith(".kml"):
|
28 |
-
fiona.drvsupport.supported_drivers["KML"] = "rw"
|
29 |
-
gdf = gpd.read_file(file_path, driver="KML")
|
30 |
-
else:
|
31 |
-
gdf = gpd.read_file(file_path)
|
32 |
-
|
33 |
-
return gdf
|
34 |
-
|
35 |
-
|
36 |
-
def app():
|
37 |
-
|
38 |
-
today = date.today()
|
39 |
-
|
40 |
-
st.title("Create Timelapse")
|
41 |
-
|
42 |
-
st.markdown(
|
43 |
-
"""
|
44 |
-
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.
|
45 |
-
The app was built using [streamlit](https://streamlit.io), [geemap](https://geemap.org), and [Google Earth Engine](https://earthengine.google.com). For more info, check out my streamlit [blog post](https://blog.streamlit.io/creating-satellite-timelapse-with-streamlit-and-earth-engine).
|
46 |
-
"""
|
47 |
-
)
|
48 |
-
|
49 |
-
row1_col1, row1_col2 = st.columns([2, 1])
|
50 |
-
|
51 |
-
if st.session_state.get("zoom_level") is None:
|
52 |
-
st.session_state["zoom_level"] = 4
|
53 |
-
|
54 |
-
st.session_state["ee_asset_id"] = None
|
55 |
-
st.session_state["bands"] = None
|
56 |
-
st.session_state["palette"] = None
|
57 |
-
st.session_state["vis_params"] = None
|
58 |
-
|
59 |
-
with row1_col1:
|
60 |
-
m = geemap.Map(
|
61 |
-
basemap="HYBRID",
|
62 |
-
plugin_Draw=True,
|
63 |
-
Draw_export=True,
|
64 |
-
locate_control=True,
|
65 |
-
plugin_LatLngPopup=False,
|
66 |
-
)
|
67 |
-
m.add_basemap("ROADMAP")
|
68 |
-
|
69 |
-
with row1_col2:
|
70 |
-
|
71 |
-
keyword = st.text_input("Search for a location:", "")
|
72 |
-
if keyword:
|
73 |
-
locations = geemap.geocode(keyword)
|
74 |
-
if locations is not None and len(locations) > 0:
|
75 |
-
str_locations = [str(g)[1:-1] for g in locations]
|
76 |
-
location = st.selectbox("Select a location:", str_locations)
|
77 |
-
loc_index = str_locations.index(location)
|
78 |
-
selected_loc = locations[loc_index]
|
79 |
-
lat, lng = selected_loc.lat, selected_loc.lng
|
80 |
-
folium.Marker(location=[lat, lng], popup=location).add_to(m)
|
81 |
-
m.set_center(lng, lat, 12)
|
82 |
-
st.session_state["zoom_level"] = 12
|
83 |
-
|
84 |
-
collection = st.selectbox(
|
85 |
-
"Select a satellite image collection: ",
|
86 |
-
[
|
87 |
-
"Any Earth Engine ImageCollection",
|
88 |
-
"Landsat TM-ETM-OLI Surface Reflectance",
|
89 |
-
"Sentinel-2 MSI Surface Reflectance",
|
90 |
-
"Geostationary Operational Environmental Satellites (GOES)",
|
91 |
-
"MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km",
|
92 |
-
"MODIS Gap filled Land Surface Temperature Daily",
|
93 |
-
"MODIS Ocean Color SMI",
|
94 |
-
"USDA National Agriculture Imagery Program (NAIP)",
|
95 |
-
],
|
96 |
-
index=1,
|
97 |
-
)
|
98 |
-
|
99 |
-
if collection in [
|
100 |
-
"Landsat TM-ETM-OLI Surface Reflectance",
|
101 |
-
"Sentinel-2 MSI Surface Reflectance",
|
102 |
-
]:
|
103 |
-
roi_options = ["Uploaded GeoJSON"] + list(landsat_rois.keys())
|
104 |
-
|
105 |
-
elif collection == "Geostationary Operational Environmental Satellites (GOES)":
|
106 |
-
roi_options = ["Uploaded GeoJSON"] + list(goes_rois.keys())
|
107 |
-
|
108 |
-
elif collection in [
|
109 |
-
"MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km",
|
110 |
-
"MODIS Gap filled Land Surface Temperature Daily",
|
111 |
-
]:
|
112 |
-
roi_options = ["Uploaded GeoJSON"] + list(modis_rois.keys())
|
113 |
-
elif collection == "MODIS Ocean Color SMI":
|
114 |
-
roi_options = ["Uploaded GeoJSON"] + list(ocean_rois.keys())
|
115 |
-
else:
|
116 |
-
roi_options = ["Uploaded GeoJSON"]
|
117 |
-
|
118 |
-
if collection == "Any Earth Engine ImageCollection":
|
119 |
-
keyword = st.text_input("Enter a keyword to search (e.g., MODIS):", "")
|
120 |
-
if keyword:
|
121 |
-
|
122 |
-
assets = geemap.search_ee_data(keyword)
|
123 |
-
ee_assets = []
|
124 |
-
for asset in assets:
|
125 |
-
if asset["ee_id_snippet"].startswith("ee.ImageCollection"):
|
126 |
-
ee_assets.append(asset)
|
127 |
-
|
128 |
-
asset_titles = [x["title"] for x in ee_assets]
|
129 |
-
dataset = st.selectbox("Select a dataset:", asset_titles)
|
130 |
-
if len(ee_assets) > 0:
|
131 |
-
st.session_state["ee_assets"] = ee_assets
|
132 |
-
st.session_state["asset_titles"] = asset_titles
|
133 |
-
index = asset_titles.index(dataset)
|
134 |
-
ee_id = ee_assets[index]["id"]
|
135 |
-
else:
|
136 |
-
ee_id = ""
|
137 |
-
|
138 |
-
if dataset is not None:
|
139 |
-
with st.expander("Show dataset details", False):
|
140 |
-
index = asset_titles.index(dataset)
|
141 |
-
html = geemap.ee_data_html(st.session_state["ee_assets"][index])
|
142 |
-
st.markdown(html, True)
|
143 |
-
# elif collection == "MODIS Gap filled Land Surface Temperature Daily":
|
144 |
-
# ee_id = ""
|
145 |
-
else:
|
146 |
-
ee_id = ""
|
147 |
-
|
148 |
-
asset_id = st.text_input("Enter an ee.ImageCollection asset ID:", ee_id)
|
149 |
-
|
150 |
-
if asset_id:
|
151 |
-
with st.expander("Customize band combination and color palette", True):
|
152 |
-
try:
|
153 |
-
col = ee.ImageCollection.load(asset_id)
|
154 |
-
st.session_state["ee_asset_id"] = asset_id
|
155 |
-
except:
|
156 |
-
st.error("Invalid Earth Engine asset ID.")
|
157 |
-
st.session_state["ee_asset_id"] = None
|
158 |
-
return
|
159 |
-
|
160 |
-
img_bands = col.first().bandNames().getInfo()
|
161 |
-
if len(img_bands) >= 3:
|
162 |
-
default_bands = img_bands[:3][::-1]
|
163 |
-
else:
|
164 |
-
default_bands = img_bands[:]
|
165 |
-
bands = st.multiselect(
|
166 |
-
"Select one or three bands (RGB):", img_bands, default_bands
|
167 |
-
)
|
168 |
-
st.session_state["bands"] = bands
|
169 |
-
|
170 |
-
if len(bands) == 1:
|
171 |
-
palette_options = st.selectbox(
|
172 |
-
"Color palette",
|
173 |
-
cm.list_colormaps(),
|
174 |
-
index=2,
|
175 |
-
)
|
176 |
-
palette_values = cm.get_palette(palette_options, 15)
|
177 |
-
palette = st.text_area(
|
178 |
-
"Enter a custom palette:",
|
179 |
-
palette_values,
|
180 |
-
)
|
181 |
-
st.write(
|
182 |
-
cm.plot_colormap(cmap=palette_options, return_fig=True)
|
183 |
-
)
|
184 |
-
st.session_state["palette"] = eval(palette)
|
185 |
-
|
186 |
-
if bands:
|
187 |
-
vis_params = st.text_area(
|
188 |
-
"Enter visualization parameters",
|
189 |
-
"{'bands': ["
|
190 |
-
+ ", ".join([f"'{band}'" for band in bands])
|
191 |
-
+ "]}",
|
192 |
-
)
|
193 |
-
else:
|
194 |
-
vis_params = st.text_area(
|
195 |
-
"Enter visualization parameters",
|
196 |
-
"{}",
|
197 |
-
)
|
198 |
-
try:
|
199 |
-
st.session_state["vis_params"] = eval(vis_params)
|
200 |
-
st.session_state["vis_params"]["palette"] = st.session_state[
|
201 |
-
"palette"
|
202 |
-
]
|
203 |
-
except Exception as e:
|
204 |
-
st.session_state["vis_params"] = None
|
205 |
-
st.error(
|
206 |
-
f"Invalid visualization parameters. It must be a dictionary."
|
207 |
-
)
|
208 |
-
|
209 |
-
elif collection == "MODIS Gap filled Land Surface Temperature Daily":
|
210 |
-
with st.expander("Show dataset details", False):
|
211 |
-
st.markdown(
|
212 |
-
"""
|
213 |
-
See the [Awesome GEE Community Datasets](https://samapriya.github.io/awesome-gee-community-datasets/projects/daily_lst/).
|
214 |
-
"""
|
215 |
-
)
|
216 |
-
|
217 |
-
MODIS_options = ["Daytime (1:30 pm)", "Nighttime (1:30 am)"]
|
218 |
-
MODIS_option = st.selectbox("Select a MODIS dataset:", MODIS_options)
|
219 |
-
if MODIS_option == "Daytime (1:30 pm)":
|
220 |
-
st.session_state[
|
221 |
-
"ee_asset_id"
|
222 |
-
] = "projects/sat-io/open-datasets/gap-filled-lst/gf_day_1km"
|
223 |
-
else:
|
224 |
-
st.session_state[
|
225 |
-
"ee_asset_id"
|
226 |
-
] = "projects/sat-io/open-datasets/gap-filled-lst/gf_night_1km"
|
227 |
-
|
228 |
-
palette_options = st.selectbox(
|
229 |
-
"Color palette",
|
230 |
-
cm.list_colormaps(),
|
231 |
-
index=90,
|
232 |
-
)
|
233 |
-
palette_values = cm.get_palette(palette_options, 15)
|
234 |
-
palette = st.text_area(
|
235 |
-
"Enter a custom palette:",
|
236 |
-
palette_values,
|
237 |
-
)
|
238 |
-
st.write(cm.plot_colormap(cmap=palette_options, return_fig=True))
|
239 |
-
st.session_state["palette"] = eval(palette)
|
240 |
-
elif collection == "MODIS Ocean Color SMI":
|
241 |
-
with st.expander("Show dataset details", False):
|
242 |
-
st.markdown(
|
243 |
-
"""
|
244 |
-
See the [Earth Engine Data Catalog](https://developers.google.com/earth-engine/datasets/catalog/NASA_OCEANDATA_MODIS-Aqua_L3SMI).
|
245 |
-
"""
|
246 |
-
)
|
247 |
-
|
248 |
-
MODIS_options = ["Aqua", "Terra"]
|
249 |
-
MODIS_option = st.selectbox("Select a satellite:", MODIS_options)
|
250 |
-
st.session_state["ee_asset_id"] = MODIS_option
|
251 |
-
# if MODIS_option == "Daytime (1:30 pm)":
|
252 |
-
# st.session_state[
|
253 |
-
# "ee_asset_id"
|
254 |
-
# ] = "projects/sat-io/open-datasets/gap-filled-lst/gf_day_1km"
|
255 |
-
# else:
|
256 |
-
# st.session_state[
|
257 |
-
# "ee_asset_id"
|
258 |
-
# ] = "projects/sat-io/open-datasets/gap-filled-lst/gf_night_1km"
|
259 |
-
|
260 |
-
band_dict = {
|
261 |
-
"Chlorophyll a concentration": "chlor_a",
|
262 |
-
"Normalized fluorescence line height": "nflh",
|
263 |
-
"Particulate organic carbon": "poc",
|
264 |
-
"Sea surface temperature": "sst",
|
265 |
-
"Remote sensing reflectance at band 412nm": "Rrs_412",
|
266 |
-
"Remote sensing reflectance at band 443nm": "Rrs_443",
|
267 |
-
"Remote sensing reflectance at band 469nm": "Rrs_469",
|
268 |
-
"Remote sensing reflectance at band 488nm": "Rrs_488",
|
269 |
-
"Remote sensing reflectance at band 531nm": "Rrs_531",
|
270 |
-
"Remote sensing reflectance at band 547nm": "Rrs_547",
|
271 |
-
"Remote sensing reflectance at band 555nm": "Rrs_555",
|
272 |
-
"Remote sensing reflectance at band 645nm": "Rrs_645",
|
273 |
-
"Remote sensing reflectance at band 667nm": "Rrs_667",
|
274 |
-
"Remote sensing reflectance at band 678nm": "Rrs_678",
|
275 |
-
}
|
276 |
-
|
277 |
-
band_options = list(band_dict.keys())
|
278 |
-
band = st.selectbox(
|
279 |
-
"Select a band",
|
280 |
-
band_options,
|
281 |
-
band_options.index("Sea surface temperature"),
|
282 |
-
)
|
283 |
-
st.session_state["band"] = band_dict[band]
|
284 |
-
|
285 |
-
colors = cm.list_colormaps()
|
286 |
-
palette_options = st.selectbox(
|
287 |
-
"Color palette",
|
288 |
-
colors,
|
289 |
-
index=colors.index("coolwarm"),
|
290 |
-
)
|
291 |
-
palette_values = cm.get_palette(palette_options, 15)
|
292 |
-
palette = st.text_area(
|
293 |
-
"Enter a custom palette:",
|
294 |
-
palette_values,
|
295 |
-
)
|
296 |
-
st.write(cm.plot_colormap(cmap=palette_options, return_fig=True))
|
297 |
-
st.session_state["palette"] = eval(palette)
|
298 |
-
|
299 |
-
sample_roi = st.selectbox(
|
300 |
-
"Select a sample ROI or upload a GeoJSON file:",
|
301 |
-
roi_options,
|
302 |
-
index=0,
|
303 |
-
)
|
304 |
-
|
305 |
-
add_outline = st.checkbox(
|
306 |
-
"Overlay an administrative boundary on timelapse", False
|
307 |
-
)
|
308 |
-
|
309 |
-
if add_outline:
|
310 |
-
|
311 |
-
with st.expander("Customize administrative boundary", True):
|
312 |
-
|
313 |
-
overlay_options = {
|
314 |
-
"User-defined": None,
|
315 |
-
"Continents": "continents",
|
316 |
-
"Countries": "countries",
|
317 |
-
"US States": "us_states",
|
318 |
-
"China": "china",
|
319 |
-
}
|
320 |
-
|
321 |
-
overlay = st.selectbox(
|
322 |
-
"Select an administrative boundary:",
|
323 |
-
list(overlay_options.keys()),
|
324 |
-
index=2,
|
325 |
-
)
|
326 |
-
|
327 |
-
overlay_data = overlay_options[overlay]
|
328 |
-
|
329 |
-
if overlay_data is None:
|
330 |
-
overlay_data = st.text_input(
|
331 |
-
"Enter an HTTP URL to a GeoJSON file or an ee.FeatureCollection asset id:",
|
332 |
-
"https://raw.githubusercontent.com/giswqs/geemap/master/examples/data/countries.geojson",
|
333 |
-
)
|
334 |
-
|
335 |
-
overlay_color = st.color_picker(
|
336 |
-
"Select a color for the administrative boundary:", "#000000"
|
337 |
-
)
|
338 |
-
overlay_width = st.slider(
|
339 |
-
"Select a line width for the administrative boundary:", 1, 20, 1
|
340 |
-
)
|
341 |
-
overlay_opacity = st.slider(
|
342 |
-
"Select an opacity for the administrative boundary:",
|
343 |
-
0.0,
|
344 |
-
1.0,
|
345 |
-
1.0,
|
346 |
-
0.05,
|
347 |
-
)
|
348 |
-
else:
|
349 |
-
overlay_data = None
|
350 |
-
overlay_color = "black"
|
351 |
-
overlay_width = 1
|
352 |
-
overlay_opacity = 1
|
353 |
-
|
354 |
-
with row1_col1:
|
355 |
-
|
356 |
-
with st.expander(
|
357 |
-
"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 👉"
|
358 |
-
):
|
359 |
-
video_empty = st.empty()
|
360 |
-
|
361 |
-
data = st.file_uploader(
|
362 |
-
"Upload a GeoJSON file to use as an ROI. Customize timelapse parameters and then click the Submit button 😇👇",
|
363 |
-
type=["geojson", "kml", "zip"],
|
364 |
-
)
|
365 |
-
|
366 |
-
crs = "epsg:4326"
|
367 |
-
if sample_roi == "Uploaded GeoJSON":
|
368 |
-
if data is None:
|
369 |
-
# st.info(
|
370 |
-
# "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"
|
371 |
-
# )
|
372 |
-
if collection in [
|
373 |
-
"Geostationary Operational Environmental Satellites (GOES)",
|
374 |
-
"USDA National Agriculture Imagery Program (NAIP)",
|
375 |
-
] and (not keyword):
|
376 |
-
m.set_center(-100, 40, 3)
|
377 |
-
# else:
|
378 |
-
# m.set_center(4.20, 18.63, zoom=2)
|
379 |
-
else:
|
380 |
-
if collection in [
|
381 |
-
"Landsat TM-ETM-OLI Surface Reflectance",
|
382 |
-
"Sentinel-2 MSI Surface Reflectance",
|
383 |
-
]:
|
384 |
-
gdf = gpd.GeoDataFrame(
|
385 |
-
index=[0], crs=crs, geometry=[landsat_rois[sample_roi]]
|
386 |
-
)
|
387 |
-
elif (
|
388 |
-
collection
|
389 |
-
== "Geostationary Operational Environmental Satellites (GOES)"
|
390 |
-
):
|
391 |
-
gdf = gpd.GeoDataFrame(
|
392 |
-
index=[0], crs=crs, geometry=[goes_rois[sample_roi]["region"]]
|
393 |
-
)
|
394 |
-
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
|
395 |
-
gdf = gpd.GeoDataFrame(
|
396 |
-
index=[0], crs=crs, geometry=[modis_rois[sample_roi]]
|
397 |
-
)
|
398 |
-
|
399 |
-
if sample_roi != "Uploaded GeoJSON":
|
400 |
-
|
401 |
-
if collection in [
|
402 |
-
"Landsat TM-ETM-OLI Surface Reflectance",
|
403 |
-
"Sentinel-2 MSI Surface Reflectance",
|
404 |
-
]:
|
405 |
-
gdf = gpd.GeoDataFrame(
|
406 |
-
index=[0], crs=crs, geometry=[landsat_rois[sample_roi]]
|
407 |
-
)
|
408 |
-
elif (
|
409 |
-
collection
|
410 |
-
== "Geostationary Operational Environmental Satellites (GOES)"
|
411 |
-
):
|
412 |
-
gdf = gpd.GeoDataFrame(
|
413 |
-
index=[0], crs=crs, geometry=[goes_rois[sample_roi]["region"]]
|
414 |
-
)
|
415 |
-
elif collection in [
|
416 |
-
"MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km",
|
417 |
-
"MODIS Gap filled Land Surface Temperature Daily",
|
418 |
-
]:
|
419 |
-
gdf = gpd.GeoDataFrame(
|
420 |
-
index=[0], crs=crs, geometry=[modis_rois[sample_roi]]
|
421 |
-
)
|
422 |
-
elif collection == "MODIS Ocean Color SMI":
|
423 |
-
gdf = gpd.GeoDataFrame(
|
424 |
-
index=[0], crs=crs, geometry=[ocean_rois[sample_roi]]
|
425 |
-
)
|
426 |
-
st.session_state["roi"] = geemap.gdf_to_ee(gdf, geodesic=False)
|
427 |
-
m.add_gdf(gdf, "ROI")
|
428 |
-
|
429 |
-
elif data:
|
430 |
-
gdf = uploaded_file_to_gdf(data)
|
431 |
-
st.session_state["roi"] = geemap.gdf_to_ee(gdf, geodesic=False)
|
432 |
-
m.add_gdf(gdf, "ROI")
|
433 |
-
|
434 |
-
m.to_streamlit(height=600)
|
435 |
-
|
436 |
-
with row1_col2:
|
437 |
-
|
438 |
-
if collection in [
|
439 |
-
"Landsat TM-ETM-OLI Surface Reflectance",
|
440 |
-
"Sentinel-2 MSI Surface Reflectance",
|
441 |
-
]:
|
442 |
-
|
443 |
-
if collection == "Landsat TM-ETM-OLI Surface Reflectance":
|
444 |
-
sensor_start_year = 1984
|
445 |
-
timelapse_title = "Landsat Timelapse"
|
446 |
-
timelapse_speed = 5
|
447 |
-
elif collection == "Sentinel-2 MSI Surface Reflectance":
|
448 |
-
sensor_start_year = 2015
|
449 |
-
timelapse_title = "Sentinel-2 Timelapse"
|
450 |
-
timelapse_speed = 5
|
451 |
-
video_empty.video("https://youtu.be/VVRK_-dEjR4")
|
452 |
-
|
453 |
-
with st.form("submit_landsat_form"):
|
454 |
-
|
455 |
-
roi = None
|
456 |
-
if st.session_state.get("roi") is not None:
|
457 |
-
roi = st.session_state.get("roi")
|
458 |
-
out_gif = geemap.temp_file_path(".gif")
|
459 |
-
|
460 |
-
title = st.text_input(
|
461 |
-
"Enter a title to show on the timelapse: ", timelapse_title
|
462 |
-
)
|
463 |
-
RGB = st.selectbox(
|
464 |
-
"Select an RGB band combination:",
|
465 |
-
[
|
466 |
-
"Red/Green/Blue",
|
467 |
-
"NIR/Red/Green",
|
468 |
-
"SWIR2/SWIR1/NIR",
|
469 |
-
"NIR/SWIR1/Red",
|
470 |
-
"SWIR2/NIR/Red",
|
471 |
-
"SWIR2/SWIR1/Red",
|
472 |
-
"SWIR1/NIR/Blue",
|
473 |
-
"NIR/SWIR1/Blue",
|
474 |
-
"SWIR2/NIR/Green",
|
475 |
-
"SWIR1/NIR/Red",
|
476 |
-
"SWIR2/NIR/SWIR1",
|
477 |
-
"SWIR1/NIR/SWIR2",
|
478 |
-
],
|
479 |
-
index=9,
|
480 |
-
)
|
481 |
-
|
482 |
-
frequency = st.selectbox(
|
483 |
-
"Select a temporal frequency:",
|
484 |
-
["year", "quarter", "month"],
|
485 |
-
index=0,
|
486 |
-
)
|
487 |
-
|
488 |
-
with st.expander("Customize timelapse"):
|
489 |
-
|
490 |
-
speed = st.slider("Frames per second:", 1, 30, timelapse_speed)
|
491 |
-
dimensions = st.slider(
|
492 |
-
"Maximum dimensions (Width*Height) in pixels", 768, 2000, 768
|
493 |
-
)
|
494 |
-
progress_bar_color = st.color_picker(
|
495 |
-
"Progress bar color:", "#0000ff"
|
496 |
-
)
|
497 |
-
years = st.slider(
|
498 |
-
"Start and end year:",
|
499 |
-
sensor_start_year,
|
500 |
-
today.year,
|
501 |
-
(sensor_start_year, today.year),
|
502 |
-
)
|
503 |
-
months = st.slider("Start and end month:", 1, 12, (1, 12))
|
504 |
-
font_size = st.slider("Font size:", 10, 50, 30)
|
505 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
506 |
-
apply_fmask = st.checkbox(
|
507 |
-
"Apply fmask (remove clouds, shadows, snow)", True
|
508 |
-
)
|
509 |
-
font_type = st.selectbox(
|
510 |
-
"Select the font type for the title:",
|
511 |
-
["arial.ttf", "alibaba.otf"],
|
512 |
-
index=0,
|
513 |
-
)
|
514 |
-
fading = st.slider(
|
515 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
516 |
-
)
|
517 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
518 |
-
|
519 |
-
empty_text = st.empty()
|
520 |
-
empty_image = st.empty()
|
521 |
-
empty_fire_image = st.empty()
|
522 |
-
empty_video = st.container()
|
523 |
-
submitted = st.form_submit_button("Submit")
|
524 |
-
if submitted:
|
525 |
-
|
526 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
527 |
-
empty_text.warning(
|
528 |
-
"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."
|
529 |
-
)
|
530 |
-
else:
|
531 |
-
|
532 |
-
empty_text.text("Computing... Please wait...")
|
533 |
-
|
534 |
-
start_year = years[0]
|
535 |
-
end_year = years[1]
|
536 |
-
start_date = str(months[0]).zfill(2) + "-01"
|
537 |
-
end_date = str(months[1]).zfill(2) + "-30"
|
538 |
-
bands = RGB.split("/")
|
539 |
-
|
540 |
-
try:
|
541 |
-
if collection == "Landsat TM-ETM-OLI Surface Reflectance":
|
542 |
-
out_gif = geemap.landsat_timelapse(
|
543 |
-
roi=roi,
|
544 |
-
out_gif=out_gif,
|
545 |
-
start_year=start_year,
|
546 |
-
end_year=end_year,
|
547 |
-
start_date=start_date,
|
548 |
-
end_date=end_date,
|
549 |
-
bands=bands,
|
550 |
-
apply_fmask=apply_fmask,
|
551 |
-
frames_per_second=speed,
|
552 |
-
dimensions=dimensions,
|
553 |
-
overlay_data=overlay_data,
|
554 |
-
overlay_color=overlay_color,
|
555 |
-
overlay_width=overlay_width,
|
556 |
-
overlay_opacity=overlay_opacity,
|
557 |
-
frequency=frequency,
|
558 |
-
date_format=None,
|
559 |
-
title=title,
|
560 |
-
title_xy=("2%", "90%"),
|
561 |
-
add_text=True,
|
562 |
-
text_xy=("2%", "2%"),
|
563 |
-
text_sequence=None,
|
564 |
-
font_type=font_type,
|
565 |
-
font_size=font_size,
|
566 |
-
font_color=font_color,
|
567 |
-
add_progress_bar=True,
|
568 |
-
progress_bar_color=progress_bar_color,
|
569 |
-
progress_bar_height=5,
|
570 |
-
loop=0,
|
571 |
-
mp4=mp4,
|
572 |
-
fading=fading,
|
573 |
-
)
|
574 |
-
elif collection == "Sentinel-2 MSI Surface Reflectance":
|
575 |
-
out_gif = geemap.sentinel2_timelapse(
|
576 |
-
roi=roi,
|
577 |
-
out_gif=out_gif,
|
578 |
-
start_year=start_year,
|
579 |
-
end_year=end_year,
|
580 |
-
start_date=start_date,
|
581 |
-
end_date=end_date,
|
582 |
-
bands=bands,
|
583 |
-
apply_fmask=apply_fmask,
|
584 |
-
frames_per_second=speed,
|
585 |
-
dimensions=dimensions,
|
586 |
-
overlay_data=overlay_data,
|
587 |
-
overlay_color=overlay_color,
|
588 |
-
overlay_width=overlay_width,
|
589 |
-
overlay_opacity=overlay_opacity,
|
590 |
-
frequency=frequency,
|
591 |
-
date_format=None,
|
592 |
-
title=title,
|
593 |
-
title_xy=("2%", "90%"),
|
594 |
-
add_text=True,
|
595 |
-
text_xy=("2%", "2%"),
|
596 |
-
text_sequence=None,
|
597 |
-
font_type=font_type,
|
598 |
-
font_size=font_size,
|
599 |
-
font_color=font_color,
|
600 |
-
add_progress_bar=True,
|
601 |
-
progress_bar_color=progress_bar_color,
|
602 |
-
progress_bar_height=5,
|
603 |
-
loop=0,
|
604 |
-
mp4=mp4,
|
605 |
-
fading=fading,
|
606 |
-
)
|
607 |
-
except:
|
608 |
-
empty_text.error(
|
609 |
-
"An error occurred while computing the timelapse. Your probably requested too much data. Try reducing the ROI or timespan."
|
610 |
-
)
|
611 |
-
st.stop()
|
612 |
-
|
613 |
-
if out_gif is not None and os.path.exists(out_gif):
|
614 |
-
|
615 |
-
empty_text.text(
|
616 |
-
"Right click the GIF to save it to your computer👇"
|
617 |
-
)
|
618 |
-
empty_image.image(out_gif)
|
619 |
-
|
620 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
621 |
-
if mp4 and os.path.exists(out_mp4):
|
622 |
-
with empty_video:
|
623 |
-
st.text(
|
624 |
-
"Right click the MP4 to save it to your computer👇"
|
625 |
-
)
|
626 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
627 |
-
|
628 |
-
else:
|
629 |
-
empty_text.error(
|
630 |
-
"Something went wrong. You probably requested too much data. Try reducing the ROI or timespan."
|
631 |
-
)
|
632 |
-
|
633 |
-
elif collection == "Geostationary Operational Environmental Satellites (GOES)":
|
634 |
-
|
635 |
-
video_empty.video("https://youtu.be/16fA2QORG4A")
|
636 |
-
|
637 |
-
with st.form("submit_goes_form"):
|
638 |
-
|
639 |
-
roi = None
|
640 |
-
if st.session_state.get("roi") is not None:
|
641 |
-
roi = st.session_state.get("roi")
|
642 |
-
out_gif = geemap.temp_file_path(".gif")
|
643 |
-
|
644 |
-
satellite = st.selectbox("Select a satellite:", ["GOES-17", "GOES-16"])
|
645 |
-
earliest_date = datetime.date(2017, 7, 10)
|
646 |
-
latest_date = datetime.date.today()
|
647 |
-
|
648 |
-
if sample_roi == "Uploaded GeoJSON":
|
649 |
-
roi_start_date = today - datetime.timedelta(days=2)
|
650 |
-
roi_end_date = today - datetime.timedelta(days=1)
|
651 |
-
roi_start_time = datetime.time(14, 00)
|
652 |
-
roi_end_time = datetime.time(1, 00)
|
653 |
-
else:
|
654 |
-
roi_start = goes_rois[sample_roi]["start_time"]
|
655 |
-
roi_end = goes_rois[sample_roi]["end_time"]
|
656 |
-
roi_start_date = datetime.datetime.strptime(
|
657 |
-
roi_start[:10], "%Y-%m-%d"
|
658 |
-
)
|
659 |
-
roi_end_date = datetime.datetime.strptime(roi_end[:10], "%Y-%m-%d")
|
660 |
-
roi_start_time = datetime.time(
|
661 |
-
int(roi_start[11:13]), int(roi_start[14:16])
|
662 |
-
)
|
663 |
-
roi_end_time = datetime.time(
|
664 |
-
int(roi_end[11:13]), int(roi_end[14:16])
|
665 |
-
)
|
666 |
-
|
667 |
-
start_date = st.date_input("Select the start date:", roi_start_date)
|
668 |
-
end_date = st.date_input("Select the end date:", roi_end_date)
|
669 |
-
|
670 |
-
with st.expander("Customize timelapse"):
|
671 |
-
|
672 |
-
add_fire = st.checkbox("Add Fire/Hotspot Characterization", False)
|
673 |
-
|
674 |
-
scan_type = st.selectbox(
|
675 |
-
"Select a scan type:", ["Full Disk", "CONUS", "Mesoscale"]
|
676 |
-
)
|
677 |
-
|
678 |
-
start_time = st.time_input(
|
679 |
-
"Select the start time of the start date:", roi_start_time
|
680 |
-
)
|
681 |
-
|
682 |
-
end_time = st.time_input(
|
683 |
-
"Select the end time of the end date:", roi_end_time
|
684 |
-
)
|
685 |
-
|
686 |
-
start = (
|
687 |
-
start_date.strftime("%Y-%m-%d")
|
688 |
-
+ "T"
|
689 |
-
+ start_time.strftime("%H:%M:%S")
|
690 |
-
)
|
691 |
-
end = (
|
692 |
-
end_date.strftime("%Y-%m-%d")
|
693 |
-
+ "T"
|
694 |
-
+ end_time.strftime("%H:%M:%S")
|
695 |
-
)
|
696 |
-
|
697 |
-
speed = st.slider("Frames per second:", 1, 30, 5)
|
698 |
-
add_progress_bar = st.checkbox("Add a progress bar", True)
|
699 |
-
progress_bar_color = st.color_picker(
|
700 |
-
"Progress bar color:", "#0000ff"
|
701 |
-
)
|
702 |
-
font_size = st.slider("Font size:", 10, 50, 20)
|
703 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
704 |
-
fading = st.slider(
|
705 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
706 |
-
)
|
707 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
708 |
-
|
709 |
-
empty_text = st.empty()
|
710 |
-
empty_image = st.empty()
|
711 |
-
empty_video = st.container()
|
712 |
-
empty_fire_text = st.empty()
|
713 |
-
empty_fire_image = st.empty()
|
714 |
-
|
715 |
-
submitted = st.form_submit_button("Submit")
|
716 |
-
if submitted:
|
717 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
718 |
-
empty_text.warning(
|
719 |
-
"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."
|
720 |
-
)
|
721 |
-
else:
|
722 |
-
empty_text.text("Computing... Please wait...")
|
723 |
-
|
724 |
-
geemap.goes_timelapse(
|
725 |
-
out_gif,
|
726 |
-
start_date=start,
|
727 |
-
end_date=end,
|
728 |
-
data=satellite,
|
729 |
-
scan=scan_type.replace(" ", "_").lower(),
|
730 |
-
region=roi,
|
731 |
-
dimensions=768,
|
732 |
-
framesPerSecond=speed,
|
733 |
-
date_format="YYYY-MM-dd HH:mm",
|
734 |
-
xy=("3%", "3%"),
|
735 |
-
text_sequence=None,
|
736 |
-
font_type="arial.ttf",
|
737 |
-
font_size=font_size,
|
738 |
-
font_color=font_color,
|
739 |
-
add_progress_bar=add_progress_bar,
|
740 |
-
progress_bar_color=progress_bar_color,
|
741 |
-
progress_bar_height=5,
|
742 |
-
loop=0,
|
743 |
-
overlay_data=overlay_data,
|
744 |
-
overlay_color=overlay_color,
|
745 |
-
overlay_width=overlay_width,
|
746 |
-
overlay_opacity=overlay_opacity,
|
747 |
-
mp4=mp4,
|
748 |
-
fading=fading,
|
749 |
-
)
|
750 |
-
|
751 |
-
if out_gif is not None and os.path.exists(out_gif):
|
752 |
-
empty_text.text(
|
753 |
-
"Right click the GIF to save it to your computer👇"
|
754 |
-
)
|
755 |
-
empty_image.image(out_gif)
|
756 |
-
|
757 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
758 |
-
if mp4 and os.path.exists(out_mp4):
|
759 |
-
with empty_video:
|
760 |
-
st.text(
|
761 |
-
"Right click the MP4 to save it to your computer👇"
|
762 |
-
)
|
763 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
764 |
-
|
765 |
-
if add_fire:
|
766 |
-
out_fire_gif = geemap.temp_file_path(".gif")
|
767 |
-
empty_fire_text.text(
|
768 |
-
"Delineating Fire Hotspot... Please wait..."
|
769 |
-
)
|
770 |
-
geemap.goes_fire_timelapse(
|
771 |
-
out_fire_gif,
|
772 |
-
start_date=start,
|
773 |
-
end_date=end,
|
774 |
-
data=satellite,
|
775 |
-
scan=scan_type.replace(" ", "_").lower(),
|
776 |
-
region=roi,
|
777 |
-
dimensions=768,
|
778 |
-
framesPerSecond=speed,
|
779 |
-
date_format="YYYY-MM-dd HH:mm",
|
780 |
-
xy=("3%", "3%"),
|
781 |
-
text_sequence=None,
|
782 |
-
font_type="arial.ttf",
|
783 |
-
font_size=font_size,
|
784 |
-
font_color=font_color,
|
785 |
-
add_progress_bar=add_progress_bar,
|
786 |
-
progress_bar_color=progress_bar_color,
|
787 |
-
progress_bar_height=5,
|
788 |
-
loop=0,
|
789 |
-
)
|
790 |
-
if os.path.exists(out_fire_gif):
|
791 |
-
empty_fire_image.image(out_fire_gif)
|
792 |
-
else:
|
793 |
-
empty_text.text(
|
794 |
-
"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."
|
795 |
-
)
|
796 |
-
|
797 |
-
elif collection == "MODIS Vegetation Indices (NDVI/EVI) 16-Day Global 1km":
|
798 |
-
|
799 |
-
video_empty.video("https://youtu.be/16fA2QORG4A")
|
800 |
-
|
801 |
-
satellite = st.selectbox("Select a satellite:", ["Terra", "Aqua"])
|
802 |
-
band = st.selectbox("Select a band:", ["NDVI", "EVI"])
|
803 |
-
|
804 |
-
with st.form("submit_modis_form"):
|
805 |
-
|
806 |
-
roi = None
|
807 |
-
if st.session_state.get("roi") is not None:
|
808 |
-
roi = st.session_state.get("roi")
|
809 |
-
out_gif = geemap.temp_file_path(".gif")
|
810 |
-
|
811 |
-
with st.expander("Customize timelapse"):
|
812 |
-
|
813 |
-
start = st.date_input(
|
814 |
-
"Select a start date:", datetime.date(2000, 2, 8)
|
815 |
-
)
|
816 |
-
end = st.date_input("Select an end date:", datetime.date.today())
|
817 |
-
|
818 |
-
start_date = start.strftime("%Y-%m-%d")
|
819 |
-
end_date = end.strftime("%Y-%m-%d")
|
820 |
-
|
821 |
-
speed = st.slider("Frames per second:", 1, 30, 5)
|
822 |
-
add_progress_bar = st.checkbox("Add a progress bar", True)
|
823 |
-
progress_bar_color = st.color_picker(
|
824 |
-
"Progress bar color:", "#0000ff"
|
825 |
-
)
|
826 |
-
font_size = st.slider("Font size:", 10, 50, 20)
|
827 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
828 |
-
|
829 |
-
font_type = st.selectbox(
|
830 |
-
"Select the font type for the title:",
|
831 |
-
["arial.ttf", "alibaba.otf"],
|
832 |
-
index=0,
|
833 |
-
)
|
834 |
-
fading = st.slider(
|
835 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
836 |
-
)
|
837 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
838 |
-
|
839 |
-
empty_text = st.empty()
|
840 |
-
empty_image = st.empty()
|
841 |
-
empty_video = st.container()
|
842 |
-
|
843 |
-
submitted = st.form_submit_button("Submit")
|
844 |
-
if submitted:
|
845 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
846 |
-
empty_text.warning(
|
847 |
-
"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."
|
848 |
-
)
|
849 |
-
else:
|
850 |
-
|
851 |
-
empty_text.text("Computing... Please wait...")
|
852 |
-
|
853 |
-
geemap.modis_ndvi_timelapse(
|
854 |
-
out_gif,
|
855 |
-
satellite,
|
856 |
-
band,
|
857 |
-
start_date,
|
858 |
-
end_date,
|
859 |
-
roi,
|
860 |
-
768,
|
861 |
-
speed,
|
862 |
-
overlay_data=overlay_data,
|
863 |
-
overlay_color=overlay_color,
|
864 |
-
overlay_width=overlay_width,
|
865 |
-
overlay_opacity=overlay_opacity,
|
866 |
-
mp4=mp4,
|
867 |
-
fading=fading,
|
868 |
-
)
|
869 |
-
|
870 |
-
geemap.reduce_gif_size(out_gif)
|
871 |
-
|
872 |
-
empty_text.text(
|
873 |
-
"Right click the GIF to save it to your computer👇"
|
874 |
-
)
|
875 |
-
empty_image.image(out_gif)
|
876 |
-
|
877 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
878 |
-
if mp4 and os.path.exists(out_mp4):
|
879 |
-
with empty_video:
|
880 |
-
st.text(
|
881 |
-
"Right click the MP4 to save it to your computer👇"
|
882 |
-
)
|
883 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
884 |
-
|
885 |
-
elif collection == "Any Earth Engine ImageCollection":
|
886 |
-
|
887 |
-
with st.form("submit_ts_form"):
|
888 |
-
with st.expander("Customize timelapse"):
|
889 |
-
|
890 |
-
title = st.text_input(
|
891 |
-
"Enter a title to show on the timelapse: ", "Timelapse"
|
892 |
-
)
|
893 |
-
start_date = st.date_input(
|
894 |
-
"Select the start date:", datetime.date(2020, 1, 1)
|
895 |
-
)
|
896 |
-
end_date = st.date_input(
|
897 |
-
"Select the end date:", datetime.date.today()
|
898 |
-
)
|
899 |
-
frequency = st.selectbox(
|
900 |
-
"Select a temporal frequency:",
|
901 |
-
["year", "quarter", "month", "day", "hour", "minute", "second"],
|
902 |
-
index=0,
|
903 |
-
)
|
904 |
-
reducer = st.selectbox(
|
905 |
-
"Select a reducer for aggregating data:",
|
906 |
-
["median", "mean", "min", "max", "sum", "variance", "stdDev"],
|
907 |
-
index=0,
|
908 |
-
)
|
909 |
-
data_format = st.selectbox(
|
910 |
-
"Select a date format to show on the timelapse:",
|
911 |
-
[
|
912 |
-
"YYYY-MM-dd",
|
913 |
-
"YYYY",
|
914 |
-
"YYMM-MM",
|
915 |
-
"YYYY-MM-dd HH:mm",
|
916 |
-
"YYYY-MM-dd HH:mm:ss",
|
917 |
-
"HH:mm",
|
918 |
-
"HH:mm:ss",
|
919 |
-
"w",
|
920 |
-
"M",
|
921 |
-
"d",
|
922 |
-
"D",
|
923 |
-
],
|
924 |
-
index=0,
|
925 |
-
)
|
926 |
-
|
927 |
-
speed = st.slider("Frames per second:", 1, 30, 5)
|
928 |
-
add_progress_bar = st.checkbox("Add a progress bar", True)
|
929 |
-
progress_bar_color = st.color_picker(
|
930 |
-
"Progress bar color:", "#0000ff"
|
931 |
-
)
|
932 |
-
font_size = st.slider("Font size:", 10, 50, 30)
|
933 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
934 |
-
font_type = st.selectbox(
|
935 |
-
"Select the font type for the title:",
|
936 |
-
["arial.ttf", "alibaba.otf"],
|
937 |
-
index=0,
|
938 |
-
)
|
939 |
-
fading = st.slider(
|
940 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
941 |
-
)
|
942 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
943 |
-
|
944 |
-
empty_text = st.empty()
|
945 |
-
empty_image = st.empty()
|
946 |
-
empty_video = st.container()
|
947 |
-
empty_fire_image = st.empty()
|
948 |
-
|
949 |
-
roi = None
|
950 |
-
if st.session_state.get("roi") is not None:
|
951 |
-
roi = st.session_state.get("roi")
|
952 |
-
out_gif = geemap.temp_file_path(".gif")
|
953 |
-
|
954 |
-
submitted = st.form_submit_button("Submit")
|
955 |
-
if submitted:
|
956 |
-
|
957 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
958 |
-
empty_text.warning(
|
959 |
-
"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."
|
960 |
-
)
|
961 |
-
else:
|
962 |
-
|
963 |
-
empty_text.text("Computing... Please wait...")
|
964 |
-
try:
|
965 |
-
geemap.create_timelapse(
|
966 |
-
st.session_state.get("ee_asset_id"),
|
967 |
-
start_date=start_date.strftime("%Y-%m-%d"),
|
968 |
-
end_date=end_date.strftime("%Y-%m-%d"),
|
969 |
-
region=roi,
|
970 |
-
frequency=frequency,
|
971 |
-
reducer=reducer,
|
972 |
-
date_format=data_format,
|
973 |
-
out_gif=out_gif,
|
974 |
-
bands=st.session_state.get("bands"),
|
975 |
-
palette=st.session_state.get("palette"),
|
976 |
-
vis_params=st.session_state.get("vis_params"),
|
977 |
-
dimensions=768,
|
978 |
-
frames_per_second=speed,
|
979 |
-
crs="EPSG:3857",
|
980 |
-
overlay_data=overlay_data,
|
981 |
-
overlay_color=overlay_color,
|
982 |
-
overlay_width=overlay_width,
|
983 |
-
overlay_opacity=overlay_opacity,
|
984 |
-
title=title,
|
985 |
-
title_xy=("2%", "90%"),
|
986 |
-
add_text=True,
|
987 |
-
text_xy=("2%", "2%"),
|
988 |
-
text_sequence=None,
|
989 |
-
font_type=font_type,
|
990 |
-
font_size=font_size,
|
991 |
-
font_color=font_color,
|
992 |
-
add_progress_bar=add_progress_bar,
|
993 |
-
progress_bar_color=progress_bar_color,
|
994 |
-
progress_bar_height=5,
|
995 |
-
loop=0,
|
996 |
-
mp4=mp4,
|
997 |
-
fading=fading,
|
998 |
-
)
|
999 |
-
except:
|
1000 |
-
empty_text.error(
|
1001 |
-
"An error occurred while computing the timelapse. You probably requested too much data. Try reducing the ROI or timespan."
|
1002 |
-
)
|
1003 |
-
|
1004 |
-
empty_text.text(
|
1005 |
-
"Right click the GIF to save it to your computer👇"
|
1006 |
-
)
|
1007 |
-
empty_image.image(out_gif)
|
1008 |
-
|
1009 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
1010 |
-
if mp4 and os.path.exists(out_mp4):
|
1011 |
-
with empty_video:
|
1012 |
-
st.text(
|
1013 |
-
"Right click the MP4 to save it to your computer👇"
|
1014 |
-
)
|
1015 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
1016 |
-
|
1017 |
-
elif collection in [
|
1018 |
-
"MODIS Gap filled Land Surface Temperature Daily",
|
1019 |
-
"MODIS Ocean Color SMI",
|
1020 |
-
]:
|
1021 |
-
|
1022 |
-
with st.form("submit_ts_form"):
|
1023 |
-
with st.expander("Customize timelapse"):
|
1024 |
-
|
1025 |
-
title = st.text_input(
|
1026 |
-
"Enter a title to show on the timelapse: ",
|
1027 |
-
"Surface Temperature",
|
1028 |
-
)
|
1029 |
-
start_date = st.date_input(
|
1030 |
-
"Select the start date:", datetime.date(2018, 1, 1)
|
1031 |
-
)
|
1032 |
-
end_date = st.date_input(
|
1033 |
-
"Select the end date:", datetime.date(2020, 12, 31)
|
1034 |
-
)
|
1035 |
-
frequency = st.selectbox(
|
1036 |
-
"Select a temporal frequency:",
|
1037 |
-
["year", "quarter", "month", "week", "day"],
|
1038 |
-
index=2,
|
1039 |
-
)
|
1040 |
-
reducer = st.selectbox(
|
1041 |
-
"Select a reducer for aggregating data:",
|
1042 |
-
["median", "mean", "min", "max", "sum", "variance", "stdDev"],
|
1043 |
-
index=0,
|
1044 |
-
)
|
1045 |
-
|
1046 |
-
vis_params = st.text_area(
|
1047 |
-
"Enter visualization parameters",
|
1048 |
-
"",
|
1049 |
-
help="Enter a string in the format of a dictionary, such as '{'min': 23, 'max': 32}'",
|
1050 |
-
)
|
1051 |
-
|
1052 |
-
speed = st.slider("Frames per second:", 1, 30, 5)
|
1053 |
-
add_progress_bar = st.checkbox("Add a progress bar", True)
|
1054 |
-
progress_bar_color = st.color_picker(
|
1055 |
-
"Progress bar color:", "#0000ff"
|
1056 |
-
)
|
1057 |
-
font_size = st.slider("Font size:", 10, 50, 30)
|
1058 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
1059 |
-
font_type = st.selectbox(
|
1060 |
-
"Select the font type for the title:",
|
1061 |
-
["arial.ttf", "alibaba.otf"],
|
1062 |
-
index=0,
|
1063 |
-
)
|
1064 |
-
add_colorbar = st.checkbox("Add a colorbar", True)
|
1065 |
-
colorbar_label = st.text_input(
|
1066 |
-
"Enter the colorbar label:", "Surface Temperature (°C)"
|
1067 |
-
)
|
1068 |
-
fading = st.slider(
|
1069 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
1070 |
-
)
|
1071 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
1072 |
-
|
1073 |
-
empty_text = st.empty()
|
1074 |
-
empty_image = st.empty()
|
1075 |
-
empty_video = st.container()
|
1076 |
-
|
1077 |
-
roi = None
|
1078 |
-
if st.session_state.get("roi") is not None:
|
1079 |
-
roi = st.session_state.get("roi")
|
1080 |
-
out_gif = geemap.temp_file_path(".gif")
|
1081 |
-
|
1082 |
-
submitted = st.form_submit_button("Submit")
|
1083 |
-
if submitted:
|
1084 |
-
|
1085 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
1086 |
-
empty_text.warning(
|
1087 |
-
"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."
|
1088 |
-
)
|
1089 |
-
else:
|
1090 |
-
|
1091 |
-
empty_text.text("Computing... Please wait...")
|
1092 |
-
try:
|
1093 |
-
if (
|
1094 |
-
collection
|
1095 |
-
== "MODIS Gap filled Land Surface Temperature Daily"
|
1096 |
-
):
|
1097 |
-
out_gif = geemap.create_timelapse(
|
1098 |
-
st.session_state.get("ee_asset_id"),
|
1099 |
-
start_date=start_date.strftime("%Y-%m-%d"),
|
1100 |
-
end_date=end_date.strftime("%Y-%m-%d"),
|
1101 |
-
region=roi,
|
1102 |
-
bands=None,
|
1103 |
-
frequency=frequency,
|
1104 |
-
reducer=reducer,
|
1105 |
-
date_format=None,
|
1106 |
-
out_gif=out_gif,
|
1107 |
-
palette=st.session_state.get("palette"),
|
1108 |
-
vis_params=None,
|
1109 |
-
dimensions=768,
|
1110 |
-
frames_per_second=speed,
|
1111 |
-
crs="EPSG:3857",
|
1112 |
-
overlay_data=overlay_data,
|
1113 |
-
overlay_color=overlay_color,
|
1114 |
-
overlay_width=overlay_width,
|
1115 |
-
overlay_opacity=overlay_opacity,
|
1116 |
-
title=title,
|
1117 |
-
title_xy=("2%", "90%"),
|
1118 |
-
add_text=True,
|
1119 |
-
text_xy=("2%", "2%"),
|
1120 |
-
text_sequence=None,
|
1121 |
-
font_type=font_type,
|
1122 |
-
font_size=font_size,
|
1123 |
-
font_color=font_color,
|
1124 |
-
add_progress_bar=add_progress_bar,
|
1125 |
-
progress_bar_color=progress_bar_color,
|
1126 |
-
progress_bar_height=5,
|
1127 |
-
add_colorbar=add_colorbar,
|
1128 |
-
colorbar_label=colorbar_label,
|
1129 |
-
loop=0,
|
1130 |
-
mp4=mp4,
|
1131 |
-
fading=fading,
|
1132 |
-
)
|
1133 |
-
elif collection == "MODIS Ocean Color SMI":
|
1134 |
-
if vis_params.startswith("{") and vis_params.endswith(
|
1135 |
-
"}"
|
1136 |
-
):
|
1137 |
-
vis_params = eval(vis_params)
|
1138 |
-
else:
|
1139 |
-
vis_params = None
|
1140 |
-
out_gif = geemap.modis_ocean_color_timelapse(
|
1141 |
-
st.session_state.get("ee_asset_id"),
|
1142 |
-
start_date=start_date.strftime("%Y-%m-%d"),
|
1143 |
-
end_date=end_date.strftime("%Y-%m-%d"),
|
1144 |
-
region=roi,
|
1145 |
-
bands=st.session_state["band"],
|
1146 |
-
frequency=frequency,
|
1147 |
-
reducer=reducer,
|
1148 |
-
date_format=None,
|
1149 |
-
out_gif=out_gif,
|
1150 |
-
palette=st.session_state.get("palette"),
|
1151 |
-
vis_params=vis_params,
|
1152 |
-
dimensions=768,
|
1153 |
-
frames_per_second=speed,
|
1154 |
-
crs="EPSG:3857",
|
1155 |
-
overlay_data=overlay_data,
|
1156 |
-
overlay_color=overlay_color,
|
1157 |
-
overlay_width=overlay_width,
|
1158 |
-
overlay_opacity=overlay_opacity,
|
1159 |
-
title=title,
|
1160 |
-
title_xy=("2%", "90%"),
|
1161 |
-
add_text=True,
|
1162 |
-
text_xy=("2%", "2%"),
|
1163 |
-
text_sequence=None,
|
1164 |
-
font_type=font_type,
|
1165 |
-
font_size=font_size,
|
1166 |
-
font_color=font_color,
|
1167 |
-
add_progress_bar=add_progress_bar,
|
1168 |
-
progress_bar_color=progress_bar_color,
|
1169 |
-
progress_bar_height=5,
|
1170 |
-
add_colorbar=add_colorbar,
|
1171 |
-
colorbar_label=colorbar_label,
|
1172 |
-
loop=0,
|
1173 |
-
mp4=mp4,
|
1174 |
-
fading=fading,
|
1175 |
-
)
|
1176 |
-
except:
|
1177 |
-
empty_text.error(
|
1178 |
-
"Something went wrong. You probably requested too much data. Try reducing the ROI or timespan."
|
1179 |
-
)
|
1180 |
-
|
1181 |
-
if out_gif is not None and os.path.exists(out_gif):
|
1182 |
-
|
1183 |
-
geemap.reduce_gif_size(out_gif)
|
1184 |
-
|
1185 |
-
empty_text.text(
|
1186 |
-
"Right click the GIF to save it to your computer👇"
|
1187 |
-
)
|
1188 |
-
empty_image.image(out_gif)
|
1189 |
-
|
1190 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
1191 |
-
if mp4 and os.path.exists(out_mp4):
|
1192 |
-
with empty_video:
|
1193 |
-
st.text(
|
1194 |
-
"Right click the MP4 to save it to your computer👇"
|
1195 |
-
)
|
1196 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
1197 |
-
|
1198 |
-
else:
|
1199 |
-
st.error(
|
1200 |
-
"Something went wrong. You probably requested too much data. Try reducing the ROI or timespan."
|
1201 |
-
)
|
1202 |
-
|
1203 |
-
elif collection == "USDA National Agriculture Imagery Program (NAIP)":
|
1204 |
-
|
1205 |
-
with st.form("submit_naip_form"):
|
1206 |
-
with st.expander("Customize timelapse"):
|
1207 |
-
|
1208 |
-
title = st.text_input(
|
1209 |
-
"Enter a title to show on the timelapse: ", "NAIP Timelapse"
|
1210 |
-
)
|
1211 |
-
|
1212 |
-
years = st.slider(
|
1213 |
-
"Start and end year:",
|
1214 |
-
2003,
|
1215 |
-
today.year,
|
1216 |
-
(2003, today.year),
|
1217 |
-
)
|
1218 |
-
|
1219 |
-
bands = st.selectbox(
|
1220 |
-
"Select a band combination:", ["N/R/G", "R/G/B"], index=0
|
1221 |
-
)
|
1222 |
-
|
1223 |
-
speed = st.slider("Frames per second:", 1, 30, 3)
|
1224 |
-
add_progress_bar = st.checkbox("Add a progress bar", True)
|
1225 |
-
progress_bar_color = st.color_picker(
|
1226 |
-
"Progress bar color:", "#0000ff"
|
1227 |
-
)
|
1228 |
-
font_size = st.slider("Font size:", 10, 50, 30)
|
1229 |
-
font_color = st.color_picker("Font color:", "#ffffff")
|
1230 |
-
font_type = st.selectbox(
|
1231 |
-
"Select the font type for the title:",
|
1232 |
-
["arial.ttf", "alibaba.otf"],
|
1233 |
-
index=0,
|
1234 |
-
)
|
1235 |
-
fading = st.slider(
|
1236 |
-
"Fading duration (seconds) for each frame:", 0.0, 3.0, 0.0
|
1237 |
-
)
|
1238 |
-
mp4 = st.checkbox("Save timelapse as MP4", True)
|
1239 |
-
|
1240 |
-
empty_text = st.empty()
|
1241 |
-
empty_image = st.empty()
|
1242 |
-
empty_video = st.container()
|
1243 |
-
empty_fire_image = st.empty()
|
1244 |
-
|
1245 |
-
roi = None
|
1246 |
-
if st.session_state.get("roi") is not None:
|
1247 |
-
roi = st.session_state.get("roi")
|
1248 |
-
out_gif = geemap.temp_file_path(".gif")
|
1249 |
-
|
1250 |
-
submitted = st.form_submit_button("Submit")
|
1251 |
-
if submitted:
|
1252 |
-
|
1253 |
-
if sample_roi == "Uploaded GeoJSON" and data is None:
|
1254 |
-
empty_text.warning(
|
1255 |
-
"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."
|
1256 |
-
)
|
1257 |
-
else:
|
1258 |
-
|
1259 |
-
empty_text.text("Computing... Please wait...")
|
1260 |
-
try:
|
1261 |
-
geemap.naip_timelapse(
|
1262 |
-
roi,
|
1263 |
-
years[0],
|
1264 |
-
years[1],
|
1265 |
-
out_gif,
|
1266 |
-
bands=bands.split("/"),
|
1267 |
-
palette=st.session_state.get("palette"),
|
1268 |
-
vis_params=None,
|
1269 |
-
dimensions=768,
|
1270 |
-
frames_per_second=speed,
|
1271 |
-
crs="EPSG:3857",
|
1272 |
-
overlay_data=overlay_data,
|
1273 |
-
overlay_color=overlay_color,
|
1274 |
-
overlay_width=overlay_width,
|
1275 |
-
overlay_opacity=overlay_opacity,
|
1276 |
-
title=title,
|
1277 |
-
title_xy=("2%", "90%"),
|
1278 |
-
add_text=True,
|
1279 |
-
text_xy=("2%", "2%"),
|
1280 |
-
text_sequence=None,
|
1281 |
-
font_type=font_type,
|
1282 |
-
font_size=font_size,
|
1283 |
-
font_color=font_color,
|
1284 |
-
add_progress_bar=add_progress_bar,
|
1285 |
-
progress_bar_color=progress_bar_color,
|
1286 |
-
progress_bar_height=5,
|
1287 |
-
loop=0,
|
1288 |
-
mp4=mp4,
|
1289 |
-
fading=fading,
|
1290 |
-
)
|
1291 |
-
except:
|
1292 |
-
empty_text.error(
|
1293 |
-
"Something went wrong. You either requested too much data or the ROI is outside the U.S."
|
1294 |
-
)
|
1295 |
-
|
1296 |
-
if out_gif is not None and os.path.exists(out_gif):
|
1297 |
-
|
1298 |
-
empty_text.text(
|
1299 |
-
"Right click the GIF to save it to your computer👇"
|
1300 |
-
)
|
1301 |
-
empty_image.image(out_gif)
|
1302 |
-
|
1303 |
-
out_mp4 = out_gif.replace(".gif", ".mp4")
|
1304 |
-
if mp4 and os.path.exists(out_mp4):
|
1305 |
-
with empty_video:
|
1306 |
-
st.text(
|
1307 |
-
"Right click the MP4 to save it to your computer👇"
|
1308 |
-
)
|
1309 |
-
st.video(out_gif.replace(".gif", ".mp4"))
|
1310 |
-
|
1311 |
-
else:
|
1312 |
-
st.error(
|
1313 |
-
"Something went wrong. You either requested too much data or the ROI is outside the U.S."
|
1314 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/vector.py
DELETED
@@ -1,98 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import fiona
|
3 |
-
import geopandas as gpd
|
4 |
-
import streamlit as st
|
5 |
-
|
6 |
-
|
7 |
-
def save_uploaded_file(file_content, file_name):
|
8 |
-
"""
|
9 |
-
Save the uploaded file to a temporary directory
|
10 |
-
"""
|
11 |
-
import tempfile
|
12 |
-
import os
|
13 |
-
import uuid
|
14 |
-
|
15 |
-
_, file_extension = os.path.splitext(file_name)
|
16 |
-
file_id = str(uuid.uuid4())
|
17 |
-
file_path = os.path.join(tempfile.gettempdir(), f"{file_id}{file_extension}")
|
18 |
-
|
19 |
-
with open(file_path, "wb") as file:
|
20 |
-
file.write(file_content.getbuffer())
|
21 |
-
|
22 |
-
return file_path
|
23 |
-
|
24 |
-
|
25 |
-
def app():
|
26 |
-
|
27 |
-
st.title("Upload Vector Data")
|
28 |
-
|
29 |
-
row1_col1, row1_col2 = st.columns([2, 1])
|
30 |
-
width = 950
|
31 |
-
height = 600
|
32 |
-
|
33 |
-
with row1_col2:
|
34 |
-
|
35 |
-
backend = st.selectbox(
|
36 |
-
"Select a plotting backend", ["folium", "kepler.gl", "pydeck"], index=2
|
37 |
-
)
|
38 |
-
|
39 |
-
if backend == "folium":
|
40 |
-
import leafmap.foliumap as leafmap
|
41 |
-
elif backend == "kepler.gl":
|
42 |
-
import leafmap.kepler as leafmap
|
43 |
-
elif backend == "pydeck":
|
44 |
-
import leafmap.deck as leafmap
|
45 |
-
|
46 |
-
url = st.text_input(
|
47 |
-
"Enter a URL to a vector dataset",
|
48 |
-
"https://github.com/giswqs/streamlit-geospatial/raw/master/data/us_states.geojson",
|
49 |
-
)
|
50 |
-
|
51 |
-
data = st.file_uploader(
|
52 |
-
"Upload a vector dataset", type=["geojson", "kml", "zip", "tab"]
|
53 |
-
)
|
54 |
-
|
55 |
-
container = st.container()
|
56 |
-
|
57 |
-
if data or url:
|
58 |
-
if data:
|
59 |
-
file_path = save_uploaded_file(data, data.name)
|
60 |
-
layer_name = os.path.splitext(data.name)[0]
|
61 |
-
elif url:
|
62 |
-
file_path = url
|
63 |
-
layer_name = url.split("/")[-1].split(".")[0]
|
64 |
-
|
65 |
-
with row1_col1:
|
66 |
-
if file_path.lower().endswith(".kml"):
|
67 |
-
fiona.drvsupport.supported_drivers["KML"] = "rw"
|
68 |
-
gdf = gpd.read_file(file_path, driver="KML")
|
69 |
-
else:
|
70 |
-
gdf = gpd.read_file(file_path)
|
71 |
-
lon, lat = leafmap.gdf_centroid(gdf)
|
72 |
-
if backend == "pydeck":
|
73 |
-
|
74 |
-
column_names = gdf.columns.values.tolist()
|
75 |
-
random_column = None
|
76 |
-
with container:
|
77 |
-
random_color = st.checkbox("Apply random colors", True)
|
78 |
-
if random_color:
|
79 |
-
random_column = st.selectbox(
|
80 |
-
"Select a column to apply random colors", column_names
|
81 |
-
)
|
82 |
-
|
83 |
-
m = leafmap.Map(center=(lat, lon))
|
84 |
-
m.add_gdf(gdf, random_color_column=random_column)
|
85 |
-
st.pydeck_chart(m)
|
86 |
-
|
87 |
-
else:
|
88 |
-
m = leafmap.Map(center=(lat, lon), draw_export=True)
|
89 |
-
m.add_gdf(gdf, layer_name=layer_name)
|
90 |
-
# m.add_vector(file_path, layer_name=layer_name)
|
91 |
-
if backend == "folium":
|
92 |
-
m.zoom_to_gdf(gdf)
|
93 |
-
m.to_streamlit(width=width, height=height)
|
94 |
-
|
95 |
-
else:
|
96 |
-
with row1_col1:
|
97 |
-
m = leafmap.Map()
|
98 |
-
st.pydeck_chart(m)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/wms.py
DELETED
@@ -1,68 +0,0 @@
|
|
1 |
-
import ast
|
2 |
-
import streamlit as st
|
3 |
-
import leafmap.foliumap as leafmap
|
4 |
-
|
5 |
-
|
6 |
-
@st.cache(allow_output_mutation=True)
|
7 |
-
def get_layers(url):
|
8 |
-
options = leafmap.get_wms_layers(url)
|
9 |
-
return options
|
10 |
-
|
11 |
-
|
12 |
-
def app():
|
13 |
-
st.title("Add Web Map Service (WMS)")
|
14 |
-
st.markdown(
|
15 |
-
"""
|
16 |
-
This app is a demonstration of loading Web Map Service (WMS) layers. Simply enter the URL of the WMS service
|
17 |
-
in the text box below and press Enter to retrieve the layers. Go to https://apps.nationalmap.gov/services to find
|
18 |
-
some WMS URLs if needed.
|
19 |
-
"""
|
20 |
-
)
|
21 |
-
|
22 |
-
row1_col1, row1_col2 = st.columns([3, 1.3])
|
23 |
-
width = 800
|
24 |
-
height = 600
|
25 |
-
layers = None
|
26 |
-
|
27 |
-
with row1_col2:
|
28 |
-
|
29 |
-
esa_landcover = "https://services.terrascope.be/wms/v2"
|
30 |
-
url = st.text_input(
|
31 |
-
"Enter a WMS URL:", value="https://services.terrascope.be/wms/v2"
|
32 |
-
)
|
33 |
-
empty = st.empty()
|
34 |
-
|
35 |
-
if url:
|
36 |
-
options = get_layers(url)
|
37 |
-
|
38 |
-
default = None
|
39 |
-
if url == esa_landcover:
|
40 |
-
default = "WORLDCOVER_2020_MAP"
|
41 |
-
layers = empty.multiselect(
|
42 |
-
"Select WMS layers to add to the map:", options, default=default
|
43 |
-
)
|
44 |
-
add_legend = st.checkbox("Add a legend to the map", value=True)
|
45 |
-
if default == "WORLDCOVER_2020_MAP":
|
46 |
-
legend = str(leafmap.builtin_legends["ESA_WorldCover"])
|
47 |
-
else:
|
48 |
-
legend = ""
|
49 |
-
if add_legend:
|
50 |
-
legend_text = st.text_area(
|
51 |
-
"Enter a legend as a dictionary {label: color}",
|
52 |
-
value=legend,
|
53 |
-
height=200,
|
54 |
-
)
|
55 |
-
|
56 |
-
with row1_col1:
|
57 |
-
m = leafmap.Map(center=(36.3, 0), zoom=2)
|
58 |
-
|
59 |
-
if layers is not None:
|
60 |
-
for layer in layers:
|
61 |
-
m.add_wms_layer(
|
62 |
-
url, layers=layer, name=layer, attribution=" ", transparent=True
|
63 |
-
)
|
64 |
-
if add_legend and legend_text:
|
65 |
-
legend_dict = ast.literal_eval(legend_text)
|
66 |
-
m.add_legend(legend_dict=legend_dict)
|
67 |
-
|
68 |
-
m.to_streamlit(width, height)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apps/xy.py
DELETED
@@ -1,65 +0,0 @@
|
|
1 |
-
import leafmap.foliumap as leafmap
|
2 |
-
import pandas as pd
|
3 |
-
import streamlit as st
|
4 |
-
|
5 |
-
|
6 |
-
def app():
|
7 |
-
|
8 |
-
st.title("Add Points from XY")
|
9 |
-
|
10 |
-
sample_url = "https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/world_cities.csv"
|
11 |
-
url = st.text_input("Enter URL:", sample_url)
|
12 |
-
m = leafmap.Map(locate_control=True, plugin_LatLngPopup=False)
|
13 |
-
|
14 |
-
if url:
|
15 |
-
|
16 |
-
try:
|
17 |
-
df = pd.read_csv(url)
|
18 |
-
|
19 |
-
columns = df.columns.values.tolist()
|
20 |
-
row1_col1, row1_col2, row1_col3, row1_col4, row1_col5 = st.columns(
|
21 |
-
[1, 1, 3, 1, 1]
|
22 |
-
)
|
23 |
-
|
24 |
-
lon_index = 0
|
25 |
-
lat_index = 0
|
26 |
-
|
27 |
-
for col in columns:
|
28 |
-
if col.lower() in ["lon", "longitude", "long", "lng"]:
|
29 |
-
lon_index = columns.index(col)
|
30 |
-
elif col.lower() in ["lat", "latitude"]:
|
31 |
-
lat_index = columns.index(col)
|
32 |
-
|
33 |
-
with row1_col1:
|
34 |
-
x = st.selectbox("Select longitude column", columns, lon_index)
|
35 |
-
|
36 |
-
with row1_col2:
|
37 |
-
y = st.selectbox("Select latitude column", columns, lat_index)
|
38 |
-
|
39 |
-
with row1_col3:
|
40 |
-
popups = st.multiselect("Select popup columns", columns, columns)
|
41 |
-
|
42 |
-
with row1_col4:
|
43 |
-
heatmap = st.checkbox("Add heatmap")
|
44 |
-
|
45 |
-
if heatmap:
|
46 |
-
with row1_col5:
|
47 |
-
if "pop_max" in columns:
|
48 |
-
index = columns.index("pop_max")
|
49 |
-
else:
|
50 |
-
index = 0
|
51 |
-
heatmap_col = st.selectbox("Select heatmap column", columns, index)
|
52 |
-
try:
|
53 |
-
m.add_heatmap(df, y, x, heatmap_col)
|
54 |
-
except:
|
55 |
-
st.error("Please select a numeric column")
|
56 |
-
|
57 |
-
try:
|
58 |
-
m.add_points_from_xy(df, x, y, popups)
|
59 |
-
except:
|
60 |
-
st.error("Please select a numeric column")
|
61 |
-
|
62 |
-
except Exception as e:
|
63 |
-
st.error(e)
|
64 |
-
|
65 |
-
m.to_streamlit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/cog_files.txt
DELETED
@@ -1,77 +0,0 @@
|
|
1 |
-
https://www.maxar.com/open-data/california-colorado-fires
|
2 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-02-16/pine-gulch-fire20/1030010076004E00.tif
|
3 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-08-18/pine-gulch-fire20/1040010041D3B300.tif
|
4 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-11-13/grizzly-creek-fire20/1040010045785200.tif
|
5 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-11-13/grizzly-creek-fire20/10400100443AEC00.tif
|
6 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-02-06/czu-lightning-complex-fire/104001004941E100.tif
|
7 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-02-18/cameron-peak-fire20/103001008DA5B500.tif
|
8 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-02-22/czu-lightning-complex-fire/103001008DB2E200.tif
|
9 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-04-01/grizzly-creek-fire20/104001004881EF00.tif
|
10 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-04-17/czu-lightning-complex-fire/103001008F905300.tif
|
11 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-04-17/czu-lightning-complex-fire/1030010092B22200.tif
|
12 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-06-27/czu-lightning-complex-fire/1030010094A52300.tif
|
13 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-09-08/czu-lightning-complex-fire/103001009C9FBB00.tif
|
14 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-09-24/lnu-lightning-complex-fire/103001009A079B00.tif
|
15 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-10-05/czu-lightning-complex-fire/103001009C10F800.tif
|
16 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-10-05/czu-lightning-complex-fire/103001009A266800.tif
|
17 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-11-04/czu-lightning-complex-fire/1050010019917900.tif
|
18 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-11-04/czu-lightning-complex-fire/1050010019917800.tif
|
19 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-11-18/czu-lightning-complex-fire/1050010019C2F600.tif
|
20 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-11-28/cameron-peak-fire20/103001009D72E000.tif
|
21 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-12-10/czu-lightning-complex-fire/105001001A3A8700.tif
|
22 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-12-28/lnu-lightning-complex-fire/10300100A1972700.tif
|
23 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2019-12-28/lnu-lightning-complex-fire/103001009F5D6B00.tif
|
24 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-01-15/cameron-peak-fire20/1040010057992100.tif
|
25 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-04-15/lnu-lightning-complex-fire/10300100A4B23600.tif
|
26 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-04-23/czu-lightning-complex-fire/10300100A589D100.tif
|
27 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-05-09/lnu-lightning-complex-fire/10300100A332EE00.tif
|
28 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-05-23/river-carmel-fires/10300100A77E9400.tif
|
29 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-05-23/river-carmel-fires/10300100A500A500.tif
|
30 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-05-24/river-carmel-fires/105001001D64E200.tif
|
31 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-06-27/lnu-lightning-complex-fire/10300100A8663800.tif
|
32 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-06-30/river-carmel-fires/10300100A9D60C00.tif
|
33 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-06-30/czu-lightning-complex-fire/10300100A8C66400.tif
|
34 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-06-30/czu-lightning-complex-fire/10300100A8892900.tif
|
35 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-07-11/czu-lightning-complex-fire/10300100AB381200.tif
|
36 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-07-11/czu-lightning-complex-fire/10300100AA180600.tif
|
37 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-07-13/pine-gulch-fire20/10300100AA57D700.tif
|
38 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-07-20/lnu-lightning-complex-fire/104001005C529000.tif
|
39 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2020-07-28/pine-gulch-fire20/104001005DB06E00.tif
|
40 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-14/pine-gulch-fire20/10300100AAC8DD00.tif
|
41 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-16/pine-gulch-fire20/104001005D4A6100.tif
|
42 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-17/grizzly-creek-fire20/10300100ACCA3700.tif
|
43 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-17/cameron-peak-fire20/10300100AB4ED400.tif
|
44 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-20/swir-cog/104A0100606FFE00.tif
|
45 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-20/pine-gulch-fire20/10300100ACD06200.tif
|
46 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-20/pine-gulch-fire20/10300100AAD4A000.tif
|
47 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-20/pine-gulch-fire20/10300100AA293800.tif
|
48 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-20/lnu-lightning-complex-fire/10400100606FFE00.tif
|
49 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/river-carmel-fires/10300100ACBA2B00.tif
|
50 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/river-carmel-fires/10300100AA49F600.tif
|
51 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/lnu-lightning-complex-fire/104001005C1AC900.tif
|
52 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/river-carmel-fires/104001005F9F5300.tif
|
53 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/river-carmel-fires/104001005F453300.tif
|
54 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/river-carmel-fires/10300100ADC14400.tif
|
55 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-21/czu-lightning-complex-fire/104001005F43D400.tif
|
56 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-23/grizzly-creek-fire20/104001005FA09C00.tif
|
57 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-23/grizzly-creek-fire20/104001005DC71000.tif
|
58 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-26/river-carmel-fires/105001001F58F000.tif
|
59 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-26/lnu-lightning-complex-fire/10300100AC163A00.tif
|
60 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-29/river-carmel-fires/10300100AAD27500.tif
|
61 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-08-29/river-carmel-fires/10300100A9C75A00.tif
|
62 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-09-03/cameron-peak-fire20/1040010060188800.tif
|
63 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-09-03/cameron-peak-fire20/104001005F7E6500.tif
|
64 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-09-03/cameron-peak-fire20/10300100AE685A00.tif
|
65 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-09-04/cameron-peak-fire20/1040010060761C00.tif
|
66 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-05/cameron-peak-fire20/104001006113B700.tif
|
67 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-05/cameron-peak-fire20/10400100610CD400.tif
|
68 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-12/cameron-peak-fire20/1040010062B14C00.tif
|
69 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-12/cameron-peak-fire20/10400100626BFA00.tif
|
70 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-12/cameron-peak-fire20/10400100622A6600.tif
|
71 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-12/cameron-peak-fire20/10400100606B6300.tif
|
72 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-12/cameron-peak-fire20/104001005F908800.tif
|
73 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-15/cameron-peak-fire20/10500100205EDA00.tif
|
74 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-15/cameron-peak-fire20/10500100205ED900.tif
|
75 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-22/east-troublesome-fire20/10300100B0004A00.tif
|
76 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-22/east-troublesome-fire20/10300100AD0D1200.tif
|
77 |
-
https://opendata.digitalglobe.com/events/california-fire-2020/post-event/2020-10-22/east-troublesome-fire20/10300100AD0CA600.tif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/html/sfo_buildings.html
DELETED
@@ -1,34 +0,0 @@
|
|
1 |
-
<!DOCTYPE html>
|
2 |
-
<html lang="en">
|
3 |
-
<head>
|
4 |
-
<meta charset="utf-8">
|
5 |
-
<!-- Include the CesiumJS JavaScript and CSS files -->
|
6 |
-
<script src="https://cesium.com/downloads/cesiumjs/releases/1.88/Build/Cesium/Cesium.js"></script>
|
7 |
-
<link href="https://cesium.com/downloads/cesiumjs/releases/1.88/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
|
8 |
-
</head>
|
9 |
-
<body>
|
10 |
-
<div id="cesiumContainer"></div>
|
11 |
-
<script>
|
12 |
-
// Your access token can be found at: https://cesium.com/ion/tokens.
|
13 |
-
// Replace `your_access_token` with your Cesium ion access token.
|
14 |
-
|
15 |
-
Cesium.Ion.defaultAccessToken = 'your_access_token';
|
16 |
-
|
17 |
-
// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
|
18 |
-
const viewer = new Cesium.Viewer('cesiumContainer', {
|
19 |
-
terrainProvider: Cesium.createWorldTerrain()
|
20 |
-
});
|
21 |
-
// Add Cesium OSM Buildings, a global 3D buildings layer.
|
22 |
-
const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
|
23 |
-
// Fly the camera to San Francisco at the given longitude, latitude, and height.
|
24 |
-
viewer.camera.flyTo({
|
25 |
-
destination : Cesium.Cartesian3.fromDegrees(-122.4175, 37.655, 400),
|
26 |
-
orientation : {
|
27 |
-
heading : Cesium.Math.toRadians(0.0),
|
28 |
-
pitch : Cesium.Math.toRadians(-15.0),
|
29 |
-
}
|
30 |
-
});
|
31 |
-
</script>
|
32 |
-
</div>
|
33 |
-
</body>
|
34 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/london_boroughs.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
data/realtor_data_dict.csv
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
Name,Label,Description
|
2 |
-
median_listing_price,Median Listing Price,The median listing price within the specified geography during the specified month.
|
3 |
-
median_listing_price_mm,Median Listing Price M/M,The percentage change in the median listing price from the previous month.
|
4 |
-
median_listing_price_yy,Median Listing Price Y/Y,The percentage change in the median listing price from the same month in the previous year.
|
5 |
-
active_listing_count,Active Listing Count,"The count of active listings within the specified geography during the specified month. The active listing count tracks the number of for sale properties on the market, excluding pending listings where a pending status is available. This is a snapshot measure of how many active listings can be expected on any given day of the specified month."
|
6 |
-
active_listing_count_mm,Active Listing Count M/M,The percentage change in the active listing count from the previous month.
|
7 |
-
active_listing_count_yy,Active Listing Count Y/Y,The percentage change in the active listing count from the same month in the previous year.
|
8 |
-
median_days_on_market,Days on Market,The median number of days property listings spend on the market within the specified geography during the specified month. Time spent on the market is defined as the time between the initial listing of a property and either its closing date or the date it is taken off the market.
|
9 |
-
median_days_on_market_mm,Days on Market M/M,The percentage change in the median days on market from the previous month.
|
10 |
-
median_days_on_market_yy,Days on Market Y/Y,The percentage change in the median days on market from the same month in the previous year.
|
11 |
-
new_listing_count,New Listing Count,The count of new listings added to the market within the specified geography. The new listing count represents a typical week’s worth of new listings in a given month. The new listing count can be multiplied by the number of weeks in a month to produce a monthly new listing count.
|
12 |
-
new_listing_count_mm,New Listing Count M/M,The percentage change in the new listing count from the previous month.
|
13 |
-
new_listing_count_yy,New Listing Count Y/Y,The percentage change in the new listing count from the same month in the previous year.
|
14 |
-
price_increased_count,Price Increase Count,The count of listings which have had their price increased within the specified geography. The price increase count represents a typical week’s worth of listings which have had their price increased in a given month. The price increase count can be multiplied by the number of weeks in a month to produce a monthly price increase count.
|
15 |
-
price_increased_count_mm,Price Increase Count M/M,The percentage change in the price increase count from the previous month.
|
16 |
-
price_increased_count_yy,Price Increase Count Y/Y,The percentage change in the price increase count from the same month in the previous year.
|
17 |
-
price_reduced_count,Price Decrease Count,The count of listings which have had their price reduced within the specified geography. The price decrease count represents a typical week’s worth of listings which have had their price reduced in a given month. The price decrease count can be multiplied by the number of weeks in a month to produce a monthly price decrease count.
|
18 |
-
price_reduced_count_mm,Price Decrease Count M/M,The percentage change in the price decrease count from the previous month.
|
19 |
-
price_reduced_count_yy,Price Decrease Count Y/Y,The percentage change in the price decrease count from the same month in the previous year.
|
20 |
-
pending_listing_count,Pending Listing Count,"The count of pending listings within the specified geography during the specified month, if a pending definition is available for that geography. This is a snapshot measure of how many pending listings can be expected on any given day of the specified month."
|
21 |
-
pending_listing_count_mm,Pending Listing Count M/M,The percentage change in the pending listing count from the previous month.
|
22 |
-
pending_listing_count_yy,Pending Listing Count Y/Y,The percentage change in the pending listing count from the same month in the previous year.
|
23 |
-
median_listing_price_per_square_foot,Median List Price Per Sqft,The median listing price per square foot within the specified geography during the specified month.
|
24 |
-
median_listing_price_per_square_foot_mm,Median List Price Per Sqft M/M,The percentage change in the median listing price per square foot from the previous month.
|
25 |
-
median_listing_price_per_square_foot_yy,Median List Price Per Sqft Y/Y,The percentage change in the median listing price per square foot from the same month in the previous year.
|
26 |
-
median_square_feet,Median Listing Sqft,The median listing square feet within the specified geography during the specified month.
|
27 |
-
median_square_feet_mm,Median Listing Sqft M/M,The percentage change in the median listing square feet from the previous month.
|
28 |
-
median_square_feet_yy,Median Listing Sqft Y/Y,The percentage change in the median listing square feet from the same month in the previous year.
|
29 |
-
average_listing_price,Avg Listing Price,The average listing price within the specified geography during the specified month.
|
30 |
-
average_listing_price_mm,Avg Listing Price M/M,The percentage change in the average listing price from the previous month.
|
31 |
-
average_listing_price_yy,Avg Listing Price Y/Y,The percentage change in the average listing price from the same month in the previous year.
|
32 |
-
total_listing_count,Total Listing Count,The total of both active listings and pending listings within the specified geography during the specified month. This is a snapshot measure of how many total listings can be expected on any given day of the specified month.
|
33 |
-
total_listing_count_mm,Total Listing Count M/M,The percentage change in the total listing count from the previous month.
|
34 |
-
total_listing_count_yy,Total Listing Count Y/Y,The percentage change in the total listing count from the same month in the previous year.
|
35 |
-
pending_ratio,Pending Ratio,The ratio of the pending listing count to the active listing count within the specified geography during the specified month.
|
36 |
-
pending_ratio_mm,Pending Ratio M/M,The change in the pending ratio from the previous month.
|
37 |
-
pending_ratio_yy,Pending Ratio Y/Y,The change in the pending ratio from the same month in the previous year.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/scotland_xyz.tsv
DELETED
@@ -1,51 +0,0 @@
|
|
1 |
-
Name URL
|
2 |
-
Ordnance Survey - Air Photos, 1944-1950 - 1:10,560 https://geo.nls.uk/maps/air-photos/{z}/{x}/{y}.png
|
3 |
-
Ordnance Survey - Six Inch Scotland, 1843-1882 - 1:10,560 https://mapseries-tilesets.s3.amazonaws.com/os/6inchfirst/{z}/{x}/{y}.png
|
4 |
-
War Office, Great Britain 1:25,000. GSGS 3906, 1940-43 https://mapseries-tilesets.s3.amazonaws.com/gsgs3906/{z}/{x}/{y}.png
|
5 |
-
Roy - Roy Highlands, 1747-1752 - 1:36000 https://mapseries-tilesets.s3.amazonaws.com/roy/highlands/{z}/{x}/{y}.png
|
6 |
-
Roy - Roy Lowlands, 1752-1755 - 1:36000 https://mapseries-tilesets.s3.amazonaws.com/roy/lowlands/{z}/{x}/{y}.png
|
7 |
-
Great Britain - OS 1:10,560, 1949-1970 https://mapseries-tilesets.s3.amazonaws.com/os/britain10knatgrid/{z}/{x}/{y}.png
|
8 |
-
Great Britain - Bartholomew Half Inch, 1897-1907 https://mapseries-tilesets.s3.amazonaws.com/bartholomew_great_britain/{z}/{x}/{y}.png
|
9 |
-
OS 25 inch, 1892-1914 - Scotland South https://mapseries-tilesets.s3.amazonaws.com/25_inch/scotland_1/{z}/{x}/{y}.png
|
10 |
-
OS 25 inch, 1892-1914 - Scotland North https://mapseries-tilesets.s3.amazonaws.com/25_inch/scotland_2/{z}/{x}/{y}.png
|
11 |
-
OS 25 inch, 1892-1914 - Bedfordshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/bedfordshire/{z}/{x}/{y}.png
|
12 |
-
OS 25 inch, 1892-1914 - Berkshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/berkshire/{z}/{x}/{y}.png
|
13 |
-
OS 25 inch, 1892-1914 - Buckinghamshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/buckingham/{z}/{x}/{y}.png
|
14 |
-
OS 25 inch, 1892-1914 - Cambridgeshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/cambridge/{z}/{x}/{y}.png
|
15 |
-
OS 25 inch, 1892-1914 - Cheshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/cheshire/{z}/{x}/{y}.png
|
16 |
-
OS 25 inch, 1892-1914 - Cornwall https://mapseries-tilesets.s3.amazonaws.com/25_inch/cornwall/{z}/{x}/{y}.png
|
17 |
-
OS 25 inch, 1892-1914 - Cumberland https://mapseries-tilesets.s3.amazonaws.com/25_inch/cumberland/{z}/{x}/{y}.png
|
18 |
-
OS 25 inch, 1892-1914 - Devon https://mapseries-tilesets.s3.amazonaws.com/25_inch/devon/{z}/{x}/{y}.png
|
19 |
-
OS 25 inch, 1892-1914 - Dorset https://mapseries-tilesets.s3.amazonaws.com/25_inch/dorset/{z}/{x}/{y}.png
|
20 |
-
OS 25 inch, 1892-1914 - Durham https://mapseries-tilesets.s3.amazonaws.com/25_inch/durham/{z}/{x}/{y}.png
|
21 |
-
OS 25 inch, 1892-1914 - Essex https://mapseries-tilesets.s3.amazonaws.com/25_inch/essex/{z}/{x}/{y}.png
|
22 |
-
OS 25 inch, 1892-1914 - Gloucestershire https://mapseries-tilesets.s3.amazonaws.com/25_inch/gloucestershire/{z}/{x}/{y}.png
|
23 |
-
OS 25 inch, 1892-1914 - Hampshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/hampshire/{z}/{x}/{y}.png
|
24 |
-
OS 25 inch, 1892-1914 - Herefordshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/herefordshire/{z}/{x}/{y}.png
|
25 |
-
OS 25 inch, 1892-1914 - Hertfordshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/hertfordshire/{z}/{x}/{y}.png
|
26 |
-
OS 25 inch, 1892-1914 - Huntingdon https://mapseries-tilesets.s3.amazonaws.com/25_inch/huntingdon/{z}/{x}/{y}.png
|
27 |
-
OS 25 inch, 1892-1914 - Kent https://mapseries-tilesets.s3.amazonaws.com/25_inch/kent/{z}/{x}/{y}.png
|
28 |
-
OS 25 inch, 1892-1914 - Lancashire https://mapseries-tilesets.s3.amazonaws.com/25_inch/lancashire/{z}/{x}/{y}.png
|
29 |
-
OS 25 inch, 1892-1914 - Leicestershire https://mapseries-tilesets.s3.amazonaws.com/25_inch/leicestershire/{z}/{x}/{y}.png
|
30 |
-
OS 25 inch, 1892-1914 - Lincolnshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/lincolnshire/{z}/{x}/{y}.png
|
31 |
-
OS 25 inch, 1892-1914 - London https://mapseries-tilesets.s3.amazonaws.com/25_inch/london/{z}/{x}/{y}.png
|
32 |
-
OS 25 inch, 1892-1914 - Middlesex https://mapseries-tilesets.s3.amazonaws.com/25_inch/middlesex/{z}/{x}/{y}.png
|
33 |
-
OS 25 inch, 1892-1914 - Norfolk https://mapseries-tilesets.s3.amazonaws.com/25_inch/norfolk/{z}/{x}/{y}.png
|
34 |
-
OS 25 inch, 1892-1914 - Northamptonshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/northampton/{z}/{x}/{y}.png
|
35 |
-
OS 25 inch, 1892-1914 - Northumberland https://mapseries-tilesets.s3.amazonaws.com/25_inch/northumberland/{z}/{x}/{y}.png
|
36 |
-
OS 25 inch, 1892-1914 - Nottinghamshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/nottinghamshire/{z}/{x}/{y}.png
|
37 |
-
OS 25 inch, 1892-1914 - Oxford https://mapseries-tilesets.s3.amazonaws.com/25_inch/oxford/{z}/{x}/{y}.png
|
38 |
-
OS 25 inch, 1892-1914 - Rutland https://mapseries-tilesets.s3.amazonaws.com/25_inch/rutland/{z}/{x}/{y}.png
|
39 |
-
OS 25 inch, 1892-1914 - Shropshire / Derbyshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/Shrop_Derby/{z}/{x}/{y}.png
|
40 |
-
OS 25 inch, 1892-1914 - Somerset https://mapseries-tilesets.s3.amazonaws.com/25_inch/somerset/{z}/{x}/{y}.png
|
41 |
-
OS 25 inch, 1892-1914 - Stafford https://mapseries-tilesets.s3.amazonaws.com/25_inch/stafford/{z}/{x}/{y}.png
|
42 |
-
OS 25 inch, 1892-1914 - Suffolk https://mapseries-tilesets.s3.amazonaws.com/25_inch/suffolk/{z}/{x}/{y}.png
|
43 |
-
OS 25 inch, 1892-1914 - Surrey https://mapseries-tilesets.s3.amazonaws.com/25_inch/surrey/{z}/{x}/{y}.png
|
44 |
-
OS 25 inch, 1892-1914 - Sussex https://mapseries-tilesets.s3.amazonaws.com/25_inch/sussex/{z}/{x}/{y}.png
|
45 |
-
OS 25 inch, 1892-1914 - Wales https://mapseries-tilesets.s3.amazonaws.com/25_inch/wales/{z}/{x}/{y}.png
|
46 |
-
OS 25 inch, 1892-1914 - Warwick https://mapseries-tilesets.s3.amazonaws.com/25_inch/warwick/{z}/{x}/{y}.png
|
47 |
-
OS 25 inch, 1892-1914 - Westmorland https://mapseries-tilesets.s3.amazonaws.com/25_inch/westmorland/{z}/{x}/{y}.png
|
48 |
-
OS 25 inch, 1892-1914 - Wiltshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/wiltshire2nd/{z}/{x}/{y}.png
|
49 |
-
OS 25 inch, 1892-1914 - Worcestershire https://mapseries-tilesets.s3.amazonaws.com/25_inch/Worcestershire/{z}/{x}/{y}.png
|
50 |
-
OS 25 inch, 1892-1914 - Yorkshire https://mapseries-tilesets.s3.amazonaws.com/25_inch/yorkshire/{z}/{x}/{y}.png
|
51 |
-
OS 25 inch, 1892-1914 'Holes' (fills gaps in series) https://geo.nls.uk/mapdata3/os/25_inch_holes_england/{z}/{x}/{y}.png
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/us_counties.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
data/us_metro_areas.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
data/us_nation.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
data/us_regions.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
data/us_states.geojson
DELETED
The diff for this file is too large to render.
See raw diff
|
|
environment-bk.yml
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
name: geo
|
2 |
-
channels:
|
3 |
-
- conda-forge
|
4 |
-
dependencies:
|
5 |
-
- gdal=3.4.3
|
6 |
-
- pip
|
7 |
-
- pip:
|
8 |
-
- geopandas
|
9 |
-
- keplergl
|
10 |
-
- streamlit
|
11 |
-
- localtileserver
|
12 |
-
- palettable
|
13 |
-
- streamlit-folium
|
14 |
-
- streamlit-keplergl
|
15 |
-
- streamlit-bokeh-events
|
16 |
-
- git+https://github.com/giswqs/leafmap
|
17 |
-
- git+https://github.com/giswqs/geemap
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index.html
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
<!DOCTYPE html>
|
2 |
-
<html>
|
3 |
-
<head>
|
4 |
-
<title>Streamlit for Geospatial</title>
|
5 |
-
<style type="text/css">
|
6 |
-
html {
|
7 |
-
overflow: auto;
|
8 |
-
}
|
9 |
-
html,
|
10 |
-
body,
|
11 |
-
div,
|
12 |
-
iframe {
|
13 |
-
margin: 0px;
|
14 |
-
padding: 0px;
|
15 |
-
height: 100%;
|
16 |
-
border: none;
|
17 |
-
}
|
18 |
-
iframe {
|
19 |
-
display: block;
|
20 |
-
width: 100%;
|
21 |
-
border: none;
|
22 |
-
overflow-y: auto;
|
23 |
-
overflow-x: hidden;
|
24 |
-
}
|
25 |
-
</style>
|
26 |
-
</head>
|
27 |
-
<body>
|
28 |
-
<iframe
|
29 |
-
src="https://share.streamlit.io/giswqs/streamlit-geospatial/app.py"
|
30 |
-
frameborder="0"
|
31 |
-
marginheight="0"
|
32 |
-
marginwidth="0"
|
33 |
-
width="100%"
|
34 |
-
height="100%"
|
35 |
-
scrolling="auto"
|
36 |
-
>
|
37 |
-
</iframe>
|
38 |
-
</body>
|
39 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
llama/__init__.py
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
from .generation import LLaMA
|
2 |
-
from .model import ModelArgs, Transformer, VisionModel
|
3 |
-
from .tokenizer import Tokenizer
|
|
|
|
|
|
|
|
llama/generation.py
DELETED
@@ -1,82 +0,0 @@
|
|
1 |
-
from typing import List
|
2 |
-
|
3 |
-
import torch
|
4 |
-
|
5 |
-
from llama.tokenizer import Tokenizer
|
6 |
-
from llama.model import Transformer
|
7 |
-
|
8 |
-
|
9 |
-
class LLaMA:
|
10 |
-
def __init__(self, model: Transformer, tokenizer: Tokenizer, vision_model = None):
|
11 |
-
self.model = model
|
12 |
-
self.tokenizer = tokenizer
|
13 |
-
self.vision_model = vision_model
|
14 |
-
|
15 |
-
def generate(
|
16 |
-
self,
|
17 |
-
prompts: List[str],
|
18 |
-
imgs = None,
|
19 |
-
max_gen_len: int = 512,
|
20 |
-
temperature: float = 0.8,
|
21 |
-
top_p: float = 0.95,
|
22 |
-
) -> List[str]:
|
23 |
-
bsz = len(prompts)
|
24 |
-
params = self.model.params
|
25 |
-
assert bsz <= params.max_batch_size, (bsz, params.max_batch_size)
|
26 |
-
|
27 |
-
mode = 'instruct'
|
28 |
-
vision_tokens = None
|
29 |
-
if imgs is not None and self.vision_model is not None:
|
30 |
-
vision_tokens = self.vision_model(imgs)
|
31 |
-
mode = 'caption'
|
32 |
-
|
33 |
-
prompt_tokens = [self.tokenizer.encode(x, bos=True, eos=False) for x in prompts]
|
34 |
-
|
35 |
-
min_prompt_size = min([len(t) for t in prompt_tokens])
|
36 |
-
max_prompt_size = max([len(t) for t in prompt_tokens])
|
37 |
-
|
38 |
-
total_len = min(params.max_seq_len, max_gen_len + max_prompt_size)
|
39 |
-
|
40 |
-
tokens = torch.full((bsz, total_len), self.tokenizer.pad_id).cuda().long()
|
41 |
-
for k, t in enumerate(prompt_tokens):
|
42 |
-
tokens[k, : len(t)] = torch.tensor(t).long()
|
43 |
-
input_text_mask = tokens != self.tokenizer.pad_id
|
44 |
-
start_pos = min_prompt_size
|
45 |
-
prev_pos = 0
|
46 |
-
for cur_pos in range(start_pos, total_len):
|
47 |
-
logits = self.model.forward(tokens[:, prev_pos:cur_pos], prev_pos, vision_tokens, mode)
|
48 |
-
if temperature > 0:
|
49 |
-
probs = torch.softmax(logits / temperature, dim=-1)
|
50 |
-
next_token = sample_top_p(probs, top_p)
|
51 |
-
else:
|
52 |
-
next_token = torch.argmax(logits, dim=-1)
|
53 |
-
next_token = next_token.reshape(-1)
|
54 |
-
# only replace token if prompt has already been generated
|
55 |
-
next_token = torch.where(
|
56 |
-
input_text_mask[:, cur_pos], tokens[:, cur_pos], next_token
|
57 |
-
)
|
58 |
-
tokens[:, cur_pos] = next_token
|
59 |
-
prev_pos = cur_pos
|
60 |
-
|
61 |
-
decoded = []
|
62 |
-
for i, t in enumerate(tokens.tolist()):
|
63 |
-
# cut to max gen len
|
64 |
-
t = t[len(prompt_tokens[i]) : len(prompt_tokens[i]) + max_gen_len]
|
65 |
-
# cut to eos tok if any
|
66 |
-
try:
|
67 |
-
t = t[: t.index(self.tokenizer.eos_id)]
|
68 |
-
except ValueError:
|
69 |
-
pass
|
70 |
-
decoded.append(self.tokenizer.decode(t))
|
71 |
-
return decoded
|
72 |
-
|
73 |
-
|
74 |
-
def sample_top_p(probs, p):
|
75 |
-
probs_sort, probs_idx = torch.sort(probs, dim=-1, descending=True)
|
76 |
-
probs_sum = torch.cumsum(probs_sort, dim=-1)
|
77 |
-
mask = probs_sum - probs_sort > p
|
78 |
-
probs_sort[mask] = 0.0
|
79 |
-
probs_sort.div_(probs_sort.sum(dim=-1, keepdim=True))
|
80 |
-
next_token = torch.multinomial(probs_sort, num_samples=1)
|
81 |
-
next_token = torch.gather(probs_idx, -1, next_token)
|
82 |
-
return next_token
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
llama/model.py
DELETED
@@ -1,424 +0,0 @@
|
|
1 |
-
from typing import Optional, Tuple
|
2 |
-
from dataclasses import dataclass
|
3 |
-
import math
|
4 |
-
|
5 |
-
import torch
|
6 |
-
from torch import nn
|
7 |
-
import torch.nn.functional as F
|
8 |
-
|
9 |
-
import clip
|
10 |
-
from timm.models.vision_transformer import Block
|
11 |
-
|
12 |
-
import fairscale.nn.model_parallel.initialize as fs_init
|
13 |
-
from fairscale.nn.model_parallel.layers import (
|
14 |
-
ParallelEmbedding,
|
15 |
-
RowParallelLinear,
|
16 |
-
ColumnParallelLinear,
|
17 |
-
)
|
18 |
-
|
19 |
-
|
20 |
-
@dataclass
|
21 |
-
class ModelArgs:
|
22 |
-
dim: int = 512
|
23 |
-
n_layers: int = 8
|
24 |
-
n_heads: int = 8
|
25 |
-
vocab_size: int = -1 # defined later by tokenizer
|
26 |
-
multiple_of: int = 256 # make SwiGLU hidden layer size multiple of large power of 2
|
27 |
-
norm_eps: float = 1e-5
|
28 |
-
|
29 |
-
max_batch_size: int = 32
|
30 |
-
max_seq_len: int = 2048
|
31 |
-
|
32 |
-
adapter_len: int = 10
|
33 |
-
adapter_layer: int = 30
|
34 |
-
|
35 |
-
cap_adapter_len: int = 10
|
36 |
-
cap_adapter_layer: int = 30
|
37 |
-
cap_vision_model: str = "ViT-L/14"
|
38 |
-
cap_vision_dim: int = 512
|
39 |
-
cap_vision_block: int = 2
|
40 |
-
|
41 |
-
|
42 |
-
class RMSNorm(torch.nn.Module):
|
43 |
-
def __init__(self, dim: int, eps: float = 1e-6):
|
44 |
-
super().__init__()
|
45 |
-
self.eps = eps
|
46 |
-
self.weight = nn.Parameter(torch.ones(dim))
|
47 |
-
|
48 |
-
def _norm(self, x):
|
49 |
-
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
|
50 |
-
|
51 |
-
def forward(self, x):
|
52 |
-
output = self._norm(x.float()).type_as(x)
|
53 |
-
return output * self.weight
|
54 |
-
|
55 |
-
|
56 |
-
def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
|
57 |
-
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
|
58 |
-
t = torch.arange(end, device=freqs.device) # type: ignore
|
59 |
-
freqs = torch.outer(t, freqs).float() # type: ignore
|
60 |
-
freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # complex64
|
61 |
-
return freqs_cis
|
62 |
-
|
63 |
-
|
64 |
-
def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
|
65 |
-
ndim = x.ndim
|
66 |
-
assert 0 <= 1 < ndim
|
67 |
-
assert freqs_cis.shape == (x.shape[1], x.shape[-1])
|
68 |
-
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
69 |
-
return freqs_cis.view(*shape)
|
70 |
-
|
71 |
-
|
72 |
-
def apply_rotary_emb(
|
73 |
-
xq: torch.Tensor,
|
74 |
-
xk: torch.Tensor,
|
75 |
-
freqs_cis: torch.Tensor,
|
76 |
-
) -> Tuple[torch.Tensor, torch.Tensor]:
|
77 |
-
xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
|
78 |
-
xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
|
79 |
-
freqs_cis = reshape_for_broadcast(freqs_cis, xq_)
|
80 |
-
xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3)
|
81 |
-
xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3)
|
82 |
-
return xq_out.type_as(xq), xk_out.type_as(xk)
|
83 |
-
|
84 |
-
|
85 |
-
class Attention(nn.Module):
|
86 |
-
def __init__(self, args: ModelArgs):
|
87 |
-
super().__init__()
|
88 |
-
|
89 |
-
self.n_local_heads = args.n_heads // fs_init.get_model_parallel_world_size()
|
90 |
-
self.head_dim = args.dim // args.n_heads
|
91 |
-
|
92 |
-
self.wq = ColumnParallelLinear(
|
93 |
-
args.dim,
|
94 |
-
args.n_heads * self.head_dim,
|
95 |
-
bias=False,
|
96 |
-
gather_output=False,
|
97 |
-
init_method=lambda x: x,
|
98 |
-
)
|
99 |
-
self.wk = ColumnParallelLinear(
|
100 |
-
args.dim,
|
101 |
-
args.n_heads * self.head_dim,
|
102 |
-
bias=False,
|
103 |
-
gather_output=False,
|
104 |
-
init_method=lambda x: x,
|
105 |
-
)
|
106 |
-
self.wv = ColumnParallelLinear(
|
107 |
-
args.dim,
|
108 |
-
args.n_heads * self.head_dim,
|
109 |
-
bias=False,
|
110 |
-
gather_output=False,
|
111 |
-
init_method=lambda x: x,
|
112 |
-
)
|
113 |
-
self.wo = RowParallelLinear(
|
114 |
-
args.n_heads * self.head_dim,
|
115 |
-
args.dim,
|
116 |
-
bias=False,
|
117 |
-
input_is_parallel=True,
|
118 |
-
init_method=lambda x: x,
|
119 |
-
)
|
120 |
-
|
121 |
-
self.cache_k = torch.zeros(
|
122 |
-
(args.max_batch_size, args.max_seq_len, self.n_local_heads, self.head_dim)
|
123 |
-
).cuda()
|
124 |
-
self.cache_v = torch.zeros(
|
125 |
-
(args.max_batch_size, args.max_seq_len, self.n_local_heads, self.head_dim)
|
126 |
-
).cuda()
|
127 |
-
self.gate = torch.nn.Parameter(torch.zeros(1))
|
128 |
-
|
129 |
-
self.cap_gate = torch.nn.Parameter(torch.zeros(1, self.n_local_heads, 1, 1))
|
130 |
-
|
131 |
-
def forward(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor],
|
132 |
-
adapter=None, mode='instruct'):
|
133 |
-
if mode == 'instruct':
|
134 |
-
return self.forward_instruct(x, start_pos, freqs_cis, mask, adapter)
|
135 |
-
elif mode == 'caption':
|
136 |
-
return self.forward_caption(x, start_pos, freqs_cis, mask, adapter)
|
137 |
-
|
138 |
-
def forward_instruct(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor],
|
139 |
-
adapter=None):
|
140 |
-
bsz, seqlen, _ = x.shape
|
141 |
-
xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
|
142 |
-
|
143 |
-
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
144 |
-
xk = xk.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
145 |
-
xv = xv.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
146 |
-
|
147 |
-
xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)
|
148 |
-
|
149 |
-
self.cache_k = self.cache_k.to(xq)
|
150 |
-
self.cache_v = self.cache_v.to(xq)
|
151 |
-
|
152 |
-
self.cache_k[:bsz, start_pos: start_pos + seqlen] = xk
|
153 |
-
self.cache_v[:bsz, start_pos: start_pos + seqlen] = xv
|
154 |
-
|
155 |
-
keys = self.cache_k[:bsz, : start_pos + seqlen]
|
156 |
-
values = self.cache_v[:bsz, : start_pos + seqlen]
|
157 |
-
|
158 |
-
if adapter is not None:
|
159 |
-
adapter_len = adapter.shape[1]
|
160 |
-
adapter_k = self.wk(adapter).view(1, adapter_len, self.n_local_heads, self.head_dim).repeat(bsz, 1, 1, 1)
|
161 |
-
adapter_v = self.wv(adapter).view(1, adapter_len, self.n_local_heads, self.head_dim).repeat(bsz, 1, 1, 1)
|
162 |
-
adapter_k = adapter_k.transpose(1, 2)
|
163 |
-
adapter_v = adapter_v.transpose(1, 2)
|
164 |
-
xq = xq.transpose(1, 2)
|
165 |
-
keys = keys.transpose(1, 2)
|
166 |
-
values = values.transpose(1, 2)
|
167 |
-
scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
|
168 |
-
if mask is not None:
|
169 |
-
scores = scores + mask # (bs, n_local_heads, slen, cache_len + slen)
|
170 |
-
scores = F.softmax(scores.float(), dim=-1).type_as(xq)
|
171 |
-
output = torch.matmul(scores, values) # (bs, n_local_heads, slen, head_dim)
|
172 |
-
if adapter is not None:
|
173 |
-
adapter_scores = torch.matmul(xq, adapter_k.transpose(2, 3)) / math.sqrt(self.head_dim)
|
174 |
-
adapter_scores = self.gate * F.softmax(adapter_scores.float(), dim=-1).type_as(xq)
|
175 |
-
output = output + torch.matmul(adapter_scores, adapter_v)
|
176 |
-
output = output.transpose(
|
177 |
-
1, 2
|
178 |
-
).contiguous().view(bsz, seqlen, -1)
|
179 |
-
|
180 |
-
return self.wo(output)
|
181 |
-
|
182 |
-
def forward_caption(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor],
|
183 |
-
adapter=None):
|
184 |
-
bsz, seqlen, _ = x.shape
|
185 |
-
xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
|
186 |
-
|
187 |
-
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
188 |
-
xk = xk.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
189 |
-
xv = xv.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
190 |
-
|
191 |
-
xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)
|
192 |
-
|
193 |
-
self.cache_k = self.cache_k.to(xq)
|
194 |
-
self.cache_v = self.cache_v.to(xq)
|
195 |
-
|
196 |
-
self.cache_k[:bsz, start_pos: start_pos + seqlen] = xk
|
197 |
-
self.cache_v[:bsz, start_pos: start_pos + seqlen] = xv
|
198 |
-
|
199 |
-
keys = self.cache_k[:bsz, : start_pos + seqlen]
|
200 |
-
values = self.cache_v[:bsz, : start_pos + seqlen]
|
201 |
-
|
202 |
-
if adapter is not None:
|
203 |
-
adapter_len = adapter.shape[1]
|
204 |
-
adapter_k = self.wk(adapter).view(bsz, adapter_len, self.n_local_heads, self.head_dim)
|
205 |
-
adapter_v = self.wv(adapter).view(bsz, adapter_len, self.n_local_heads, self.head_dim)
|
206 |
-
adapter_k = adapter_k.transpose(1, 2)
|
207 |
-
adapter_v = adapter_v.transpose(1, 2)
|
208 |
-
xq = xq.transpose(1, 2)
|
209 |
-
keys = keys.transpose(1, 2)
|
210 |
-
values = values.transpose(1, 2)
|
211 |
-
scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
|
212 |
-
if mask is not None:
|
213 |
-
scores = scores + mask # (bs, n_local_heads, slen, cache_len + slen)
|
214 |
-
scores = F.softmax(scores.float(), dim=-1).type_as(xq)
|
215 |
-
output = torch.matmul(scores, values) # (bs, n_local_heads, slen, head_dim)
|
216 |
-
if adapter is not None:
|
217 |
-
adapter_scores = torch.matmul(xq, adapter_k.transpose(2, 3)) / math.sqrt(self.head_dim)
|
218 |
-
adapter_scores = self.cap_gate.tanh() * F.softmax(adapter_scores.float(), dim=-1).type_as(xq)
|
219 |
-
|
220 |
-
output = output + torch.matmul(adapter_scores, adapter_v)
|
221 |
-
output = output.transpose(
|
222 |
-
1, 2
|
223 |
-
).contiguous().view(bsz, seqlen, -1)
|
224 |
-
|
225 |
-
return self.wo(output)
|
226 |
-
|
227 |
-
|
228 |
-
class FeedForward(nn.Module):
|
229 |
-
def __init__(
|
230 |
-
self,
|
231 |
-
dim: int,
|
232 |
-
hidden_dim: int,
|
233 |
-
multiple_of: int,
|
234 |
-
):
|
235 |
-
super().__init__()
|
236 |
-
hidden_dim = int(2 * hidden_dim / 3)
|
237 |
-
hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of)
|
238 |
-
|
239 |
-
self.w1 = ColumnParallelLinear(
|
240 |
-
dim, hidden_dim, bias=False, gather_output=False, init_method=lambda x: x
|
241 |
-
)
|
242 |
-
self.w2 = RowParallelLinear(
|
243 |
-
hidden_dim, dim, bias=False, input_is_parallel=True, init_method=lambda x: x
|
244 |
-
)
|
245 |
-
self.w3 = ColumnParallelLinear(
|
246 |
-
dim, hidden_dim, bias=False, gather_output=False, init_method=lambda x: x
|
247 |
-
)
|
248 |
-
|
249 |
-
def forward(self, x):
|
250 |
-
return self.w2(F.silu(self.w1(x)) * self.w3(x))
|
251 |
-
|
252 |
-
|
253 |
-
class TransformerBlock(nn.Module):
|
254 |
-
def __init__(self, layer_id: int, args: ModelArgs):
|
255 |
-
super().__init__()
|
256 |
-
self.n_heads = args.n_heads
|
257 |
-
self.dim = args.dim
|
258 |
-
self.head_dim = args.dim // args.n_heads
|
259 |
-
self.attention = Attention(args)
|
260 |
-
self.feed_forward = FeedForward(
|
261 |
-
dim=args.dim, hidden_dim=4 * args.dim, multiple_of=args.multiple_of
|
262 |
-
)
|
263 |
-
self.layer_id = layer_id
|
264 |
-
self.attention_norm = RMSNorm(args.dim, eps=args.norm_eps)
|
265 |
-
self.ffn_norm = RMSNorm(args.dim, eps=args.norm_eps)
|
266 |
-
|
267 |
-
def forward(self, x: torch.Tensor, start_pos: int, freqs_cis: torch.Tensor, mask: Optional[torch.Tensor],
|
268 |
-
adapter=None, mode='instruct'):
|
269 |
-
h = x + self.attention.forward(self.attention_norm(x), start_pos, freqs_cis, mask, adapter, mode=mode)
|
270 |
-
out = h + self.feed_forward.forward(self.ffn_norm(h))
|
271 |
-
return out
|
272 |
-
|
273 |
-
|
274 |
-
class Transformer(nn.Module):
|
275 |
-
def __init__(self, params: ModelArgs):
|
276 |
-
super().__init__()
|
277 |
-
self.params = params
|
278 |
-
self.vocab_size = params.vocab_size
|
279 |
-
self.n_layers = params.n_layers
|
280 |
-
|
281 |
-
self.tok_embeddings = ParallelEmbedding(
|
282 |
-
params.vocab_size, params.dim, init_method=lambda x: x
|
283 |
-
)
|
284 |
-
|
285 |
-
self.layers = torch.nn.ModuleList()
|
286 |
-
for layer_id in range(params.n_layers):
|
287 |
-
self.layers.append(TransformerBlock(layer_id, params))
|
288 |
-
|
289 |
-
self.norm = RMSNorm(params.dim, eps=params.norm_eps)
|
290 |
-
self.output = ColumnParallelLinear(
|
291 |
-
params.dim, params.vocab_size, bias=False, init_method=lambda x: x
|
292 |
-
)
|
293 |
-
|
294 |
-
self.freqs_cis = precompute_freqs_cis(
|
295 |
-
self.params.dim // self.params.n_heads, self.params.max_seq_len * 2
|
296 |
-
)
|
297 |
-
|
298 |
-
# Note: this is only a preview of multimodal LLaMA-Adapter
|
299 |
-
# and requires more efforts to decouple LLaMA-Adapter from LLaMA.
|
300 |
-
# instruct model
|
301 |
-
self.adapter_query = nn.Embedding(params.adapter_len * params.adapter_layer, params.dim)
|
302 |
-
self.adapter_len = params.adapter_len
|
303 |
-
self.adapter_layer = params.adapter_layer
|
304 |
-
|
305 |
-
# caption model
|
306 |
-
self.cap_adapter_query = nn.Embedding(params.cap_adapter_len * params.cap_adapter_layer, params.dim)
|
307 |
-
self.cap_adapter_len = params.cap_adapter_len
|
308 |
-
self.cap_adapter_layer = params.cap_adapter_layer
|
309 |
-
|
310 |
-
@torch.inference_mode()
|
311 |
-
def forward(self, tokens: torch.Tensor, start_pos: int, visual_tokens: torch.Tensor = None, mode: str = 'instruct'):
|
312 |
-
if mode == 'instruct':
|
313 |
-
return self.forward_instruct(tokens, start_pos, mode)
|
314 |
-
elif mode == 'caption':
|
315 |
-
return self.forward_caption(tokens, start_pos, visual_tokens, mode)
|
316 |
-
|
317 |
-
def forward_instruct(self, tokens: torch.Tensor, start_pos: int, mode=None):
|
318 |
-
_bsz, seqlen = tokens.shape
|
319 |
-
h = self.tok_embeddings(tokens)
|
320 |
-
self.freqs_cis = self.freqs_cis.to(h.device)
|
321 |
-
freqs_cis = self.freqs_cis[start_pos: start_pos + seqlen]
|
322 |
-
adapter = self.adapter_query.weight.reshape(self.params.adapter_layer, self.params.adapter_len,
|
323 |
-
self.params.dim).unsqueeze(1)
|
324 |
-
mask = None
|
325 |
-
if seqlen > 1:
|
326 |
-
mask = torch.full((1, 1, seqlen, seqlen), float("-inf"), device=tokens.device)
|
327 |
-
mask = torch.triu(mask, diagonal=start_pos + 1).type_as(h)
|
328 |
-
|
329 |
-
for layer in self.layers[: -1 * self.params.adapter_layer]:
|
330 |
-
h = layer(h, start_pos, freqs_cis, mask)
|
331 |
-
layer_index = 0
|
332 |
-
for layer in self.layers[-1 * self.params.adapter_layer:]:
|
333 |
-
h = layer(h, start_pos, freqs_cis, mask, adapter[layer_index], mode=mode)
|
334 |
-
layer_index = layer_index + 1
|
335 |
-
h = self.norm(h)
|
336 |
-
output = self.output(h[:, -1, :]) # only compute last logits
|
337 |
-
return output.float()
|
338 |
-
|
339 |
-
def forward_caption(self, tokens: torch.Tensor, start_pos: int, visual_tokens: torch.Tensor = None, mode=None):
|
340 |
-
_bsz, seqlen = tokens.shape
|
341 |
-
h = self.tok_embeddings(tokens)
|
342 |
-
self.freqs_cis = self.freqs_cis.to(h.device)
|
343 |
-
freqs_cis = self.freqs_cis[start_pos: start_pos + seqlen]
|
344 |
-
adapter = self.cap_adapter_query.weight.reshape(self.params.cap_adapter_layer, self.params.cap_adapter_len,
|
345 |
-
self.params.dim).unsqueeze(1)
|
346 |
-
mask = None
|
347 |
-
if seqlen > 1:
|
348 |
-
mask = torch.full((1, 1, seqlen, seqlen), float("-inf"), device=tokens.device)
|
349 |
-
mask = torch.triu(mask, diagonal=start_pos + 1).type_as(h)
|
350 |
-
|
351 |
-
for layer in self.layers[: -1 * self.params.cap_adapter_layer]:
|
352 |
-
h = layer(h, start_pos, freqs_cis, mask)
|
353 |
-
layer_index = 0
|
354 |
-
for layer in self.layers[-1 * self.params.cap_adapter_layer:]:
|
355 |
-
adapter_per_layer = adapter[layer_index]
|
356 |
-
if visual_tokens is not None:
|
357 |
-
adapter_per_layer = adapter_per_layer + visual_tokens
|
358 |
-
h = layer(h, start_pos, freqs_cis, mask, adapter_per_layer, mode=mode)
|
359 |
-
layer_index = layer_index + 1
|
360 |
-
h = self.norm(h)
|
361 |
-
output = self.output(h[:, -1, :]) # only compute last logits
|
362 |
-
return output.float()
|
363 |
-
|
364 |
-
|
365 |
-
class VisionModel(nn.Module):
|
366 |
-
def __init__(self, params: ModelArgs):
|
367 |
-
super().__init__()
|
368 |
-
|
369 |
-
self.params = params
|
370 |
-
|
371 |
-
self.clip, self.clip_transform = clip.load(params.cap_vision_model)
|
372 |
-
self.clip.float()
|
373 |
-
for param in self.clip.parameters():
|
374 |
-
param.requires_grad = False
|
375 |
-
|
376 |
-
self.clip_proj = nn.Linear(self.clip.visual.output_dim, params.cap_vision_dim)
|
377 |
-
self.clip_proj_norm = nn.LayerNorm(params.cap_vision_dim)
|
378 |
-
|
379 |
-
self.visual_query = nn.Embedding(params.cap_adapter_len, params.cap_vision_dim)
|
380 |
-
|
381 |
-
self.visual_blocks = nn.ModuleList([
|
382 |
-
Block(params.cap_vision_dim, 16, 4, qkv_bias=True, qk_scale=None, norm_layer=nn.LayerNorm)
|
383 |
-
for i in range(params.cap_vision_block)])
|
384 |
-
|
385 |
-
self.visual_proj = nn.Linear(params.cap_vision_dim, params.dim)
|
386 |
-
self.visual_proj_norm = nn.LayerNorm(params.dim)
|
387 |
-
|
388 |
-
def clip_encode_image(self, x):
|
389 |
-
x = self.clip.visual.conv1(x) # shape = [*, width, grid, grid]
|
390 |
-
x = x.reshape(x.shape[0], x.shape[1], -1) # shape = [*, width, grid ** 2]
|
391 |
-
x = x.permute(0, 2, 1) # shape = [*, grid ** 2, width]
|
392 |
-
x = torch.cat([self.clip.visual.class_embedding.to(x.dtype) + torch.zeros(x.shape[0], 1, x.shape[-1],
|
393 |
-
dtype=x.dtype, device=x.device), x],
|
394 |
-
dim=1) # shape = [*, grid ** 2 + 1, width]
|
395 |
-
x = x + self.clip.visual.positional_embedding.to(x.dtype)
|
396 |
-
x = self.clip.visual.ln_pre(x)
|
397 |
-
|
398 |
-
x = x.permute(1, 0, 2) # NLD -> LND
|
399 |
-
x = self.clip.visual.transformer(x)
|
400 |
-
x = x.permute(1, 0, 2) # LND -> NLD
|
401 |
-
|
402 |
-
x = self.clip.visual.ln_post(x[:, :, :])
|
403 |
-
|
404 |
-
if self.clip.visual.proj is not None:
|
405 |
-
x = x @ self.clip.visual.proj
|
406 |
-
|
407 |
-
return x
|
408 |
-
|
409 |
-
def forward(self, imgs):
|
410 |
-
x = [self.clip_transform(img) for img in imgs]
|
411 |
-
x = torch.stack(x, dim=0).to(self.visual_query.weight.device)
|
412 |
-
_bsz = x.shape[0]
|
413 |
-
|
414 |
-
visual_feats = self.clip_encode_image(x).half()
|
415 |
-
visual_feats = self.clip_proj_norm(self.clip_proj(visual_feats))
|
416 |
-
visual_query = self.visual_query.weight.unsqueeze(0).repeat(_bsz, 1, 1)
|
417 |
-
visual_query = torch.cat([visual_query, visual_feats], dim=1)
|
418 |
-
for block in self.visual_blocks:
|
419 |
-
visual_query = block(visual_query)
|
420 |
-
visual_query = visual_query[:, :self.params.cap_adapter_len, :]
|
421 |
-
visual_query = self.visual_proj(visual_query)
|
422 |
-
visual_query = self.visual_proj_norm(visual_query)
|
423 |
-
|
424 |
-
return visual_query
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
llama/tokenizer.py
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
from sentencepiece import SentencePieceProcessor
|
2 |
-
from logging import getLogger
|
3 |
-
from typing import List
|
4 |
-
import os
|
5 |
-
|
6 |
-
|
7 |
-
logger = getLogger()
|
8 |
-
|
9 |
-
|
10 |
-
class Tokenizer:
|
11 |
-
def __init__(self, model_path: str):
|
12 |
-
# reload tokenizer
|
13 |
-
assert os.path.isfile(model_path), model_path
|
14 |
-
self.sp_model = SentencePieceProcessor(model_file=model_path)
|
15 |
-
logger.info(f"Reloaded SentencePiece model from {model_path}")
|
16 |
-
|
17 |
-
# BOS / EOS token IDs
|
18 |
-
self.n_words: int = self.sp_model.vocab_size()
|
19 |
-
self.bos_id: int = self.sp_model.bos_id()
|
20 |
-
self.eos_id: int = self.sp_model.eos_id()
|
21 |
-
self.pad_id: int = self.sp_model.pad_id()
|
22 |
-
logger.info(
|
23 |
-
f"#words: {self.n_words} - BOS ID: {self.bos_id} - EOS ID: {self.eos_id}"
|
24 |
-
)
|
25 |
-
assert self.sp_model.vocab_size() == self.sp_model.get_piece_size()
|
26 |
-
|
27 |
-
def encode(self, s: str, bos: bool, eos: bool) -> List[int]:
|
28 |
-
assert type(s) is str
|
29 |
-
t = self.sp_model.encode(s)
|
30 |
-
if bos:
|
31 |
-
t = [self.bos_id] + t
|
32 |
-
if eos:
|
33 |
-
t = t + [self.eos_id]
|
34 |
-
return t
|
35 |
-
|
36 |
-
def decode(self, t: List[int]) -> str:
|
37 |
-
return self.sp_model.decode(t)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
multiapp.py
DELETED
@@ -1,81 +0,0 @@
|
|
1 |
-
"""Frameworks for running multiple Streamlit applications as a single app.
|
2 |
-
"""
|
3 |
-
import streamlit as st
|
4 |
-
|
5 |
-
# app_state = st.experimental_get_query_params()
|
6 |
-
# app_state = {k: v[0] if isinstance(v, list) else v for k, v in app_state.items()} # fetch the first item in each query string as we don't have multiple values for each query string key in this example
|
7 |
-
|
8 |
-
|
9 |
-
class MultiApp:
|
10 |
-
"""Framework for combining multiple streamlit applications.
|
11 |
-
Usage:
|
12 |
-
def foo():
|
13 |
-
st.title("Hello Foo")
|
14 |
-
def bar():
|
15 |
-
st.title("Hello Bar")
|
16 |
-
app = MultiApp()
|
17 |
-
app.add_app("Foo", foo)
|
18 |
-
app.add_app("Bar", bar)
|
19 |
-
app.run()
|
20 |
-
It is also possible keep each application in a separate file.
|
21 |
-
import foo
|
22 |
-
import bar
|
23 |
-
app = MultiApp()
|
24 |
-
app.add_app("Foo", foo.app)
|
25 |
-
app.add_app("Bar", bar.app)
|
26 |
-
app.run()
|
27 |
-
"""
|
28 |
-
|
29 |
-
def __init__(self):
|
30 |
-
self.apps = []
|
31 |
-
|
32 |
-
def add_app(self, title, func):
|
33 |
-
"""Adds a new application.
|
34 |
-
Parameters
|
35 |
-
----------
|
36 |
-
func:
|
37 |
-
the python function to render this app.
|
38 |
-
title:
|
39 |
-
title of the app. Appears in the dropdown in the sidebar.
|
40 |
-
"""
|
41 |
-
self.apps.append({"title": title, "function": func})
|
42 |
-
|
43 |
-
def run(self):
|
44 |
-
app_state = st.experimental_get_query_params()
|
45 |
-
app_state = {
|
46 |
-
k: v[0] if isinstance(v, list) else v for k, v in app_state.items()
|
47 |
-
} # fetch the first item in each query string as we don't have multiple values for each query string key in this example
|
48 |
-
|
49 |
-
# st.write('before', app_state)
|
50 |
-
|
51 |
-
titles = [a["title"] for a in self.apps]
|
52 |
-
functions = [a["function"] for a in self.apps]
|
53 |
-
default_radio = titles.index(app_state["page"]) if "page" in app_state else 0
|
54 |
-
|
55 |
-
st.sidebar.title("Navigation")
|
56 |
-
|
57 |
-
title = st.sidebar.radio("Go To", titles, index=default_radio, key="radio")
|
58 |
-
|
59 |
-
app_state["page"] = st.session_state.radio
|
60 |
-
# st.write('after', app_state)
|
61 |
-
|
62 |
-
st.experimental_set_query_params(**app_state)
|
63 |
-
# st.experimental_set_query_params(**st.session_state.to_dict())
|
64 |
-
functions[titles.index(title)]()
|
65 |
-
|
66 |
-
st.sidebar.title("Contribute")
|
67 |
-
st.sidebar.info(
|
68 |
-
"This is an open source project and you are very welcome to contribute your "
|
69 |
-
"comments, questions, resources and apps as "
|
70 |
-
"[issues](https://github.com/giswqs/streamlit-geospatial/issues) or "
|
71 |
-
"[pull requests](https://github.com/giswqs/streamlit-geospatial/pulls) "
|
72 |
-
"to the [source code](https://github.com/giswqs/streamlit-geospatial). "
|
73 |
-
)
|
74 |
-
st.sidebar.title("About")
|
75 |
-
st.sidebar.info(
|
76 |
-
"""
|
77 |
-
This web [app](https://share.streamlit.io/giswqs/streamlit-geospatial/app.py) is maintained by [Qiusheng Wu](https://wetlands.io). You can follow me on social media:
|
78 |
-
[GitHub](https://github.com/giswqs) | [Twitter](https://twitter.com/giswqs) | [YouTube](https://www.youtube.com/c/QiushengWu) | [LinkedIn](https://www.linkedin.com/in/qiushengwu).
|
79 |
-
This web app URL: <https://streamlit.gishub.org>
|
80 |
-
"""
|
81 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
packages.txt
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
ffmpeg
|
2 |
-
gifsicle
|
3 |
-
build-essential
|
4 |
-
python3-dev
|
5 |
-
gdal-bin
|
6 |
-
libgdal-dev
|
7 |
-
libproj-dev
|
8 |
-
libgeos-dev
|
9 |
-
proj-bin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
streamlit_app.py
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import leafmap.foliumap as leafmap
|
3 |
-
|
4 |
-
st.set_page_config(layout="wide")
|
5 |
-
|
6 |
-
st.sidebar.info(
|
7 |
-
"""
|
8 |
-
- Web App URL: <https://streamlit.gishub.org>
|
9 |
-
- GitHub repository: <https://github.com/giswqs/streamlit-geospatial>
|
10 |
-
"""
|
11 |
-
)
|
12 |
-
|
13 |
-
st.sidebar.title("Contact")
|
14 |
-
st.sidebar.info(
|
15 |
-
"""
|
16 |
-
Qiusheng Wu at [wetlands.io](https://wetlands.io) | [GitHub](https://github.com/giswqs) | [Twitter](https://twitter.com/giswqs) | [YouTube](https://www.youtube.com/c/QiushengWu) | [LinkedIn](https://www.linkedin.com/in/qiushengwu)
|
17 |
-
"""
|
18 |
-
)
|
19 |
-
|
20 |
-
# Customize page title
|
21 |
-
st.title("Streamlit for Geospatial Applications")
|
22 |
-
|
23 |
-
st.markdown(
|
24 |
-
"""
|
25 |
-
This multipage app template demonstrates various interactive web apps created using [streamlit](https://streamlit.io) and [leafmap](https://leafmap.org). It is an open-source project and you are very welcome to contribute to the [GitHub repository](https://github.com/giswqs/streamlit-multipage-template).
|
26 |
-
"""
|
27 |
-
)
|
28 |
-
|
29 |
-
st.header("Instructions")
|
30 |
-
|
31 |
-
markdown = """
|
32 |
-
1. For the [GitHub repository](https://github.com/giswqs/streamlit-multipage-template) or [use it as a template](https://github.com/giswqs/streamlit-multipage-template/generate) for your own project.
|
33 |
-
2. Customize the sidebar by changing the sidebar text and logo in each Python files.
|
34 |
-
3. Find your favorite emoji from https://emojipedia.org.
|
35 |
-
4. Add a new app to the `pages/` directory with an emoji in the file name, e.g., `1_🚀_Chart.py`.
|
36 |
-
|
37 |
-
"""
|
38 |
-
|
39 |
-
st.markdown(markdown)
|
40 |
-
|
41 |
-
m = leafmap.Map(minimap_control=True)
|
42 |
-
m.add_basemap("OpenTopoMap")
|
43 |
-
m.to_streamlit(height=500)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
streamlit_call.py
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
from subprocess import Popen
|
2 |
-
|
3 |
-
|
4 |
-
def load_jupyter_server_extension(nbapp):
|
5 |
-
"""serve the streamlit app"""
|
6 |
-
Popen(
|
7 |
-
[
|
8 |
-
"streamlit",
|
9 |
-
"run",
|
10 |
-
"Home.py",
|
11 |
-
"--browser.serverAddress=0.0.0.0",
|
12 |
-
"--server.enableCORS=False",
|
13 |
-
]
|
14 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|