C3D-HCWA-POLY · HCWA migration 3/5: close polygons + build EOP done
Close sewer easement polylines (gaps are an explicit rejection criterion) and build ROAD_EDGE_OF_PAVEMENT as a closed polygon from the 8 surveyed EP points. TBC back-of-curb deliberately excluded per HCWA 'not back of curb'.
C# payload
// HCWA migration 3/5: POLYGON CLOSURE. "All Polygon type features must be completely closed.
// Gaps in polygon boundaries will not be accepted." Closes sewer easements, and builds
// ROAD_EDGE_OF_PAVEMENT from the surveyed EP points (HCWA: EOP is "not back of curb", so the
// TBC curb linework is deliberately NOT used here).
var bt = (BlockTable)Tx.GetObject(Db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)Tx.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
var lt = (LayerTable)Tx.GetObject(Db.LayerTableId, OpenMode.ForRead);
foreach (var n in new[]{"SEWER_EASEMENT","ROAD_EDGE_OF_PAVEMENT"})
if (!lt.Has(n)) { Log($"ABORT: layer '{n}' missing — run C3D-HCWALAYERS first."); return; }
// --- 1) close any open polyline already on SEWER_EASEMENT ---
int closed = 0;
foreach (ObjectId id in ms) {
var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is Polyline pl && pl.Layer == "SEWER_EASEMENT" && !pl.Closed && pl.NumberOfVertices >= 3) {
var w = (Polyline)Tx.GetObject(id, OpenMode.ForWrite);
w.Closed = true;
var chk = (Polyline)Tx.GetObject(id, OpenMode.ForRead);
if (chk.Closed) { closed++; Log($" closed easement polyline v={chk.NumberOfVertices} len={chk.Length:F1}'"); }
}
}
Log($"Sewer easements closed: {closed}");
// --- 2) build ROAD_EDGE_OF_PAVEMENT from surveyed EP points ---
// Idempotent: skip if an EOP polygon already exists.
bool haveEop = false;
foreach (ObjectId id in ms) {
var e = Tx.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e != null && e.Layer == "ROAD_EDGE_OF_PAVEMENT") { haveEop = true; break; }
}
if (haveEop) { Log("ROAD_EDGE_OF_PAVEMENT already populated — skipping (idempotent)."); return; }
var cd = CivilDoc;
var ep = new List<(uint num, Point2d p)>();
foreach (ObjectId id in cd.CogoPoints) {
var cp = (Autodesk.Civil.DatabaseServices.CogoPoint)Tx.GetObject(id, OpenMode.ForRead);
var u = (cp.RawDescription ?? "").Trim().ToUpperInvariant();
if (u == "EP" || u == "EP TBC") ep.Add((cp.PointNumber, new Point2d(cp.Easting, cp.Northing)));
}
if (ep.Count < 3) { Log($"only {ep.Count} EP points — cannot close a polygon"); return; }
// EP points fall in two north/south bands (two sides of the pavement). Split on the midpoint of
// the N range, order each band by Easting, then walk out along one band and back along the other
// so the ring never self-crosses.
double nMin = ep.Min(x => x.p.Y), nMax = ep.Max(x => x.p.Y), nMid = (nMin + nMax) / 2.0;
var south = ep.Where(x => x.p.Y <= nMid).OrderBy(x => x.p.X).ToList();
var north = ep.Where(x => x.p.Y > nMid).OrderByDescending(x => x.p.X).ToList();
Log($"EP bands: south={south.Count} (N<={nMid:F1}), north={north.Count}");
if (south.Count == 0 || north.Count == 0) { Log("EP points not in two bands — needs manual review"); return; }
var ring = new List<(uint num, Point2d p)>();
ring.AddRange(south); ring.AddRange(north);
var poly = new Polyline();
for (int i = 0; i < ring.Count; i++) poly.AddVertexAt(i, ring[i].p, 0, 0, 0);
poly.Closed = true; // HCWA: must be a closed polygon
poly.Layer = "ROAD_EDGE_OF_PAVEMENT";
ms.AppendEntity(poly); Tx.AddNewlyCreatedDBObject(poly, true);
var v = (Polyline)Tx.GetObject(poly.ObjectId, OpenMode.ForRead);
Log($"ROAD_EDGE_OF_PAVEMENT: closed={v.Closed} v={v.NumberOfVertices} perim={v.Length:F1}' area={v.Area:F0} sf (verified)");
Log(" ring order: " + string.Join(" ", ring.Select(x => x.num)));
Log("NOTE: built from EP shots only. TBC (back of curb) intentionally excluded per HCWA.");
Result
Log
closed easement polyline v=3 len=452.3' closed easement polyline v=6 len=1020.4' closed easement polyline v=4 len=481.7' Sewer easements closed: 3 EP bands: south=6 (N<=1280321.6), north=2 ROAD_EDGE_OF_PAVEMENT: closed=True v=8 perim=429.2' area=9221 sf (verified) ring order: 1047 1049 1048 1027 1026 1003 1014 1016 NOTE: built from EP shots only. TBC (back of curb) intentionally excluded per HCWA.
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.