AbdelChoufani commited on
Commit
3141721
Β·
1 Parent(s): 65927d1

Add converted Gradio app

Browse files
Files changed (2) hide show
  1. app.py +42 -131
  2. requirements.txt +6 -3
app.py CHANGED
@@ -16,12 +16,6 @@
16
 
17
 
18
 
19
-
20
-
21
-
22
-
23
-
24
-
25
  import gradio as gr
26
  import requests
27
  import pyproj
@@ -38,7 +32,6 @@ import time
38
 
39
 
40
 
41
-
42
  # OSM Overpass API URL
43
  OVERPASS_URL = "https://overpass-api.de/api/interpreter"
44
 
@@ -48,9 +41,8 @@ def latlon_to_utm(lat: float, lon: float) -> Tuple[float, float]:
48
  x, y = proj(lon, lat) # Note: pyproj uses (lon, lat) order
49
  return x, y
50
 
51
-
52
-
53
  def fetch_osm_data(lat: float, lon: float, radius: int = 500) -> Optional[Dict]:
 
54
  query = f"""
55
  [out:json];
56
  (
@@ -60,22 +52,17 @@ def fetch_osm_data(lat: float, lon: float, radius: int = 500) -> Optional[Dict]:
60
  >;
61
  out skel qt;
62
  """
63
-
64
  try:
65
- r = requests.get(OVERPASS_URL, params={"data": query}, timeout=30)
66
- print(f"πŸ›°οΈ Overpass status code: {r.status_code}")
67
- if r.status_code != 200:
68
- print(f"🚨 Overpass error body: {r.text[:200]!r}")
 
69
  return None
70
-
71
- data = r.json()
72
- print(f"βœ… Fetched {len(data.get('elements', []))} OSM elements")
73
- return data
74
-
75
  except Exception as e:
76
- print(f"❌ Exception while fetching OSM data: {e}")
77
- raise # re-raise so we see the traceback in the logs
78
-
79
 
80
  def parse_osm_data(osm_data: Dict) -> List[Dict]:
81
  """Extract building footprints and heights from OSM data."""
@@ -119,130 +106,60 @@ def parse_osm_data(osm_data: Dict) -> List[Dict]:
119
  return buildings
120
 
121
  def create_3d_model(buildings: List[Dict]) -> trimesh.Scene:
122
- """Create a 3D model using trimesh with debug logging."""
123
  scene = trimesh.Scene()
124
 
125
- for i, building in enumerate(buildings):
126
- debug = (i < 5)
127
  footprint = building["footprint"]
128
  height = building.get("height", 10)
129
- if debug:
130
- print(f"πŸ—οΈ Building #{i}: {len(footprint)} points, height={height}")
131
  if height <= 0:
132
- if debug: print(" ↳ skipping: non-positive height")
133
  continue
134
-
135
- # build the polygon
136
  try:
137
  polygon = sg.Polygon(footprint)
138
- if debug:
139
- print(f" ↳ polygon valid={polygon.is_valid}, area={polygon.area:.2f}")
140
- if not polygon.is_valid or polygon.area == 0:
141
  polygon = polygon.buffer(0)
142
- if debug:
143
- print(f" ↳ after buffer valid={polygon.is_valid}, area={polygon.area:.2f}")
144
- if not polygon.is_valid or polygon.area == 0:
145
- if debug: print(" ↳ skipping: invalid or zero-area polygon")
146
  continue
147
- except Exception as e:
148
- if debug: print(f" ↳ skipping: polygon error: {e}")
149
  continue
150
- # **Extrude + orient + add all in one try/except**
151
  try:
152
- # Try multiple extrusion methods in order of preference
153
- extruded = None
154
-
155
- # Method 1: Try triangle engine
156
  try:
157
  extruded = trimesh.creation.extrude_polygon(polygon, height, engine="triangle")
158
- if debug: print(" ↳ used triangle engine")
159
- except (ImportError, ValueError) as e:
160
- if debug: print(f" ↳ triangle engine error ({e})")
161
-
162
- # Method 2: Try earcut engine
163
- if extruded is None:
164
  try:
165
  extruded = trimesh.creation.extrude_polygon(polygon, height, engine="earcut")
166
- if debug: print(" ↳ used earcut engine")
167
- except (ImportError, ValueError) as e:
168
- if debug: print(f" ↳ earcut engine error ({e})")
169
 
170
- # Method 3: Manual extrusion fallback
171
- if extruded is None:
172
- if debug: print(" ↳ using manual extrusion fallback")
173
-
174
- # Get polygon exterior coordinates
175
- coords = list(polygon.exterior.coords[:-1]) # Remove duplicate last point
176
-
177
- # Create bottom vertices
178
- bottom_vertices = np.array([[x, y, 0] for x, y in coords])
179
- # Create top vertices
180
- top_vertices = np.array([[x, y, height] for x, y in coords])
181
- # Combine all vertices
182
- vertices = np.vstack([bottom_vertices, top_vertices])
183
-
184
- # Create faces
185
- faces = []
186
- n = len(coords)
187
-
188
- # Triangulate bottom face (reversed for correct normal)
189
- if n >= 3:
190
- bottom_triangles = simple_triangulate_polygon(list(range(n)))
191
- for tri in bottom_triangles:
192
- faces.append([tri[2], tri[1], tri[0]]) # Reverse for correct normal
193
-
194
- # Triangulate top face
195
- if n >= 3:
196
- top_triangles = simple_triangulate_polygon(list(range(n)))
197
- for tri in top_triangles:
198
- faces.append([tri[0] + n, tri[1] + n, tri[2] + n])
199
-
200
- # Side faces
201
- for i in range(n):
202
- next_i = (i + 1) % n
203
- # Two triangles per side
204
- faces.append([i, next_i, next_i + n])
205
- faces.append([i, next_i + n, i + n])
206
-
207
- extruded = trimesh.Trimesh(vertices=vertices, faces=faces)
208
- if debug: print(" ↳ manual extrusion successful")
209
 
210
- if extruded is not None:
211
- if debug:
212
- print(f" ↳ extruded mesh vertices={len(extruded.vertices)}, faces={len(extruded.faces)}")
213
-
214
- # orientation fix
215
- transform_x = trimesh.transformations.rotation_matrix(np.pi/2, (1, 0, 0))
216
- transform_z = trimesh.transformations.rotation_matrix(np.pi, (0, 0, 1))
217
- extruded.apply_transform(transform_x)
218
- extruded.apply_transform(transform_z)
219
-
220
- scene.add_geometry(extruded)
221
- if debug: print(" ↳ added to scene")
222
- else:
223
- if debug: print(" ↳ all extrusion methods failed")
224
-
225
- except Exception as e:
226
- if debug: print(f" ↳ skipping: extrusion/orientation error: {e}")
227
  continue
228
-
229
- print(f"πŸ”§ Final scene.geometry count: {len(scene.geometry)}")
230
  return scene
231
 
232
-
233
-
234
  def save_3d_model(scene: trimesh.Scene, filename: str) -> bool:
235
  """Export the 3D scene to a GLB file."""
236
  try:
237
  scene.export(filename)
238
- file_exists = os.path.exists(filename)
239
- print("πŸ’Ύ wrote file?", file_exists)
240
- if file_exists:
241
- print("πŸ’Ύ filesize:", os.path.getsize(filename))
242
- return file_exists
243
-
244
- except Exception as e:
245
- print(f"❌ Error saving 3D model: {e}")
246
  return False
247
 
248
 
@@ -280,18 +197,14 @@ def generate_3d_model(latitude: float, longitude: float, radius: int) -> Tuple[s
280
  # Step 3: Create 3D model
281
  status_msg += f"\nβœ… Found {len(buildings)} buildings\n🏠 Creating 3D model..."
282
  scene = create_3d_model(buildings)
283
- print(f"πŸ”§ trimesh scene has {len(scene.geometry)} geometries")
284
-
285
 
286
  if len(scene.geometry) == 0:
287
  return None, "❌ Could not create 3D model from the buildings found.", ""
288
- # Step 4: Save model
 
289
  timestamp = int(time.time())
290
  filename = f"osm_3d_model_{timestamp}.glb"
291
 
292
- # Ensure we can write to the current directory
293
- os.makedirs(os.path.dirname(os.path.abspath(filename)) if os.path.dirname(filename) else ".", exist_ok=True)
294
-
295
  status_msg += f"\nβœ… 3D model created with {len(scene.geometry)} buildings\nπŸ’Ύ Saving model..."
296
 
297
  if save_3d_model(scene, filename):
@@ -312,10 +225,7 @@ def generate_3d_model(latitude: float, longitude: float, radius: int) -> Tuple[s
312
  return None, "❌ Failed to save 3D model file.", ""
313
 
314
  except Exception as e:
315
- import traceback
316
- traceback.print_exc()
317
- raise
318
-
319
 
320
 
321
 
@@ -462,6 +372,7 @@ app = create_gradio_app()
462
  # Launch the app
463
  if __name__ == "__main__":
464
  app.launch(
 
465
  server_name="0.0.0.0", # Allow external connections
466
  server_port=7860, # Standard port for Hugging Face
467
  show_error=True,
 
16
 
17
 
18
 
 
 
 
 
 
 
19
  import gradio as gr
20
  import requests
21
  import pyproj
 
32
 
33
 
34
 
 
35
  # OSM Overpass API URL
36
  OVERPASS_URL = "https://overpass-api.de/api/interpreter"
37
 
 
41
  x, y = proj(lon, lat) # Note: pyproj uses (lon, lat) order
42
  return x, y
43
 
 
 
44
  def fetch_osm_data(lat: float, lon: float, radius: int = 500) -> Optional[Dict]:
45
+ """Fetch OSM data for buildings within a given radius of a coordinate."""
46
  query = f"""
47
  [out:json];
48
  (
 
52
  >;
53
  out skel qt;
54
  """
55
+
56
  try:
57
+ response = requests.get(OVERPASS_URL, params={"data": query}, timeout=30)
58
+ if response.status_code == 200:
59
+ data = response.json()
60
+ return data
61
+ else:
62
  return None
 
 
 
 
 
63
  except Exception as e:
64
+ print(f"Error fetching OSM data: {e}")
65
+ return None
 
66
 
67
  def parse_osm_data(osm_data: Dict) -> List[Dict]:
68
  """Extract building footprints and heights from OSM data."""
 
106
  return buildings
107
 
108
  def create_3d_model(buildings: List[Dict]) -> trimesh.Scene:
109
+ """Create a 3D model using trimesh with PROPER ORIENTATION FIX."""
110
  scene = trimesh.Scene()
111
 
112
+ for building in buildings:
 
113
  footprint = building["footprint"]
114
  height = building.get("height", 10)
115
+
 
116
  if height <= 0:
 
117
  continue
118
+
 
119
  try:
120
  polygon = sg.Polygon(footprint)
121
+ if not polygon.is_valid:
 
 
122
  polygon = polygon.buffer(0)
123
+ if not polygon.is_valid:
 
 
 
124
  continue
125
+ except Exception:
 
126
  continue
127
+
128
  try:
129
+ # Try triangle engine first, then earcut
 
 
 
130
  try:
131
  extruded = trimesh.creation.extrude_polygon(polygon, height, engine="triangle")
132
+ except ValueError:
 
 
 
 
 
133
  try:
134
  extruded = trimesh.creation.extrude_polygon(polygon, height, engine="earcut")
135
+ except ValueError:
136
+ continue
 
137
 
138
+ # βœ… PROPER ORIENTATION FIX - This is the solution you provided
139
+ # This rotates the model so the front view shows properly
140
+ transform_x = trimesh.transformations.rotation_matrix(np.pi/2, (1, 0, 0))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
+ # Also rotate around Z-axis for proper left-right orientation
143
+ transform_z = trimesh.transformations.rotation_matrix(np.pi, (0, 0, 1))
144
+
145
+ # Apply the transformations
146
+ extruded.apply_transform(transform_x)
147
+ extruded.apply_transform(transform_z)
148
+
149
+ # Add to scene
150
+ scene.add_geometry(extruded)
151
+
152
+ except Exception:
 
 
 
 
 
 
153
  continue
154
+
 
155
  return scene
156
 
 
 
157
  def save_3d_model(scene: trimesh.Scene, filename: str) -> bool:
158
  """Export the 3D scene to a GLB file."""
159
  try:
160
  scene.export(filename)
161
+ return os.path.exists(filename)
162
+ except Exception:
 
 
 
 
 
 
163
  return False
164
 
165
 
 
197
  # Step 3: Create 3D model
198
  status_msg += f"\nβœ… Found {len(buildings)} buildings\n🏠 Creating 3D model..."
199
  scene = create_3d_model(buildings)
 
 
200
 
201
  if len(scene.geometry) == 0:
202
  return None, "❌ Could not create 3D model from the buildings found.", ""
203
+
204
+ # Step 4: Save model
205
  timestamp = int(time.time())
206
  filename = f"osm_3d_model_{timestamp}.glb"
207
 
 
 
 
208
  status_msg += f"\nβœ… 3D model created with {len(scene.geometry)} buildings\nπŸ’Ύ Saving model..."
209
 
210
  if save_3d_model(scene, filename):
 
225
  return None, "❌ Failed to save 3D model file.", ""
226
 
227
  except Exception as e:
228
+ return None, f"❌ Unexpected error: {str(e)}", ""
 
 
 
229
 
230
 
231
 
 
372
  # Launch the app
373
  if __name__ == "__main__":
374
  app.launch(
375
+ share=True, # Creates public link for Hugging Face
376
  server_name="0.0.0.0", # Allow external connections
377
  server_port=7860, # Standard port for Hugging Face
378
  show_error=True,
requirements.txt CHANGED
@@ -1,6 +1,9 @@
1
  gradio
2
- requests
3
- pyproj
4
  shapely
 
5
  trimesh
6
- numpy
 
 
 
 
1
  gradio
2
+ osmnx
 
3
  shapely
4
+ pyproj
5
  trimesh
6
+ numpy
7
+ requests
8
+ triangle
9
+ mapbox_earcut