チュートリアル 10: Python スクリプティング

チュートリアル 10: Python スクリプティング

チュートリアル データ

チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。

概要

このチュートリアルでは、Python コンソールおよびエディターの基本的な使用方法を学習し、CityEngine タスクの自動化の例をいくつか紹介します。 Python スクリプティング インターフェイスを利用すると、CityEngine の可能性をより広げることができます。

Part 1: Python コンソールおよびエディター

このセクションでは、Python コンソールおよびエディターを使用して簡単な選択を実行します。

CityEngine 2025.1 では Python 3 (Beta) のサポートが導入されました。しかし、このチュートリアルでは、従来から使用されている成熟した Jython 環境を対象とします。

Python コンソール

新しい Python コンソール ウィンドウを起動します。

  1. [Navigator] ウィンドウの Tutorial_10_Python_Scripting フォルダーを展開します。

  2. scenes フォルダーのシーン 01_PythonScripting.cej を開きます。

  3. [Window] メニュー → [Console] から [Console] ウィンドウを開きます。

  4. 右端にある小さな三角形から [Python Console] ウィンドウを開きます。


    CityEngine 2025.1 以降を使用している場合は、プロンプトが表示されたら Jython Console を選択してください。

  5. 「ce.setS」 と入力します。

    入力すると選択できるコマンドのリストが表示されます。最初の CityEngine Python コマンドでは、シーン オブジェクトを固有の名前ですばやく選択できます。
    まだ開いていない場合、Ctrl+Space キーを押すとコマンド補完のポップアップが表示されます。

  6. リストの ce.setSelection をダブルクリックし、コンソールのエントリを完了します。

  7. ce.setSelection を以下のフル コマンドに拡張します。

    >>> ce.setSelection(ce.getObjectsFrom(ce.scene, ce.withName("*Broadway*")))
  8. Enter を押して、名前に Broadway を含むシーン内のすべてのオブジェクトを選択します。

エディターで Python モジュールを作成

より長く、より高度な Python コマンド、あるいはコマンド群を使いたいときは、CityEngine の Python エディターを使ってスクリプトを書くと便利です。

新しい Python モジュールを作成します。

  1. [File] メニュー → [New…] → [Python] → [Python Module] を選択し、[Next] をクリックして [Create a new Python module] ダイアログボックスを開きます

  2. [Create a new Python module] ダイアログボックスの [Source Folder]scripts フォルダーを参照し、[OK] をクリックします。

  3. [Name] ボックスで 「myHelpers」 と入力します。

  4. [Finish] をクリックし、[Template] ウィンドウを開きます。

  5. [Module: Main] テンプレートを選択します。

  6. [OK] をクリックします。

    新しい Python モジュール myHelpersmyHelpers.py ファイルとして scripts フォルダーに作成され、Python エディターで開かれます。

  7. ce = CE() の行の後に、新しい関数 selectByAttribute(attr, value) を追加します。main 関数の上に以下のコードを追加します。

    def selectByAttribute(attr, value):
        objects = ce.getObjectsFrom(ce.scene)
        selection = []
        for o in objects:
            attrvalue = ce.getAttribute(o, attr)
            if attrvalue  ==  value:
                selection.append(o)
    
        ce.setSelection(selection)

    この関数は、シーン内のすべてのオブジェクトをループし、最初のパラメーターとして渡された属性の値を読み取ります。そして、その値を 2 番目のパラメーターとして渡された値と比較し、一致すれば、そのオブジェクトを選択範囲に追加します。

  8. シーン内の道路形状を選択し、[Inspector] ウィンドウの [Object Attributes] をクリックして、使用可能な属性を確認します。

  9. スクリプトでジャンクションに接続されているすべての道路を選択するために、スクリプトの main に特定のパラメーターを持つ関数呼び出しを追加します。

    if __name__ == '__main__':
        selectByAttribute("connectionStart","JUNCTION")
  10. Ctrl+S キーを押して、モジュールを保存します。

  11. [Python Editor] ウィンドウ内で F9 キーを押して、スクリプトを実行します。

    Junction との交差点を持つ道路が選択されます。

コンソールからのスクリプト実行

別の方法として、Python コンソールからスクリプト内の関数を直接呼び出すことができます。

  1. [Python Console] ウィンドウで、先ほど作成したモジュールを含むパスをシステムパスに追加します。

    >>> sys.path.append(ce.toFSPath("scripts"))
  2. myHelpers モジュールをインポートします。

    >>> import myHelpers
  3. 最後に、任意のパラメーターを指定してヘルパー関数を呼び出します。

    >>> myHelpers.selectByAttribute("connectionEnd", "JUNCTION")

scripting.py を使用したモジュールの自動読み込み

CityEngine の起動時に scripting.py スクリプトを作成して読み込むには、以下の作業を行います。

  1. 使用しているオペレーティング システムのファイル ブラウザーを使用して、CityEngineのワークスペースに scripting.py という名前のファイルを作成します。

    CityEngine のワークスペースのルート フォルダー内に {CityEngine Workspace}\scripting.py のように scripting.py ファイルを保存します。

    パスは、[File] メニュー → [Switch Workspace] → [Other] で確認するか、[Navigator] ウィンドウでプロジェクトを右クリックし、[Show in File Manager] を選択します。

  2. 以下の行を追加し、起動時にヘルパー スクリプトが自動的にマップされるようにします。

    from scripting import *
    
    # get a CityEngine instance
    ce = CE()
    
    import sys
    
    sys.path.append(ce.toFSPath("/Tutorial_10_Python_Scripting__2021_1/scripts"))
    import myHelpers
  3. CityEngine を再起動して、シーン 01_PythonScripting.cej を開き、myHelpers モジュールを自動的に読み込みます。

  4. Python コンソールからヘルパー関数を直接呼び出して、自動読み込みが機能していることを確認します。

    >>> myHelpers.selectByAttribute("sidewalkWidthLeft", 4)

    sidewalkWidthLeft オブジェクト属性の値が 4 であるすべての道路が選択されます。


任意のコードを scripting.py ファイルに追加することができます。scripting モジュールは、新しい Python コンソールが開かれた時や Python エディターからスクリプトが実行された時に自動的に実行されます。

scripting.py ファイルが有効で、正しく実行されることを確認してください。そうでない場合は CityEngine 内の Python コードは実行できません。scripting.py ファイルを作成または変更した後、CityEngine の Python コンソールを開くと、scripting ファイルの実行に伴う問題はそこに表示されます。

scripting.py ファイルは、CityEngine の起動時に一度だけ読み込まれます。変更する際は必ず CityEngine を再起動してください。

CityEngine の起動時にスクリプトが正常に更新されない場合、Python キャッシュ ディレクトリー $USER_DIR/.cityengine/$CEVERSION_DIR/pythonCache/ を削除してください。

Part 2: 建物の変化のアニメーション

Python スクリプトを使用することで、生成やエクスポート処理を自動化できます。このワークフローでは、建物属性を設定して建物アニメーションを生成し、結果のモデルセットをエクスポートする方法を説明します。

建物の生成

建物を生成します。

  1. シーン 03_PythonScripting.cej を開きます。

    “Would you like to regenerate these models?” と出たら、[Yes] をクリックします。

  2. 建物を選択します。

    建物が生成されない場合、区画を選択し、ツールバーの [Generate] (Ctrl+G キー) をクリックします。

    ルール ファイルには、建物の寸法を変更する属性が含まれています。[Inspector] ウィンドウでこれらの値を手動で設定するのではなく、値を変更するスクリプトを作成し、異なるバージョンのモデルを一括生成します。

アニメーション スクリプト

アニメーション スクリプトを記述します。

  1. growBuilding という新しい Python モジュールを作成します。

  2. 2 つのレンジをループする非常にシンプルなタイムラインを作成し、setAttribute 関数を呼び出すために、growBuilding 関数を追加します。

    def growBuilding():
        for i in range(1,14):
            height = 20+i
            doStep(i,height,1)
    
        for i in range(15,35):
            height = 34
            width = i-14
            doStep(i,height,width)
  3. doStep 関数を使用して、区画のオブジェクトの height と width の属性を変更します。

    def doStep(i,height,width):    
        object = ce.getObjectsFrom(ce.scene, ce.withName("'Lot1'"))
        ce.setAttributeSource(object, "height", "OBJECT")
        ce.setAttributeSource(object, "width", "OBJECT")
        ce.setAttribute(object, "height", height)
        ce.setAttribute(object, "width", width)
    
        Generate(object)
  4. 建物を生成する Generate 関数を追加します。

    def Generate(object):
        ce.generateModels(object)
  5. スクリプトの main 節で、growBuilding 関数を呼び出します。

    if __name__ == '__main__':
        growBuilding()
  6. モジュールを保存します。

  7. F9 キーを押して、スクリプトを実行し、建物を一括生成します。

  8. モデルを一括エクスポートするには、Export 関数を追加します。main 節の前に関数があることを確認します。

    def Export(i, object):
        dir = ce.toFSPath("models")
        file = "building_merge_" + str(i)
        # prepare export settings 
        settings = OBJExportModelSettings()
        settings.setBaseName(file)
        settings.setOutputPath(dir)
        # do export 
        ce.export(object, settings)
  9. doStep() 関数内の Generate コールを置き換えます。

    #Generate(object)
    Export(i, object)
  10. モジュールを保存します。

  11. F9 キーを押して、スクリプトを実行します。

    モデルはプロジェクト内の models フォルダーに一括エクスポートされます。

Part 3: アセット ライブラリ ルール ファイルの作成

大量のアセットがある場合には、それらすべてを一度に表示できると便利です。このセクションでは、プロジェクトに含まれるアセットを表示する CGA ルール ファイルを自動的に生成する Python スクリプトの書き方を紹介します。
Python スクリプトは、次の構造を持つルール ファイルを書き出します。

Lot -->  Geometries Textures

Geometries --> 
    Geometry(assetpath)
    Geometry(assetpath)
    ...

Geometry(asset) --> i(asset)

これらはジオメトリ アセットおよびそれと同様のテクスチャー画像のための構造です。

  1. まだ開いていない場合、シーン 03_PythonScripting.cej を開きます。

  2. 新しい Python メイン モジュール asset_lib を作成します。

  3. 新しい関数 writeCGALib を作成します。

    def writeCGAlib():
  4. ヘッダー情報およびスタート ルール Lot を記述します。

    cga = "/*Asset Library Loader : Generated by asset_lib.py*/\n version \"2023.0\"\n\n"
    
    cga += "Lot -->  Geometries Textures"
  5. Geometries ルールを記述し、asset フォルダー内のすべての .obj ファイルを取得します。各アセットの Geometry(assetpath) 呼び出しルールを用意します。

    cga += "\n\nGeometries --> "
    for obj in ce.getObjectsFrom("/", ce.isFile, ce.withName("/Tutorial_10*/assets/*.obj")):   
        cga += "\n\t t(2,0,0)  Geometry(\""+obj+"\")"
  6. テクスチャー アセットについても同様のルールを記述します。

    cga+="\n\nTextures --> \n\ts(1,0,0) set(scope.ty,3) set(scope.tz,0) i(\"facades/xy-plane.obj\")"   
    for jpg in ce.getObjectsFrom("/", ce.isFile, ce.withName("/Tutorial_10*/assets/*.jpg")):
        cga += "\n\tt(2,0,0)  Texture(\""+jpg+"\")"
  7. アセット ローダーのルールを記述します。

    cga += "\n\n Geometry(asset) --> s(1,0,0) i(asset) set(scope.ty,0) set(scope.tz,0)"
    
    cga += "\n\n Texture(asset) --> set(material.colormap, asset)"
  8. .cga ファイルのファイル ハンドルを開き、CGA コンテンツを記述します。

    cgafile = ce.toFSPath("rules/asset_lib.cga")
    CGA = open(cgafile, "w")
    CGA.write(cga)
    CGA.close()
    print "written file "+cgafile
  9. 新しい assignAndGenerateLib() 関数を追加します。

    def assignAndGenerateLib():
        object = ce.getObjectsFrom(ce.scene, ce.withName("'Lot2'"))
        ce.refreshWorkspace()
        ce.setRuleFile(object, "asset_lib.cga")
        ce.setStartRule(object, "Lot")
        ce.generateModels(object)

    この関数は、生成された .cga ファイルをシーンの区画に適用し、モデルを生成します。

  10. 最後に、main 節の中から 2 つの関数を呼び出します。

    if __name__ == '__main__':
        writeCGAlib() 
        assignAndGenerateLib()
  11. モジュールを保存します。

  12. F9 キーを押して、ライブラリ モデルを生成します。

    for ループ文を正しく動作させるためには、適切な Python の構文とインデントに従うことが重要です。そうでない場合、スクリプトが失敗する可能性があります。

Part 4: startup.py を用いた CityEngine タスクの自動化

Python を使用して、大きなタスクや反復的なタスクを自動化することができます。例えば郡全体の区画情報からモデル生成を自動化することができます。

タスクを自動化します。

  1. automationJob という新しい Python モジュールを作成します。

  2. 自動化されたジョブのタスクを含む関数を挿入します。

    def fgdbToKml(pathFGDB,layerName,ruleName,startRule = "Generate"):
        # open scene in the automation project
        ce.newFile('/scenes/emptyScene.cej')
    
        # load a database
        importSettings = FGDBImportSettings()
        importSettings.setDatasetFilter(['/'+layerName])
        ce.importFile(ce.toFSPath(pathFGDB), importSettings)
    
        # assign rule file based on the layer name
        layer = ce.getObjectsFrom(ce.scene, ce.isShapeLayer, ce.withName(layerName))[0]
        shapes = ce.getObjectsFrom(layer, ce.isShape)
        ce.setRuleFile(shapes, ruleName)
        ce.setStartRule(shapes, startRule)
    
        # export models to KML
        exportSettings = KMLExportModelSettings()
        exportSettings.setOutputPath(ce.toFSPath("models"))
        exportSettings.setBaseName(layerName)
        exportSettings.setCompression(True)
        ce.export(shapes, exportSettings)
    
        # close Scene
        ce.waitForUIIdle()
        ce.closeFile()

    テストするには、’__main__’ セクションにこの関数への呼び出しを追加します。提供されている fgdbToKml のサンプルは、モデルを生成するために fileGDB からシェープをインポートし、KML に書き出します。

    if __name__ == '__main__':
        fgdbToKml("data/CityData.gdb", "Footprints", 
                  "/ESRI.lib/rules/Buildings/Building_From_Footprint.cga", "Generate")
        pass
  3. 保存し、F9 キーを押して、すべてが正しく動作するかテストします。テスト後、models フォルダーには Footprints.kmz ファイルが含まれています。

    Footprints.kmz ファイルは、後のステップで再度作成するため削除します。

  4. このプロセスを自動化するためには、上記の関数のテストに使用したすべての関数パラメーターを構成ファイルに記述する必要があります。

    チュートリアル プロジェクトには、data フォルダーにある jobConfig.cfg ファイルの例が含まれています。

    [config]
    pathFGDB=data/CityData.gdb
    layerName=Footprints
    ruleName=/ESRI.lib/rules/Buildings/Building_From_Footprint.cga
    startRule=Generate
  5. automationJob.py スクリプトで、fgdbToKml 関数の下に run(cfg)getCfgValue(cfg,name) 関数を追加して、構成ファイルに保存されたパラメーターで自動化ジョブを実行します。

    def getCfgValue(cfg,name):
        for c in cfg:
            if  c[0] == name: return c[1]
        return None
    
    def run(cfg):
        pathFGDB = getCfgValue(cfg,'pathfgdb')
        layerName = getCfgValue(cfg,'layername')
        ruleName = getCfgValue(cfg,'rulename')
        startRule = getCfgValue(cfg,'startrule')
    
        fgdbToKml(pathFGDB, layerName, ruleName, startRule)

    参考までに、scripts フォルダーにある automationJob_dist.py ファイルを開きます。

  6. モジュールを保存します。

  7. 次に、自動化用として別の CityEngine ワークスペースを作成するために、[File] → [Switch Workspace] → [Other] をクリックします。新しいワークスペースの名前を C:\Automation Workspace にします。

  8. [Launch] をクリックします。

  9. チュートリアル プロジェクトにある scripts\startup_dist.py スクリプトを、新しい C:\Automation Workspace\ のルート ディレクトリーにコピーします。

  10. これを startup.py にリネームします。startup.py スクリプトには以下の内容が含まれています。

from scripting import *
from java import lang
import ConfigParser, sys
if __name__ == '__startup__':
   # get a CityEngine instance
   ce = CE()
    
   # get startup arguments
   projectFolder = lang.System.getProperty("projectFolder")
   configFilePath = lang.System.getProperty("configFilePath")
    
   # link the automation project into automation workspace
   if "automationProject" in ce.listProjects(): ce.removeProject("automationProject")
   ce.importProject(projectFolder, False, "automationProject")
    
   # read configuration file
   cp = ConfigParser.ConfigParser()
   cp.read(configFilePath)
   cfg = cp.items('config') # list of (name,value) pairs
    
   # run automation job
   sys.path.append(ce.toFSPath("/automationProject/scripts"))
   import automationJob
   automationJob.run(cfg)
    
   # safely shut down CityEngine
   ce.exit()

この Python スクリプトの startup セクション内のコマンドは、CityEngine の起動時に自動的に実行されます。 最初の起動引数は、自動化ジョブを含む CityEngine プロジェクトを定義します。これは自動化用ワークスペースにリンクされます。 2 番目の引数には設定ファイル (config file) が含まれます。これは解析され、(name, value) ペアのリストとして自動化ジョブに渡されます。 ジョブが完了すると、CityEngine は安全にシャットダウンされます。

  1. コマンド ラインを開き、自動化用ワークスペースで CityEngine を起動し、ジョブ定義とパラメーターを渡します。
<Path_to_CityEngine.exe> -data <Workspace_Folder> -vmargs -
DprojectFolder=<Project_Folder> -DconfigFilePath=<Configuration_FilePath>

> "C:\Program Files\ArcGIS\CityEngine2025.1\CityEngine.exe" -data 
"C:\Automation Workspace" -vmargs 
-DprojectFolder="C:\CE_Workspace\Tutorial_10_Python_Scripting__2025_1" 
-DconfigFilePath="C:\CE_Workspace\Tutorial_10_Python_Scripting__2025_1\data\jobConfig.cfg"

ジョブが完了すると、[models] フォルダーには Footprints.kmz という出力ファイルが生成されます。必要であれば、このファイルを ArcGIS Earth で開くことができます。

ArcGIS Earth の結果

automationJob.py には最小限のジョブのみが含まれています。必要に応じてカスタマイズするには、CityEngine Python リファレンスを参照してください。

このチュートリアルでは、以下の方法を学習しました。

  • Python コンソールとエディターの基本的な使い方を学習しました。
  • CityEngine タスクの自動化の例をいくつか学習しました。

詳細については、CityEngine Python リファレンスを参照ください。 CityEngine の学習を続けるには、CityEngine チュートリアル カタログをご覧ください。