A Dynamo Script for Auto PVIs on Design Profile

Hi Everyone,

I am trying to create a script through Dynamo with custom Python code. First, I will try my best to explain what I am trying to acheive, then I will explain the approach i’ve taken so far.

The high level explanation is I have a point group in all of my models titled “Bends”. All of the descriptions for the COGO pts in this group contain a common bit of text called “_Cuts_X.XX”… where XX is equal to a numerical value which represents how deep, in meters, we are to excavate at that location. Currently, I project these pts to a profile view, and manually draw a polyline under each projection to this numerical value, then create a PVI on my design profile at the end of this polyline. This is the task I am trying to automate as it’s hundreds of points a week.

The workflow I’ve been trying so far:

  1. Select all the pts in point group “Bends”

  2. Extract the Cut value from the Description

  3. Create a new PVI at this new elevation below the projected point.

I am also open to abandoning this entire workflow if anyone has a better way of solving it. The one way I can think of is creating a separate CSV file, and offsetting the elevation value directly in the CSV by the Cut value in the description and then importing both CSVs but… that makes the file very messy.

If anyone can help streamline this workflow it would be great!

Note: As I am a new user it will not allow me to upload. I did post this question on the Civil3D forums as well and you can find the dynamo file there

Link to Other Thread

Thank you

EDIT: here is an image of an example of my current workflow

1 Like

Hi @PLM1987 ,

Could you also share an AutoCAD file together with a Dynamo file?
You could upload those to WeTransfer and then share the link here.

Hi Daan,

See attached. I’ve created a bit of a dummy file depicting the scenario with some arbitrary numbers.

Admittedly, I believe the dynamo file I’ve attached to be useless as it improperly calls libs that do not exist in Civil3D. I had previously tried to do this with the nodes that exist in the software but it looks like I saved over that file.

Hey @PLM1987,

I have thrown together something that i believe fits your criteria, it does use a couple of packages mind, though you could use it as guidance for building something completely pythonic.
(Though personally I find the packages mandatory for doing anything with Civil 3D as the ootb nodes are very lacking).

Cogo point to profile by cut elevation.dyn

Method:

  1. Get Cogo Point Group and extract the description from the points.
  2. Split the description up to get index of the Cut field.
  3. Increment the index by 1 to get the Cut value.
  4. If statement to get the actual Cut value and substitute in the default value.
  5. Calculate the station along the alignment for each of the Cogo points.
  6. Sample the Profile at these stations.
  7. Had to round to a more sensible decimal place due to the accuracy of the floating point numbers generated.
  8. Substituted the cut value from the sampled elevations.
  9. Created a new profile.
  10. Added PVI’s to the profile at the sampled stations and the elevations calculated at 8.

If you have a fixed naming scheme for the alignments and profiles you can swap the object selections for a string and add a few more nodes to select alignment and profile by name.
If you have an existing profile you want the points adding to you can remove the new profile step and use nodes to get profile by name.
If you need the profile to connect to the alignment start and end then you can pull the alignment start and end stations and add them to the start and end of the stations list.
And then add default cut values to the start and end of the cut list generated at 4.

3 Likes

Wow first of all thank you so much for this reply I appreciate it.

This is nearly working nearly exactly as I’d envisioned. I do have a couple questions that you might know the answer to. For reference, I’ve replaced the part where you generate a new profile and instead I’ve added an input node where I select the existing profile to add the PVIs:

  1. Is it possible to add an additional step after the PVIs have been created? That adds a radius to all of them of some input from the user? Say 50m radius in this example using the “free vertical curve - Circular” option in the profile layout tools.

  2. When I test this in a real file it doesn’t seem to work. I am trying to understand your logic in how it extracts the Cut value. I see that it breaks the description up into a list based on underscores, but then I am a little lost on the indexing. The issue I am having is our points may not always come back in the exact format I had in the provided dwg file. Essentially, what I am asking is, is it possible to have the script exact the cut value, assuming it always exists directly after the word cut_, but can vary in location within the description? Additionally, it’s not creating the 2.1m default where the word cut or the cut value does not exist (it seems to work in the DWG I provided you, but not the real one). Here is a link to a CSV of a sample of the “Bends” through a 10km section
    https://we.tl/t-OnfbxnrpH8

EDIT: By going into the csv and doing a replace all on certain characters (: for example) with an underscore it seems to have forced it to work on the majority of the points. So two things: We will simply instruct our surveyors to only use underscores or adjust the CSV ourselves (easy). The big ask here is can the script, no matter what the circumstance, at a minimum, place a PVI of 2.1m? Example: if the word cut doesn’t exist at all, if the formatting is weird, etc… if all logic fails, at a bare minimum, it should PVI at 2.1m

  1. Yes that is possible you would add another node on the end like this, with an extra input for the radius or list of radii.
    image

  2. Ahh i see, yes with that data set it can be problematic to determine if a cut value exists.
    A hotfix as it were, is to add this node that checks for a null return when casting from string to number and substitutes the default cut value in.

A more elegant solution most likely requires a regular expression to find “Cut_” and extract any “digit digit decimal digit digit” that comes after and stops at the next _ or the end of string.
Likely have to use some python code like at the end of this thread
Regular expressions in Dynamo - Packages - Dynamo (dynamobim.com)
Along with this Regex:

(?<=Cut_)(\d)+(.)(\d)+

It has a Positive Lookbehind to find “Cut_” and then matches any number of digits followed by a “.” and any number of subsequent digits that follows after it.

In the python code you would edit this statement to have a false action that appends the defaultCutValue.

1 Like

Perfect! This is now working great thank you so much. This will honestly save hundreds of hours of work over a year. We are in the process of standardizing our raw description formatting so this will be a lot easier once that has been finalized.

Final question for you if you have the time:
I am looking to repurpose your the script here to use the first couple parts to help with another mundane task and I was wondering - can I add multiple point groups in the “bends” input node? Essentially I have 4 point groups, Bends, BH, Grd, Cut, and I am looking to add another user input node where the user can input a desired point range (Example 361-400), and the script will select any cogopoints that exist in those 4 point groups AND within the point numbering range, find where they exist along the user input alignment and get the relative station value, then draw a 3D polyline that begins at the cogo point at the LOWEST station value, then connects to the next, and connects all the way to the last point that exists at the highest station value. The end result is a continuous 3dpolyline with a vertex at each COGO point, starting from the lowest station value and ending at the highest station value.

The purpose of this is sometimes my survey doesn’t pick up their shots starting from X location and ending at Y location. They may survey from X to B, walk back to their truck, drive to Y, then survey to B for example and now I cannot just do a simple 3DPoly>'pn>361-400 as the points are not in order

I considered starting a new thread for this but I will pose the question to you first.

I’ve attached as far as I’ve gotten but I am stumped on the List.Contains part and filtering the points.
Cogo to 3dPoly.dyn (44.0 KB)

Sorry, it seems for some reason step 1 (radius at PVIs) is only inserting them at a few of the PVIs and not all of them. At first I thought it would be a no solution error, but I manually went in and created them just fine - any thoughts?

Example, it added 3 here

Happy to be of help.

I made an alteration to the previous graph to amend the way it gets the Cut value.
It Sanitizes the data instead to ensure we always have a Cut value even if one was not specified.


Cogo to 3dPoly V2.dyn (85.9 KB)

As for the Radius issue, try running it from a separate graph.
Otherwise would probably need to see a larger dataset in cad that isn’t working to test with.

Thanks again, James.

I am fiddling with amending the .dyn file now to reflect your update but I am getting the following error on my string.ToNumber Node:
“Warning: String.ToNumber operation failed.
Not a valid number.
Parameter name: str”

Its possible there is an edge case with the data set that hasn’t been picked up.
I’m guessing its something like “Cut_Note” in the description.
Which becomes “Cut_0Note” after the 1.1 node.
After it gets split and you get “0Note” as the value which cannot be cast to a number.
If you scroll through list under the code block you will likely find it.
Even managed to replicate it.

I also amended this to capture cases where a 0.00 value was already present for Cut.

((List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>) != “0” &&
List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>) != “00.00”)?
List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>):defaultCutValue+“”);

When I paste your code into a code block I get a “invalid Associative_Factor” error. I’ve triple checked your image against my code block and it appears identical

EDIT: Fixed it. Was missing spaces.

((List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>) != "0" && 
List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>) != "00.00") ? 
List.GetItemAtIndex(splitDescription@L2<1>,valueIndex@L1<1>) : defaultCutValue + "");

Hi James,

Still trying to figure out this radius thing on the PVIs. I believe the reason it is not creating radius curve at each PVI and is only selecting a few is this error: Warning: ProfileExtensions.AddFreeCircularCurveByPVIAndRadius operation failed.
Object reference not set to an instance of an object.
It’s returning null values for the ProfileEntity

Morning @PLM1987,

I believe this is perhaps a symptom of the fact that nodes in Dynamo activate instantly and dont have any built in way to wait for the previous command to finish, so its only adding curves to the first few PVI’s generated.
You can possibly find was to make it “waitFor” here on the forum or my personal approach would be to split off the Radii creation into a separate Graph.

1 Like

Thanks, James. I split it out into a separate graph and it’s working as intended now.

1 Like