|
import streamlit as st |
|
import geopandas as gpd |
|
import rasterio |
|
import numpy as np |
|
from pyproj import Transformer |
|
import trimesh |
|
import logging |
|
from io import BytesIO |
|
import folium |
|
from streamlit_folium import folium_static |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
def load_swiss_dem(): |
|
""" |
|
Charge le MNT de la Suisse. Cette fonction devrait ĂȘtre implĂ©mentĂ©e pour charger |
|
le MNT complet de la Suisse à partir d'une source de données appropriée. |
|
""" |
|
|
|
return None, None |
|
|
|
|
|
def extract_dem_region(dem, transform, bbox): |
|
""" |
|
Extrait une région spécifique du MNT basée sur une boßte englobante. |
|
|
|
Args: |
|
dem (numpy.array): Données du MNT complet |
|
transform (affine.Affine): Transformation géospatiale |
|
bbox (tuple): BoĂźte englobante (minx, miny, maxx, maxy) |
|
|
|
Returns: |
|
tuple: (données du MNT extraites, nouvelle transformation) |
|
""" |
|
|
|
minx, miny, maxx, maxy = bbox |
|
rows, cols = rasterio.transform.rowcol(transform, [minx, maxx], [miny, maxy]) |
|
|
|
|
|
window = ((rows[0], rows[1]), (cols[0], cols[1])) |
|
dem_region = dem[window[0][0]:window[0][1], window[1][0]:window[1][1]] |
|
|
|
|
|
new_transform = rasterio.transform.from_bounds(minx, miny, maxx, maxy, |
|
dem_region.shape[1], dem_region.shape[0]) |
|
|
|
return dem_region, new_transform |
|
|
|
def create_mesh(dem, transform, resolution): |
|
""" |
|
Crée un maillage 3D à partir des données du MNT. |
|
|
|
Args: |
|
dem (numpy.array): Données du MNT |
|
transform (affine.Affine): Transformation géospatiale |
|
resolution (int): RĂ©solution du maillage |
|
|
|
Returns: |
|
trimesh.Trimesh: Maillage 3D |
|
""" |
|
try: |
|
height, width = dem.shape |
|
x, y = np.meshgrid(np.arange(width), np.arange(height)) |
|
lon, lat = rasterio.transform.xy(transform, y, x) |
|
|
|
vertices = np.column_stack((lon.flatten(), lat.flatten(), dem.flatten())) |
|
faces = [] |
|
for i in range(height - 1): |
|
for j in range(width - 1): |
|
idx = i * width + j |
|
faces.append([idx, idx + 1, idx + width]) |
|
faces.append([idx + 1, idx + width + 1, idx + width]) |
|
|
|
mesh = trimesh.Trimesh(vertices=vertices, faces=faces) |
|
|
|
target_faces = (height * width) // (resolution ** 2) |
|
mesh = mesh.simplify_quadric_decimation(target_faces) |
|
|
|
return mesh |
|
except Exception as e: |
|
logger.error(f"Erreur lors de la création du maillage: {str(e)}") |
|
st.error("Erreur lors de la création du maillage. Veuillez vérifier les paramÚtres.") |
|
return None |
|
|
|
def visualize_mesh(mesh): |
|
""" |
|
Crée une visualisation du maillage. |
|
|
|
Args: |
|
mesh (trimesh.Trimesh): Maillage 3D Ă visualiser |
|
|
|
Returns: |
|
matplotlib.figure.Figure: Figure contenant la visualisation du maillage |
|
""" |
|
import matplotlib.pyplot as plt |
|
from mpl_toolkits.mplot3d import Axes3D |
|
|
|
fig = plt.figure() |
|
ax = fig.add_subplot(111, projection='3d') |
|
ax.plot_trisurf(mesh.vertices[:, 0], mesh.vertices[:, 1], mesh.vertices[:, 2], |
|
triangles=mesh.faces, cmap='viridis') |
|
ax.set_xlabel('X') |
|
ax.set_ylabel('Y') |
|
ax.set_zlabel('Z') |
|
return fig |
|
|
|
def export_for_blender(mesh): |
|
""" |
|
Exporte le maillage dans un format compatible avec Blender. |
|
|
|
Args: |
|
mesh (trimesh.Trimesh): Maillage 3D Ă exporter |
|
|
|
Returns: |
|
bytes: Contenu du fichier d'export |
|
""" |
|
try: |
|
obj_file = BytesIO() |
|
mesh.export(obj_file, file_type='obj') |
|
return obj_file.getvalue() |
|
except Exception as e: |
|
logger.error(f"Erreur lors de l'export pour Blender: {str(e)}") |
|
st.error("Erreur lors de l'export pour Blender. Veuillez réessayer.") |
|
return None |
|
|
|
|
|
st.title("Mesh Tiler CH - Streamlit Edition") |
|
st.write("Sélectionnez une zone en Suisse pour créer un maillage 3D.") |
|
|
|
|
|
swiss_dem, swiss_transform = load_swiss_dem() |
|
|
|
|
|
input_method = st.radio("Choisissez la méthode de sélection de la zone :", |
|
("Dessiner sur la carte", "Entrer les coordonnées")) |
|
|
|
if input_method == "Dessiner sur la carte": |
|
|
|
m = folium.Map(location=[46.8182, 8.2275], zoom_start=8) |
|
|
|
|
|
draw = folium.plugins.Draw(export=True) |
|
draw.add_to(m) |
|
|
|
|
|
folium_static(m) |
|
|
|
|
|
if st.button("Créer le maillage à partir de la sélection"): |
|
|
|
|
|
st.write("Fonctionnalité en cours de développement.") |
|
|
|
else: |
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
minx = st.number_input("Min X", value=7.0) |
|
miny = st.number_input("Min Y", value=46.0) |
|
with col2: |
|
maxx = st.number_input("Max X", value=8.0) |
|
maxy = st.number_input("Max Y", value=47.0) |
|
|
|
if st.button("Créer le maillage"): |
|
bbox = (minx, miny, maxx, maxy) |
|
dem_region, region_transform = extract_dem_region(swiss_dem, swiss_transform, bbox) |
|
|
|
resolution = st.slider("RĂ©solution du maillage", min_value=1, max_value=100, value=10) |
|
mesh = create_mesh(dem_region, region_transform, resolution) |
|
|
|
if mesh is not None: |
|
fig = visualize_mesh(mesh) |
|
st.pyplot(fig) |
|
|
|
if st.button("Exporter pour Blender"): |
|
blender_file = export_for_blender(mesh) |
|
if blender_file is not None: |
|
st.download_button("Télécharger le fichier Blender (.obj)", |
|
blender_file, |
|
file_name="mesh_export.obj", |
|
mime="application/octet-stream") |
|
|
|
|
|
st.markdown(""" |
|
## Guide d'utilisation |
|
|
|
1. Choisissez entre dessiner sur la carte ou entrer les coordonnées manuellement. |
|
2. SĂ©lectionnez la zone d'intĂ©rĂȘt en Suisse. |
|
3. Cliquez sur "Créer le maillage" pour générer le modÚle 3D. |
|
4. Ajustez la résolution du maillage si nécessaire. |
|
5. Visualisez le maillage 3D généré. |
|
6. Exportez le maillage au format OBJ pour Blender si souhaité. |
|
|
|
Pour toute question ou problÚme, n'hésitez pas à contacter le support technique. |
|
""") |