mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
WIP implement georeference module for correctly geolocated files. See #1222.
This commit is contained in:
@@ -20,6 +20,7 @@ if bpy is not None:
|
||||
"bimtester": None,
|
||||
"debug": None,
|
||||
"geometry": None,
|
||||
"georeference": None,
|
||||
"material": None,
|
||||
"model": None,
|
||||
"owner": None,
|
||||
@@ -146,8 +147,6 @@ if bpy is not None:
|
||||
operator.AddVariable,
|
||||
operator.RemoveVariable,
|
||||
operator.PropagateTextData,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.ConvertGlobalToLocal,
|
||||
operator.SelectIfcPatchInput,
|
||||
operator.SelectIfcPatchOutput,
|
||||
operator.ExecuteIfcPatch,
|
||||
@@ -170,8 +169,6 @@ if bpy is not None:
|
||||
operator.BuildSchedule,
|
||||
operator.AddScheduleToSheet,
|
||||
operator.SetViewportShadowFromSun,
|
||||
operator.SetNorthOffset,
|
||||
operator.GetNorthOffset,
|
||||
operator.AddPresentationLayer,
|
||||
operator.AssignPresentationLayer,
|
||||
operator.UnassignPresentationLayer,
|
||||
@@ -216,8 +213,6 @@ if bpy is not None:
|
||||
prop.BIMProperties,
|
||||
prop.DocProperties,
|
||||
prop.BIMLibrary,
|
||||
prop.MapConversion,
|
||||
prop.TargetCRS,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
@@ -242,7 +237,6 @@ if bpy is not None:
|
||||
ui.BIM_PT_search,
|
||||
ui.BIM_PT_ifcclash,
|
||||
ui.BIM_PT_library,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_presentation_layers,
|
||||
ui.BIM_PT_diff,
|
||||
ui.BIM_PT_patch,
|
||||
@@ -294,8 +288,6 @@ if bpy is not None:
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||
bpy.types.Scene.MapConversion = bpy.props.PointerProperty(type=prop.MapConversion)
|
||||
bpy.types.Scene.TargetCRS = bpy.props.PointerProperty(type=prop.TargetCRS)
|
||||
bpy.types.Object.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
bpy.types.Material.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties)
|
||||
bpy.types.Collection.BIMObjectProperties = bpy.props.PointerProperty(type=prop.BIMObjectProperties) # Check if we need this
|
||||
@@ -319,8 +311,6 @@ if bpy is not None:
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
del bpy.types.Scene.BIMProperties
|
||||
del bpy.types.Scene.DocProperties
|
||||
del bpy.types.Scene.MapConversion
|
||||
del bpy.types.Scene.TargetCRS
|
||||
del bpy.types.Object.BIMObjectProperties
|
||||
del bpy.types.Material.BIMObjectProperties
|
||||
del bpy.types.Collection.BIMObjectProperties # Check if we need this
|
||||
|
||||
@@ -371,8 +371,6 @@ class IfcImporter:
|
||||
if self.ifc_import_settings.should_import_native:
|
||||
self.parse_native_elements()
|
||||
self.profile_code("Parsing native elements")
|
||||
self.create_georeferencing()
|
||||
self.profile_code("Georeferencing ifc")
|
||||
self.create_groups()
|
||||
self.profile_code("Creating groups")
|
||||
self.create_grids()
|
||||
@@ -645,46 +643,6 @@ class IfcImporter:
|
||||
if element.ObjectPlacement.RelativePlacement.RefDirection:
|
||||
element.ObjectPlacement.RelativePlacement.RefDirection.DirectionRatios = (1.0, 0.0, 0.0)
|
||||
|
||||
def create_georeferencing(self):
|
||||
try:
|
||||
map_conversion = self.file.by_type("IfcMapConversion")
|
||||
projected_crs = self.file.by_type("IfcProjectedCRS")
|
||||
if not map_conversion or not projected_crs:
|
||||
return
|
||||
except:
|
||||
return # For example, in IFC2X3
|
||||
map_conversion = map_conversion[0]
|
||||
projected_crs = projected_crs[0]
|
||||
scene = bpy.context.scene
|
||||
scene.BIMProperties.has_georeferencing = True
|
||||
map_conversion_map = {
|
||||
"Eastings": "eastings",
|
||||
"Northings": "northings",
|
||||
"OrthogonalHeight": "orthogonal_height",
|
||||
"XAxisAbscissa": "x_axis_abscissa",
|
||||
"XAxisOrdinate": "x_axis_ordinate",
|
||||
"Scale": "scale",
|
||||
}
|
||||
target_crs_map = {
|
||||
"Name": "name",
|
||||
"Description": "description",
|
||||
"GeodeticDatum": "geodetic_datum",
|
||||
"VerticalDatum": "vertical_datum",
|
||||
"MapProjection": "map_projection",
|
||||
"MapZone": "map_zone",
|
||||
"MapUnit": "map_unit",
|
||||
}
|
||||
for keyA, keyB in map_conversion_map.items():
|
||||
value = getattr(map_conversion, keyA)
|
||||
if value is not None:
|
||||
setattr(scene.MapConversion, keyB, str(value))
|
||||
for keyA, keyB in target_crs_map.items():
|
||||
value = getattr(projected_crs, keyA)
|
||||
if value is not None:
|
||||
if keyA == "MapUnit":
|
||||
value = self.get_unit_name(value)
|
||||
setattr(scene.TargetCRS, keyB, str(value))
|
||||
|
||||
def get_unit_name(self, named_unit):
|
||||
name = ""
|
||||
if hasattr(named_unit, "Prefix") and named_unit.Prefix:
|
||||
@@ -710,7 +668,6 @@ class IfcImporter:
|
||||
else:
|
||||
obj = bpy.data.objects.new(f"{element.is_a()}/{element.Name}", None)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
self.add_element_attributes(element, obj.BIMObjectProperties)
|
||||
group_collection.objects.link(obj)
|
||||
self.groups[element.GlobalId] = {"ifc": element, "blender": obj}
|
||||
|
||||
@@ -748,7 +705,6 @@ class IfcImporter:
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
||||
obj.matrix_world = matrix_world
|
||||
self.add_element_attributes(axis, obj.BIMObjectProperties)
|
||||
grid.objects.link(obj)
|
||||
|
||||
def create_type_products(self):
|
||||
@@ -1756,21 +1712,6 @@ class IfcImporter:
|
||||
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
|
||||
def add_element_attributes(self, element, props):
|
||||
attributes = element.get_info()
|
||||
for key, value in attributes.items():
|
||||
if (
|
||||
value is None
|
||||
or isinstance(value, (tuple, ifcopenshell.entity_instance))
|
||||
or key == "id"
|
||||
or key == "type"
|
||||
):
|
||||
continue
|
||||
attribute = props.attributes.add()
|
||||
attribute.name = key
|
||||
attribute.data_type = "string"
|
||||
attribute.string_value = str(self.cast_edge_case_attribute(element.is_a(), key, value))
|
||||
|
||||
def cast_edge_case_attribute(self, ifc_class, key, value):
|
||||
if key == "RefLatitude" or key == "RefLongitude":
|
||||
return ifcopenshell.util.geolocation.dms2dd(*value)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.EnableEditingGeoreferencing,
|
||||
operator.DisableEditingGeoreferencing,
|
||||
operator.EditGeoreferencing,
|
||||
operator.SetNorthOffset,
|
||||
operator.GetNorthOffset,
|
||||
operator.RemoveGeoreferencing,
|
||||
operator.AddGeoreferencing,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.ConvertGlobalToLocal,
|
||||
prop.BIMGeoreferenceProperties,
|
||||
ui.BIM_PT_gis,
|
||||
ui.BIM_PT_gis_utilities,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMGeoreferenceProperties = bpy.props.PointerProperty(type=prop.BIMGeoreferenceProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMGeoreferenceProperties
|
||||
@@ -0,0 +1,20 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
|
||||
def execute(self):
|
||||
source_crs = None
|
||||
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if context.ContextType == "Model":
|
||||
source_crs = context
|
||||
break
|
||||
if not source_crs:
|
||||
return
|
||||
projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""})
|
||||
self.file.create_entity("IfcMapConversion", **{
|
||||
"SourceCRS": source_crs,
|
||||
"TargetCRS": projected_crs,
|
||||
"Eastings": 0,
|
||||
"Northings": 0,
|
||||
"OrthogonalHeight": 0,
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class Data:
|
||||
is_loaded = False
|
||||
map_conversion = {}
|
||||
projected_crs = {}
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
file = IfcStore.get_file()
|
||||
if not file:
|
||||
return
|
||||
cls.map_conversion = {}
|
||||
cls.projected_crs = {}
|
||||
if file.schema == "IFC2X3":
|
||||
return
|
||||
for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
if not context.HasCoordinateOperation:
|
||||
continue
|
||||
map_conversion = context.HasCoordinateOperation[0]
|
||||
cls.map_conversion = map_conversion.get_info()
|
||||
cls.map_conversion["SourceCRS"] = cls.map_conversion["SourceCRS"].id()
|
||||
cls.map_conversion["TargetCRS"] = cls.map_conversion["TargetCRS"].id()
|
||||
cls.projected_crs = map_conversion.TargetCRS.get_info()
|
||||
if cls.projected_crs["MapUnit"]:
|
||||
cls.projected_crs["MapUnit"] = map_conversion.TargetCRS.MapUnit.get_info()
|
||||
break
|
||||
cls.is_loaded = True
|
||||
@@ -0,0 +1,52 @@
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"map_conversion": {},
|
||||
"projected_crs": {},
|
||||
"map_unit": "",
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
map_conversion = self.file.by_type("IfcMapConversion")[0]
|
||||
projected_crs = self.file.by_type("IfcProjectedCRS")[0]
|
||||
for name, value in self.settings["map_conversion"].items():
|
||||
setattr(map_conversion, name, value)
|
||||
for name, value in self.settings["projected_crs"].items():
|
||||
setattr(projected_crs, name, value)
|
||||
self.remove_existing_map_unit(projected_crs)
|
||||
self.set_map_unit(projected_crs)
|
||||
|
||||
def remove_existing_map_unit(self, projected_crs):
|
||||
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
|
||||
# TODO: go deeper for conversion units
|
||||
self.file.remove(projected_crs.MapUnit)
|
||||
|
||||
def set_map_unit(self, projected_crs):
|
||||
if not self.settings["map_unit"]:
|
||||
return
|
||||
|
||||
if "METRE" in self.settings["map_unit"]:
|
||||
projected_crs.MapUnit = self.file.createIfcSIUnit(
|
||||
None,
|
||||
"LENGTHUNIT",
|
||||
ifcopenshell.util.unit.get_prefix(self.settings["map_unit"]),
|
||||
ifcopenshell.util.unit.get_unit_name(self.settings["map_unit"]),
|
||||
)
|
||||
return
|
||||
|
||||
value_component = self.file.create_entity(
|
||||
"IfcReal", **{"wrappedValue": ifcopenshell.util.unit.si_conversions[self.settings["map_unit"]]}
|
||||
)
|
||||
si_unit = self.file.createIfcSIUnit(None, "LENGTHUNIT", None, "METRE")
|
||||
projected_crs.MapUnit = self.file.createIfcConversionBasedUnit(
|
||||
self.file.createIfcDimensionalExponents(1, 0, 0, 0, 0, 0, 0),
|
||||
"LENGTHUNIT",
|
||||
self.settings["map_unit"],
|
||||
self.file.createIfcMeasureWithUnit(value_component, si_unit),
|
||||
)
|
||||
@@ -0,0 +1,242 @@
|
||||
import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.bim.module.georeference.add_georeferencing as add_georeferencing
|
||||
import blenderbim.bim.module.georeference.edit_georeferencing as edit_georeferencing
|
||||
import blenderbim.bim.module.georeference.remove_georeferencing as remove_georeferencing
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.georeference.data import Data
|
||||
from math import radians, degrees, atan, tan, cos, sin
|
||||
|
||||
|
||||
class EnableEditingGeoreferencing(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_georeferencing"
|
||||
bl_label = "Enable Editing Georeferencing"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
while len(props.map_conversion) > 0:
|
||||
props.map_conversion.remove(0)
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
new = props.map_conversion.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = Data.map_conversion[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if "<string>" in data_type:
|
||||
new.string_value = "" if new.is_null else Data.map_conversion[attribute.name()]
|
||||
new.data_type = "string"
|
||||
elif "<real>" in data_type:
|
||||
new.float_value = 0.0 if new.is_null else Data.map_conversion[attribute.name()]
|
||||
new.data_type = "float"
|
||||
elif "<integer>" in data_type:
|
||||
new.int_value = 0 if new.is_null else Data.map_conversion[attribute.name()]
|
||||
new.data_type = "integer"
|
||||
elif "<boolean>" in data_type or "<logical>" in data_type:
|
||||
new.bool_value = False if new.is_null else Data.map_conversion[attribute.name()]
|
||||
new.data_type = "boolean"
|
||||
|
||||
while len(props.projected_crs) > 0:
|
||||
props.projected_crs.remove(0)
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
new = props.projected_crs.add()
|
||||
new.name = attribute.name()
|
||||
new.is_null = Data.projected_crs[attribute.name()] is None
|
||||
new.is_optional = attribute.optional()
|
||||
if "<string>" in data_type:
|
||||
new.string_value = "" if new.is_null else Data.projected_crs[attribute.name()]
|
||||
new.data_type = "string"
|
||||
elif "<real>" in data_type:
|
||||
new.float_value = 0.0 if new.is_null else Data.projected_crs[attribute.name()]
|
||||
new.data_type = "float"
|
||||
elif "<integer>" in data_type:
|
||||
new.int_value = 0 if new.is_null else Data.projected_crs[attribute.name()]
|
||||
new.data_type = "integer"
|
||||
elif "<boolean>" in data_type or "<logical>" in data_type:
|
||||
new.bool_value = False if new.is_null else Data.projected_crs[attribute.name()]
|
||||
new.data_type = "boolean"
|
||||
|
||||
props.is_map_unit_null = Data.projected_crs["MapUnit"] is None
|
||||
if not props.is_map_unit_null:
|
||||
props.map_unit_type = Data.projected_crs["MapUnit"]["type"]
|
||||
if props.map_unit_type == "IfcSIUnit":
|
||||
prefix = ifcopenshell.util.unit.get_prefix(Data.projected_crs["MapUnit"]["Prefix"]) or ""
|
||||
name = ifcopenshell.util.unit.get_unit_name(Data.projected_crs["MapUnit"]["Name"])
|
||||
props.map_unit_si = prefix + name
|
||||
elif props.map_unit_type == "IfcConversionBasedUnit":
|
||||
props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"]
|
||||
|
||||
props.is_editing = True
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingGeoreferencing(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_georeferencing"
|
||||
bl_label = "Disable Editing Georeferencing"
|
||||
|
||||
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"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
map_conversion = {}
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
blender_attribute = props.map_conversion.get(attribute.name())
|
||||
if blender_attribute.is_null:
|
||||
map_conversion[attribute.name()] = None
|
||||
elif blender_attribute.data_type == "string":
|
||||
map_conversion[attribute.name()] = blender_attribute.string_value
|
||||
elif blender_attribute.data_type == "float":
|
||||
map_conversion[attribute.name()] = blender_attribute.float_value
|
||||
elif blender_attribute.data_type == "integer":
|
||||
map_conversion[attribute.name()] = blender_attribute.int_value
|
||||
elif blender_attribute.data_type == "boolean":
|
||||
map_conversion[attribute.name()] = blender_attribute.bool_value
|
||||
|
||||
projected_crs = {}
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
|
||||
data_type = str(attribute.type_of_attribute)
|
||||
if "<entity" in data_type:
|
||||
continue
|
||||
blender_attribute = props.projected_crs.get(attribute.name())
|
||||
if blender_attribute.is_null:
|
||||
projected_crs[attribute.name()] = None
|
||||
elif blender_attribute.data_type == "string":
|
||||
projected_crs[attribute.name()] = blender_attribute.string_value
|
||||
elif blender_attribute.data_type == "float":
|
||||
projected_crs[attribute.name()] = blender_attribute.float_value
|
||||
elif blender_attribute.data_type == "integer":
|
||||
projected_crs[attribute.name()] = blender_attribute.int_value
|
||||
elif blender_attribute.data_type == "boolean":
|
||||
projected_crs[attribute.name()] = blender_attribute.bool_value
|
||||
|
||||
map_unit = ""
|
||||
if not props.is_map_unit_null:
|
||||
map_unit = props.map_unit_si if props.map_unit_type == "IfcSIUnit" else props.map_unit_imperial
|
||||
|
||||
edit_georeferencing.Usecase(self.file, {
|
||||
"map_conversion": map_conversion,
|
||||
"projected_crs": projected_crs,
|
||||
"map_unit": map_unit
|
||||
}).execute()
|
||||
Data.load()
|
||||
bpy.ops.bim.disable_editing_georeferencing()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetNorthOffset(bpy.types.Operator):
|
||||
bl_idname = "bim.set_north_offset"
|
||||
bl_label = "Set North Offset"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.sun_pos_properties.north_offset = -radians(
|
||||
ifcopenshell.util.geolocation.xy2angle(
|
||||
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value,
|
||||
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value
|
||||
)
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetNorthOffset(bpy.types.Operator):
|
||||
bl_idname = "bim.get_north_offset"
|
||||
bl_label = "Get North Offset"
|
||||
|
||||
def execute(self, context):
|
||||
x_angle = -context.scene.sun_pos_properties.north_offset
|
||||
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value = cos(x_angle)
|
||||
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value = sin(x_angle)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveGeoreferencing(bpy.types.Operator):
|
||||
bl_idname = "bim.remove_georeferencing"
|
||||
bl_label = "Remove Georeferencing"
|
||||
|
||||
def execute(self, context):
|
||||
remove_georeferencing.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddGeoreferencing(bpy.types.Operator):
|
||||
bl_idname = "bim.add_georeferencing"
|
||||
bl_label = "Add Georeferencing"
|
||||
|
||||
def execute(self, context):
|
||||
add_georeferencing.Usecase(IfcStore.get_file()).execute()
|
||||
Data.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ConvertLocalToGlobal(bpy.types.Operator):
|
||||
bl_idname = "bim.convert_local_to_global"
|
||||
bl_label = "Convert Local To Global"
|
||||
|
||||
def execute(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
|
||||
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("XAxisAbscissa", 0.0),
|
||||
Data.map_conversion.get("Scale", 1.0),
|
||||
)
|
||||
props.coordinate_output = ",".join([str(r) for r in results])
|
||||
bpy.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"
|
||||
|
||||
def execute(self, context):
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
|
||||
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("XAxisAbscissa", 0.0),
|
||||
Data.map_conversion.get("Scale", 1.0),
|
||||
)
|
||||
props.coordinate_output = ",".join([str(r) for r in results])
|
||||
bpy.context.scene.cursor.location = results
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
class BIMGeoreferenceProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
map_conversion: CollectionProperty(name="Map Conversion", type=Attribute)
|
||||
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
||||
map_unit_type: EnumProperty(
|
||||
items=[(n, n, "") for n in ["IfcSIUnit", "IfcConversionBasedUnit"]],
|
||||
name="Map Unit Type",
|
||||
default="IfcSIUnit",
|
||||
)
|
||||
map_unit_si: EnumProperty(
|
||||
items=[(n, n.lower().capitalize(), "") for n in ["MILLIMETRE", "CENTIMETRE", "METRE", "KILOMETRE"]],
|
||||
name="Map Unit SI",
|
||||
default="METRE",
|
||||
)
|
||||
map_unit_imperial: EnumProperty(
|
||||
items=[(n, n.lower().capitalize(), "") for n in ["inch", "foot", "yard", "mile"]],
|
||||
name="Map Unit SI",
|
||||
default="foot",
|
||||
)
|
||||
is_map_unit_null: BoolProperty(name="Is Map Unit Null")
|
||||
coordinate_input: StringProperty(name="Coordinate Input")
|
||||
coordinate_output: StringProperty(name="Coordinate Output")
|
||||
@@ -0,0 +1,12 @@
|
||||
class Usecase:
|
||||
def __init__(self, file, settings=None):
|
||||
self.file = file
|
||||
|
||||
def execute(self):
|
||||
map_conversion = self.file.by_type("IfcMapConversion")[0]
|
||||
projected_crs = self.file.by_type("IfcProjectedCRS")[0]
|
||||
if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
|
||||
# TODO: go deeper for conversion units
|
||||
self.file.remove(projected_crs.MapUnit)
|
||||
self.file.remove(projected_crs)
|
||||
self.file.remove(map_conversion)
|
||||
@@ -0,0 +1,125 @@
|
||||
from bpy.types import Panel
|
||||
from blenderbim.bim.module.georeference.data import Data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
class BIM_PT_gis(Panel):
|
||||
bl_label = "IFC Georeferencing"
|
||||
bl_idname = "BIM_PT_gis"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
if not Data.is_loaded:
|
||||
Data.load()
|
||||
|
||||
if props.is_editing:
|
||||
return self.draw_editable_ui(context)
|
||||
self.draw_ui()
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Map Conversion", icon="GRID")
|
||||
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
|
||||
|
||||
for attribute in props.map_conversion:
|
||||
if attribute.name == "XAxisAbscissa" 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 = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "float":
|
||||
row.prop(attribute, "float_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
|
||||
for attribute in props.projected_crs:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
row.prop(attribute, "string_value", text=attribute.name)
|
||||
elif attribute.data_type == "integer":
|
||||
row.prop(attribute, "int_value", text=attribute.name)
|
||||
elif attribute.data_type == "float":
|
||||
row.prop(attribute, "float_value", text=attribute.name)
|
||||
elif attribute.data_type == "boolean":
|
||||
row.prop(attribute, "bool_value", text=attribute.name)
|
||||
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "map_unit_type", text="MapUnit")
|
||||
if props.map_unit_type == "IfcSIUnit":
|
||||
row.prop(props, "map_unit_si", text="")
|
||||
elif props.map_unit_type == "IfcConversionBasedUnit":
|
||||
row.prop(props, "map_unit_imperial", text="")
|
||||
row.prop(props, "is_map_unit_null", icon="RADIOBUT_OFF" if props.is_map_unit_null else "RADIOBUT_ON", text="")
|
||||
|
||||
def draw_ui(self):
|
||||
if not Data.map_conversion:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Not Georeferenced")
|
||||
row.operator("bim.add_georeferencing", icon="ADD", text="")
|
||||
|
||||
if Data.map_conversion:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Map Conversion", icon="GRID")
|
||||
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
|
||||
row.operator("bim.remove_georeferencing", icon="X", text="")
|
||||
|
||||
for key, value in Data.map_conversion.items():
|
||||
if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or not value:
|
||||
continue
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=key)
|
||||
row.label(text=str(value))
|
||||
|
||||
if Data.projected_crs:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
|
||||
for key, value in Data.projected_crs.items():
|
||||
if key == "id" or key == "type" or not value:
|
||||
continue
|
||||
if key == "MapUnit":
|
||||
unit_value = value.get("Prefix", "") or ""
|
||||
unit_value += value["Name"]
|
||||
value = unit_value
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=key)
|
||||
row.label(text=str(value))
|
||||
|
||||
|
||||
class BIM_PT_gis_utilities(Panel):
|
||||
bl_idname = "BIM_PT_gis_utilities"
|
||||
bl_label = "Georeferencing Utilities"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "coordinate_input", text="Input")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "coordinate_output", text="Output")
|
||||
|
||||
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")
|
||||
@@ -2519,56 +2519,6 @@ class PropagateTextData(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ConvertLocalToGlobal(bpy.types.Operator):
|
||||
bl_idname = "bim.convert_local_to_global"
|
||||
bl_label = "Convert Local To Global"
|
||||
|
||||
def execute(self, context):
|
||||
if bpy.context.scene.MapConversion.scale:
|
||||
scale = float(bpy.context.scene.MapConversion.scale)
|
||||
else:
|
||||
scale = 1.0
|
||||
results = ifcopenshell.util.geolocation.xyz2enh(
|
||||
bpy.context.scene.cursor.location[0],
|
||||
bpy.context.scene.cursor.location[1],
|
||||
bpy.context.scene.cursor.location[2],
|
||||
float(bpy.context.scene.MapConversion.eastings),
|
||||
float(bpy.context.scene.MapConversion.northings),
|
||||
float(bpy.context.scene.MapConversion.orthogonal_height),
|
||||
float(bpy.context.scene.MapConversion.x_axis_abscissa),
|
||||
float(bpy.context.scene.MapConversion.x_axis_ordinate),
|
||||
scale,
|
||||
)
|
||||
print("Coordinates:", results)
|
||||
bpy.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"
|
||||
|
||||
def execute(self, context):
|
||||
if bpy.context.scene.MapConversion.scale:
|
||||
scale = float(bpy.context.scene.MapConversion.scale)
|
||||
else:
|
||||
scale = 1.0
|
||||
results = ifcopenshell.util.geolocation.enh2xyz(
|
||||
float(bpy.context.scene.BIMProperties.eastings),
|
||||
float(bpy.context.scene.BIMProperties.northings),
|
||||
float(bpy.context.scene.BIMProperties.orthogonal_height),
|
||||
float(bpy.context.scene.MapConversion.eastings),
|
||||
float(bpy.context.scene.MapConversion.northings),
|
||||
float(bpy.context.scene.MapConversion.orthogonal_height),
|
||||
float(bpy.context.scene.MapConversion.x_axis_abscissa),
|
||||
float(bpy.context.scene.MapConversion.x_axis_ordinate),
|
||||
scale,
|
||||
)
|
||||
print("Coordinates:", results)
|
||||
bpy.context.scene.cursor.location = results
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectIfcPatchInput(bpy.types.Operator):
|
||||
bl_idname = "bim.select_ifc_patch_input"
|
||||
bl_label = "Select IFC Patch Input"
|
||||
@@ -3110,31 +3060,6 @@ class SetViewportShadowFromSun(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SetNorthOffset(bpy.types.Operator):
|
||||
bl_idname = "bim.set_north_offset"
|
||||
bl_label = "Set North Offset"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.sun_pos_properties.north_offset = -radians(
|
||||
ifcopenshell.util.geolocation.xy2angle(
|
||||
float(bpy.context.scene.MapConversion.x_axis_abscissa),
|
||||
float(bpy.context.scene.MapConversion.x_axis_ordinate),
|
||||
)
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetNorthOffset(bpy.types.Operator):
|
||||
bl_idname = "bim.get_north_offset"
|
||||
bl_label = "Get North Offset"
|
||||
|
||||
def execute(self, context):
|
||||
x_angle = -context.scene.sun_pos_properties.north_offset
|
||||
bpy.context.scene.MapConversion.x_axis_abscissa = str(cos(x_angle))
|
||||
bpy.context.scene.MapConversion.x_axis_ordinate = str(sin(x_angle))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignPresentationLayer(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_presentation_layer"
|
||||
bl_label = "Assign Presentation Layer"
|
||||
|
||||
@@ -478,6 +478,7 @@ class Attribute(PropertyGroup):
|
||||
int_value: IntProperty(name="Value")
|
||||
float_value: FloatProperty(name="Value")
|
||||
is_null: BoolProperty(name="Is Null")
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
enum_items: StringProperty(name="Value")
|
||||
enum_value: EnumProperty(items=getAttributeEnumValues, name="Value")
|
||||
|
||||
@@ -1207,9 +1208,6 @@ class BIMProperties(PropertyGroup):
|
||||
smart_clash_grouping_max_distance: IntProperty(name="Smart Clash Grouping Max Distance", default=3, soft_min=1, soft_max=10)
|
||||
constraints: CollectionProperty(name="Constraints", type=Constraint)
|
||||
active_constraint_index: IntProperty(name="Active Constraint Index")
|
||||
eastings: StringProperty(name="Eastings")
|
||||
northings: StringProperty(name="Northings")
|
||||
orthogonal_height: StringProperty(name="Orthogonal Height")
|
||||
ifc_patch_recipes: EnumProperty(items=getIfcPatchRecipes, name="Recipes")
|
||||
ifc_patch_input: StringProperty(default="", name="IFC Patch Input IFC")
|
||||
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
|
||||
@@ -1265,25 +1263,6 @@ class BIMProperties(PropertyGroup):
|
||||
presentation_layers: CollectionProperty(name="Presentation Layers", type=PresentationLayer)
|
||||
|
||||
|
||||
class MapConversion(PropertyGroup):
|
||||
eastings: StringProperty(name="Eastings")
|
||||
northings: StringProperty(name="Northings")
|
||||
orthogonal_height: StringProperty(name="Orthogonal Height")
|
||||
x_axis_abscissa: StringProperty(name="X Axis Abscissa")
|
||||
x_axis_ordinate: StringProperty(name="X Axis Ordinate")
|
||||
scale: StringProperty(name="Scale")
|
||||
|
||||
|
||||
class TargetCRS(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
description: StringProperty(name="Description")
|
||||
geodetic_datum: StringProperty(name="Geodetic Datum")
|
||||
vertical_datum: StringProperty(name="Vertical Datum")
|
||||
map_projection: StringProperty(name="Map Projection")
|
||||
map_zone: StringProperty(name="Map Zone")
|
||||
map_unit: StringProperty(name="Map Unit")
|
||||
|
||||
|
||||
class BIMLibrary(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
version: StringProperty(name="Version")
|
||||
|
||||
@@ -437,53 +437,6 @@ class BIM_PT_presentation_layer_data(Panel):
|
||||
op.index = scene_props.active_presentation_layer_index
|
||||
|
||||
|
||||
class BIM_PT_gis(Panel):
|
||||
bl_label = "IFC Georeferencing"
|
||||
bl_idname = "BIM_PT_gis"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
scene = context.scene
|
||||
layout.row().prop(scene.BIMProperties, "has_georeferencing")
|
||||
|
||||
layout.label(text="Map Conversion:")
|
||||
layout.row().prop(scene.MapConversion, "eastings")
|
||||
layout.row().prop(scene.MapConversion, "northings")
|
||||
layout.row().prop(scene.MapConversion, "orthogonal_height")
|
||||
layout.row().prop(scene.MapConversion, "x_axis_abscissa")
|
||||
layout.row().prop(scene.MapConversion, "x_axis_ordinate")
|
||||
layout.row().prop(scene.MapConversion, "scale")
|
||||
|
||||
layout.label(text="Target CRS:")
|
||||
layout.row().prop(scene.TargetCRS, "name")
|
||||
layout.row().prop(scene.TargetCRS, "description")
|
||||
layout.row().prop(scene.TargetCRS, "geodetic_datum")
|
||||
layout.row().prop(scene.TargetCRS, "vertical_datum")
|
||||
layout.row().prop(scene.TargetCRS, "map_projection")
|
||||
layout.row().prop(scene.TargetCRS, "map_zone")
|
||||
layout.row().prop(scene.TargetCRS, "map_unit")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.convert_local_to_global")
|
||||
|
||||
layout.row().prop(scene.BIMProperties, "eastings")
|
||||
layout.row().prop(scene.BIMProperties, "northings")
|
||||
layout.row().prop(scene.BIMProperties, "orthogonal_height")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.convert_global_to_local")
|
||||
|
||||
if hasattr(bpy.context.scene, "sun_pos_properties"):
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.get_north_offset")
|
||||
row.operator("bim.set_north_offset")
|
||||
|
||||
|
||||
class BIM_PT_presentation_layers(Panel):
|
||||
bl_label = "IFC Presentation Layers"
|
||||
bl_idname = "BIM_PT_presentation_layer"
|
||||
|
||||
@@ -89,9 +89,10 @@ si_conversions = {
|
||||
|
||||
|
||||
def get_prefix(text):
|
||||
for prefix in prefixes.keys():
|
||||
if prefix in text.upper():
|
||||
return prefix
|
||||
if text:
|
||||
for prefix in prefixes.keys():
|
||||
if prefix in text.upper():
|
||||
return prefix
|
||||
|
||||
|
||||
def get_prefix_multiplier(text):
|
||||
|
||||
Reference in New Issue
Block a user