Extract 23 stormwater structures / 22 pipes and 18 sewer manholes / 17 pipes with sizes, inverts, ground levels and positions from the C3277 long-section tables (superseded sewer copies dropped by matching the issued PDFs). New model folder 2026-10-21. Map 3 draws them as round pipes and manholes, adds status popups for pipes and manholes, and reuses Map 2's services panel; water stays plan routes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
51 lines
2.3 KiB
Python
51 lines
2.3 KiB
Python
import json
|
|
import math
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2] / "reports/site-models/cedar-gate/2026-10-21"
|
|
|
|
|
|
def seg_dist(p, a, b):
|
|
dx, dy = b[0] - a[0], b[1] - a[1]
|
|
l2 = dx * dx + dy * dy
|
|
t = 0 if not l2 else max(0, min(1, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / l2))
|
|
return math.hypot(p[0] - a[0] - t * dx, p[1] - a[1] - t * dy)
|
|
|
|
|
|
class CedarGateServices(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.s = json.loads((ROOT / "services.json").read_text())
|
|
cls.report = json.loads((ROOT / "services-extraction.json").read_text())
|
|
|
|
def test_pipes_join_known_structures_and_fall_downhill(self):
|
|
sw = {x["name"] for x in self.s["stormwater"]["structures"]}
|
|
for p in self.s["stormwater"]["pipes"]:
|
|
self.assertIn(p["from"], sw); self.assertIn(p["to"], sw)
|
|
self.assertGreaterEqual(p["ilFrom"], p["ilTo"]); self.assertGreater(p["length"], 0); self.assertGreater(p["dia"], 0)
|
|
sr = {x["id"] for x in self.s["sewer"]["manholes"]}
|
|
for p in self.s["sewer"]["pipes"]:
|
|
self.assertIn(p["from"], sr); self.assertIn(p["to"], sr)
|
|
self.assertGreaterEqual(p["ilFrom"], p["ilTo"])
|
|
|
|
def test_sizes_match_the_plan_labels(self):
|
|
dia = sorted(p["dia"] for p in self.s["stormwater"]["pipes"] if p["run"] == 1)
|
|
self.assertEqual(dia, [675, 750, 750, 825, 1350, 1350, 1500, 1500, 1500]) # the eight SW-M1 plan labels plus the short outlet pipe
|
|
self.assertTrue(all(p["dia"] in (160, 250) for p in self.s["sewer"]["pipes"]))
|
|
|
|
def test_structures_sit_on_their_plan_routes(self):
|
|
routes = [r for r in self.s["planRoutes"] if r["service"] == "stormwater"]
|
|
for q in self.s["stormwater"]["structures"]:
|
|
d = min(seg_dist((q["e"], q["n"]), r["pts"][i], r["pts"][i + 1]) for r in routes for i in range(len(r["pts"]) - 1))
|
|
self.assertLess(d, 3.5, q["name"])
|
|
|
|
def test_superseded_sewer_copies_are_dropped(self):
|
|
titles = [r["title"] for r in self.report["runs"] if r["kind"] == "sewer"]
|
|
self.assertEqual(sorted(titles), ["SE-2", "SE-3", "SE-M1"])
|
|
self.assertTrue(all(r["verifiedOnIssuedSheets"] for r in self.report["runs"] if r["kind"] == "sewer"))
|
|
self.assertEqual(len(self.report["discarded"]), 3)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|