mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 22:43:41 +00:00
Assigning a brick reference now adds a 2-way IFC reference in the Brick model
This commit is contained in:
@@ -20,6 +20,12 @@ import bpy
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.tool.brick import BrickStore
|
from blenderbim.tool.brick import BrickStore
|
||||||
|
|
||||||
|
try:
|
||||||
|
from rdflib import URIRef, BNode
|
||||||
|
except:
|
||||||
|
# See #1860
|
||||||
|
print("Warning: brickschema not available.")
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
BrickschemaData.is_loaded = False
|
BrickschemaData.is_loaded = False
|
||||||
@@ -67,15 +73,30 @@ class BrickschemaData:
|
|||||||
"{uri}", uri
|
"{uri}", uri
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for row in query:
|
for row in query:
|
||||||
|
name = row.get("name").toPython().split("#")[-1]
|
||||||
|
value = row.get("value")
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"name": row.get("name").toPython().split("#")[-1],
|
"name": name,
|
||||||
"value": row.get("value").toPython().split("#")[-1],
|
"value": value.toPython().split("#")[-1],
|
||||||
"is_uri": "#" in row.get("value").toPython(),
|
"is_uri": isinstance(value, URIRef),
|
||||||
"value_uri": row.get("value").toPython(),
|
"value_uri": value.toPython(),
|
||||||
|
"is_globalid": name == "globalID",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if isinstance(row.get("value"), BNode):
|
||||||
|
for s, p, o in BrickStore.graph.triples((value, None, None)):
|
||||||
|
results.append(
|
||||||
|
{
|
||||||
|
"name": name + ":" + p.toPython().split("#")[-1],
|
||||||
|
"value": o.toPython().split("#")[-1],
|
||||||
|
"is_uri": isinstance(o, URIRef),
|
||||||
|
"value_uri": o.toPython(),
|
||||||
|
"is_globalid": p.toPython().split("#")[-1] == "globalID",
|
||||||
|
}
|
||||||
|
)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@@ -96,7 +117,10 @@ class BrickschemaReferencesData:
|
|||||||
def libraries(cls):
|
def libraries(cls):
|
||||||
results = []
|
results = []
|
||||||
for library in tool.Ifc.get().by_type("IfcLibraryInformation"):
|
for library in tool.Ifc.get().by_type("IfcLibraryInformation"):
|
||||||
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
if tool.Ifc.get_schema() == "IFC2X3":
|
||||||
|
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||||
|
elif ".ttl" in library.Location:
|
||||||
|
results.append((str(library.id()), library.Name or "Unnamed", ""))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -60,4 +60,9 @@ def assign_brick_reference(ifc, brick, obj=None, library=None, brick_uri=None):
|
|||||||
if not reference:
|
if not reference:
|
||||||
reference = ifc.run("library.add_reference", library=library)
|
reference = ifc.run("library.add_reference", library=library)
|
||||||
ifc.run("library.edit_reference", reference=reference, attributes=brick.export_brick_attributes(brick_uri))
|
ifc.run("library.edit_reference", reference=reference, attributes=brick.export_brick_attributes(brick_uri))
|
||||||
ifc.run("library.assign_reference", product=ifc.get_entity(obj), reference=reference)
|
product = ifc.get_entity(obj)
|
||||||
|
ifc.run("library.assign_reference", product=product, reference=reference)
|
||||||
|
project = brick.get_brickifc_project()
|
||||||
|
if not project:
|
||||||
|
project = brick.add_brickifc_project(brick.get_namespace(brick_uri))
|
||||||
|
brick.add_brickifc_reference(brick_uri, product, project)
|
||||||
|
|||||||
@@ -44,13 +44,17 @@ class Blender: pass
|
|||||||
@interface
|
@interface
|
||||||
class Brick:
|
class Brick:
|
||||||
def add_brick_breadcrumb(cls): pass
|
def add_brick_breadcrumb(cls): pass
|
||||||
|
def add_brickifc_project(cls, namespace): pass
|
||||||
|
def add_brickifc_reference(cls, brick, element, project): pass
|
||||||
def clear_brick_browser(cls): pass
|
def clear_brick_browser(cls): pass
|
||||||
def clear_project(cls): pass
|
def clear_project(cls): pass
|
||||||
def export_brick_attributes(cls, brick_uri): pass
|
def export_brick_attributes(cls, brick_uri): pass
|
||||||
def get_brick_path(cls): pass
|
def get_brick_path(cls): pass
|
||||||
def get_brick_path_name(cls): pass
|
def get_brick_path_name(cls): pass
|
||||||
|
def get_brickifc_project(cls): pass
|
||||||
def get_item_class(cls, item): pass
|
def get_item_class(cls, item): pass
|
||||||
def get_library_brick_reference(cls, library, brick_uri): pass
|
def get_library_brick_reference(cls, library, brick_uri): pass
|
||||||
|
def get_namespace(cls, uri): pass
|
||||||
def import_brick_classes(cls, brick_class): pass
|
def import_brick_classes(cls, brick_class): pass
|
||||||
def import_brick_items(cls, brick_class): pass
|
def import_brick_items(cls, brick_class): pass
|
||||||
def load_brick_file(cls, filepath): pass
|
def load_brick_file(cls, filepath): pass
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import blenderbim.tool as tool
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import brickschema
|
import brickschema
|
||||||
|
import urllib.parse
|
||||||
|
from rdflib import Literal, URIRef, Namespace
|
||||||
|
from rdflib.namespace import RDF
|
||||||
except:
|
except:
|
||||||
# See #1860
|
# See #1860
|
||||||
print("Warning: brickschema not available.")
|
print("Warning: brickschema not available.")
|
||||||
@@ -38,6 +41,39 @@ class Brick(blenderbim.core.tool.Brick):
|
|||||||
new = bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.add()
|
new = bpy.context.scene.BIMBrickProperties.brick_breadcrumbs.add()
|
||||||
new.name = bpy.context.scene.BIMBrickProperties.active_brick_class
|
new.name = bpy.context.scene.BIMBrickProperties.active_brick_class
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_brickifc_project(cls, namespace):
|
||||||
|
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||||
|
ns = Namespace(namespace)
|
||||||
|
ns_brickifc = Namespace("https://brickschema.org/extension/ifc#")
|
||||||
|
BrickStore.graph.bind("brickifc", ns_brickifc)
|
||||||
|
brick_project = ns[project.GlobalId]
|
||||||
|
BrickStore.graph.add((brick_project, RDF.type, ns_brickifc["Project"]))
|
||||||
|
if project.Name:
|
||||||
|
BrickStore.graph.add(
|
||||||
|
(brick_project, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal(project.Name))
|
||||||
|
)
|
||||||
|
BrickStore.graph.add((brick_project, ns_brickifc["projectID"], Literal(project.GlobalId)))
|
||||||
|
BrickStore.graph.add(
|
||||||
|
(brick_project, ns_brickifc["fileLocation"], Literal(bpy.context.scene.BIMProperties.ifc_file))
|
||||||
|
)
|
||||||
|
return str(brick_project)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_brickifc_reference(cls, brick, element, project):
|
||||||
|
ns_brickifc = Namespace("https://brickschema.org/extension/ifc#")
|
||||||
|
BrickStore.graph.bind("brickifc", ns_brickifc)
|
||||||
|
BrickStore.graph.add(
|
||||||
|
(
|
||||||
|
URIRef(brick),
|
||||||
|
ns_brickifc["hasIFCReference"],
|
||||||
|
[
|
||||||
|
(ns_brickifc["hasProjectReference"], URIRef(project)),
|
||||||
|
(ns_brickifc["globalID"], Literal(element.GlobalId)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_brick_browser(cls):
|
def clear_brick_browser(cls):
|
||||||
bpy.context.scene.BIMBrickProperties.bricks.clear()
|
bpy.context.scene.BIMBrickProperties.bricks.clear()
|
||||||
@@ -63,6 +99,25 @@ class Brick(blenderbim.core.tool.Brick):
|
|||||||
def get_brick_path_name(cls):
|
def get_brick_path_name(cls):
|
||||||
return os.path.basename(BrickStore.path)
|
return os.path.basename(BrickStore.path)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_brickifc_project(cls):
|
||||||
|
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||||
|
query = BrickStore.graph.query(
|
||||||
|
"""
|
||||||
|
PREFIX brickifc: <https://brickschema.org/extension/ifc#>
|
||||||
|
SELECT ?proj WHERE {
|
||||||
|
?proj a brickifc:Project .
|
||||||
|
?proj brickifc:projectID "{project_globalid}"
|
||||||
|
}
|
||||||
|
LIMIT 1
|
||||||
|
""".replace(
|
||||||
|
"{project_globalid}", project.GlobalId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
results = list(query)
|
||||||
|
if results:
|
||||||
|
return results[0][0].toPython()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_item_class(cls, item):
|
def get_item_class(cls, item):
|
||||||
query = BrickStore.graph.query(
|
query = BrickStore.graph.query(
|
||||||
@@ -90,6 +145,10 @@ class Brick(blenderbim.core.tool.Brick):
|
|||||||
if reference.Identification == brick_uri:
|
if reference.Identification == brick_uri:
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_namespace(cls, uri):
|
||||||
|
return uri.split("#")[0] + "#"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_brick_classes(cls, brick_class):
|
def import_brick_classes(cls, brick_class):
|
||||||
query = BrickStore.graph.query(
|
query = BrickStore.graph.query(
|
||||||
|
|||||||
@@ -30,3 +30,22 @@ Scenario: Close Brick project
|
|||||||
And I press "bim.load_brick_project(filepath='{cwd}/test/files/spaces.ttl')"
|
And I press "bim.load_brick_project(filepath='{cwd}/test/files/spaces.ttl')"
|
||||||
When I press "bim.close_brick_project"
|
When I press "bim.close_brick_project"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Convert brick project
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.load_brick_project(filepath='{cwd}/test/files/spaces.ttl')"
|
||||||
|
When I press "bim.convert_brick_project"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Assign brick reference
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.load_brick_project(filepath='{cwd}/test/files/spaces.ttl')"
|
||||||
|
And I press "bim.view_brick_item(item='https://brickschema.org/schema/Brick#Chiller')"
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcChiller"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And the object "IfcChiller/Cube" is selected
|
||||||
|
And I press "bim.convert_brick_project"
|
||||||
|
When I press "bim.assign_brick_reference"
|
||||||
|
Then nothing happens
|
||||||
|
|||||||
@@ -92,10 +92,24 @@ class TestAssignBrickReference:
|
|||||||
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
|
||||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||||
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
||||||
|
brick.get_brickifc_project().should_be_called().will_return("project")
|
||||||
|
brick.add_brickifc_reference("brick", "product", "project").should_be_called()
|
||||||
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
||||||
|
|
||||||
def test_assigning_to_an_existing_reference(self, ifc, brick):
|
def test_assigning_to_an_existing_reference(self, ifc, brick):
|
||||||
brick.get_library_brick_reference("library", "brick").should_be_called().will_return("reference")
|
brick.get_library_brick_reference("library", "brick").should_be_called().will_return("reference")
|
||||||
ifc.get_entity("obj").should_be_called().will_return("product")
|
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||||
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
||||||
|
brick.get_brickifc_project().should_be_called().will_return("project")
|
||||||
|
brick.add_brickifc_reference("brick", "product", "project").should_be_called()
|
||||||
|
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
||||||
|
|
||||||
|
def test_adding_a_brickifc_project_if_it_doesnt_exist(self, ifc, brick):
|
||||||
|
brick.get_library_brick_reference("library", "brick").should_be_called().will_return("reference")
|
||||||
|
ifc.get_entity("obj").should_be_called().will_return("product")
|
||||||
|
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
|
||||||
|
brick.get_brickifc_project().should_be_called().will_return(None)
|
||||||
|
brick.get_namespace("brick").should_be_called().will_return("namespace")
|
||||||
|
brick.add_brickifc_project("namespace").should_be_called().will_return("project")
|
||||||
|
brick.add_brickifc_reference("brick", "product", "project").should_be_called()
|
||||||
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
subject.assign_brick_reference(ifc, brick, obj="obj", library="library", brick_uri="brick")
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import brickschema
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
from rdflib.namespace import RDF
|
||||||
|
from rdflib import Literal, URIRef, Namespace
|
||||||
from test.bim.bootstrap import NewFile
|
from test.bim.bootstrap import NewFile
|
||||||
from blenderbim.tool.brick import Brick as subject
|
from blenderbim.tool.brick import Brick as subject
|
||||||
from blenderbim.tool.brick import BrickStore
|
from blenderbim.tool.brick import BrickStore
|
||||||
@@ -41,6 +43,62 @@ class TestAddBrickBreadcrumb(NewFile):
|
|||||||
assert bpy.context.scene.BIMBrickProperties.brick_breadcrumbs[1].name == "brick_class"
|
assert bpy.context.scene.BIMBrickProperties.brick_breadcrumbs[1].name == "brick_class"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAddBrickifcProject(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
project = ifc.createIfcProject(ifcopenshell.guid.new())
|
||||||
|
project.Name = "My Project"
|
||||||
|
BrickStore.graph = brickschema.Graph()
|
||||||
|
result = subject.add_brickifc_project("http://example.org/digitaltwin#")
|
||||||
|
assert result == f"http://example.org/digitaltwin#{project.GlobalId}"
|
||||||
|
brick = URIRef(result)
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples((brick, RDF.type, URIRef("https://brickschema.org/extension/ifc#Project")))
|
||||||
|
)
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples(
|
||||||
|
(brick, URIRef("http://www.w3.org/2000/01/rdf-schema#label"), Literal("My Project"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples(
|
||||||
|
(brick, URIRef("https://brickschema.org/extension/ifc#projectID"), Literal(project.GlobalId))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples(
|
||||||
|
(
|
||||||
|
brick,
|
||||||
|
URIRef("https://brickschema.org/extension/ifc#fileLocation"),
|
||||||
|
Literal(bpy.context.scene.BIMProperties.ifc_file),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAddBrickifcReference(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
TestAddBrickifcProject().test_run()
|
||||||
|
element = tool.Ifc.get().createIfcChiller(ifcopenshell.guid.new())
|
||||||
|
project = URIRef(f"http://example.org/digitaltwin#{tool.Ifc.get().by_type('IfcProject')[0].GlobalId}")
|
||||||
|
subject.add_brickifc_reference("http://example.org/digitaltwin#foo", element, project)
|
||||||
|
brick = URIRef("http://example.org/digitaltwin#foo")
|
||||||
|
bnode = list(
|
||||||
|
BrickStore.graph.triples((brick, URIRef("https://brickschema.org/extension/ifc#hasIFCReference"), None))
|
||||||
|
)[0][2]
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples(
|
||||||
|
(bnode, URIRef("https://brickschema.org/extension/ifc#hasProjectReference"), project)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert list(
|
||||||
|
BrickStore.graph.triples(
|
||||||
|
(bnode, URIRef("https://brickschema.org/extension/ifc#globalID"), Literal(element.GlobalId))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestClearBrickBrowser(NewFile):
|
class TestClearBrickBrowser(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
bpy.context.scene.BIMBrickProperties.bricks.add()
|
bpy.context.scene.BIMBrickProperties.bricks.add()
|
||||||
@@ -81,6 +139,15 @@ class TestGetBrickPathName(NewFile):
|
|||||||
assert subject.get_brick_path_name() == "spaces.ttl"
|
assert subject.get_brick_path_name() == "spaces.ttl"
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetBrickifcProject(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
TestAddBrickifcProject().test_run()
|
||||||
|
assert (
|
||||||
|
subject.get_brickifc_project()
|
||||||
|
== f"http://example.org/digitaltwin#{tool.Ifc.get().by_type('IfcProject')[0].GlobalId}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestGetItemClass(NewFile):
|
class TestGetItemClass(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
TestLoadBrickFile().test_run()
|
TestLoadBrickFile().test_run()
|
||||||
@@ -102,6 +169,11 @@ class TestGetLibraryBrickReference(NewFile):
|
|||||||
assert subject.get_library_brick_reference(library, "ex:#floor") == reference
|
assert subject.get_library_brick_reference(library, "ex:#floor") == reference
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetNamespace(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
assert subject.get_namespace("http://example.org/digitaltwin#globalid") == "http://example.org/digitaltwin#"
|
||||||
|
|
||||||
|
|
||||||
class TestImportBrickClasses(NewFile):
|
class TestImportBrickClasses(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
TestLoadBrickFile().test_run()
|
TestLoadBrickFile().test_run()
|
||||||
|
|||||||
Reference in New Issue
Block a user