The solution is to search drawing to find all labels that associate to the polyline/curve to be split, and save these labels' information for later need (when re-creating labels). So, I defined this class SegmentLabelInfo:
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace Civil3DLabels
{
public enum SegmentLabelType
{
LineLabel=0,
CurveLabel=1,
}
public class SegmentLabelInfo
{
public ObjectId LabelStyleId { set; get; }
public string LabelLayer { set; get; }
public Point3d LabelPosition { set; get; }
public SegmentLabelType LabelType { set; get; }
public SegmentLabelInfo()
{
LabelStyleId = ObjectId.Null;
LabelLayer = "";
LabelPosition = Point3d.Origin;
LabelType = SegmentLabelType.LineLabel;
}
public bool IsOnCurve(ObjectId curveId, out double ratio)
{
ratio = 0.5;
bool onCurve = false;
Point3d pt;
using (var tran =
curveId.Database.TransactionManager.StartTransaction())
{
var curve = (Curve)tran.GetObject(
curveId, OpenMode.ForRead);
pt = curve.GetClosestPointTo(LabelPosition, false);
//If the label anchored on this curve
var dist = LabelPosition.DistanceTo(pt);
onCurve = dist <= Tolerance.Global.EqualPoint;
//the distance from curve's start point to anchor point
dist = curve.GetDistAtPoint(pt);
ratio = dist / curve.GetDistAtPoint(curve.EndPoint);
tran.Commit();
}
return onCurve;
}
}
}
As the code shows, besides the 4 public properties used to store a label's information, a method IsOnCurve() is also defined to determine if the label sits on one of the segment split from the original polyline/curve; if yes, what location (distance to the start point) the label is at (output parameter ratio).
Now, here is the updated working code, where I changed the class name from PolylineSplitter to MyPolylineSplitter:
using System.Collections.Generic;
using System.Linq;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using CadDb = Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using CivilApp = Autodesk.Civil.ApplicationServices.CivilApplication;
using Civil = Autodesk.Civil;
using CivilDb = Autodesk.Civil.DatabaseServices;
using Autodesk.Civil.DatabaseServices.Styles;
[assembly: CommandClass(typeof(Civil3DLabels.MyPolylineSplitter))]
namespace Civil3DLabels
{
public class MyPolylineSplitter
{
private const string DXF_LABEL = "AECC_GENERAL_SEGMENT_LABEL";
[CommandMethod("ExplodePoly")]
public void DoSplit()
{
var dwg = CadApp.DocumentManager.MdiActiveDocument;
var ed = dwg.Editor;
try
{
Split(dwg);
}
catch (System.Exception ex)
{
ed.WriteMessage("\nError: {0}", ex.Message);
ed.WriteMessage("\n*Cancel*");
}
Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
}
public void Split(Document dwg)
{
CadDb.ObjectId polyId = SelectPolyline(dwg.Editor);
if (polyId.IsNull) return;
//Collect all labels' information that associate with the polyline
IEnumerable<SegmentLabelInfo> labels = FindAssociatedLabels(polyId);
//Get default line/curve label style IDs
var defaultLineLabelStyleId = CadDb.ObjectId.Null;
var defaultCurveLabelStyleId = CadDb.ObjectId.Null;
if (labels.Count() > 0)
{
GetSettingsCmdAddSegmentLabelDefaultStyles(
out defaultLineLabelStyleId, out defaultCurveLabelStyleId);
}
using (var tran = dwg.TransactionManager.StartTransaction())
{
var poly = (CadDb.Polyline)tran.GetObject(
polyId, CadDb.OpenMode.ForRead);
//Find current space
var curSpace = (CadDb.BlockTableRecord)tran.GetObject(
poly.OwnerId, CadDb.OpenMode.ForRead);
//Newly created entities because of the splitting
var newIds = new List<CadDb.ObjectId>();
//Get split segments and add them into current space
using (var ents = GetSplitCurvesFromPolyline(poly))
{
if (ents.Count > 1)
{
curSpace.UpgradeOpen();
foreach (CadDb.DBObject ent in ents)
{
var id = curSpace.AppendEntity(ent as CadDb.Entity);
tran.AddNewlyCreatedDBObject(ent, true);
newIds.Add(id);
}
// Erase the polyline,
// Associated labels would also be gone
poly.UpgradeOpen();
poly.Erase(true);
}
else
{
ents[0].Dispose();
}
}
//Add label to each newly added entities
if (newIds.Count>0 && labels.Count()>0)
{
foreach (var entId in newIds)
{
AddSegmentLabel(
entId,
labels,
defaultLineLabelStyleId,
defaultCurveLabelStyleId,
tran);
}
}
tran.Commit();
}
}
#region private methods
private CadDb.ObjectId SelectPolyline(Editor ed)
{
var opt = new PromptEntityOptions(
"\nSelect a polyline:");
opt.SetRejectMessage("\nInvalid: not a polyline.");
opt.AddAllowedClass(typeof(CadDb.Polyline), true);
var res = ed.GetEntity(opt);
if (res.Status==PromptStatus.OK)
{
return res.ObjectId;
}
else
{
return CadDb.ObjectId.Null;
}
}
private CadDb.DBObjectCollection GetSplitCurvesFromPolyline(
CadDb.Polyline poly)
{
var points = new Point3dCollection();
for (int i = 0; i < poly.NumberOfVertices; i++)
{
points.Add(poly.GetPoint3dAt(i));
}
var dbObjects = poly.GetSplitCurves(points);
return dbObjects;
}
private IEnumerable<SegmentLabelInfo> FindAssociatedLabels(
CadDb.ObjectId polyId)
{
var labels = new List<SegmentLabelInfo>();
CadDb.Database db = polyId.Database;
using (var tran = db.TransactionManager.StartTransaction())
{
var ent = (CadDb.Entity)tran.GetObject(
polyId, CadDb.OpenMode.ForRead);
var space = (CadDb.BlockTableRecord)tran.GetObject(
ent.OwnerId, CadDb.OpenMode.ForRead);
foreach (CadDb.ObjectId id in space)
{
if (id.ObjectClass.DxfName.ToUpper() == DXF_LABEL)
{
var label = tran.GetObject(
id, CadDb.OpenMode.ForRead) as CivilDb.Label;
if (label != null && label.FeatureId==polyId)
{
var info = new SegmentLabelInfo()
{
LabelStyleId = label.StyleId,
LabelLayer = label.Layer,
LabelPosition = label.AnchorInfo.Location
};
// Since each label uses either line label style or curve label style
// We test if the label style is line label style.
// if not, the label style must be curve label style
var lineStyles =
CivilApp.ActiveDocument.Styles.LabelStyles.GeneralLineLabelStyles;
info.LabelType = IsSegmentLabelStyle(label.StyleId, lineStyles, tran) ?
SegmentLabelType.LineLabel : SegmentLabelType.CurveLabel;
labels.Add(info);
}
}
}
tran.Commit();
}
return labels;
}
private void AddSegmentLabel(
CadDb.ObjectId entId,
IEnumerable<SegmentLabelInfo> labels,
CadDb.ObjectId defaultLineLabelStyleId,
CadDb.ObjectId defaultCurveLabelStyleId,
CadDb.Transaction tran )
{
foreach (var labelInfo in labels)
{
double ratio;
if (labelInfo.IsOnCurve(entId, out ratio))
{
var lineLabel = labelInfo.LabelType == SegmentLabelType.LineLabel ?
labelInfo.LabelStyleId : defaultLineLabelStyleId;
var curveLabel = labelInfo.LabelType == SegmentLabelType.CurveLabel ?
labelInfo.LabelStyleId : defaultCurveLabelStyleId;
//Add label to the segment
var id = CivilDb.GeneralSegmentLabel.Create(
entId, ratio, lineLabel, curveLabel);
var label = (CivilDb.Label)tran.GetObject(
id, CadDb.OpenMode.ForRead);
if (label.Layer.ToUpper()!=labelInfo.LabelLayer.ToUpper())
{
label.UpgradeOpen();
label.Layer = labelInfo.LabelLayer;
}
}
}
}
private bool IsSegmentLabelStyle(
CadDb.ObjectId styleId,
IEnumerable<CadDb.ObjectId> styleCollection,
CadDb.Transaction tran)
{
bool found = false;
foreach (CadDb.ObjectId id in styleCollection)
{
if (styleId == id)
{
found = true;
}
else
{
var style = (LabelStyle)tran.GetObject(
id, CadDb.OpenMode.ForRead);
if (style.ChildrenCount > 0)
{
var styleIds = new List<CadDb.ObjectId>();
for (int i = 0; i < style.ChildrenCount; i++)
{
styleIds.Add(style[i]);
}
//Recursive call
found =
IsSegmentLabelStyle(styleId, styleIds, tran);
}
}
if (found) break;
}
return found;
}
private void GetSettingsCmdAddSegmentLabelDefaultStyles(
out CadDb.ObjectId lineLabelStyleId,
out CadDb.ObjectId curveLabelStyleId)
{
var settings = CivilApp.ActiveDocument.Settings;
var cmdSettings =
settings.GetSettings<Civil.Settings.SettingsCmdAddSegmentLabel>();
lineLabelStyleId = cmdSettings.Styles.LineLabelStyleId.Value;
curveLabelStyleId = cmdSettings.Styles.CurveLabelStyleId.Value;
}
#endregion
}
}
The polyline to be split in this video clip is annotated with 2 labels for each of its segment; some of the labels are on different layers (in different colors); Also, the labels' anchor point (location) on the segment have been dragged randomly along the segment. After execute command "ExplodePoly", all labels look like unchanged, even though they are all re-created.