0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IfcOpenShellで逆属性を取得する

Last updated at Posted at 2023-11-24

はじめに

IfcOpenShellはBIMの標準フォーマットであるIFCファイルを扱うライブラリだよ。
そのIfcOpenShellで逆属性を取得するよ!!

「逆属性」 = 「inverse attribute」だよ!

使用したIfcOpenShellはバージョンは 0.7.0です。

結論

早く回答ください(・Д・)っ/凵 ⌒☆チン チン
っていう人のために結果のコードだよ!

import ifcopenshell
model = ifcopenshell.open(path)
item = model.by_type('IfcWall')[0]

# これだよ!!( ゚∀゚)o彡°
inv_keys = (
    set(dir(item))
    - set(dir(ifcopenshell.entity_instance))
    - set(item.get_info().keys())
)
inverse_attributes = { key: getattr(item, key) for key in inv_keys }

そもそも逆属性って何?

IFCを構成する各要素(IfcWallとかIfcSlabとかエンティティって呼ばれている要素たち)は、それぞれ属性を持っているよ。
たとえばIfcSlabだとGlobalIdPredefinedTypeまでの9個の属性があるね。IfcOpenShellで表示してみると下記のような感じだね。
あ、データはBlenderBIMのドキュメントページにあるIFCファイルを使用しているよ。

item = model.by_type('IfcSlab')[0]
item.get_info()
# {'id': 34509,
#  'type': 'IfcSlab',
#  'GlobalId': '1pPHnf7cXCpPsNEnQf8_6B',
#  'OwnerHistory': #12=IfcOwnerHistory(#7,#11,$,.ADDED.,$,$,$,1482339244),
#  'Name': 'Bodenplatte',
#  'Description': None,
#  'ObjectType': None,
#  'ObjectPlacement': #34464=IfcLocalPlacement(#477,#34463),
#  'Representation': #34505=IfcProductDefinitionShape($,$,(#34495,#34503)),
#  'Tag': 'E4D9CD4B-CA43-4735-94-BD-1FD4376BD455',
#  'PredefinedType': 'BASESLAB'}

じゃあ"逆"属性は?っていうとIFCのドキュメントのAttributesの表を見ると上記の9個の属性以外にもHasAssignmentsとかIsDeclaredByとかみたいに#列に数字のついていない属性があるよね?これを逆属性とここでは呼んでいるよ。

逆属性.PNG

え?それってget_inverseで取得できるよね?

IfcOpenShellにget_inverseって関数あるからそれ使えばいいんじゃないの?って思うよね。確かにそれで逆属性も取得できるんだよね。

model.get_inverse(item)
# {#34531=IfcRelAssociatesMaterial('22HuPqu8qBpALMZnyfqhUK',#12,$,$,(#34509),#34530),
#  #14517=IfcRelContainedInSpatialStructure('13J1BKcWxmCqHLM0nJ4nFJ',#12,$,$,(#14502,#15042,#15372,#15848,#16507,#16982,#17040,#17468,#18407,#18465,#18698,#19199,#19504,#20069,#20268,#20299,#20329,#20374,#20598,#20808,#21966,#23024,#23944,#27013,#27421,#27833,#28113,#31079,#31470,#31818,#32098,#32407,#32829,#33109,#33389,#33672,#34509,#35053),#479),
#  #34613=IfcRelDefinesByProperties('0NBjOOE7bx9S$iH5ujkO2m',#12,$,$,(#34509),#34611),
#  #34602=IfcRelDefinesByProperties('0tDKLSbXIVoh$W$V5W$PwA',#12,$,$,(#34509),#34575),
#  #34539=IfcRelDefinesByProperties('1B6RMRVlt73_0vPr1dONZL',#12,$,$,(#34509),#34536),
#  #34547=IfcRelDefinesByProperties('1ce7nQALQ4Uw9kMrk75YsH',#12,$,$,(#34509),#34544),
#  #34645=IfcRelDefinesByProperties('2Gsr2AVG5$zczeHEInpXFk',#12,$,$,(#34509),#34643),
#  #34661=IfcRelDefinesByType('3jE0353rdOAedUCYpD$jiH',#12,$,$,(#34509),#34648),
#  #77456=IfcRelSpaceBoundary('0wxqSYU9XfzU3VLFEoAGQ9',#12,'2ndLevel','2a',#33774,#34509,#77455,.PHYSICAL.,.EXTERNAL.),
#  #78243=IfcRelSpaceBoundary('1Wo7jfPGGvdgmZeiVZPQDp',#12,'2ndLevel','2a',#34763,#34509,#78242,.PHYSICAL.,.EXTERNAL.),
#  #78041=IfcRelSpaceBoundary('29sJT6Ho2mKZovzttaNM5H',#12,'2ndLevel','2a',#34191,#34509,#78040,.PHYSICAL.,.EXTERNAL.),
#  #77144=IfcRelSpaceBoundary('2Sn7uAlFxA$POrax5b6wqo',#12,'2ndLevel','2a',#21640,#34509,#77143,.PHYSICAL.,.EXTERNAL.),
#  #76656=IfcRelSpaceBoundary('2daBdKK14H6N6ekhTDweXH',#12,'2ndLevel','2a',#20909,#34509,#76655,.PHYSICAL.,.EXTERNAL.),
#  #76945=IfcRelSpaceBoundary('2weIpzIiCR4kwbSmcs97PG',#12,'2ndLevel','2a',#21283,#34509,#76944,.PHYSICAL.,.EXTERNAL.)}

ただこれ、正確には対象のエンティティを参照している要素すべてが取得されるんだよね。どういうことかというと、ドキュメントに記載されていない属性も実は取得していたりする。

どういうことかわかりにくいので例を上げてみる。

IfcApplicationについてのget_inverseの結果は以下のようにIfcOwnerHistoryが取得される。でもIFCのドキュメントをみるとIfcApplicationに逆属性はない。
逆にIfcOwnerHistoryの属性をget_infoで確認するとIfcApplicationが存在している。
つまりget_inverseはドキュメント上の逆属性を取得しているのではなく、通常の属性として参照されているエンティティ全てが取得されていると思われる。

IFCファイルの中身でいえば、
#12= IFCOWNERHISTORY(#7,#11,$,.ADDED.,$,$,$,1482339244);
のカッコ内に#11が含まれているので#12get_inverseで取得されたということになる。

item = model.by_type('IfcApplication')[0]
model.get_inverse(item)
# {#12=IfcOwnerHistory(#7,#11,$,.ADDED.,$,$,$,1482339244)}

# `IfcOwnerHistory`の属性
item = model.by_id(12)
item.get_info()
# {'id': 12,
#  'type': 'IfcOwnerHistory',
#  'OwningUser': #7=IfcPersonAndOrganization(#1,#3,$),
#  'OwningApplication': #11=IfcApplication(#10,'20.0.0','ARCHICAD-64','IFC2x3 add-on version: 4009 GER FULL'),
#  'State': None,
#  'ChangeAction': 'ADDED',
#  'LastModifiedDate': None,
#  'LastModifyingUser': None,
#  'LastModifyingApplication': None,
#  'CreationDate': 1482339244}

つまりどういうことだってばよ

つまりはget_inverseだと逆属性以外も取得してしまうんだよ。逆属性のみ取得したい場合には適さないんだよね。あと属性名が取得できないんだよね。

ではどうやって逆属性のみを取得すればいいのか

IfcOpenShellでは逆属性のみを直接取得する関数はなさそう。
(あったら教えてください!!!!)

でもIfcOpenShellのデータ定義上では逆属性も定義されている。

item = model.by_type('IfcSlab')[0]
dir(item)
# ['ConnectedFrom',
#  'ConnectedTo',
#  'ContainedInStructure',
#  'Declares',
#  'Decomposes',
#  'Description',
#  'FillsVoids',
#  'GlobalId',
#  'HasAssignments',
#  'HasAssociations',
#  'HasContext',
#  'HasCoverings',
#  'HasOpenings',
#  'HasProjections',
#  'InterferesElements',
#  'IsConnectionRealization',
#  'IsDeclaredBy',
#  'IsDecomposedBy',
#  'IsDefinedBy',
#  'IsInterferedByElements',
#  'IsNestedBy',
#  'IsTypedBy',
#  'Name',
#  'Nests',
#  'ObjectPlacement',
#  'ObjectType',
#  'OwnerHistory',
#  'PredefinedType',
#  'ProvidesBoundaries',
#  'ReferencedBy',
#  'ReferencedInStructures',
#  'Representation',
#  'Tag',
#  ...

そして全てのエンティティは ifcopenshell.entity_instance を継承している。なおかつ明示属性はget_infoで取得できる。
そのため、逆属性を取得したいエンティティのプロパティから、継承元の共通プロパティを削除し、さらに明示属性も削除すると逆属性が残ることになる。

# 逆属性の属性名を取得
inv_keys = (
    set(dir(item))
    - set(dir(ifcopenshell.entity_instance))
    - set(item.get_info().keys())
)
# {'ConnectedFrom',
#  'ConnectedTo',
#  'ContainedInStructure',
#  'Declares',
#  'Decomposes',
#  'FillsVoids',
#  'HasAssignments',
#  'HasAssociations',
#  'HasContext',
#  'HasCoverings',
#  'HasOpenings',
#  'HasProjections',
#  'InterferesElements',
#  'IsConnectionRealization',
#  'IsDeclaredBy',
#  'IsDecomposedBy',
#  'IsDefinedBy',
#  'IsInterferedByElements',
#  'IsNestedBy',
#  'IsTypedBy',
#  'Nests',
#  'ProvidesBoundaries',
#  'ReferencedBy',
#  'ReferencedInStructures'}

逆属性の属性名が取得できたので、あとはgetattrで値を取得するだけ。

inverse_attributes = { key: getattr(item, key) for key in inv_keys }
# {'IsDecomposedBy': (),
#  'IsDefinedBy': (#34539=IfcRelDefinesByProperties('1B6RMRVlt73_0vPr1dONZL',#12,$,$,(#34509),#34536),
#   #34547=IfcRelDefinesByProperties('1ce7nQALQ4Uw9kMrk75YsH',#12,$,$,(#34509),#34544),
#   #34602=IfcRelDefinesByProperties('0tDKLSbXIVoh$W$V5W$PwA',#12,$,$,(#34509),#34575),
#   #34613=IfcRelDefinesByProperties('0NBjOOE7bx9S$iH5ujkO2m',#12,$,$,(#34509),#34611),
#   #34645=IfcRelDefinesByProperties('2Gsr2AVG5$zczeHEInpXFk',#12,$,$,(#34509),#34643)),
#  'IsDeclaredBy': (),
#  'HasOpenings': (),
#  'IsTypedBy': (#34661=IfcRelDefinesByType('3jE0353rdOAedUCYpD$jiH',#12,$,$,(#34509),#34648),),
#  'HasAssignments': (),
#  'Decomposes': (),
#  'IsInterferedByElements': (),
#  'HasProjections': (),
#  'HasCoverings': (),
#  'ConnectedFrom': (),
#  'IsConnectionRealization': (),
#  'IsNestedBy': (),
#  'Nests': (),
# ...
#   #78041=IfcRelSpaceBoundary('29sJT6Ho2mKZovzttaNM5H',#12,'2ndLevel','2a',#34191,#34509,#78040,.PHYSICAL.,.EXTERNAL.),
#   #78243=IfcRelSpaceBoundary('1Wo7jfPGGvdgmZeiVZPQDp',#12,'2ndLevel','2a',#34763,#34509,#78242,.PHYSICAL.,.EXTERNAL.)),
#  'ConnectedTo': (),
#  'ReferencedBy': (),
#  'Declares': ()}

まとめ

IfcOpenShellでの逆属性の取得方法でした。

これ需要あるの?(というか需要がないからIfcOpenShellで実装されてないんじゃ...?)と思いつつ、下記みたいなことしたくて必要だったのでまとめました。

そして途中で口調に飽きて普通の文章になりました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?