How to set Civil 3D Drawing Units and Zones in all dwg files of a path and then join all drawings into one dwg by separating them into blocks using Dynamo

Hi everyone! How can I create a routine like this?

  1. User inputs Civil 3D “Drawing Units”, “Zone”, “Files Path”, “New file Name” and “New Layer Name” in Dynamo Player;

  2. Dynamo opens up all dwg files from that “Files Path”, check all “Drawing Units” and “Zone”, if they are not correct than it sets to the right values chosen from the user options listed at the beginning;

  3. A new file is create with “New file Name” and it must be set at the same “Drawing Units” and “Zone” as checked before;

  4. Dynamo joins all drawings checked at step number 2 into one using Xref in a single dwg file created at step number 3;

  5. Each Xref file must turn into a block, named as its files reference in a specific Layer listed at the beginning by the user as “New Layer Name”.

  6. Bind and Insert all Xref in the file.

  7. End.

The intention of doing this is to create a full road or rail drawing to evaluate the whole project or part of it. I understand the Civil 3D’s limitations, but sometimes I only have AutoCAD drawings and Alignments from Civil 3D on it and it works well! Usually I do it manually in thousands of files and problems like those listed above are very common.

After all those steps, I finally start doing engineering’s evaluations and analysis.

I made part of the routine steps but it is working only in one file and the Xref is happening on Current file. I need a routine to work without opening Dynamo or Civil 3D. Many examples I found only for Revit. I need Python’s help guys!

Thanks in advance!

1 Like

@Paolo_Emilio_Serra1 any suggestions?

Hi @Michelle.Maura,

Can you share what you have already done?

1 Like

Hi @mzjensen!!! The site dos not allow me to upload yet unfortunately ;(

Let me explain below:

a) The Python on the top is not working because I used Revit example, so I need to learn how to use Civil 3D interface:

import clr

Importar bibliotecas e referências dos Assemblies do AutoCAD

clr.AddReference(‘AcMgd’)
clr.AddReference(‘AcDbMgd’)

Importar bibliotecas e referências do arquivo de AutoCAD

from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

doc = Application.DocumentManager.MdiActiveDocument

if IN[0]:
TransactionManager.Instance.EnsureInTransaction(doc) #Exemplo do Revit>>alterar para CIvil 3D
unit = doc.GetUnits()
format = FormatOptions(DisplayUnitType.DUT_METERS,0.0001) #Exemplo do Revit>>alterar para CIvil 3D
unit.SetFormatOptions(UnitType.UT_Length,format) #Exemplo do Revit>>alterar para CIvil 3D
doc.SetUnits(unit) #Exemplo do Revit>>alterar para CIvil 3D
TransactionManager.Instance.TransactionTaskDone() #Exemplo do Revit>>alterar para CIvil 3D
OUT = “Unidade de medida alterada para METRO e configurado 4 casas decimais”
else:
OUT = “Set IN[0] to true!”

b) The Python on the left is working only in current file, I need to add a float routine to read all dwg documents in a path right? I don’t know how to do it:

import clr

Add Assemblies for AutoCAD

clr.AddReference(‘AcMgd’)
clr.AddReference(‘AcDbMgd’)

Importar referências do arquivo de AutoCAD

from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

adoc = Application.DocumentManager.MdiActiveDocument #documento corrente>>Alterar para documento a ser escolhido pelo usuário
editor = adoc.Editor

def add_xref(path,blockName,switch):

global adoc

with adoc.LockDocument():
	with adoc.Database as db:
		with db.TransactionManager.StartTransaction() as t:
			# Determinar se sobrepoe desenho existente ou insere
			if switch==True:
				oid = db.OverlayXref(path, blockName)
			else:
				oid = db.AttachXref(path, blockName)
			
			if not oid.IsNull:
				insPt = Point3d(IN[2],IN[3],IN[4])
				
				# Create block reference
				blkRef = BlockReference(insPt,oid)
				# Add block table record
				bt = t.GetObject(db.BlockTableId, OpenMode.ForWrite)
				btr = t.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite)
				btr.AppendEntity(blkRef);
				t.AddNewlyCreatedDBObject(blkRef, True)		
			t.Commit()			

return blkRef.Name
# IN[0]=local do documento
# IN[1]=documento.extensão
	# IN[2]=string=origem=coordenada de referência atual
	# IN[3]=string=origem=coordenada de referência atual
	# IN[5]=string=origem=coordenada de referência atual
# IN[5]=verdadeiro ou falso

OUT = add_xref(IN[0],IN[1],IN[5])

c) The Python in the Right creates a Layer in current file, it is working, but I could not set this new layer to the Xref yet:

import clr

Importar referencias e bibliotecas dos Assemblies do AutoCAD e Civil 3D APIs

clr.AddReference(‘acmgd’)
clr.AddReference(‘acdbmgd’)
clr.AddReference(‘accoremgd’)
clr.AddReference(‘AecBaseMgd’)
clr.AddReference(‘AecPropDataMgd’)
clr.AddReference(‘AeccDbMgd’)
clr.AddReference(‘AeccPressurePipesMgd’)
clr.AddReference(‘acdbmgdbrep’)
clr.AddReference(‘System.Windows.Forms’)

Importar referencias e bibliotecas do standard Python references

import sys
sys.path.append(‘C:\Program Files (x86)\IronPython 2.7\Lib’)
import os
import math

Importar referencias e bibliotecas do manage arrays, collections and interact com usuário

from System import *
from System.IO import *
from System.Collections.Specialized import *
from System.Windows.Forms import MessageBox

Criar um alias do Autodesk.AutoCAD.ApplicationServices.Application class (Alias quer dizer “pseudônimo”, “apelido” e, em computação, é um comando que permite substituir uma palavra por outras ou por uma cadeia de caracteres)

import Autodesk.AutoCAD.ApplicationServices.Application as acapp

Importar referencias e bibliotecas do 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 *
from Autodesk.AutoCAD.Colors import Color
from Autodesk.AutoCAD.Colors import ColorMethod

Importar referencias e bibliotecas do PropertySets

from Autodesk.Aec.PropertyData import *
from Autodesk.Aec.PropertyData.DatabaseServices import *

Importar referencias e bibliotecas do Civil 3D

from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

adoc = acapp.DocumentManager.MdiActiveDocument
ed = adoc.Editor

Exemplo da função

def create_layer(name, index):

indx = int(index)

# Se falhar
if not isinstance(name, str) or len(name) == 0:
	raise Exception('The name is not a valid string')
if not isinstance(indx, int) or indx < 0 or indx > 255:
	raise Exception('The index component is not a valid integer')

# Substituir caracteres especiais não permitido sno nome
name = name.replace(':', '_').replace(';', '_').replace(',', '_')
	
global adoc
result = False

with adoc.LockDocument():
	with adoc.Database as db:
		with db.TransactionManager.StartTransaction() as t:
			# Definir o Layer Table Object
			lt = t.GetObject(db.LayerTableId, OpenMode.ForRead)
			# Checar se o layer já existe
			if not lt.Has(name):
				with LayerTableRecord() as ltr:
					ltr = LayerTableRecord()
					ltr.Name = name
					ltr.Color = Color.FromColorIndex(ColorMethod.ByAci,indx)
					lt.UpgradeOpen()
					lt.Add(ltr)
					lt.DowngradeOpen()
					t.AddNewlyCreatedDBObject(ltr, True)
			else:
				ltr = t.GetObject(lt[name], OpenMode.ForWrite)
			# Definir cor
			if ltr is not None:
				ltr.Color = Color.FromColorIndex(ColorMethod.ByAci,indx)
			
			result = True
			t.Commit()
	
return result

if IN[0] == None or IN[1] == None:
OUT = create_layer.doc
else:
OUT = create_layer(IN[0], IN[1])

Thanks a lot!

Here’s an example to get you started. This will process an entire folder of DWGs and update the coordinate system code and drawing units for each file. You’ll need to have a DWG open so that you can run it through Dynamo or Dynamo Player, but the other files are not actually opened, which makes it a lot faster. The code is adapted from @Paolo_Emilio_Serra1’s example below.

If you haven’t already, it would be good to read up on element bindings/trace data before you continue working on this workflow. The short story is that you’ll probably have to accomplish pretty much all of the tasks via Python, although you could try to utilize some of the built-in nodes (or even the Civil 3D Toolkit nodes) within your code.

UpdateDrawingSettings1

# Load the Python Standard and DesignScript Libraries
import clr
import System
import os

# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AeccDbMgd')

# Import references from AutoCAD
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.DatabaseServices import *

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *
from Autodesk.Civil.Settings import *

def set_drawing_units(path, units, code):
	"""
	Loads a DWG file as a side database.
	Gets the drawings settings for coordinate system code and drawing units.
	Updates the coordinate system code and units if necessary.
	Saves the changes.
	
	@path: A path to a Civil 3D DWG file
	@units: A string for the desired document units, either "Feet" or "Meters"
	@code: A string for the desired coordinate system code
	@returns: True if successful, otherwise False
	"""

	# Check for null inputs
	if path is None or units is None or code is None:
		return False
		
	# Initialize variables	
	adoc = Application.DocumentManager.MdiActiveDocument
	output = True
	
	with adoc.LockDocument():
		# Load DWG as side database
		db = Database(False, True)
		db.ReadDwgFile(path, System.IO.FileShare.ReadWrite, True, "")
		if db is None:
			return False
		with db.TransactionManager.StartTransaction() as t:
			cdoc = CivilDocument.GetCivilDocument(db)
			# Get drawing settings
			settings = cdoc.Settings.DrawingSettings.UnitZoneSettings
			drawingCode = settings.CoordinateSystemCode
			drawingUnits = settings.DrawingUnits.ToString()
			# Update coordinate system code and units if different
			if drawingCode != code: # 
				settings.CoordinateSystemCode = code  
			if drawingUnits != units:
				settings.DrawingUnits = System.Enum.Parse(DrawingUnitType, units)	            	
			t.Commit()	            
		# Save changes
		db.SaveAs(db.Filename, DwgVersion.Current)
	return output

def main(folder, units, code):
	"""
	Credit to original author, Paolo Emilio Serra.
	Gets all the DWGs in a folder path recursively.
	Loads each file as a side database, updates drawing settings, and saves the changes.
	
	@folder: The path to folder containing Civil 3D DWGs
	@units: A string for the desired document units, either "Feet" or "Meters"
	@code: A string for the desired coordinate system code
	@returns: A Dictionary of successes and failures
	"""
	# Check for null inputs
	if folder is None or units is None or code is None:
		return None

	# Initialize variables
	docs = []
	adoc =  Application.DocumentManager.MdiActiveDocument
	output = {}
	output['Success'] = []
	output['Failure'] = []

	# Get documents
	for dirpath, dnames, fnames in os.walk(folder):
		for f in fnames:
			if f.endswith('.dwg'):
				docs.append(os.path.join(dirpath, f))
	
	# Loop through each document and update settings
	for doc in docs:
		result = set_drawing_units(doc, units, code)
		if result:
			output.setdefault('Success', []).append(doc)
		else:
			output.setdefault('Failure', []).append(doc)

	Application.DocumentManager.CurrentDocument = adoc
	
	return output

OUT = main(IN[0], IN[1], IN[2])
5 Likes

Hi @mzjensen ,

Is there also a way to combine this method with the “ExternalDocument”(s) from your Camber Package?
I am looking for a way to also automatically update the coordinatesystem of all autocad models which we receive, together with some stuff which I am already doing through the use of the Camber package:

Or would you recommend splitting that then into two scripts which should be run separately.

PS: I am unable to get the Python code you supplied to work, I am getting some Python errors and I don’t know what causes them. Could you maybe share a .dyn-file containing the Python file and the corresponding inputs?

@Daan
I’m not by my PC today, so I’ll get back to you tomorrow.

1 Like

Hi @mzjensen ,

Were you able to take a look at this yet?

@Michelle.Maura @Daan
There are nodes for getting/setting the units and zone settings starting in Camber v4.1.0.

2 Likes

Very nice! This will help out immensly!

1 Like