2018-03-04 10:43:40 +01:00
|
|
|
/********************************************************************************
|
|
|
|
|
* *
|
|
|
|
|
* 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 <http://www.gnu.org/licenses/>. *
|
|
|
|
|
* *
|
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
|
#include <boost/property_tree/xml_parser.hpp>
|
|
|
|
|
#include <boost/version.hpp>
|
2018-05-13 08:45:33 +02:00
|
|
|
#include <boost/foreach.hpp>
|
2018-03-04 10:43:40 +01:00
|
|
|
|
|
|
|
|
#include "XmlSerializer.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "../../ifcparse/IfcSIPrefix.h"
|
2019-03-08 11:40:16 +01:00
|
|
|
#include "../../ifcparse/utils.h"
|
2023-10-24 14:12:42 +02:00
|
|
|
#include "../../ifcparse/IfcLogger.h"
|
2018-03-04 10:43:40 +01:00
|
|
|
|
|
|
|
|
using boost::property_tree::ptree;
|
|
|
|
|
|
2018-03-16 16:04:48 +01:00
|
|
|
#include "XmlSerializer.h"
|
|
|
|
|
|
|
|
|
|
namespace {
|
2023-03-21 20:15:01 +01:00
|
|
|
struct POSTFIX_SCHEMA(factory_t) {
|
2018-03-16 16:04:48 +01:00
|
|
|
XmlSerializer* operator()(IfcParse::IfcFile* file, const std::string& xml_filename) const {
|
2023-03-21 20:15:01 +01:00
|
|
|
POSTFIX_SCHEMA(XmlSerializer)* s = new POSTFIX_SCHEMA(XmlSerializer)(file, xml_filename);
|
2018-03-16 16:04:48 +01:00
|
|
|
s->setFile(file);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) {
|
|
|
|
|
static const std::string schema_name = STRINGIFY(IfcSchema);
|
2023-03-21 20:15:01 +01:00
|
|
|
POSTFIX_SCHEMA(factory_t) factory;
|
2018-03-16 16:04:48 +01:00
|
|
|
mapping->bind(schema_name, factory);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
namespace {
|
|
|
|
|
|
2018-03-16 16:04:48 +01:00
|
|
|
// TODO: Make this a member of XmlSerializer?
|
2023-03-21 20:15:01 +01:00
|
|
|
std::map<std::string, std::string> POSTFIX_SCHEMA(argument_name_map);
|
2018-03-04 10:43:40 +01:00
|
|
|
|
|
|
|
|
// Format an IFC attribute and maybe returns as string. Only literal scalar
|
|
|
|
|
// values are converted. Things like entity instances and lists are omitted.
|
2026-01-04 10:40:02 +01:00
|
|
|
std::optional<std::string> format_attribute(ifcopenshell::geometry::abstract_mapping* mapping, AttributeValue argument, IfcUtil::ArgumentType argument_type, const std::string& argument_name) {
|
|
|
|
|
std::optional<std::string> value;
|
2018-03-04 10:43:40 +01:00
|
|
|
|
|
|
|
|
// Hard-code lat-lon as it represents an array
|
|
|
|
|
// of integers best emitted as a single decimal
|
|
|
|
|
if (argument_name == "IfcSite.RefLatitude" ||
|
|
|
|
|
argument_name == "IfcSite.RefLongitude")
|
|
|
|
|
{
|
2024-08-23 20:29:07 +02:00
|
|
|
std::vector<int> angle = argument;
|
2018-03-04 10:43:40 +01:00
|
|
|
double deg;
|
|
|
|
|
if (angle.size() >= 3) {
|
|
|
|
|
deg = angle[0] + angle[1] / 60. + angle[2] / 3600.;
|
|
|
|
|
int prec = 8;
|
|
|
|
|
if (angle.size() == 4) {
|
|
|
|
|
deg += angle[3] / (1000000. * 3600.);
|
|
|
|
|
prec = 14;
|
|
|
|
|
}
|
|
|
|
|
std::stringstream stream;
|
|
|
|
|
stream << std::setprecision(prec) << deg;
|
|
|
|
|
value = stream.str();
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(argument_type) {
|
2025-03-12 15:44:14 +00:00
|
|
|
case IfcUtil::Argument_BOOL:
|
|
|
|
|
case IfcUtil::Argument_LOGICAL:{
|
|
|
|
|
const boost::logic::tribool b = argument;
|
|
|
|
|
value = b.value == boost::logic::tribool::indeterminate_value ? "unknown" : b ? "true" : "false";
|
2018-03-04 10:43:40 +01:00
|
|
|
break; }
|
|
|
|
|
case IfcUtil::Argument_DOUBLE: {
|
2024-08-23 20:29:07 +02:00
|
|
|
const double d = argument;
|
2018-03-04 10:43:40 +01:00
|
|
|
std::stringstream stream;
|
2021-06-21 21:37:53 +02:00
|
|
|
stream << std::setprecision (std::numeric_limits< double >::max_digits10) << d;
|
2018-03-04 10:43:40 +01:00
|
|
|
value = stream.str();
|
|
|
|
|
break; }
|
|
|
|
|
case IfcUtil::Argument_STRING:
|
|
|
|
|
case IfcUtil::Argument_ENUMERATION: {
|
2024-08-23 20:29:07 +02:00
|
|
|
value = static_cast<std::string>(argument);
|
2018-03-04 10:43:40 +01:00
|
|
|
break; }
|
|
|
|
|
case IfcUtil::Argument_INT: {
|
2024-08-23 20:29:07 +02:00
|
|
|
const int v = argument;
|
2018-03-04 10:43:40 +01:00
|
|
|
std::stringstream stream;
|
|
|
|
|
stream << v;
|
|
|
|
|
value = stream.str();
|
|
|
|
|
break; }
|
|
|
|
|
case IfcUtil::Argument_ENTITY_INSTANCE: {
|
2026-01-04 10:40:02 +01:00
|
|
|
express::Base e = argument;
|
|
|
|
|
if (e.declaration().as_entity() == nullptr) {
|
|
|
|
|
auto f = e.as<express::DeclaredType>();
|
|
|
|
|
value = format_attribute(mapping, f.get_attribute_value(0), f.get_attribute_value(0).type(), argument_name);
|
|
|
|
|
} else if (e.declaration().is(IfcSchema::IfcSIUnit::Class()) || e.declaration().is(IfcSchema::IfcConversionBasedUnit::Class())) {
|
2018-03-04 10:43:40 +01:00
|
|
|
// Some string concatenation to have a unit name as a XML attribute.
|
|
|
|
|
|
|
|
|
|
std::string unit_name;
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto unit = e.as<IfcSchema::IfcSIUnit>()) {
|
|
|
|
|
unit_name = IfcSchema::IfcSIUnitName::ToString(unit.Name());
|
|
|
|
|
if (unit.Prefix()) {
|
|
|
|
|
unit_name = IfcSchema::IfcSIPrefix::ToString(*unit.Prefix()) + unit_name;
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2026-01-04 10:40:02 +01:00
|
|
|
auto cunit = e.as<IfcSchema::IfcConversionBasedUnit>();
|
|
|
|
|
unit_name = cunit.Name();
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = unit_name;
|
2026-01-04 10:40:02 +01:00
|
|
|
} else if (auto placement = e.as<IfcSchema::IfcLocalPlacement>()) {
|
2023-03-21 20:15:01 +01:00
|
|
|
auto item = mapping->map(e);
|
2023-07-27 16:42:06 +08:00
|
|
|
auto matrix = ifcopenshell::geometry::taxonomy::cast< ifcopenshell::geometry::taxonomy::matrix4>(item);
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2023-03-21 20:15:01 +01:00
|
|
|
std::stringstream stream;
|
2024-12-04 10:39:01 +01:00
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
|
for (int j = 0; j < 4; ++j) {
|
2023-03-21 20:15:01 +01:00
|
|
|
const double trsf_value = matrix->ccomponents()(j, i);
|
|
|
|
|
stream << std::setprecision (std::numeric_limits< double >::max_digits10) << trsf_value << " ";
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
2023-03-21 20:15:01 +01:00
|
|
|
}
|
|
|
|
|
value = stream.str();
|
|
|
|
|
|
2023-07-27 16:42:06 +08:00
|
|
|
#ifdef TAXONOMY_USE_NAKED_PTR
|
2023-03-21 20:15:01 +01:00
|
|
|
delete item;
|
2023-07-27 16:42:06 +08:00
|
|
|
#endif
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
break; }
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Appends to a node with possibly existing attributes
|
2026-01-04 10:40:02 +01:00
|
|
|
ptree* format_entity_instance(ifcopenshell::geometry::abstract_mapping* mapping, const express::Base& instance, ptree& child, ptree& tree, bool as_link = false) {
|
|
|
|
|
const unsigned n = instance.declaration().as_entity()->attribute_count();
|
2018-03-04 10:43:40 +01:00
|
|
|
for (unsigned i = 0; i < n; ++i) {
|
2018-08-05 19:14:10 -03:00
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
instance.get_attribute_value(i);
|
2018-08-15 17:10:29 +02:00
|
|
|
} catch (const std::exception&) {
|
2018-09-04 13:23:11 +02:00
|
|
|
Logger::Error("Expected " + boost::lexical_cast<std::string>(n) + " attributes for:", instance);
|
2018-08-10 14:57:33 +02:00
|
|
|
break;
|
2018-08-05 19:14:10 -03:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
auto argument = instance.get_attribute_value(i);
|
2024-08-23 20:29:07 +02:00
|
|
|
if (argument.isNull()) continue;
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
std::string argument_name = instance.declaration().as_entity()->attribute_by_index(i)->name();
|
2018-03-04 10:43:40 +01:00
|
|
|
std::map<std::string, std::string>::const_iterator argument_name_it;
|
2023-03-21 20:15:01 +01:00
|
|
|
argument_name_it = POSTFIX_SCHEMA(argument_name_map).find(argument_name);
|
|
|
|
|
if (argument_name_it != POSTFIX_SCHEMA(argument_name_map).end()) {
|
2018-03-04 10:43:40 +01:00
|
|
|
argument_name = argument_name_it->second;
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
const IfcUtil::ArgumentType argument_type = instance.get_attribute_value(i).type();
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
const std::string qualified_name = instance.declaration().name() + "." + argument_name;
|
|
|
|
|
std::optional<std::string> value;
|
2018-03-04 10:43:40 +01:00
|
|
|
try {
|
2023-03-21 20:15:01 +01:00
|
|
|
value = format_attribute(mapping, argument, argument_type, qualified_name);
|
2018-03-04 10:43:40 +01:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
Logger::Error(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
if (as_link) {
|
|
|
|
|
if (argument_name == "id") {
|
|
|
|
|
child.put("<xmlattr>.xlink:href", std::string("#") + *value);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::stringstream stream;
|
|
|
|
|
stream << "<xmlattr>." << argument_name;
|
|
|
|
|
child.put(stream.str(), *value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
return &tree.add_child(instance.declaration().name(), child);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Formats an entity instances as a ptree node, and insert into the DOM. Recurses
|
|
|
|
|
// over the entity attributes and writes them as xml attributes of the node.
|
2026-01-04 10:40:02 +01:00
|
|
|
ptree* format_entity_instance(ifcopenshell::geometry::abstract_mapping* mapping, const express::Base& instance, ptree& tree, bool as_link = false) {
|
2018-03-04 10:43:40 +01:00
|
|
|
ptree child;
|
2023-03-21 20:15:01 +01:00
|
|
|
return format_entity_instance(mapping, instance, child, tree, as_link);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
std::string qualify_unrooted_instance(const express::Base& inst) {
|
|
|
|
|
return inst.declaration().name() + "_" + std::to_string(inst.id());
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A function to be called recursively. Template specialization is used
|
|
|
|
|
// to descend into decomposition, containment and property relationships.
|
|
|
|
|
template <typename A>
|
2026-01-04 10:40:02 +01:00
|
|
|
ptree* descend(ifcopenshell::geometry::abstract_mapping* mapping, A instance, ptree& tree, express::Base parent = express::Base()) {
|
|
|
|
|
if (instance.declaration().is(IfcSchema::IfcObjectDefinition::Class())) {
|
|
|
|
|
return descend(mapping, instance.template as<IfcSchema::IfcObjectDefinition>(), tree, parent);
|
2018-03-04 10:43:40 +01:00
|
|
|
} else {
|
2023-03-21 20:15:01 +01:00
|
|
|
return format_entity_instance(mapping, instance, tree);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns related entity instances using IFC's objectified relationship
|
|
|
|
|
// model. The second and third argument require a member function pointer.
|
|
|
|
|
template <typename T, typename U, typename V, typename F, typename G>
|
2026-01-04 10:40:02 +01:00
|
|
|
auto get_related(T t, F f, G g) {
|
|
|
|
|
auto li = (t.*f)();
|
|
|
|
|
std::vector<V> acc;
|
|
|
|
|
for (auto& u : li) {
|
2022-01-29 09:43:33 +01:00
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
auto vs = (u.as<U>().*g)();
|
|
|
|
|
if constexpr (std::is_base_of_v<express::Base, decltype(vs)>) {
|
|
|
|
|
if (auto vv = vs.as<V>()) {
|
|
|
|
|
acc.push_back(vv);
|
|
|
|
|
}
|
|
|
|
|
} else if constexpr (std::is_base_of_v<express::Select, decltype(vs)>) {
|
|
|
|
|
if (auto vv = vs.concrete().as<V>()) {
|
|
|
|
|
acc.push_back(vv);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (auto& v : vs) {
|
|
|
|
|
if (auto vv = v.as<V>()) {
|
|
|
|
|
acc.push_back(vv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-29 09:43:33 +01:00
|
|
|
} catch (IfcParse::IfcException& e) {
|
|
|
|
|
Logger::Error(e);
|
|
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
return acc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Descends into the tree by recursing into IfcRelContainedInSpatialStructure,
|
|
|
|
|
// IfcRelDecomposes, IfcRelDefinesByType, IfcRelDefinesByProperties relations.
|
|
|
|
|
template <>
|
2026-01-04 10:40:02 +01:00
|
|
|
ptree* descend(ifcopenshell::geometry::abstract_mapping* mapping, const IfcSchema::IfcObjectDefinition& product, ptree& tree, express::Base parent) {
|
|
|
|
|
if (product.declaration().is(IfcSchema::IfcElement::Class())) {
|
|
|
|
|
auto voids = product.as<IfcSchema::IfcElement>().FillsVoids();
|
|
|
|
|
if (voids.size() == 1 && voids.front().RelatingOpeningElement() != parent) {
|
2020-03-11 15:16:18 +01:00
|
|
|
// Fills are placed under their corresponding opening, return early to avoid duplication.
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree& child = *format_entity_instance(mapping, product, tree);
|
2020-03-11 15:16:18 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto opening = product.as<IfcSchema::IfcOpeningElement>()) {
|
|
|
|
|
auto fills = get_related<IfcSchema::IfcOpeningElement, IfcSchema::IfcRelFillsElement, IfcSchema::IfcElement>(
|
2020-03-11 15:16:18 +01:00
|
|
|
opening, &IfcSchema::IfcOpeningElement::HasFillings, &IfcSchema::IfcRelFillsElement::RelatedBuildingElement);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& f : fills) {
|
|
|
|
|
descend(mapping, f, child, product);
|
2020-03-11 15:16:18 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto structure = product.as<IfcSchema::IfcSpatialStructureElement>()) {
|
|
|
|
|
auto elements = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcSpatialStructureElement, IfcSchema::IfcRelContainedInSpatialStructure, IfcSchema::IfcObjectDefinition>
|
|
|
|
|
(structure, &IfcSchema::IfcSpatialStructureElement::ContainsElements, &IfcSchema::IfcRelContainedInSpatialStructure::RelatedElements);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& el : elements) {
|
|
|
|
|
descend(mapping, el, child, product);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto element = product.as<IfcSchema::IfcElement>()) {
|
|
|
|
|
auto openings = get_related<IfcSchema::IfcElement, IfcSchema::IfcRelVoidsElement, IfcSchema::IfcOpeningElement>(
|
2018-03-04 10:43:40 +01:00
|
|
|
element, &IfcSchema::IfcElement::HasOpenings, &IfcSchema::IfcRelVoidsElement::RelatedOpeningElement);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& op : openings) {
|
|
|
|
|
descend(mapping, op, child, product);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-26 13:50:46 +02:00
|
|
|
#ifdef SCHEMA_IfcRelDecomposes_HAS_RelatedObjects
|
2026-01-04 10:40:02 +01:00
|
|
|
auto structures = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelDecomposes, IfcSchema::IfcObjectDefinition>
|
|
|
|
|
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelDecomposes::RelatedObjects);
|
|
|
|
|
#else
|
2026-01-04 10:40:02 +01:00
|
|
|
auto structures = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelAggregates, IfcSchema::IfcObjectDefinition>
|
2020-02-10 16:36:56 +01:00
|
|
|
(product, &IfcSchema::IfcObjectDefinition::IsDecomposedBy, &IfcSchema::IfcRelAggregates::RelatedObjects);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto nested = get_related
|
2020-02-10 16:36:56 +01:00
|
|
|
<IfcSchema::IfcObjectDefinition, IfcSchema::IfcRelNests, IfcSchema::IfcObjectDefinition>
|
2026-01-04 10:40:02 +01:00
|
|
|
(product, &IfcSchema::IfcObjectDefinition::IsNestedBy, &IfcSchema::IfcRelNests::RelatedObjects);
|
|
|
|
|
|
|
|
|
|
structures.insert(structures.end(), nested.begin(), nested.end());
|
2018-03-04 10:43:40 +01:00
|
|
|
#endif
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& ob : structures) {
|
2023-03-21 20:15:01 +01:00
|
|
|
descend(mapping, ob, child, product);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto object = product.as<IfcSchema::IfcObject>()) {
|
|
|
|
|
auto property_sets = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
|
|
|
|
|
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
|
|
|
|
|
2025-04-19 09:21:22 +02:00
|
|
|
#ifdef SCHEMAS_HAS_IfcPropertySetDefinitionSet
|
2026-01-04 10:40:02 +01:00
|
|
|
auto property_set_sets = get_related
|
2025-04-11 15:53:27 +02:00
|
|
|
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinitionSet>
|
|
|
|
|
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& s : property_set_sets) {
|
|
|
|
|
auto set_sets_value = (decltype(property_sets))s;
|
|
|
|
|
property_sets.insert(property_sets.end(), set_sets_value.begin(), set_sets_value.end());
|
2025-04-11 15:53:27 +02:00
|
|
|
}
|
2025-04-19 09:21:22 +02:00
|
|
|
#endif
|
2025-04-11 15:53:27 +02:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& pset : property_sets) {
|
|
|
|
|
if (pset.declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, pset, child, true);
|
2026-01-04 10:40:02 +01:00
|
|
|
} else if (pset.declaration().is(IfcSchema::IfcElementQuantity::Class())) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, pset, child, true);
|
2017-12-22 09:57:59 +01:00
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-10 16:36:56 +01:00
|
|
|
#ifdef SCHEMA_IfcObject_HAS_IsTypedBy
|
2026-01-04 10:40:02 +01:00
|
|
|
auto types = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
|
|
|
|
(object, &IfcSchema::IfcObject::IsTypedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
|
|
|
|
#else
|
2026-01-04 10:40:02 +01:00
|
|
|
auto types = get_related
|
2018-03-04 10:43:40 +01:00
|
|
|
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByType, IfcSchema::IfcTypeObject>
|
|
|
|
|
(object, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByType::RelatingType);
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& type : types) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, type, child, true);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (product.declaration().is(IfcSchema::IfcProduct::Class())) {
|
|
|
|
|
auto layers = mapping->get_layers(product);
|
|
|
|
|
for (auto& p : layers) {
|
2018-03-04 10:43:40 +01:00
|
|
|
// IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier) so use name as the ID.
|
|
|
|
|
// Note that the IfcPresentationLayerAssignment passed here doesn't really matter as as_link is true
|
|
|
|
|
// for the format_entity_instance() call.
|
|
|
|
|
ptree node;
|
2026-01-04 10:40:02 +01:00
|
|
|
node.put("<xmlattr>.xlink:href", "#" + p.first);
|
|
|
|
|
format_entity_instance(mapping, p.second, node, child, true);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto associations = product.HasAssociations();
|
|
|
|
|
for (auto& rel : associations) {
|
|
|
|
|
if (auto relmat = rel.as<IfcSchema::IfcRelAssociatesMaterial>()) {
|
|
|
|
|
IfcSchema::IfcMaterialSelect mat = relmat.RelatingMaterial();
|
2018-03-04 10:43:40 +01:00
|
|
|
ptree node;
|
|
|
|
|
node.put("<xmlattr>.xlink:href", "#" + qualify_unrooted_instance(mat));
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping, mat.concrete(), node, child, true);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-17 16:01:28 +02:00
|
|
|
#if defined(SCHEMA_HAS_IfcAlignmentSegment) && defined(SCHEMA_IfcAlignmentSegment_HAS_DesignParameters)
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto als = product.as<IfcSchema::IfcAlignmentSegment>()) {
|
2024-09-17 13:03:25 +02:00
|
|
|
ptree node;
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping, als.DesignParameters(), node, child, false);
|
2024-09-17 13:03:25 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-03-11 15:16:18 +01:00
|
|
|
return &child;
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format IfcProperty instances and insert into the DOM. IfcComplexProperties are flattened out.
|
2026-01-04 10:40:02 +01:00
|
|
|
void format_properties(ifcopenshell::geometry::abstract_mapping* mapping, const std::vector<IfcSchema::IfcProperty>& properties, ptree& node) {
|
|
|
|
|
for (auto& p : properties) {
|
|
|
|
|
if (auto complex = p.as<IfcSchema::IfcComplexProperty>()) {
|
|
|
|
|
format_properties(mapping, complex.HasProperties(), node);
|
2018-03-04 10:43:40 +01:00
|
|
|
} else {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, p, node);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
void writeGroupToNode(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::IfcGroup group, ptree& node, std::set<std::string> notRootGroups) {
|
2021-01-11 10:59:03 +08:00
|
|
|
// @todo tfk: instead of a set<string> shouldn't we just have a set<IfcGroup>, the current approach
|
|
|
|
|
// might not work with non-unique or NIL group names.
|
|
|
|
|
|
|
|
|
|
// @todo tfk: should the set be a passed as a reference?
|
2026-01-04 10:40:02 +01:00
|
|
|
if (!group.Name()) {
|
2021-01-11 10:59:03 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (notRootGroups.find(*group.Name()) != notRootGroups.end()) {
|
2021-01-11 10:59:03 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Write one group to root
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node2 = descend(mapping, group, node);
|
2026-01-04 10:40:02 +01:00
|
|
|
auto father = group.IsGroupedBy();
|
|
|
|
|
for (auto& ii : father)
|
2021-01-11 10:59:03 +08:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
auto objs = ii.RelatedObjects();
|
|
|
|
|
for (auto entity : objs) {
|
|
|
|
|
if (entity.as<IfcSchema::IfcGroup>() && entity.Name()) {
|
|
|
|
|
writeGroupToNode(mapping, entity.as<IfcSchema::IfcGroup>(), *node2, notRootGroups);
|
|
|
|
|
notRootGroups.emplace(*entity.Name());
|
2021-01-11 10:59:03 +08:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Write child to father group
|
2023-03-21 20:15:01 +01:00
|
|
|
descend(mapping, entity, *node2);
|
2021-01-11 10:59:03 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
// Format IfcElementQuantity instances and insert into the DOM.
|
2026-01-04 10:40:02 +01:00
|
|
|
void format_quantities(ifcopenshell::geometry::abstract_mapping* mapping, const std::vector<IfcSchema::IfcPhysicalQuantity>& quantities, ptree& node) {
|
|
|
|
|
for (auto& p : quantities) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node2 = format_entity_instance(mapping, p, node);
|
2026-01-04 10:40:02 +01:00
|
|
|
if (node2 && p.declaration().is(IfcSchema::IfcPhysicalComplexQuantity::Class())) {
|
|
|
|
|
format_quantities(mapping, p.as<IfcSchema::IfcPhysicalComplexQuantity>().HasQuantities(), *node2);
|
2018-08-15 17:10:29 +02:00
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 15:02:12 +02:00
|
|
|
// Format IfcTask instances and insert into the DOM.
|
2026-01-04 10:40:02 +01:00
|
|
|
void format_tasks(ifcopenshell::geometry::abstract_mapping* mapping, IfcSchema::IfcTask task, ptree& node) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* ntask = format_entity_instance(mapping, task, node);
|
2022-04-11 15:51:08 +02:00
|
|
|
|
2022-03-29 15:02:12 +02:00
|
|
|
if (ntask) {
|
|
|
|
|
#ifdef SCHEMA_IfcTask_HAS_TaskTime
|
2026-01-04 10:40:02 +01:00
|
|
|
IfcSchema::IfcTaskTime task_time = task.TaskTime();
|
2022-03-29 15:02:12 +02:00
|
|
|
if (task_time)
|
|
|
|
|
{
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, task_time, *ntask);
|
2022-03-29 15:02:12 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-03-30 09:44:28 +02:00
|
|
|
#ifdef SCHEMA_IfcProcess_HAS_IsSuccessorFrom
|
2026-01-04 10:40:02 +01:00
|
|
|
auto successor_from = task.IsSuccessorFrom();
|
|
|
|
|
for (auto& rel : successor_from)
|
2022-03-29 15:02:12 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
IfcSchema::IfcProcess relating_process = rel.RelatingProcess();
|
2022-03-30 09:44:28 +02:00
|
|
|
ptree nobject;
|
2026-01-04 10:40:02 +01:00
|
|
|
nobject.put("<xmlattr>.id", relating_process.GlobalId());
|
2022-03-30 09:44:28 +02:00
|
|
|
ntask->add_child("IsSuccessorFrom", nobject);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef SCHEMA_IfcProcess_HAS_IsPredecessorTo
|
2026-01-04 10:40:02 +01:00
|
|
|
auto predecessor_to = task.IsPredecessorTo();
|
|
|
|
|
for (auto& rel : predecessor_to)
|
2022-03-30 09:44:28 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
IfcSchema::IfcProcess relating_process = rel.RelatedProcess();
|
2022-03-30 09:44:28 +02:00
|
|
|
ptree nobject;
|
2026-01-04 10:40:02 +01:00
|
|
|
nobject.put("<xmlattr>.id", relating_process.GlobalId());
|
2022-03-30 09:44:28 +02:00
|
|
|
ntask->add_child("IsPredecessorTo", nobject);
|
2022-03-29 15:02:12 +02:00
|
|
|
}
|
2022-03-30 09:44:28 +02:00
|
|
|
#endif
|
2022-03-29 16:23:18 +02:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto property_sets = get_related
|
2022-03-31 11:33:09 +02:00
|
|
|
<IfcSchema::IfcObject, IfcSchema::IfcRelDefinesByProperties, IfcSchema::IfcPropertySetDefinition>
|
|
|
|
|
(task, &IfcSchema::IfcObject::IsDefinedBy, &IfcSchema::IfcRelDefinesByProperties::RelatingPropertyDefinition);
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& pset : property_sets) {
|
|
|
|
|
if (pset.declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, pset, *ntask, true);
|
2022-03-31 11:33:09 +02:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
else if (pset.declaration().is(IfcSchema::IfcElementQuantity::Class())) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping, pset, *ntask, true);
|
2022-03-31 11:33:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-29 16:23:18 +02:00
|
|
|
#ifdef SCHEMA_IfcProcess_HAS_OperatesOn
|
2026-01-04 10:40:02 +01:00
|
|
|
auto operates = task.OperatesOn();
|
|
|
|
|
for (auto& operation : operates)
|
2022-03-29 16:23:18 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
auto objects = operation.RelatedObjects();
|
|
|
|
|
for (auto& object : objects)
|
2022-03-29 16:23:18 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
ptree nobject;
|
|
|
|
|
nobject.put("<xmlattr>.id", object.GlobalId());
|
|
|
|
|
if (object.declaration().is(IfcSchema::IfcProduct::Class()))
|
2022-03-29 16:23:18 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
ntask->add_child("Input", nobject);
|
|
|
|
|
}
|
|
|
|
|
else if (object.declaration().is(IfcSchema::IfcResource::Class()))
|
|
|
|
|
{
|
|
|
|
|
ntask->add_child("Resource", nobject);
|
|
|
|
|
}
|
|
|
|
|
else if (object.declaration().is(IfcSchema::IfcControl::Class()))
|
|
|
|
|
{
|
|
|
|
|
ntask->add_child("Control", nobject);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
nobject.put("<xmlattr>.Type", object.declaration().name());
|
|
|
|
|
ntask->add_child("OperatesOn", nobject);
|
2022-03-29 16:23:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-03-30 09:44:28 +02:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto assignments = task.HasAssignments();
|
|
|
|
|
for (auto& assignment : assignments)
|
2022-04-11 15:51:08 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto assign_to_product = assignment.as<IfcSchema::IfcRelAssignsToProduct>()) {
|
|
|
|
|
IfcSchema::IfcRoot product = assign_to_product.RelatingProduct().as<IfcSchema::IfcProduct>();
|
|
|
|
|
if (!product) {
|
|
|
|
|
product = assign_to_product.RelatingProduct().as<IfcSchema::IfcTypeProduct>();
|
|
|
|
|
}
|
2022-04-11 15:51:08 +02:00
|
|
|
ptree nobject;
|
2026-01-04 10:40:02 +01:00
|
|
|
nobject.put("<xmlattr>.id", product.GlobalId());
|
2022-04-11 15:51:08 +02:00
|
|
|
ntask->add_child("Output", nobject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 09:44:28 +02:00
|
|
|
#ifdef SCHEMA_IfcObjectDefinition_HAS_IsNestedBy
|
2026-01-04 10:40:02 +01:00
|
|
|
auto nested_by = task.IsNestedBy();
|
|
|
|
|
for (auto& rel : nested_by)
|
2022-03-30 09:44:28 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
auto related_objects = rel.RelatedObjects();
|
|
|
|
|
for (auto& object : related_objects)
|
2022-03-30 09:44:28 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
if (auto task2 = object.as<IfcSchema::IfcTask>()) {
|
|
|
|
|
format_tasks(mapping, task2, *ntask);
|
|
|
|
|
}
|
2022-03-30 09:44:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-29 15:02:12 +02:00
|
|
|
#endif
|
2022-03-30 09:44:28 +02:00
|
|
|
}
|
2022-04-11 15:51:08 +02:00
|
|
|
}
|
2022-03-29 15:02:12 +02:00
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
} // ~unnamed namespace
|
|
|
|
|
|
2023-03-21 20:15:01 +01:00
|
|
|
void POSTFIX_SCHEMA(XmlSerializer)::finalize() {
|
|
|
|
|
POSTFIX_SCHEMA(argument_name_map).insert(std::make_pair("GlobalId", "id"));
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto projects = file->instances_by_type<IfcSchema::IfcProject>();
|
|
|
|
|
if (projects.size() != 1) {
|
2018-03-04 10:43:40 +01:00
|
|
|
Logger::Message(Logger::LOG_ERROR, "Expected a single IfcProject");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
IfcSchema::IfcProject& project = projects.front();
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2021-01-11 10:59:03 +08:00
|
|
|
ptree root, header, units, decomposition, properties, quantities, types, layers, materials, work, calendars, connections, groups;
|
|
|
|
|
|
2024-10-26 17:21:05 +02:00
|
|
|
auto catch_exceptions = [this](const auto& fn) {
|
|
|
|
|
try {
|
|
|
|
|
return fn();
|
|
|
|
|
} catch(const std::exception& e) {
|
|
|
|
|
Logger::Error(e);
|
|
|
|
|
static std::invoke_result_t<decltype(fn)> v;
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
// Write the SPF header as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
BOOST_FOREACH(const std::string & s, catch_exceptions([this]() { return file->header().file_description().description(); })) {
|
2018-03-04 10:43:40 +01:00
|
|
|
header.add_child("file_description.description", ptree(s));
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
BOOST_FOREACH(const std::string& s, catch_exceptions([this]() { return file->header().file_name().author(); })) {
|
2018-03-04 10:43:40 +01:00
|
|
|
header.add_child("file_name.author", ptree(s));
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
BOOST_FOREACH(const std::string& s, catch_exceptions([this]() { return file->header().file_name().organization(); })) {
|
2018-03-04 10:43:40 +01:00
|
|
|
header.add_child("file_name.organization", ptree(s));
|
2024-09-17 13:07:01 +02:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
BOOST_FOREACH(const std::string& s, catch_exceptions([this]() { return file->header().file_schema().schema_identifiers(); })) {
|
2018-03-04 10:43:40 +01:00
|
|
|
header.add_child("file_schema.schema_identifiers", ptree(s));
|
|
|
|
|
}
|
2018-05-30 12:57:30 +03:00
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_description.implementation_level", file->header().file_description().implementation_level());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_description implementation_level, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_name.name", file->header().file_name().name());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_name name, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_name.time_stamp", file->header().file_name().time_stamp());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_name time_stamp, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_name.preprocessor_version", file->header().file_name().preprocessor_version());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_name preprocessor_version, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
try {
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_name.originating_system", file->header().file_name().originating_system());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_name originating_system, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
try {
|
2025-02-27 22:07:31 +01:00
|
|
|
// @nb inconsistent spelling
|
2026-01-04 10:40:02 +01:00
|
|
|
header.put("file_name.authorization", file->header().file_name().authorization());
|
2018-05-30 12:57:30 +03:00
|
|
|
}
|
|
|
|
|
catch (const IfcParse::IfcException& ex) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "Failed to get ifc file header file_name authorization, error: '" << ex.what() << "'";
|
|
|
|
|
Logger::Message(Logger::LOG_ERROR, ss.str());
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
// Descend into the decomposition structure of the IFC file.
|
2023-03-21 20:15:01 +01:00
|
|
|
descend(mapping_, project, decomposition);
|
2018-03-04 10:43:40 +01:00
|
|
|
|
|
|
|
|
// Write all property sets and values as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
auto psets = file->instances_by_type<IfcSchema::IfcPropertySet>();
|
|
|
|
|
for (auto& pset : psets) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node = format_entity_instance(mapping_, pset, properties);
|
2020-03-11 15:16:18 +01:00
|
|
|
if (node) {
|
2026-01-04 10:40:02 +01:00
|
|
|
format_properties(mapping_, pset.HasProperties(), *node);
|
2020-03-11 15:16:18 +01:00
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
2021-01-11 10:59:03 +08:00
|
|
|
|
|
|
|
|
// Write all group sets and values as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
auto gsets = file->instances_by_type<IfcSchema::IfcGroup>();
|
2021-01-11 10:59:03 +08:00
|
|
|
std::set<std::string> notRootGroups; //selfname, fathername
|
2026-01-04 10:40:02 +01:00
|
|
|
for (auto& g : gsets) {
|
|
|
|
|
writeGroupToNode(mapping_, g, groups, notRootGroups);
|
2021-01-11 10:59:03 +08:00
|
|
|
}
|
|
|
|
|
for (auto it = groups.begin(); it != groups.end();) {
|
|
|
|
|
if (notRootGroups.find(it->second.get<std::string>("<xmlattr>.Name")) != notRootGroups.end()) {
|
|
|
|
|
it = groups.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
// Write all quantities and values as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
auto qtosets = file->instances_by_type<IfcSchema::IfcElementQuantity>();
|
|
|
|
|
for (auto& qto : qtosets) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node = format_entity_instance(mapping_, qto, quantities);
|
2020-03-11 15:16:18 +01:00
|
|
|
if (node) {
|
2026-01-04 10:40:02 +01:00
|
|
|
format_quantities(mapping_, qto.Quantities(), *node);
|
2020-03-11 15:16:18 +01:00
|
|
|
}
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-30 13:16:09 +02:00
|
|
|
// Write all work schedules and values as XML nodes.
|
2022-04-21 14:08:36 +02:00
|
|
|
ptree pwork_schedules;
|
2026-01-04 10:40:02 +01:00
|
|
|
auto pschedules = file->instances_by_type<IfcSchema::IfcWorkSchedule>();
|
|
|
|
|
for (auto& schedule : pschedules) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* nschedule = format_entity_instance(mapping_, schedule, pwork_schedules);
|
2022-03-31 11:33:09 +02:00
|
|
|
|
2022-03-30 13:16:09 +02:00
|
|
|
if(nschedule) {
|
2026-01-04 10:40:02 +01:00
|
|
|
auto controls = schedule.Controls();
|
|
|
|
|
for(auto& control : controls) {
|
|
|
|
|
auto objects = control.RelatedObjects();
|
|
|
|
|
for(auto& object : objects) {
|
|
|
|
|
if (object && object.declaration().is(IfcSchema::IfcTask::Class())) {
|
|
|
|
|
IfcSchema::IfcTask task = object.as<IfcSchema::IfcTask>();
|
2023-03-21 20:15:01 +01:00
|
|
|
format_tasks(mapping_, task, *nschedule);
|
2022-03-30 13:16:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-29 15:02:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-04-21 14:08:36 +02:00
|
|
|
work.add_child("schedules", pwork_schedules);
|
|
|
|
|
|
|
|
|
|
// Write all work plans and values as XML nodes.
|
|
|
|
|
ptree pwork_plans;
|
2026-01-04 10:40:02 +01:00
|
|
|
auto pplans = file->instances_by_type<IfcSchema::IfcWorkPlan>();
|
|
|
|
|
for (auto& plan : pplans) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* nschedule = format_entity_instance(mapping_, plan, pwork_plans);
|
2022-04-21 14:08:36 +02:00
|
|
|
|
|
|
|
|
if (nschedule) {
|
|
|
|
|
#ifdef SCHEMA_IfcObjectDefinition_HAS_IsDecomposedBy
|
2026-01-04 10:40:02 +01:00
|
|
|
auto decomposed_by = plan.IsDecomposedBy();
|
|
|
|
|
for (auto& rel : decomposed_by)
|
2022-04-21 14:08:36 +02:00
|
|
|
{
|
2026-01-04 10:40:02 +01:00
|
|
|
auto related_objects = rel.RelatedObjects();
|
|
|
|
|
for (auto& work_schedule : related_objects)
|
2022-04-21 14:08:36 +02:00
|
|
|
{
|
|
|
|
|
ptree pwork_schedule;
|
2026-01-04 10:40:02 +01:00
|
|
|
pwork_schedule.put("<xmlattr>.id", work_schedule.GlobalId());
|
2022-04-21 14:08:36 +02:00
|
|
|
nschedule->add_child("IfcWorkSchedule", pwork_schedule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
work.add_child("plans", pwork_plans);
|
|
|
|
|
|
2022-04-11 11:47:53 +02:00
|
|
|
// Write all work calendars and values as XML nodes.
|
|
|
|
|
#ifdef SCHEMA_HAS_IfcWorkCalendar
|
2026-01-04 10:40:02 +01:00
|
|
|
auto pcalendars = file->instances_by_type<IfcSchema::IfcWorkCalendar>();
|
|
|
|
|
for (auto& calendar : pcalendars) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* ncalendar = format_entity_instance(mapping_, calendar, calendars);
|
2022-04-21 14:08:36 +02:00
|
|
|
|
2022-04-11 11:47:53 +02:00
|
|
|
if (ncalendar) {
|
2026-01-04 10:40:02 +01:00
|
|
|
auto working_times = calendar.WorkingTimes().value_or(std::vector<IfcSchema::IfcWorkTime>{});
|
|
|
|
|
for (auto& working_time : working_times)
|
|
|
|
|
{
|
|
|
|
|
format_entity_instance(mapping_, working_time, *ncalendar);
|
2022-04-11 11:47:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-09-05 14:58:22 +02:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto pconnections = file->instances_by_type<IfcSchema::IfcRelConnectsElements>();
|
|
|
|
|
for (auto& connection : pconnections) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* nconnection = format_entity_instance(mapping_, connection, connections);
|
2022-09-05 14:58:22 +02:00
|
|
|
|
|
|
|
|
ptree nrelatedElement;
|
|
|
|
|
ptree nrelatingElement;
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping_,connection.RelatedElement(), nrelatedElement, true);
|
|
|
|
|
format_entity_instance(mapping_,connection.RelatingElement(), nrelatingElement, true);
|
2022-09-05 14:58:22 +02:00
|
|
|
|
|
|
|
|
nconnection->add_child("RelatedElement", nrelatedElement);
|
|
|
|
|
nconnection->add_child("RelatingElement", nrelatingElement);
|
|
|
|
|
}
|
2022-04-11 11:47:53 +02:00
|
|
|
|
2018-03-04 10:43:40 +01:00
|
|
|
// Write all type objects as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
auto type_objects = file->instances_by_type<IfcSchema::IfcTypeObject>();
|
|
|
|
|
for (auto& type_object : type_objects) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node = descend(mapping_, type_object, types);
|
2018-03-04 10:43:40 +01:00
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
if (node && type_object.HasPropertySets()) {
|
|
|
|
|
auto property_sets = *type_object.HasPropertySets();
|
|
|
|
|
for (auto& pset : property_sets) {
|
|
|
|
|
if (pset.declaration().is(IfcSchema::IfcPropertySet::Class())) {
|
2023-03-21 20:15:01 +01:00
|
|
|
format_entity_instance(mapping_, pset, *node, true);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write all assigned units as XML nodes.
|
2026-01-04 10:40:02 +01:00
|
|
|
auto unit_assignments = project.UnitsInContext().Units();
|
|
|
|
|
for (auto& unit : unit_assignments) {
|
|
|
|
|
if (auto named_unit = unit.as<IfcSchema::IfcNamedUnit>()) {
|
2023-03-21 20:15:01 +01:00
|
|
|
ptree* node = format_entity_instance(mapping_, named_unit, units);
|
2020-03-11 15:16:18 +01:00
|
|
|
if (node) {
|
|
|
|
|
node->put("<xmlattr>.SI_equivalent", IfcParse::get_SI_equivalent<IfcSchema>(named_unit));
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
} else if (auto mon_unit = unit.as<IfcSchema::IfcMonetaryUnit>()) {
|
|
|
|
|
format_entity_instance(mapping_, mon_unit, units);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Layer assignments. IfcPresentationLayerAssignments don't have GUIDs (only optional Identifier)
|
|
|
|
|
// so use names as the IDs and only insert those with unique names. In case of possible duplicate names/IDs
|
2018-09-06 12:23:07 -04:00
|
|
|
// the first IfcPresentationLayerAssignment occurrence takes precedence.
|
2018-03-04 10:43:40 +01:00
|
|
|
std::set<std::string> layer_names;
|
2026-01-04 10:40:02 +01:00
|
|
|
auto layer_assignments = file->instances_by_type<IfcSchema::IfcPresentationLayerAssignment>();
|
|
|
|
|
for (auto& assignment : layer_assignments) {
|
|
|
|
|
const std::string& name = assignment.Name();
|
2018-03-04 10:43:40 +01:00
|
|
|
if (layer_names.find(name) == layer_names.end()) {
|
|
|
|
|
layer_names.insert(name);
|
|
|
|
|
ptree node;
|
|
|
|
|
node.put("<xmlattr>.id", name);
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping_, assignment, node, layers);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 10:40:02 +01:00
|
|
|
auto materal_associations = file->instances_by_type<IfcSchema::IfcRelAssociatesMaterial>();
|
|
|
|
|
std::set<express::Base> emitted_materials;
|
|
|
|
|
for (auto& rel : materal_associations) {
|
|
|
|
|
IfcSchema::IfcMaterialSelect mat = rel.RelatingMaterial();
|
2018-03-04 10:43:40 +01:00
|
|
|
if (emitted_materials.find(mat) == emitted_materials.end()) {
|
|
|
|
|
emitted_materials.insert(mat);
|
|
|
|
|
ptree node;
|
|
|
|
|
node.put("<xmlattr>.id", qualify_unrooted_instance(mat));
|
2026-01-04 10:40:02 +01:00
|
|
|
// @todo this does not handle IfcMaterialProfileSetUsage and IfcMaterialConstituentSet
|
2026-01-07 13:51:48 +01:00
|
|
|
if (mat.concrete().as<IfcSchema::IfcMaterialLayerSetUsage>() || mat.concrete().as<IfcSchema::IfcMaterialLayerSet>()) {
|
2026-01-04 10:40:02 +01:00
|
|
|
IfcSchema::IfcMaterialLayerSet layerset = mat.concrete().as<IfcSchema::IfcMaterialLayerSet>();
|
2018-03-16 11:03:02 +01:00
|
|
|
if (!layerset) {
|
2026-01-04 10:40:02 +01:00
|
|
|
layerset = mat.concrete().as<IfcSchema::IfcMaterialLayerSetUsage>().ForLayerSet();
|
2018-03-16 11:03:02 +01:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
if (layerset.LayerSetName()) {
|
|
|
|
|
node.put("<xmlattr>.LayerSetName", *layerset.LayerSetName());
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
auto ls = layerset.MaterialLayers();
|
|
|
|
|
for (auto& layer : ls) {
|
2018-03-04 10:43:40 +01:00
|
|
|
ptree subnode;
|
2026-01-04 10:40:02 +01:00
|
|
|
if (layer.Material()) {
|
|
|
|
|
subnode.put("<xmlattr>.Name", layer.Material());
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping_, layer, subnode, node);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
} else if (auto matlist = mat.concrete().as<IfcSchema::IfcMaterialList>()) {
|
|
|
|
|
auto mats = matlist.Materials();
|
|
|
|
|
for (auto& mat : mats) {
|
2018-03-04 10:43:40 +01:00
|
|
|
ptree subnode;
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping_, mat, subnode, node);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
format_entity_instance(mapping_, mat.concrete(), node, materials);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root.add_child("ifc.header", header);
|
|
|
|
|
root.add_child("ifc.units", units);
|
2022-09-05 14:58:22 +02:00
|
|
|
root.add_child("ifc.connections", connections);
|
2018-03-04 10:43:40 +01:00
|
|
|
root.add_child("ifc.properties", properties);
|
|
|
|
|
root.add_child("ifc.quantities", quantities);
|
2022-04-21 14:08:36 +02:00
|
|
|
root.add_child("ifc.work", work);
|
2022-04-11 11:47:53 +02:00
|
|
|
root.add_child("ifc.calendars", calendars);
|
2018-03-04 10:43:40 +01:00
|
|
|
root.add_child("ifc.types", types);
|
2022-03-30 09:44:28 +02:00
|
|
|
root.add_child("ifc.layers", layers);
|
2021-01-11 10:59:03 +08:00
|
|
|
root.add_child("ifc.groups", groups);
|
2018-03-04 10:43:40 +01:00
|
|
|
root.add_child("ifc.materials", materials);
|
|
|
|
|
root.add_child("ifc.decomposition", decomposition);
|
|
|
|
|
|
|
|
|
|
root.put("ifc.<xmlattr>.xmlns:xlink", "http://www.w3.org/1999/xlink");
|
|
|
|
|
|
|
|
|
|
#if BOOST_VERSION >= 105600
|
|
|
|
|
boost::property_tree::xml_writer_settings<ptree::key_type> settings = boost::property_tree::xml_writer_make_settings<ptree::key_type>('\t', 1);
|
|
|
|
|
#else
|
|
|
|
|
boost::property_tree::xml_writer_settings<char> settings('\t', 1);
|
|
|
|
|
#endif
|
2019-02-08 15:18:55 +01:00
|
|
|
|
|
|
|
|
std::ofstream f(IfcUtil::path::from_utf8(xml_filename).c_str());
|
|
|
|
|
boost::property_tree::write_xml(f, root, settings);
|
2018-03-04 10:43:40 +01:00
|
|
|
}
|