mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Implement Blender UI for handling new coordinate operations in IFC4X3
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user