Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -277,18 +277,16 @@ async def generate_model(request: GenerateRequest):
|
|
| 277 |
raise HTTPException(status_code=500, detail=str(e))
|
| 278 |
|
| 279 |
@app.post("/generate_i3s")
|
| 280 |
-
async def generate_i3s(request:
|
| 281 |
-
"""Generate an I3S Layer (SLPK unpacked)
|
| 282 |
|
| 283 |
-
# 0. Cleanup Old Layers
|
| 284 |
-
# Remove all subdirectories in LAYERS_DIR to keep server clean
|
| 285 |
try:
|
| 286 |
if os.path.exists(LAYERS_DIR):
|
| 287 |
for item in os.listdir(LAYERS_DIR):
|
| 288 |
item_path = os.path.join(LAYERS_DIR, item)
|
| 289 |
if os.path.isdir(item_path):
|
| 290 |
shutil.rmtree(item_path)
|
| 291 |
-
logger.info(f"Cleaned up old layer: {item}")
|
| 292 |
except Exception as e:
|
| 293 |
logger.warning(f"Cleanup failed: {e}")
|
| 294 |
|
|
@@ -297,68 +295,74 @@ async def generate_i3s(request: GenerateI3SRequest):
|
|
| 297 |
raise HTTPException(status_code=404, detail="RPK not found")
|
| 298 |
|
| 299 |
try:
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
cz = coords[idx+2]
|
| 324 |
-
|
| 325 |
-
ux, uy = transformer.transform(cx, cy)
|
| 326 |
-
utm_coords.extend([ux, uy, cz])
|
| 327 |
-
|
| 328 |
-
# 3. Calculate Anchor (Mercator)
|
| 329 |
-
anchor_x = utm_coords[0]
|
| 330 |
-
anchor_y = utm_coords[1]
|
| 331 |
-
anchor_z = utm_coords[2]
|
| 332 |
-
|
| 333 |
-
# 4. Localize
|
| 334 |
-
local_coords = []
|
| 335 |
-
for i in range(num_points):
|
| 336 |
-
idx = i * 3
|
| 337 |
-
ux = utm_coords[idx]
|
| 338 |
-
uy = utm_coords[idx+1]
|
| 339 |
-
uz = utm_coords[idx+2]
|
| 340 |
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
initial_shape = pyprt.InitialShape(
|
| 347 |
|
| 348 |
-
#
|
| 349 |
layer_id = str(uuid.uuid4())
|
| 350 |
output_dir = os.path.join(LAYERS_DIR, layer_id)
|
| 351 |
os.makedirs(output_dir, exist_ok=True)
|
| 352 |
|
| 353 |
enc_options = {
|
| 354 |
-
'sceneType': 'Global',
|
| 355 |
-
'sceneWkid':
|
| 356 |
'baseName': 'SceneLayer',
|
| 357 |
'sceneName': 'SceneLayer',
|
| 358 |
'writePackage': False,
|
| 359 |
'compression': False,
|
| 360 |
'outputPath': output_dir,
|
| 361 |
-
|
|
|
|
|
|
|
| 362 |
}
|
| 363 |
|
| 364 |
# Clean attributes (same as before)
|
|
|
|
| 277 |
raise HTTPException(status_code=500, detail=str(e))
|
| 278 |
|
| 279 |
@app.post("/generate_i3s")
|
| 280 |
+
async def generate_i3s(request: GenerateRequest):
|
| 281 |
+
"""Generate an I3S Layer (SLPK unpacked) using same logic as GLB generation."""
|
| 282 |
|
| 283 |
+
# 0. Cleanup Old Layers
|
|
|
|
| 284 |
try:
|
| 285 |
if os.path.exists(LAYERS_DIR):
|
| 286 |
for item in os.listdir(LAYERS_DIR):
|
| 287 |
item_path = os.path.join(LAYERS_DIR, item)
|
| 288 |
if os.path.isdir(item_path):
|
| 289 |
shutil.rmtree(item_path)
|
|
|
|
| 290 |
except Exception as e:
|
| 291 |
logger.warning(f"Cleanup failed: {e}")
|
| 292 |
|
|
|
|
| 295 |
raise HTTPException(status_code=404, detail="RPK not found")
|
| 296 |
|
| 297 |
try:
|
| 298 |
+
# 1. Parse Geometry (Same as generate_model)
|
| 299 |
+
geom_dict = request.geometry
|
| 300 |
+
if geom_dict.get("type") == "Feature":
|
| 301 |
+
geom_dict = geom_dict.get("geometry")
|
| 302 |
+
|
| 303 |
+
shape = shapely.geometry.shape(geom_dict)
|
| 304 |
+
|
| 305 |
+
if geom_dict.get("type") != "Polygon":
|
| 306 |
+
raise HTTPException(status_code=400, detail="Only Polygons are supported")
|
| 307 |
+
|
| 308 |
+
# Re-orient
|
| 309 |
+
if not shape.is_valid: shape = shape.buffer(0)
|
| 310 |
+
shape = shapely.ops.orient(shape, sign=-1.0)
|
| 311 |
+
|
| 312 |
+
# 2. Calculate Centroid & Recentering (Same as generate_model)
|
| 313 |
+
centroid = shape.centroid
|
| 314 |
+
logger.info(f"I3S Centroid: {centroid.x}, {centroid.y}")
|
| 315 |
+
|
| 316 |
+
# Translate to (0,0)
|
| 317 |
+
shape_centered = translate(shape, xoff=-centroid.x, yoff=-centroid.y)
|
| 318 |
+
|
| 319 |
+
coords = list(shape_centered.exterior.coords)
|
| 320 |
+
if coords[0] == coords[-1]: coords = coords[:-1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
|
| 322 |
+
# Flatten: [x, 0, y, ...]
|
| 323 |
+
# Note: Input is degrees, so these are delta degrees.
|
| 324 |
+
# However, for I3S, we usually need meters.
|
| 325 |
+
# BUT user insists on "same method". If "same method" works for GLB, it likely relies on frontend placement.
|
| 326 |
+
# For I3S, the placement is burned into the file via globalOffset.
|
| 327 |
+
|
| 328 |
+
# If we assume input is Meters (already projected by frontend? No, standard GeoJSON is Lon/Lat).
|
| 329 |
+
# If we pass delta-degrees to PyPRT, it generates a tiny model.
|
| 330 |
+
# UNLESS 'generate_model' also does projection?
|
| 331 |
+
# Checking generate_model... it does NOT project. It just centers.
|
| 332 |
+
# So it produces a tiny GLB. Cesium scales it?
|
| 333 |
+
# CesiumComponent: scale: 1.0.
|
| 334 |
+
# PROBABLY the RPK handles the small numbers? Or PyPRT has a setting?
|
| 335 |
+
# OR the user is mistaken and GLB is also tiny but they haven't noticed?
|
| 336 |
+
|
| 337 |
+
# Wait, if I3S is tiny, `globalOffset` places it correctly but size is wrong.
|
| 338 |
+
# Let's trust the "Same Method" request.
|
| 339 |
+
# We pass centroid as globalOffset.
|
| 340 |
+
|
| 341 |
+
flattened_coords = []
|
| 342 |
+
for p in coords:
|
| 343 |
+
flattened_coords.extend([p[0], 0, p[1]])
|
| 344 |
|
| 345 |
+
indices = list(range(len(coords)))
|
| 346 |
+
face_counts = [len(coords)]
|
| 347 |
+
|
| 348 |
+
initial_shape = pyprt.InitialShape(flattened_coords, indices, face_counts)
|
| 349 |
|
| 350 |
+
# 3. Encoder Options
|
| 351 |
layer_id = str(uuid.uuid4())
|
| 352 |
output_dir = os.path.join(LAYERS_DIR, layer_id)
|
| 353 |
os.makedirs(output_dir, exist_ok=True)
|
| 354 |
|
| 355 |
enc_options = {
|
| 356 |
+
'sceneType': 'Global',
|
| 357 |
+
'sceneWkid': '4326', # WGS84
|
| 358 |
'baseName': 'SceneLayer',
|
| 359 |
'sceneName': 'SceneLayer',
|
| 360 |
'writePackage': False,
|
| 361 |
'compression': False,
|
| 362 |
'outputPath': output_dir,
|
| 363 |
+
|
| 364 |
+
# Critical: Global Offset is the Centroid
|
| 365 |
+
'globalOffset': [centroid.x, centroid.y, 0]
|
| 366 |
}
|
| 367 |
|
| 368 |
# Clean attributes (same as before)
|