Implement Blender UI for handling new coordinate operations in IFC4X3

This commit is contained in:
Dion Moult
2024-06-22 23:32:25 +10:00
parent 4ef237e0f7
commit fd8cb7e8ed
8 changed files with 113 additions and 51 deletions
@@ -20,6 +20,7 @@
import bpy
import ifcopenshell.util.geolocation
import blenderbim.tool as tool
from ifcopenshell.util.doc import get_entity_doc
def refresh():
@@ -32,8 +33,9 @@ class GeoreferenceData:
@classmethod
def load(cls):
cls.data["coordinate_operation_class"] = cls.coordinate_operation_class()
cls.data["blender_derived_angle"] = cls.blender_derived_angle()
cls.data["map_conversion"] = cls.map_conversion()
cls.data["coordinate_operation"] = cls.coordinate_operation()
cls.data["map_derived_angle"] = cls.map_derived_angle()
cls.data["projected_crs"] = cls.projected_crs()
cls.data["true_north"] = cls.true_north()
@@ -42,6 +44,14 @@ class GeoreferenceData:
cls.data["map_unit_symbol"] = cls.map_unit_symbol()
cls.is_loaded = True
@classmethod
def coordinate_operation_class(cls):
declaration = tool.Ifc.schema().declaration_by_name("IfcCoordinateOperation")
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
names = [d.name() for d in declarations]
version = tool.Ifc.get_schema()
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
@classmethod
def blender_derived_angle(cls):
props = bpy.context.scene.BIMGeoreferenceProperties
@@ -56,36 +66,37 @@ class GeoreferenceData:
)
@classmethod
def map_conversion(cls):
def coordinate_operation(cls):
if tool.Ifc.get_schema() == "IFC2X3":
project = tool.Ifc.get().by_type("IfcProject")[0]
map_conversion = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion")
if not map_conversion:
coordinate_operation = ifcopenshell.util.element.get_pset(project, "ePSet_MapConversion")
if not coordinate_operation:
return {}
del map_conversion["id"]
return map_conversion
del coordinate_operation["id"]
coordinate_operation["type"] = "ePSet_MapConversion"
return coordinate_operation
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if context.HasCoordinateOperation:
map_conversion = context.HasCoordinateOperation[0].get_info()
del map_conversion["id"]
del map_conversion["type"]
del map_conversion["SourceCRS"]
del map_conversion["TargetCRS"]
return map_conversion
coordinate_operation = context.HasCoordinateOperation[0].get_info()
del coordinate_operation["id"]
del coordinate_operation["SourceCRS"]
del coordinate_operation["TargetCRS"]
return coordinate_operation
return {}
@classmethod
def map_derived_angle(cls):
if (
cls.data["map_conversion"]
and cls.data["map_conversion"].get("XAxisAbscissa", None) is not None
and cls.data["map_conversion"].get("XAxisOrdinate", None) is not None
cls.data["coordinate_operation"]
and cls.data["coordinate_operation"].get("XAxisAbscissa", None) is not None
and cls.data["coordinate_operation"].get("XAxisOrdinate", None) is not None
):
return str(
round(
ifcopenshell.util.geolocation.xaxis2angle(
cls.data["map_conversion"]["XAxisAbscissa"], cls.data["map_conversion"]["XAxisOrdinate"]
cls.data["coordinate_operation"]["XAxisAbscissa"],
cls.data["coordinate_operation"]["XAxisOrdinate"],
),
3,
)
@@ -29,7 +29,7 @@ class AddGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Add a new georeference"
def _execute(self, context):
core.add_georeferencing(tool.Ifc)
core.add_georeferencing(tool.Georeference)
class EnableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
@@ -29,11 +29,21 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from blenderbim.bim.module.georeference.data import GeoreferenceData
def get_coordinate_operation_class(self, context):
if not GeoreferenceData.is_loaded:
GeoreferenceData.load()
return GeoreferenceData.data["coordinate_operation_class"]
class BIMGeoreferenceProperties(PropertyGroup):
coordinate_operation_class: bpy.props.EnumProperty(
items=get_coordinate_operation_class, name="Coordinate Operation Class"
)
is_editing: BoolProperty(name="Is Editing")
map_conversion: CollectionProperty(name="Map Conversion", type=Attribute)
coordinate_operation: CollectionProperty(name="Coordinate Operation", type=Attribute)
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
local_coordinates: StringProperty(
name="Local Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0"
@@ -53,9 +53,9 @@ class BIM_PT_gis(Panel):
draw_attributes(props.projected_crs, self.layout)
row = self.layout.row()
row.label(text="Map Conversion", icon="GRID")
row.label(text="Coordinate Operation", icon="GRID")
for attribute in props.map_conversion:
for attribute in props.coordinate_operation:
if attribute.name == "Scale" and hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True)
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
@@ -80,12 +80,14 @@ class BIM_PT_gis(Panel):
if tool.Ifc.get_schema() == "IFC2X3":
row = self.layout.row()
row.label(text="IFC2X3 Fallback In Use", icon="ERROR")
row.label(text="IFC2X3 Fallback In Use", icon="INFO")
if not GeoreferenceData.data["projected_crs"]:
row = self.layout.row(align=True)
row.label(text="Not Georeferenced")
row = self.layout.row()
row.label(text="Not Georeferenced", icon="ERROR")
if tool.Ifc.get_schema() != "IFC2X3":
row = self.layout.row(align=True)
row.prop(props, "coordinate_operation_class", text="")
row.operator("bim.add_georeferencing", icon="ADD", text="")
if props.has_blender_offset:
@@ -125,13 +127,15 @@ class BIM_PT_gis(Panel):
row.label(text=key)
row.label(text=str(value))
if GeoreferenceData.data["map_conversion"]:
if GeoreferenceData.data["coordinate_operation"]:
row = self.layout.row(align=True)
row.label(text="Map Conversion", icon="GRID")
row.label(text="Coordinate Operation", icon="GRID")
for key, value in GeoreferenceData.data["map_conversion"].items():
for key, value in GeoreferenceData.data["coordinate_operation"].items():
if value is None:
continue
if key == "type":
key = "Type"
row = self.layout.row(align=True)
if key in ("Eastings", "Northings", "OrthogonalHeight"):
row.label(text=f"{key} ({GeoreferenceData.data['map_unit_symbol']})")
@@ -17,13 +17,13 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
def add_georeferencing(ifc):
ifc.run("georeference.add_georeferencing")
def add_georeferencing(georeference):
georeference.add_georeferencing()
def enable_editing_georeferencing(georeference):
georeference.import_projected_crs()
georeference.import_map_conversion()
georeference.import_coordinate_operation()
georeference.import_true_north()
georeference.enable_editing()
@@ -40,7 +40,7 @@ def edit_georeferencing(ifc, georeference):
ifc.run(
"georeference.edit_georeferencing",
projected_crs=georeference.get_projected_crs_attributes(),
map_conversion=georeference.get_map_conversion_attributes(),
coordinate_operation=georeference.get_coordinate_operation_attributes(),
true_north=georeference.get_true_north_attributes(),
)
georeference.disable_editing()
@@ -77,9 +77,11 @@ def convert_global_to_local(georeference):
georeference.set_coordinates("local", coordinates)
georeference.set_cursor_location()
def convert_angle_to_coord(georeference, type):
vector_coordinates = georeference.angle2coords(georeference.get_angle(type), type)
georeference.set_vector_coordinates(vector_coordinates,type)
georeference.set_vector_coordinates(vector_coordinates, type)
def import_plot(georeference, filepath):
georeference.import_plot(filepath)
+2 -2
View File
@@ -423,7 +423,7 @@ class Georeference:
def get_angle(cls, type): pass
def get_coordinates(cls, io): pass
def get_cursor_location(cls): pass
def get_map_conversion_attributes(cls): pass
def get_coordinate_operation_attributes(cls): pass
def get_projected_crs_attributes(cls): pass
def get_true_north_attributes(cls): pass
def import_map_conversion(cls): pass
@@ -1019,4 +1019,4 @@ class Voider:
@interface
class Web:
pass
pass
+50 -14
View File
@@ -22,10 +22,17 @@ import blenderbim.core.tool
import blenderbim.tool as tool
import ifcopenshell
import blenderbim.bim.helper
from math import radians, degrees, atan, tan, cos, sin
from math import radians, cos, sin
class Georeference(blenderbim.core.tool.Georeference):
@classmethod
def add_georeferencing(cls):
tool.Ifc.run(
"georeference.add_georeferencing",
ifc_class=bpy.context.scene.BIMGeoreferenceProperties.coordinate_operation_class,
)
@classmethod
def import_projected_crs(cls):
def callback(name, prop, data):
@@ -59,24 +66,44 @@ class Georeference(blenderbim.core.tool.Georeference):
return
@classmethod
def import_map_conversion(cls):
def import_coordinate_operation(cls):
def callback(name, prop, data):
if name not in ["SourceCRS", "TargetCRS"]:
if name in ("FirstCoordinate", "SecondCoordinate"):
props = bpy.context.scene.BIMGeoreferenceProperties
if name == "FirstCoordinate":
new = props.coordinate_operation.add()
new.name = "Measure Type"
new.data_type = "enum"
new.is_optional = False
new.is_null = False
new.enum_items = json.dumps(["IfcLengthMeasure", "IfcPlaneAngleMeasure"])
new.enum_value = data[name].is_a()
prop = props.coordinate_operation.add()
prop.name = name
prop.is_optional = False
prop.is_null = False
# Enforce a string data type to prevent data loss in single-precision Blender props
prop.data_type = "string"
prop.string_value = "" if prop.is_null else str(data[name].wrappedValue)
return True
elif name not in ("SourceCRS", "TargetCRS"):
# Enforce a string data type to prevent data loss in single-precision Blender props
prop.data_type = "string"
prop.string_value = "" if prop.is_null else str(data[name])
return True
props = bpy.context.scene.BIMGeoreferenceProperties
props.map_conversion.clear()
props.coordinate_operation.clear()
if tool.Ifc.get_schema() == "IFC2X3":
return
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if context.HasCoordinateOperation:
map_conversion = context.HasCoordinateOperation[0]
blenderbim.bim.helper.import_attributes2(map_conversion, props.map_conversion, callback=callback)
coordinate_operation = context.HasCoordinateOperation[0]
blenderbim.bim.helper.import_attributes2(
coordinate_operation, props.coordinate_operation, callback=callback
)
return
@classmethod
@@ -105,15 +132,24 @@ class Georeference(blenderbim.core.tool.Georeference):
return blenderbim.bim.helper.export_attributes(props.projected_crs, callback=callback)
@classmethod
def get_map_conversion_attributes(cls):
def get_coordinate_operation_attributes(cls):
measure_type = None
def callback(attributes, prop):
if not prop.is_null and prop.data_type == "string":
global measure_type
if prop.name == "Measure Type":
measure_type = prop.get_value()
return True
elif prop.name in ("FirstCoordinate", "SecondCoordinate"):
attributes[prop.name] = tool.Ifc.get().create_entity(measure_type, float(prop.string_value))
return True
elif not prop.is_null and prop.data_type == "string":
# We store our floats as string to prevent single precision data loss
attributes[prop.name] = float(prop.string_value)
return True
props = bpy.context.scene.BIMGeoreferenceProperties
return blenderbim.bim.helper.export_attributes(props.map_conversion, callback=callback)
return blenderbim.bim.helper.export_attributes(props.coordinate_operation, callback=callback)
@classmethod
def get_true_north_attributes(cls):
@@ -210,15 +246,15 @@ class Georeference(blenderbim.core.tool.Georeference):
def set_ifc_grid_north(cls):
x_angle = bpy.context.scene.sun_pos_properties.north_offset
props = bpy.context.scene.BIMGeoreferenceProperties
props.map_conversion.get("XAxisAbscissa").string_value = str(cos(x_angle))
props.map_conversion.get("XAxisOrdinate").string_value = str(sin(x_angle))
props.coordinate_operation.get("XAxisAbscissa").string_value = str(cos(x_angle))
props.coordinate_operation.get("XAxisOrdinate").string_value = str(sin(x_angle))
@classmethod
def set_blender_grid_north(cls):
props = bpy.context.scene.BIMGeoreferenceProperties
angle = ifcopenshell.util.geolocation.xaxis2angle(
float(props.map_conversion.get("XAxisAbscissa").string_value),
float(props.map_conversion.get("XAxisOrdinate").string_value),
float(props.coordinate_operation.get("XAxisAbscissa").string_value),
float(props.coordinate_operation.get("XAxisOrdinate").string_value),
)
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
@@ -247,7 +283,7 @@ class Georeference(blenderbim.core.tool.Georeference):
bpy.context.scene.BIMGeoreferenceProperties.y_axis_ordinate_output = str(y)
@classmethod
def import_plot(cls, filepath, map_conversion):
def import_plot(cls, filepath):
import bmesh
def parse_csv(file_path):
@@ -226,13 +226,12 @@ def get_helmert_transformation_parameters(ifc_file: ifcopenshell.file) -> Option
else:
factor_x = factor_y = factor_z = 1
elif conversion.is_a() == "IfcRigidOperation":
# TODO
e = conversion.FirstCoordinate
n = conversion.SecondCoordinate
e = conversion.FirstCoordinate.wrappedValue
n = conversion.SecondCoordinate.wrappedValue
h = conversion.Height or 0
xaa = 1.0
xao = 0.0
factor_x = factor_y = factor_z = 1
scale = factor_x = factor_y = factor_z = 1
if not xaa and not xao:
xaa = 1.0