Update pages/12_🌲_VertXtractor.py
Browse files- pages/12_🌲_VertXtractor.py +162 -74
pages/12_🌲_VertXtractor.py
CHANGED
@@ -2,28 +2,33 @@ import streamlit as st
|
|
2 |
import geopandas as gpd
|
3 |
import folium
|
4 |
from streamlit_folium import folium_static
|
5 |
-
from
|
|
|
|
|
6 |
import requests
|
7 |
import json
|
8 |
from io import BytesIO
|
9 |
-
import zipfile
|
10 |
from PIL import Image
|
11 |
import numpy as np
|
|
|
|
|
12 |
|
13 |
-
# Liste des couches de données Swisstopo
|
14 |
LAYERS = {
|
15 |
-
"Bâtiments": "ch.swisstopo.vec25-gebaeude",
|
16 |
-
"Bâtiments 3D": "ch.swisstopo.swissbuildings3d_2",
|
17 |
-
"RĂ©seau hydrographique": "ch.swisstopo.vec25-gewaessernetz",
|
18 |
-
"RĂ©seau routier": "ch.swisstopo.vec25-strassen",
|
19 |
-
"Couverture du sol": "ch.swisstopo.vec25-landesbedeckung",
|
20 |
-
"Limites administratives": "ch.swisstopo.swissboundaries3d",
|
21 |
-
"Modèle numérique de terrain": "ch.swisstopo.swissalti3d",
|
22 |
-
"Carte nationale 1:25'000": "ch.swisstopo.pixelkarte-farbe-pk25.noscale",
|
23 |
-
"Carte nationale 1:50'000": "ch.swisstopo.pixelkarte-farbe-pk50.noscale",
|
24 |
-
"Carte nationale 1:100'000": "ch.swisstopo.pixelkarte-farbe-pk100.noscale",
|
25 |
-
"Orthophoto SWISSIMAGE": "ch.swisstopo.swissimage-product",
|
26 |
-
"Noms géographiques": "ch.swisstopo.swissnames3d",
|
|
|
|
|
27 |
}
|
28 |
|
29 |
def extract_swisstopo_data(min_x, min_y, max_x, max_y, layer):
|
@@ -55,7 +60,6 @@ def download_swissimage(min_x, min_y, max_x, max_y):
|
|
55 |
|
56 |
params = {
|
57 |
"bbox": f"{min_x},{min_y},{max_x},{max_y}",
|
58 |
-
"datetime": "2022-01-01T00:00:00Z/..",
|
59 |
"limit": 1
|
60 |
}
|
61 |
|
@@ -68,69 +72,153 @@ def download_swissimage(min_x, min_y, max_x, max_y):
|
|
68 |
image_response = requests.get(asset_url)
|
69 |
image_response.raise_for_status()
|
70 |
|
71 |
-
with
|
72 |
-
|
73 |
-
|
74 |
-
image = Image.
|
75 |
-
return
|
76 |
return None
|
77 |
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
-
|
89 |
-
|
90 |
-
with col1:
|
91 |
-
min_x = st.number_input("Coordonnée X minimale", value=2600000)
|
92 |
-
min_y = st.number_input("Coordonnée Y minimale", value=1200000)
|
93 |
-
with col2:
|
94 |
-
max_x = st.number_input("Coordonnée X maximale", value=2601000)
|
95 |
-
max_y = st.number_input("Coordonnée Y maximale", value=1201000)
|
96 |
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
119 |
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
131 |
|
132 |
-
|
133 |
-
|
134 |
-
st.sidebar.markdown("Cet outil permet d'extraire des données géospatiales de Swisstopo.")
|
135 |
-
st.sidebar.markdown("Développé par Vertdure")
|
136 |
-
st.sidebar.markdown("[Code source](https://huggingface.co/spaces/Vertdure/Streamlit/tree/main)")
|
|
|
2 |
import geopandas as gpd
|
3 |
import folium
|
4 |
from streamlit_folium import folium_static
|
5 |
+
from folium.plugins import Draw
|
6 |
+
from streamlit_folium import st_folium
|
7 |
+
from shapely.geometry import box, shape
|
8 |
import requests
|
9 |
import json
|
10 |
from io import BytesIO
|
|
|
11 |
from PIL import Image
|
12 |
import numpy as np
|
13 |
+
import rasterio
|
14 |
+
from rasterio.io import MemoryFile
|
15 |
|
16 |
+
# Liste des couches de données Swisstopo et ESRI
|
17 |
LAYERS = {
|
18 |
+
"Swisstopo - Bâtiments": "ch.swisstopo.vec25-gebaeude",
|
19 |
+
"Swisstopo - Bâtiments 3D": "ch.swisstopo.swissbuildings3d_2",
|
20 |
+
"Swisstopo - RĂ©seau hydrographique": "ch.swisstopo.vec25-gewaessernetz",
|
21 |
+
"Swisstopo - RĂ©seau routier": "ch.swisstopo.vec25-strassen",
|
22 |
+
"Swisstopo - Couverture du sol": "ch.swisstopo.vec25-landesbedeckung",
|
23 |
+
"Swisstopo - Limites administratives": "ch.swisstopo.swissboundaries3d",
|
24 |
+
"Swisstopo - Modèle numérique de terrain": "ch.swisstopo.swissalti3d",
|
25 |
+
"Swisstopo - Carte nationale 1:25'000": "ch.swisstopo.pixelkarte-farbe-pk25.noscale",
|
26 |
+
"Swisstopo - Carte nationale 1:50'000": "ch.swisstopo.pixelkarte-farbe-pk50.noscale",
|
27 |
+
"Swisstopo - Carte nationale 1:100'000": "ch.swisstopo.pixelkarte-farbe-pk100.noscale",
|
28 |
+
"Swisstopo - Orthophoto SWISSIMAGE": "ch.swisstopo.swissimage-product",
|
29 |
+
"Swisstopo - Noms géographiques": "ch.swisstopo.swissnames3d",
|
30 |
+
"ESRI - World Imagery": "esri_world_imagery",
|
31 |
+
"ESRI - World Elevation": "esri_world_elevation",
|
32 |
}
|
33 |
|
34 |
def extract_swisstopo_data(min_x, min_y, max_x, max_y, layer):
|
|
|
60 |
|
61 |
params = {
|
62 |
"bbox": f"{min_x},{min_y},{max_x},{max_y}",
|
|
|
63 |
"limit": 1
|
64 |
}
|
65 |
|
|
|
72 |
image_response = requests.get(asset_url)
|
73 |
image_response.raise_for_status()
|
74 |
|
75 |
+
with MemoryFile(image_response.content) as memfile:
|
76 |
+
with memfile.open() as dataset:
|
77 |
+
image_array = dataset.read()
|
78 |
+
image = Image.fromarray(np.transpose(image_array, (1, 2, 0)))
|
79 |
+
return image
|
80 |
return None
|
81 |
|
82 |
+
def download_esri_imagery(min_x, min_y, max_x, max_y):
|
83 |
+
api_url = "https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/export"
|
84 |
+
params = {
|
85 |
+
"bbox": f"{min_x},{min_y},{max_x},{max_y}",
|
86 |
+
"bboxSR": "2056",
|
87 |
+
"size": "1000,1000",
|
88 |
+
"format": "png",
|
89 |
+
"f": "image"
|
90 |
+
}
|
91 |
+
response = requests.get(api_url, params=params)
|
92 |
+
response.raise_for_status()
|
93 |
+
return Image.open(BytesIO(response.content))
|
94 |
|
95 |
+
def download_esri_elevation(min_x, min_y, max_x, max_y):
|
96 |
+
api_url = "https://elevation.arcgis.com/arcgis/rest/services/WorldElevation/Terrain/ImageServer/exportImage"
|
97 |
+
params = {
|
98 |
+
"bbox": f"{min_x},{min_y},{max_x},{max_y}",
|
99 |
+
"bboxSR": "2056",
|
100 |
+
"size": "1000,1000",
|
101 |
+
"format": "tiff",
|
102 |
+
"pixelType": "F32",
|
103 |
+
"noDataInterpretation": "esriNoDataMatchAny",
|
104 |
+
"interpolation": "RSP_BilinearInterpolation",
|
105 |
+
"f": "image"
|
106 |
+
}
|
107 |
+
response = requests.get(api_url, params=params)
|
108 |
+
response.raise_for_status()
|
109 |
+
return Image.open(BytesIO(response.content))
|
110 |
|
111 |
+
def main():
|
112 |
+
st.title("Extracteur de données Swisstopo et ESRI")
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
+
# Sélection de la méthode d'entrée
|
115 |
+
input_method = st.radio("Choisissez la méthode d'entrée",
|
116 |
+
["Coordonnées manuelles", "Charger un GeoJSON", "Dessiner sur la carte"])
|
117 |
+
|
118 |
+
if input_method == "Coordonnées manuelles":
|
119 |
+
col1, col2 = st.columns(2)
|
120 |
+
with col1:
|
121 |
+
min_x = st.number_input("Coordonnée X minimale", value=2600000)
|
122 |
+
min_y = st.number_input("Coordonnée Y minimale", value=1200000)
|
123 |
+
with col2:
|
124 |
+
max_x = st.number_input("Coordonnée X maximale", value=2601000)
|
125 |
+
max_y = st.number_input("Coordonnée Y maximale", value=1201000)
|
126 |
+
bbox = box(min_x, min_y, max_x, max_y)
|
127 |
+
|
128 |
+
elif input_method == "Charger un GeoJSON":
|
129 |
+
uploaded_file = st.file_uploader("Choisissez un fichier GeoJSON", type="geojson")
|
130 |
+
if uploaded_file is not None:
|
131 |
+
gdf = gpd.read_file(uploaded_file)
|
132 |
+
bbox = gdf.total_bounds
|
133 |
+
min_x, min_y, max_x, max_y = bbox
|
134 |
+
|
135 |
+
elif input_method == "Dessiner sur la carte":
|
136 |
+
m = folium.Map(location=[46.8, 8.2], zoom_start=8)
|
137 |
+
Draw(export=True).add_to(m)
|
138 |
+
output = st_folium(m, width=700, height=500)
|
139 |
|
140 |
+
if output["last_active_drawing"]:
|
141 |
+
geom = shape(output["last_active_drawing"]["geometry"])
|
142 |
+
bbox = geom.bounds
|
143 |
+
min_x, min_y, max_x, max_y = bbox
|
144 |
+
|
145 |
+
# SĂ©lection de la couche
|
146 |
+
selected_layer_name = st.selectbox("Sélectionnez la couche de données", list(LAYERS.keys()))
|
147 |
+
layer = LAYERS[selected_layer_name]
|
148 |
+
|
149 |
+
if st.button("Extraire les données"):
|
150 |
+
try:
|
151 |
+
if "Swisstopo" in selected_layer_name:
|
152 |
+
if layer == "ch.swisstopo.swissimage-product":
|
153 |
+
st.subheader("Image SWISSIMAGE")
|
154 |
+
image = download_swissimage(min_x, min_y, max_x, max_y)
|
155 |
+
if image is not None:
|
156 |
+
st.image(image, caption="Image SWISSIMAGE", use_column_width=True)
|
157 |
+
|
158 |
+
buf = BytesIO()
|
159 |
+
image.save(buf, format="PNG")
|
160 |
+
st.download_button(
|
161 |
+
label="Télécharger l'image SWISSIMAGE (PNG)",
|
162 |
+
data=buf.getvalue(),
|
163 |
+
file_name="swissimage.png",
|
164 |
+
mime="image/png"
|
165 |
+
)
|
166 |
+
else:
|
167 |
+
st.warning("Aucune image SWISSIMAGE disponible pour cette zone.")
|
168 |
+
else:
|
169 |
+
gdf = extract_swisstopo_data(min_x, min_y, max_x, max_y, layer)
|
170 |
+
|
171 |
+
st.subheader("Aperçu des données")
|
172 |
+
st.write(gdf.head())
|
173 |
+
|
174 |
+
st.subheader("Visualisation des données")
|
175 |
+
m = folium.Map(location=[gdf.geometry.centroid.y.mean(), gdf.geometry.centroid.x.mean()], zoom_start=14)
|
176 |
+
folium.GeoJson(gdf).add_to(m)
|
177 |
+
folium_static(m)
|
178 |
+
|
179 |
+
st.download_button(
|
180 |
+
label="Télécharger les données (GeoJSON)",
|
181 |
+
data=gdf.to_json(),
|
182 |
+
file_name=f"{selected_layer_name.lower().replace(' ', '_')}.geojson",
|
183 |
+
mime="application/json"
|
184 |
+
)
|
185 |
+
|
186 |
+
elif "ESRI" in selected_layer_name:
|
187 |
+
if layer == "esri_world_imagery":
|
188 |
+
st.subheader("Image ESRI World Imagery")
|
189 |
+
image = download_esri_imagery(min_x, min_y, max_x, max_y)
|
190 |
+
st.image(image, caption="ESRI World Imagery", use_column_width=True)
|
191 |
+
|
192 |
+
buf = BytesIO()
|
193 |
+
image.save(buf, format="PNG")
|
194 |
+
st.download_button(
|
195 |
+
label="Télécharger l'image (PNG)",
|
196 |
+
data=buf.getvalue(),
|
197 |
+
file_name="esri_world_imagery.png",
|
198 |
+
mime="image/png"
|
199 |
+
)
|
200 |
+
|
201 |
+
elif layer == "esri_world_elevation":
|
202 |
+
st.subheader("Modèle numérique de terrain ESRI")
|
203 |
+
image = download_esri_elevation(min_x, min_y, max_x, max_y)
|
204 |
+
st.image(image, caption="ESRI World Elevation", use_column_width=True, clamp=True)
|
205 |
+
|
206 |
+
buf = BytesIO()
|
207 |
+
image.save(buf, format="TIFF")
|
208 |
+
st.download_button(
|
209 |
+
label="Télécharger le MNT (TIFF)",
|
210 |
+
data=buf.getvalue(),
|
211 |
+
file_name="esri_world_elevation.tiff",
|
212 |
+
mime="image/tiff"
|
213 |
+
)
|
214 |
|
215 |
+
except Exception as e:
|
216 |
+
st.error(f"Une erreur s'est produite : {str(e)}")
|
217 |
+
|
218 |
+
st.sidebar.markdown("## À propos")
|
219 |
+
st.sidebar.markdown("Cet outil permet d'extraire des données géospatiales de Swisstopo et ESRI.")
|
220 |
+
st.sidebar.markdown("Développé par Vertdure")
|
221 |
+
st.sidebar.markdown("[Code source](https://huggingface.co/spaces/Vertdure/Streamlit/tree/main)")
|
222 |
|
223 |
+
if __name__ == "__main__":
|
224 |
+
main()
|
|
|
|
|
|