mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
Refactor Georeference Module
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
def refresh():
|
||||||
|
GeoreferenceData.is_loaded = False
|
||||||
|
|
||||||
|
class GeoreferenceData:
|
||||||
|
data = {}
|
||||||
|
is_loaded = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
cls.data = {
|
||||||
|
"map_conversion" : cls.map_conversion(),
|
||||||
|
"projected_crs" : cls.projected_crs(),
|
||||||
|
"true_north" : cls.true_north(),
|
||||||
|
}
|
||||||
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def map_conversion(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
map_conversion_v = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
map_conversion_v = context.HasCoordinateOperation[0].get_info()
|
||||||
|
map_conversion_v["SourceCRS"] = map_conversion_v["SourceCRS"].id()
|
||||||
|
map_conversion_v["TargetCRS"] = map_conversion_v["TargetCRS"].id()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
return map_conversion_v
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def projected_crs(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
projected_crs = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
projected_crs = context.HasCoordinateOperation[0].TargetCRS.get_info()
|
||||||
|
if projected_crs["MapUnit"]:
|
||||||
|
projected_crs["MapUnit"] = map_conversion.TargetCRS.MapUnit.get_info()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
return projected_crs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def true_north(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
true_north = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.TrueNorth:
|
||||||
|
continue
|
||||||
|
true_north = context.TrueNorth.DirectionRatios
|
||||||
|
break
|
||||||
|
|
||||||
|
return true_north
|
||||||
|
|
||||||
@@ -17,345 +17,146 @@
|
|||||||
# 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 ifcopenshell
|
|
||||||
import ifcopenshell.util.unit
|
|
||||||
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
|
|
||||||
|
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.georeference as core
|
||||||
|
|
||||||
class EnableEditingGeoreferencing(bpy.types.Operator):
|
class AddGeoreferencing(bpy.types.Operator, tool.Ifc.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):
|
||||||
|
bl_idname = "bim.enable_editing_georeferencing"
|
||||||
|
bl_label = "Enable Editing Georeferencing"
|
||||||
class ConvertLocalToGlobal(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.convert_local_to_global"
|
|
||||||
bl_label = "Convert Local To Global"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Enable editing georeferencing"
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.enable_editing_georeferencing(tool.Georeference)
|
||||||
|
|
||||||
@classmethod
|
class RemoveGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
def poll(cls, context):
|
bl_idname = "bim.remove_georeferencing"
|
||||||
file = IfcStore.get_file()
|
bl_label = "Remove Georeferencing"
|
||||||
props = context.scene.BIMGeoreferenceProperties
|
|
||||||
return file and props.coordinate_input.count(",") == 2
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
if not Data.is_loaded:
|
|
||||||
Data.load(IfcStore.get_file())
|
|
||||||
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"
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.remove_georeferencing(tool.Ifc)
|
||||||
|
|
||||||
|
class EditGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.edit_georeferencing"
|
||||||
|
bl_label = "Edit Georeferencing"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Edit the georeferencing"
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.edit_georeferencing(tool.Ifc, tool.Georeference)
|
||||||
|
|
||||||
|
class DisableEditingGeoreferencing(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.disable_editing_georeferencing"
|
||||||
|
bl_label = "Disable Editing Georeferencing"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Close editing panel"
|
||||||
|
|
||||||
@classmethod
|
def _execute(self, context):
|
||||||
def poll(cls, context):
|
core.disable_editing_georeferencing(tool.Georeference)
|
||||||
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 SetIfcGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
if not Data.is_loaded:
|
bl_idname = "bim.set_ifc_grid_north"
|
||||||
Data.load(IfcStore.get_file())
|
bl_label = "Set IFC Grid North"
|
||||||
props = context.scene.BIMGeoreferenceProperties
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
|
bl_description = "Set IFC grid north"
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.set_ifc_grid_north(tool.Georeference)
|
||||||
|
|
||||||
if Data.map_conversion:
|
class SetBlenderGridNorth(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
results = ifcopenshell.util.geolocation.enh2xyz(
|
bl_idname = "bim.set_blender_grid_north"
|
||||||
x,
|
bl_label = "Set Blender Grid North"
|
||||||
y,
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
z,
|
bl_description = "Set Blender grid north"
|
||||||
Data.map_conversion["Eastings"],
|
|
||||||
Data.map_conversion["Northings"],
|
def _execute(self, context):
|
||||||
Data.map_conversion["OrthogonalHeight"],
|
core.set_blender_grid_north(tool.Georeference)
|
||||||
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:
|
class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
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_idname = "bim.get_cursor_location"
|
||||||
bl_label = "Get Cursor Location"
|
bl_label = "Get Cursor Location"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Insert the current cursor coordinates"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
file = IfcStore.get_file()
|
return tool.Ifc.get()
|
||||||
return file and file.by_type("IfcUnitAssignment")
|
|
||||||
|
def _execute(self, context):
|
||||||
def execute(self, context):
|
core.get_cursor_location(tool.Georeference)
|
||||||
props = context.scene.BIMGeoreferenceProperties
|
|
||||||
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
class SetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
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_idname = "bim.set_cursor_location"
|
||||||
bl_label = "Set Cursor Location"
|
bl_label = "Set Cursor Location"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Move curson location to the specified coordinates"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
file = IfcStore.get_file()
|
file = tool.Ifc.get()
|
||||||
props = context.scene.BIMGeoreferenceProperties
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
return file and file.by_type("IfcUnitAssignment") and props.coordinate_output.count(",") == 2
|
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):
|
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
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
scale = ifcopenshell.util.unit.calculate_unit_scale(IfcStore.get_file())
|
return file and props.coordinate_input.count(",") == 2
|
||||||
context.scene.cursor.location = [float(co) * scale for co in props.coordinate_output.split(",")]
|
|
||||||
return {"FINISHED"}
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,9 @@
|
|||||||
|
|
||||||
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 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"
|
||||||
@@ -31,18 +30,15 @@ class BIM_PT_gis(Panel):
|
|||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
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)
|
||||||
self.draw_ui(context)
|
self.draw_ui(context)
|
||||||
@@ -79,15 +75,16 @@ class BIM_PT_gis(Panel):
|
|||||||
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": #TODO Is it correct to use IfcStore.get_file() or is it better tool.Ifc.get()?
|
||||||
row.operator("bim.add_georeferencing", icon="ADD", text="")
|
row.operator("bim.add_georeferencing", icon="ADD", text="")
|
||||||
|
|
||||||
if props.has_blender_offset:
|
if props.has_blender_offset:
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
|
||||||
@@ -120,13 +117,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 +134,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 +151,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):
|
||||||
@@ -181,6 +178,9 @@ class BIM_PT_gis_utilities(Panel):
|
|||||||
bl_category = "BlenderBIM"
|
bl_category = "BlenderBIM"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
if not GeoreferenceData.is_loaded:
|
||||||
|
GeoreferenceData.load()
|
||||||
|
|
||||||
props = context.scene.BIMGeoreferenceProperties
|
props = context.scene.BIMGeoreferenceProperties
|
||||||
|
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
@@ -193,3 +193,4 @@ class BIM_PT_gis_utilities(Panel):
|
|||||||
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")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
def add_georeferencing(ifc):
|
||||||
|
ifc.run("georeference.add_georeferencing")
|
||||||
|
|
||||||
|
def enable_editing_georeferencing(georeference):
|
||||||
|
georeference.clear_projected_crs()
|
||||||
|
georeference.import_projected_crs()
|
||||||
|
georeference.clear_map_conversion()
|
||||||
|
georeference.import_map_conversion()
|
||||||
|
georeference.set_has_true_north_prop()
|
||||||
|
georeference.set_true_north_props()
|
||||||
|
georeference.enable_editing()
|
||||||
|
|
||||||
|
def remove_georeferencing(ifc):
|
||||||
|
ifc.run("georeference.remove_georeferencing")
|
||||||
|
|
||||||
|
def disable_editing_georeferencing(georeference):
|
||||||
|
georeference.disable_editing()
|
||||||
|
|
||||||
|
def edit_georeferencing(ifc, georeference):
|
||||||
|
projected_crs = georeference.get_projected_crs_attributes()
|
||||||
|
map_conversion = georeference.get_map_conversion_attributes()
|
||||||
|
true_north = georeference.get_true_north_attributes()
|
||||||
|
georeference.edit_georeferencing(projected_crs, map_conversion, true_north)
|
||||||
|
georeference.disable_editing()
|
||||||
|
|
||||||
|
def set_ifc_grid_north(georeference):
|
||||||
|
x_angle = georeference.get_x_angle_from_sun_position()
|
||||||
|
georeference.set_xaxis_vector_from_angle(x_angle)
|
||||||
|
|
||||||
|
def set_blender_grid_north(georeference):
|
||||||
|
angle = georeference.get_angle_from_xaxis()
|
||||||
|
georeference.set_sun_pos_north_offset(angle)
|
||||||
|
|
||||||
|
def get_cursor_location(georeference):
|
||||||
|
georeference.get_cursor_location()
|
||||||
|
|
||||||
|
def set_cursor_location(georeference):
|
||||||
|
scale = georeference.get_scale()
|
||||||
|
coordinates = georeference.get_coordinates_from_coordinate_output_prop()
|
||||||
|
georeference.set_cursor_location(coordinates, scale)
|
||||||
|
|
||||||
|
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):
|
||||||
|
map_conversion = georeference.get_map_conversion()
|
||||||
|
coordinates = georeference.get_easting_northing_height_from_xyz(map_conversion)
|
||||||
|
georeference.set_coordinate_output_prop(coordinates)
|
||||||
|
georeference.set_cursor_location(coordinates, scale = 1)
|
||||||
|
|
||||||
|
def convert_global_to_local(georeference):
|
||||||
|
map_conversion = georeference.get_map_conversion()
|
||||||
|
coordinates = georeference.get_xyz_from_easting_northig_height(map_conversion)
|
||||||
|
georeference.set_coordinate_output_prop(coordinates)
|
||||||
|
scale = georeference.get_scale()
|
||||||
|
georeference.set_cursor_location(coordinates, scale)
|
||||||
|
|
||||||
@@ -247,6 +247,41 @@ 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 import_projected_crs(cls): pass
|
||||||
|
def import_map_conversion(cls): pass
|
||||||
|
def set_has_true_north_prop(cls): pass
|
||||||
|
def set_true_north_props(cls): pass
|
||||||
|
def enable_editing(cls): pass
|
||||||
|
def disable_editing(cls): pass
|
||||||
|
def get_projected_crs_attributes(cls): pass
|
||||||
|
def get_map_conversion_attributes(cls): pass
|
||||||
|
def get_true_north_attributes(cls): pass
|
||||||
|
def edit_georeferencing(cls, projected_crs, map_conversion, true_north): pass
|
||||||
|
def get_file(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, coordinates, scale): pass
|
||||||
|
def get_scale(cls): pass
|
||||||
|
def get_coordinates_from_coordinate_output_prop(cls): pass
|
||||||
|
def set_ifc_true_north(cls): pass
|
||||||
|
def set_blender_true_north(cls): pass
|
||||||
|
def get_map_conversion(cls): pass
|
||||||
|
def get_easting_northing_height_from_xyz(cls, map_conversion): pass
|
||||||
|
def get_xyz_from_easting_northig_height(cls, map_conversion): pass
|
||||||
|
def set_coordinate_output_prop(cls, coordinates): pass
|
||||||
|
def get_x_angle_from_sun_position(cls): pass
|
||||||
|
def set_xaxis_vector_from_angle(cls, x_angle): pass
|
||||||
|
def get_angle_from_xaxis(cls): pass
|
||||||
|
def set_sun_pos_north_offset(cls, angle): pass
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Ifc:
|
class Ifc:
|
||||||
|
|||||||
@@ -28,6 +28,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
|
||||||
|
|||||||
@@ -0,0 +1,361 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# This file is part of BlenderBIM Add-on.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
from math import radians, degrees, atan, tan, cos, sin
|
||||||
|
|
||||||
|
class Georeference(blenderbim.core.tool.Georeference):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_projected_crs(cls):
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.projected_crs.clear()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_map_conversion(cls):
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.map_conversion.clear()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_projected_crs(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
projected_crs = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
projected_crs = context.HasCoordinateOperation[0].TargetCRS.get_info()
|
||||||
|
if projected_crs["MapUnit"]:
|
||||||
|
projected_crs["MapUnit"] = map_conversion.TargetCRS.MapUnit.get_info()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
blenderbim.bim.helper.import_attributes(
|
||||||
|
"IfcProjectedCRS",
|
||||||
|
props.projected_crs,
|
||||||
|
projected_crs,
|
||||||
|
cls.import_projected_crs_attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_map_conversion(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
map_conversion_v = {}
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
map_conversion_v = context.HasCoordinateOperation[0].get_info()
|
||||||
|
map_conversion_v["SourceCRS"] = map_conversion_v["SourceCRS"].id()
|
||||||
|
map_conversion_v["TargetCRS"] = map_conversion_v["TargetCRS"].id()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
blenderbim.bim.helper.import_attributes(
|
||||||
|
"IfcMapConversion",
|
||||||
|
props.map_conversion,
|
||||||
|
map_conversion_v,
|
||||||
|
cls.import_map_conversion_attributes,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_has_true_north_prop(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
true_north = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.TrueNorth:
|
||||||
|
continue
|
||||||
|
true_north = context.TrueNorth.DirectionRatios
|
||||||
|
break
|
||||||
|
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.has_true_north = bool(true_north)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_true_north_props(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
true_north = {}
|
||||||
|
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.TrueNorth:
|
||||||
|
continue
|
||||||
|
true_north = context.TrueNorth.DirectionRatios
|
||||||
|
break
|
||||||
|
|
||||||
|
if true_north:
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(true_north[0])
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(true_north[1])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_projected_crs_attributes(cls):
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
projected_crs = blenderbim.bim.helper.export_attributes(props.projected_crs, cls.export_crs_attributes)
|
||||||
|
return projected_crs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_map_conversion_attributes(cls):
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
map_conversion = blenderbim.bim.helper.export_attributes(props.map_conversion, cls.export_map_attributes)
|
||||||
|
return map_conversion
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_true_north_attributes(cls):
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
true_north = None
|
||||||
|
if props.has_true_north:
|
||||||
|
try:
|
||||||
|
true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)]
|
||||||
|
except ValueError:
|
||||||
|
print("ERROR, True North Abscissa and Ordinate expect a number")
|
||||||
|
#self.report({"ERROR"}, "True North Abscissa and Ordinate expect a number")
|
||||||
|
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 edit_georeferencing(cls, projected_crs, map_conversion, true_north):
|
||||||
|
tool.Ifc.run(
|
||||||
|
"georeference.edit_georeferencing",
|
||||||
|
**{
|
||||||
|
"projected_crs": projected_crs,
|
||||||
|
"map_conversion": map_conversion,
|
||||||
|
"true_north": true_north,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_file(cls):
|
||||||
|
return tool.Ifc.get()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_projected_crs_attributes(cls, name, prop, data):
|
||||||
|
if name == "MapUnit":
|
||||||
|
new = bpy.context.scene.BIMGeoreferenceProperties.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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def import_map_conversion_attributes(cls, 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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
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))
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_map_attributes(cls, 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
|
||||||
|
|
||||||
|
@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, coordinates, scale):
|
||||||
|
bpy.context.scene.cursor.location = coordinates * scale
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_scale(cls):
|
||||||
|
return ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_coordinates_from_coordinate_output_prop(cls):
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
coordinates = [float(co) for co in props.coordinate_output.split(",")]
|
||||||
|
return coordinates
|
||||||
|
|
||||||
|
@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 get_map_conversion(cls):
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
map_conversion = {}
|
||||||
|
if ifc.schema == "IFC2X3":
|
||||||
|
return
|
||||||
|
|
||||||
|
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
|
if not context.HasCoordinateOperation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
map_conversion = context.HasCoordinateOperation[0].get_info()
|
||||||
|
map_conversion["SourceCRS"] = map_conversion["SourceCRS"].id()
|
||||||
|
map_conversion["TargetCRS"] = map_conversion["TargetCRS"].id()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
return map_conversion
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_easting_northing_height_from_xyz(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: #TODO return error when there is the map conversiona but no Xaxis, scale, etc..
|
||||||
|
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)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_xyz_from_easting_northig_height(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,
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_coordinate_output_prop(cls, coordinates):
|
||||||
|
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||||
|
props.coordinate_output = ",".join([str(r) for r in coordinates])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_x_angle_from_sun_position(cls):
|
||||||
|
return -bpy.context.scene.sun_pos_properties.north_offset
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_xaxis_vector_from_angle(cls, x_angle):
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value = str(cos(x_angle))
|
||||||
|
bpy.context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value = str(sin(x_angle))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_angle_from_xaxis(cls):
|
||||||
|
return ifcopenshell.util.geolocation.xaxis2angle(
|
||||||
|
float(bpy.context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value),
|
||||||
|
float(bpy.context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_sun_pos_north_offset(cls, angle):
|
||||||
|
bpy.context.scene.sun_pos_properties.north_offset = -radians(angle)
|
||||||
|
|
||||||
Reference in New Issue
Block a user