From fcd5908ac95a70bdaa356024206f4f85ee6b783c Mon Sep 17 00:00:00 2001 From: maxfb87 Date: Fri, 15 Apr 2022 14:52:15 +0200 Subject: [PATCH] Finish refactor georeference module --- .../bim/module/georeference/__init__.py | 12 +- .../bim/module/georeference/data.py | 16 -- .../bim/module/georeference/operator.py | 78 +++++++++- .../blenderbim/bim/module/georeference/ui.py | 18 ++- .../blenderbim/core/georeference.py | 64 ++++---- src/blenderbim/blenderbim/core/tool.py | 13 +- .../blenderbim/tool/georeference.py | 144 +++++++++++++++++- 7 files changed, 276 insertions(+), 69 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/__init__.py b/src/blenderbim/blenderbim/bim/module/georeference/__init__.py index 609e9ce0d2..7d644a30df 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/__init__.py @@ -25,14 +25,14 @@ classes = ( operator.EditGeoreferencing, operator.SetIfcGridNorth, operator.SetBlenderGridNorth, -# operator.SetIfcTrueNorth, -# operator.SetBlenderTrueNorth, + operator.SetIfcTrueNorth, + operator.SetBlenderTrueNorth, operator.RemoveGeoreferencing, operator.AddGeoreferencing, -# operator.ConvertLocalToGlobal, -# operator.ConvertGlobalToLocal, -# operator.GetCursorLocation, -# operator.SetCursorLocation, + operator.ConvertLocalToGlobal, + operator.ConvertGlobalToLocal, + operator.GetCursorLocation, + operator.SetCursorLocation, prop.BIMGeoreferenceProperties, ui.BIM_PT_gis, ui.BIM_PT_gis_utilities, diff --git a/src/blenderbim/blenderbim/bim/module/georeference/data.py b/src/blenderbim/blenderbim/bim/module/georeference/data.py index 2fa8ef89a0..263e75774e 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/data.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/data.py @@ -16,8 +16,6 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . -# ############################################################################ # - import blenderbim.tool as tool def refresh(): @@ -35,20 +33,6 @@ class GeoreferenceData: "true_north" : cls.true_north(), } cls.is_loaded = True - - @classmethod - def is_georeferenced(cls): - ifc = tool.Ifc.get() - if ifc.schema == "IFC2X3": - return - - map_conversions = [] - for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False): - if not context.HasCoordinateOperation: - continue - map_conversions.append(context) - - return True if map_conversions else False @classmethod def map_conversion(cls): diff --git a/src/blenderbim/blenderbim/bim/module/georeference/operator.py b/src/blenderbim/blenderbim/bim/module/georeference/operator.py index 62e8e02b7d..b41a36379a 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/operator.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/operator.py @@ -19,7 +19,6 @@ import bpy import blenderbim.tool as tool import blenderbim.core.georeference as core -import blenderbim.bim.handler class AddGeoreferencing(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_georeferencing" @@ -79,10 +78,85 @@ class SetBlenderGridNorth(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.set_blender_grid_north" bl_label = "Set Blender Grid North" bl_options = {"REGISTER", "UNDO"} - bl_description = "Set Blender grif north" + bl_description = "Set Blender grid north" def _execute(self, context): core.set_blender_grid_north() +class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.get_cursor_location" + bl_label = "Get Cursor Location" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Insert the current cursor coordinates" + + @classmethod + def poll(cls, context): #TODO is it right to use the poll method? + file = tool.Ifc.get() + return file and file.by_type("IfcUnitAssignment") + + def _execute(self, context): + core.get_cursor_location(tool.Georeference) + +class SetCursorLocation(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.set_cursor_location" + bl_label = "Set Cursor Location" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Set curson location to coordinates" + @classmethod + def poll(cls, context): #TODO is it right to use the poll method? + file = tool.Ifc.get() + props = context.scene.BIMGeoreferenceProperties + return file and file.by_type("IfcUnitAssignment") and props.coordinate_output.count(",") == 2 + + def _execute(self, context): + core.set_cursor_location(tool.Georeference) + +class SetIfcTrueNorth(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.set_ifc_true_north" + bl_label = "Set IFC True North" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Set IFC True north" + + def _execute(self, context): + core.set_ifc_true_north(tool.Georeference) + +class SetBlenderTrueNorth(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.set_blender_true_north" + bl_label = "Set Blender True North" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Set Blender true north" + + def _execute(self, context): + core.set_blender_true_north(tool.Georeference) + +class ConvertLocalToGlobal(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.convert_local_to_global" + bl_label = "Convert Local To Global" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Convert local coordinate to global coordinate" + + @classmethod + def poll(cls, context): + file = tool.Ifc.get() + props = context.scene.BIMGeoreferenceProperties + return file and props.coordinate_input.count(",") == 2 + + def _execute(self, context): + core.convert_local_to_global(tool.Georeference) + +class ConvertGlobalToLocal(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.convert_global_to_local" + bl_label = "Convert Global To Local" + bl_options = {"REGISTER", "UNDO"} + bl_description = "Convert global coordinate to local coordinate" + + @classmethod + def poll(cls, context): + file = tool.Ifc.get() + props = context.scene.BIMGeoreferenceProperties + return file and file.by_type("IfcUnitAssignment") and props.coordinate_input.count(",") == 2 + + def _execute(self, context): + core.convert_global_to_local(tool.Georeference) diff --git a/src/blenderbim/blenderbim/bim/module/georeference/ui.py b/src/blenderbim/blenderbim/bim/module/georeference/ui.py index 984590e51c..93dcc34c38 100644 --- a/src/blenderbim/blenderbim/bim/module/georeference/ui.py +++ b/src/blenderbim/blenderbim/bim/module/georeference/ui.py @@ -18,7 +18,6 @@ import ifcopenshell.util.geolocation from bpy.types import Panel -#from ifcopenshell.api.georeference.data import Data from blenderbim.bim.ifc import IfcStore from blenderbim.bim.helper import draw_attributes, draw_attribute from blenderbim.bim.module.georeference.data import GeoreferenceData @@ -73,8 +72,8 @@ class BIM_PT_gis(Panel): row.prop(props, "true_north_ordinate") if hasattr(context.scene, "sun_pos_properties"): row = self.layout.row(align=True) - # row.operator("bim.set_ifc_true_north", text="Set IFC North") - # row.operator("bim.set_blender_true_north", text="Set Blender North") + row.operator("bim.set_ifc_true_north", text="Set IFC North") + row.operator("bim.set_blender_true_north", text="Set Blender North") def draw_ui(self, context): @@ -83,7 +82,7 @@ class BIM_PT_gis(Panel): if not GeoreferenceData.data["projected_crs"]: row = self.layout.row(align=True) row.label(text="Not Georeferenced") - if IfcStore.get_file().schema != "IFC2X3": + if IfcStore.get_file().schema != "IFC2X3": #TODO Is it correct to use IfcStore.get_file() or is it better tool.Ifc.get()? row.operator("bim.add_georeferencing", icon="ADD", text="") if props.has_blender_offset: @@ -179,16 +178,19 @@ class BIM_PT_gis_utilities(Panel): bl_category = "BlenderBIM" def draw(self, context): + if not GeoreferenceData.is_loaded: + GeoreferenceData.load() + props = context.scene.BIMGeoreferenceProperties row = self.layout.row(align=True) row.prop(props, "coordinate_input", text="Input") - #row.operator("bim.get_cursor_location", text="", icon="TRACKER") + row.operator("bim.get_cursor_location", text="", icon="TRACKER") row = self.layout.row(align=True) row.prop(props, "coordinate_output", text="Output") - #row.operator("bim.set_cursor_location", text="", icon="TRACKER") + row.operator("bim.set_cursor_location", text="", icon="TRACKER") row = self.layout.row(align=True) - #row.operator("bim.convert_local_to_global", text="Local to Global") - #row.operator("bim.convert_global_to_local", text="Global to Local") + row.operator("bim.convert_local_to_global", text="Local to Global") + row.operator("bim.convert_global_to_local", text="Global to Local") diff --git a/src/blenderbim/blenderbim/core/georeference.py b/src/blenderbim/blenderbim/core/georeference.py index 4ef8dcf960..4e5a174b90 100644 --- a/src/blenderbim/blenderbim/core/georeference.py +++ b/src/blenderbim/blenderbim/core/georeference.py @@ -18,71 +18,60 @@ import bpy import blenderbim.bim.helper -import ifcopenshell from blenderbim.bim.module.georeference.data import GeoreferenceData +from math import radians, degrees, atan, tan, cos, sin + def add_georeferencing(ifc): ifc.run("georeference.add_georeferencing") -def enable_editing_georeferencing(georeference): #TODO simplify this function +def enable_editing_georeferencing(georeference): + props = bpy.context.scene.BIMGeoreferenceProperties + georeference.clear_projected_crs() blenderbim.bim.helper.import_attributes( "IfcProjectedCRS", - bpy.context.scene.BIMGeoreferenceProperties.projected_crs, + props.projected_crs, GeoreferenceData.data["projected_crs"], georeference.import_projected_crs_attributes, ) - + georeference.clear_map_conversion() - + blenderbim.bim.helper.import_attributes( "IfcMapConversion", - bpy.context.scene.BIMGeoreferenceProperties.map_conversion, + props.map_conversion, GeoreferenceData.data["map_conversion"], georeference.import_map_conversion_attributes, ) - bpy.context.scene.BIMGeoreferenceProperties.has_true_north = bool(GeoreferenceData.data["true_north"]) - - if GeoreferenceData.data["true_north"]: - bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(GeoreferenceData.data["true_north"][0]) - bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(GeoreferenceData.data["true_north"][1]) - - bpy.context.scene.BIMGeoreferenceProperties.is_editing = True + georeference.set_has_true_north_prop(GeoreferenceData.data["true_north"]) + georeference.set_true_north_prop(GeoreferenceData.data["true_north"]) + georeference.enable_editing() def remove_georeferencing(ifc): ifc.run("georeference.remove_georeferencing") def edit_georeferencing(ifc, georeference): props = bpy.context.scene.BIMGeoreferenceProperties - ifc_file = georeference.get_file() projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs, georeference.export_crs_attributes) map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, georeference.export_map_attributes) - - true_north = None -# if props.has_true_north: -# try: -# true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)] -# except ValueError: -# self.report({"ERROR"}, "True North Abscissa and Ordinate expect a number") - - ifcopenshell.api.run( #TODO use ifc.run + + true_north = georeference.get_true_north_props(props) + + ifc.run( "georeference.edit_georeferencing", - ifc_file, **{ "map_conversion": map_conversion, "projected_crs": projected_crs, "true_north": true_north, } ) - - bpy.ops.bim.disable_editing_georeferencing() - -def disable_editing_georeferencing(georeference): - georeference.set_false_is_editing() + + georeference.disable_editing() def set_ifc_grid_north(georeference): georeference.set_ifc_grid_north() @@ -90,4 +79,21 @@ def set_ifc_grid_north(georeference): def set_blender_grid_north(georeference): georeference.set_blender_grid_north() +def get_cursor_location(georeference): + georeference.get_cursor_location() + +def set_cursor_location(georeference): + georeference.set_cursor_location() + +def set_ifc_true_north(georeference): + georeference.set_ifc_true_north() + +def set_blender_true_north(georeference): + georeference.set_blender_true_north() + +def convert_local_to_global(georeference): + georeference.convert_local_to_global(GeoreferenceData.data["map_conversion"]) + +def convert_global_to_local(georeference): + georeference.convert_global_to_local(GeoreferenceData.data["map_conversion"]) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 8d5997e8c4..da0733aef4 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -250,15 +250,24 @@ class Geometry: class Georeference: def clear_projected_crs(cls): pass def clear_map_conversion(cls): pass + def set_has_true_north_prop(cls, data): pass + def set_true_north_prop(cls, data): pass + def get_true_north_props(cls, props): pass + def enable_editing(cls): pass + def disable_editing(cls): pass def get_file(cls): pass - def set_false_is_editing(cls): pass def import_projected_crs_attributes(cls, name, prop, data): pass def import_map_conversion_attributes(cls, name, prop, data): pass def export_crs_attributes(cls, attributes, prop): pass def export_map_attributes(cls, attributes, prop): pass def set_ifc_grid_north(cls): pass def set_blender_grid_north(cls): pass - + def get_cursor_location(cls): pass + def set_cursor_location(cls): pass + def set_ifc_true_north(cls): pass + def set_blender_true_north(cls): pass + def convert_local_to_global(cls, map_conversion): pass + def convert_global_to_local(cls, map_conversion): pass @interface class Ifc: diff --git a/src/blenderbim/blenderbim/tool/georeference.py b/src/blenderbim/blenderbim/tool/georeference.py index 16ee96efe0..587acfcabe 100644 --- a/src/blenderbim/blenderbim/tool/georeference.py +++ b/src/blenderbim/blenderbim/tool/georeference.py @@ -17,11 +17,13 @@ # along with BlenderBIM Add-on. If not, see . + import bpy import json import blenderbim.core.tool import blenderbim.tool as tool import ifcopenshell +import blenderbim.bim.helper from ifcopenshell.api.unit.data import Data as UnitData @@ -35,6 +37,34 @@ class Georeference(blenderbim.core.tool.Georeference): def clear_map_conversion(cls): bpy.context.scene.BIMGeoreferenceProperties.map_conversion.clear() + @classmethod + def set_has_true_north_prop(cls, data): + bpy.context.scene.BIMGeoreferenceProperties.has_true_north = bool(data) + + @classmethod + def set_true_north_prop(cls, data): + if data: + bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(data[0]) + bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(data[1]) + + @classmethod + def get_true_north_props(cls, props): + true_north = None + if props.has_true_north: + try: + true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)] + except ValueError: + self.report({"ERROR"}, "True North Abscissa and Ordinate expect a number") #TODO use the correct report method + return true_north + + @classmethod + def enable_editing(cls): + bpy.context.scene.BIMGeoreferenceProperties.is_editing = True + + @classmethod + def disable_editing(cls): + bpy.context.scene.BIMGeoreferenceProperties.is_editing = False + @classmethod def get_file(cls): return tool.Ifc.get() @@ -63,7 +93,7 @@ class Georeference(blenderbim.core.tool.Georeference): return True @classmethod - def export_crs_attributes(cls, attributes, prop,): + def export_crs_attributes(cls, attributes, prop): ifc_file = tool.Ifc.get() if not prop.is_null and prop.name == "MapUnit": attributes[prop.name] = ifc_file.by_id(int(prop.enum_value)) @@ -76,11 +106,6 @@ class Georeference(blenderbim.core.tool.Georeference): attributes[prop.name] = float(prop.string_value) return True - @classmethod - def set_false_is_editing(cls): - props = bpy.context.scene.BIMGeoreferenceProperties - props.is_editing = False - @classmethod def set_ifc_grid_north(cls): x_angle = -bpy.context.scene.sun_pos_properties.north_offset @@ -96,4 +121,111 @@ class Georeference(blenderbim.core.tool.Georeference): ) ) + @classmethod + def get_cursor_location(cls): + props = bpy.context.scene.BIMGeoreferenceProperties + scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + project_coordinates = [o / scale for o in bpy.context.scene.cursor.location] + props.coordinate_input = ",".join([str(o) for o in project_coordinates]) + + + @classmethod + def set_cursor_location(cls): + props = bpy.context.scene.BIMGeoreferenceProperties + scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + bpy.context.scene.cursor.location = [float(co) * scale for co in props.coordinate_output.split(",")] + + @classmethod + def set_ifc_true_north(cls): + y_angle = -bpy.context.scene.sun_pos_properties.north_offset + radians(90) + bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(cos(y_angle)) + bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(sin(y_angle)) + + @classmethod + def set_blender_true_north(cls): + bpy.context.scene.sun_pos_properties.north_offset = -radians( + ifcopenshell.util.geolocation.yaxis2angle( + float(context.scene.BIMGeoreferenceProperties.true_north_abscissa), + float(context.scene.BIMGeoreferenceProperties.true_north_ordinate), + ) + ) + + + @classmethod + def convert_local_to_global(cls, map_conversion): + props = bpy.context.scene.BIMGeoreferenceProperties + x, y, z = [float(co) for co in props.coordinate_input.split(",")] + + if props.has_blender_offset: + results = ifcopenshell.util.geolocation.xyz2enh( + x, + y, + z, + float(props.blender_eastings), + float(props.blender_northings), + float(props.blender_orthogonal_height), + float(props.blender_x_axis_abscissa), + float(props.blender_x_axis_ordinate), + 1.0, + ) + x, y, z = results + + # TODO: what if the project CRS units and the project units are different? + + if map_conversion: + results = ifcopenshell.util.geolocation.xyz2enh( + x, + y, + z, + map_conversion["Eastings"], + map_conversion["Northings"], + map_conversion["OrthogonalHeight"], + map_conversion.get("XAxisAbscissa", 1.0), + map_conversion.get("XAxisOrdinate", 0.0), + map_conversion.get("Scale", 1.0), + ) + else: + results = (x, y, z) + + props.coordinate_output = ",".join([str(r) for r in results]) + bpy.context.scene.cursor.location = results + + @classmethod + def convert_global_to_local(cls, map_conversion): + props = bpy.context.scene.BIMGeoreferenceProperties + + x, y, z = [float(co) for co in props.coordinate_input.split(",")] + + if map_conversion: + results = ifcopenshell.util.geolocation.enh2xyz( + x, + y, + z, + map_conversion["Eastings"], + map_conversion["Northings"], + map_conversion["OrthogonalHeight"], + map_conversion.get("XAxisAbscissa", 1.0), + map_conversion.get("XAxisOrdinate", 0.0), + map_conversion.get("Scale", 1.0), + ) + else: + results = (x, y, z) + + if props.has_blender_offset: + results = ifcopenshell.util.geolocation.enh2xyz( + results[0], + results[1], + results[2], + float(props.blender_eastings), + float(props.blender_northings), + float(props.blender_orthogonal_height), + float(props.blender_x_axis_abscissa), + float(props.blender_x_axis_ordinate), + 1.0, + ) + + props.coordinate_output = ",".join([str(r) for r in results]) + + scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + bpy.context.scene.cursor.location = [o * scale for o in results]