Implement getting and setting true north rotation independent of grid north

This commit is contained in:
Dion Moult
2021-04-04 08:58:24 +10:00
parent 12c94fafab
commit 7b5aae9487
6 changed files with 119 additions and 11 deletions
@@ -5,8 +5,10 @@ classes = (
operator.EnableEditingGeoreferencing,
operator.DisableEditingGeoreferencing,
operator.EditGeoreferencing,
operator.SetNorthOffset,
operator.GetNorthOffset,
operator.SetIfcGridNorth,
operator.SetBlenderGridNorth,
operator.SetIfcTrueNorth,
operator.SetBlenderTrueNorth,
operator.RemoveGeoreferencing,
operator.AddGeoreferencing,
operator.ConvertLocalToGlobal,
@@ -69,6 +69,11 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
elif data_type == "boolean":
new.bool_value = False if new.is_null else Data.map_conversion[attribute.name()]
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"}
@@ -129,19 +134,31 @@ class EditGeoreferencing(bpy.types.Operator):
elif blender_attribute.data_type == "boolean":
map_conversion[attribute.name()] = blender_attribute.bool_value
true_north = None
if props.has_true_north:
try:
true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)]
except:
pass
ifcopenshell.api.run(
"georeference.edit_georeferencing",
self.file,
**{"map_conversion": map_conversion, "projected_crs": projected_crs, "map_unit": map_unit}
**{
"map_conversion": map_conversion,
"projected_crs": projected_crs,
"map_unit": map_unit,
"true_north": true_north,
}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_georeferencing()
return {"FINISHED"}
class SetNorthOffset(bpy.types.Operator):
bl_idname = "bim.set_north_offset"
bl_label = "Set North Offset"
class SetBlenderGridNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_grid_north"
bl_label = "Set Blender Grid North"
def execute(self, context):
context.scene.sun_pos_properties.north_offset = -radians(
@@ -153,9 +170,9 @@ class SetNorthOffset(bpy.types.Operator):
return {"FINISHED"}
class GetNorthOffset(bpy.types.Operator):
bl_idname = "bim.get_north_offset"
bl_label = "Get North Offset"
class SetIfcGridNorth(bpy.types.Operator):
bl_idname = "bim.set_ifc_grid_north"
bl_label = "Set IFC Grid North"
def execute(self, context):
x_angle = -context.scene.sun_pos_properties.north_offset
@@ -164,6 +181,31 @@ class GetNorthOffset(bpy.types.Operator):
return {"FINISHED"}
class SetBlenderTrueNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_true_north"
bl_label = "Set Blender True North"
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"
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"
@@ -46,3 +46,6 @@ class BIMGeoreferenceProperties(PropertyGroup):
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0")
blender_x_axis_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1")
blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0")
has_true_north: BoolProperty(name="Has True North", default=True)
true_north_abscissa: StringProperty(name="True North Abscissa")
true_north_ordinate: StringProperty(name="True North Ordinate")
@@ -1,7 +1,9 @@
import ifcopenshell.util.geolocation
from bpy.types import Panel
from ifcopenshell.api.georeference.data import Data
from blenderbim.bim.ifc import IfcStore
class BIM_PT_gis(Panel):
bl_label = "IFC Georeferencing"
bl_idname = "BIM_PT_gis"
@@ -58,8 +60,8 @@ class BIM_PT_gis(Panel):
for attribute in props.map_conversion:
if attribute.name == "Scale" and hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True)
row.operator("bim.get_north_offset", text="Set IFC North")
row.operator("bim.set_north_offset", text="Set Blender North")
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
row.operator("bim.set_blender_grid_north", text="Set Blender North")
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
@@ -72,6 +74,18 @@ class BIM_PT_gis(Panel):
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN")
row = self.layout.row()
row.prop(props, "has_true_north")
row = self.layout.row()
row.prop(props, "true_north_abscissa")
row = self.layout.row()
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")
def draw_ui(self, context):
props = context.scene.BIMGeoreferenceProperties
@@ -103,6 +117,17 @@ class BIM_PT_gis(Panel):
row = self.layout.row(align=True)
row.label(text="XAxisOrdinate")
row.label(text=props.blender_x_axis_ordinate)
row.label(text="Derived Grid North")
row.label(
text=str(
round(
ifcopenshell.util.geolocation.xaxis2angle(
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
),
3,
)
)
)
elif IfcStore.get_file().schema == "IFC2X3":
row = self.layout.row()
row.label(text="Not Georeferenced")
@@ -152,6 +177,15 @@ class BIM_PT_gis(Panel):
)
)
if Data.true_north:
row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN")
row = self.layout.row(align=True)
row.label(text="Vector")
row.label(text=str(Data.true_north[0:2])[1:-1])
row = self.layout.row(align=True)
row.label(text="Derived Angle")
row.label(text=str(round(ifcopenshell.util.geolocation.yaxis2angle(*Data.true_north[0:2]), 3)))
class BIM_PT_gis_utilities(Panel):
@@ -2,12 +2,14 @@ class Data:
is_loaded = False
map_conversion = {}
projected_crs = {}
true_north = {}
@classmethod
def purge(cls):
cls.is_loaded = False
cls.map_conversion = {}
cls.projected_crs = {}
cls.true_north = None
@classmethod
def load(cls, file):
@@ -15,6 +17,7 @@ class Data:
return
cls.map_conversion = {}
cls.projected_crs = {}
cls.true_north = {}
if file.schema == "IFC2X3":
return
for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
@@ -28,4 +31,9 @@ class Data:
if cls.projected_crs["MapUnit"]:
cls.projected_crs["MapUnit"] = map_conversion.TargetCRS.MapUnit.get_info()
break
for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if not context.TrueNorth:
continue
cls.true_north = context.TrueNorth.DirectionRatios
break
cls.is_loaded = True
@@ -7,6 +7,7 @@ class Usecase:
self.settings = {
"map_conversion": {},
"projected_crs": {},
"true_north": [],
"map_unit": "",
}
for key, value in settings.items():
@@ -21,6 +22,7 @@ class Usecase:
setattr(projected_crs, name, value)
self.remove_existing_map_unit(projected_crs)
self.set_map_unit(projected_crs)
self.set_true_north()
def remove_existing_map_unit(self, projected_crs):
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
@@ -50,3 +52,20 @@ class Usecase:
self.settings["map_unit"],
self.file.createIfcMeasureWithUnit(value_component, si_unit),
)
def set_true_north(self):
if self.settings["true_north"] == []:
return
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if context.TrueNorth:
if len(self.file.get_inverse(context.TrueNorth)) != 1:
context.TrueNorth = self.file.create_entity("IfcDirection")
else:
context.TrueNorth = self.file.create_entity("IfcDirection")
direction = context.TrueNorth
if self.settings["true_north"] is None:
context.TrueNorth = self.settings["true_north"]
elif context.CoordinateSpaceDimension == 2:
direction.DirectionRatios = self.settings["true_north"][0:2]
else:
direction.DirectionRatios = self.settings["true_north"][0:2] + [0.0]