ezdxfを使ってみよう #4~実践・DXF読込編~【U型トラフの規格別自動作図とpandasによるデータ抽出】

ezdxfを使ってみよう #4~実践・DXF読込編~【U型トラフの規格別自動作図とpandasによるデータ抽出】
  • URLをコピーしました!

#1~#3で、図形の描画・レイヤーや色などの属性・寸法線の描き方を一通りまとめました。今回はその集大成として、実際の構造物を規格違いでまとめて自動作図してみます。後半では、作った図面を今度はezdxf側で読み込んで、図形データをpandasでExcelに書き出す方法も解説します。

前回(第3回)の内容はこちらです。

目次

【作図実践】今回作るもの

土木・造園の現場でおなじみのU形トラフ(U字溝)を題材にします。
JIS A 5372で規格化されたサイズごとに寸法が微妙に異なりますが、1つのコードに規格値を辞書で渡すだけで、まとめて作図できるようにします。

JIS A 5372 U形トラフを規格別に自動作図した例

参考:JIS A 5372 上ぶた式U形側溝・蓋の規格表(三和コンクリート工業株式会社)

150・240・360Aの3規格を例に、同じロジックで寸法もハッチングも規格ごとに自動生成しています。パラメータを増やせば、JIS規格の13サイズ全てを同様に量産できます。

前提事項

今回はこれまでの描画メソッドの総復習を兼ねるため、座標の管理方法は必ずしも合理的ではありません。また、正確な形状の再現には三角関数が必要になる箇所がありますが、可読性を優先してその部分は簡略化し、寸法が入っていない辺があります。

つかだ

あくまで「Pythonで規格違いの構造物を同じコードから量産できる」という考え方のサンプルです。実務で使う際は、必要な精度・法規に応じて座標計算を作り直してください。

規格パラメータを辞書で管理する

U形トラフの各規格値(全幅A・全高H・内幅a・内高h・厚みなど)を辞書にまとめ、リストに格納します。

import ezdxf
from ezdxf.addons.drawing import RenderContext, Frontend
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
from ezdxf.enums import TextEntityAlignment
import matplotlib.pyplot as plt

doc = ezdxf.new("R2010", setup=True)
msp = doc.modelspace()

# JIS A 5372 に基づくU形トラフの規格値(一部抜粋)
traf_150 = {'name': "150", 'A':210,'H':185,'a':150,'h':150,'b':140,'c':35,'d':30,'e':35,'g':160,'r':30}
traf_240 = {'name': "240", 'A':330,'H':290,'a':240,'h':240,'b':220,'c':50,'d':45,'e':50,'g':240,'r':50}
traf_360A= {'name': "360A",'A':460,'H':365,'a':360,'h':300,'b':310,'c':65,'d':50,'e':65,'g':360,'r':50}
trafs = [traf_150, traf_240, traf_360A]

for layer in ["structure", "dimension", "text", "frame"]:
    doc.layers.add(name=layer)

実際のJIS A 5372には150〜600まで13サイズありますが、記事では3サイズに絞って動作を確認します。trafsのリストに辞書を追加するだけで、他の規格もそのまま増やせます。

座標を計算して図形を描く

規格値から各頂点の座標を計算し、直線と円弧で断面形状を描きます。x = 500 * iで規格ごとに横にずらして並べているだけで、描画ロジック自体は共通です。

for i, traf in enumerate(trafs):
    x = 500 * i
    y = 0

    A_point = (x, traf['H']+y)
    B_point = (traf['d']+x, A_point[1])
    C_center = (traf['e']+traf['r']+x, traf['c']+traf['r']+y)
    D_point = (traf['A']+x, A_point[1])
    E_point = (traf['A']-traf['d']+x, A_point[1])
    F_center = (traf['A']-traf['e']-traf['r']+x, C_center[1])
    G_point = ((traf['A']-traf['g'])/2+x, y)
    H_point = (G_point[0]+traf['g'], y)
    I_point = (x, (traf['A']-traf['g'])/2+y)
    J_point = (traf['A']+x, I_point[1])

    arc_C = msp.add_arc(center=C_center, radius=traf['r'], start_angle=180, end_angle=270, dxfattribs={'layer': 'structure'})
    arc_F = msp.add_arc(center=F_center, radius=traf['r'], start_angle=270, end_angle=360, dxfattribs={'layer': 'structure'})

    def line_add(p1, p2):
        return msp.add_line(p1, p2, dxfattribs={'layer': 'structure'})

    line_AB = line_add(A_point, B_point)
    line_DE = line_add(E_point, D_point)
    line_GH = line_add(G_point, H_point)
    line_GI = line_add(I_point, G_point)
    line_AI = line_add(I_point, A_point)
    line_HJ = line_add(H_point, J_point)
    line_DJ = line_add(J_point, D_point)

円弧の始点・終点はarc_C.start_pointのように作成済みの図形から直接座標を取得できます。自分で三角関数を計算し直す必要がないので、地味に便利です。

寸法線とハッチングを追加する

寸法線は第3回で解説したAligned Dimensionを使います。ここで1つ、実際に検証していて気づいた注意点があります。

override = {"dimtxsty": "Standard", "dimtxt": 15, "dimclrt": 0, "dimtad": 1, "dimexo": 2, "dimlfac": 1}

def aligned_dim(p1, p2, dis):
    msp.add_aligned_dim(p1=p1, p2=p2, distance=dis, dimstyle="EZDXF",
        override=override, dxfattribs={'layer': 'dimension'}).render()

aligned_dim(line_AB.dxf.start, line_AB.dxf.end, 30)
aligned_dim(line_DE.dxf.start, line_DE.dxf.end, 30)
aligned_dim(line_AB.dxf.end, line_DE.dxf.start, 30)
aligned_dim(line_AB.dxf.start, line_DE.dxf.end, 60)

注意点:標準の"EZDXF"dimstyleは、そのまま使うと寸法値が実際の座標の100倍で表示されます(210mmの辺が「21000」と表示される)。override"dimlfac": 1を加えることで、座標そのままの値を表示できます。

ハッチングは第2回で紹介したエッジパス(add_edge_path)で、直線と円弧を交互につないで断面形状を塗りつぶします。

patterns = ['ANSI31', 'ANSI32', 'ANSI33']

hatch = msp.add_hatch()
edge_path = hatch.paths.add_edge_path()
edge_path.add_line(A_point, B_point)
edge_path.add_line(B_point, arc_C.start_point)
edge_path.add_arc(arc_C.dxf.center, arc_C.dxf.radius,
    start_angle=arc_C.dxf.start_angle, end_angle=arc_C.dxf.end_angle, ccw=False)
edge_path.add_line(arc_C.end_point, arc_F.start_point)
edge_path.add_arc(arc_F.dxf.center, arc_F.dxf.radius,
    start_angle=arc_F.dxf.start_angle, end_angle=arc_F.dxf.end_angle, ccw=False)
edge_path.add_line(E_point, arc_F.end_point)
edge_path.add_line(E_point, D_point)
edge_path.add_line(J_point, D_point)
edge_path.add_line(H_point, J_point)
edge_path.add_line(G_point, H_point)
edge_path.add_line(I_point, G_point)
edge_path.add_line(I_point, A_point)
hatch.set_pattern_fill(patterns[i], scale=0.5, color=i+1)

文字ラベルを配置する

規格名を第2回で解説したset_placement()で配置します。

msp.add_text(
    'JIS A 5372 ' + traf['name'],
    dxfattribs={'height': 15, 'color': 4, 'layer': 'text'}
).set_placement((traf['A']/2+x, traf['H']+95+y), align=TextEntityAlignment.TOP_CENTER)

オリジナルのコード(3年以上前に書いたもの)では.set_pos()を使っていましたが、現在のezdxfでは.set_placement()に置き換わっています。バージョン差異については第2回で詳しく解説しています。

実行結果

ここまでのコードを規格3種でforループを回すと、冒頭の画像のように規格ごとに寸法とハッチングが自動でついた断面図ができあがります。パラメータを増やせば、13規格すべてを同じコードで量産できます。

上記コードを統合版し、13種類のトラフを作成するサンプルコード

手作業なら規格の数だけ図面を描き直す必要がありますが、自動作図なら辞書に規格値を1行足すだけで済みます。ここにPythonで作図する一番のメリットがあります。

ここまでの13規格版のコードをまとめて、そのままダウンロードできる形にしました。

【DXF読込】作った図面をezdxfで読み込む

ここまでは「作る」側でしたが、ezdxfは既存のDXFを読み込んで図形情報を取り出すこともできます。ここでは、先ほど作ったDXFを読み込んで、図形の座標や種類をpandasでExcelに書き出す方法をまとめます。

DXFファイルの読み込み

作図とは別に、読み込み専用のライブラリとしてpandasを使います。ExcelやCSVへの書き出しに便利なので、ここで導入します。

読み込む対象は、記事の前半で作った3規格版のDXF(traf_sample.dxf)です。保存部分だけファイル名を変えています。

doc.saveas('traf_sample.dxf')

fig = plt.figure(figsize=(10, 4.2), dpi=140)
ax = fig.add_axes([0, 0, 1, 1])
ctx = RenderContext(doc)
out = MatplotlibBackend(ax)
Frontend(ctx, out).draw_layout(msp, finalize=True)
fig.savefig('traf_sample.png')
traf_sample.dxfの実行結果

このDXFファイルはそのままダウンロードできます。手元で読み込みを試したい方はどうぞ。

import ezdxf
import pandas as pd

dxf = ezdxf.readfile('traf_sample.dxf')  # 読み込むDXFのパス
msp = dxf.modelspace()

図形情報の取得(query)

読み込んだモデルスペースに対して.query()を使うと、指定した種類の図形だけをまとめて取得できます。
図形の種類(LINE・ARC・TEXTなど)はすべて大文字で指定する決まりです。

lines = [entity.dxfattribs() for entity in msp.query('LINE')]
arcs  = [entity.dxfattribs() for entity in msp.query('ARC')]
texts = [entity.dxfattribs() for entity in msp.query('TEXT')]

参考:公式ドキュメント(Query Language)

pandasでデータフレーム化

取得した図形属性のリストを、そのままpd.DataFrame()に渡すだけでデータフレームになります。ここでは「Excelに変換するための道具」くらいの理解で問題ありません。

df_line = pd.DataFrame(lines)
df_arc  = pd.DataFrame(arcs)
df_text = pd.DataFrame(texts)

print(df_text[['layer', 'text', 'height']])

実際に先ほどのU形トラフ3規格分のDXFを読み込むと、次のように取得できます。

layertextheight
textJIS A 5372 15015.0
textJIS A 5372 24015.0
textJIS A 5372 360A15.0

同じDXFからLINEは30件、ARCは6件取得できました。図形の数が多いDXFでも、種類ごとに一括で情報を吸い出せるのがこの方法の強みです。

Excelに保存する

pd.ExcelWriterを使うと、1つのブックに図形種類ごとのシートをまとめて保存できます。

with pd.ExcelWriter('dxf_excel.xlsx') as writer:
    df_line.to_excel(writer, sheet_name='line')
    df_text.to_excel(writer, sheet_name='text')
    df_arc.to_excel(writer, sheet_name='arc')

LINEエンティティのみ抽出したシート

TEXTエンティティのみ抽出したシート

ARCエンティティのみ抽出したシート

これで、DXFの中身をExcelで一覧確認できるようになりました。逆に言えば、Excelの表を整えてezdxfに読み込ませれば、座標データから自動作図することもできるということです。#1〜#4で扱った作図メソッドと組み合わせれば、「Excel入力→自動作図」の仕組みも作れます。

まとめ

今回は、これまでの内容を組み合わせた実践編として、規格違いの構造物をまとめて自動作図する方法と、逆にDXFを読み込んでデータ化する方法をまとめました。

  • 規格値を辞書+リストで管理すれば、同じコードで規格違いをまとめて作図できる
  • 円弧の始点・終点は作成済み図形から直接取得すると計算が楽
  • 標準の“EZDXF”dimstyleは寸法値が100倍表示される。"dimlfac": 1で解消できる
  • 図形の読み込みはmodelspace.query('LINE')のように大文字で指定する
  • 取得した属性リストはpandasでそのままデータフレーム化でき、Excelにも簡単に書き出せる

これで「ezdxfを使ってみよう」シリーズは、導入から実践・データ読み込みまで一通り完結です。ここまでの内容を組み合わせれば、Excelの座標データから自動作図する仕組みも自分で作れるはずです。

応用編として、ここまでの内容を使って2つのDXFの差分を自動で色分け判定するツールも作ってみました。

この記事は、以前Qiitaに投稿した内容を書き直したものです。元記事はこちらです。

シリーズ記事一覧

「ezdxfを使ってみよう」シリーズの全記事です。

よかったらシェアしてね!
  • URLをコピーしました!

コメントお待ちしてます!

コメントする

CAPTCHA


目次