Property Sets <-> Object Data сonverter

Property Sets <-> Object Data сonverter

АлексЮстасу
Advisor Advisor
10,582 Views
21 Replies
Message 1 of 22

Property Sets <-> Object Data сonverter

АлексЮстасу
Advisor
Advisor

It would be nice to make the Converter of the Custom Property Sets <-> Object Data from Map 3D. Such a proposal already exists in ideas: https://forums.autodesk.com/t5/civil-3d-ideas/add-object-data-table-attributes-to-property-set-data/...
For example, in the form of two utilities: PS2OD and OD2PS.
For Object Data in Map 3D, there are advanced features for use - export/import, labeling, etc. Civil 3D Property Sets can be transferred to other applications. The converter could give all the possibilities.
It seems to me that such a converter is not very difficult to write.
What is needed for this?
Unfortunately, I'm not a programmer.

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
10,583 Views
21 Replies
Replies (21)
Message 2 of 22

dwattersAMXH5
Enthusiast
Enthusiast

or even a lisp that you could use to auto-populate property set tables with data from object data tables. 

 


after applying property set to all objects (making the table by hand) 
a routine similar to t2m where you could grab all lines/plines/other, 
lisp selects each object individually, 

then searches for value in object data that matches property set name then copies that data into the property set 
then repeats for each property set item, 
then repeats for each object. 

so if you had object data field named "test1" and a property set field named "test1" it would pull the data from the object data to populate the corresponding property set data.

 

 

don't know how to make that work though

0 Likes
Message 3 of 22

АлексЮстасу
Advisor
Advisor

As I thought...
PS2OD:
0. The same OD with PS - the same names of which do not contain any separating symbols, except for underscores and dashes, and not more than 24 characters in length. These are the limitations of OD.
1. If dwg does not describe the same OD with PS, then their descriptions with names like PS are created.
2. If the same OD is described in dwg, then create new OD descriptions with these names, but with the suffix "..._ 1", "..._ 2", etc., shortening the names if you need up to 24 characters.
3. For created ODs create corresponding fields for the data. Separating symbols, other than underlines and dashes, are replaced by underscores. The length of field names can not exceed 31 characters.
4. Such ODs with the corresponding data are attached to the corresponding graphic elements, as in PS.
5. The original PS is not deleted.
OD2PS:
Symmetrically.

Option: if already exist OD and PS with the same name, and they have the same data fields, then do not create OD/PS with the suffix "..._ 1", "..._ 2" etc., and transfer the data in existing OD/PS.

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 4 of 22

АлексЮстасу
Advisor
Advisor

Is it possible to create, fill and attach Property Sets on Lisp?

 

And what is t2m?

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 5 of 22

dwattersAMXH5
Enthusiast
Enthusiast

not sure, 

t2m is a lisp update to the t2m command

t2m converts text 2 multi-text but the standard command from autocad  can not handle multiple text objects (it converts them all into one mtext) 

 

t2m lisp goes through and runs t2m on each line selected separately performing the t2m command resulting in multiple mtext boxes. 

this functionality of processing lines one by one is what i was referring to as being important in this instance because each line would theoretically  have different Object Data so you wouldn't want a command treating all line items and Object Data tables as one entity. 

0 Likes
Message 6 of 22

АлексЮстасу
Advisor
Advisor

At first I thought of taking as the prototype xd2od.lsp.
There everything is very simple - one directly creates another.
Now I doubt that it can be so simple.
In general, there can already exist both Object Data and Property Sets in dwg.
Perhaps, it would be better to be able to directly establish the correspondence between specific Object Data and Property Sets and their fields. Then it would make sense to store the details of these correspondences - say, in a simple text file:
name_OD_1     name_OD_field1     name_PS_1     name_PS_field1
name_OD_1     name_OD_field2     name_PS_1     name_PS_field2
...
name_OD_2     name_OD_field1     name_PS_2     name_PS_field1
name_OD_2     name_OD_field2     name_PS_2     name_PS_field2
...
With this approach, it would be nice the program to give a window in which the names of the Object Data and Property Sets would be displayed, and users could select the correspondences or load the ready-made matching files.

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 7 of 22

dwattersAMXH5
Enthusiast
Enthusiast

so here's another thought i've had on this 

all you really need to do is create variables based with object data fields you want :

  say you set variables for name,length,tag#, description and pull the Vars info from corisponding data fields in object data (your object data would need to be similar every time as far as the field name goes) 


you then have the info in your lisp as variables 

next you would just need to find a way to assign property set data or populate fields in property set data (make the table before hand possible and just have the lisp fill it) 
then it would be as simple as plugging in the Variables for the property set field values. 

field one object data = var1 (a lisp variable) = field one property set

 

0 Likes
Message 8 of 22

АлексЮстасу
Advisor
Advisor

Details of programming are difficult for me to appreciate - I'm not a programmer, alas.


How would you rate the chances that there is a programmer for this task?
Probably, it can only be a Civil 3D programmer?
I have already been told that in the usual access there is no information about the Property Sets for programmers. Just some examples.
And on what conditions could a programmer agree to solve such a problem?

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 9 of 22

dwattersAMXH5
Enthusiast
Enthusiast

it can probably only be done by a .NET programmer 


both object data and property set data are accessible with visual basic via .NET API's 
but this will involve Visual Basic and .NET programming which is far more advanced then AutoLisp and would probably require a programming engineer, but it appears there's enough resources online to build this program. 



0 Likes
Message 10 of 22

Norman_Yuan
Mentor
Mentor

@АлексЮстасу wrote:

... ...
And on what conditions could a programmer agree to solve such a problem?

... ...


The conditions (or the qualifications) for a programmer to solve such problem would be:

1. Have enough experience of AutoCAD .NET API programming. Intermediate level is minimum, advanced level would be preferred;

2. Understanding AutoCAD Map's Object Data and AutoCAD Map's .NET API that deals with Map Object Data

3. Understanding PropertySet used in AutoCAD Archtecture and Civil3D.

 

It may be easier to find someone who is good at AutoCAD .NET API (plain AutoCAD), but he/she may not have worked with either Map's Object Data, or Archtecture/Civil3D's PropertySet data, or both. But an experienced AutoCAD programmer should be able to learn either OD or PropertySet or both fairly quick. 

 

For example, after seeing post on this topic in VBA forum by @dwattersAMXH5, I spent a little bit time on studying the APIs for accessing PropertySet data in a drawing, I was able to write come code to actually retrieve PropertySet data attached to entities in drawing. I would not have problem to easily go ahead to convert the retrieved PropertySet data into Object Data. 

 

At this moment, I can see some kind of uitility for PropertySet<=>Object Data would be quite useful. I was a bit surprise at what you said that there is no PropertySet<=>Object Data converting application available from third parties (are you sure you have searched Autodesk's app store?).

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 11 of 22

АлексЮстасу
Advisor
Advisor

@Norman_Yuan wrote:

 

At this moment, I can see some kind of uitility for PropertySet<=>Object Data would be quite useful. I was a bit surprise at what you said that there is no PropertySet<=>Object Data converting application available from third parties (are you sure you have searched Autodesk's app store?).


Yes, I searched in search engines, forums, in the App Store on the "Property Set", "Object Data", "OD", etc. But I did not. Perhaps I know the language too badly...

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 12 of 22

frederic-classon
Autodesk
Autodesk

How about a Dynamo for Civil 3D Script: https://knowledge.autodesk.com/community/screencast/551d4a45-4143-4a46-a475-2b7f3431879c

“https://damassets.autodesk.net/content/dam/autodesk/logos/autodesk-logo-primary-rgb-black-small_forum.png”,
0 Likes
Message 13 of 22

АлексЮстасу
Advisor
Advisor

Thank you!
Yeah, I've seen the opportunity to use Dynamo.
But many do not have 2020 or 2021.

 

I'm not a programmer at all. Is it possible to get code from the Dynamo script that can be used for .Net or ObjectARX programs?

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

Message 14 of 22

ocseyar
Explorer
Explorer

It's a useful work. But what I want to do is the opposite. I want to transfer object data to property sets. Is there a way to do this? Can you help me about it

0 Likes
Message 15 of 22

frederic-classon
Autodesk
Autodesk

Hi there! Just posted a Video and a Script of the other way around (Property Set to Object Data Conversion) on my AKN Profile: https://autode.sk/3qH1Gm6  Hope that helps as a starting point. Best regards

“https://damassets.autodesk.net/content/dam/autodesk/logos/autodesk-logo-primary-rgb-black-small_forum.png”,
Message 16 of 22

ocseyar
Explorer
Explorer
Thank you so much for your help! Appreciate it
0 Likes
Message 17 of 22

MGO-Norsyn
Advocate
Advocate

I've created a routine to convert OdTables to PropertySets as it is what I needed. Below is the code:

 

 

 

public static class ODDataConverter
    {
        //Loops through all ODTables in document and creates corresponding PropertySetDefinitions
        public static void oddatacreatepropertysetsdefs()
        {
            DocumentCollection docCol = Application.DocumentManager;
            Database localDb = docCol.MdiActiveDocument.Database;
            Editor ed = docCol.MdiActiveDocument.Editor;
            Document doc = docCol.MdiActiveDocument;
            CivilDocument civilDoc = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument;

            //Reference ODTables
            Tables tables = HostMapApplicationServices.Application.ActiveProject.ODTables;

            StringCollection tableNames = tables.GetTableNames();

            foreach (string name in tableNames)
            {
                Table curTable = tables[name];

                try
                {
                    // (1) create prop set def
                    PropertySetDefinition propSetDef = new PropertySetDefinition();
                    propSetDef.SetToStandard(localDb);
                    propSetDef.SubSetDatabaseDefaults(localDb);
                    // alternatively, you can use dictionary's NewEntry
                    // Dim dictPropSetDef = New DictionaryPropertySetDefinitions(db)
                    // Dim propSetDef As PropertySetDefinition =
                    // dictPropSetDef.NewEntry()

                    // General tab
                    propSetDef.Description = name;
                    // Applies To tab
                    // apply to objects or styles. True if style, False if objects
                    bool isStyle = false;
                    var appliedTo = new StringCollection();

                    //Add more RXClasses here as you need
                    appliedTo.Add("AcDb3dPolyline");
                    propSetDef.SetAppliesToFilter(appliedTo, isStyle);

                    FieldDefinitions defs = curTable.FieldDefinitions;
                    int defsCount = defs.Count;
                    for (int i = 0; i < defsCount; i++)
                    {
                        FieldDefinition curFd = defs[i];
                        string fieldDefName = curFd.Name;
                        string fieldDefDescription = curFd.Description;
                        DataType fieldType = curFd.Type;


                        // Definition tab
                        // (2) we can add a set of property definitions. 
                        // We first make a container to hold them.
                        // This is the main part. A property set definition can contain
                        // a set of property definition.
                        // (2.1) let's first add manual property.
                        // Here we use text type
                        var propDefManual = new PropertyDefinition();
                        propDefManual.SetToStandard(localDb);
                        propDefManual.SubSetDatabaseDefaults(localDb);
                        propDefManual.Name = fieldDefName;
                        propDefManual.Description = fieldDefDescription;
                        propDefManual.DataType = GetCorrespondingPropertyDataType(fieldType);
                        propDefManual.DefaultData = ConvertDefaultValue(curFd);
                        // add to the prop set def
                        propSetDef.Definitions.Add(propDefManual);
                    }

                    using (Transaction tx = localDb.TransactionManager.StartTransaction())
                    {
                        //check if prop set already exists
                        var dictPropSetDef = new DictionaryPropertySetDefinitions(localDb);
                        if (dictPropSetDef.Has(name, tx))
                        {
                            ed.WriteMessage("\nError - the property set defintion already exists: " + name);
                            tx.Abort();
                            continue;
                        }

                        dictPropSetDef.AddNewRecord(name, propSetDef);
                        tx.AddNewlyCreatedDBObject(propSetDef, true);
                        tx.Commit();
                    }

                }
                catch (Exception ex)
                {
                    ed.WriteMessage("\nError while creating Property Set definitions: " + ex.ToString());
                    return;
                }
            }

            ed.WriteMessage("\nFinished!");
        }

        public static void attachpropertysetstoobjects()
        {
            DocumentCollection docCol = Application.DocumentManager;
            Database localDb = docCol.MdiActiveDocument.Database;
            Editor ed = docCol.MdiActiveDocument.Editor;
            Document doc = docCol.MdiActiveDocument;
            CivilDocument civilDoc = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument;

            //Reference ODTables
            Tables tables = HostMapApplicationServices.Application.ActiveProject.ODTables;
            DictionaryPropertySetDefinitions dictPropSetDef = new DictionaryPropertySetDefinitions(localDb);

            using (Transaction tx = localDb.TransactionManager.StartTransaction())
            {
                //I need to work with 3d polylines
                //Change here to add other types of objects
                HashSet<Polyline3d> p3ds = localDb.HashSetOfType<Polyline3d>(tx);

                foreach (Polyline3d p3d in p3ds)
                {
                    using (Records records =
                        tables.GetObjectRecords(0, p3d.Id, Autodesk.Gis.Map.Constants.OpenMode.OpenForRead, false))
                    {
                        int recordsCount = records.Count;
                        for (int i = 0; i < recordsCount; i++)
                        {
                            Record record = records[i];
                            int recordCount = record.Count;
                            string tableName = record.TableName;

                            //Attach correct property set to entity
                            Oid dictId = dictPropSetDef.GetAt(tableName);
                            if (dictId == Oid.Null)
                            {
                                ed.WriteMessage($"\nODTable {tableName} does not have corresponding propertyset!" +
                                    $"Create propertysets first.");
                                tx.Abort();
                                return;
                            }
                            //Add property set to the object
                            p3d.CheckOrOpenForWrite();
                            PropertyDataServices.AddPropertySet(p3d, dictId);
                        }
                    }
                }
                tx.Commit();
            }
        }

        public static void populatepropertysetswithoddata()
        {
            DocumentCollection docCol = Application.DocumentManager;
            Database localDb = docCol.MdiActiveDocument.Database;
            Editor ed = docCol.MdiActiveDocument.Editor;
            Document doc = docCol.MdiActiveDocument;
            CivilDocument civilDoc = Autodesk.Civil.ApplicationServices.CivilApplication.ActiveDocument;

            //Reference ODTables
            Tables tables = HostMapApplicationServices.Application.ActiveProject.ODTables;
            DictionaryPropertySetDefinitions dictPropSetDef = new DictionaryPropertySetDefinitions(localDb);

            using (Transaction tx = localDb.TransactionManager.StartTransaction())
            {
                try
                {
                    //I need to work with 3d polylines
                    //Change here to add other types of objects
                    HashSet<Polyline3d> p3ds = localDb.HashSetOfType<Polyline3d>(tx);

                    foreach (Polyline3d p3d in p3ds)
                    {
                        ObjectIdCollection psIds = PropertyDataServices.GetPropertySets(p3d);
                        List<PropertySet> pss = new List<PropertySet>();
                        foreach (Oid oid in psIds) pss.Add(oid.Go<PropertySet>(tx, OpenMode.ForWrite));

                        using (Records records =
                            tables.GetObjectRecords(0, p3d.Id, Autodesk.Gis.Map.Constants.OpenMode.OpenForRead, false))
                        {
                            int recordsCount = records.Count;
                            for (int i = 0; i < recordsCount; i++)
                            {
                                Record record = records[i];
                                string tableName = record.TableName;

                                PropertySet propertySet = pss.Find(x => x.PropertySetDefinitionName == tableName);
                                if (propertySet == null)
                                {
                                    tx.Abort();
                                    ed.WriteMessage($"\nPropertySet with the name {tableName} could not be found!");
                                    return;
                                }

                                Table table = tables[tableName];
                                FieldDefinitions fDefs = table.FieldDefinitions;
                                int fieldsCount = fDefs.Count;

                                for (int j = 0; j < fieldsCount; j++)
                                {
                                    FieldDefinition fDef = fDefs[j];
                                    string fieldName = fDef.Name;

                                    int columnIndex = fDefs.GetColumnIndex(fieldName);
                                    MapValue value = record[columnIndex];

                                    int psIdCurrent = propertySet.PropertyNameToId(fieldName);
                                    propertySet.SetAt(psIdCurrent, GetMapValueData(value));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    tx.Abort();
                    ed.WriteMessage(ex.ToString());
                    return;
                }
                tx.Commit();
            }
        }

        private static Autodesk.Aec.PropertyData.DataType GetCorrespondingPropertyDataType(DataType fieldDefType)
        {
            switch (fieldDefType)
            {
                case DataType.UnknownType:
                    return Autodesk.Aec.PropertyData.DataType.Text;
                case DataType.Integer:
                    return Autodesk.Aec.PropertyData.DataType.Integer;
                case DataType.Real:
                    return Autodesk.Aec.PropertyData.DataType.Real;
                case DataType.Character:
                    return Autodesk.Aec.PropertyData.DataType.Text;
                case DataType.Point:
                    return Autodesk.Aec.PropertyData.DataType.Text;
                default:
                    throw new Exception("DataType Default case not implemented!");
            }
        }
        private static object GetMapValueData(MapValue mapValue)
        {
            switch (mapValue.Type)
            {
                case DataType.UnknownType:
                    return mapValue.StrValue;
                case DataType.Integer:
                    return mapValue.Int32Value;
                case DataType.Real:
                    return mapValue.DoubleValue;
                case DataType.Character:
                    return mapValue.StrValue;
                case DataType.Point:
                    return mapValue.Point.ToString();
                default:
                    throw new Exception("DataType Default case not implemented!");
            }
        }
        /// <summary>
        /// Basic default values. Need to implement reading from field definition.
        /// </summary>
        private static object ConvertDefaultValue(FieldDefinition fieldDefinition)
        {
            switch (fieldDefinition.Type)
            {
                case DataType.UnknownType:
                    return "";
                case DataType.Integer:
                    return 0;
                case DataType.Real:
                    return 0;
                case DataType.Character:
                    return "";
                case DataType.Point:
                    return "";
                default:
                    throw new Exception($"Default DataType {fieldDefinition.Type.ToString()} not implemented!");
            }
        }
    }

 

 

Message 18 of 22

MGO-Norsyn
Advocate
Advocate

The above code is implemented to work on 3d polylines. You have to modify the code to add your own object classes to filters and what objects property sets attaches to.

 

Just call the three methods in succession:

 

ODDataConverter.oddatacreatepropertysetsdefs();
ODDataConverter.attachpropertysetstoobjects();
ODDataConverter.populatepropertysetswithoddata();

 

By the way, you need to save, close and reopen the drawing right after running the routines. Because if you run AUDIT after these routines all data is destroyed for some reason. Anyway, that's what happened to me, but the problem went away after a save and reload.

 

Message 19 of 22

АлексЮстасу
Advisor
Advisor

Great!
Thank you!

 

(I was briefly in Denmark, and I still remember with interest)  🙂


But I'm not a programmer at all.
I created this post so that such a converter would be available to users. To simplify the exchange of descriptive non-graphical users data between different AutoCADs. (Since for some reason Autodesk itself hasn't done this and doesn't intend to).
I myself have absolutely no idea how to turn this code into a command.
And how to refine it for all types of AutoCAD primitives and objects.
I hope someone can help do it.

 


-- Alexander, private person, pacifist, english only with translator 🙂 --

Object-modeling _ odclass-odedit.com _ Help

0 Likes
Message 20 of 22

MGO-Norsyn
Advocate
Advocate

You are welcome. I am from Russia originally, but I am living in Denmark for 30+ years...
Regarding your request, I think one could change the filter and the types collected to Entity then you would get ALL objects in drawing and thus it would work for everything.
You could start making a simple command by following the Autodesk MyFirstAutocadCommand tutorial (I think it is named) or something like that. Then you could add the above code to your project and call those three methods (functions) as I wrote...

In our time everybody needs to be able to program to some extent, as everything is digital going forward and those who can't become B-citizens.

Also, Autodesk is hampering the progress by hanging on to their old legacy software and only very slowly adding more flexibility and features to their products. This can be, to some degree, alleviated by learning to code, which I was forced to do.