0

I have a BIM model in IFC format and I want to add a new property, say cost, to every object in the model using Xbim. I am building a .NET application. The following code works well, except, the property is also added to storeys, buildings and sites - and I only want to add it to the lowest-level objects that nest no other objects.

To begin with, I have tried various methods to print the "related objects" of each object, thinking that I could filter out any objects with non-null related objects. This has led me to look at this:

IfcRelDefinesByType.RelatedObjects (http://docs.xbim.net/XbimDocs/html/7fb93e55-dcf7-f6da-0e08-f8b5a70accf2.htm) from thinking that RelatedObjects (https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifckernel/lexical/ifcreldecomposes.htm) would contain this information.

But I have not managed to implement working code from this documentation.

Here is my code:

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xbim.Ifc;
using Xbim.Ifc2x3.Interfaces;
using Xbim.Ifc4.Kernel;
using Xbim.Ifc4.MeasureResource;
using Xbim.Ifc4.PropertyResource;
using Xbim.Ifc4.Interfaces;
using IIfcProject = Xbim.Ifc4.Interfaces.IIfcProject;

namespace MyPlugin0._1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            outputBox.AppendText("Plugin launched successfully");
        }


        private void button1_Click(object sender, EventArgs e)
        {
            // Setup the editor
            var editor = new XbimEditorCredentials
            {
                ApplicationDevelopersName = "O",
                ApplicationFullName = "MyPlugin",
                ApplicationIdentifier = "99990100",
                ApplicationVersion = "0.1",
                EditorsFamilyName = "B",
                EditorsGivenName = "O",
                EditorsOrganisationName = "MyWorkplace"
            };

            // Choose an IFC file to work with        
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.ShowDialog();
            string filename = dialog.FileName;
            string newLine = Environment.NewLine;
            
            // Check if the file is valid and continue
            if (!filename.ToLower().EndsWith(".ifc"))
            {
                // Output error if the file is the wrong format
                outputBox.AppendText(newLine + "Error: select an .ifc-file");
            }
            else
            {
                // Open the selected file (## Not sure what the response is to a corrupt/invalid .ifc-file)
                using (var model = IfcStore.Open(filename, editor, 1.0))
                {
                    // Output success when the file has been opened
                    string reversedName = Form1.ReversedString(filename);
                    int filenameShortLength = reversedName.IndexOf("\\");
                    string filenameShort = filename.Substring(filename.Length - filenameShortLength, filenameShortLength);
                    outputBox.AppendText(newLine + filenameShort + " opened successfully for editing");

                    

                    ////////////////////////////////////////////////////////////////////
                    // Get all the objects in the model ( ### lowest level only??? ###)
                    var objs = model.Instances.OfType<IfcObjectDefinition>();
                    ////////////////////////////////////////////////////////////////////



                    // Create and store a new property
                    using (var txn = model.BeginTransaction("Store Costs"))
                    {
                        // Iterate over all the walls to initiate the Point Source property
                        foreach (var obj in objs)
                        {

                            // Create new property set to host properties
                            var pSetRel = model.Instances.New<IfcRelDefinesByProperties>(r =>
                            {
                                r.GlobalId = Guid.NewGuid();
                                r.RelatingPropertyDefinition = model.Instances.New<IfcPropertySet>(pSet =>
                                {
                                    pSet.Name = "Economy";
                                    pSet.HasProperties.Add(model.Instances.New<IfcPropertySingleValue>(p =>
                                    {
                                        p.Name = "Cost";
                                        p.NominalValue = new IfcMonetaryMeasure(200.00); // Default Currency set on IfcProject
                                    }));
                                });
                            });

                            // Add property to the object
                            pSetRel.RelatedObjects.Add(obj);

                            // Rename the object
                            outputBox.AppendText(newLine + "Cost property added to " + obj.Name);
                            obj.Name += "_withCost";

                            
                            //outputBox.AppendText(newLine + obj.OwnerHistory.ToString());
                            
                        }

                        // Commit changes to this model
                        txn.Commit();
                    };

                    // Save the changed model with a new name. Does not overwrite existing files but generates a unique name
                    string newFilename = filenameShort.Substring(0, filenameShort.Length - 4) + "_Modified.IFC";
                    int i = 1;
                    while (File.Exists(newFilename))
                    {
                        newFilename = filenameShort.Substring(0, filenameShort.Length - 4) + "_Modified(" + i.ToString() + ").IFC";
                        i += 1;
                    }
                    model.SaveAs(newFilename); // (!) Gets stored in the project folder > bin > Debug
                    outputBox.AppendText(newLine + newFilename + " has been saved");
                       
                };
            }
            
        }



        // Reverse string-function
        static string ReversedString(string text)
        {
            if (text == null) return null;
            char[] array = text.ToCharArray();
            Array.Reverse(array);
            return new String(array);
        }


        private void Form1_Load(object sender, EventArgs e)
        {

        }


        
    }
}

1 Answers1

0

You're starting out by getting too broad a set of elements in the model. Pretty much everything in an IFC model will be classed as (or 'derived from') an instance of IfcObjectDefinition - including Spatial concepts (spaces, levels, zones etc) as well as more abstract concepts of Actors (people), Resources and the like.

You'd be better off filtering down objs to the more specific types such as IfcElement, IfcBuildingElement - or even the more real world elements below (IfcWindow, IfcDoor etc.)

// Get all the building elements in the model 
var objs = model.Instances.OfType<IfcBuildingElement>();

You could also filter by more specific clauses more than just their type by using the other IFC relationships.

Andy Ward
  • 324
  • 2
  • 7
  • Thank you. However, using IfcBuildingElement and IfcElement returns zero objects (objs.Count() = 0). My IFC file is a CAD-assembly exported directly as IFC - is the problem that the elements in the IFC file are not defined as BuildingElements or Elements by me? I am eventually going to use this plugin on "homemade" IFC BIMs and would prefer a way to robustly pick all the lowest level objects – Oskar Billington Jul 21 '20 at 06:59
  • Looking at the using statement you seem to be mixing up Ifc2x3 and Ifc4 entities. The recommended approach is to use only the IFC4 interfaces to access types since this will pick up both 2x3 and Ifc4 implementations of the entities. So try removing `using Xbim.Ifc2x3.Interfaces;` ...and change the query to: `var objs = model.Instances.OfType();` Note the extra 'I' in IfcBuildingElement – Andy Ward Jul 21 '20 at 08:49
  • Awesome! Works. Although now this line (below) gives the error: _cannot convert from IIfcBuildingElement to IfcObjectDefinition_. But I can run the original code for each ObjectDefinition iff the Name also exists in the BuildingElement list, or look for an equivalent method that works with IIfcBuildingElements. `/* Add property to the object */ pSetRel.RelatedObjects.Add(obj);` – Oskar Billington Jul 21 '20 at 13:48