Refactor BIM_PT_gis

This commit is contained in:
maxfb87
2022-04-12 22:13:55 +02:00
committed by Dion Moult
parent 07fa7003fb
commit 265ebaebb8
5 changed files with 102 additions and 362 deletions
@@ -24,15 +24,15 @@ classes = (
operator.DisableEditingGeoreferencing, operator.DisableEditingGeoreferencing,
operator.EditGeoreferencing, operator.EditGeoreferencing,
operator.SetIfcGridNorth, operator.SetIfcGridNorth,
operator.SetBlenderGridNorth, # operator.SetBlenderGridNorth,
operator.SetIfcTrueNorth, # operator.SetIfcTrueNorth,
operator.SetBlenderTrueNorth, # operator.SetBlenderTrueNorth,
operator.RemoveGeoreferencing, operator.RemoveGeoreferencing,
operator.AddGeoreferencing, operator.AddGeoreferencing,
operator.ConvertLocalToGlobal, # operator.ConvertLocalToGlobal,
operator.ConvertGlobalToLocal, # operator.ConvertGlobalToLocal,
operator.GetCursorLocation, # operator.GetCursorLocation,
operator.SetCursorLocation, # operator.SetCursorLocation,
prop.BIMGeoreferenceProperties, prop.BIMGeoreferenceProperties,
ui.BIM_PT_gis, ui.BIM_PT_gis,
ui.BIM_PT_gis_utilities, ui.BIM_PT_gis_utilities,
@@ -17,345 +17,72 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import json import blenderbim.tool as tool
import ifcopenshell import blenderbim.core.georeference as core
import ifcopenshell.util.unit import blenderbim.bim.handler
import ifcopenshell.util.attribute
import ifcopenshell.api
import blenderbim.bim.helper
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.georeference.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
from math import radians, degrees, atan, tan, cos, sin
class AddGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
class EnableEditingGeoreferencing(bpy.types.Operator):
bl_idname = "bim.enable_editing_georeferencing"
bl_label = "Enable Editing Georeferencing"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
self.props = props
props.projected_crs.clear()
blenderbim.bim.helper.import_attributes(
"IfcProjectedCRS", props.projected_crs, Data.projected_crs, self.import_projected_crs_attributes
)
props.map_conversion.clear()
blenderbim.bim.helper.import_attributes(
"IfcMapConversion", props.map_conversion, Data.map_conversion, self.import_map_conversion_attributes
)
props.has_true_north = bool(Data.true_north)
if Data.true_north:
props.true_north_abscissa = str(Data.true_north[0])
props.true_north_ordinate = str(Data.true_north[1])
props.is_editing = True
return {"FINISHED"}
def import_projected_crs_attributes(self, name, prop, data):
if name == "MapUnit":
new = self.props.projected_crs.add()
new.name = name
new.data_type = "enum"
new.is_null = data[name] is None
new.is_optional = True
new.enum_items = json.dumps(
{u["id"]: u["Name"] for u in UnitData.units.values() if u["UnitType"] == "LENGTHUNIT"}
)
if data["MapUnit"]:
new.enum_value = str(data["MapUnit"]["id"])
return True
def import_map_conversion_attributes(self, name, prop, data):
if 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
class DisableEditingGeoreferencing(bpy.types.Operator):
bl_idname = "bim.disable_editing_georeferencing"
bl_label = "Disable Editing Georeferencing"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMGeoreferenceProperties
props.is_editing = False
return {"FINISHED"}
class EditGeoreferencing(bpy.types.Operator):
bl_idname = "bim.edit_georeferencing"
bl_label = "Edit Georeferencing"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs, self.export_crs_attributes)
map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, self.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(
"georeference.edit_georeferencing",
self.file,
**{
"map_conversion": map_conversion,
"projected_crs": projected_crs,
"true_north": true_north,
}
)
Data.load(self.file)
bpy.ops.bim.disable_editing_georeferencing()
return {"FINISHED"}
def export_map_attributes(self, attributes, prop):
if 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
def export_crs_attributes(self, attributes, prop):
if not prop.is_null and prop.name == "MapUnit":
attributes[prop.name] = self.file.by_id(int(prop.enum_value))
return True
class SetBlenderGridNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_grid_north"
bl_label = "Set Blender Grid North"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.sun_pos_properties.north_offset = -radians(
ifcopenshell.util.geolocation.xaxis2angle(
float(context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value),
float(context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value),
)
)
return {"FINISHED"}
class SetIfcGridNorth(bpy.types.Operator):
bl_idname = "bim.set_ifc_grid_north"
bl_label = "Set IFC Grid North"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
x_angle = -context.scene.sun_pos_properties.north_offset
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value = str(cos(x_angle))
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value = str(sin(x_angle))
return {"FINISHED"}
class SetBlenderTrueNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_true_north"
bl_label = "Set Blender True North"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
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),
)
)
return {"FINISHED"}
class SetIfcTrueNorth(bpy.types.Operator):
bl_idname = "bim.set_ifc_true_north"
bl_label = "Set IFC True North"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
y_angle = -context.scene.sun_pos_properties.north_offset + radians(90)
context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(cos(y_angle))
context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(sin(y_angle))
return {"FINISHED"}
class RemoveGeoreferencing(bpy.types.Operator):
bl_idname = "bim.remove_georeferencing"
bl_label = "Remove Georeferencing"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
ifcopenshell.api.run("georeference.remove_georeferencing", IfcStore.get_file())
Data.load(IfcStore.get_file())
return {"FINISHED"}
class AddGeoreferencing(bpy.types.Operator):
bl_idname = "bim.add_georeferencing" bl_idname = "bim.add_georeferencing"
bl_label = "Add Georeferencing" bl_label = "Add Georeferencing"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Add a new georeference"
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
ifcopenshell.api.run("georeference.add_georeferencing", IfcStore.get_file()) core.add_georeferencing(tool.Ifc)
Data.load(IfcStore.get_file())
return {"FINISHED"}
class EnableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
class ConvertLocalToGlobal(bpy.types.Operator): bl_idname = "bim.enable_editing_georeferencing"
bl_idname = "bim.convert_local_to_global" bl_label = "Enable Editing Georeferencing"
bl_label = "Convert Local To Global"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Enable editing georeferencing"
@classmethod def _execute(self, context):
def poll(cls, context): core.enable_editing_georeferencing(tool.Georeference)
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and props.coordinate_input.count(",") == 2
def execute(self, context): class RemoveGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
if not Data.is_loaded: bl_idname = "bim.remove_georeferencing"
Data.load(IfcStore.get_file()) bl_label = "Remove Georeferencing"
props = 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 Data.map_conversion:
results = ifcopenshell.util.geolocation.xyz2enh(
x,
y,
z,
Data.map_conversion["Eastings"],
Data.map_conversion["Northings"],
Data.map_conversion["OrthogonalHeight"],
Data.map_conversion.get("XAxisAbscissa", 1.0),
Data.map_conversion.get("XAxisOrdinate", 0.0),
Data.map_conversion.get("Scale", 1.0),
)
else:
results = (x, y, z)
props.coordinate_output = ",".join([str(r) for r in results])
context.scene.cursor.location = results
return {"FINISHED"}
class ConvertGlobalToLocal(bpy.types.Operator):
bl_idname = "bim.convert_global_to_local"
bl_label = "Convert Global To Local"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Remove the georeferencing"
@classmethod def _execute(self, context):
def poll(cls, context): core.remove_georeferencing(tool.Ifc)
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and file.by_type("IfcUnitAssignment") and props.coordinate_input.count(",") == 2
def execute(self, context): class EditGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
if not Data.is_loaded: bl_idname = "bim.edit_georeferencing"
Data.load(IfcStore.get_file()) bl_label = "Edit Georeferencing"
props = context.scene.BIMGeoreferenceProperties
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
if Data.map_conversion:
results = ifcopenshell.util.geolocation.enh2xyz(
x,
y,
z,
Data.map_conversion["Eastings"],
Data.map_conversion["Northings"],
Data.map_conversion["OrthogonalHeight"],
Data.map_conversion.get("XAxisAbscissa", 1.0),
Data.map_conversion.get("XAxisOrdinate", 0.0),
Data.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(IfcStore.get_file())
context.scene.cursor.location = [o * scale for o in results]
return {"FINISHED"}
class GetCursorLocation(bpy.types.Operator):
bl_idname = "bim.get_cursor_location"
bl_label = "Get Cursor Location"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Edit the georeferencing"
@classmethod def _execute(self, context):
def poll(cls, context): core.edit_georeferencing(tool.Ifc, tool.Georeference)
file = IfcStore.get_file()
return file and file.by_type("IfcUnitAssignment")
def execute(self, context): class DisableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
props = context.scene.BIMGeoreferenceProperties bl_idname = "bim.disable_editing_georeferencing"
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file()) bl_label = "Disable Editing Georeferencing"
project_coordinates = [o / scale for o in context.scene.cursor.location]
props.coordinate_input = ",".join([str(o) for o in project_coordinates])
return {"FINISHED"}
class SetCursorLocation(bpy.types.Operator):
bl_idname = "bim.set_cursor_location"
bl_label = "Set Cursor Location"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
bl_description = "Close editing panel"
def _execute(self, context):
core.disable_editing_georeferencing(tool.Georeference)
class SetIfcGridNorth(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.set_ifc_grid_north"
bl_label = "Set IFC Grid North"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Set IFC grid north"
def _execute(self, context):
core.set_ifc_grid_north()
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"
def _execute(self, context):
core.set_blender_grid_north()
@classmethod
def poll(cls, context):
file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
return file and file.by_type("IfcUnitAssignment") and props.coordinate_output.count(",") == 2
def execute(self, context):
props = context.scene.BIMGeoreferenceProperties
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
context.scene.cursor.location = [float(co) * scale for co in props.coordinate_output.split(",")]
return {"FINISHED"}
@@ -18,10 +18,10 @@
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
from bpy.types import Panel from bpy.types import Panel
from ifcopenshell.api.georeference.data import Data #from ifcopenshell.api.georeference.data import Data
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes, draw_attribute from blenderbim.bim.helper import draw_attributes, draw_attribute
from blenderbim.bim.module.georeference.data import GeoreferenceData
class BIM_PT_gis(Panel): class BIM_PT_gis(Panel):
bl_label = "IFC Georeferencing" bl_label = "IFC Georeferencing"
@@ -32,16 +32,13 @@ class BIM_PT_gis(Panel):
bl_context = "scene" bl_context = "scene"
bl_parent_id = "BIM_PT_geometry" bl_parent_id = "BIM_PT_geometry"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context): def draw(self, context):
self.layout.use_property_split = True self.layout.use_property_split = True
self.layout.use_property_decorate = False self.layout.use_property_decorate = False
props = context.scene.BIMGeoreferenceProperties props = context.scene.BIMGeoreferenceProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file()) if not GeoreferenceData.is_loaded:
GeoreferenceData.load()
if props.is_editing: if props.is_editing:
return self.draw_editable_ui(context) return self.draw_editable_ui(context)
@@ -76,13 +73,14 @@ class BIM_PT_gis(Panel):
row.prop(props, "true_north_ordinate") row.prop(props, "true_north_ordinate")
if hasattr(context.scene, "sun_pos_properties"): if hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.operator("bim.set_ifc_true_north", text="Set IFC North") # 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_blender_true_north", text="Set Blender North")
def draw_ui(self, context): def draw_ui(self, context):
props = context.scene.BIMGeoreferenceProperties props = context.scene.BIMGeoreferenceProperties
if not Data.projected_crs: if not GeoreferenceData.data["projected_crs"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Not Georeferenced") row.label(text="Not Georeferenced")
if IfcStore.get_file().schema != "IFC2X3": if IfcStore.get_file().schema != "IFC2X3":
@@ -120,13 +118,13 @@ class BIM_PT_gis(Panel):
) )
) )
if Data.projected_crs: if GeoreferenceData.data["projected_crs"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Projected CRS", icon="WORLD") row.label(text="Projected CRS", icon="WORLD")
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="") row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
row.operator("bim.remove_georeferencing", icon="X", text="") row.operator("bim.remove_georeferencing", icon="X", text="")
for key, value in Data.projected_crs.items(): for key, value in GeoreferenceData.data["projected_crs"].items():
if key == "id" or key == "type" or not value: if key == "id" or key == "type" or not value:
continue continue
if key == "MapUnit": if key == "MapUnit":
@@ -137,11 +135,11 @@ class BIM_PT_gis(Panel):
row.label(text=key) row.label(text=key)
row.label(text=str(value)) row.label(text=str(value))
if Data.map_conversion: if GeoreferenceData.data["map_conversion"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Map Conversion", icon="GRID") row.label(text="Map Conversion", icon="GRID")
for key, value in Data.map_conversion.items(): for key, value in GeoreferenceData.data["map_conversion"].items():
if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or value is None: if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or value is None:
continue continue
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -154,22 +152,22 @@ class BIM_PT_gis(Panel):
text=str( text=str(
round( round(
ifcopenshell.util.geolocation.xaxis2angle( ifcopenshell.util.geolocation.xaxis2angle(
Data.map_conversion["XAxisAbscissa"], Data.map_conversion["XAxisOrdinate"] GeoreferenceData.data["map_conversion"]["XAxisAbscissa"], GeoreferenceData.data["map_conversion"]["XAxisOrdinate"]
), ),
3, 3,
) )
) )
) )
if Data.true_north: if GeoreferenceData.data["true_north"]:
row = self.layout.row() row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN") row.label(text="True North", icon="LIGHT_SUN")
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Vector") row.label(text="Vector")
row.label(text=str(Data.true_north[0:2])[1:-1]) row.label(text=str(GeoreferenceData.data["true_north"][0:2])[1:-1])
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Derived Angle") row.label(text="Derived Angle")
row.label(text=str(round(ifcopenshell.util.geolocation.yaxis2angle(*Data.true_north[0:2]), 3))) row.label(text=str(round(ifcopenshell.util.geolocation.yaxis2angle(*GeoreferenceData.data["true_north"][0:2]), 3)))
class BIM_PT_gis_utilities(Panel): class BIM_PT_gis_utilities(Panel):
@@ -185,11 +183,12 @@ class BIM_PT_gis_utilities(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(props, "coordinate_input", text="Input") 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 = self.layout.row(align=True)
row.prop(props, "coordinate_output", text="Output") 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 = self.layout.row(align=True)
row.operator("bim.convert_local_to_global", text="Local to Global") #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_global_to_local", text="Global to Local")
+13
View File
@@ -246,6 +246,19 @@ class Geometry:
def should_generate_uvs(cls, obj): pass def should_generate_uvs(cls, obj): pass
def should_use_presentation_style_assignment(cls): pass def should_use_presentation_style_assignment(cls): pass
@interface
class Georeference:
def clear_projected_crs(cls): pass
def clear_map_conversion(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
@interface @interface
class Ifc: class Ifc:
@@ -27,6 +27,7 @@ from blenderbim.tool.demo import Demo
from blenderbim.tool.document import Document from blenderbim.tool.document import Document
from blenderbim.tool.drawing import Drawing from blenderbim.tool.drawing import Drawing
from blenderbim.tool.geometry import Geometry from blenderbim.tool.geometry import Geometry
from blenderbim.tool.georeference import Georeference
from blenderbim.tool.ifc import Ifc from blenderbim.tool.ifc import Ifc
from blenderbim.tool.library import Library from blenderbim.tool.library import Library
from blenderbim.tool.material import Material from blenderbim.tool.material import Material