C3D-MATCHLBL · Match bearing labels to TRACT A/B segments (read-only) error
Assign each bearing label to the tract boundary segment it annotates (A parcel exploded, B polyline). Reports handle+layer+tract+distance.
C# payload
// READ-ONLY: match each bearing label to the tract boundary it annotates.
// Build A and B boundary segment lists (A = Parcel segments; B = Polyline segments).
// For every bearing-type label (GeneralSegmentLabel/NoteLabel + MText/DBText with bearing chars),
// find the nearest boundary segment across A and B; assign to that tract if within ~15'. Report each
// label: handle, type, layer, assigned tract, distance.
using System;
using System.Linq;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.Civil.ApplicationServices;
using Autodesk.Civil.DatabaseServices;
var db = Db;
// collect boundary segments as (p1,p2) for A and B
var segA = new List<(Point3d a, Point3d b)>();
var segB = new List<(Point3d a, Point3d b)>();
using (var tr = db.TransactionManager.StartTransaction())
{
var ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead);
// B: polyline on SS-BOUNDARY TRACT B
foreach (ObjectId id in ms)
{
var e = tr.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e is Polyline pl && string.Equals(e.Layer,"SS-BOUNDARY TRACT B",StringComparison.OrdinalIgnoreCase))
{
int n = pl.NumberOfVertices;
for (int i=0;i<n;i++){ var p1=pl.GetPoint3dAt(i); var p2=pl.GetPoint3dAt((i+1)%n); segB.Add((p1,p2)); }
}
}
// A: Parcel on SS-BOUNDARY TRACT A — explode to component curves for segment geometry.
var cdoc = CivilDocument.GetCivilDocument(db);
foreach (ObjectId sid in cdoc.GetSiteIds())
{
var site=(Site)tr.GetObject(sid,OpenMode.ForRead);
foreach (ObjectId pid in site.GetParcelIds())
{
var p=(Parcel)tr.GetObject(pid,OpenMode.ForRead);
if (!string.Equals(p.Layer,"SS-BOUNDARY TRACT A",StringComparison.OrdinalIgnoreCase)) continue;
var dbo = new DBObjectCollection();
try { p.Explode(dbo); } catch (System.Exception ex) { Log($"parcel A explode note: {ex.Message}"); }
foreach (DBObject o in dbo)
{
if (o is Line l) segA.Add((l.StartPoint, l.EndPoint));
else if (o is Arc ar) { segA.Add((ar.StartPoint, ar.EndPoint)); }
else if (o is Polyline pp){ int n=pp.NumberOfVertices; for(int i=0;i<n-1;i++) segA.Add((pp.GetPoint3dAt(i),pp.GetPoint3dAt(i+1))); }
}
}
}
Log($"segA(Parcel)={segA.Count} segB(Polyline)={segB.Count}");
double DistToSeg(Point3d pt, Point3d a, Point3d b)
{
var ab=b-a; double len2=ab.LengthSqrd; if(len2==0) return pt.DistanceTo(a);
double t=((pt-a).DotProduct(ab))/len2; t=Math.Max(0,Math.Min(1,t));
var proj=a+ab*t; return pt.DistanceTo(proj);
}
int aN=0,bN=0,un=0;
foreach (ObjectId id in ms)
{
var e = tr.GetObject(id, OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Entity;
if (e==null) continue;
string tn=e.GetType().Name;
bool cand = tn=="GeneralSegmentLabel" || tn=="NoteLabel";
string s=null; if(e is MText m) s=m.Text; else if(e is DBText t) s=t.TextString;
if((tn=="MText"||tn=="DBText") && s!=null && (s.Contains("°")||s.Contains("\""))) cand=true;
if(!cand) continue;
Point3d pos; try{ var ex=e.GeometricExtents; pos=new Point3d((ex.MinPoint.X+ex.MaxPoint.X)/2,(ex.MinPoint.Y+ex.MaxPoint.Y)/2,0);}catch{continue;}
double dA=segA.Count>0?segA.Min(sg=>DistToSeg(pos,sg.a,sg.b)):9e9;
double dB=segB.Count>0?segB.Min(sg=>DistToSeg(pos,sg.a,sg.b)):9e9;
string who; double d;
if(Math.Min(dA,dB)>40){ who="(none)"; d=Math.Min(dA,dB); un++; }
else if(dA<=dB){ who="A"; d=dA; aN++; } else { who="B"; d=dB; bN++; }
Log($" {tn} h={e.Handle} layer='{e.Layer}' -> {who} (d={d:0.0}')");
}
Log($"ASSIGNED: A={aN} B={bN} none={un}");
tr.Commit();
}
Result
Notes
What worked, what didn't, job-specific gotchas — flagged notes feed the recipes.
No notes yet.