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 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 ./pages
13
- COPY /pages ./pages
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
- leafmap
3
- pmtiles
4
- geopandas
 
 
 
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
- @solara.component
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