Update pages/5_đ_VertXtractor.py
Browse files- pages/5_đ_VertXtractor.py +181 -162
pages/5_đ_VertXtractor.py
CHANGED
@@ -1,179 +1,198 @@
|
|
1 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import folium
|
3 |
from streamlit_folium import folium_static
|
4 |
-
from folium.plugins import Draw
|
5 |
-
import requests
|
6 |
-
import json
|
7 |
-
import os
|
8 |
-
import urllib.request
|
9 |
-
import csv
|
10 |
-
from osgeo import gdal
|
11 |
-
import numpy as np
|
12 |
-
import math
|
13 |
-
import shutil
|
14 |
-
import sys
|
15 |
-
import glob
|
16 |
|
17 |
-
# Configuration
|
18 |
-
|
|
|
19 |
|
20 |
-
#
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
'Höhenmodell': 'ch.swisstopo.swissalti3d',
|
29 |
-
}
|
30 |
|
31 |
-
#
|
32 |
-
def
|
33 |
-
"""
|
34 |
-
|
35 |
-
lon_deg = tile_x / n * 360.0 - 180.0
|
36 |
-
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * tile_y / n)))
|
37 |
-
lat_deg = math.degrees(lat_rad)
|
38 |
-
return lat_deg, lon_deg
|
39 |
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
items_result = json.loads(response.content)
|
45 |
-
assets = items_result.get('features', [])
|
46 |
-
|
47 |
-
items_files = []
|
48 |
-
for asset in assets:
|
49 |
-
asset_url = asset.get('assets', {}).get('data', {}).get('href')
|
50 |
-
if asset_url:
|
51 |
-
items_files.append(asset_url)
|
52 |
-
|
53 |
-
# Filtrage des fichiers selon les cas spécifiques
|
54 |
-
if "_krel_" in productname:
|
55 |
-
items_files = [i for i in items_files if "_krel_" in i]
|
56 |
-
|
57 |
-
if "0.1" in productname:
|
58 |
-
items_files = [i for i in items_files if "_0.1_" in i]
|
59 |
-
|
60 |
-
if productname == 'ch.swisstopo.swissalti3d':
|
61 |
-
items_files = [i for i in items_files if i.endswith(".tif")]
|
62 |
-
items_files = [i for i in items_files if "_0.5_" in i]
|
63 |
-
|
64 |
-
return items_files
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
79 |
|
80 |
-
def
|
81 |
-
"""
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
# Fusionner les autres fichiers
|
98 |
-
for file in input_files[1:]:
|
99 |
-
gdal.Warp(output_file, file, format="GTiff", options=[f"COMPRESS={compress_method}"])
|
100 |
-
|
101 |
-
# Fermer le fichier de sortie
|
102 |
-
dst_ds = None
|
103 |
-
|
104 |
-
def process_csv(csv_filepath, no_merge=False):
|
105 |
-
"""Traite le fichier CSV pour télécharger et fusionner les données."""
|
106 |
-
download_dir = os.path.dirname(csv_filepath)
|
107 |
-
order_name = os.path.basename(csv_filepath).split('.')[0]
|
108 |
-
|
109 |
-
with open(csv_filepath, 'r') as file:
|
110 |
-
urls = file.readlines()
|
111 |
-
|
112 |
-
downloaded_files = []
|
113 |
-
for i, url in enumerate(urls):
|
114 |
-
url = url.strip()
|
115 |
-
filename = os.path.join(download_dir, url.split('/')[-1])
|
116 |
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
st.
|
130 |
-
|
131 |
-
return output_file if not no_merge else download_dir
|
132 |
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
m = folium.Map(location=[46.8182, 8.2275], zoom_start=8)
|
143 |
-
draw = Draw(
|
144 |
-
draw_options={
|
145 |
-
'rectangle': True,
|
146 |
-
'polygon': False,
|
147 |
-
'polyline': False,
|
148 |
-
'circle': False,
|
149 |
-
'marker': False,
|
150 |
-
'circlemarker': False,
|
151 |
-
},
|
152 |
-
edit_options={'edit': False}
|
153 |
-
)
|
154 |
-
draw.add_to(m)
|
155 |
-
|
156 |
-
output = folium_static(m)
|
157 |
|
158 |
-
#
|
159 |
-
|
160 |
-
|
161 |
-
bbox = st.session_state.last_active_drawing['geometry']['coordinates'][0]
|
162 |
-
lons = [coord[0] for coord in bbox]
|
163 |
-
lats = [coord[1] for coord in bbox]
|
164 |
-
bbox_coords = [min(lons), min(lats), max(lons), max(lats)]
|
165 |
-
st.write(f"Coordonnées de la bbox : {bbox_coords}")
|
166 |
|
167 |
-
#
|
168 |
-
|
169 |
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
st.
|
175 |
-
|
176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
|
178 |
-
|
179 |
-
|
|
|
1 |
import streamlit as st
|
2 |
+
import geopandas as gpd
|
3 |
+
import rasterio
|
4 |
+
import numpy as np
|
5 |
+
from pyproj import Transformer
|
6 |
+
import trimesh
|
7 |
+
import logging
|
8 |
+
from io import BytesIO
|
9 |
import folium
|
10 |
from streamlit_folium import folium_static
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
+
# Configuration du logging
|
13 |
+
logging.basicConfig(level=logging.INFO)
|
14 |
+
logger = logging.getLogger(__name__)
|
15 |
|
16 |
+
# Fonction pour charger le MNT de la Suisse (à implémenter)
|
17 |
+
def load_swiss_dem():
|
18 |
+
"""
|
19 |
+
Charge le MNT de la Suisse. Cette fonction devrait ĂȘtre implĂ©mentĂ©e pour charger
|
20 |
+
le MNT complet de la Suisse à partir d'une source de données appropriée.
|
21 |
+
"""
|
22 |
+
# Placeholder - à remplacer par le chargement réel du MNT suisse
|
23 |
+
return None, None
|
|
|
|
|
24 |
|
25 |
+
# Fonction pour extraire une partie du MNT
|
26 |
+
def extract_dem_region(dem, transform, bbox):
|
27 |
+
"""
|
28 |
+
Extrait une région spécifique du MNT basée sur une boßte englobante.
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
Args:
|
31 |
+
dem (numpy.array): Données du MNT complet
|
32 |
+
transform (affine.Affine): Transformation géospatiale
|
33 |
+
bbox (tuple): BoĂźte englobante (minx, miny, maxx, maxy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
Returns:
|
36 |
+
tuple: (données du MNT extraites, nouvelle transformation)
|
37 |
+
"""
|
38 |
+
# Conversion des coordonnées de la bbox en indices de pixels
|
39 |
+
minx, miny, maxx, maxy = bbox
|
40 |
+
rows, cols = rasterio.transform.rowcol(transform, [minx, maxx], [miny, maxy])
|
41 |
+
|
42 |
+
# Extraction de la région
|
43 |
+
window = ((rows[0], rows[1]), (cols[0], cols[1]))
|
44 |
+
dem_region = dem[window[0][0]:window[0][1], window[1][0]:window[1][1]]
|
45 |
+
|
46 |
+
# Calcul de la nouvelle transformation
|
47 |
+
new_transform = rasterio.transform.from_bounds(minx, miny, maxx, maxy,
|
48 |
+
dem_region.shape[1], dem_region.shape[0])
|
49 |
+
|
50 |
+
return dem_region, new_transform
|
51 |
|
52 |
+
def create_mesh(dem, transform, resolution):
|
53 |
+
"""
|
54 |
+
Crée un maillage 3D à partir des données du MNT.
|
55 |
+
|
56 |
+
Args:
|
57 |
+
dem (numpy.array): Données du MNT
|
58 |
+
transform (affine.Affine): Transformation géospatiale
|
59 |
+
resolution (int): RĂ©solution du maillage
|
60 |
+
|
61 |
+
Returns:
|
62 |
+
trimesh.Trimesh: Maillage 3D
|
63 |
+
"""
|
64 |
+
try:
|
65 |
+
height, width = dem.shape
|
66 |
+
x, y = np.meshgrid(np.arange(width), np.arange(height))
|
67 |
+
lon, lat = rasterio.transform.xy(transform, y, x)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
+
vertices = np.column_stack((lon.flatten(), lat.flatten(), dem.flatten()))
|
70 |
+
faces = []
|
71 |
+
for i in range(height - 1):
|
72 |
+
for j in range(width - 1):
|
73 |
+
idx = i * width + j
|
74 |
+
faces.append([idx, idx + 1, idx + width])
|
75 |
+
faces.append([idx + 1, idx + width + 1, idx + width])
|
76 |
|
77 |
+
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
|
78 |
+
|
79 |
+
target_faces = (height * width) // (resolution ** 2)
|
80 |
+
mesh = mesh.simplify_quadric_decimation(target_faces)
|
81 |
+
|
82 |
+
return mesh
|
83 |
+
except Exception as e:
|
84 |
+
logger.error(f"Erreur lors de la création du maillage: {str(e)}")
|
85 |
+
st.error("Erreur lors de la création du maillage. Veuillez vérifier les paramÚtres.")
|
86 |
+
return None
|
|
|
87 |
|
88 |
+
def visualize_mesh(mesh):
|
89 |
+
"""
|
90 |
+
Crée une visualisation du maillage.
|
91 |
+
|
92 |
+
Args:
|
93 |
+
mesh (trimesh.Trimesh): Maillage 3D Ă visualiser
|
94 |
+
|
95 |
+
Returns:
|
96 |
+
matplotlib.figure.Figure: Figure contenant la visualisation du maillage
|
97 |
+
"""
|
98 |
+
import matplotlib.pyplot as plt
|
99 |
+
from mpl_toolkits.mplot3d import Axes3D
|
100 |
+
|
101 |
+
fig = plt.figure()
|
102 |
+
ax = fig.add_subplot(111, projection='3d')
|
103 |
+
ax.plot_trisurf(mesh.vertices[:, 0], mesh.vertices[:, 1], mesh.vertices[:, 2],
|
104 |
+
triangles=mesh.faces, cmap='viridis')
|
105 |
+
ax.set_xlabel('X')
|
106 |
+
ax.set_ylabel('Y')
|
107 |
+
ax.set_zlabel('Z')
|
108 |
+
return fig
|
109 |
+
|
110 |
+
def export_for_blender(mesh):
|
111 |
+
"""
|
112 |
+
Exporte le maillage dans un format compatible avec Blender.
|
113 |
+
|
114 |
+
Args:
|
115 |
+
mesh (trimesh.Trimesh): Maillage 3D Ă exporter
|
116 |
+
|
117 |
+
Returns:
|
118 |
+
bytes: Contenu du fichier d'export
|
119 |
+
"""
|
120 |
+
try:
|
121 |
+
obj_file = BytesIO()
|
122 |
+
mesh.export(obj_file, file_type='obj')
|
123 |
+
return obj_file.getvalue()
|
124 |
+
except Exception as e:
|
125 |
+
logger.error(f"Erreur lors de l'export pour Blender: {str(e)}")
|
126 |
+
st.error("Erreur lors de l'export pour Blender. Veuillez réessayer.")
|
127 |
+
return None
|
128 |
+
|
129 |
+
# Interface Streamlit
|
130 |
+
st.title("Mesh Tiler CH - Streamlit Edition")
|
131 |
+
st.write("Sélectionnez une zone en Suisse pour créer un maillage 3D.")
|
132 |
+
|
133 |
+
# Chargement du MNT complet de la Suisse
|
134 |
+
swiss_dem, swiss_transform = load_swiss_dem()
|
135 |
+
|
136 |
+
# Sélection de la méthode d'entrée
|
137 |
+
input_method = st.radio("Choisissez la méthode de sélection de la zone :",
|
138 |
+
("Dessiner sur la carte", "Entrer les coordonnées"))
|
139 |
+
|
140 |
+
if input_method == "Dessiner sur la carte":
|
141 |
+
# Créer une carte centrée sur la Suisse
|
142 |
m = folium.Map(location=[46.8182, 8.2275], zoom_start=8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
+
# Ajouter un outil de dessin de rectangle
|
145 |
+
draw = folium.plugins.Draw(export=True)
|
146 |
+
draw.add_to(m)
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
+
# Afficher la carte
|
149 |
+
folium_static(m)
|
150 |
|
151 |
+
# Récupérer les coordonnées du rectangle dessiné
|
152 |
+
if st.button("Créer le maillage à partir de la sélection"):
|
153 |
+
# Ici, vous devriez récupérer les coordonnées du rectangle dessiné
|
154 |
+
# Cette partie nécessite une implémentation plus avancée avec JavaScript
|
155 |
+
st.write("Fonctionnalité en cours de développement.")
|
156 |
+
|
157 |
+
else:
|
158 |
+
# Entrer les coordonnées manuellement
|
159 |
+
col1, col2 = st.columns(2)
|
160 |
+
with col1:
|
161 |
+
minx = st.number_input("Min X", value=7.0)
|
162 |
+
miny = st.number_input("Min Y", value=46.0)
|
163 |
+
with col2:
|
164 |
+
maxx = st.number_input("Max X", value=8.0)
|
165 |
+
maxy = st.number_input("Max Y", value=47.0)
|
166 |
+
|
167 |
+
if st.button("Créer le maillage"):
|
168 |
+
bbox = (minx, miny, maxx, maxy)
|
169 |
+
dem_region, region_transform = extract_dem_region(swiss_dem, swiss_transform, bbox)
|
170 |
+
|
171 |
+
resolution = st.slider("RĂ©solution du maillage", min_value=1, max_value=100, value=10)
|
172 |
+
mesh = create_mesh(dem_region, region_transform, resolution)
|
173 |
+
|
174 |
+
if mesh is not None:
|
175 |
+
fig = visualize_mesh(mesh)
|
176 |
+
st.pyplot(fig)
|
177 |
+
|
178 |
+
if st.button("Exporter pour Blender"):
|
179 |
+
blender_file = export_for_blender(mesh)
|
180 |
+
if blender_file is not None:
|
181 |
+
st.download_button("Télécharger le fichier Blender (.obj)",
|
182 |
+
blender_file,
|
183 |
+
file_name="mesh_export.obj",
|
184 |
+
mime="application/octet-stream")
|
185 |
+
|
186 |
+
# Guide d'utilisation simplifié
|
187 |
+
st.markdown("""
|
188 |
+
## Guide d'utilisation
|
189 |
+
|
190 |
+
1. Choisissez entre dessiner sur la carte ou entrer les coordonnées manuellement.
|
191 |
+
2. SĂ©lectionnez la zone d'intĂ©rĂȘt en Suisse.
|
192 |
+
3. Cliquez sur "Créer le maillage" pour générer le modÚle 3D.
|
193 |
+
4. Ajustez la résolution du maillage si nécessaire.
|
194 |
+
5. Visualisez le maillage 3D généré.
|
195 |
+
6. Exportez le maillage au format OBJ pour Blender si souhaité.
|
196 |
|
197 |
+
Pour toute question ou problÚme, n'hésitez pas à contacter le support technique.
|
198 |
+
""")
|