IU Tips

Plateauデータを使って市街モデルを作る③ Archicad オブジェクト編

2024.12.24

こんにちは。
IU BIM STUDIOの原田です。

前回、Plateauのデータを使ってGrasshopperで市街モデルを作成しました。
今回は、同じJSONデータを流用してArchicadで市街モデルを作成したいと思います。

前回までの記事はこちらをご覧ください。

Archicadでプログラミング活用

Archicadでプログラミングを活用するには3つの方法があります。

それは
・Archicadアドオン
・Pythonスクリプト
・GDLオブジェクト

の3つです。

しかし、これらはできることが違ったり、一長一短があります。
アドオンはArchicadでできることはほぼできると考えてよいと思いますが、プログラミング言語はC++で少し作成のハードルが高いです。
PythonスクリプトはPythonを使って手軽にプログラミングによる作業効率化ができますが、現状ではモデル要素を作成したり、変更することはできません。
GDLオブジェクトはモデル要素を作成することができますが、基本的にはオブジェクトやドア、ラベルなどの限られた要素のみです。

今回は比較的簡単なGDLオブジェクトを使った作成方法を試したいと思います。

GDLとは

GDLはArchicadでオブジェクトを作るためのプログラミング言語です。
形状を作るためのコマンドを使ってパラメトリックなオブジェクトを作成することができます。
今回は基本的な部分については説明しませんが、ArchicadにGDLリファレンスガイドがついていますので興味のある方はそれを読んでみてください。

GDLオブジェクトの作成

Archicadでオブジェクトを作るには、通常Archicad内のスクリプトエディタでコードを記述していきます。ただ、今回は建物が何千個もあり、一つ一つ書いていくのはかなり大変です。

今回のデータに関しては構造が単純なためPythonを使ってGDLのコードを作成することにしました。GDLオブジェクトを直接作るのではなく、GDLの文法に合ったテキストデータを作成してコーディング作業を効率化したいと思います。

JSONファイルの読み込み

JSONデータは初回に作成したものを使います。

file_path = "data.json"
with open(file_path, "r", encoding="utf-8") as f:
    bldg_data = json.load(f)

位置調整

Grasshopperの回では無視していましたが、平面直角座標のデータは原点(x,y) = (0,0)からかなり離れています。Archicadのオブジェクトが原点と離れていると扱いづらいので、原点付近に位置調整します。

Archicadのオブジェクトにはローカル原点というものがあり、それを使って位置調整できます。

ADD 2,3,0 
SPHERE 5
DEL 1

上記のコマンドで
1.ローカル原点を(x,y,z) = (2,3,0)に移動
2.ローカル原点に半径5mの球を作成
3.ローカル原点の移動を1つ戻す
というような感じで記述をします。

今回は座標がx,yともにマイナスなので、最小値を取ってローカル原点を移動する処理を追加します。
xyの座標値の最小値を取得する関数を作成します。

def get_minxy(data: dict) -> tuple[float, float]:
    min_x = data[0]["coords"][0][0]
    min_y = data[0]["coords"][0][1]

    for item in data:
        for coord in item["coords"]:
            temp_x = coord[0]
            temp_y = coord[1]

            if min_x > temp_x:
                min_x = temp_x
            if min_y > temp_y:
                min_y = temp_y

    return min_x, min_y

データをループ処理し、最小値を取得します。この最小値が原点になるように調整します。

形状の作成

次に各建物形状を作成するコマンドを記述していきます。GDLではシンプルな押出形状を作成するためにはPRISMコマンドを使いますが、それは下記のように記述します。

PRISM n, h, x1, y1, ..., xn, yn

コマンド引数の内容はnが頂点数、hが押し出し高さ、xn,ynがxyの座標値です。

PRISM 5,1,0,0,1,0,1,1,0,1,0,0

上記のように書いた場合、1mの立方体を作成します。最初の点に戻ってくるので頂点は5つです。
PRISMの場合頂点4つでも大丈夫なようですが、コマンドによっては違うかもしれないので注意が必要です。

では、GDLスクリプトのテキストを作成する関数を作成します。

def dict_to_gdl_script(data: dict, min_x: float, min_y: float) -> str:
    script = f"ADD {str(round(-min_x,4))}, {str(round(-min_y,4))}, 0\n"
    script += "MATERIAL 4\n\n"

    for item in data:
        count = len(item["coords"])
        height = item["height"]
        prism_str = f"PRISM {count}, {height}, "

        for x, y, _ in item["coords"]:
            prism_str += f"{round(x,4)}, {round(y,4)}, \n"

        script += prism_str[:-4] + "\n\n"

    script += "DEL 1"
    return script

Add でローカル座標の調整、Materialで材質の設定をします。

後はprismをひたすら作っていきます。
GDLではコード内の単位はメートルです。
座標の小数は0.1mm以下は必要ないので、四捨五入してしまいましょう。

PRISMの最後の頂点の後にカンマがあるとエラーになるので末尾のカンマは削除します。

GDLスクリプトができたらテキストとして保存します。

min_x, min_y = get_minxy(bldg_data)
script = dict_to_gdl_script(bldg_data, min_x, min_y)

with open("3d_script.txt", "w", encoding="utf-8") as f:
    f.write(script)

できたテキストは下記のようなものです。

ADD 46974.2, 146881.13, 0
MATERIAL 4

PRISM 5, 35.8, -46869.83, -146672.14, 
-46868.33, -146652.14, 
-46880.08, -146651.14, 
-46881.78, -146671.06, 
-46869.83, -146672.1

PRISM 7, 19.7, -46524.498, -145969.614, 
-46535.015, -145973.038, 
-46529.07, -145989.9, 
-46527.07, -145989.15, 
-46524.82, -145996.15, 
-46516.32, -145993.4, 
-46524.498, -145969.61

PRISM 5, 12.3, -46779.08, -146577.89, 
-46779.33, -146597.14, 
-46774.58, -146597.14, 
-46774.33, -146577.89, 
-46779.08, -146577.8

今回はしませんでしたが、xyの最小値で座標の値自体を変えてあげてもよいかもしれません。

GDLオブジェクトの作成

テキストが保存できたらArchicadでオブジェクトを作成します。

メニューから新規オブジェクトを選択します。

オブジェクトエディタが表示されたら3Dスクリプトを選択します。

保存したテキストをコピーして、エディタ上に貼り付けます。

スクリプトを確認ボタンを押して、エラーが出なければ大丈夫です。

2Dスクリプトについては、同様の手順でPythonで作成することもできますが、今回は3D形状を投影したものを使用します。

PROJECT2 3, 270, 1

これは3D形状を2Dに投影するコマンドです。処理が重くなるため推奨はされていないようですので、必要であれば2Dスクリプトを作成しましょう。手順としては3Dとほぼ変わりません。

オブジェクトを保存して、配置します。確認して形状に問題がなければOKです。

以上でPlateauデータから書き出したJSONを元に、Archicadでオブジェクトを作成することができました。

最後までお読みいただきありがとうございました。

以下にコード全文を載せておきます。

import json


def get_minxy(data: dict) -> tuple[float, float]:
    min_x = data[0]["coords"][0][0]
    min_y = data[0]["coords"][0][1]
    for item in data:
        for coord in item["coords"]:
            temp_x = coord[0]
            temp_y = coord[1]

            if min_x > temp_x:
                min_x = temp_x
            if min_y > temp_y:
                min_y = temp_y

    return min_x, min_y


def dict_to_gdl_script(data: dict, min_x: float, min_y: float) -> str:
    script = f"ADD {str(round(-min_x,4))}, {str(round(-min_y,4))}, 0\n"
    script += "MATERIAL 4\n\n"

    for item in data:
        count = len(item["coords"])
        height = item["height"]
        row = f"PRISM {count}, {height}, "

        for x, y, _ in item["coords"]:
            row += f"{round(x,4)}, {round(y,4)}, \n"

        script += row[:-4] + "\n\n"

    script += "DEL 1"
    return script


def main():
    file_path = "data.json"
    with open(file_path, "r", encoding="utf-8") as f:
        bldg_data = json.load(f)

    min_x, min_y = get_minxy(bldg_data)
    script = dict_to_gdl_script(bldg_data, min_x, min_y)

    with open("3d_script.txt", "w", encoding="utf-8") as f:
        f.write(script)


if __name__ == "__main__":
    main()