From 09a6f56f4747dc5b01567e9de47724775e6ec580 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 18 Aug 2022 16:11:59 +1000 Subject: [PATCH] #2319 Add documentation for IfcDiff --- src/ifcdiff/ifcdiff.py | 49 +++++-- src/ifcopenshell-python/docs/conf.py | 2 +- src/ifcopenshell-python/docs/ifcdiff.rst | 128 ++++++++++++++++-- .../docs/ifcopenshell-python/installation.rst | 7 +- 4 files changed, 159 insertions(+), 27 deletions(-) diff --git a/src/ifcdiff/ifcdiff.py b/src/ifcdiff/ifcdiff.py index ea5d6b2ffe..224cdaa161 100755 --- a/src/ifcdiff/ifcdiff.py +++ b/src/ifcdiff/ifcdiff.py @@ -34,6 +34,38 @@ from deepdiff import DeepDiff class IfcDiff: + """Main IfcDiff application + + If you are using IfcDiff as a library, this is the class you should use. + + :param old_file: Filepath to the old file + :type old_file: string + :param new_file: Filepath to the new file + :type new_file: string + :param output_file: Filepath to the output JSON file to store the diff + results + :type output_file: string + :param relationships: List of relationships to check. An empty list means + that only geometry and attributes are compared. + :type relationships: list[string] + :param is_shallow: True if you want only the first difference to be listed. + False if you want all differences to be checked. Choosing False means + that comparisons will take longer. + :type is_shallow: bool + :param filter_elements: An IFC filter query if you only want to compare a + subset of elements. For example: ``.IfcWall`` to only compare walls. + :type filter_elements: string + + Example:: + + from ifcdiff import IfcDiff + + ifc_diff = IfcDiff("/path/to/old.ifc", "/path/to/new.ifc", "/path/to/diff.json") + ifc_diff.diff() + print(ifc_diff.change_register) + ifc_diff.export() + """ + def __init__(self, old_file, new_file, output_file, relationships=None, is_shallow=True, filter_elements=None): self.old_file = old_file self.new_file = new_file @@ -56,7 +88,7 @@ class IfcDiff: selector = ifcopenshell.util.selector.Selector() old_elements = set(e.GlobalId for e in selector.parse(self.old, self.filter_elements)) new_elements = set(e.GlobalId for e in selector.parse(self.new, self.filter_elements)) - else: + else: old_elements = set(e.GlobalId for e in self.old.by_type("IfcProduct")) new_elements = set(e.GlobalId for e in self.new.by_type("IfcProduct")) @@ -101,7 +133,6 @@ class IfcDiff: }, diff_file, indent=4, - cls=DiffEncoder, ) def load(self): @@ -173,9 +204,9 @@ class IfcDiff: def diff_element_geometry(self, old, new): old_placement = ifcopenshell.util.placement.get_local_placement(old.ObjectPlacement) new_placement = ifcopenshell.util.placement.get_local_placement(new.ObjectPlacement) - if not np.allclose(old_placement[:,3], new_placement[:,3], atol=self.precision): + if not np.allclose(old_placement[:, 3], new_placement[:, 3], atol=self.precision): return True - if not np.allclose(old_placement[0:3,0:3], new_placement[0:3,0:3], atol=1e-2): + if not np.allclose(old_placement[0:3, 0:3], new_placement[0:3, 0:3], atol=1e-2): return True old_openings = [o.RelatedOpeningElement.GlobalId for o in getattr(old, "HasOpenings", []) or []] new_openings = [o.RelatedOpeningElement.GlobalId for o in getattr(new, "HasOpenings", []) or []] @@ -218,7 +249,7 @@ class IfcDiff: new_item.get_info_2(recursive=True), custom_operators=[DiffTerminator()] if self.is_shallow else [], math_epsilon=self.precision, - exclude_regex_paths=[r".*id']$"] + exclude_regex_paths=[r".*id']$"], ) except: return True @@ -249,14 +280,6 @@ class DiffTerminator: raise Exception("Terminated") -class DiffEncoder(json.JSONEncoder): - def default(self, obj): - try: - return json.JSONEncoder.default(self, obj) - except: - return str(obj) - - if __name__ == "__main__": parser = argparse.ArgumentParser(description="Show the difference between two IFC files") parser.add_argument("old", type=str, help="The old IFC file") diff --git a/src/ifcopenshell-python/docs/conf.py b/src/ifcopenshell-python/docs/conf.py index c4df1f62d3..ef496e66a0 100644 --- a/src/ifcopenshell-python/docs/conf.py +++ b/src/ifcopenshell-python/docs/conf.py @@ -60,7 +60,7 @@ extensions = ["autoapi.extension"] autoapi_type = 'python' # autoapi works by reading source code instead of importing modules -autoapi_dirs = ['../ifcopenshell'] +autoapi_dirs = ['../ifcopenshell', '../../ifcdiff'] # autoapi_options doesn't have show-module-summary, as it tends to create one # page per function which contradicts the presentation of showing all functions diff --git a/src/ifcopenshell-python/docs/ifcdiff.rst b/src/ifcopenshell-python/docs/ifcdiff.rst index 78a8a2efe9..046b72fb21 100644 --- a/src/ifcopenshell-python/docs/ifcdiff.rst +++ b/src/ifcopenshell-python/docs/ifcdiff.rst @@ -1,16 +1,124 @@ IfcDiff ======= -This documentation is free software! You are free to contribute and help write -this document. +IfcDiff is both a CLI utility and library that lets you compare the changes +between two IFC models. Changes are made on the assumption that the GlobalId of +an element in one model is consistent with the same element in another model. +You may compare geometric changes, and changes in various IFC relationships and +properties. IfcDiff supports comparing across different IFC schema versions. -.. toctree:: - :maxdepth: 1 - :caption: Contents: +Changes will be sorted into three lists: -Indices and tables ------------------- +- **Added**: a list of GlobalIds of elements present in the new file but not + present in the old file. +- **Deleted**: a list of GlobalIds of elements present in the old file but not + present in the new file. +- **Changed**: A list of GlobalIds of elements present in both the old and new + file, but changes were detected. A list of changes are provided. -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +There are different methods of installation, depending on your situation. + +1. **Source installation** is recommended for users wanting to use the latest + code as a library or a CLI utility. +2. **Using the BlenderBIM Add-on** is recommended for non-developers wanting a + graphical interface. + +Source installation +------------------- + +1. :doc:`Install IfcOpenShell ` +2. `Clone the source code `_. +3. ``pip install -r requirements.txt`` + +Here is a minimal example of how to use IfcDiff as a Python module or CLI +utility: + +:: + + $ python -m ifcdiff -h + usage: ifcdiff.py [-h] [-o OUTPUT] [-r RELATIONSHIPS] old new + + Show the difference between two IFC files + + positional arguments: + old The old IFC file + new The new IFC file + + options: + -h, --help show this help message and exit + -o OUTPUT, --output OUTPUT + The JSON diff file to output. Defaults to diff.json + -r RELATIONSHIPS, --relationships RELATIONSHIPS + A list of space-separated relationships, chosen from "type", "property", "container", "aggregate", "classification" + $ python -m ifcdiff old.ifc new.ifc + $ cat diff.json + +Here is a minimal example of how to use IfcDiff as a library: + +.. code-block:: python + + from ifcdiff import IfcDiff + + ifc_diff = IfcDiff("/path/to/old.ifc", "/path/to/new.ifc", "/path/to/diff.json") + ifc_diff.diff() + print(ifc_diff.change_register) + ifc_diff.export() + +.. seealso:: + + For more information on how to use IfcDiff as a library, check out the :doc:`API + reference `. + + +Using the BlenderBIM Add-on +--------------------------- + +The BlenderBIM Add-on is a Blender based graphical interface to IfcOpenShell. +Other than providing a graphical IFC authoring platform, it also comes with +IfcOpenShell, its utilities, and a Python shell built-in. This means you don't +need to install Python first, and you also can compare your IfcOpenShell +scripting to what you see with a visual model viewer, or use a graphical +interface to access the IfcOpenShell utilities. + +1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on + installation documentation + `_. + +2. Launch Blender. Change to the **Scene Properties** tab in the **Properties + Panel**. Scroll down to the **IFC Quality Control > IFC Diff** panel. + +3. Browse to your old IFC file, new IFC file. + +4. Optionally add any relationships you want to check. + +5. Optionally type in a filter query. + +6. Press **Execute IFC Diff** + +TODO: add pictures and make this clearer for non-developers. + +Geometry changes +---------------- + +IfcDiff compares geometry changes using the underlying IFC geometric definition. +This means that if a shape is described in one file as an extrusion, and as a +mesh in another file, it is considered to be a change in geometry, even if they +resolve to be the same boundary representation. + +Geometric tolerance is defined using the precision defined in the new IFC model. + +Relationships +------------- + +By default, IfcDiff only compares changes in attributes and geometry. You may +wish to optionally specify more relationships to compare. You may choose from: + +- **type**: detects changes in the type relationship, such as when an + occurrence now belongs to a different type. +- **property**: detects changes in property sets, properties, quantity sets, + and quantities. Also includes detected changes in inherited properties. +- **container**: detects changes in the spatial container, handling indirect + containment such as when an element is part of an aggregate. +- **aggregate**: detects changes in aggregation. +- **classification**: detects changes in classification references. Also + includes detected changes in inherited classifications. diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/installation.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/installation.rst index 1f7eb8b5ce..c54ede8297 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/installation.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/installation.rst @@ -91,9 +91,10 @@ Using the BlenderBIM Add-on The BlenderBIM Add-on is a Blender based graphical interface to IfcOpenShell. Other than providing a graphical IFC authoring platform, it also comes with -IfcOpenShell and a Python shell built-in. This means you don't need to install -Python first, and you also can compare your IfcOpenShell scripting to what you -see with a visual model viewer. +IfcOpenShell, its utilities, and a Python shell built-in. This means you don't +need to install Python first, and you also can compare your IfcOpenShell +scripting to what you see with a visual model viewer, or use a graphical +interface to access the IfcOpenShell utilities. 1. Install the BlenderBIM Add-on by following the `BlenderBIM Add-on installation documentation