mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 18:21:59 +00:00
You can now load and save searches from the search panel
This commit is contained in:
@@ -48,7 +48,7 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
new = self.props.groups.add()
|
new = self.props.groups.add()
|
||||||
new.ifc_definition_id = group.id()
|
new.ifc_definition_id = group.id()
|
||||||
new.name = group.Name or "Unnamed"
|
new.name = group.Name or "Unnamed"
|
||||||
new.selection_query = group.Description.split("*selector*")[1] if group.Description else ""
|
new.selection_query = ""
|
||||||
new.tree_depth = tree_depth
|
new.tree_depth = tree_depth
|
||||||
new.has_children = False
|
new.has_children = False
|
||||||
new.is_expanded = group.id() in self.expanded_groups
|
new.is_expanded = group.id() in self.expanded_groups
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ classes = (
|
|||||||
operator.FilterModelElements,
|
operator.FilterModelElements,
|
||||||
operator.IfcSelector,
|
operator.IfcSelector,
|
||||||
operator.LoadQuery,
|
operator.LoadQuery,
|
||||||
|
operator.LoadSearch,
|
||||||
operator.OpenQueryLibrary,
|
operator.OpenQueryLibrary,
|
||||||
operator.RemoveFilter,
|
operator.RemoveFilter,
|
||||||
operator.RemoveFilterGroup,
|
operator.RemoveFilterGroup,
|
||||||
operator.ResetObjectColours,
|
operator.ResetObjectColours,
|
||||||
|
operator.SaveSearch,
|
||||||
operator.SaveSelectorQuery,
|
operator.SaveSelectorQuery,
|
||||||
operator.Search,
|
operator.Search,
|
||||||
operator.SelectAttribute,
|
operator.SelectAttribute,
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import json
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
|
||||||
|
def refresh():
|
||||||
|
SearchData.is_loaded = False
|
||||||
|
|
||||||
|
|
||||||
|
class SearchData:
|
||||||
|
data = {}
|
||||||
|
is_loaded = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
cls.is_loaded = True
|
||||||
|
cls.data = {}
|
||||||
|
cls.data["saved_searches"] = cls.saved_searches()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def saved_searches(cls):
|
||||||
|
groups = tool.Ifc.get().by_type("IfcGroup")
|
||||||
|
results = []
|
||||||
|
for group in groups:
|
||||||
|
try:
|
||||||
|
data = json.loads(group.Description)
|
||||||
|
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search":
|
||||||
|
results.append(group)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import bpy
|
import bpy
|
||||||
|
import json
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
@@ -120,48 +121,9 @@ class Search(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.BIMSearchProperties
|
props = context.scene.BIMSearchProperties
|
||||||
query = []
|
results = ifcopenshell.util.selector.filter_elements(
|
||||||
for filter_group in props.filter_groups:
|
tool.Ifc.get(), tool.Search.export_filter_query(props.filter_groups)
|
||||||
filter_group_query = []
|
)
|
||||||
has_instance_or_entity_filter = False
|
|
||||||
for ifc_filter in filter_group.filters:
|
|
||||||
if not ifc_filter.value:
|
|
||||||
continue
|
|
||||||
if ifc_filter.type == "instance":
|
|
||||||
has_instance_or_entity_filter = True
|
|
||||||
filter_group_query.append(ifc_filter.value)
|
|
||||||
elif ifc_filter.type == "entity":
|
|
||||||
has_instance_or_entity_filter = True
|
|
||||||
filter_group_query.append(ifc_filter.value)
|
|
||||||
elif ifc_filter.type == "attribute":
|
|
||||||
if not ifc_filter.name:
|
|
||||||
continue
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"{ifc_filter.name}{comparison}{value}")
|
|
||||||
elif ifc_filter.type == "type":
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"type{comparison}{value}")
|
|
||||||
elif ifc_filter.type == "material":
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"material{comparison}{value}")
|
|
||||||
elif ifc_filter.type == "property":
|
|
||||||
if not ifc_filter.pset or not ifc_filter.name:
|
|
||||||
continue
|
|
||||||
pset = self.wrap_value(ifc_filter, ifc_filter.pset)
|
|
||||||
name = self.wrap_value(ifc_filter, ifc_filter.name)
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"{pset}.{name}{comparison}{value}")
|
|
||||||
elif ifc_filter.type == "classification":
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"classification{comparison}{value}")
|
|
||||||
elif ifc_filter.type == "location":
|
|
||||||
comparison, value = self.get_comparison_and_value(ifc_filter)
|
|
||||||
filter_group_query.append(f"location{comparison}{value}")
|
|
||||||
if not has_instance_or_entity_filter:
|
|
||||||
filter_group_query.insert(0, "IfcElement")
|
|
||||||
query.append(", ".join(filter_group_query))
|
|
||||||
query = " + ".join(query)
|
|
||||||
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
|
||||||
|
|
||||||
total_selected = 0
|
total_selected = 0
|
||||||
for element in results:
|
for element in results:
|
||||||
@@ -171,17 +133,47 @@ class Search(Operator):
|
|||||||
self.report({"INFO"}, f"{len(results)} Results")
|
self.report({"INFO"}, f"{len(results)} Results")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def get_comparison_and_value(self, ifc_filter):
|
|
||||||
if ifc_filter.value.startswith("!="):
|
|
||||||
return ("!=", self.wrap_value(ifc_filter, ifc_filter.value[2:].strip()))
|
|
||||||
return ("=", self.wrap_value(ifc_filter, ifc_filter.value.strip()))
|
|
||||||
|
|
||||||
def wrap_value(self, ifc_filter, value):
|
class SaveSearch(Operator, tool.Ifc.Operator):
|
||||||
if value.startswith("/") and value.endswith("/"):
|
bl_idname = "bim.save_search"
|
||||||
return value
|
bl_label = "Save Search"
|
||||||
elif value in ("NULL", "TRUE", "FALSE"):
|
bl_description = "Save search filter to an IFC group"
|
||||||
return value
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
return '"' + value + '"'
|
name: StringProperty(name="Name")
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
if not self.name:
|
||||||
|
return
|
||||||
|
|
||||||
|
query = tool.Search.export_filter_query(context.scene.BIMSearchProperties.filter_groups)
|
||||||
|
results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query)
|
||||||
|
|
||||||
|
description = json.dumps({"type": "BBIM_Search", "query": query})
|
||||||
|
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.name, Description=description)
|
||||||
|
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=results, group=group)
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
|
||||||
|
class LoadSearch(Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.load_search"
|
||||||
|
bl_label = "Load Search"
|
||||||
|
bl_description = "Load search filter from an IFC group"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
props = context.scene.BIMSearchProperties
|
||||||
|
group = tool.Ifc.get().by_id(int(props.saved_searches))
|
||||||
|
query = tool.Search.import_filter_query(group, context.scene.BIMSearchProperties.filter_groups)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.BIMSearchProperties
|
||||||
|
row = self.layout.row()
|
||||||
|
row.prop(props, "saved_searches", text="")
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
|
||||||
class SelectGlobalId(Operator):
|
class SelectGlobalId(Operator):
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from ifcopenshell.util.selector import Selector
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.prop import ObjProperty, StrProperty
|
from blenderbim.bim.prop import ObjProperty, StrProperty
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.search.data import SearchData
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from blenderbim.tool.ifc import Ifc
|
from blenderbim.tool.ifc import Ifc
|
||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
@@ -37,6 +38,12 @@ from bpy.props import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_saved_searches(self, context):
|
||||||
|
if not SearchData.is_loaded:
|
||||||
|
SearchData.load()
|
||||||
|
return SearchData.data["saved_searches"]
|
||||||
|
|
||||||
|
|
||||||
def update_is_class_selected(self, context):
|
def update_is_class_selected(self, context):
|
||||||
if self.is_selected:
|
if self.is_selected:
|
||||||
for obj in self.unselected_objects:
|
for obj in self.unselected_objects:
|
||||||
@@ -106,6 +113,7 @@ class BIMSearchProperties(PropertyGroup):
|
|||||||
("instance", "GlobalId", "", "GRIP", 7),
|
("instance", "GlobalId", "", "GRIP", 7),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
saved_searches: EnumProperty(items=get_saved_searches, name="Saved Searches")
|
||||||
should_use_regex: BoolProperty(name="Search With Regex", default=False)
|
should_use_regex: BoolProperty(name="Search With Regex", default=False)
|
||||||
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
|
should_ignorecase: BoolProperty(name="Search Ignoring Case", default=True)
|
||||||
global_id: StringProperty(name="GlobalId")
|
global_id: StringProperty(name="GlobalId")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.module.search.data import SearchData
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_search(Panel):
|
class BIM_PT_search(Panel):
|
||||||
@@ -30,8 +30,18 @@ class BIM_PT_search(Panel):
|
|||||||
bl_parent_id = "BIM_PT_selection"
|
bl_parent_id = "BIM_PT_selection"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
if not SearchData.is_loaded:
|
||||||
|
SearchData.load()
|
||||||
|
|
||||||
props = context.scene.BIMSearchProperties
|
props = context.scene.BIMSearchProperties
|
||||||
|
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text=f"{len(SearchData.data['saved_searches'])} Saved Searches")
|
||||||
|
|
||||||
|
if SearchData.data["saved_searches"]:
|
||||||
|
row.operator("bim.load_search", text="", icon="IMPORT")
|
||||||
|
row.operator("bim.save_search", text="", icon="EXPORT")
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD")
|
row.operator("bim.add_filter_group", text="Add Search Group", icon="ADD")
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,173 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
import json
|
||||||
|
import lark
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import ifcopenshell.util.selector
|
||||||
from ifcopenshell.util.selector import Selector
|
from ifcopenshell.util.selector import Selector
|
||||||
|
|
||||||
|
|
||||||
class Search(blenderbim.core.tool.Search):
|
class Search(blenderbim.core.tool.Search):
|
||||||
|
@classmethod
|
||||||
|
def import_filter_query(cls, group, filter_groups):
|
||||||
|
query = json.loads(group.Description)["query"]
|
||||||
|
filter_groups.clear()
|
||||||
|
l = lark.Lark(ifcopenshell.util.selector.filter_elements_grammar)
|
||||||
|
transformer = ImportFilterQueryTransformer(filter_groups)
|
||||||
|
transformer.transform(l.parse(query))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_filter_query(cls, filter_groups):
|
||||||
|
query = []
|
||||||
|
for filter_group in filter_groups:
|
||||||
|
filter_group_query = []
|
||||||
|
has_instance_or_entity_filter = False
|
||||||
|
for ifc_filter in filter_group.filters:
|
||||||
|
if not ifc_filter.value:
|
||||||
|
continue
|
||||||
|
if ifc_filter.type == "instance":
|
||||||
|
has_instance_or_entity_filter = True
|
||||||
|
filter_group_query.append(ifc_filter.value)
|
||||||
|
elif ifc_filter.type == "entity":
|
||||||
|
has_instance_or_entity_filter = True
|
||||||
|
filter_group_query.append(ifc_filter.value)
|
||||||
|
elif ifc_filter.type == "attribute":
|
||||||
|
if not ifc_filter.name:
|
||||||
|
continue
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"{ifc_filter.name}{comparison}{value}")
|
||||||
|
elif ifc_filter.type == "type":
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"type{comparison}{value}")
|
||||||
|
elif ifc_filter.type == "material":
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"material{comparison}{value}")
|
||||||
|
elif ifc_filter.type == "property":
|
||||||
|
if not ifc_filter.pset or not ifc_filter.name:
|
||||||
|
continue
|
||||||
|
pset = cls.wrap_value(ifc_filter, ifc_filter.pset)
|
||||||
|
name = cls.wrap_value(ifc_filter, ifc_filter.name)
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"{pset}.{name}{comparison}{value}")
|
||||||
|
elif ifc_filter.type == "classification":
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"classification{comparison}{value}")
|
||||||
|
elif ifc_filter.type == "location":
|
||||||
|
comparison, value = cls.get_comparison_and_value(ifc_filter)
|
||||||
|
filter_group_query.append(f"location{comparison}{value}")
|
||||||
|
if not has_instance_or_entity_filter:
|
||||||
|
filter_group_query.insert(0, "IfcElement")
|
||||||
|
query.append(", ".join(filter_group_query))
|
||||||
|
return " + ".join(query)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_comparison_and_value(cls, ifc_filter):
|
||||||
|
if ifc_filter.value.startswith("!="):
|
||||||
|
return ("!=", cls.wrap_value(ifc_filter, ifc_filter.value[2:].strip()))
|
||||||
|
return ("=", cls.wrap_value(ifc_filter, ifc_filter.value.strip()))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wrap_value(cls, ifc_filter, value):
|
||||||
|
if value.startswith("/") and value.endswith("/"):
|
||||||
|
return value
|
||||||
|
elif value in ("NULL", "TRUE", "FALSE"):
|
||||||
|
return value
|
||||||
|
return '"' + value.replace('"', '\\"') + '"'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_selector_query(cls, query):
|
def from_selector_query(cls, query):
|
||||||
"""Returns a list of products from a selector query"""
|
"""Returns a list of products from a selector query"""
|
||||||
return Selector().parse(tool.Ifc.get(), query)
|
return Selector().parse(tool.Ifc.get(), query)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportFilterQueryTransformer(lark.Transformer):
|
||||||
|
def __init__(self, filter_groups):
|
||||||
|
self.filter_groups = filter_groups
|
||||||
|
|
||||||
|
def get_results(self):
|
||||||
|
results = set()
|
||||||
|
for r in self.results:
|
||||||
|
results |= r
|
||||||
|
return results
|
||||||
|
|
||||||
|
def facet_list(self, args):
|
||||||
|
new = self.filter_groups.add()
|
||||||
|
for arg in args:
|
||||||
|
new2 = new.filters.add()
|
||||||
|
new2.type = arg["type"]
|
||||||
|
new2.value = arg["value"]
|
||||||
|
if "name" in arg:
|
||||||
|
new2.name = arg["name"]
|
||||||
|
if "pset" in arg:
|
||||||
|
new2.pset = arg["pset"]
|
||||||
|
|
||||||
|
def facet(self, args):
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
def instance(self, args):
|
||||||
|
return {"type": "instance", "value": " ".join([a.children[0].value for a in args])}
|
||||||
|
|
||||||
|
def entity(self, args):
|
||||||
|
return {"type": "entity", "value": " ".join([a.children[0].value for a in args])}
|
||||||
|
|
||||||
|
def attribute(self, args):
|
||||||
|
name, comparison, value = args
|
||||||
|
name = name.children[0].value
|
||||||
|
return {"type": "attribute", "name": name, "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def type(self, args):
|
||||||
|
comparison, value = args
|
||||||
|
return {"type": "type", "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def material(self, args):
|
||||||
|
comparison, value = args
|
||||||
|
return {"type": "material", "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def property(self, args):
|
||||||
|
pset, prop, comparison, value = args
|
||||||
|
return {"type": "property", "pset": pset, "name": prop, "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def classification(self, args):
|
||||||
|
comparison, value = args
|
||||||
|
return {"type": "classification", "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def location(self, args):
|
||||||
|
comparison, value = args
|
||||||
|
return {"type": "location", "value": f"{comparison}{value}"}
|
||||||
|
|
||||||
|
def comparison(self, args):
|
||||||
|
return "" if args[0].data == "equals" else "!="
|
||||||
|
|
||||||
|
def pset(self, args):
|
||||||
|
return self.value(args)
|
||||||
|
|
||||||
|
def prop(self, args):
|
||||||
|
return self.value(args)
|
||||||
|
|
||||||
|
def value(self, args):
|
||||||
|
if args[0].data == "unquoted_string":
|
||||||
|
return args[0].children[0].value
|
||||||
|
elif args[0].data == "quoted_string":
|
||||||
|
return args[0].children[0].value[1:-1].replace('\\"', '"')
|
||||||
|
elif args[0].data == "regex_string":
|
||||||
|
return args[0].children[0].value
|
||||||
|
elif args[0].data == "special":
|
||||||
|
if args[0].children[0].data == "null":
|
||||||
|
return "NULL"
|
||||||
|
elif args[0].children[0].data == "true":
|
||||||
|
return "TRUE"
|
||||||
|
elif args[0].children[0].data == "false":
|
||||||
|
return "FALSE"
|
||||||
|
|
||||||
|
def compare(self, element_value, comparison, value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
if isinstance(element_value, int):
|
||||||
|
value = int(value)
|
||||||
|
elif isinstance(element_value, float):
|
||||||
|
value = float(value)
|
||||||
|
result = element_value == value
|
||||||
|
elif isinstance(value, re.Pattern):
|
||||||
|
result = bool(value.match(element_value))
|
||||||
|
elif value in (None, True, False):
|
||||||
|
result = element_value is value
|
||||||
|
return result if comparison == "=" else not result
|
||||||
|
|||||||
@@ -24,6 +24,71 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
|
|
||||||
|
|
||||||
|
filter_elements_grammar = """start: filter_group
|
||||||
|
filter_group: facet_list ("+" facet_list)*
|
||||||
|
facet_list: facet ("," facet)*
|
||||||
|
|
||||||
|
facet: instance | entity | attribute | type | material | property | classification | location
|
||||||
|
|
||||||
|
instance: not? globalid
|
||||||
|
globalid: /[0-3][a-zA-Z0-9_$]{21}/
|
||||||
|
entity: not? ifc_class
|
||||||
|
attribute: attribute_name comparison value
|
||||||
|
type: "type" comparison value
|
||||||
|
material: "material" comparison value
|
||||||
|
property: pset "." prop comparison value
|
||||||
|
classification: "classification" comparison value
|
||||||
|
location: "location" comparison value
|
||||||
|
|
||||||
|
pset: quoted_string | unquoted_string | regex_string
|
||||||
|
prop: quoted_string | unquoted_string | regex_string
|
||||||
|
|
||||||
|
attribute_name: /[A-Z]\\w+/
|
||||||
|
ifc_class: /Ifc\\w+/
|
||||||
|
|
||||||
|
value: special | quoted_string | unquoted_string | regex_string
|
||||||
|
unquoted_string: /[^.=\\s]+/
|
||||||
|
quoted_string: ESCAPED_STRING
|
||||||
|
regex_string: "/" /[^\\/]+/ "/"
|
||||||
|
|
||||||
|
special: null | true | false
|
||||||
|
|
||||||
|
comparison: not? equals
|
||||||
|
not: "!"
|
||||||
|
equals: "="
|
||||||
|
null: "NULL"
|
||||||
|
true: "TRUE"
|
||||||
|
false: "FALSE"
|
||||||
|
|
||||||
|
// Embed common.lark for packaging
|
||||||
|
DIGIT: "0".."9"
|
||||||
|
HEXDIGIT: "a".."f"|"A".."F"|DIGIT
|
||||||
|
INT: DIGIT+
|
||||||
|
SIGNED_INT: ["+"|"-"] INT
|
||||||
|
DECIMAL: INT "." INT? | "." INT
|
||||||
|
_EXP: ("e"|"E") SIGNED_INT
|
||||||
|
FLOAT: INT _EXP | DECIMAL _EXP?
|
||||||
|
SIGNED_FLOAT: ["+"|"-"] FLOAT
|
||||||
|
NUMBER: FLOAT | INT
|
||||||
|
SIGNED_NUMBER: ["+"|"-"] NUMBER
|
||||||
|
_STRING_INNER: /.*?/
|
||||||
|
_STRING_ESC_INNER: _STRING_INNER /(?<!\\\\)(\\\\\\\\)*?/
|
||||||
|
ESCAPED_STRING : "\\"" _STRING_ESC_INNER "\\""
|
||||||
|
LCASE_LETTER: "a".."z"
|
||||||
|
UCASE_LETTER: "A".."Z"
|
||||||
|
LETTER: UCASE_LETTER | LCASE_LETTER
|
||||||
|
WORD: LETTER+
|
||||||
|
CNAME: ("_"|LETTER) ("_"|LETTER|DIGIT)*
|
||||||
|
WS_INLINE: (" "|/\\t/)+
|
||||||
|
WS: /[ \\t\\f\\r\\n]/+
|
||||||
|
CR : /\\r/
|
||||||
|
LF : /\\n/
|
||||||
|
NEWLINE: (CR? LF)+
|
||||||
|
|
||||||
|
%ignore WS // Disregard spaces in text
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_element_value(element, query):
|
def get_element_value(element, query):
|
||||||
l = lark.Lark(
|
l = lark.Lark(
|
||||||
"""start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
"""start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
||||||
@@ -50,72 +115,7 @@ def get_element_value(element, query):
|
|||||||
|
|
||||||
|
|
||||||
def filter_elements(ifc_file, query, elements=None):
|
def filter_elements(ifc_file, query, elements=None):
|
||||||
l = lark.Lark(
|
l = lark.Lark(filter_elements_grammar)
|
||||||
"""start: filter_group
|
|
||||||
filter_group: facet_list ("+" facet_list)*
|
|
||||||
facet_list: facet ("," facet)*
|
|
||||||
|
|
||||||
facet: instance | entity | attribute | type | material | property | classification | location
|
|
||||||
|
|
||||||
instance: not? globalid
|
|
||||||
globalid: /[0-3][a-zA-Z0-9_$]{21}/
|
|
||||||
entity: not? ifc_class
|
|
||||||
attribute: attribute_name comparison value
|
|
||||||
type: "type" comparison value
|
|
||||||
material: "material" comparison value
|
|
||||||
property: pset "." prop comparison value
|
|
||||||
classification: "classification" comparison value
|
|
||||||
location: "location" comparison value
|
|
||||||
|
|
||||||
pset: quoted_string | unquoted_string | regex_string
|
|
||||||
prop: quoted_string | unquoted_string | regex_string
|
|
||||||
|
|
||||||
attribute_name: /[A-Z]\\w+/
|
|
||||||
ifc_class: /Ifc\\w+/
|
|
||||||
|
|
||||||
value: special | quoted_string | unquoted_string | regex_string
|
|
||||||
unquoted_string: /[^.=\\s]+/
|
|
||||||
quoted_string: ESCAPED_STRING
|
|
||||||
regex_string: "/" /[^\\/]+/ "/"
|
|
||||||
|
|
||||||
special: null | true | false
|
|
||||||
|
|
||||||
comparison: not? equals
|
|
||||||
not: "!"
|
|
||||||
equals: "="
|
|
||||||
null: "NULL"
|
|
||||||
true: "TRUE"
|
|
||||||
false: "FALSE"
|
|
||||||
|
|
||||||
// Embed common.lark for packaging
|
|
||||||
DIGIT: "0".."9"
|
|
||||||
HEXDIGIT: "a".."f"|"A".."F"|DIGIT
|
|
||||||
INT: DIGIT+
|
|
||||||
SIGNED_INT: ["+"|"-"] INT
|
|
||||||
DECIMAL: INT "." INT? | "." INT
|
|
||||||
_EXP: ("e"|"E") SIGNED_INT
|
|
||||||
FLOAT: INT _EXP | DECIMAL _EXP?
|
|
||||||
SIGNED_FLOAT: ["+"|"-"] FLOAT
|
|
||||||
NUMBER: FLOAT | INT
|
|
||||||
SIGNED_NUMBER: ["+"|"-"] NUMBER
|
|
||||||
_STRING_INNER: /.*?/
|
|
||||||
_STRING_ESC_INNER: _STRING_INNER /(?<!\\\\)(\\\\\\\\)*?/
|
|
||||||
ESCAPED_STRING : "\\"" _STRING_ESC_INNER "\\""
|
|
||||||
LCASE_LETTER: "a".."z"
|
|
||||||
UCASE_LETTER: "A".."Z"
|
|
||||||
LETTER: UCASE_LETTER | LCASE_LETTER
|
|
||||||
WORD: LETTER+
|
|
||||||
CNAME: ("_"|LETTER) ("_"|LETTER|DIGIT)*
|
|
||||||
WS_INLINE: (" "|/\\t/)+
|
|
||||||
WS: /[ \\t\\f\\r\\n]/+
|
|
||||||
CR : /\\r/
|
|
||||||
LF : /\\n/
|
|
||||||
NEWLINE: (CR? LF)+
|
|
||||||
|
|
||||||
%ignore WS // Disregard spaces in text
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
transformer = FacetTransformer(ifc_file, elements)
|
transformer = FacetTransformer(ifc_file, elements)
|
||||||
transformer.transform(l.parse(query))
|
transformer.transform(l.parse(query))
|
||||||
return transformer.get_results()
|
return transformer.get_results()
|
||||||
|
|||||||
Reference in New Issue
Block a user