From 3a098e55c4d07f68fa03c2b8944ee1dcda79e8eb Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 29 Feb 2024 16:23:21 +1100 Subject: [PATCH] New IfcPatch recipe to extract flattened properties and relationships to SQLite for simple queries --- .../recipes/ExtractPropertiesToSQLite.py | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py diff --git a/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py new file mode 100644 index 0000000000..42df8170e8 --- /dev/null +++ b/src/ifcpatch/ifcpatch/recipes/ExtractPropertiesToSQLite.py @@ -0,0 +1,136 @@ +# IfcPatch - IFC patching utiliy +# Copyright (C) 2024 Dion Moult +# +# This file is part of IfcPatch. +# +# IfcPatch 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. +# +# IfcPatch 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 IfcPatch. If not, see . + + +import os +import re +import json +import time +import tempfile +import ifcopenshell + +try: + import sqlite3 +except: + print("No SQLite support") + + +class Patcher: + def __init__( + self, + src, + file, + logger, + ): + """Extracts properties and relationships from a IFC-SPF model to SQLite. + + This is a lossy extraction which simplifies popular properties to key + value pairs. + + Example: + + .. code:: python + + result = ifcpatch.execute({"input": fn, "file": model, "recipe": "ExtractPropertiesToSQLite"}) + ifcpatch.write(result, "output.sqlite") + """ + self.src = src + self.file = file + self.logger = logger + + def patch(self): + tmp = tempfile.NamedTemporaryFile(delete=False) + db_file = tmp.name + self.db = sqlite3.connect(db_file) + self.c = self.db.cursor() + self.file_patched = db_file + + self.c.execute( + """ + CREATE TABLE IF NOT EXISTS elements ( + id integer PRIMARY KEY NOT NULL UNIQUE, + global_id text, + ifc_class text, + predefined_type text, + name text, + description text + ); + """ + ) + self.c.execute("CREATE INDEX IF NOT EXISTS idx_global_id ON elements (global_id);") + self.c.execute("CREATE INDEX IF NOT EXISTS idx_ifc_class ON elements (ifc_class);") + self.c.execute("CREATE INDEX IF NOT EXISTS idx_predefined_type ON elements (predefined_type);") + + self.c.execute( + """ + CREATE TABLE IF NOT EXISTS relationships ( + from_id integer NOT NULL, + type text, + to_id integer NOT NULL + ); + """ + ) + self.c.execute("CREATE INDEX IF NOT EXISTS idx_from_id ON relationships (from_id);") + + self.c.execute( + """ + CREATE TABLE IF NOT EXISTS properties ( + element_id integer NOT NULL, + set_name text, + name text, + value text + ); + """ + ) + self.c.execute("CREATE INDEX IF NOT EXISTS idx_element_id ON properties (element_id);") + + elements = self.file.by_type("IfcObjectDefinition") + + rows = [] + properties = [] + relationships = [] + id_map = {e.id(): i for i, e in enumerate(elements)} + for i, element in enumerate(elements): + rows.append([ + i, + element[0], + element.is_a(), + ifcopenshell.util.element.get_predefined_type(element), + element[2], + element[3], + ]) + psets = ifcopenshell.util.element.get_psets(element, should_inherit=False) + for pset_name, pset_data in psets.items(): + for prop_name, value in pset_data.items(): + if prop_name == "id" or value is None or value == "": + continue + if isinstance(value, bool): + value = "True" if value else "False" + elif not isinstance(value, str): + value = str(value) + properties.append([i, pset_name, prop_name, value]) + relating_type = ifcopenshell.util.element.get_type(element) + if relating_type and relating_type != element: + relationships.append([i, "IfcRelDefinesByType", id_map[relating_type.id()]]) + + self.c.executemany("INSERT INTO elements VALUES (?, ?, ?, ?, ?, ?);", rows) + self.c.executemany("INSERT INTO properties VALUES (?, ?, ?, ?);", properties) + self.c.executemany("INSERT INTO relationships VALUES (?, ?, ?);", relationships) + + self.db.commit() + self.db.close()