buildinves commited on
Commit
3a33624
·
verified ·
1 Parent(s): 3247bda

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +531 -1
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()