C3D-QACHECK · HCWA QA sweep (read-only) done
Verify layers, polygon closure, sewer tangents, text type and symbology against the HCWA standard
C# payload
// Read-only QA sweep against the HCWA As-built CAD Standards. Checks the mechanical, verifiable
// items so the worksheet reflects the drawing rather than assumptions.
var bt = (BlockTable)Tx.GetObject(Db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)Tx.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
var lt = (LayerTable)Tx.GetObject(Db.LayerTableId, OpenMode.ForRead);
Log($"UNITS: INSUNITS={Db.Insunits}");
// 1) required HCWA layers present + populated?
string[] req = { "SEWER_LINE","SEWER_MANHOLE","SEWER_MANHOLE_TEXT","SEWER_LINE_TEXT","SEWER_EASEMENT",
"SEWER_TAP","SEWER_END_OF_LINE","WATER_LINE","WATER_LINE_TEXT","WATER_VALVE","WATER_VALVE_TEXT",
"FIRE_HYDRANT","FIRE_HYDRANT_TEXT","FIRE_HYDRANT_VALVE","WATER_METER","WATER_VAULT","WATER_SERVICE",
"WATER_END_CAP","WATER_EASEMENT","ROAD_EDGE_OF_PAVEMENT","ROAD_RIGHT-OF-WAY","ROAD_TEXT",
"PROJECT_BOUNDARY","PROPERTY_LINE","ADDRESS_TEXT","LOT_NUMBER_TEXT",
"HORIZONTAL_AND_VERTICAL_CONTROL_POINT","UTILITY_EASEMENT" };
var count = new Dictionary<string,int>();
foreach (ObjectId id in ms) {
var e = Tx.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e == null) continue;
count[e.Layer] = count.TryGetValue(e.Layer, out var n) ? n+1 : 1;
}
Log("--- required layers ---");
foreach (var r in req) {
bool exists = lt.Has(r);
int c = count.TryGetValue(r, out var n) ? n : 0;
Log($" {r,-38} exists={exists,-5} entities={c}");
}
// 2) polygon closure on poly-type layers
Log("--- polygon closure (HCWA: polygons must be closed) ---");
string[] polyLayers = { "ROAD_EDGE_OF_PAVEMENT","ROAD_RIGHT-OF-WAY","SEWER_EASEMENT","WATER_EASEMENT",
"PROJECT_BOUNDARY","PROPERTY_LINE","UTILITY_EASEMENT","WATER_VAULT" };
foreach (var L in polyLayers) {
int open = 0, closed = 0;
foreach (ObjectId id in ms) {
var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is Polyline p && p.Layer == L) { if (p.Closed) closed++; else open++; }
}
if (open + closed > 0) Log($" {L,-28} closed={closed} OPEN={open}{(open>0?" <== FAIL":"")}");
}
// 3) sewer tangents: one line per tangent + endpoints on manhole centres
var mh = new List<Point2d>();
foreach (ObjectId id in ms) { var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is Circle c && c.Layer == "SEWER_MANHOLE") mh.Add(new Point2d(c.Center.X, c.Center.Y)); }
int tang = 0, multi = 0, snapped = 0, loose = 0;
foreach (ObjectId id in ms) {
var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is Polyline p && p.Layer == "SEWER_LINE" && p.NumberOfVertices > 2) multi++;
if (!(o is Line l) || l.Layer != "SEWER_LINE") continue;
tang++;
foreach (var pt in new[]{ l.StartPoint, l.EndPoint }) {
double bd = double.MaxValue;
foreach (var m in mh) { double d = new Point2d(pt.X, pt.Y).GetDistanceTo(m); if (d < bd) bd = d; }
if (bd < 0.01) snapped++; else loose++;
}
}
Log($"--- sewer ---");
Log($" manholes symbolised: {mh.Count}");
Log($" tangents (Line): {tang}, multi-tangent polylines remaining: {multi}");
Log($" endpoints exactly on a manhole: {snapped}, not snapped: {loose}");
// 4) text: any MText left on HCWA layers? (HCWA wants single-line text)
int mtext = 0;
foreach (ObjectId id in ms) {
var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is MText m2) {
var u = m2.Layer.ToUpperInvariant();
if (u.StartsWith("SEWER")||u.StartsWith("WATER")||u.StartsWith("ROAD")||u.StartsWith("FIRE")||u.StartsWith("ADDRESS")) mtext++;
}
}
Log($"--- text ---");
Log($" MText still on HCWA layers (should be 0): {mtext}");
// 5) symbols: HCWA wants inserts, not circles
int circMh = 0, circWat = 0, blocks = 0;
foreach (ObjectId id in ms) {
var o = Tx.GetObject(id, OpenMode.ForRead);
if (o is Circle c) {
if (c.Layer == "SEWER_MANHOLE") circMh++;
if (c.Layer.StartsWith("WATER") || c.Layer.StartsWith("FIRE")) circWat++;
}
if (o is BlockReference) blocks++;
}
Log($"--- symbology (HCWA wants Inserts) ---");
Log($" manholes drawn as Circle: {circMh} water/hydrant as Circle: {circWat} BlockReferences: {blocks}");
Result
Log
UNITS: INSUNITS=Feet --- required layers --- SEWER_LINE exists=True entities=12 SEWER_MANHOLE exists=True entities=10 SEWER_MANHOLE_TEXT exists=True entities=20 SEWER_LINE_TEXT exists=True entities=7 SEWER_EASEMENT exists=True entities=3 SEWER_TAP exists=True entities=0 SEWER_END_OF_LINE exists=True entities=0 WATER_LINE exists=True entities=4 WATER_LINE_TEXT exists=True entities=11 WATER_VALVE exists=True entities=1 WATER_VALVE_TEXT exists=True entities=5 FIRE_HYDRANT exists=True entities=2 FIRE_HYDRANT_TEXT exists=True entities=3 FIRE_HYDRANT_VALVE exists=True entities=2 WATER_METER exists=True entities=0 WATER_VAULT exists=True entities=2 WATER_SERVICE exists=True entities=6 WATER_END_CAP exists=True entities=1 WATER_EASEMENT exists=True entities=0 ROAD_EDGE_OF_PAVEMENT exists=True entities=1 ROAD_RIGHT-OF-WAY exists=True entities=0 ROAD_TEXT exists=True entities=1 PROJECT_BOUNDARY exists=True entities=0 PROPERTY_LINE exists=True entities=0 ADDRESS_TEXT exists=True entities=0 LOT_NUMBER_TEXT exists=True entities=0 HORIZONTAL_AND_VERTICAL_CONTROL_POINT exists=True entities=0 UTILITY_EASEMENT exists=True entities=0 --- polygon closure (HCWA: polygons must be closed) --- ROAD_EDGE_OF_PAVEMENT closed=1 OPEN=0 WATER_VAULT closed=2 OPEN=0 --- sewer --- manholes symbolised: 10 tangents (Line): 12, multi-tangent polylines remaining: 0 endpoints exactly on a manhole: 16, not snapped: 8 --- text --- MText still on HCWA layers (should be 0): 0 --- symbology (HCWA wants Inserts) --- manholes drawn as Circle: 10 water/hydrant as Circle: 12 BlockReferences: 1
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.