Skip to content

Commit 9f01f90

Browse files
committed
Removed dependency on subprocess and ifcconvert
1 parent a4d935f commit 9f01f90

File tree

4 files changed

+345
-420
lines changed

4 files changed

+345
-420
lines changed

ifc2json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
with open(jsonFilePath, 'w') as outfile:
6464
json.dump(jsonData, outfile, indent=indent)
6565
elif args.v == "5a":
66-
jsonData = ifcjson.IFC2JSON5a(ifcFilePath).spf2Json()
66+
jsonData = ifcjson.IFC2JSON5a(ifcFilePath,compact).spf2Json()
6767
with open(jsonFilePath, 'w') as outfile:
6868
json.dump(jsonData, outfile, indent=indent)
6969
else:

ifcjson/common.py

Lines changed: 133 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,140 @@
2323
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424
# SOFTWARE.
2525

26-
def toLowerCamelcase(string):
27-
"""Convert string from upper to lower camelCase"""
26+
import ifcopenshell
27+
import ifcopenshell.guid as guid
2828

29-
return string[0].lower() + string[1:]
3029

30+
class IFC2JSON:
31+
"""Base class for all IFC SPF to IFC.JSON writers
32+
"""
3133

32-
def createReferenceObject(objectDict, compact=False):
33-
"""Returns value for referenced object"""
34-
ref = {}
35-
if not compact:
36-
ref['type'] = objectDict['type']
37-
ref['ref'] = objectDict['globalId']
38-
return ref
34+
DIMENSIONALEXPONENTS = {
35+
'METRE': (1, 0, 0, 0, 0, 0, 0),
36+
'SQUARE_METRE': (2, 0, 0, 0, 0, 0, 0),
37+
'CUBIC_METRE': (3, 0, 0, 0, 0, 0, 0),
38+
'GRAM': (0, 1, 0, 0, 0, 0, 0),
39+
'SECOND': (0, 0, 1, 0, 0, 0, 0),
40+
'AMPERE': (0, 0, 0, 1, 0, 0, 0),
41+
'KELVIN': (0, 0, 0, 0, 1, 0, 0),
42+
'MOLE': (0, 0, 0, 0, 0, 1, 0),
43+
'CANDELA': (0, 0, 0, 0, 0, 0, 1),
44+
'RADIAN': (0, 0, 0, 0, 0, 0, 0),
45+
'STERADIAN': (0, 0, 0, 0, 0, 0, 0),
46+
'HERTZ': (0, 0, -1, 0, 0, 0, 0),
47+
'NEWTON': (1, 1, -2, 0, 0, 0, 0),
48+
'PASCAL': (-1, 1, -2, 0, 0, 0, 0),
49+
'JOULE': (2, 1, -2, 0, 0, 0, 0),
50+
'WATT': (2, 1, -3, 0, 0, 0, 0),
51+
'COULOMB': (0, 0, 1, 1, 0, 0, 0),
52+
'VOLT': (2, 1, -3, -1, 0, 0, 0),
53+
'FARAD': (-2, -1, 4, 2, 0, 0, 0),
54+
'OHM': (2, 1, -3, -2, 0, 0, 0),
55+
'SIEMENS': (-2, -1, 3, 2, 0, 0, 0),
56+
'WEBER': (2, 1, -2, -1, 0, 0, 0),
57+
'TESLA': (0, 1, -2, -1, 0, 0, 0),
58+
'HENRY': (2, 1, -2, -2, 0, 0, 0),
59+
'DEGREE_CELSIUS': (0, 0, 0, 0, 1, 0, 0),
60+
'LUMEN': (0, 0, 0, 0, 0, 0, 1),
61+
'LUX': (-2, 0, 0, 0, 0, 0, 1),
62+
'BECQUEREL': (0, 0, -1, 0, 0, 0, 0),
63+
'GRAY': (2, 0, -2, 0, 0, 0, 0),
64+
'SIEVERT': (2, 0, -2, 0, 0, 0, 0),
65+
'OTHERWISE': (0, 0, 0, 0, 0, 0, 0)
66+
}
3967

40-
# More compact alternative
41-
# return objectDict['globalId']
68+
def toLowerCamelcase(self, string):
69+
"""Convert string from upper to lower camelCase"""
70+
71+
return string[0].lower() + string[1:]
72+
73+
def getDimensionsForSiUnit(self, entity):
74+
dimensions = {}
75+
if entity.Name in self.DIMENSIONALEXPONENTS:
76+
dimExps = self.DIMENSIONALEXPONENTS[entity.Name]
77+
if dimExps[0] != 0:
78+
dimensions['LengthExponent'] = dimExps[0]
79+
if dimExps[1] != 0:
80+
dimensions['MassExponent'] = dimExps[1]
81+
if dimExps[2] != 0:
82+
dimensions['TimeExponent'] = dimExps[2]
83+
if dimExps[3] != 0:
84+
dimensions['ElectricCurrentExponent'] = dimExps[3]
85+
if dimExps[4] != 0:
86+
dimensions['ThermodynamicTemperatureExponent'] = dimExps[4]
87+
if dimExps[5] != 0:
88+
dimensions['AmountOfSubstanceExponent'] = dimExps[5]
89+
if dimExps[6] != 0:
90+
dimensions['LuminousIntensityExponent'] = dimExps[6]
91+
92+
return dimensions
93+
94+
def getAttributeValue(self, value):
95+
"""Recursive method that walks through all nested objects of an attribute
96+
and returns a IFC.JSON-4 model structure
97+
98+
Parameters:
99+
value
100+
101+
Returns:
102+
attribute data converted to IFC.JSON-4 model structure
103+
104+
"""
105+
if value == None or value == '':
106+
jsonValue = None
107+
elif isinstance(value, ifcopenshell.entity_instance):
108+
entity = value
109+
entityAttributes = entity.__dict__
110+
111+
# Remove empty properties
112+
if entity.is_a() == 'IfcPropertySingleValue':
113+
try:
114+
value = entity.NominalValue.wrappedValue
115+
if not value:
116+
return None
117+
except:
118+
return None
119+
120+
# Add unit dimensions https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcdimensionsforsiunit.htm
121+
if entity.is_a() == 'IfcSIUnit':
122+
entityAttributes['dimensions'] = self.getDimensionsForSiUnit(
123+
entity)
124+
125+
# All objects with a GlobalId must be referenced, all others nested
126+
if entity.id() in self.rootobjects:
127+
entityAttributes["GlobalId"] = self.rootobjects[entity.id()]
128+
return self.createReferenceObject(entityAttributes, self.compact)
129+
else:
130+
if 'GlobalId' in entityAttributes:
131+
entityAttributes["GlobalId"] = guid.split(
132+
guid.expand(entity.GlobalId))[1:-1]
133+
134+
return self.createFullObject(entityAttributes)
135+
elif isinstance(value, tuple):
136+
jsonValue = None
137+
subEnts = []
138+
for subEntity in value:
139+
attrValue = self.getAttributeValue(subEntity)
140+
if attrValue:
141+
subEnts.append(attrValue)
142+
jsonValue = subEnts
143+
else:
144+
jsonValue = value
145+
return jsonValue
146+
147+
def createReferenceObject(self, entityAttributes, compact=False):
148+
"""Returns object reference
149+
150+
Parameters:
151+
entityAttributes (dict): Dictionary of IFC object data
152+
compact (boolean): verbose or non verbose IFC.JSON-4 output
153+
154+
Returns:
155+
dict: object containing reference to another object
156+
157+
"""
158+
ref = {}
159+
if not compact:
160+
ref['type'] = entityAttributes['type']
161+
ref['ref'] = entityAttributes['GlobalId']
162+
return ref

ifcjson/ifc2json4.py

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,16 @@
2626

2727
import os
2828
import uuid
29-
import functools
3029
import ifcopenshell
3130
import ifcopenshell.guid as guid
3231
import ifcjson.common as common
3332
from datetime import datetime
3433
from ifcopenshell.entity_instance import entity_instance
3534

3635

37-
class IFC2JSON4:
38-
maxCache = 2048
39-
36+
class IFC2JSON4(common.IFC2JSON):
4037
def __init__(self, ifcModel, compact=False):
41-
"""IFC SPF file to IFC.JSON-4 writer
38+
"""IFC SPF to IFC.JSON-4 writer
4239
4340
parameters:
4441
ifcModel: IFC filePath or ifcopenshell model instance
@@ -89,75 +86,23 @@ def spf2Json(self):
8986
if not entityType in ['IfcGeometricRepresentationContext', 'IfcOwnerHistory']:
9087
for attr in entity.wrapped_data.get_inverse_attribute_names():
9188
inverseAttribute = getattr(entity, attr)
92-
entityAttributes[attr] = self.getAttributeValue(
93-
inverseAttribute)
89+
attrValue = self.getAttributeValue(inverseAttribute)
90+
if attrValue:
91+
entityAttributes[attr] = attrValue
92+
else:
93+
continue
9494

9595
entityAttributes["GlobalId"] = self.rootobjects[entity.id()]
9696
jsonObjects.append(self.createFullObject(entityAttributes))
9797

9898
return {
99-
'fileSchema': 'IFC.JSON4',
99+
'fileSchema': 'IFC.JSON-4',
100100
'originatingSystem': 'IFC2JSON_python',
101101
'timeStamp': datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
102102
'data': jsonObjects
103103
}
104104

105-
@functools.lru_cache(maxsize=maxCache)
106-
def getAttributeValue(self, value):
107-
"""Recursive method that walks through all nested objects of an attribute
108-
and returns a IFC.JSON-4 model structure
109-
110-
Parameters:
111-
value
112-
113-
Returns:
114-
attribute data converted to IFC.JSON-4 model structure
115-
116-
"""
117-
if value == None or value == '':
118-
jsonValue = None
119-
elif isinstance(value, ifcopenshell.entity_instance):
120-
entity = value
121-
entityAttributes = entity.__dict__
122-
123-
# All objects with a GlobalId must be referenced, all others nested
124-
if entity.id() in self.rootobjects:
125-
entityAttributes["GlobalId"] = self.rootobjects[entity.id()]
126-
return self.createReferenceObject(entityAttributes, self.compact)
127-
else:
128-
if 'GlobalId' in entityAttributes:
129-
entityAttributes["GlobalId"] = guid.split(
130-
guid.expand(entity.GlobalId))[1:-1]
131-
return self.createFullObject(entityAttributes)
132-
elif isinstance(value, tuple):
133-
jsonValue = None
134-
subEnts = []
135-
for subEntity in value:
136-
subEnts.append(self.getAttributeValue(subEntity))
137-
jsonValue = subEnts
138-
else:
139-
jsonValue = value
140-
return jsonValue
141-
142-
@functools.lru_cache(maxsize=maxCache)
143-
def createReferenceObject(self, entityAttributes, compact=False):
144-
"""Returns object reference
145-
146-
Parameters:
147-
entityAttributes (dict): Dictionary of IFC object data
148-
compact (boolean): verbose or non verbose IFC.JSON-4 output
149-
150-
Returns:
151-
dict: object containing reference to another object
152-
153-
"""
154-
ref = {}
155-
if not compact:
156-
ref['type'] = entityAttributes['type']
157-
ref['ref'] = entityAttributes['GlobalId']
158-
return ref
159105

160-
@functools.lru_cache(maxsize=maxCache)
161106
def createFullObject(self, entityAttributes):
162107
"""Returns complete IFC.JSON-4 object
163108
@@ -168,16 +113,20 @@ def createFullObject(self, entityAttributes):
168113
dict: containing complete IFC.JSON-4 object
169114
170115
"""
171-
entityType = entityAttributes['type']
172116
fullObject = {}
173117

174118
for attr in entityAttributes:
175-
attrKey = common.toLowerCamelcase(attr)
176119

177120
# Line numbers are not part of IFC JSON
178121
if attr == 'id':
179122
continue
180123

124+
attrKey = self.toLowerCamelcase(attr)
125+
126+
# Replace wrappedvalue key names to value
127+
if attrKey == 'wrappedValue':
128+
attrKey = 'value'
129+
181130
jsonValue = self.getAttributeValue(entityAttributes[attr])
182131
if jsonValue:
183132
fullObject[attrKey] = jsonValue

0 commit comments

Comments
 (0)