Maya + pyshp でシェープファイル (.shp)を取り込む


CGソフトにShapefileを取り込みたい

建築系CGプロダクションで働く3DCGモデラーです。

建築CGを制作していると、時々、シェープファイル(.shp)をCGソフトに取り込みたい時があります。
地図なんかを取り込みたいときです。
GIS系のソフトは持ってないけど、何かお手軽に取り込むことは出来ないか?
というときに使える方法を紹介します。

Python + pyshp

CG系のソフトはスクリプティング環境としてPythonを使えることが多いです。
Pythonには pyshp というシェープファイルを扱うライブラリがあるので、それを使って.shpファイルを取り込めます。
pyshp

Maya

この記事ではMayaに.shpファイルを取り込み、Curveとして描画する方法について書きます。

pyshpをMayaへインストール

まず、Maya上のPythonである、"mayapy"にpyshpをインストールします。

install.bat
cd "C:\Program Files\Autodesk\Maya2018\bin"
"mayapy.exe" -m pip install "C:\Users\Takeharu\Documents\maya\3rdParty\pyshp-2.1.0.tar.gz"

スクリプティング

そしてMaya用のPythonスクリプト。

import_shapefile.py

# -*- coding: utf-8 -*-
import pymel.core as pm
import shapefile
import os.path

#  ファイルオープンダイアログ
def get_file():
    startdir = ''
    if pm.optionVar( exists='defaultStarDirectoryOfImportShapefile' ):
        startdir = pm.optionVar( q='defaultStarDirectoryOfImportShapefile' )
    else:
        startdir = 'C:/Users/Takeharu/Documents'
    fn = pm.fileDialog2(fileMode=1, caption="Open Shape File...", fileFilter="*.shp", startingDirectory=startdir, okCaption='Open')
    pm.optionVar( sv=('defaultStarDirectoryOfImportShapefile', os.path.dirname(fn[0])) )
    return fn[0]



file_path = get_file()  # ファイルパス取得
with open(file_path, "rb") as myshp:  #  ファイルを開く
    r = shapefile.Reader(shp = myshp)  # リーダー
    if len(r.shapes()):
        pm.refresh(suspend=True)
        grp = pm.group(empty=1,name='shp_group0')
        crvs = []
        for s in r.shapes():
            t = s.shapeType
            if t == 3 or t == 5:  # 今回は地図をCurveで描画したいだけなので、シェイプのタイプを 3:Polyline5:Polygonに限定します。
                mapped = map(lambda x: x+(0.0,), s.points)  # 元が2D座標データなので、Z軸座標を追加して3次元座標に変換します。
                crv = None
                if len(s.parts) == 1:
                    crv = pm.curve(p=mapped,d=1)
                    crvs.append(crv)
                else:  # ひとつのシェイプに複数のPolygonが含まれることがあります。それ切り分けます。
                       # parts`が[0,256,150] みたいなリストなら、複数のオブジェクトを内包しています。
                       # 数字は座標リストの区切りです。
                    for i in range(0,len(s.parts)-1):  
                        pnts = []
                        pnts = mapped[s.parts[i]:s.parts[i+1]]
                        crv = pm.curve(p=pnts,d=1)
                        crvs.append(crv)
                    crv = pm.curve(p=mapped[s.parts[-1]:],d=1)
                    crvs.append(crv)

        pm.parent(crvs,grp)
        g = grp.getParent()
        pm.select(grp,r=1)
        pm.rotate(g, [-90.0,0,0]) # MayaY-Upに合わせて全体を回転します。
        pm.viewFit( pm.selected())
        pm.refresh(suspend=False)

    for c in pm.ls(cameras=1): # 地図の場合、巨大な図形となるので、カメラのクリッピングプレーンを調整します。
        c.setFarClipPlane(1000000)
        c.setNearClipPlane(1)


# ※コピペするときは、使う前にコメントを全部削除してください。
シェルフボタン

import sys
if 'terrain.import_shapefile' in sys.modules:
    reload(terrain.import_shapefile)
else:
    import terrain.import_shapefile

道路のデータを取り込んでみた状態

元データが大きいと、結構時間がかかります。時に数時間かかることもあります。
一見、フリーズしたように見えても放っておけばそのうち終わります。
それがMaya。

Mayaの単位の話

Mayaの単位について知っておくといいと思います。
MayaはCADソフトなどに比べて内部精度が低いです。
そのため、原点から遠く離れた位置に図形を描画するのが苦手です。

GIS系のデータであるシェープファイルは、この "原点から遠く離れた位置" に座標を持つことが珍しくありません。
これをMayaのデフォルト単位であるcmに変換して取り込もうとすると、描画エラーになることがあります。

詳しいことはわかりませんが、"桁落ち"とかそういう類のエラーだと思われます。
DXF/DWGを取り込もうとして、なぜか図が出てこないようなときはこれかもしれません。

面倒を避けたいなら、1ユニット = 1cm ではなく、1ユニット = 1m に読み換えて作業するといいです。
取り込み後に、原点の近くにオブジェクトを移動させてしまえば、その後はスケールを修正しても大丈夫です。