xBimTeam / XbimEssentials

A .NET library to work with data in the IFC format. This is the core component of the Xbim Toolkit
https://xbimteam.github.io/
Other
485 stars 172 forks source link

Extracting Predefined values for IFC Schema #477

Closed HL-code closed 1 year ago

HL-code commented 1 year ago

I need a list of all subtypes of IfcProduct with Predefined types in a given IFC version. Something like this: IfcActuator.ELECTRICACTUATOR IfcActuator.HANDOPERATEDACTUATOR IfcActuator.HYDRAULICACTUATOR IfcActuator.PNEUMATICACTUATOR IfcActuator.THERMOSTATICACTUATOR IfcAirTerminal.DIFFUSER IfcAirTerminal.GRILLE IfcAirTerminal.LOUVRE IfcAirTerminal.REGISTER

For example getting PropertySets is quite easy in XBIM like this:

Definitions<PropertySetDef> propertySetDefs = new Definitions<PropertySetDef>(Xbim.Properties.Version.IFC4);
propertySetDefs.LoadAllDefault();

This will load all available IFC4 propertysets for display in a UI.

I need to do the same thing for all subtypes of IfcProduct, like IfcWall, IfcDoor etc etc I figure its probably in the XBim.Essentials package but i cant find the methods i need. The methods i find all requires an IFC file which i dont have. This is before a user exports an IFC file.

Anyone have some pointers in the right direction ? :-)

Thank you.

andyward commented 1 year ago

I'm not aware of this being in Essentials, and arguably it should be a helper or perhaps in xbim.PropertySets - it's a simple bit of code to implement using ExpressMetaData classes. You can access the ExpressMetaData, which describes an IFC schema, without loading a model. E.g. to get PredefinedType values for all Ifc4 Products:

    // Use any type in the schema to get the Module. You have to do get the metadata for each IFC schema you want to support - Ifc2x3 etc
    Module mod = typeof(Xbim.Ifc4.Kernel.IfcRoot).Module;
    ExpressMetaData meta = ExpressMetaData.GetMetadata(mod);

    // Find all sub types of IfcProduct. You will probably also want to do IfcTypeObjects as well
    var product = meta.ExpressType(typeof(Xbim.Ifc4.Kernel.IfcProduct));

    foreach(ExpressType type in product.SubTypes)
    {
        ExpressMetaProperty predefinedProp = type.Properties.Values.FirstOrDefault(v => v.Name == nameof(IIfcDoor.PredefinedType));
        if (predefinedProp != null)
        {
            // It has a PredefinedType property - get the underlyng Type
            Type enumType = predefinedProp.PropertyInfo.PropertyType;
            enumType = Nullable.GetUnderlyingType(enumType) ?? enumType;

            // Get the values
            Array enumValues = Enum.GetValues(enumType);
            foreach (var predefinedValue in enumValues)
            {
                Console.WriteLine("{0}: {1}", type.Name, predefinedValue);
            }

        }
    }

Essentially the steps are:

  1. Enumerate all possible IFC types in your schema that have a field named "PredefinedType", using the ExpressType.Properties dictionary
  2. Get the PropertyInfo
  3. Get the underlying ProprertyType and unwap any nullable
  4. Enumerate the Enum values against the type with Enum.GetValues(type)

We're doing something similar in the xbim IDS project.

HL-code commented 1 year ago

Thank you Andy

It did work and then i realized (or at least pretty sure) the descriptions for the types is not available. `// Use any type in the schema to get the Module. You have to do get the metadata for each IFC schema you want to support - Ifc2x3 etc Module mod = typeof(Xbim.Ifc4.Kernel.IfcRoot).Module; ExpressMetaData meta = ExpressMetaData.GetMetadata(mod);

        // Find all sub types of IfcProduct. You will probably also want to do IfcTypeObjects as well
        var product = meta.ExpressType(typeof(Xbim.Ifc4.Kernel.IfcProduct));

        var ifcElements = product.SubTypes.OfType<IfcElement>();

        foreach (ExpressType type in product.SubTypes)
        {
            foreach(var subType in type.AllSubTypes)
            {

                ExpressMetaProperty predefinedProp = subType.Properties.Values.Where(t => t.Name == "PredefinedType").FirstOrDefault();
                if (predefinedProp != null)
                {
                    // It has a PredefinedType property - get the underlyng Type
                    Type enumType = predefinedProp.PropertyInfo.PropertyType;
                    enumType = Nullable.GetUnderlyingType(enumType) ?? enumType;

                    //// Get the values
                    if (enumType.IsEnum)
                    {
                        Array enumValues = Enum.GetValues(enumType);
                        foreach (var predefinedValue in enumValues)
                        {
                            Console.WriteLine("{0}: {1}", type.Name, predefinedValue);
                        }
                    }
                }

                ExpressMetaProperty descriptionProp = subType.Properties.Values.Where(t => t.Name == "Description").FirstOrDefault();
                if(descriptionProp != null)
                {

                }

            }

        }`

Its still WIP but im looking into this file: https://github.com/buildingSMART/IFC4.3.x-output/blob/master/IFC.json

It contains descriptions for each type and enum. But no domain information (like IfcSharedBldgElements); I suspect i will use your XBIM code and then get the descriptions from the json file. Attached the csv version of it if anyone needs it. Domain is not critical but its nice to have something to filter on in the UI.

Thanks for the advice.

IFC.csv

martin1cerny commented 1 year ago

Unfortunately, I'm not aware of a unified source of names and descriptions like this for all IFC schema versions. Thank you for this one. In the future, I'd like to gather comments for all of them and make them part of our generated code to help developers understand the schema as they code. I created a project item #478 for this task.

HL-code commented 1 year ago

Ok tnx!