First Commit

This commit is contained in:
2026-05-31 10:17:09 +07:00
commit 17a9c69379
4547 changed files with 1170384 additions and 0 deletions
@@ -0,0 +1,48 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""Reference external project documents and associate them to model elements
Some project information (drawings, specifications, certificates, reports, etc)
may be stored in external documents (locally or in a CDE). IFC lets you store a
register of documents with metadata and associate them with elements (both
physical and non-physical).
"""
from .. import wrap_usecases
from .add_information import add_information
from .add_reference import add_reference
from .assign_document import assign_document
from .edit_information import edit_information
from .edit_reference import edit_reference
from .remove_information import remove_information
from .remove_reference import remove_reference
from .unassign_document import unassign_document
wrap_usecases(__path__, __name__)
__all__ = [
"add_information",
"add_reference",
"assign_document",
"edit_information",
"edit_reference",
"remove_information",
"remove_reference",
"unassign_document",
]
@@ -0,0 +1,83 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.guid
def add_information(
file: ifcopenshell.file, parent: Optional[ifcopenshell.entity_instance] = None
) -> ifcopenshell.entity_instance:
"""Adds a new document information to the project
An IFC document information is a document associated with the project.
It may be a drawing, specification, schedule, certificate, warranty
guarantee, manual, contract, and so on. They are often used for drawings
and facility management purposes.
A document may also be a subdocument of a larger document, this is
useful for superseding documents or tracking older versions. The parent
is considered the latest version and the children are older revisions.
:param parent: The parent document, if necessary.
:return: The newly created IfcDocumentInformation entity
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
# A document typically has a unique drawing or document name (which
# follows a coding system depending on the project), as well as a
# title. This should match what is shown on the titleblock or title
# page of the document. At a minimum you'd also want to specify a
# URI location. The location may be on local, or on a CDE, or any
# other platform.
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
"""
id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification"
information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"})
if not parent and not (parent := next(iter(file.by_type("IfcProject")), None)):
raise Exception("IfcProject is not found.")
if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
file.create_entity(
"IfcRelAssociatesDocument",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
RelatingDocument=information,
RelatedObjects=[parent],
)
elif parent.is_a("IfcDocumentInformation"):
if parent.IsPointer:
rel = parent.IsPointer[0]
documents = set(rel.RelatedDocuments)
documents.add(information)
rel.RelatedDocuments = list(documents)
else:
file.create_entity(
"IfcDocumentInformationRelationship", RelatingDocument=parent, RelatedDocuments=[information]
)
return information
@@ -0,0 +1,71 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Creates a new reference to a document to assign to products
A document may be associated with physical products, tasks, cost items,
and so on. For example, spaces, storeys, and buildings may have a list
of associated drawings so you can see which drawings (e.g. plans,
sections, details) are documenting that location. Alternatively,
equipment may have associated training manuals, operation and
maintenance manuals or detailed assembly drawings. Resources may be
training certification required, schedules may have gantt charts or bid
documents, and so on.
In order to associate a document with an object, a reference to that
document needs to be created. It could be a reference to the entire
document, or a reference to a particular page or chapter. See
ifcopenshell.api.document.assign_document for more information.
:param information: The IfcDocumentInformation that the reference will
be created for
:return: The newly created IfcDocumentReference entity
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
# In this case, we don't specify any more information, and so the
# reference is for the entire document, as opposed to a single page or
# chapter or section.
reference = ifcopenshell.api.document.add_reference(model, information=document)
# Alternatively, we can specify a single section, such as by a
# subheading code.
reference2 = ifcopenshell.api.document.add_reference(model, information=document)
ifcopenshell.api.document.edit_reference(model,
reference=reference2, attributes={"Identification": "2.1.15"})
"""
if file.schema == "IFC2X3":
reference = file.create_entity("IfcDocumentReference", ItemReference="X")
if information:
references = list(information.DocumentReferences or [])
references.append(reference)
information.DocumentReferences = references
return reference
return file.create_entity("IfcDocumentReference", ReferencedDocument=information, Identification="X")
@@ -0,0 +1,103 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.guid
import ifcopenshell.util.element
def assign_document(
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance,
) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a document to a list of products
An object may be assigned to zero, one, or multiple documents. Almost
any object or property may be assigned to a document, though typically
we'd only use it for spaces, types, physical products and schedules.
Adding a new assignment is typically done using a document reference and
an object. IFC technically allows association with a document
information and an object, but this is not encouraged because it is not
consistent with other external relationships (such as classification
systems or libraries).
:param product: The list of objects to associate the document to. This could be
almost any sensible object in IFC.
:param document: The IfcDocumentReference to associate to, or
alternatively an IfcDocumentInformation, though this is not
recommended.
:return: The IfcRelAssociatesDocument relationship
or `None` if `products` was an empty list or all products were
already assigned to the `document`.
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
reference = ifcopenshell.api.document.add_reference(model, information=document)
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
ifcopenshell.api.document.assign_document(model, products=[storey], document=reference)
"""
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.assign_reference`
referenced_elements = ifcopenshell.util.element.get_referenced_elements(document)
products_set: set[ifcopenshell.entity_instance] = set(products)
products_set = products_set - referenced_elements
if not products_set:
return
if file.schema == "IFC2X3":
rel = next(
(r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == document),
None,
)
else:
ifc_class = document.is_a()
if ifc_class == "IfcDocumentReference":
rel = next(iter(document.DocumentRefForObjects), None)
elif ifc_class == "IfcDocumentInformation":
rel = next(iter(document.DocumentInfoForObjects), None)
else:
assert False, f"Unexpected document type: {ifc_class}"
if not rel:
return file.create_entity(
"IfcRelAssociatesDocument",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
RelatedObjects=list(products_set),
RelatingDocument=document,
)
related_objects = set(rel.RelatedObjects) | products_set
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
return rel
@@ -0,0 +1,48 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
import ifcopenshell
def edit_information(
file: ifcopenshell.file,
information: ifcopenshell.entity_instance,
attributes: dict[str, Any],
) -> None:
"""Edits the attributes of an IfcDocumentInformation
For more information about the attributes and data types of an
IfcDocumentInformation, consult the IFC documentation.
:param reference: The IfcDocumentInformation entity you want to edit
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
"""
for name, value in attributes.items():
setattr(information, name, value)
@@ -0,0 +1,51 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
import ifcopenshell
def edit_reference(
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
attributes: dict[str, Any],
) -> None:
"""Edits the attributes of an IfcDocumentReference
For more information about the attributes and data types of an
IfcDocumentReference, consult the IFC documentation.
:param reference: The IfcDocumentReference entity you want to edit
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
reference = ifcopenshell.api.document.add_reference(model, information=document)
ifcopenshell.api.document.edit_reference(model,
reference=reference, attributes={"Identification": "2.1.15"})
"""
for name, value in attributes.items():
setattr(reference, name, value)
@@ -0,0 +1,70 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.document
import ifcopenshell.util.element
def remove_information(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> None:
"""Removes a document information
All references and associations are also removed.
:param information: The IfcDocumentInformation to remove
:return: None
Example:
.. code:: python
# Add a document
document = ifcopenshell.api.document.add_information(model)
# ... and remove it!
ifcopenshell.api.document.remove_information(model, information=document)
"""
if file.schema == "IFC2X3":
references = information.DocumentReferences or []
else:
references = information.HasDocumentReferences
for reference in references:
ifcopenshell.api.document.remove_reference(file, reference=reference)
for rel in information.IsPointer or []:
for info in rel.RelatedDocuments:
ifcopenshell.api.document.remove_information(file, information=info)
for rel in information.IsPointedTo or []:
if rel.RelatedDocuments == (information,):
# This relationship is non-rooted
file.remove(rel)
if file.schema == "IFC2X3":
rels = [r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == information]
else:
rels = information.DocumentInfoForObjects
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(information)
@@ -0,0 +1,50 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
"""Remove a document reference
All associations with objects are removed.
:param reference: The IfcDocumentReference to remove
:return: None
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
reference = ifcopenshell.api.document.add_reference(model, information=document)
ifcopenshell.api.document.remove_reference(model, reference=reference)
"""
if file.schema == "IFC2X3":
rels = [r for r in file.get_inverse(reference) if r.is_a("IfcRelAssociatesDocument")]
else:
rels = reference.DocumentRefForObjects
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(reference)
@@ -0,0 +1,76 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.util.element
def unassign_document(
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
document: ifcopenshell.entity_instance,
) -> None:
"""Unassigns a document and an association to the list of products
:param product: The list of objects that the document reference or information is
related to.
:param document: The IfcDocumentReference (typically) or in rare cases
the IfcDocumentInformation that is associated with the product
:return: None
Example:
.. code:: python
document = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model,
information=document,
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
"Location": "A-GA-6100 - Overall Plan.pdf"})
reference = ifcopenshell.api.document.add_reference(model, information=document)
# Let's imagine storey represents an IfcBuildingStorey for the ground floor
ifcopenshell.api.document.assign_document(model, products=[storey], document=reference)
# Now let's change our mind and remove the association
ifcopenshell.api.document.unassign_document(model, products=[storey], document=reference)
"""
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
# NOTE: reuses code from `library.un assign_reference`
reference_rels: set[ifcopenshell.entity_instance] = set()
products_set = set(products)
for product in products_set:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == document
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products_set
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
else:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)