Creating ParcelSegmentLabels example

Creating ParcelSegmentLabels example

Jeff_M
Consultant Consultant
1,716 Views
18 Replies
Message 1 of 19

Creating ParcelSegmentLabels example

Jeff_M
Consultant
Consultant

This is an example of how to add labels at the picked points along a ParcelSegment without needing to reference the COM libraries (no .NET methods exist to do this, as of C3D 2021).

 

        [CommandMethod("TestCreateParcelLabels")]
        public void createparcellabels()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var entOpts = new PromptEntityOptions("\nSelect a parcelsegment at location for label: ");
            entOpts.SetRejectMessage("..not a ParcelSegment, try again.");
            entOpts.AddAllowedClass(typeof(ParcelSegment), true);
            var entsel = ed.GetEntity(entOpts);
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                var linId = CivilApplication.ActiveDocument.Settings.GetSettings<SettingsCmdAddParcelSegmentLabels>().Styles.ParcelLineLabelStyleId.Value;
                var crvId = CivilApplication.ActiveDocument.Settings.GetSettings<SettingsCmdAddParcelSegmentLabels>().Styles.ParcelCurveLabelStyleId.Value;
                var linStyle = tr.GetObject(linId, OpenMode.ForRead).AcadObject;
                var crvStyle = tr.GetObject(crvId, OpenMode.ForRead).AcadObject;
                while (entsel.Status == PromptStatus.OK)
                {
                    dynamic p = tr.GetObject(entsel.ObjectId, OpenMode.ForRead).AcadObject; 
                    p.SegmentLabels.Add(linStyle, crvStyle, entsel.PickedPoint.ToArray());
                    entsel = ed.GetEntity(entOpts);
                }
                tr.Commit();
            }            
        }

 

@mehdi-guida 

Jeff_M, also a frequent Swamper
EESignature
Accepted solutions (2)
1,717 Views
18 Replies
Replies (18)
Message 2 of 19

hosneyalaa
Advisor
Advisor

Thank you @Jeff_M 
For this post

 

Thank you

0 Likes
Message 3 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

I looks like they added the class ParcelSegmentLabel to 2022.  It appears to inherit GeneralSegmentLabel. 

 

The code below returns a not within expected range exception.  Not really sure why the error is occurring yet.  Do you have any suggestions?

if (acObjId.ObjectClass.DxfName == "AECC_PARCEL_SEGMENT")
{
ParcelSegment parc = tr.GetObject(acObjId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead) as ParcelSegment;
for (int i = parc.StartParam; i <= parc.EndParam - 1; i++)
{
ObjectId PLabelId = ParcelSegmentLabel.Create(parc.ObjectId,(i + 0.5);
}
}

 

Stacy Dunn
0 Likes
Message 4 of 19

BlackBox_
Advisor
Advisor

@stacy.dunn -

 

Is the ParcelSegment open or closed?


"How we think determines what we do, and what we do determines what we get."

Sincpac C3D ~ Autodesk Exchange Apps

0 Likes
Message 5 of 19

stacy.dunn
Collaborator
Collaborator

@BlackBox_  Both open and closed segments give the same error.

Stacy Dunn
0 Likes
Message 6 of 19

Jeff_M
Consultant
Consultant
Accepted solution

@stacy.dunn , not sure why you are getting that error. I did have to change the code slightly as it showed an error in the VS IDE regarding the StartParam being a double and couldn't cast to an int.

 

        [CommandMethod("ParcelSegLabelTest")]
        public void parcelseglabeltest()
        {
            var doc = Application.DocumentManager.CurrentDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var civdoc = CivilApplication.ActiveDocument;
            
            var entOpts = new PromptEntityOptions("\nSelect Segment");
            entOpts.SetRejectMessage("...not a parcelsegment, try again!");
            entOpts.AddAllowedClass(typeof(ParcelSegment), true);
            var sel = ed.GetEntity(entOpts);

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                while (sel.Status == PromptStatus.OK)
                {
                    if (sel.ObjectId.ObjectClass.DxfName == "AECC_PARCEL_SEGMENT")
                    {
                        ParcelSegment parc = tr.GetObject(sel.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead) as ParcelSegment;
                        for (int i = (int)parc.StartParam; i <= parc.EndParam - 1; i++)
                        {
                            ObjectId PLabelId = ParcelSegmentLabel.Create(parc.ObjectId, (i + 0.5));
                        }
                    }
                    sel = ed.GetEntity(entOpts);
                }
                tr.Commit();
            }
        }
Jeff_M, also a frequent Swamper
EESignature
Message 7 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

 

Do you have an example of labeling parcel segments by selecting the parcel and looping through the parcel elements?

 

Thank you,

Stacy

Stacy Dunn
0 Likes
Message 8 of 19

Jeff_M
Consultant
Consultant
Accepted solution

@stacy.dunn sure, here is such an example. 

       [CommandMethod("ParcelSegLabelTest2")]
        public void parcelseglabeltest2()
        {
            var doc = Application.DocumentManager.CurrentDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var civdoc = CivilApplication.ActiveDocument;

            var entOpts = new PromptEntityOptions("\nSelect Parcel:");
            entOpts.SetRejectMessage("...not a parcel, try again!");
            entOpts.AddAllowedClass(typeof(Parcel), true);
            var sel = ed.GetEntity(entOpts);

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                while (sel.Status == PromptStatus.OK)
                {
                    var parcel = tr.GetObject(sel.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead) as Parcel;
                    dynamic comparcel = parcel.AcadObject;
                    dynamic loop = comparcel.ParcelLoops.Item(0);//this will be the outer loop if more than 1
                    var count = loop.Count;
                    for (int i = 0; i < count; i++)
                    {
                        dynamic l_item = loop.Item(i);
                        var com_seg = l_item.ParcelSegment;
                        var idx = 0;
                        for (idx = 0; idx < com_seg.Count; idx++)
                        {
                            var curseg = com_seg.Item(idx);
                            if ((curseg.StartX == l_item.StartX && curseg.StartY == l_item.StartY && curseg.EndX == l_item.EndX && curseg.EndY == l_item.EndY) ||
                                (curseg.StartX == l_item.EndX && curseg.StartY == l_item.EndY && curseg.EndX == l_item.StartX && curseg.EndY == l_item.StartY))
                            {
                                var segelement = CivDb.DBObject.FromAcadObject(com_seg);
                                ObjectId PLabelId = ParcelSegmentLabel.Create(segelement.ObjectId, idx + 0.5);
                                continue;
                            }
                        }
                    }
                    sel = ed.GetEntity(entOpts);
                }
                tr.Commit();
            }
        }
Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 9 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

 

Thank you again!

 

Stacy

Stacy Dunn
0 Likes
Message 10 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

The .net method ParcelSegmentLabel.Create is still not working for me.  Did you get it to work without getting the index out of range error?

 

I tweaked the code to use the com method to add the labels and also added a check so that labels were not duplicated within the command.  It works while in the command, but I need to come up with a way to not duplicate existing labels.  Any advice would be appreaciated.

                        for (idx = 0; idx < com_seg.Count; idx++)
                        {
                            var curseg = com_seg.Item(idx);
                            if ((curseg.StartX == l_item.StartX && 
                                curseg.StartY == l_item.StartY && 
                                curseg.EndX == l_item.EndX && curseg.EndY == l_item.EndY) ||
                                (curseg.StartX == l_item.EndX && 
                                curseg.StartY == l_item.EndY && curseg.EndX == l_item.StartX 
                                && curseg.EndY == l_item.StartY))
                            {
                                var segelement = CivDb.DBObject.FromAcadObject(com_seg);
                                //ObjectId PLabelId = ParcelSegmentLabel.Create(segelement.ObjectId, idx + 0.5);
                      
                                Curve cv = tr.GetObject(segelement,OpenMode.ForRead)  as Curve;
                                var len = cv.GetDistanceAtParameter(idx + 0.5);
                                var pt = cv.GetPointAtDist(len);

                                if (!LabeledPoints.Contains(pt))
                                {
                                    LabeledPoints.Add(pt);
                                    curseg.ParcelSegment.SegmentLabels.Add(linStyle, crvStyle, pt.ToArray());
                                }
                                continue;
                            }
                        }

 

Thank you for your help over the years!

 

Stacy

 

Stacy Dunn
0 Likes
Message 11 of 19

Jeff_M
Consultant
Consultant

@stacy.dunn wrote:

Jeff,

The .net method ParcelSegmentLabel.Create is still not working for me.  Did you get it to work without getting the index out of range error?

 

I tweaked the code to use the com method to add the labels and also added a check so that labels were not duplicated within the command.  It works while in the command, but I need to come up with a way to not duplicate existing labels.  Any advice would be appreaciated.

 

Thank you for your help over the years!

 

Stacy

 


Yes, Stacy, the last code I posted worked without error in the drawing I was testing with. Are you saying it gave you the index out of range error?

 

For the existing labels, COM has the SegmentLabels property and .NET has the GetAvailableParcelSegmentLabelIds() method. With either of these you can loop through them to see if the current element has a label.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 12 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

I tried a few things and did get the .net method to work only in a specific instance.

 

If I open a blank drawing using the OOTB acad.dwt, create a parcel using a polyline, and run the code I get:

System.ArgumentException: The ratio should be in the range [0, 0].

 

 

If create a parcel using lines and curves and run the code it works as expected.

 

 

When I test the code with my test drawing I get the error:

System.ArgumentException: Value does not fall within the expected range.

 

 

My test drawing or new drawing based on our production template produces this same error regardless if the parcel was created with lines or polylines.  The template is based on a previous version of C3d.  The built in commands work as expected in all drawings. 

 

 

I am not sure what it going on, but I will submit a support request to see if Autodesk can shed any light on it.

 

 

Thank you,

Stacy

 

 

 

 

 

 

 

Stacy Dunn
0 Likes
Message 13 of 19

Jeff_M
Consultant
Consultant

Ah, selecting a parcel created from a polyline fails. That is strange.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 14 of 19

Jeff_M
Consultant
Consultant

@stacy.dunn See the last few posts in this thread  the GeneralLineLabel fails the same way when a closed polyline is selected. Working around the polyline issue was fairly simple...just make the polyline open then reclose it. Really can't do the same thing with parcels since it will remove/recreate the parcel and likely cause havoc with numbering.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 15 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

Thank you for the heads up on how to work around the polyline issue.  For my current needs I am using the COM methods to get the labeling done. 

 

I am having trouble figuring out how to get the individual parcel segment to check for labelIds.  When getting the objectids from the parcel segment, it is returning all of the labels attached to the segment and not the ones within the current parcel loop,  The actual linework is a rear lot line that is drawing across the back of a block.  There are multiple side lot lines that intersect it.  Is there a way to get only the label objectid from the segment within the loop, ie the line segment within the lot itself?

 

Using your example I am using the code below to get the ids.  It returns all of the ids along the entire length of the line instead of the one inside the parcel.

 

ObjectId oParcelSegmentId = CivDb.DBObject.FromAcadObject(com_seg);
idcol = ParcelSegmentLabel.GetAvailableLabelIds(oParcelSegmentId);

 

I have also tried getting the component of the parcel segment  com_seg.Item(0).ParcelSegment, but this returns the same list of all labels attached to the line.  What am I missing?

 

thank you,

Stacy

 

 

Stacy Dunn
0 Likes
Message 16 of 19

Jeff_M
Consultant
Consultant

@stacy.dunn the following seems to work correctly. Just check of an existing label exists in the same parameter as the segment to be labeled. Yes, this is still using the .NET method to add the label, but that comes after the check.

 

                            if ((curseg.StartX == l_item.StartX && curseg.StartY == l_item.StartY && curseg.EndX == l_item.EndX && curseg.EndY == l_item.EndY) ||
                                (curseg.StartX == l_item.EndX && curseg.StartY == l_item.EndY && curseg.EndX == l_item.StartX && curseg.EndY == l_item.StartY))
                            {
                                var segelementId = (ObjectId)CivDb.DBObject.FromAcadObject(com_seg);
                                var segelement = (ParcelSegment)segelementId.GetObject(OpenMode.ForRead);
                                var lbls = ParcelSegmentLabel.GetAvailableLabelIds(segelementId);
                                var lblfound = false;
                                foreach (ObjectId lblId in lbls)
                                {
                                    var lbl = (ParcelSegmentLabel)lblId.GetObject(OpenMode.ForRead);
                                        var p = segelement.GetParameterAtPoint(lbl.LabelLocation);
                                    if (Math.Floor(p) == idx)
                                    {
                                        lblfound = true; ; //label found on segemnt, continue on to next
                                        continue;
                                    }
                                }
                                if (lblfound)
                                    continue;
                                ObjectId PLabelId = ParcelSegmentLabel.Create(segelementId, idx + 0.5);
                                continue;
                            }

  

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 17 of 19

stacy.dunn
Collaborator
Collaborator

Jeff,

 

It didn't occur to me to simply check through the list and compare the location point of the existing label to the new label point.

 

thank you again.

 

Stacy

Stacy Dunn
0 Likes
Message 18 of 19

Jeff_M
Consultant
Consultant

@stacy.dunn looks like the closed polyline issue has been corrected with C3D 2022.1:

Fixed an API issue in which GeneralSegmentLabel.Create() failed on closed polylines.

I haven't yet tested it, just saw it in the readme.

Jeff_M, also a frequent Swamper
EESignature
0 Likes
Message 19 of 19

stacy.dunn
Collaborator
Collaborator

Thank you Jeff

Stacy Dunn
0 Likes