|
import streamlit as st |
|
import geopandas as gpd |
|
import folium |
|
from streamlit_folium import folium_static |
|
from shapely.geometry import box |
|
import requests |
|
import json |
|
from io import BytesIO |
|
import zipfile |
|
from PIL import Image |
|
import numpy as np |
|
|
|
|
|
LAYERS = { |
|
"Bâtiments": "ch.swisstopo.vec25-gebaeude", |
|
"Bâtiments 3D": "ch.swisstopo.swissbuildings3d_2", |
|
"RĂ©seau hydrographique": "ch.swisstopo.vec25-gewaessernetz", |
|
"RĂ©seau routier": "ch.swisstopo.vec25-strassen", |
|
"Couverture du sol": "ch.swisstopo.vec25-landesbedeckung", |
|
"Limites administratives": "ch.swisstopo.swissboundaries3d", |
|
"Modèle numérique de terrain": "ch.swisstopo.swissalti3d", |
|
"Carte nationale 1:25'000": "ch.swisstopo.pixelkarte-farbe-pk25.noscale", |
|
"Carte nationale 1:50'000": "ch.swisstopo.pixelkarte-farbe-pk50.noscale", |
|
"Carte nationale 1:100'000": "ch.swisstopo.pixelkarte-farbe-pk100.noscale", |
|
"Orthophoto SWISSIMAGE": "ch.swisstopo.swissimage-product", |
|
"Noms géographiques": "ch.swisstopo.swissnames3d", |
|
} |
|
|
|
def extract_swisstopo_data(min_x, min_y, max_x, max_y, layer): |
|
bbox = box(min_x, min_y, max_x, max_y) |
|
api_url = "https://api3.geo.admin.ch/rest/services/api/MapServer/identify" |
|
|
|
params = { |
|
"geometry": f"{bbox.bounds[0]},{bbox.bounds[1]},{bbox.bounds[2]},{bbox.bounds[3]}", |
|
"geometryType": "esriGeometryEnvelope", |
|
"layers": f"all:{layer}", |
|
"mapExtent": f"{bbox.bounds[0]},{bbox.bounds[1]},{bbox.bounds[2]},{bbox.bounds[3]}", |
|
"imageDisplay": "1000,1000,96", |
|
"tolerance": "0", |
|
"lang": "fr", |
|
"sr": "2056" |
|
} |
|
|
|
response = requests.get(api_url, params=params) |
|
response.raise_for_status() |
|
|
|
data = response.json() |
|
features = [feature["geometry"] for feature in data["results"]] |
|
|
|
gdf = gpd.GeoDataFrame.from_features(features, crs="EPSG:2056") |
|
return gdf |
|
|
|
def download_swissimage(min_x, min_y, max_x, max_y): |
|
api_url = "https://data.geo.admin.ch/api/stac/v0.9/collections/ch.swisstopo.swissimage-dop10/items" |
|
|
|
params = { |
|
"bbox": f"{min_x},{min_y},{max_x},{max_y}", |
|
"datetime": "2022-01-01T00:00:00Z/..", |
|
"limit": 1 |
|
} |
|
|
|
response = requests.get(api_url, params=params) |
|
response.raise_for_status() |
|
|
|
data = response.json() |
|
if data['features']: |
|
asset_url = data['features'][0]['assets']['rgb']['href'] |
|
image_response = requests.get(asset_url) |
|
image_response.raise_for_status() |
|
|
|
with zipfile.ZipFile(BytesIO(image_response.content)) as zf: |
|
image_file = [f for f in zf.namelist() if f.endswith('.tif')][0] |
|
with zf.open(image_file) as file: |
|
image = Image.open(file) |
|
return np.array(image) |
|
return None |
|
|
|
|
|
st.title("Extracteur de données Swisstopo") |
|
|
|
|
|
selected_layer_name = st.selectbox( |
|
"Sélectionnez la couche de données", |
|
list(LAYERS.keys()) |
|
) |
|
layer = LAYERS[selected_layer_name] |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
min_x = st.number_input("Coordonnée X minimale", value=2600000) |
|
min_y = st.number_input("Coordonnée Y minimale", value=1200000) |
|
with col2: |
|
max_x = st.number_input("Coordonnée X maximale", value=2601000) |
|
max_y = st.number_input("Coordonnée Y maximale", value=1201000) |
|
|
|
if st.button("Extraire les données"): |
|
try: |
|
|
|
gdf = extract_swisstopo_data(min_x, min_y, max_x, max_y, layer) |
|
|
|
|
|
st.subheader("Aperçu des données") |
|
st.write(gdf.head()) |
|
|
|
|
|
st.subheader("Visualisation des données") |
|
m = folium.Map(location=[gdf.geometry.centroid.y.mean(), gdf.geometry.centroid.x.mean()], zoom_start=14) |
|
folium.GeoJson(gdf).add_to(m) |
|
folium_static(m) |
|
|
|
|
|
st.download_button( |
|
label="Télécharger les données (GeoJSON)", |
|
data=gdf.to_json(), |
|
file_name=f"{selected_layer_name.lower().replace(' ', '_')}.geojson", |
|
mime="application/json" |
|
) |
|
|
|
|
|
if layer == "ch.swisstopo.swissimage-product": |
|
st.subheader("Image SWISSIMAGE") |
|
image = download_swissimage(min_x, min_y, max_x, max_y) |
|
if image is not None: |
|
st.image(image, caption="Image SWISSIMAGE", use_column_width=True) |
|
else: |
|
st.warning("Aucune image SWISSIMAGE disponible pour cette zone.") |
|
|
|
except Exception as e: |
|
st.error(f"Une erreur s'est produite : {str(e)}") |
|
|
|
|
|
st.sidebar.markdown("## À propos") |
|
st.sidebar.markdown("Cet outil permet d'extraire des données géospatiales de Swisstopo.") |
|
st.sidebar.markdown("Développé par Vertdure") |
|
st.sidebar.markdown("[Code source](https://huggingface.co/spaces/Vertdure/Streamlit/tree/main)") |