Eric2mangel commited on
Commit
ee60444
·
verified ·
1 Parent(s): 85a6ca6

Upload 3 files

Browse files
Files changed (3) hide show
  1. Communes_all_fr.parquet +3 -0
  2. app.py +120 -0
  3. requirements.txt +7 -3
Communes_all_fr.parquet ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:76f327985f05bea7a21c286b5dc836cfa7af517b95a9804d72490144dabe6ba8
3
+ size 2893361
app.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import h3
4
+ import pydeck as pdk
5
+ import os
6
+
7
+ st.set_page_config(page_title="France H3 3D", layout="wide", initial_sidebar_state="expanded")
8
+ st.title("France 3D avec H3 – PyDeck + Streamlit")
9
+
10
+ # --- SIDEBAR ---
11
+ with st.sidebar:
12
+ st.header("Paramètres")
13
+
14
+ COLONNE_VALEUR = st.selectbox(
15
+ 'Colonne (hauteur + couleur)',
16
+ ['population', 'altitude_moyenne', 'superficie_km2', 'densite'],
17
+ index=0
18
+ )
19
+
20
+ RESOLUTION_H3 = st.slider('Résolution H3', 6, 9, 7)
21
+
22
+ default_seuil = {'population': 1000, 'altitude_moyenne': 300, 'superficie_km2': 50, 'densite': 300}.get(COLONNE_VALEUR, 100)
23
+ SEUIL_MIN = st.number_input(f'Seuil minimum ({COLONNE_VALEUR})', value=default_seuil, step=100)
24
+
25
+ style_name = st.selectbox('Style', ['Dark Matter', 'Positron (Clair)', 'Voyager (Coloré)'], index=0)
26
+ MAP_STYLES = {
27
+ 'Dark Matter': 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
28
+ 'Positron (Clair)': 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
29
+ 'Voyager (Coloré)': 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json'
30
+ }
31
+ MAP_STYLE = MAP_STYLES[style_name]
32
+
33
+ # --- CHARGEMENT + CACHE ---
34
+ @st.cache_data
35
+ def load_data():
36
+ if not os.path.exists("Communes_all_fr.parquet"):
37
+ st.error("Fichier Communes_all_fr.parquet manquant !")
38
+ st.stop()
39
+ df = pd.read_parquet("Communes_all_fr.parquet")
40
+ df = df[df['superficie_km2'] < 800].copy()
41
+ df = df.dropna(subset=['latitude', 'longitude', 'population', COLONNE_VALEUR])
42
+ df = df[df['population'] > 0]
43
+ return df
44
+
45
+ df = load_data()
46
+
47
+ # --- TRAITEMENT H3 ---
48
+ @st.cache_data
49
+ def prepare_h3(df, res, col, seuil):
50
+ df = df.copy()
51
+ df['h3'] = df.apply(lambda r: h3.latlng_to_cell(r.latitude, r.longitude, res), axis=1)
52
+
53
+ agg = 'sum' if col == 'population' else 'mean'
54
+ grouped = df.groupby('h3').agg(
55
+ valeur=(col, agg),
56
+ population=('population', 'sum'),
57
+ ville=('nom_standard', 'first')
58
+ ).reset_index()
59
+
60
+ grouped = grouped[grouped.valeur >= seuil]
61
+ if grouped.empty:
62
+ return []
63
+
64
+ vmax = grouped.valeur.max()
65
+ vmin = grouped.valeur.min()
66
+ grouped['norm'] = 1.0 if vmax == vmin else (grouped.valeur - vmin) / (vmax - vmin)
67
+
68
+ data = []
69
+ for _, row in grouped.iterrows():
70
+ boundary = h3.cell_to_boundary(row.h3)
71
+ polygon = [[lon, lat] for lat, lon in boundary]
72
+ ratio = row['norm']
73
+ color = [255, int(255*(1-ratio)), 0, 230] if ratio > 0.5 else [0, int(255*ratio*2), 255, 230]
74
+ data.append({
75
+ "polygon": polygon,
76
+ "valeur": round(row.valeur, 2),
77
+ "ville": str(row.ville) if pd.notna(row.ville) else "Inconnue",
78
+ "population": int(row.population),
79
+ "color": color,
80
+ "elevation": row.valeur * (10 if col == "superficie_km2" else 1)
81
+ })
82
+ return data, vmax
83
+
84
+ data, vmax = prepare_h3(df, RESOLUTION_H3, COLONNE_VALEUR, SEUIL_MIN)
85
+
86
+ if not data:
87
+ st.warning("Aucun hexagone avec ce seuil – baisse-le !")
88
+ st.stop()
89
+
90
+ # --- CARTE PYDECK SANS BOUCLE INFINIE ---
91
+ layer = pdk.Layer(
92
+ "PolygonLayer",
93
+ data,
94
+ get_polygon="polygon",
95
+ get_fill_color="color",
96
+ get_elevation="elevation",
97
+ elevation_scale=0.06 if COLONNE_VALEUR == "population" else 1.2,
98
+ extruded=True,
99
+ wireframe=True,
100
+ pickable=False, # ← DÉSACTIVE LES INTERACTIONS
101
+ auto_highlight=False, # ← ÉVITE LES RERUNS
102
+ line_width_min_pixels=1
103
+ )
104
+
105
+ deck = pdk.Deck(
106
+ layers=[layer],
107
+ initial_view_state=pdk.ViewState(latitude=46.5, longitude=2.5, zoom=5.5, pitch=50),
108
+ map_style=MAP_STYLE
109
+ )
110
+
111
+ # LE PARAMÈTRE MAGIQUE QUI ÉVITE LA BOUCLE
112
+ st.pydeck_chart(deck, use_container_width=True, on_select="ignore")
113
+
114
+ # --- MÉTRIQUES ---
115
+ col1, col2, col3 = st.columns(3)
116
+ col1.metric("Hexagones", len(data))
117
+ col2.metric("Valeur max", f"{vmax:,.0f}")
118
+ col3.metric("Résolution H3", RESOLUTION_H3)
119
+
120
+ st.success("Carte affichée sans boucle infinie – tout fonctionne !")
requirements.txt CHANGED
@@ -1,3 +1,7 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ polars
4
+ pydeck
5
+ numpy
6
+ h3
7
+ pyarrow