Points By Shape Code At Nearest Station Issue

Using the node in the title of this post to extract points by shape code so I can ultimately create solids using loft. For some reason this node does not return all the points associated with the shape code in C3D. When creating solids in C3D it does not have the same issue where points are missing. Anyone else having this issue? Images below show geometry preview in Dynamo and the assembly in C3D (getting shape in solid gray in this image)

I’m assuming you already checked this, but worth asking anyway. Is the geometry scaling set to medium?

Yes set to medium. A work around might be to get what I need from the AppliedAssemblies in Baseline using .net. Was hoping to avoid this to cut down effort on my part. This issue persists in C3D 2022 and 2023.2. Will share if the points obtained in Python are the same as what dynamo is getting.

1 Like

So when using Python I’m getting all the points. I’ve included the code so no one else needs to spend 3 hours+/- of their day fumbling through this :sweat_smile:. Perhaps Autodesk can address this in a future build of Dynamo for Civil 3d? @solamour.

# Load Python standard libraries
import clr

# Add Assemblies for AutoCAD, Civil 3D and Dynamo APIs
clr.AddReference('acmgd')
clr.AddReference('acdbmgd')
clr.AddReference('accoremgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')
clr.AddReference('acdbmgdbrep')
clr.AddReference('System.Windows.Forms')
clr.AddReference('ProtoGeometry')

# Add standard Python references
import sys
import os
# import math

# Add references to manage arrays, collections and interact with the user
from System import *
from System.IO import *
from System.Collections.Specialized import *

# Import references for Civil 3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *


# Import references from Dynamo
from Autodesk.DesignScript.Geometry import Point as DynPoint, Line as DynLine


# Declare global variables
ac_app = Application
a_doc = ac_app.DocumentManager.MdiActiveDocument
ed = a_doc.Editor
c_doc = CivilApplication.ActiveDocument


# Functions
def ex_traceback():
    """
    This method writes the full Python stack trace to the AutoCAD commandline.
    """

    global ed
    str_1 = "\n"
    str_2 = "================================================================="
    import traceback
    result = [str_1, str_2, str_1, traceback.format_exc(), str_2, str_1]
    for r in result:
        ed.WriteMessage(r)
    # ed.WriteMessage(str_2)


def invalid_obj_message(obj, str_1):
    """
    Formatted string for notifying user, typically used on AutoCAD/C3D
    entities.
    :param obj: object
    :param str_1: custom message to user
    """

    return "{}{}{}{}{}{}".format(
        "\n", obj.GetType().Name, " (Name=",
        obj.Name, "), ", str_1
        )


def pts_by_shape_code_at_nearest_station(dyn_corridor, baseline_name):
    """
    This method replicates what Dynamo for Civil 3D does with a similarly name method. It has been created to address
    the limitations of Dynamo not getting all the Points associated with a Shape within the context of a Civil 3D
    Corridor.

    @param dyn_corridor: Dynamo Corridor
    @param baseline_name: str
    @return: None
    """
    global a_doc
    global ed
    global c_doc

    result = []
    if dyn_corridor is None or baseline_name is None:
        return

    try:
        # Get the active document in the AutoCAD session:
        with a_doc.LockDocument():
            with a_doc.Database as db:
                with db.TransactionManager.StartTransaction() as t:
                    # Get the Corridor
                    corr = t.GetObject(dyn_corridor.InternalObjectId, OpenMode.ForRead)

                    # Get the Baseline by Name
                    # bl = corr.Baselines.Item(baseline_name)
                    bl_coll = corr.Baselines
                    bl = bl_coll.get_Item(baseline_name)

                    # If the Baseline isn't built
                    if not bl.NeedsProcessing:
                        ed.WriteMessage(
                            invalid_obj_message(
                                bl, "cannot be read as it is set to not be built."
                            )
                        )
                        return

                    # Get the stations
                    stations = bl.SortedStations()

                    if stations.Length <= 1:
                        ed.WriteMessage("Baseline only has <= 1 stations.")
                        return

                    coll_of_coll_of_pt_coll = []
                    # Iterate through each station
                    for s in stations:
                        # Get the AppliedAssembly
                        aa = bl.GetAppliedAssemblyAtStation(s)

                        # Get the collection of CalculatedShapes that have a particular code
                        cs_coll = aa.GetShapesByCode("Deck")

                        if cs_coll.get_Count == 0:
                            continue

                        # Declare variable
                        coll_of_pt_coll = []  # Collection of Point Collections

                        # Iterate through the CalculatedShapesCollection
                        for cs in cs_coll:
                            pt_coll = []  # Point Collection
                            pt_coll_strs = []  # Point Collection as str representations
                            # Get the CalculatedLinksCollection
                            cl_coll = cs.CalculatedLinks

                            # Iterate through each CalculatedLink
                            for cl in cl_coll:
                                # Get the CalculatedPointsCollection
                                cp_coll = cl.CalculatedPoints

                                # Iterate through each CalculatedPoint
                                for cp in cp_coll:
                                    # Create a Dynamo Point and get its' string repr
                                    pt3d = cp.XYZ  # Point3d
                                    pt = DynPoint.ByCoordinates(pt3d.X, pt3d.Y, pt3d.Z)
                                    pt_str = str(pt)

                                    if pt_str not in pt_coll_strs:
                                        pt_coll.append(pt)
                                        pt_coll_strs.append(pt_str)

                            coll_of_pt_coll.append(pt_coll)

                        coll_of_coll_of_pt_coll.append(coll_of_pt_coll)

        result = coll_of_coll_of_pt_coll

    except:
        ex_traceback()
    return result

# Output
OUT = pts_by_shape_code_at_nearest_station(IN[0], IN[1])

5 Likes

That seems really useful. One question: what do you refer as Dynamo Corridor? What node gives me that? Maybe I’m missing something. Hope you can help. Thanks.

hi Alex,
would you mind to share your sample dwg and script so that we can investigate into it?

DynCorridor is a Corridor object that Dynamo for C3D provides out of the box, not to be confused with a Corridor as defined in the host application (Autodesk.Civil.DatabaseServices). Sorry can’t share the sample drawing.