mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-09 21:53:40 +00:00
New IfcPatch recipe to extract flattened properties and relationships to SQLite for simple queries
This commit is contained in:
@@ -0,0 +1,136 @@
|
|||||||
|
# IfcPatch - IFC patching utiliy
|
||||||
|
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
Reference in New Issue
Block a user