Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1318,4 +1318,534 @@ def create_advanced_app():
|
|
| 1318 |
if not optimized_solution or abs(sum(w for w, _ in optimized_solution) - stage_width) > 0.001:
|
| 1319 |
# Provide suggestions
|
| 1320 |
return None, pd.DataFrame(), f"""
|
| 1321 |
-
### ❌ Cannot achieve 100% usage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1318 |
if not optimized_solution or abs(sum(w for w, _ in optimized_solution) - stage_width) > 0.001:
|
| 1319 |
# Provide suggestions
|
| 1320 |
return None, pd.DataFrame(), f"""
|
| 1321 |
+
### ❌ Cannot achieve 100% usage with selected widths
|
| 1322 |
+
|
| 1323 |
+
**Stage Width**: {stage_width}m
|
| 1324 |
+
**Available Widths**: {', '.join([f"{w}m" for w in sorted(enabled_widths)])}
|
| 1325 |
+
|
| 1326 |
+
**Try:**
|
| 1327 |
+
1. Enable more lot types for flexibility
|
| 1328 |
+
2. Enable "Custom Corners" option
|
| 1329 |
+
3. Try common stage widths: 84m, 105m, 126m
|
| 1330 |
+
""", "", ""
|
| 1331 |
+
|
| 1332 |
+
# Create visualizations with variance indicator
|
| 1333 |
+
fig_2d = optimizer.create_enhanced_visualization(
|
| 1334 |
+
optimized_solution, stage_width, stage_depth,
|
| 1335 |
+
"AI-Optimized Diverse Subdivision Layout",
|
| 1336 |
+
show_variance=variance
|
| 1337 |
+
)
|
| 1338 |
+
|
| 1339 |
+
# Create results table
|
| 1340 |
+
width_counts = {}
|
| 1341 |
+
for width, lot_type in optimized_solution:
|
| 1342 |
+
key = f"{width:.1f}m"
|
| 1343 |
+
if key in width_counts:
|
| 1344 |
+
width_counts[key]['count'] += 1
|
| 1345 |
+
else:
|
| 1346 |
+
# Handle both standard and custom widths
|
| 1347 |
+
if width in optimizer.lot_specifications:
|
| 1348 |
+
spec = optimizer.lot_specifications[width]
|
| 1349 |
+
elif int(width) in optimizer.lot_specifications:
|
| 1350 |
+
spec = optimizer.lot_specifications[int(width)]
|
| 1351 |
+
else:
|
| 1352 |
+
# Custom width - find closest
|
| 1353 |
+
closest = min(optimizer.lot_specifications.keys(),
|
| 1354 |
+
key=lambda x: abs(x - width))
|
| 1355 |
+
spec = optimizer.lot_specifications[closest]
|
| 1356 |
+
spec = {**spec, 'type': 'Custom', 'squares': 'Custom'}
|
| 1357 |
+
|
| 1358 |
+
width_counts[key] = {
|
| 1359 |
+
'count': 1,
|
| 1360 |
+
'type': spec.get('type', 'Custom'),
|
| 1361 |
+
'squares': spec.get('squares', 'N/A'),
|
| 1362 |
+
'area': width * stage_depth
|
| 1363 |
+
}
|
| 1364 |
+
|
| 1365 |
+
results_data = []
|
| 1366 |
+
for width, info in sorted(width_counts.items()):
|
| 1367 |
+
results_data.append({
|
| 1368 |
+
'Lot Width': width,
|
| 1369 |
+
'Count': info['count'],
|
| 1370 |
+
'Type': info['type'],
|
| 1371 |
+
'Area Each': f"{info['area']:.0f}m²",
|
| 1372 |
+
'Total Width': f"{float(width[:-1]) * info['count']:.1f}m",
|
| 1373 |
+
'Total Area': f"{info['area'] * info['count']:.0f}m²"
|
| 1374 |
+
})
|
| 1375 |
+
|
| 1376 |
+
results_df = pd.DataFrame(results_data)
|
| 1377 |
+
|
| 1378 |
+
# Generate report
|
| 1379 |
+
report = optimizer.generate_report(optimized_solution, stage_width, stage_depth, None)
|
| 1380 |
+
|
| 1381 |
+
# Create summary
|
| 1382 |
+
total_lots = len(optimized_solution)
|
| 1383 |
+
unique_widths = len(set(w for w, _ in optimized_solution))
|
| 1384 |
+
|
| 1385 |
+
# Count SLHC pairs
|
| 1386 |
+
slhc_pairs = sum(1 for i in range(len(optimized_solution) - 1)
|
| 1387 |
+
if optimized_solution[i][0] <= 10.5 and optimized_solution[i+1][0] <= 10.5)
|
| 1388 |
+
|
| 1389 |
+
# Analyze corners
|
| 1390 |
+
corner_info = "N/A"
|
| 1391 |
+
if len(optimized_solution) >= 2:
|
| 1392 |
+
first = optimized_solution[0][0]
|
| 1393 |
+
last = optimized_solution[-1][0]
|
| 1394 |
+
diff = abs(first - last)
|
| 1395 |
+
|
| 1396 |
+
if diff < 0.1:
|
| 1397 |
+
corner_info = f"✨ PERFECT ({first:.1f}m × 2)"
|
| 1398 |
+
elif diff <= 1.0:
|
| 1399 |
+
corner_info = f"✅ Excellent ({first:.1f}m + {last:.1f}m)"
|
| 1400 |
+
elif diff <= 2.0:
|
| 1401 |
+
corner_info = f"👍 Good ({first:.1f}m + {last:.1f}m)"
|
| 1402 |
+
else:
|
| 1403 |
+
corner_info = f"⚠️ Unbalanced ({first:.1f}m + {last:.1f}m)"
|
| 1404 |
+
|
| 1405 |
+
summary = f"""
|
| 1406 |
+
**Stage**: {stage_width}m × {stage_depth}m = {stage_width * stage_depth}m²
|
| 1407 |
+
**Total Lots**: {total_lots}
|
| 1408 |
+
**Unique Lot Types**: {unique_widths}
|
| 1409 |
+
**Grid Variance**: {variance:+.2f}m {"✅" if abs(variance) < 0.001 else "⚠️"}
|
| 1410 |
+
"""
|
| 1411 |
+
|
| 1412 |
+
# Convert solution to string for manual editing
|
| 1413 |
+
manual_edit_string = optimizer.solution_to_string(optimized_solution)
|
| 1414 |
+
|
| 1415 |
+
return fig_2d, results_df, summary, report, manual_edit_string
|
| 1416 |
+
|
| 1417 |
+
def update_manual_adjustment(manual_widths_text, stage_width, stage_depth, color_scheme):
|
| 1418 |
+
"""Update visualization based on manual adjustment"""
|
| 1419 |
+
optimizer.current_scheme = color_scheme
|
| 1420 |
+
|
| 1421 |
+
# Parse manual widths
|
| 1422 |
+
widths = optimizer.parse_manual_adjustments(manual_widths_text)
|
| 1423 |
+
|
| 1424 |
+
if not widths:
|
| 1425 |
+
return None, "Please enter lot widths (e.g., '14.0, 8.5, 10.5, 8.5, 14.0')"
|
| 1426 |
+
|
| 1427 |
+
# Validate and get feedback
|
| 1428 |
+
solution, feedback = optimizer.validate_manual_solution(widths, stage_width)
|
| 1429 |
+
|
| 1430 |
+
if not solution:
|
| 1431 |
+
return None, feedback
|
| 1432 |
+
|
| 1433 |
+
# Calculate variance
|
| 1434 |
+
total_width = sum(widths)
|
| 1435 |
+
variance = total_width - stage_width
|
| 1436 |
+
|
| 1437 |
+
# Create visualization with variance
|
| 1438 |
+
fig = optimizer.create_enhanced_visualization(
|
| 1439 |
+
solution, stage_width, stage_depth,
|
| 1440 |
+
"Manually Adjusted Layout",
|
| 1441 |
+
show_variance=variance
|
| 1442 |
+
)
|
| 1443 |
+
|
| 1444 |
+
return fig, feedback
|
| 1445 |
+
|
| 1446 |
+
# Create Gradio interface
|
| 1447 |
+
with gr.Blocks(
|
| 1448 |
+
title="Advanced AI Grid Optimizer",
|
| 1449 |
+
theme=gr.themes.Base(),
|
| 1450 |
+
css="""
|
| 1451 |
+
.gradio-container {
|
| 1452 |
+
font-family: 'Segoe UI', sans-serif;
|
| 1453 |
+
background: #1a1a1a;
|
| 1454 |
+
color: white;
|
| 1455 |
+
}
|
| 1456 |
+
.gr-button-primary {
|
| 1457 |
+
background: linear-gradient(45deg, #FF073A 30%, #0AEFFF 90%);
|
| 1458 |
+
border: none;
|
| 1459 |
+
box-shadow: 0 3px 5px 2px rgba(255, 7, 58, .3);
|
| 1460 |
+
}
|
| 1461 |
+
h1 {
|
| 1462 |
+
background: linear-gradient(45deg, #FF073A, #0AEFFF);
|
| 1463 |
+
-webkit-background-clip: text;
|
| 1464 |
+
-webkit-text-fill-color: transparent;
|
| 1465 |
+
text-align: center;
|
| 1466 |
+
font-size: 2.5em;
|
| 1467 |
+
}
|
| 1468 |
+
.gr-form {
|
| 1469 |
+
background: rgba(42, 42, 42, 0.9);
|
| 1470 |
+
border-radius: 10px;
|
| 1471 |
+
padding: 20px;
|
| 1472 |
+
border: 1px solid #444;
|
| 1473 |
+
}
|
| 1474 |
+
.gr-input {
|
| 1475 |
+
background-color: #2a2a2a;
|
| 1476 |
+
color: white;
|
| 1477 |
+
border: 1px solid #444;
|
| 1478 |
+
}
|
| 1479 |
+
.gr-check-radio {
|
| 1480 |
+
background-color: #2a2a2a;
|
| 1481 |
+
}
|
| 1482 |
+
"""
|
| 1483 |
+
) as demo:
|
| 1484 |
+
gr.Markdown("""
|
| 1485 |
+
# 🏗️ Advanced AI Grid Cut Optimizer Pro
|
| 1486 |
+
### AI-Powered Subdivision Planning with Manual Fine-Tuning
|
| 1487 |
+
""")
|
| 1488 |
+
|
| 1489 |
+
with gr.Tabs() as main_tabs:
|
| 1490 |
+
with gr.TabItem("🤖 AI Optimization"):
|
| 1491 |
+
with gr.Row():
|
| 1492 |
+
with gr.Column(scale=1):
|
| 1493 |
+
with gr.Group():
|
| 1494 |
+
gr.Markdown("### 📐 Stage Dimensions")
|
| 1495 |
+
stage_width = gr.Number(
|
| 1496 |
+
label="Stage Width (m)",
|
| 1497 |
+
value=105.0,
|
| 1498 |
+
info="Width along the street"
|
| 1499 |
+
)
|
| 1500 |
+
stage_depth = gr.Number(
|
| 1501 |
+
label="Stage Depth (m)",
|
| 1502 |
+
value=32.0,
|
| 1503 |
+
info="Depth of lots (perpendicular to street)"
|
| 1504 |
+
)
|
| 1505 |
+
|
| 1506 |
+
gr.Markdown("### 📏 Lot Width Options")
|
| 1507 |
+
|
| 1508 |
+
with gr.Group():
|
| 1509 |
+
gr.Markdown("**Standard Widths**")
|
| 1510 |
+
with gr.Row():
|
| 1511 |
+
enable_8_5 = gr.Checkbox(label="8.5m SLHC", value=True)
|
| 1512 |
+
enable_10_5 = gr.Checkbox(label="10.5m SLHC", value=True)
|
| 1513 |
+
enable_12_5 = gr.Checkbox(label="12.5m", value=True)
|
| 1514 |
+
with gr.Row():
|
| 1515 |
+
enable_14 = gr.Checkbox(label="14.0m", value=True)
|
| 1516 |
+
enable_16 = gr.Checkbox(label="16.0m", value=True)
|
| 1517 |
+
enable_18 = gr.Checkbox(label="18.0m", value=False)
|
| 1518 |
+
|
| 1519 |
+
with gr.Group():
|
| 1520 |
+
enable_corners = gr.Checkbox(
|
| 1521 |
+
label="Enable Corner-Specific Widths",
|
| 1522 |
+
value=True,
|
| 1523 |
+
info="Adds variety and helps achieve 100%"
|
| 1524 |
+
)
|
| 1525 |
+
with gr.Row():
|
| 1526 |
+
enable_11 = gr.Checkbox(label="11.0m", value=True)
|
| 1527 |
+
enable_13_3 = gr.Checkbox(label="13.3m", value=True)
|
| 1528 |
+
with gr.Row():
|
| 1529 |
+
enable_14_8 = gr.Checkbox(label="14.8m", value=True)
|
| 1530 |
+
enable_16_8 = gr.Checkbox(label="16.8m", value=True)
|
| 1531 |
+
|
| 1532 |
+
with gr.Column(scale=1):
|
| 1533 |
+
gr.Markdown("### ⚙️ Advanced Settings")
|
| 1534 |
+
|
| 1535 |
+
allow_custom_corners = gr.Checkbox(
|
| 1536 |
+
label="🎯 Allow Flexible Corner Widths",
|
| 1537 |
+
value=True,
|
| 1538 |
+
info="Enables 13.8m, 13.9m etc. for perfect fits"
|
| 1539 |
+
)
|
| 1540 |
+
|
| 1541 |
+
optimization_strategy = gr.Radio(
|
| 1542 |
+
["diversity_focus", "balanced"],
|
| 1543 |
+
label="Optimization Strategy",
|
| 1544 |
+
value="diversity_focus",
|
| 1545 |
+
info="Diversity creates more interesting layouts"
|
| 1546 |
+
)
|
| 1547 |
+
|
| 1548 |
+
color_scheme = gr.Radio(
|
| 1549 |
+
["modern", "professional", "neon"],
|
| 1550 |
+
label="🎨 Color Scheme",
|
| 1551 |
+
value="neon",
|
| 1552 |
+
info="Neon colors work best with dark background"
|
| 1553 |
+
)
|
| 1554 |
+
|
| 1555 |
+
optimize_btn = gr.Button(
|
| 1556 |
+
"🚀 Optimize with AI",
|
| 1557 |
+
variant="primary",
|
| 1558 |
+
size="lg",
|
| 1559 |
+
elem_id="optimize-button"
|
| 1560 |
+
)
|
| 1561 |
+
|
| 1562 |
+
gr.Markdown("""
|
| 1563 |
+
### 💡 Quick Tips:
|
| 1564 |
+
- **Visual Fix**: All lots now align at rear boundary
|
| 1565 |
+
- **Corner Lots**: Always wider than internals
|
| 1566 |
+
- **Grid Variance**: Shows if layout is perfect (0.0m)
|
| 1567 |
+
- **Manual Adjust**: Edit the result below after optimization
|
| 1568 |
+
""")
|
| 1569 |
+
|
| 1570 |
+
with gr.Row():
|
| 1571 |
+
plot_2d = gr.Plot(label="2D Layout with Corner Splays")
|
| 1572 |
+
|
| 1573 |
+
# Manual adjustment section
|
| 1574 |
+
gr.Markdown("### ✏️ Fine-Tune AI Result")
|
| 1575 |
+
with gr.Row():
|
| 1576 |
+
with gr.Column(scale=2):
|
| 1577 |
+
manual_widths = gr.Textbox(
|
| 1578 |
+
label="Manually Adjust Lot Widths",
|
| 1579 |
+
placeholder="Widths will appear here after optimization",
|
| 1580 |
+
info="Edit the widths (comma-separated) and click 'Update Layout'",
|
| 1581 |
+
lines=2
|
| 1582 |
+
)
|
| 1583 |
+
with gr.Column(scale=1):
|
| 1584 |
+
update_btn = gr.Button("🔄 Update Layout", variant="secondary")
|
| 1585 |
+
adjustment_feedback = gr.Markdown(
|
| 1586 |
+
value="",
|
| 1587 |
+
label="Adjustment Feedback"
|
| 1588 |
+
)
|
| 1589 |
+
|
| 1590 |
+
with gr.Row():
|
| 1591 |
+
results_table = gr.DataFrame(label="Lot Distribution Analysis")
|
| 1592 |
+
|
| 1593 |
+
with gr.Row():
|
| 1594 |
+
with gr.Column():
|
| 1595 |
+
summary_output = gr.Markdown(label="Optimization Summary")
|
| 1596 |
+
with gr.Column():
|
| 1597 |
+
report_output = gr.Markdown(label="Professional Report")
|
| 1598 |
+
|
| 1599 |
+
with gr.TabItem("📊 Plan Reader"):
|
| 1600 |
+
gr.Markdown("""
|
| 1601 |
+
## 🏢 AI Plan Reader
|
| 1602 |
+
### Upload your subdivision plan to automatically extract lot information
|
| 1603 |
+
|
| 1604 |
+
**Workflow:**
|
| 1605 |
+
1. Upload plan → 2. Review/edit extracted data → 3. Send to optimizer → 4. Optimize layout
|
| 1606 |
+
""")
|
| 1607 |
+
|
| 1608 |
+
with gr.Row():
|
| 1609 |
+
with gr.Column(scale=1):
|
| 1610 |
+
plan_upload = gr.File(
|
| 1611 |
+
label="Upload Subdivision Plan",
|
| 1612 |
+
file_types=["image", "pdf"],
|
| 1613 |
+
type="filepath"
|
| 1614 |
+
)
|
| 1615 |
+
|
| 1616 |
+
gr.Markdown("""
|
| 1617 |
+
**Supported Formats:**
|
| 1618 |
+
- PDF plans
|
| 1619 |
+
- PNG/JPG images
|
| 1620 |
+
- CAD exports
|
| 1621 |
+
|
| 1622 |
+
**Best Results:**
|
| 1623 |
+
- High resolution (300+ DPI)
|
| 1624 |
+
- Clear lot numbers
|
| 1625 |
+
- Visible frontage dimensions
|
| 1626 |
+
- North arrow included
|
| 1627 |
+
""")
|
| 1628 |
+
|
| 1629 |
+
process_plan_btn = gr.Button(
|
| 1630 |
+
"🔍 Analyze Plan",
|
| 1631 |
+
variant="primary",
|
| 1632 |
+
size="lg"
|
| 1633 |
+
)
|
| 1634 |
+
|
| 1635 |
+
# Analysis options
|
| 1636 |
+
with gr.Group():
|
| 1637 |
+
gr.Markdown("**Analysis Settings**")
|
| 1638 |
+
scale_input = gr.Number(
|
| 1639 |
+
label="Scale (1:X)",
|
| 1640 |
+
value=1000,
|
| 1641 |
+
info="Drawing scale ratio"
|
| 1642 |
+
)
|
| 1643 |
+
|
| 1644 |
+
auto_detect_scale = gr.Checkbox(
|
| 1645 |
+
label="Auto-detect scale from plan",
|
| 1646 |
+
value=True
|
| 1647 |
+
)
|
| 1648 |
+
|
| 1649 |
+
confidence_threshold = gr.Slider(
|
| 1650 |
+
label="Detection Confidence",
|
| 1651 |
+
minimum=0.5,
|
| 1652 |
+
maximum=0.95,
|
| 1653 |
+
value=0.75,
|
| 1654 |
+
step=0.05,
|
| 1655 |
+
info="Higher = more accurate but may miss some lots"
|
| 1656 |
+
)
|
| 1657 |
+
|
| 1658 |
+
with gr.Column(scale=2):
|
| 1659 |
+
# Preview with annotations
|
| 1660 |
+
plan_preview = gr.Image(
|
| 1661 |
+
label="Analyzed Plan Preview",
|
| 1662 |
+
type="numpy"
|
| 1663 |
+
)
|
| 1664 |
+
|
| 1665 |
+
analysis_status = gr.Markdown(
|
| 1666 |
+
value="Upload a plan to begin analysis",
|
| 1667 |
+
label="Analysis Status"
|
| 1668 |
+
)
|
| 1669 |
+
|
| 1670 |
+
# Results section
|
| 1671 |
+
gr.Markdown("### 📊 Extracted Lot Data")
|
| 1672 |
+
|
| 1673 |
+
with gr.Row():
|
| 1674 |
+
extracted_data = gr.DataFrame(
|
| 1675 |
+
headers=["Lot #", "Frontage (m)", "Depth (m)", "Area (m²)", "Type"],
|
| 1676 |
+
label="Detected Lots",
|
| 1677 |
+
interactive=True
|
| 1678 |
+
)
|
| 1679 |
+
|
| 1680 |
+
with gr.Column():
|
| 1681 |
+
extraction_summary = gr.Markdown(
|
| 1682 |
+
label="Extraction Summary"
|
| 1683 |
+
)
|
| 1684 |
+
|
| 1685 |
+
export_btn = gr.Button(
|
| 1686 |
+
"📥 Export to CSV",
|
| 1687 |
+
variant="secondary"
|
| 1688 |
+
)
|
| 1689 |
+
|
| 1690 |
+
send_to_optimizer_btn = gr.Button(
|
| 1691 |
+
"➡️ Send to Optimizer",
|
| 1692 |
+
variant="primary"
|
| 1693 |
+
)
|
| 1694 |
+
|
| 1695 |
+
# Manual correction section
|
| 1696 |
+
gr.Markdown("### ✏️ Manual Corrections")
|
| 1697 |
+
with gr.Row():
|
| 1698 |
+
with gr.Column():
|
| 1699 |
+
gr.Markdown("""
|
| 1700 |
+
**Quick Edit Tools:**
|
| 1701 |
+
- Double-click cells to edit
|
| 1702 |
+
- Add missing lots manually
|
| 1703 |
+
- Correct misread numbers
|
| 1704 |
+
- Adjust frontages
|
| 1705 |
+
""")
|
| 1706 |
+
|
| 1707 |
+
add_lot_btn = gr.Button("➕ Add Lot", size="sm")
|
| 1708 |
+
|
| 1709 |
+
with gr.Column():
|
| 1710 |
+
validation_result = gr.Markdown(
|
| 1711 |
+
label="Data Validation"
|
| 1712 |
+
)
|
| 1713 |
+
|
| 1714 |
+
export_output = gr.Textbox(
|
| 1715 |
+
label="CSV Export (Copy and save as .csv file)",
|
| 1716 |
+
lines=10,
|
| 1717 |
+
visible=False
|
| 1718 |
+
)
|
| 1719 |
+
|
| 1720 |
+
# Wire up the buttons
|
| 1721 |
+
optimize_btn.click(
|
| 1722 |
+
optimize_grid,
|
| 1723 |
+
inputs=[
|
| 1724 |
+
stage_width,
|
| 1725 |
+
stage_depth,
|
| 1726 |
+
enable_8_5, enable_10_5, enable_12_5, enable_14, enable_16, enable_18,
|
| 1727 |
+
enable_corners, enable_11, enable_13_3, enable_14_8, enable_16_8,
|
| 1728 |
+
allow_custom_corners, optimization_strategy, color_scheme
|
| 1729 |
+
],
|
| 1730 |
+
outputs=[plot_2d, results_table, summary_output, report_output, manual_widths]
|
| 1731 |
+
)
|
| 1732 |
+
|
| 1733 |
+
update_btn.click(
|
| 1734 |
+
update_manual_adjustment,
|
| 1735 |
+
inputs=[manual_widths, stage_width, stage_depth, color_scheme],
|
| 1736 |
+
outputs=[plot_2d, adjustment_feedback]
|
| 1737 |
+
)
|
| 1738 |
+
|
| 1739 |
+
# Plan reader functions
|
| 1740 |
+
def process_uploaded_plan(file_path, scale, auto_detect, confidence):
|
| 1741 |
+
if not file_path:
|
| 1742 |
+
return None, pd.DataFrame(), "Please upload a plan file"
|
| 1743 |
+
|
| 1744 |
+
preview, lot_data, status = optimizer.process_plan_image(
|
| 1745 |
+
file_path, scale, auto_detect, confidence
|
| 1746 |
+
)
|
| 1747 |
+
|
| 1748 |
+
if lot_data:
|
| 1749 |
+
df = optimizer.lot_data_to_dataframe(lot_data)
|
| 1750 |
+
return preview, df, status
|
| 1751 |
+
else:
|
| 1752 |
+
return preview, pd.DataFrame(), status
|
| 1753 |
+
|
| 1754 |
+
def export_to_csv(df):
|
| 1755 |
+
if df is None or df.empty:
|
| 1756 |
+
return gr.update(visible=False), "No data to export"
|
| 1757 |
+
|
| 1758 |
+
csv_content = optimizer.export_lot_data_to_csv(df)
|
| 1759 |
+
return gr.update(value=csv_content, visible=True), "✅ CSV data ready - copy and save as .csv file"
|
| 1760 |
+
|
| 1761 |
+
def send_to_optimizer(df):
|
| 1762 |
+
if df is None or df.empty:
|
| 1763 |
+
return 0, 32, "No data to send"
|
| 1764 |
+
|
| 1765 |
+
width, depth = optimizer.convert_lot_data_to_stage_format(df)
|
| 1766 |
+
return width, depth, f"✅ Stage dimensions set to {width:.1f}m × {depth:.1f}m\nSwitch to 'AI Optimization' tab to continue"
|
| 1767 |
+
|
| 1768 |
+
def validate_lot_data(df):
|
| 1769 |
+
if df is None or df.empty:
|
| 1770 |
+
return "No data to validate"
|
| 1771 |
+
|
| 1772 |
+
# Check for common issues
|
| 1773 |
+
issues = []
|
| 1774 |
+
|
| 1775 |
+
# Check for missing values
|
| 1776 |
+
if df.isnull().any().any():
|
| 1777 |
+
issues.append("⚠️ Missing values detected")
|
| 1778 |
+
|
| 1779 |
+
# Check for unrealistic dimensions
|
| 1780 |
+
if (df['Frontage (m)'] < 6).any():
|
| 1781 |
+
issues.append("⚠️ Some lots have frontage < 6m")
|
| 1782 |
+
if (df['Frontage (m)'] > 30).any():
|
| 1783 |
+
issues.append("⚠️ Some lots have frontage > 30m")
|
| 1784 |
+
|
| 1785 |
+
# Check total lots
|
| 1786 |
+
total_lots = len(df)
|
| 1787 |
+
if total_lots < 5:
|
| 1788 |
+
issues.append("ℹ️ Few lots detected - check if all were found")
|
| 1789 |
+
|
| 1790 |
+
if not issues:
|
| 1791 |
+
return f"✅ Data looks good! {total_lots} lots ready for optimization"
|
| 1792 |
+
else:
|
| 1793 |
+
return "\n".join(issues)
|
| 1794 |
+
|
| 1795 |
+
def add_lot_row(df):
|
| 1796 |
+
if df is None or df.empty:
|
| 1797 |
+
new_row = pd.DataFrame({
|
| 1798 |
+
"Lot #": ["L1"],
|
| 1799 |
+
"Frontage (m)": [12.5],
|
| 1800 |
+
"Depth (m)": [32.0],
|
| 1801 |
+
"Area (m²)": [400.0],
|
| 1802 |
+
"Type": ["Standard"]
|
| 1803 |
+
})
|
| 1804 |
+
return new_row
|
| 1805 |
+
else:
|
| 1806 |
+
last_lot_num = len(df) + 1
|
| 1807 |
+
new_row = pd.DataFrame({
|
| 1808 |
+
"Lot #": [f"L{last_lot_num}"],
|
| 1809 |
+
"Frontage (m)": [12.5],
|
| 1810 |
+
"Depth (m)": [32.0],
|
| 1811 |
+
"Area (m²)": [400.0],
|
| 1812 |
+
"Type": ["Standard"]
|
| 1813 |
+
})
|
| 1814 |
+
return pd.concat([df, new_row], ignore_index=True)
|
| 1815 |
+
|
| 1816 |
+
process_plan_btn.click(
|
| 1817 |
+
process_uploaded_plan,
|
| 1818 |
+
inputs=[plan_upload, scale_input, auto_detect_scale, confidence_threshold],
|
| 1819 |
+
outputs=[plan_preview, extracted_data, analysis_status]
|
| 1820 |
+
)
|
| 1821 |
+
|
| 1822 |
+
export_btn.click(
|
| 1823 |
+
export_to_csv,
|
| 1824 |
+
inputs=[extracted_data],
|
| 1825 |
+
outputs=[export_output, extraction_summary]
|
| 1826 |
+
)
|
| 1827 |
+
|
| 1828 |
+
send_to_optimizer_btn.click(
|
| 1829 |
+
send_to_optimizer,
|
| 1830 |
+
inputs=[extracted_data],
|
| 1831 |
+
outputs=[stage_width, stage_depth, extraction_summary]
|
| 1832 |
+
)
|
| 1833 |
+
|
| 1834 |
+
extracted_data.change(
|
| 1835 |
+
validate_lot_data,
|
| 1836 |
+
inputs=[extracted_data],
|
| 1837 |
+
outputs=[validation_result]
|
| 1838 |
+
)
|
| 1839 |
+
|
| 1840 |
+
add_lot_btn.click(
|
| 1841 |
+
add_lot_row,
|
| 1842 |
+
inputs=[extracted_data],
|
| 1843 |
+
outputs=[extracted_data]
|
| 1844 |
+
)
|
| 1845 |
+
|
| 1846 |
+
return demo
|
| 1847 |
+
|
| 1848 |
+
# Create and launch
|
| 1849 |
+
if __name__ == "__main__":
|
| 1850 |
+
app = create_advanced_app()
|
| 1851 |
+
app.launch()
|