Vertdure commited on
Commit
583726c
1 Parent(s): b2ca552

Update pages/12_🌲_VertXtractor.py

Browse files
Files changed (1) hide show
  1. pages/12_🌲_VertXtractor.py +75 -166
pages/12_🌲_VertXtractor.py CHANGED
@@ -1,15 +1,15 @@
1
  import streamlit as st
2
- import folium
3
- from streamlit_folium import folium_static
4
  import geopandas as gpd
5
  import tempfile
6
  import os
7
- import urllib
8
  import json
9
  from pathlib import Path
10
  import datetime
11
  from osgeo import gdal
12
- from PIL import Image
 
 
13
 
14
  # Constants
15
  CATEGORIES = {
@@ -33,16 +33,15 @@ DIC_LAYERS = {
33
  # Helper functions
34
  def wgs84_to_lv95(lat, lon):
35
  url = f'http://geodesy.geo.admin.ch/reframe/wgs84tolv95?easting={lat}&northing={lon}&format=json'
36
- site = urllib.request.urlopen(url)
37
- data = json.load(site)
38
  return data['easting'], data['northing']
39
 
40
  def lv95_to_wgs84(x, y):
41
  url = f'http://geodesy.geo.admin.ch/reframe/lv95towgs84?easting={x}&northing={y}&format=json'
42
- f = urllib.request.urlopen(url)
43
- txt = f.read().decode('utf-8')
44
- json_res = json.loads(txt)
45
- return json_res
46
 
47
  def detect_and_convert_bbox(bbox):
48
  xmin, ymin, xmax, ymax = bbox
@@ -82,7 +81,7 @@ def detect_and_convert_bbox(bbox):
82
  wgs84_min = lv95_to_wgs84(xmin, ymin)
83
  wgs84_max = lv95_to_wgs84(xmax, ymax)
84
 
85
- bbox_wgs84 = (wgs84_min['easting'], wgs84_min['northing'], wgs84_max['easting'], wgs84_max['northing'])
86
  return (bbox_wgs84, bbox)
87
 
88
  return None
@@ -98,9 +97,8 @@ def get_list_from_STAC_swisstopo(url, est, sud, ouest, nord, gdb=False):
98
  res = []
99
 
100
  while url:
101
- f = urllib.request.urlopen(url)
102
- txt = f.read().decode('utf-8')
103
- json_res = json.loads(txt)
104
  url = None
105
  links = json_res.get('links', None)
106
  if links:
@@ -164,26 +162,31 @@ def suppr_doublons_list_mnt(lst):
164
  res.append(url)
165
  return res
166
 
167
- def get_trees_points_from_bbox(bbox, fn_geojson):
168
- xmin, ymin, xmax, ymax = bbox
169
- url_base = 'https://hepiadata.hesge.ch/arcgis/rest/services/suisse/TLM_C4D_couverture_sol/FeatureServer/0/query?'
170
- params = {
171
- "geometry": f"{xmin},{ymin},{xmax},{ymax}",
172
- "geometryType": "esriGeometryEnvelope",
173
- "returnGeometry": "true",
174
- "returnZ": "true",
175
- "spatialRel": "esriSpatialRelIntersects",
176
- "f": "json"
177
- }
178
- query_string = urllib.parse.urlencode(params)
179
- url = url_base + query_string
180
-
181
- with urllib.request.urlopen(url) as response:
182
- response_data = response.read()
183
- data = json.loads(response_data)
184
-
185
- with open(fn_geojson, 'w') as f:
186
- json.dump(data, f)
 
 
 
 
 
187
 
188
  def geojson_forest(bbox, fn_geojson):
189
  xmin, ymin, xmax, ymax = bbox
@@ -207,137 +210,12 @@ def geojson_forest(bbox, fn_geojson):
207
  url = url_base + query_string
208
 
209
  with urllib.request.urlopen(url) as response:
210
- response_data = response.read()
211
- data = json.loads(response_data)
212
-
213
- with open(fn_geojson, 'w') as f:
214
- json.dump(data, f)
215
-
216
- def get_urls(bbox_wgs84, mnt=True, mns=True, bati3D_v2=True, bati3D_v3=True, ortho=True, mnt_resol=0.5, ortho_resol=0.1):
217
- est, sud, ouest, nord = bbox_wgs84
218
- urls = []
219
-
220
- if mnt:
221
- mnt_resol = 0.5 if mnt_resol < 2 else 2
222
- tri = f'_{mnt_resol}_'
223
- url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS['mnt']
224
- lst = [v for v in get_list_from_STAC_swisstopo(url, est, sud, ouest, nord) if tri in v]
225
- lst = suppr_doublons_list_mnt(lst)
226
- urls += lst
227
-
228
- if mns:
229
- url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS['mns']
230
- lst = [v for v in get_list_from_STAC_swisstopo(url, est, sud, ouest, nord) if 'raster' in v]
231
- lst = suppr_doublons_list_mnt(lst)
232
- urls += lst
233
-
234
- if bati3D_v2:
235
- url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS['bati3D_v2']
236
- lst = get_list_from_STAC_swisstopo(url, est, sud, ouest, nord)
237
- lst = suppr_doublons_bati3D_v2(lst)
238
- urls += lst
239
-
240
- if bati3D_v3:
241
- url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS['bati3D_v3']
242
- lst = get_list_from_STAC_swisstopo(url, est, sud, ouest, nord, gdb=True)
243
- lst = suppr_doublons_bati3D_v3(lst)
244
- urls += lst
245
-
246
- if ortho:
247
- ortho_resol = 0.1 if ortho_resol < 2 else 2
248
- tri = f'_{ortho_resol}_'
249
- url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS['ortho']
250
- lst = [v for v in get_list_from_STAC_swisstopo(url, est, sud, ouest, nord) if tri in v]
251
- lst = suppr_doublons_list_ortho(lst)
252
- urls += lst
253
-
254
- return urls
255
-
256
- def classification_urls(urls):
257
- dic = {}
258
- for url in urls:
259
- fn = url.split('/')[-1]
260
- dirname = fn.split('_')[0]
261
-
262
- if dirname == 'swissbuildings3d':
263
- name, version, *a = fn.split('_')
264
- if version == '2':
265
- an = fn.split('_')[2].split('-')[0]
266
- elif version == '3':
267
- an = fn.split('_')[3]
268
- dirname = f'{name}_v{version}_{an}'
269
-
270
- elif dirname == 'swissalti3d':
271
- name, an, no_flle, resol, *a = fn.split('_')
272
- if resol == '0.5':
273
- resol = '50cm'
274
- fn = fn.replace('0.5', '50cm')
275
- elif resol == '2':
276
- resol = '2m'
277
- fn = fn.replace('2', '2m')
278
- dirname = f'{name}_{an}_{resol}'
279
- elif dirname == 'swisssurface3d-raster':
280
- name, an, no_flle, resol, *a = fn.split('_')
281
- if resol == '0.5':
282
- resol = '50cm'
283
- fn = fn.replace('0.5', '50cm')
284
- dirname = f'{name}_{an}_{resol}'
285
- elif dirname == 'swissimage-dop10':
286
- name, an, no_flle, resol, *a = fn.split('_')
287
- if resol == '0.1':
288
- resol = '10cm'
289
- fn = fn.replace('0.1', '10cm')
290
- elif resol == '2':
291
- resol = '2m'
292
- fn = fn.replace('2', '2m')
293
- dirname = f'{name}_{an}_{resol}'
294
-
295
- dic.setdefault(dirname, []).append((url, fn))
296
- return dic
297
-
298
- def download_files(urls, path):
299
- now = datetime.datetime.now()
300
- path = Path(path) / f'swisstopo_extraction_{now.strftime("%Y%m%d_%H%M")}'
301
- path.mkdir(exist_ok=True)
302
-
303
- for k, v in classification_urls(urls).items():
304
- p = path / k
305
- p.mkdir(exist_ok=True)
306
- for url, fn in v:
307
- urllib.request.urlretrieve(url, p / fn)
308
- return path
309
-
310
- def download_and_merge_ortho(urls, output_path):
311
- """
312
- Download ortho images from URLs and merge them into a single GeoTIFF.
313
-
314
- :param urls: List of URLs to download ortho images from
315
- :param output_path: Path to save the merged image
316
- :return: Path to the merged image
317
- """
318
- # Create a temporary directory to store downloaded files
319
- with tempfile.TemporaryDirectory() as temp_dir:
320
- # Download all files
321
- local_files = []
322
- for i, url in enumerate(urls):
323
- local_filename = os.path.join(temp_dir, f"ortho_{i}.tif")
324
- urllib.request.urlretrieve(url, local_filename)
325
- local_files.append(local_filename)
326
-
327
- # Merge downloaded files
328
- vrt_options = gdal.BuildVRTOptions(resampleAlg='nearest', addAlpha=False)
329
- vrt_path = os.path.join(temp_dir, "merged.vrt")
330
- vrt = gdal.BuildVRT(vrt_path, local_files, options=vrt_options)
331
- vrt = None # Close the dataset
332
 
333
- # Translate VRT to GeoTIFF
334
- translate_options = gdal.TranslateOptions(format="GTiff", creationOptions=["COMPRESS=LZW", "TILED=YES"])
335
- gdal.Translate(output_path, vrt_path, options=translate_options)
336
-
337
- return output_path
338
 
339
  def create_geojson_with_links(urls, bbox):
340
- """Create a GeoJSON file with download links"""
341
  features = []
342
  for url in urls:
343
  feature = {
@@ -348,7 +226,7 @@ def create_geojson_with_links(urls, bbox):
348
  },
349
  "properties": {
350
  "url": url,
351
- "type": url.split('/')[-2].split('_')[0] # Assuming type is in the URL
352
  }
353
  }
354
  features.append(feature)
@@ -360,7 +238,6 @@ def create_geojson_with_links(urls, bbox):
360
  return json.dumps(geojson)
361
 
362
  def merge_ortho_images(urls, output_format='GTiff'):
363
- """Merge ortho images and return the result as bytes"""
364
  with tempfile.TemporaryDirectory() as temp_dir:
365
  local_files = []
366
  for i, url in enumerate(urls):
@@ -387,7 +264,6 @@ def merge_ortho_images(urls, output_format='GTiff'):
387
  return f.read()
388
 
389
  def prepare_download_package(urls, bbox, ortho_format):
390
- """Prepare a zip file containing all data"""
391
  geojson_data = create_geojson_with_links(urls, bbox)
392
  ortho_urls = [url for url in urls if 'swissimage-dop10' in url]
393
  ortho_data = merge_ortho_images(ortho_urls, ortho_format) if ortho_urls else None
@@ -405,7 +281,40 @@ st.set_page_config(page_title="Swiss Geospatial Data Downloader", layout="wide")
405
 
406
  st.title("Swiss Geospatial Data Downloader")
407
 
408
- # (Include the sidebar and main content area from the previous version)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
  if st.session_state.bbox:
411
  st.write(f"Selected bounding box (WGS84): {st.session_state.bbox}")
 
1
  import streamlit as st
 
 
2
  import geopandas as gpd
3
  import tempfile
4
  import os
5
+ import urllib.request
6
  import json
7
  from pathlib import Path
8
  import datetime
9
  from osgeo import gdal
10
+ import io
11
+ import zipfile
12
+ import base64
13
 
14
  # Constants
15
  CATEGORIES = {
 
33
  # Helper functions
34
  def wgs84_to_lv95(lat, lon):
35
  url = f'http://geodesy.geo.admin.ch/reframe/wgs84tolv95?easting={lat}&northing={lon}&format=json'
36
+ with urllib.request.urlopen(url) as response:
37
+ data = json.load(response)
38
  return data['easting'], data['northing']
39
 
40
  def lv95_to_wgs84(x, y):
41
  url = f'http://geodesy.geo.admin.ch/reframe/lv95towgs84?easting={x}&northing={y}&format=json'
42
+ with urllib.request.urlopen(url) as response:
43
+ data = json.load(response)
44
+ return data['easting'], data['northing']
 
45
 
46
  def detect_and_convert_bbox(bbox):
47
  xmin, ymin, xmax, ymax = bbox
 
81
  wgs84_min = lv95_to_wgs84(xmin, ymin)
82
  wgs84_max = lv95_to_wgs84(xmax, ymax)
83
 
84
+ bbox_wgs84 = (wgs84_min, wgs84_max[0], wgs84_max[1], wgs84_min[1])
85
  return (bbox_wgs84, bbox)
86
 
87
  return None
 
97
  res = []
98
 
99
  while url:
100
+ with urllib.request.urlopen(url) as response:
101
+ json_res = json.load(response)
 
102
  url = None
103
  links = json_res.get('links', None)
104
  if links:
 
162
  res.append(url)
163
  return res
164
 
165
+ @st.cache_data
166
+ def get_urls(bbox_wgs84, data_types, resolutions):
167
+ urls = []
168
+ for data_type, enabled in data_types.items():
169
+ if enabled:
170
+ url = URL_STAC_SWISSTOPO_BASE + DIC_LAYERS[data_type]
171
+ if data_type in ['mnt', 'ortho']:
172
+ resolution = resolutions[data_type]
173
+ tri = f'_{resolution}_'
174
+ lst = [v for v in get_list_from_STAC_swisstopo(url, *bbox_wgs84) if tri in v]
175
+ if data_type == 'mnt':
176
+ lst = suppr_doublons_list_mnt(lst)
177
+ else:
178
+ lst = suppr_doublons_list_ortho(lst)
179
+ elif data_type == 'mns':
180
+ lst = [v for v in get_list_from_STAC_swisstopo(url, *bbox_wgs84) if 'raster' in v]
181
+ lst = suppr_doublons_list_mnt(lst)
182
+ elif data_type == 'bati3D_v2':
183
+ lst = get_list_from_STAC_swisstopo(url, *bbox_wgs84)
184
+ lst = suppr_doublons_bati3D_v2(lst)
185
+ elif data_type == 'bati3D_v3':
186
+ lst = get_list_from_STAC_swisstopo(url, *bbox_wgs84, gdb=True)
187
+ lst = suppr_doublons_bati3D_v3(lst)
188
+ urls.extend(lst)
189
+ return urls
190
 
191
  def geojson_forest(bbox, fn_geojson):
192
  xmin, ymin, xmax, ymax = bbox
 
210
  url = url_base + query_string
211
 
212
  with urllib.request.urlopen(url) as response:
213
+ data = json.load(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ with open(fn_geojson, 'w') as f:
216
+ json.dump(data, f)
 
 
 
217
 
218
  def create_geojson_with_links(urls, bbox):
 
219
  features = []
220
  for url in urls:
221
  feature = {
 
226
  },
227
  "properties": {
228
  "url": url,
229
+ "type": url.split('/')[-2].split('_')[0]
230
  }
231
  }
232
  features.append(feature)
 
238
  return json.dumps(geojson)
239
 
240
  def merge_ortho_images(urls, output_format='GTiff'):
 
241
  with tempfile.TemporaryDirectory() as temp_dir:
242
  local_files = []
243
  for i, url in enumerate(urls):
 
264
  return f.read()
265
 
266
  def prepare_download_package(urls, bbox, ortho_format):
 
267
  geojson_data = create_geojson_with_links(urls, bbox)
268
  ortho_urls = [url for url in urls if 'swissimage-dop10' in url]
269
  ortho_data = merge_ortho_images(ortho_urls, ortho_format) if ortho_urls else None
 
281
 
282
  st.title("Swiss Geospatial Data Downloader")
283
 
284
+ # Sidebar for data selection
285
+ st.sidebar.header("Data Selection")
286
+ data_types = {
287
+ 'mnt': st.sidebar.checkbox("Digital Terrain Model (MNT)", value=True),
288
+ 'mns': st.sidebar.checkbox("Digital Surface Model (MNS)", value=True),
289
+ 'bati3D_v2': st.sidebar.checkbox("3D Buildings v2", value=True),
290
+ 'bati3D_v3': st.sidebar.checkbox("3D Buildings v3", value=True),
291
+ 'ortho': st.sidebar.checkbox("Orthophotos", value=True),
292
+ }
293
+
294
+ resolutions = {
295
+ 'mnt': st.sidebar.selectbox("MNT Resolution", [0.5, 2.0], index=0),
296
+ 'ortho': st.sidebar.selectbox("Orthophoto Resolution", [0.1, 2.0], index=0),
297
+ }
298
+
299
+ ortho_format = st.sidebar.selectbox("Ortho Output Format", ['GTiff', 'JPEG', 'PNG'], index=0)
300
+
301
+ # Main content area
302
+ st.subheader("Enter Bounding Box Coordinates")
303
+ col1, col2, col3, col4 = st.columns(4)
304
+ with col1:
305
+ xmin = st.number_input("Min Longitude", value=6.0500, step=0.0001, format="%.4f")
306
+ with col2:
307
+ ymin = st.number_input("Min Latitude", value=46.1800, step=0.0001, format="%.4f")
308
+ with col3:
309
+ xmax = st.number_input("Max Longitude", value=6.2200, step=0.0001, format="%.4f")
310
+ with col4:
311
+ ymax = st.number_input("Max Latitude", value=46.2500, step=0.0001, format="%.4f")
312
+
313
+ if 'bbox' not in st.session_state:
314
+ st.session_state.bbox = None
315
+
316
+ if st.button("Set Bounding Box"):
317
+ st.session_state.bbox = [xmin, ymin, xmax, ymax]
318
 
319
  if st.session_state.bbox:
320
  st.write(f"Selected bounding box (WGS84): {st.session_state.bbox}")