2022-01-19 12:18:33 +11:00
|
|
|
# IfcOpenShell - IFC toolkit and geometry engine
|
|
|
|
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
|
|
|
|
#
|
|
|
|
|
# This file is part of IfcOpenShell.
|
|
|
|
|
#
|
|
|
|
|
# IfcOpenShell is free software: you can redistribute it and/or modify
|
|
|
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
#
|
|
|
|
|
# IfcOpenShell 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 Lesser General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
2020-05-17 18:32:56 +10:00
|
|
|
from math import pi
|
|
|
|
|
|
2020-11-01 20:08:27 +07:00
|
|
|
prefixes = {
|
|
|
|
|
"EXA": 1e18,
|
|
|
|
|
"PETA": 1e15,
|
|
|
|
|
"TERA": 1e12,
|
|
|
|
|
"GIGA": 1e9,
|
|
|
|
|
"MEGA": 1e6,
|
|
|
|
|
"KILO": 1e3,
|
|
|
|
|
"HECTO": 1e2,
|
|
|
|
|
"DECA": 1e1,
|
|
|
|
|
"DECI": 1e-1,
|
|
|
|
|
"CENTI": 1e-2,
|
|
|
|
|
"MILLI": 1e-3,
|
|
|
|
|
"MICRO": 1e-6,
|
|
|
|
|
"NANO": 1e-9,
|
|
|
|
|
"PICO": 1e-12,
|
|
|
|
|
"FEMTO": 1e-15,
|
|
|
|
|
"ATTO": 1e-18,
|
|
|
|
|
}
|
2020-05-17 18:32:56 +10:00
|
|
|
|
2020-11-01 20:08:27 +07:00
|
|
|
unit_names = [
|
|
|
|
|
"AMPERE",
|
|
|
|
|
"BECQUEREL",
|
|
|
|
|
"CANDELA",
|
|
|
|
|
"COULOMB",
|
|
|
|
|
"CUBIC_METRE",
|
2021-08-13 16:18:05 +10:00
|
|
|
"DEGREE_CELSIUS",
|
2020-11-01 20:08:27 +07:00
|
|
|
"FARAD",
|
|
|
|
|
"GRAM",
|
|
|
|
|
"GRAY",
|
|
|
|
|
"HENRY",
|
|
|
|
|
"HERTZ",
|
|
|
|
|
"JOULE",
|
|
|
|
|
"KELVIN",
|
|
|
|
|
"LUMEN",
|
|
|
|
|
"LUX",
|
|
|
|
|
"MOLE",
|
|
|
|
|
"NEWTON",
|
|
|
|
|
"OHM",
|
|
|
|
|
"PASCAL",
|
|
|
|
|
"RADIAN",
|
|
|
|
|
"SECOND",
|
|
|
|
|
"SIEMENS",
|
|
|
|
|
"SIEVERT",
|
2021-08-13 16:18:05 +10:00
|
|
|
"SQUARE_METRE",
|
2020-11-01 20:08:27 +07:00
|
|
|
"METRE",
|
|
|
|
|
"STERADIAN",
|
|
|
|
|
"TESLA",
|
|
|
|
|
"VOLT",
|
|
|
|
|
"WATT",
|
|
|
|
|
"WEBER",
|
|
|
|
|
]
|
2020-05-17 18:32:56 +10:00
|
|
|
|
2021-08-06 12:38:34 +10:00
|
|
|
si_dimensions = {
|
|
|
|
|
"METRE": (1, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"SQUARE_METRE": (2, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"CUBIC_METRE": (3, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"GRAM": (0, 1, 0, 0, 0, 0, 0),
|
|
|
|
|
"SECOND": (0, 0, 1, 0, 0, 0, 0),
|
|
|
|
|
"AMPERE": (0, 0, 0, 1, 0, 0, 0),
|
|
|
|
|
"KELVIN": (0, 0, 0, 0, 1, 0, 0),
|
|
|
|
|
"MOLE": (0, 0, 0, 0, 0, 1, 0),
|
|
|
|
|
"CANDELA": (0, 0, 0, 0, 0, 0, 1),
|
|
|
|
|
"RADIAN": (0, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"STERADIAN": (0, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"HERTZ": (0, 0, -1, 0, 0, 0, 0),
|
|
|
|
|
"NEWTON": (1, 1, -2, 0, 0, 0, 0),
|
|
|
|
|
"PASCAL": (-1, 1, -2, 0, 0, 0, 0),
|
|
|
|
|
"JOULE": (2, 1, -2, 0, 0, 0, 0),
|
|
|
|
|
"WATT": (2, 1, -3, 0, 0, 0, 0),
|
|
|
|
|
"COULOMB": (0, 0, 1, 1, 0, 0, 0),
|
|
|
|
|
"VOLT": (2, 1, -3, -1, 0, 0, 0),
|
|
|
|
|
"FARAD": (-2, -1, 4, 2, 0, 0, 0),
|
|
|
|
|
"OHM": (2, 1, -3, -2, 0, 0, 0),
|
|
|
|
|
"SIEMENS": (-2, -1, 3, 2, 0, 0, 0),
|
|
|
|
|
"WEBER": (2, 1, -2, -1, 0, 0, 0),
|
|
|
|
|
"TESLA": (0, 1, -2, -1, 0, 0, 0),
|
|
|
|
|
"HENRY": (2, 1, -2, -2, 0, 0, 0),
|
|
|
|
|
"DEGREE_CELSIUS": (0, 0, 0, 0, 1, 0, 0),
|
|
|
|
|
"LUMEN": (0, 0, 0, 0, 0, 0, 1),
|
|
|
|
|
"LUX": (-2, 0, 0, 0, 0, 0, 1),
|
|
|
|
|
"BECQUEREL": (0, 0, -1, 0, 0, 0, 0),
|
|
|
|
|
"GRAY": (2, 0, -2, 0, 0, 0, 0),
|
|
|
|
|
"SIEVERT": (2, 0, -2, 0, 0, 0, 0),
|
2021-08-06 15:48:07 +10:00
|
|
|
"OTHERWISE": (0, 0, 0, 0, 0, 0, 0),
|
2021-08-06 12:38:34 +10:00
|
|
|
}
|
|
|
|
|
|
2021-08-13 16:18:05 +10:00
|
|
|
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/72
|
|
|
|
|
si_type_names = {
|
|
|
|
|
"ABSORBEDDOSEUNIT": "GRAY",
|
|
|
|
|
"AMOUNTOFSUBSTANCEUNIT": "MOLE",
|
|
|
|
|
"AREAUNIT": "SQUARE_METRE",
|
|
|
|
|
"DOSEEQUIVALENTUNIT": "SIEVERT",
|
|
|
|
|
"ELECTRICCAPACITANCEUNIT": "FARAD",
|
|
|
|
|
"ELECTRICCHARGEUNIT": "COULOMB",
|
|
|
|
|
"ELECTRICCONDUCTANCEUNIT": "SIEMENS",
|
|
|
|
|
"ELECTRICCURRENTUNIT": "AMPERE",
|
|
|
|
|
"ELECTRICRESISTANCEUNIT": "OHM",
|
|
|
|
|
"ELECTRICVOLTAGEUNIT": "VOLT",
|
|
|
|
|
"ENERGYUNIT": "JOULE",
|
|
|
|
|
"FORCEUNIT": "NEWTON",
|
|
|
|
|
"FREQUENCYUNIT": "HERTZ",
|
|
|
|
|
"ILLUMINANCEUNIT": "LUX",
|
|
|
|
|
"INDUCTANCEUNIT": "HENRY",
|
|
|
|
|
"LENGTHUNIT": "METRE",
|
|
|
|
|
"LUMINOUSFLUXUNIT": "LUMEN",
|
|
|
|
|
"LUMINOUSINTENSITYUNIT": "CANDELA",
|
|
|
|
|
"MAGNETICFLUXDENSITYUNIT": "TESLA",
|
|
|
|
|
"MAGNETICFLUXUNIT": "WEBER",
|
|
|
|
|
"MASSUNIT": "GRAM",
|
|
|
|
|
"PLANEANGLEUNIT": "RADIAN",
|
|
|
|
|
"POWERUNIT": "WATT",
|
|
|
|
|
"PRESSUREUNIT": "PASCAL",
|
|
|
|
|
"RADIOACTIVITYUNIT": "BECQUEREL",
|
|
|
|
|
"SOLIDANGLEUNIT": "STERADIAN",
|
2021-09-09 21:27:23 +10:00
|
|
|
"THERMODYNAMICTEMPERATUREUNIT": "KELVIN", # Or, DEGREE_CELSIUS, but this is a quirk of IFC
|
2021-08-13 16:18:05 +10:00
|
|
|
"TIMEUNIT": "SECOND",
|
|
|
|
|
"VOLUMEUNIT": "CUBIC_METRE",
|
2021-10-14 12:44:13 +11:00
|
|
|
"USERDEFINED": "METRE",
|
2021-08-13 16:18:05 +10:00
|
|
|
}
|
|
|
|
|
|
2021-08-20 00:08:49 +02:00
|
|
|
# See IfcDimensionalExponents:
|
|
|
|
|
# (Length, Mass, Time, ElectricCurrent, ThermodynamicTemperature, AmountOfSubstance, LuminousIntensity)
|
2021-08-13 16:20:30 +10:00
|
|
|
named_dimensions = {
|
2021-08-20 00:08:49 +02:00
|
|
|
"ABSORBEDDOSEUNIT": (2, 0, -2, 0, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
"AMOUNTOFSUBSTANCEUNIT": (0, 0, 0, 0, 0, 1, 0),
|
|
|
|
|
"AREAUNIT": (2, 0, 0, 0, 0, 0, 0),
|
2021-08-20 00:08:49 +02:00
|
|
|
"DOSEEQUIVALENTUNIT": (2, 0, -2, 0, 0, 0, 0),
|
|
|
|
|
"ELECTRICCAPACITANCEUNIT": (-2, -1, 4, 2, 0, 0, 0),
|
|
|
|
|
"ELECTRICCHARGEUNIT": (0, 0, 1, 1, 0, 0, 0),
|
|
|
|
|
"ELECTRICCONDUCTANCEUNIT": (-2, -1, 3, 2, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
"ELECTRICCURRENTUNIT": (0, 0, 0, 1, 0, 0, 0),
|
2021-08-20 00:08:49 +02:00
|
|
|
"ELECTRICRESISTANCEUNIT": (2, 1, -3, -2, 0, 0, 0),
|
|
|
|
|
"ELECTRICVOLTAGEUNIT": (2, 1, -3, -1, 0, 0, 0),
|
2021-08-13 12:11:15 +02:00
|
|
|
"ENERGYUNIT": (2, 1, -2, 0, 0, 0, 0),
|
|
|
|
|
"FORCEUNIT": (1, 1, -2, 0, 0, 0, 0),
|
|
|
|
|
"FREQUENCYUNIT": (0, 0, -1, 0, 0, 0, 0),
|
2021-08-20 00:08:49 +02:00
|
|
|
"ILLUMINANCEUNIT": (-2, 0, 0, 0, 0, 1, 1),
|
|
|
|
|
"INDUCTANCEUNIT": (2, 1, -2, -2, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
"LENGTHUNIT": (1, 0, 0, 0, 0, 0, 0),
|
2021-08-20 00:08:49 +02:00
|
|
|
"LUMINOUSFLUXUNIT": (0, 0, 0, 0, 0, 1, 1),
|
2021-08-13 16:20:30 +10:00
|
|
|
"LUMINOUSINTENSITYUNIT": (0, 0, 0, 0, 0, 0, 1),
|
2021-08-20 00:08:49 +02:00
|
|
|
"MAGNETICFLUXDENSITYUNIT": (0, 1, -2, -1, 0, 0, 0),
|
|
|
|
|
"MAGNETICFLUXUNIT": (2, 1, -2, -1, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
"MASSUNIT": (0, 1, 0, 0, 0, 0, 0),
|
2021-08-13 12:11:15 +02:00
|
|
|
"PLANEANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
|
|
|
|
"POWERUNIT": (2, 1, -3, 0, 0, 0, 0),
|
|
|
|
|
"PRESSUREUNIT": (-1, 1, -2, 0, 0, 0, 0),
|
2021-08-20 00:08:49 +02:00
|
|
|
"RADIOACTIVITYUNIT": (0, 0, -1, 0, 0, 0, 0),
|
2021-08-13 12:11:15 +02:00
|
|
|
"SOLIDANGLEUNIT": (0, 0, 0, 0, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
"THERMODYNAMICTEMPERATUREUNIT": (0, 0, 0, 0, 1, 0, 0),
|
|
|
|
|
"TIMEUNIT": (0, 0, 1, 0, 0, 0, 0),
|
|
|
|
|
"VOLUMEUNIT": (3, 0, 0, 0, 0, 0, 0),
|
2021-10-14 12:44:13 +11:00
|
|
|
"USERDEFINED": (0, 0, 0, 0, 0, 0, 0),
|
2021-08-13 16:20:30 +10:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 18:32:56 +10:00
|
|
|
si_conversions = {
|
2021-09-19 10:02:34 +02:00
|
|
|
"thou": 0.0000254,
|
2020-11-01 20:08:27 +07:00
|
|
|
"inch": 0.0254,
|
|
|
|
|
"foot": 0.3048,
|
|
|
|
|
"yard": 0.914,
|
|
|
|
|
"mile": 1609,
|
2021-09-19 10:02:34 +02:00
|
|
|
"square thou": 6.4516e-10,
|
2020-11-01 20:08:27 +07:00
|
|
|
"square inch": 0.0006452,
|
|
|
|
|
"square foot": 0.09290304,
|
|
|
|
|
"square yard": 0.83612736,
|
|
|
|
|
"acre": 4046.86,
|
|
|
|
|
"square mile": 2588881,
|
2021-09-19 10:02:34 +02:00
|
|
|
"cubic thou": 1.6387064e-14,
|
2020-11-01 20:08:27 +07:00
|
|
|
"cubic inch": 0.00001639,
|
|
|
|
|
"cubic foot": 0.02831684671168849,
|
|
|
|
|
"cubic yard": 0.7636,
|
2021-09-19 10:02:34 +02:00
|
|
|
"cubic mile": 4165509529,
|
2020-11-01 20:08:27 +07:00
|
|
|
"litre": 0.001,
|
|
|
|
|
"fluid ounce UK": 0.0000284130625,
|
|
|
|
|
"fluid ounce US": 0.00002957353,
|
|
|
|
|
"pint UK": 0.000568,
|
|
|
|
|
"pint US": 0.000473,
|
|
|
|
|
"gallon UK": 0.004546,
|
|
|
|
|
"gallon US": 0.003785,
|
|
|
|
|
"degree": pi / 180,
|
|
|
|
|
"ounce": 0.02835,
|
|
|
|
|
"pound": 0.454,
|
|
|
|
|
"ton UK": 1016.0469088,
|
|
|
|
|
"ton US": 907.18474,
|
|
|
|
|
"lbf": 4.4482216153,
|
|
|
|
|
"kip": 4448.2216153,
|
|
|
|
|
"psi": 6894.7572932,
|
|
|
|
|
"ksi": 6894757.2932,
|
|
|
|
|
"minute": 60,
|
|
|
|
|
"hour": 3600,
|
|
|
|
|
"day": 86400,
|
|
|
|
|
"btu": 1055.056,
|
2021-10-14 12:46:01 +11:00
|
|
|
"fahrenheit": 1.8,
|
2020-11-01 20:08:27 +07:00
|
|
|
}
|
|
|
|
|
|
2021-10-14 12:44:13 +11:00
|
|
|
|
2021-10-14 12:46:01 +11:00
|
|
|
si_offsets = {
|
|
|
|
|
"fahrenheit": -459.67,
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-14 12:44:13 +11:00
|
|
|
|
|
|
|
|
imperial_types = {
|
|
|
|
|
"thou": "LENGTHUNIT",
|
|
|
|
|
"inch": "LENGTHUNIT",
|
|
|
|
|
"foot": "LENGTHUNIT",
|
|
|
|
|
"yard": "LENGTHUNIT",
|
|
|
|
|
"mile": "LENGTHUNIT",
|
|
|
|
|
"square thou": "AREAUNIT",
|
|
|
|
|
"square inch": "AREAUNIT",
|
|
|
|
|
"square foot": "AREAUNIT",
|
|
|
|
|
"square yard": "AREAUNIT",
|
|
|
|
|
"acre": "AREAUNIT",
|
|
|
|
|
"square mile": "AREAUNIT",
|
|
|
|
|
"cubic thou": "VOLUMEUNIT",
|
|
|
|
|
"cubic inch": "VOLUMEUNIT",
|
|
|
|
|
"cubic foot": "VOLUMEUNIT",
|
|
|
|
|
"cubic yard": "VOLUMEUNIT",
|
|
|
|
|
"cubic mile": "VOLUMEUNIT",
|
|
|
|
|
"litre": "VOLUMEUNIT",
|
|
|
|
|
"fluid ounce UK": "VOLUMEUNIT",
|
|
|
|
|
"fluid ounce US": "VOLUMEUNIT",
|
|
|
|
|
"pint UK": "VOLUMEUNIT",
|
|
|
|
|
"pint US": "VOLUMEUNIT",
|
|
|
|
|
"gallon UK": "VOLUMEUNIT",
|
|
|
|
|
"gallon US": "VOLUMEUNIT",
|
|
|
|
|
"degree": "PLANEANGLEUNIT",
|
|
|
|
|
"ounce": "MASSUNIT",
|
|
|
|
|
"pound": "MASSUNIT",
|
|
|
|
|
"ton UK": "MASSUNIT",
|
|
|
|
|
"ton US": "MASSUNIT",
|
|
|
|
|
"lbf": "FORCEUNIT",
|
|
|
|
|
"kip": "FORCEUNIT",
|
|
|
|
|
"psi": "PRESSUREUNIT",
|
|
|
|
|
"ksi": "PRESSUREUNIT",
|
|
|
|
|
"minute": "TIMEUNIT",
|
|
|
|
|
"hour": "TIMEUNIT",
|
|
|
|
|
"day": "TIMEUNIT",
|
|
|
|
|
"btu": "ENERGYUNIT",
|
2021-10-14 12:46:01 +11:00
|
|
|
"fahrenheit": "THERMODYNAMICTEMPERATUREUNIT",
|
2021-10-14 12:44:13 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-06 18:19:41 +10:00
|
|
|
prefix_symbols = {
|
|
|
|
|
"EXA": "E",
|
|
|
|
|
"PETA": "P",
|
|
|
|
|
"TERA": "T",
|
|
|
|
|
"GIGA": "G",
|
|
|
|
|
"MEGA": "M",
|
|
|
|
|
"KILO": "k",
|
|
|
|
|
"HECTO": "h",
|
|
|
|
|
"DECA": "da",
|
|
|
|
|
"DECI": "d",
|
|
|
|
|
"CENTI": "c",
|
|
|
|
|
"MILLI": "m",
|
|
|
|
|
"MICRO": "μ",
|
|
|
|
|
"NANO": "n",
|
|
|
|
|
"PICO": "p",
|
|
|
|
|
"FEMTO": "f",
|
|
|
|
|
"ATTO": "a",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unit_symbols = {
|
|
|
|
|
"CUBIC_METRE": "m3",
|
|
|
|
|
"GRAM": "g",
|
|
|
|
|
"SECOND": "s",
|
|
|
|
|
"SQUARE_METRE": "m2",
|
|
|
|
|
"METRE": "m",
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 18:32:56 +10:00
|
|
|
|
|
|
|
|
def get_prefix(text):
|
2021-01-19 11:18:26 +11:00
|
|
|
if text:
|
|
|
|
|
for prefix in prefixes.keys():
|
|
|
|
|
if prefix in text.upper():
|
|
|
|
|
return prefix
|
2020-05-17 18:32:56 +10:00
|
|
|
|
2020-11-01 20:08:27 +07:00
|
|
|
|
2020-05-17 18:32:56 +10:00
|
|
|
def get_prefix_multiplier(text):
|
|
|
|
|
if not text:
|
|
|
|
|
return 1
|
|
|
|
|
prefix = get_prefix(text)
|
|
|
|
|
if prefix:
|
|
|
|
|
return prefixes[prefix]
|
|
|
|
|
return 1
|
|
|
|
|
|
2020-11-01 20:08:27 +07:00
|
|
|
|
2020-05-17 18:32:56 +10:00
|
|
|
def get_unit_name(text):
|
2021-08-06 15:48:07 +10:00
|
|
|
text = text.upper().replace("METER", "METRE")
|
2020-05-17 18:32:56 +10:00
|
|
|
for name in unit_names:
|
2021-08-13 16:18:05 +10:00
|
|
|
if name.replace("_", " ") in text:
|
2020-05-17 18:32:56 +10:00
|
|
|
return name
|
2020-08-08 16:31:27 +10:00
|
|
|
|
2020-11-01 20:08:27 +07:00
|
|
|
|
2021-08-06 12:38:34 +10:00
|
|
|
def get_si_dimensions(name):
|
|
|
|
|
return si_dimensions.get(name, si_dimensions["OTHERWISE"])
|
|
|
|
|
|
|
|
|
|
|
2021-08-13 16:20:30 +10:00
|
|
|
def get_named_dimensions(name):
|
|
|
|
|
return named_dimensions.get(name, (0, 0, 0, 0, 0, 0, 0))
|
|
|
|
|
|
|
|
|
|
|
2021-08-16 15:07:09 +10:00
|
|
|
def get_unit_assignment(ifc_file):
|
|
|
|
|
unit_assignments = ifc_file.by_type("IfcUnitAssignment")
|
|
|
|
|
if unit_assignments:
|
|
|
|
|
return unit_assignments[0]
|
|
|
|
|
|
|
|
|
|
|
2023-02-02 23:01:12 +11:00
|
|
|
def get_project_unit(ifc_file, unit_type):
|
|
|
|
|
"""Get the default project unit of a particular unit type
|
|
|
|
|
|
|
|
|
|
:param ifc_file: The IFC file.
|
|
|
|
|
:type ifc_file: ifcopenshell.file.file
|
|
|
|
|
:param unit_type: The type of unit, taken from the list of IFC unit types,
|
|
|
|
|
such as "LENGTHUNIT".
|
|
|
|
|
:type unit_type: str
|
|
|
|
|
:return: The IFC unit entity, or nothing if there is no default project unit
|
|
|
|
|
defined.
|
|
|
|
|
:rtype: ifcopenshell.entity_instance,None
|
|
|
|
|
"""
|
|
|
|
|
unit_assignment = get_unit_assignment(ifc_file)
|
|
|
|
|
if unit_assignment:
|
|
|
|
|
for unit in unit_assignment.Units or []:
|
|
|
|
|
if getattr(unit, "UnitType", None) == unit_type:
|
|
|
|
|
return unit
|
|
|
|
|
|
|
|
|
|
|
2021-08-06 15:48:07 +10:00
|
|
|
def get_property_unit(prop, ifc_file):
|
|
|
|
|
unit = getattr(prop, "Unit", None)
|
|
|
|
|
if unit:
|
|
|
|
|
return unit
|
2021-08-16 15:07:09 +10:00
|
|
|
unit_assignment = get_unit_assignment(ifc_file)
|
2021-08-06 15:48:07 +10:00
|
|
|
if not unit_assignment:
|
|
|
|
|
return
|
|
|
|
|
entity = prop.wrapped_data.declaration().as_entity()
|
|
|
|
|
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
2021-08-16 15:07:09 +10:00
|
|
|
measure_class = entity.attribute_by_index(3).type_of_attribute().declared_type().name()
|
2021-08-06 15:48:07 +10:00
|
|
|
elif prop.is_a("IfcPropertySingleValue") and prop.NominalValue:
|
2021-08-16 15:07:09 +10:00
|
|
|
measure_class = prop.NominalValue.is_a()
|
2022-09-12 21:34:06 +10:00
|
|
|
elif prop.is_a("IfcPropertyEnumeratedValue") and prop.EnumerationValues:
|
|
|
|
|
measure_class = prop.EnumerationValues[0].is_a()
|
|
|
|
|
elif prop.is_a("IfcPropertyListValue") and prop.ListValues:
|
|
|
|
|
measure_class = prop.ListValues[0].is_a()
|
2022-09-12 22:35:05 +10:00
|
|
|
elif prop.is_a("IfcPropertyBoundedValue"):
|
|
|
|
|
if prop.UpperBoundValue:
|
|
|
|
|
measure_class = prop.UpperBoundValue.is_a()
|
|
|
|
|
elif prop.LowerBoundValue:
|
|
|
|
|
measure_class = prop.LowerBoundValue.is_a()
|
|
|
|
|
elif prop.SetPointValue:
|
|
|
|
|
measure_class = prop.SetPointValue.is_a()
|
|
|
|
|
elif prop.is_a("IfcPropertyTableValue"):
|
|
|
|
|
table_units = {}
|
|
|
|
|
for attribute in ["Defining", "Defined"]:
|
|
|
|
|
if getattr(prop, f"{attribute}Unit"):
|
|
|
|
|
table_units[f"{attribute}Unit"] = getattr(prop, f"{attribute}Unit")
|
|
|
|
|
elif getattr(prop, f"{attribute}Values"):
|
|
|
|
|
measure_class = getattr(prop, f"{attribute}Values")[0].is_a()
|
|
|
|
|
unit_type = get_measure_unit_type(measure_class)
|
|
|
|
|
units = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == unit_type]
|
|
|
|
|
if units:
|
|
|
|
|
table_units[f"{attribute}Unit"] = units[0]
|
|
|
|
|
else:
|
|
|
|
|
table_units[f"{attribute}Unit"] = None
|
|
|
|
|
else:
|
|
|
|
|
table_units[f"{attribute}Unit"] = None
|
|
|
|
|
return table_units
|
2021-08-16 15:07:09 +10:00
|
|
|
unit_type = get_measure_unit_type(measure_class)
|
|
|
|
|
units = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == unit_type]
|
2021-08-06 15:48:07 +10:00
|
|
|
if units:
|
|
|
|
|
return units[0]
|
|
|
|
|
|
|
|
|
|
|
2021-08-16 15:07:09 +10:00
|
|
|
def get_unit_measure_class(unit_type):
|
|
|
|
|
if unit_type == "USERDEFINED":
|
|
|
|
|
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/71
|
|
|
|
|
return "IfcNumericMeasure"
|
2021-08-13 11:56:33 +10:00
|
|
|
return "Ifc" + unit_type[0:-4].lower().capitalize() + "Measure"
|
|
|
|
|
|
|
|
|
|
|
2021-08-16 15:07:09 +10:00
|
|
|
def get_measure_unit_type(measure_class):
|
|
|
|
|
if measure_class == "IfcNumericMeasure":
|
|
|
|
|
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/71
|
|
|
|
|
return "USERDEFINED"
|
|
|
|
|
for text in ("Ifc", "Measure", "Non", "Positive", "Negative"):
|
|
|
|
|
measure_class = measure_class.replace(text, "")
|
|
|
|
|
return measure_class.upper() + "UNIT"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_symbol_measure_class(symbol):
|
|
|
|
|
# Dumb, but everybody gets it, unlike regex golf
|
|
|
|
|
if not symbol:
|
|
|
|
|
return "IfcNumericMeasure"
|
|
|
|
|
symbol = symbol.lower()
|
|
|
|
|
if symbol in ["km", "m", "cm", "mm", "ly", "lf", "lin", "yd", "ft", "in"]:
|
|
|
|
|
return "IfcLengthMeasure"
|
|
|
|
|
elif symbol in ["km2", "m2", "cm2", "mm2", "sqy", "sqft", "sqin"]:
|
|
|
|
|
return "IfcAreaMeasure"
|
|
|
|
|
elif symbol in ["km3", "m3", "cm3", "mm3", "cy", "cft", "cin"]:
|
|
|
|
|
return "IfcVolumeMeasure"
|
|
|
|
|
elif symbol in ["kg", "g", "mt", "kt", "t"]:
|
|
|
|
|
return "IfcMassMeasure"
|
|
|
|
|
elif symbol in ["day", "d", "hour", "hr", "h", "minute", "min", "m", "second", "sec", "s"]:
|
|
|
|
|
return "IfcTimeMeasure"
|
|
|
|
|
return "IfcNumericMeasure"
|
|
|
|
|
|
|
|
|
|
|
2021-08-09 16:56:30 +10:00
|
|
|
def get_symbol_quantity_class(symbol):
|
2021-08-09 17:57:34 +10:00
|
|
|
# Dumb, but everybody gets it, unlike regex golf
|
2021-08-09 16:56:30 +10:00
|
|
|
if not symbol:
|
|
|
|
|
return "IfcQuantityCount"
|
2021-08-09 17:57:34 +10:00
|
|
|
symbol = symbol.lower()
|
2021-08-16 15:07:09 +10:00
|
|
|
if symbol in ["km", "m", "cm", "mm", "ly", "lf", "lin", "yd", "ft", "in"]:
|
|
|
|
|
return "IfcQuantityLength"
|
|
|
|
|
elif symbol in ["km2", "m2", "cm2", "mm2", "sqy", "sqft", "sqin"]:
|
|
|
|
|
return "IfcQuantityArea"
|
|
|
|
|
elif symbol in ["km3", "m3", "cm3", "mm3", "cy", "cft", "cin"]:
|
|
|
|
|
return "IfcQuantityVolume"
|
|
|
|
|
elif symbol in ["kg", "g", "mt", "kt", "t"]:
|
2021-08-09 16:56:30 +10:00
|
|
|
return "IfcQuantityWeight"
|
2021-08-09 17:57:34 +10:00
|
|
|
elif symbol in ["day", "d", "hour", "hr", "h", "minute", "min", "m", "second", "sec", "s"]:
|
2021-08-09 16:56:30 +10:00
|
|
|
return "IfcQuantityTime"
|
|
|
|
|
return "IfcQuantityCount"
|
|
|
|
|
|
|
|
|
|
|
2021-08-06 18:19:41 +10:00
|
|
|
def get_unit_symbol(unit):
|
|
|
|
|
if unit.is_a("IfcSIUnit"):
|
|
|
|
|
symbol = ""
|
|
|
|
|
symbol += prefix_symbols.get(unit.Prefix, "")
|
|
|
|
|
symbol += unit_symbols.get(unit.Name.replace("METER", "METRE"), "?")
|
|
|
|
|
return symbol
|
|
|
|
|
return "?"
|
|
|
|
|
|
|
|
|
|
|
2023-02-02 23:01:12 +11:00
|
|
|
def convert_unit(value, from_unit, to_unit):
|
|
|
|
|
"""Convert from one unit to another unit
|
|
|
|
|
|
|
|
|
|
:param value: The numeric value you want to convert
|
|
|
|
|
:type value: float
|
|
|
|
|
:param from_unit: The IfcNamedUnit to confirm from.
|
|
|
|
|
:type from_unit: ifcopenshell.entity_instance.entity_instance
|
|
|
|
|
:param to_unit: The IfcNamedUnit to confirm from.
|
|
|
|
|
:type to_unit: ifcopenshell.entity_instance.entity_instance
|
|
|
|
|
:return: The converted value.
|
|
|
|
|
:rtype: float
|
|
|
|
|
"""
|
|
|
|
|
return convert(
|
|
|
|
|
value, getattr(from_unit, "Prefix", None), from_unit.Name, getattr(to_unit, "Prefix", None), to_unit.Name
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2020-08-08 16:31:27 +10:00
|
|
|
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
|
|
|
|
|
"""Converts between length, area, and volume units
|
|
|
|
|
|
2023-02-02 23:01:12 +11:00
|
|
|
In this case, you manually specify the names and (optionally) prefixes to
|
|
|
|
|
convert to and from. In case you want to automatically convert to units
|
|
|
|
|
already available as IFC entities, consider using convert_unit() instead.
|
|
|
|
|
|
2020-08-08 16:31:27 +10:00
|
|
|
:param value: The numeric value you want to convert
|
|
|
|
|
:type value: float
|
|
|
|
|
:param from_prefix: A prefix from IfcSIPrefix. Can be None.
|
2023-02-02 23:01:12 +11:00
|
|
|
:type from_prefix: str,optional
|
2020-08-08 16:31:27 +10:00
|
|
|
:param from_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
|
2023-02-02 23:01:12 +11:00
|
|
|
:type from_unit: str
|
2020-08-08 16:31:27 +10:00
|
|
|
:param to_prefix: A prefix from IfcSIPrefix. Can be None.
|
2023-02-02 23:01:12 +11:00
|
|
|
:type to_prefix: str,optional
|
2020-08-08 16:31:27 +10:00
|
|
|
:param to_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
|
2023-02-02 23:01:12 +11:00
|
|
|
:type to_unit: str
|
|
|
|
|
:return: The converted value.
|
|
|
|
|
:rtype: float
|
2020-08-08 16:31:27 +10:00
|
|
|
"""
|
|
|
|
|
if from_unit in si_conversions:
|
|
|
|
|
value *= si_conversions[from_unit]
|
|
|
|
|
elif from_prefix:
|
|
|
|
|
value *= get_prefix_multiplier(from_prefix)
|
2020-11-01 20:08:27 +07:00
|
|
|
if "SQUARE" in from_unit:
|
2020-08-08 16:31:27 +10:00
|
|
|
value *= get_prefix_multiplier(from_prefix)
|
2020-11-01 20:08:27 +07:00
|
|
|
elif "CUBIC" in from_unit:
|
2020-08-08 16:31:27 +10:00
|
|
|
value *= get_prefix_multiplier(from_prefix)
|
|
|
|
|
value *= get_prefix_multiplier(from_prefix)
|
|
|
|
|
if to_unit in si_conversions:
|
|
|
|
|
return value * (1 / si_conversions[to_unit])
|
|
|
|
|
elif to_prefix:
|
2020-11-01 20:08:27 +07:00
|
|
|
value *= 1 / get_prefix_multiplier(to_prefix)
|
|
|
|
|
if "SQUARE" in from_unit:
|
|
|
|
|
value *= 1 / get_prefix_multiplier(to_prefix)
|
|
|
|
|
elif "CUBIC" in from_unit:
|
|
|
|
|
value *= 1 / get_prefix_multiplier(to_prefix)
|
|
|
|
|
value *= 1 / get_prefix_multiplier(to_prefix)
|
2020-08-08 16:31:27 +10:00
|
|
|
return value
|
2021-01-02 19:33:32 +11:00
|
|
|
|
|
|
|
|
|
2021-08-13 16:18:05 +10:00
|
|
|
def calculate_unit_scale(file):
|
|
|
|
|
"""Returns a unit scale factor to convert to and from IFC project length units and SI meters
|
2021-07-06 12:20:22 +10:00
|
|
|
|
2023-01-10 10:16:28 +11:00
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
.. code:: python
|
2021-08-06 15:48:07 +10:00
|
|
|
|
2021-08-13 16:18:05 +10:00
|
|
|
ifc_project_length * unit_scale = si_meters
|
|
|
|
|
si_meters / unit_scale = ifc_project_length
|
2021-08-06 15:48:07 +10:00
|
|
|
|
2021-08-13 16:18:05 +10:00
|
|
|
:returns: The scale factor
|
|
|
|
|
:rtype: float
|
|
|
|
|
"""
|
2022-10-15 11:06:09 +11:00
|
|
|
if not file.by_type("IfcUnitAssignment"):
|
|
|
|
|
return 1
|
2021-01-02 19:33:32 +11:00
|
|
|
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
|