Spaces:
Runtime error
Runtime error
Jonathan Marokhovsky
commited on
Commit
β’
c140c33
1
Parent(s):
139b81f
Made the cog-focused page, reorganized into more of an app framework, and updated the requirements
Browse files- Dockerfile +4 -4
- etc/cog_test.py +48 -0
- requirements.txt +5 -3
- requirements.txt.bak +4 -0
- toy_app/__init__.py +0 -0
- {pages β toy_app/pages}/00_home.py +0 -0
- {pages β toy_app/pages}/01_overture.py +3 -1
- {pages β toy_app/pages}/02_eurocrops.py.bak +0 -0
- toy_app/pages/03_cog.py +167 -0
- toy_app/pages/__init__.py +0 -0
- toy_app/src/__init__.py +0 -0
- toy_app/src/map_utils.py +23 -0
Dockerfile
CHANGED
@@ -9,10 +9,11 @@ RUN mamba install -c conda-forge leafmap geopandas localtileserver -y && \
|
|
9 |
COPY requirements.txt .
|
10 |
RUN pip install -r requirements.txt
|
11 |
|
12 |
-
RUN mkdir ./
|
13 |
-
COPY /
|
14 |
|
15 |
ENV PROJ_LIB='/opt/conda/share/proj'
|
|
|
16 |
|
17 |
USER root
|
18 |
RUN chown -R ${NB_UID} ${HOME}
|
@@ -20,5 +21,4 @@ USER ${NB_USER}
|
|
20 |
|
21 |
EXPOSE 8765
|
22 |
|
23 |
-
CMD ["solara", "run", "./pages", "--host=0.0.0.0"]
|
24 |
-
|
|
|
9 |
COPY requirements.txt .
|
10 |
RUN pip install -r requirements.txt
|
11 |
|
12 |
+
RUN mkdir ./toy_app
|
13 |
+
COPY /toy_app ./toy_app
|
14 |
|
15 |
ENV PROJ_LIB='/opt/conda/share/proj'
|
16 |
+
ENV PYTHONPATH "${PYTHONPATH}:/toy_app/src"
|
17 |
|
18 |
USER root
|
19 |
RUN chown -R ${NB_UID} ${HOME}
|
|
|
21 |
|
22 |
EXPOSE 8765
|
23 |
|
24 |
+
CMD ["solara", "run", "./toy_app/pages", "--host=0.0.0.0"]
|
|
etc/cog_test.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import List
|
2 |
+
from pystac_client import Client
|
3 |
+
import leafmap
|
4 |
+
|
5 |
+
# s2_url = "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a"
|
6 |
+
# client = Client.open(s2_url)
|
7 |
+
#
|
8 |
+
# s2_cols = client.get_collections()
|
9 |
+
# cog_ids: List[str] = list()
|
10 |
+
# for collection in s2_cols:
|
11 |
+
# cog_ids.append(collection.id)
|
12 |
+
#
|
13 |
+
# selected_cog = client.get_collection(cog_ids[1])
|
14 |
+
# selected_cog
|
15 |
+
esa_url = "https://services.terrascope.be/stac/"
|
16 |
+
|
17 |
+
client = Client.open(esa_url)
|
18 |
+
collections = client.get_collections()
|
19 |
+
stac_ids: List[str] = list()
|
20 |
+
for collection in collections:
|
21 |
+
stac_ids.append(collection.id)
|
22 |
+
|
23 |
+
center_point = [41.633484, -70.154371]
|
24 |
+
converted_center_point = dict(type="Point", coordinates=center_point)
|
25 |
+
m = leafmap.Map(center=center_point, zoom=8, height="500px")
|
26 |
+
esa_results = client.search(
|
27 |
+
collections=[stac_ids[15]],
|
28 |
+
intersects=converted_center_point,
|
29 |
+
max_items=12,
|
30 |
+
datetime="2021-01-01/2023-09-22",
|
31 |
+
query=["eo:cloud_cover<5"],
|
32 |
+
)
|
33 |
+
|
34 |
+
# import pystac
|
35 |
+
#
|
36 |
+
# lulc_url = (
|
37 |
+
# "https://api.impactobservatory.com/stac-aws/collections/io-10m-annual-lulc/items"
|
38 |
+
# )
|
39 |
+
# collections = pystac
|
40 |
+
# print(collections)
|
41 |
+
|
42 |
+
|
43 |
+
# import leafmap
|
44 |
+
#
|
45 |
+
# esa_url = "https://services.terrascope.be/stac/api/"
|
46 |
+
#
|
47 |
+
# m = leafmap.Map(center=[41.633484, -70.154371], zoom=8, height="500px")
|
48 |
+
# m.add_stac_layer(esa_url)
|
requirements.txt
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
solara
|
2 |
-
|
3 |
-
|
4 |
-
|
|
|
|
|
|
1 |
solara
|
2 |
+
area==1.1.1
|
3 |
+
geojson==3.1.0
|
4 |
+
ipyleaflet==0.18.2
|
5 |
+
ipywidgets==8.1.2
|
6 |
+
leafmap==0.31.9
|
requirements.txt.bak
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
solara
|
2 |
+
leafmap
|
3 |
+
pmtiles
|
4 |
+
geopandas
|
toy_app/__init__.py
ADDED
File without changes
|
{pages β toy_app/pages}/00_home.py
RENAMED
File without changes
|
{pages β toy_app/pages}/01_overture.py
RENAMED
@@ -1,8 +1,10 @@
|
|
1 |
import leafmap
|
2 |
from leafmap import pmtiles_style
|
|
|
3 |
import solara
|
4 |
import ipywidgets as widgets
|
5 |
|
|
|
6 |
# THings to come:
|
7 |
# TODO: Calculate some metrics given the area.
|
8 |
# TODO: add a raster to the toy
|
@@ -65,7 +67,7 @@ class Map(leafmap.Map):
|
|
65 |
print(self.user_roi)
|
66 |
|
67 |
|
68 |
-
@
|
69 |
def Page():
|
70 |
with solara.Row():
|
71 |
solara.Button(label="Area", on_click=Map.run_area)
|
|
|
1 |
import leafmap
|
2 |
from leafmap import pmtiles_style
|
3 |
+
from reacton import component as component
|
4 |
import solara
|
5 |
import ipywidgets as widgets
|
6 |
|
7 |
+
|
8 |
# THings to come:
|
9 |
# TODO: Calculate some metrics given the area.
|
10 |
# TODO: add a raster to the toy
|
|
|
67 |
print(self.user_roi)
|
68 |
|
69 |
|
70 |
+
@component
|
71 |
def Page():
|
72 |
with solara.Row():
|
73 |
solara.Button(label="Area", on_click=Map.run_area)
|
{pages β toy_app/pages}/02_eurocrops.py.bak
RENAMED
File without changes
|
toy_app/pages/03_cog.py
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from ipyleaflet import Polygon, WidgetControl
|
2 |
+
from ipywidgets import widgets
|
3 |
+
import leafmap
|
4 |
+
import logging
|
5 |
+
import solara
|
6 |
+
from reacton import component as component
|
7 |
+
|
8 |
+
from toy_app.src import map_utils
|
9 |
+
|
10 |
+
log = logging.getLogger(__name__)
|
11 |
+
|
12 |
+
|
13 |
+
# Display a cog in leafmap
|
14 |
+
# Allow the user to draw a shape on the map
|
15 |
+
# Retrieve the user's shape and run metrics on it
|
16 |
+
# Used Stac to get the available collections from the 10m Annual Land Use Land Cover (9-class)
|
17 |
+
libya_fire_url = (
|
18 |
+
"https://github.com/opengeos/data/releases/download/raster/Libya-2023-07-01.tif"
|
19 |
+
)
|
20 |
+
libya_fire_name = "Libya Fire"
|
21 |
+
libya_layer_visible = solara.reactive("True")
|
22 |
+
test_poly = {
|
23 |
+
"type": "Feature",
|
24 |
+
"properties": {},
|
25 |
+
"geometry": {
|
26 |
+
"type": "Polygon",
|
27 |
+
"coordinates": [
|
28 |
+
[
|
29 |
+
[-72.949219, 42.447781],
|
30 |
+
[-73.35022, 42.195969],
|
31 |
+
[-73.372192, 41.799983],
|
32 |
+
[-73.026123, 41.566142],
|
33 |
+
[-72.608643, 41.343825],
|
34 |
+
[-72.301025, 41.590797],
|
35 |
+
[-71.993408, 41.442726],
|
36 |
+
[-71.768188, 41.967659],
|
37 |
+
[-71.28479, 42.138968],
|
38 |
+
[-72.064819, 42.362603],
|
39 |
+
[-72.614136, 42.195969],
|
40 |
+
[-72.949219, 42.447781],
|
41 |
+
]
|
42 |
+
],
|
43 |
+
},
|
44 |
+
}
|
45 |
+
|
46 |
+
|
47 |
+
class Map(leafmap.Map):
|
48 |
+
"""
|
49 |
+
This is the map which will be displayed in the solara webapp
|
50 |
+
"""
|
51 |
+
|
52 |
+
def __init__(self, **kwargs):
|
53 |
+
log.warning("Starting Map init")
|
54 |
+
kwargs["toolbar_control"] = False
|
55 |
+
super().__init__(**kwargs)
|
56 |
+
basemap = {
|
57 |
+
"url": "https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}",
|
58 |
+
"attribution": "Google",
|
59 |
+
"name": "Google Satellite",
|
60 |
+
"opacity": 1.0,
|
61 |
+
}
|
62 |
+
self.add_tile_layer(**basemap, shown=False)
|
63 |
+
self.add_layer_manager(opened=False)
|
64 |
+
self.add_cog_layer(
|
65 |
+
url=libya_fire_url, name=libya_fire_name
|
66 |
+
) # Just display the cog for now, the layer toggle isn't working as expected
|
67 |
+
# self.toggle_libya_layer()
|
68 |
+
# user_poly = map_utils.get_user_polygon(self)
|
69 |
+
# if user_poly:
|
70 |
+
# log.warning(f"Polygon? {user_poly}")
|
71 |
+
# self.add_click_detector_widget()
|
72 |
+
self.add_button()
|
73 |
+
|
74 |
+
def add_click_detector_widget(self, position="bottomright"):
|
75 |
+
output = widgets.Output()
|
76 |
+
control = WidgetControl(widget=output, position=position)
|
77 |
+
self.add(control)
|
78 |
+
|
79 |
+
def update_user_click(**kwargs):
|
80 |
+
with output:
|
81 |
+
print(kwargs)
|
82 |
+
|
83 |
+
self.on_interaction(update_user_click)
|
84 |
+
|
85 |
+
def add_button(self):
|
86 |
+
button = widgets.Button(description="Get ROI")
|
87 |
+
output = widgets.Output()
|
88 |
+
|
89 |
+
def on_button_click(_):
|
90 |
+
if self.user_roi is not None:
|
91 |
+
with output:
|
92 |
+
output.clear_output()
|
93 |
+
print(map_utils.calculate_area(self.user_roi))
|
94 |
+
|
95 |
+
button.on_click(on_button_click)
|
96 |
+
|
97 |
+
widget = widgets.VBox([button, output])
|
98 |
+
self.add_widget(widget)
|
99 |
+
|
100 |
+
def toggle_libya_layer(self):
|
101 |
+
if libya_layer_visible.value == "True":
|
102 |
+
# The layer should be visible, if you don't find it, add it
|
103 |
+
log.warning("Layer should be visible, checking it needs to be added")
|
104 |
+
for layer in self.layers:
|
105 |
+
l_name = layer.name
|
106 |
+
if l_name == libya_fire_name:
|
107 |
+
return
|
108 |
+
else:
|
109 |
+
log.warning("Layer not found, adding new cog layer")
|
110 |
+
self.add_cog_layer(url=libya_fire_url, name=libya_fire_name)
|
111 |
+
else:
|
112 |
+
# The layer should not be visible, if you find it, remove it
|
113 |
+
log.warning("Layer should not be visible, checking to see if it's there")
|
114 |
+
for layer in self.layers:
|
115 |
+
l_name = layer.name
|
116 |
+
if l_name != libya_fire_name:
|
117 |
+
continue
|
118 |
+
log.warning("Layer found, removing it")
|
119 |
+
self.remove_layer(l_name)
|
120 |
+
|
121 |
+
|
122 |
+
zoom = solara.reactive(2)
|
123 |
+
center = solara.reactive((20, 0))
|
124 |
+
drawing = solara.reactive(Polygon())
|
125 |
+
|
126 |
+
|
127 |
+
@component
|
128 |
+
def Page():
|
129 |
+
l_map = Map()
|
130 |
+
|
131 |
+
# def toggle_map():
|
132 |
+
# log.warning("Attempting to toggle the layer")
|
133 |
+
# if libya_layer_visible.value == "True":
|
134 |
+
# libya_layer_visible.set("False")
|
135 |
+
# else:
|
136 |
+
# libya_layer_visible.set("True")
|
137 |
+
# log.warning(f"New layer visibility is set to {libya_layer_visible.value}")
|
138 |
+
def calc_area():
|
139 |
+
log.info("Trying to calculate the area")
|
140 |
+
# user_polygon = l_map.user_roi
|
141 |
+
user_polygon = drawing.value
|
142 |
+
log.warning(f"The polygon is: {user_polygon}")
|
143 |
+
log.warning(f"The user_roi is: {l_map.user_rois}")
|
144 |
+
|
145 |
+
print("Not yet implemented")
|
146 |
+
|
147 |
+
with solara.Column(style={"min_width": "500px"}):
|
148 |
+
# Map.element( # type: ignore
|
149 |
+
l_map.element( # type: ignore
|
150 |
+
zoom=zoom.value,
|
151 |
+
on_zoom=zoom.set,
|
152 |
+
center=center.value,
|
153 |
+
on_center=center.set,
|
154 |
+
scroll_wheel_zoom=True,
|
155 |
+
toolbar_ctrl=False,
|
156 |
+
draw_control=True,
|
157 |
+
on_draw=drawing.set,
|
158 |
+
# data_ctrl=False,
|
159 |
+
height="780px",
|
160 |
+
)
|
161 |
+
with solara.Row():
|
162 |
+
solara.Text(f"Center: {center.value}")
|
163 |
+
solara.Text(f"Zoom: {zoom.value}")
|
164 |
+
|
165 |
+
with solara.Row():
|
166 |
+
solara.Button(label="Calculate Polygon Area", on_click=calc_area)
|
167 |
+
# solara.Button(label="Toggle Fire Image", on_click=toggle_map) # Disable toggle for now since it's not working as expected
|
toy_app/pages/__init__.py
ADDED
File without changes
|
toy_app/src/__init__.py
ADDED
File without changes
|
toy_app/src/map_utils.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
These functions are meant to manipulate and act on a given leafmap instance in solara.
|
3 |
+
"""
|
4 |
+
|
5 |
+
import logging
|
6 |
+
import typing
|
7 |
+
import leafmap
|
8 |
+
import geojson
|
9 |
+
from area import area as area
|
10 |
+
|
11 |
+
|
12 |
+
log = logging.getLogger(__name__)
|
13 |
+
|
14 |
+
|
15 |
+
def calculate_area(poly: geojson.GeoJSON) -> float:
|
16 |
+
output: float = 0.0
|
17 |
+
try:
|
18 |
+
if poly["geometry"] and poly["geometry"]["type"] == "Polgon":
|
19 |
+
output = area(poly["geometry"])
|
20 |
+
except KeyError:
|
21 |
+
raise TypeError("The given GeoJSON did not contain a Polygon as was expected")
|
22 |
+
|
23 |
+
return output
|