/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #include "ifcparse/macros.h" #ifndef IfcSchema #define IfcSchema Ifc2x3 #endif #include "ifcparse/IfcFile.h" #include "ifcparse/IfcLogger.h" #include INCLUDE_SCHEMA(ifcparse, IfcSchema) #include INCLUDE_SCHEMA_DEFINITIONS(ifcparse, IfcSchema) #include #include #include #include #include #include #ifdef _MSC_VER #define strcasecmp _stricmp #endif #ifndef SCHEMA_SEQ static_assert(false, "A boost preprocessor sequence of schema identifiers is needed for this file to compile."); #endif #include #if USE_VLD #include #endif template struct is_ifc4_or_higher : std::false_type {}; template struct is_ifc4_or_higher> : std::true_type { }; typedef std::map> element_properties; #ifdef SCHEMA_HAS_IfcBuildingElement typedef IfcSchema::IfcBuildingElement element_t; #else typedef IfcSchema::IfcBuiltElement element_t; #endif std::string format_string(const AttributeValue& argument) { // Argument is a runtime tagged variant for the various data types in a IFC model, // in this particular case we only care about flattening it to a string. // @todo mostly duplicated from XmlSerializer.cpp if (argument.isNull()) { return "-"; } auto argument_type = argument.type(); switch (argument_type) { case IfcUtil::Argument_BOOL: { const bool b = argument; return b ? "true" : "false"; } case IfcUtil::Argument_DOUBLE: { const double d = argument; std::stringstream stream; stream << std::setprecision(std::numeric_limits< double >::max_digits10) << d; return stream.str(); break; } case IfcUtil::Argument_STRING: case IfcUtil::Argument_ENUMERATION: { return static_cast(argument); break; } case IfcUtil::Argument_INT: { const int v = argument; std::stringstream stream; stream << v; return stream.str(); break; } } return "?"; } template void process_pset(element_properties& props, const T* inst) { // Process an individual Property or Quantity set. if (auto pset = inst->template as()) { if (!pset->Name()) { return; } auto ps = pset->HasProperties(); for (auto it = ps->begin(); it != ps->end(); ++it) { auto& p = *it; if (auto singleval = p->template as()) { std::string propname, propvalue; if constexpr (is_ifc4_or_higher::value) { if (!singleval->Name()) { continue; } propname = *singleval->Name(); } if constexpr (!is_ifc4_or_higher::value) { propname = singleval->Name(); } if (!singleval->NominalValue()) { propvalue = "-"; } else { props[*pset->Name()][propname] = format_string(singleval->NominalValue()->template as()->get_attribute_value(0)); } } } } if (auto qset = inst->template as()) { if (!qset->Name()) { return; } auto qs = qset->Quantities(); for (auto it = qs->begin(); it != qs->end(); ++it) { auto& q = *it; if (q->template as() && q->get_attribute_value(3).type() == IfcUtil::Argument_DOUBLE) { double v = q->get_attribute_value(3); props[*qset->Name()][q->Name()] = std::to_string(v); } } } if constexpr (is_ifc4_or_higher::value) { if (auto extprops = inst->template as()) { // @todo } } } template void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefinition* inst) { // Extracts the property definitions for an IFC instance. if (auto tyob = inst->template as()) { if (tyob->HasPropertySets()) { auto defs = *tyob->HasPropertySets(); for (auto it = defs->begin(); it != defs->end(); ++it) { auto& def = *it; process_pset(props, def); } } } if constexpr (is_ifc4_or_higher::value) { if (auto mdef = inst->template as()) { auto defs = mdef->HasProperties(); for (auto it = defs->begin(); it != defs->end(); ++it) { auto& def = *it; process_pset(props, def); } } if (auto pdef = inst->template as()) { auto defs = pdef->HasProperties(); for (auto it = defs->begin(); it != defs->end(); ++it) { auto& def = *it; process_pset(props, def); } } } if (auto ob = inst->template as()) { if constexpr (is_ifc4_or_higher::value) { auto rels = ob->IsTypedBy(); for (auto it = rels->begin(); it != rels->end(); ++it) { auto& rel = *it; get_psets_s(props, rel->RelatingType()); } } { auto rels = ob->IsDefinedBy(); for (auto it = rels->begin(); it != rels->end(); ++it) { auto& rel = *it; if (auto bytype = rel->template as()) { get_psets_s(props, bytype->RelatingType()); } else if (auto byprops = rel->template as()) { process_pset(props, byprops->RelatingPropertyDefinition()); } } } } } // What follows is machinery to create a preprocessor-based dispatch mechanism to dispatch to the // correct get_psets_s() based on inst->declaration().schema()->name(). #define EXPAND_AND_CONCATENATE(elem) Ifc##elem #define GENERATE_LITERAL_STRING(elem) "Ifc" # elem #define TEST_AND_DISPATCH(r, data, elem) \ if (strcasecmp(schema_name, GENERATE_LITERAL_STRING(elem)) == 0) { get_psets_s(props, inst->as()); } void get_psets(element_properties& props, const IfcUtil::IfcBaseClass* inst) { auto schema_name = inst->declaration().schema()->name().c_str(); BOOST_PP_SEQ_FOR_EACH(TEST_AND_DISPATCH, , SCHEMA_SEQ) } int main(int argc, char** argv) { if (argc != 2) { std::cout << "usage: IfcParseExamples " << std::endl; return 1; } // Redirect the output (both progress and log) to stdout Logger::Root().SetOutput(&std::cout, &std::cout); // Parse the IFC file provided in argv[1] IfcParse::IfcFile file(argv[1]); if (!file.good()) { std::cout << "Unable to parse .ifc file" << std::endl; return 1; } // Lets get a list of IfcBuildingElements, this is the parent // type of things like walls, windows and doors. // entitiesByType is a templated function and returns a // templated class that behaves like a std::vector. // Note that the return types are all typedef'ed as members of // the generated classes, ::list for the templated vector class, // ::ptr for a shared pointer and ::it for an iterator. // We will simply iterate over the vector and print a string // representation of the entity to stdout. // // Secondly, lets find out which of them are IfcWindows. // In order to access the additional properties that windows // have on top af the properties of building elements, // we need to cast them to IfcWindows. Since these properties // are optional we need to make sure the properties are // defined for the window in question before accessing them. auto elements = file.instances_by_type(); std::cout << "Found " << elements->size() << " elements in " << argv[1] << ":" << std::endl; for (auto it = elements->begin(); it != elements->end(); ++it) { const auto* element = *it; element->toString(std::cout); std::cout << std::endl; const IfcSchema::IfcWindow* window; if ((window = element->as()) != 0) { if (window->OverallWidth() && window->OverallHeight()) { const double area = *window->OverallWidth() * *window->OverallHeight(); std::cout << "The area of this window is " << area << std::endl; } } element_properties props; get_psets(props, element); for (auto& ps : props) { std::cout << ps.first << std::endl; std::cout << std::string(ps.first.size(), '=') << std::endl; size_t max_key_len = 0; for (auto& p : ps.second) { if (p.first.size() > max_key_len) { max_key_len = p.first.size(); } } for (auto& p : ps.second) { std::cout << p.first << std::string(max_key_len - p.first.size(), ' ') << ":" << p.second << std::endl; } std::cout << std::endl; } } }