-
-
Notifications
You must be signed in to change notification settings - Fork 789
Merging two IFC files with different length units (mm and m) #1247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
In fact, I was just going to embark on this myself. I'm rewriting the BlenderBIM Add-on to incrementally edit the IFC as you change things in Blender instead of an export process, and so if the user togges the length unit in Blender, it also needs to push the change back to IFC... not a trivial task as you've guessed. If you solve it, I can reuse it in the BlenderBIM Add-on, both as an IfcPatch and also within the Add-on. Have you seen this? https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.6.0/src/ifcopenshell-python/ifcopenshell/util/unit.py the convert function may be useful. |
Theoretically although length units are the most noticable thing, it may be good to consider different area / volume units as well, which may require you to check things like property set and quantity set unit types... quite a nasty little issue. |
Wow that was quick :) Yes, I was actually playing around with the code in unit.py! My first test was to see if I can somehow traverse all referenced attributes on a given IfcProduct and look for a IfcLengthMeasure and scale it. I agree it would definitively be worthwhile to add conversion of other quantity types. |
Maybe instead of traversing, which maybe complex, how about this really dumb method instead?
|
That's a great suggestion. Okay I have gotten this far based on your suggestion: import ifcopenshell
from ifcopenshell.util.unit import get_prefix_multiplier, convert
ifc_file = ifcopenshell.open(ifc_file_path)
def calculate_unit_scale(file):
units = file.by_type("IfcUnitAssignment")[0]
unit_scale = 1
for unit in units.Units:
if not hasattr(unit, "UnitType") or unit.UnitType != "LENGTHUNIT":
continue
while unit.is_a("IfcConversionBasedUnit"):
unit_scale *= unit.ConversionFactor.ValueComponent.wrappedValue
unit = unit.ConversionFactor.UnitComponent
if unit.is_a("IfcSIUnit"):
unit_scale *= get_prefix_multiplier(unit.Prefix)
return unit_scale
s = ifcopenshell.ifcopenshell_wrapper.schema_by_name("IFC4")
classes_to_modify = {}
for d in s.declarations():
if not hasattr(d, "all_attributes") or "IfcLength" not in str(d.all_attributes()):
continue
attributes_to_modify = []
for attribute in d.all_attributes():
if "IfcLength" in str(attribute):
attributes_to_modify.append(attribute.name())
classes_to_modify[d.name()] = attributes_to_modify
print(classes_to_modify)
scale_factor = calculate_unit_scale(ifc_file)
def scale_all(obj, sf):
def serialize(obj):
"""Recursively walk object's hierarchy."""
if isinstance(obj, (int, float)):
return obj * sf
elif isinstance(obj, list):
return [serialize(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(serialize([item for item in obj]))
else:
try:
if obj.is_a('IfcLengthMeasure') is True:
obj.wrappedValue = obj.wrappedValue * sf
return obj
elif obj.is_a('IfcReal') is True:
obj.wrappedValue = obj.wrappedValue * sf
return obj
elif obj.is_a('IfcInteger') is True:
obj.wrappedValue = int(obj.wrappedValue * sf)
return obj
elif obj.is_a('IfcPlaneAngleMeasure') is True:
obj.wrappedValue = obj.wrappedValue * sf
return obj
elif obj.is_a('IfcText') is True:
return obj
elif obj.is_a('IfcLogical') is True:
return obj
except:
pass
raise ValueError(f'Unknown entity "{type(obj)}", "{obj}"')
return serialize(obj)
for ifc_class, attributes in classes_to_modify.items():
for element in ifc_file.by_type(ifc_class):
for attribute in attributes:
old_val = getattr(element, attribute)
if old_val is None:
continue
setattr(element, attribute, scale_all(old_val, scale_factor))
new_val = getattr(element, attribute) It worked on my particular IFC file, but I see that I would need to add a lot of conditions to catch all the IFC real, integer etc... Any tip for a more elegant solution? :) |
In C++ the conversion factor should be applied for length units as long as both files have a length unit and you're copying from one to the other. https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.6.0/src/ifcparse/IfcParse.cpp#L1657 |
Instead of adding a lot of conditions, how about this dirty trick...
(Note: will bork on lists) |
@aothms awesome! So I guess just create a new IfcOpenShell file object, add a length unit assignment, then |
Yes, but you're right, this should be extended for other units. Now IfcLengthMeasure and LENGTHUNIT are hardcoded, but it should be pretty straightforward to extend this to all < measures unit > pairs. Might be a nice issue to get familiar with C++ ;) |
I do have a plan to jump into C++, so I would love to give it a go. But at the moment I'm a bit short on time. Maybe I can start to look at it in a couple of weeks if no one else has given it a go :) |
In the middle of a big refactor now on the Python side, but if @Krande doesn't beat me to it, I'll investigate this too :) |
Really cool, feel free to ping me any time once any of you start to have a go at this. |
ping @aothms. I have some time to dive into c++. I've already started tackling the local compilation of ifcopenshell on windows with the hope of at least getting a local dev setup. Any help or hints with setting up a good local development setup is as always much appreciated:) |
Very cool.
For windows the |
The build-deps step seemed to work just fine. However I did run into an error with "run-cmake" step though. The error seems to be this:
It might be unrelated, but for some reason I see references to my system python interpreter (even though I build a new python in the
The full output is shown below
I see there has been a lot of recent activity on the windows build process, so I am trying to read through the various issues to see if I can catch something that might help. But if you have any suggestions I would appreciate any help :) Best Regards |
I think it's the same as #1273 (but that only addressed it for the nix build script). Can you try and add that same |
Using I thought I would start by working my way through the c++ example files and just debug and step through some of the more basic operations to get a feel for how everything is tied together. However, when trying to build, I ran into this:
Any ideas? I noticed it looks for Another thing I am a bit curious about (and I guess this question is more of a general c++\python type of question) is if it is possible to attach and debug a running instance of python using ifcopenshell and step through portion of the c++ code that seemingly generates segmentation errors? Or if I have to recreate the examples in c++ and debug in "pure c++"? Anyways, thanks for the help so far! Best Regards |
Thanks, I'll add it to run-cmake then.
I think the default of the build script is RelWithDebInfo, but Microsoft VS will always open a solution in Debug mode. So you need to switch that to RelWithDebInfo to be consistent with the built libraries (the _d prefix is for debug).
yes this is what I tend to do as well. Make sure that the .pyd and .pdb (msvc debugging archive) match and are both in the ifcopenshell py module directory and it matches the msvc solution you have open. Then in msvc process [Ctrl] [Alt] [P] for attach to process (it's also somewhere in the menus) and select your python process. Then you can simply set a breakpoint in the code. |
Did you find a satisfying solution ? |
I assume you mean so called |
I tried to generalize what have been said and done before, to get a global conversion, for all measurement + point the assumptions. Classes concerned by measurement can be calculated one per schema. I thought it would be better to just write the resulting dictionnary instead of recalculating it each time. I tried to minimize file parsing by only parsing classes that are actually concerned by resizing. It seems to work. If there are test IFCs (especially ones using non-geometrical entities), I would be glad to test them PS : I am having trouble copying the code here. How can I do so ? |
@Maozerhouni this looks really cool. Sorry for the delay in replying. The best way to "copying" the code is to submit a Pull Request so that the code gets bundled with IfcOpenShell. Would you be so kind? In later versions of ifcopenshell some of the directionaries are not strictly necessary anymore.
But a pull request in the current form is also already a good addition to the library :) |
Bump @Maozerhouni |
Hello, sorry for the late answer |
Hi,
I am looking into merging ifc files with length units (millimeters and meters). My initial thought was to simply scale all length units for all physical elements of one of the ifc files with the appropriate scale factor (and of course change the length unit definition in the ifc) prior to merge.
Unfortunately I haven't found any existing methods or functions in IfcOpenshell that does this specifically, nor any specific examples for how to go about this in particular. However, I have found some related topics (#237, #1000) and a recipe that might be extended to do what I am after.
I guess ultimately my question is if there exists such a functionality already within ifcopenshell that does this?
If not, I could contribute with a recipe for this. My first thought would be to iterate all elements that have a IfcLengthMeasure attribute and scale with the correct scale factor (IfcCartesianPoint, geometry lengths, heights, etc..). Any hints on how to proceed (if this path is necessary) is as always much appreciated.
Best Regards
Kristoffer
The text was updated successfully, but these errors were encountered: