PythonからArcObjectsを使用するためのガイドライン


10

はるかに、PythonからArcObjectsアクセスしますか?は、GIS Stack Exchangeに関する私の最もよく読まれ、参照されているQ&Aです。その成功にもかかわらず、それは実際の使用に関してはおそらく私の最も弱い領域の1つです。その貧弱な表示​​の大部分は、ArcObjectsのドキュメントを読んで理解する能力が低いことに起因しています。

それで、与えられたタスクに対して、.net / c ++ / java / ...のドキュメントと例を同等のpythonに翻訳するためのガイドラインは何ですか?(そのためにはどの言語が最適ですか?)そして、最初に最適なインデックスまたはランディングページは何ですか?どのようなことに焦点を当てるべきであり、おそらく少なくとも同じくらい重要なのに、自由に無視できるものは何ですか?

あなたの聴衆は少なくともいくらかpythonの読み書き能力があり、他の開発言語では読み書きができないと仮定します。最初のアイデアや研究からPythonの結果の動作まで、小さなコーディング演習を行ってください。


1
ここでは会話に何も追加されないかもしれませんが、このウォークスルーのセットが発展するのを見て本当に興味があると思いますので、記録のために述べたいと思います。マットに感謝します。Darren WiensがMXDをゼロから作成し、レイアウトにガイドを追加した記事を見つけました。また、Mark Cederholmの スニペットモジュールは、これらの取り組みに非常に役立ち、頻繁に使用されているようです。
ジム

使用可能な例:gis.stackexchange.com/questions/86007/…(開示:これは私が取り組んできた問題であり、Qを促しました(巧妙に細工された)答えに私を打ち負かし、すべての信用を得てください!;-)
マットウィルキー2015年

Arcobjectsにアクセスするのは難しい場合があります。ヘルプドキュメントは問題ありませんが、例は優れています。最大の問題の1つは、オブジェクトXがあるように、あるオブジェクトから別のオブジェクトへの継承を処理することです。 ?Visual Studio 2008または2010 Express(入手可能な場合は無料でダウンロード)を入手できる場合は、SDKをインストールすると、ヘルプドキュメントと一連のサンプルがローカルで取得されます。
Michael Stimson

1
@mattwilkieうまくいけば、これで水を汚すことはあまりありません...しかし、既存の.NETコードをpythonに移植して型キャスト構文を理解するために、.NETのpythonは、comtypesアプローチよりも少し単純に見えます。とはいえ、私は.NET用のpythonを発見したばかりで、まだテストしていません。
user2856 2015年

1
@mattwilkieがpython.Netを発見したばかりです。ArcGISDesktopに加えて、ArcGIS SDKがインストールされている必要があります(アセンブリラッパーDLLがスクリプトと共に配布されていない場合)。そのため、comtypesアプローチほど移植性が高くありません。
user2856 2015年

回答:


9

私はこの分野にもあまり強くありませんが、スニペットモジュールを変更し、非常に単純なタスク用のラッパーをいくつか作成しました。線要素を追加するだけの例があります。メインブロックの下の例では、ドキュメントのすぐ外側のレイアウトビューに三角形を形成しています。

このスクリプトを別の弧のある検索カーソルと組み合わせて使用​​して、個々の行とテキスト要素からレイアウト内にグラフィックテーブルを作成しますが、「単純な」例からすぐに離れてしまいます。以下のコードはかなり単純で、変更されたバージョンのスニペットを使用しています。

from snippets import *
def add_line(pApp=None, name='Line', x=None, y=None, end_x=None, end_y=None,
             x_len=0, y_len=0, anchor=0, view='layout'):
    '''adds a line to an ArcMap Document

    Required:
    pApp -- reference to either open ArcMap document or path on disk
    name -- name of line element

    Optional:
    x -- start x coordinate, if none, middle of the extent will be used (data view)
    y -- start y coordinate, if none, middle of the extent will be used (data view)
    end_x -- end x coordinate, if making straight lines use x_len
    end_y -- end y coordinate, if making straight lines use y_len
    x_len -- length of line in east/west direction
    y_len -- length of line in north/south direction
    anchor -- anchor point for line element
    view -- choose view for text element (layout|data)

        Anchor Points:
        esriTopLeftCorner   0   Anchor to the top left corner.
        esriTopMidPoint     1   Anchor to the top mid point.
        esriTopRightCorner  2   Anchor to the top right corner.
        esriLeftMidPoint    3   Anchor to the left mid point.
        esriCenterPoint     4   Anchor to the center point.
        esriRightMidPoint   5   Anchor to the right mid point.
        esriBottomLeftCorner    6   Anchor to the bottom left corner.
        esriBottomMidPoint  7   Anchor to the bottom mid point.
        esriBottomRightCorner   8   Anchor to the botton right corner.
    '''
    GetDesktopModules()
    import comtypes.gen.esriFramework as esriFramework
    import comtypes.gen.esriArcMapUI as esriArcMapUI
    import comtypes.gen.esriSystem as esriSystem
    import comtypes.gen.esriGeometry as esriGeometry
    import comtypes.gen.esriCarto as esriCarto
    import comtypes.gen.esriDisplay as esriDisplay
    import comtypes.gen.stdole as stdole

    # set mxd
    if not pApp:
        pApp = GetApp()
    pDoc = pApp.Document
    pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)
    pMap = pMxDoc.FocusMap
    pMapL = pMap
    if view.lower() == 'layout':
        pMapL = pMxDoc.PageLayout
    pAV = CType(pMapL, esriCarto.IActiveView)
    pSD = pAV.ScreenDisplay

    # set coords for elment
    pFact = CType(pApp, esriFramework.IObjectFactory)
    if view.lower() == 'data':
        pEnv = pAV.Extent
        if x == None:
            x = (pEnv.XMin + pEnv.XMax) / 2
        if y == None:
            y = (pEnv.YMin + pEnv.YMax) / 2
    else:
        # default layout position, move off page
        if x == None: x = -4
        if y == None: y = 4

    # from point
    pUnk_pt = pFact.Create(CLSID(esriGeometry.Point))
    pPt = CType(pUnk_pt, esriGeometry.IPoint)
    pPt.PutCoords(x, y)

    # to point
    pUnk_pt2 = pFact.Create(CLSID(esriGeometry.Point))
    pPt2 = CType(pUnk_pt2, esriGeometry.IPoint)
    if x_len or y_len:
        pPt2.PutCoords(x + x_len, y + y_len)
    elif end_x or end_y:
        pPt2.PutCoords(end_x, end_y)

    # line (from point - to point)
    pUnk_line = pFact.Create(CLSID(esriGeometry.Polyline))
    pLg = CType(pUnk_line, esriGeometry.IPolyline)
    pLg.FromPoint = pPt
    pLg.ToPoint = pPt2

    # preset color according to RGB values
    pUnk_color = pFact.Create(CLSID(esriDisplay.RgbColor))
    pColor = CType(pUnk_color, esriDisplay.IRgbColor)
    pColor.Red, pColor.Green, pColor.Blue = (0,0,0) #black line

    # set line properties
    pUnk_line = pFact.Create(CLSID(esriDisplay.SimpleLineSymbol))
    pLineSymbol = CType(pUnk_line, esriDisplay.ISimpleLineSymbol)
    pLineSymbol.Color = pColor

    # create the actual element
    pUnk_elm = pFact.Create(CLSID(esriCarto.LineElement))
    pLineElement = CType(pUnk_elm, esriCarto.ILineElement)
    pLineElement.Symbol = pLineSymbol
    pElement = CType(pLineElement, esriCarto.IElement)

    # elm properties
    pElmProp = CType(pElement, esriCarto.IElementProperties3)
    pElmProp.Name = name
    pElmProp.AnchorPoint = esriCarto.esriAnchorPointEnum(anchor)
    pElement.Geometry = pLg

    # add to map
    pGC = CType(pMapL, esriCarto.IGraphicsContainer)
    pGC.AddElement(pElement, 0)
    pGCSel = CType(pMapL, esriCarto.IGraphicsContainerSelect)
    pGCSel.SelectElement(pElement)
    iOpt = esriCarto.esriViewGraphics + \
    esriCarto.esriViewGraphicSelection
    pAV.PartialRefresh(iOpt, None, None)
    return pElement

if __name__ == '__main__':

    # testing (make a triangle)
    add_line(name='hypot', end_x=-2, end_y=2, anchor=3)
    add_line(name='vertLine', y_len=-2, anchor=1)
    add_line(name='bottom', y=2, end_x=-2, end_y=2)

ここに画像の説明を入力してください

編集:

@マット・ウィルキー

インポートの把握については、ここで、ArcObjectsモデル図を確認するか、.NET SDKヘルプドキュメントで特定のクラスまたはインターフェイスがどのネームスペースから呼び出されているかを確認する必要があります。継承のために、複数の名前空間を使用できる場合があります。

私はArcObjectsの専門家ではないので、CType()を使用していつキャストするかを理解するには、通常少し時間がかかります。このほとんどは、オンラインのサンプルから入手したものです。また、VB.NETの例の構文は、Pythonで行うことにより近いように見えますが、C#の例は、読みやすさの点で(私が理解できる場合)よりわかりやすくなっています。しかし、経験則として、私は通常、次の手順を実行します。

  1. 新しいCOMオブジェクト(通常はクラス)の変数を作成して、オブジェクトをインスタンス化します
  2. CTypeを使用してCOMオブジェクトをインターフェイスにキャストし、メソッドとプロパティへのアクセスを許可します。CTypeは、QueryInterface()を介してcomtypesインターフェイスポインターも返します。ポインタが返されると、そのプロパティとメソッドを操作できます。

適切な用語を使用しているかどうかわからない...私は主に一部のArcObjectsで「手を出して」いるPython開発者です...しかし、氷山の一角に触れただけです。

また、このヘルパー関数はすべてのArcObjectsオブジェクトライブラリ(.olb)をロードします。

def load_all():
    '''loads all object libraries'''
    from comtypes.client import GetModule
    mods = glob.glob(os.path.join(GetLibPath(), '*.olb'))
    for mod in mods:
        GetModule(mod)
    return


def GetLibPath():
    '''Reference to com directory which houses ArcObjects
    Ojbect Libraries (*.OLB)'''
    return glob.glob(os.path.join(arcpy.GetInstallInfo()['InstallDir'], 'com'))[0]

役立つ例をありがとう!Qの推力は、特定のタスクレシピではなく(意図されている)、レシピを作成するための情報を最初に取得して書き込む方法についての詳細です。たとえば、どのようにして知ってimport comtypes.gen.esriArcMapUI as esriArcMapUI、後で使用したのですかpMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(そしてそのステートメントの構文を明らかにしましたか)。
マットウィルキー2015年

私は元の回答を編集して、質問に答えようとしました。他にもいくつか例がありますが、上記のスニペットがおそらく最も読みやすいでしょう。
crmackey 2015年

また、私は昨年、この本を購入:amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/...
crmackey

7

関連するが、わずかに異なる他、投稿、私はEsriのArcObjectsのヘルプドキュメントの周りに頭をラップしようとしているPythonのユーザーのために関心のある答えを提供...

私は反対側から来ました:私はPythonのことを聞く前からArcObjectsを知っていました(このような投稿のおかげで、Pythonの簡単なスクリプトにいくつかの重要なArcObjectsを含めることができます(この投稿の例を参照))。継承、メソッド、プロパティを理解しようとすることへの苛立ちを覚えています。Yに関連するXを持っているようなジレンマ...それで、XからY.Method()にどうやって行きますか?

答えは、インターフェイスを実装するCoClassesを調べることです(ここで全文を参照してください)。基本的な例として、レイヤーに定義クエリがあるかどうかを確認したい場合は、それが何かを確認します。

C#の場合:

ILayer pLayer = pMap.get_Layer(LayerIndex);
IFeatureLayer pFtLayer = pLayer as IFeatureLayer; // also written pFtLayer = (IFeatureLayer) pLayer
IFeatureLayerDefinition pFtLayDef = (IFeatureLayerDefinition)pFtLayer; // also works as pFtLayDef = pFtLayer as IFeatureLayerDefinition;
if (pFtLayDef.DefinitionExpression.Length == 0)
    Console.WriteLine("No definition query");
else
    Console.WriteLine("Query is " + pFtLayDef.DefinitionExpression);

ctype(VBで目立つ)の代わりに、C#が使用()またはasキャストする場合、たとえばIObject x = (IObject)y;(基本的に)VBのIObject x = y as IObject;場合と同じdim x as IObject = ctype(y,IObject)です。

IFeatureLayerDefinitionに到達するにはIFeatureLayerが必要であることがわかります。 ここに画像の説明を入力してください

IFeatureLayerのヘルプドキュメントを読むと、次のことがわかります。 ここに画像の説明を入力してください

これは、ILayerのタイプがFeatureLayer(または他のCoClasses)であれば、ILayer-> IFeatureLayer-> IFeatureLayerDefに移動しても安全であることを示しています。

だから私は何をしているのですか?Iはインターフェイスを意味します。IがなければCoCo()であり、実際に使用するものはすべてIで始まる必要があり、新しいものを作成するか、型をチェックする場合Iをスキップします。インターフェイスには多くのCoClassを含めることができ、CoClassは多くのインターフェイスをサポートできますが、実際に機能するのはインターフェイスです。

Pythonでは:

# I'm assuming arcpy is already imported and comtypes installed
from comtypes.client import GetModule, CreateObject
mC = GetModule(r'C:\Your path\Desktop10.1\com\esriCarto.olb')
mU = GetModule(r'C:\Your path\Desktop10.1\com\esriArcMapUI.olb')
mF = GetModule(r"C:\Your path\Desktop10.1\com\esriFramework.olb")

import comtypes.gen.esriCarto as esriCarto
import comtypes.gen.esriFramework as esriFramework
import comtypes.gen.esriArcMapUI as esriArcMapUI

app = CreateObject(mF.AppROT, interface=mF.IAppROT) # a reference to the ArcMap application
pDoc = ctype(app.Item(0).Document,mU.IMxDocument)   # a reference to the current document
pMap = pDoc.FocusMap # the currently active map
pLayer = pMap.get_layer(LayerIndex)
pFtLayer = ctype(pLayer,esriCarto.IFeatureLayer)
pFtLayDef = ctype(pFtLayer,esriCarto.IFeatureLayerDefinition)
if len(pFtLayDef.DefinitionExpression) == 0:
    print("No definition expression")
else:
    print("Query is " + pFtLayDef.DefinitionExpression)

このサンプルは、現在のアプリケーションへの道を見つけるという点で、Cよりもわずかに多く機能します。これは、Pythonウィンドウまたはアドインでのみ使用できます。これをコマンドラインから実行しようとすると、アプリケーションはNullであり、スクリプトはnull参照例外でクラッシュします。


うわー、これを投稿してくれてありがとう!ArcObjectダイアグラムを理解するのに苦労しました。フェンスの反対側から来たあなた自身のような誰かからの入力があると便利です(多くの.NET ArcObjectsの経験)。私がいくつかの問題を抱えているのは、comtypesとpythonを介して、フィーチャデータセットにあるフィーチャクラスにアクセスすることです。以前は、最初にフィーチャデータセットを開いてからフィーチャクラスを開いてみたが、うまくいかなかった(一部のnullポインタを取得した)と思います。そのためのPythonサンプルはありますか?
crmackey 2015年

1
それほど多くはありませんが、私は実際にはpythonのcomtypeから始めているだけですが、ワークスペース(IFeatueWorkspace)オブジェクトからフィーチャクラスを開く場合は、名前を使用するだけで、フィーチャデータセットを含めないでください。フィーチャデータセット内にあり、すべての名前は一意です... help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/を参照してください。コードを使用して新しい質問を開くことができます。見てみましょう。フィーチャデータセットは、データセット(IFeatureDataset.Subsets)の反復で使用できますが、名前で開くだけの方がわかりやすくなっています。
Michael Stimson

1
@Michael Miles-Stimsonに感謝します。別のショットを与えます。わからない場合は、現在のコードで新しい質問を投稿します。
crmackey

@MichaelStimson私はPythonでcomtypeを使用してarcobjectsを使用できることを理解しています。私はアークオブジェクトを使ったことがありません。たとえば、comtypesとarcpyを使用してネットワークデータセットを構築するなどのタスクを実行するためのサンプルがない場合、comtypesを使用する前に、まずarcobjectsを理解する必要がありますか?または、arcpyとcomtypeを使用するために、comtypeを単独で学習することはできますか?
ケタール2017

1
@ ketar、PythonでArcObjectsを使用する前に、ArcObjectsについて少し知っておくことをお勧めします。PythonのArcObjectsのサンプルはまだ(まだ)ありませんが、ネットワークデータセット用のresources.arcgis.com/en/help/arcobjects-net/conceptualhelp/…のようなArcObjectsヘルプにサンプルがあります(ビルドはその最後のアイテムです)ページ)。ArcObjectsコードは、python(arcpy)よりもはるかに詳細です。個人的には、VBまたはC#でコーディングし、結果に満足したら、Pythonにコピー/貼り付けします。
マイケルスティムソン2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.