Richtlinien für die Verwendung von ArcObjects aus Python

10

Bei weitem Zugriff auf ArcObjects von Python? ist meine am häufigsten gelesene und referenzierte Frage und Antwort zu GIS Stack Exchange. Trotz dieses Erfolgs ist es wahrscheinlich einer meiner schwächsten Bereiche, wenn es um die tatsächliche Nutzung geht. Ein großer Teil dieser schlechten Darstellung ist auf meine schlechte Fähigkeit zurückzuführen, die ArcObjects-Dokumente zu lesen und zu verstehen .

Welche Richtlinien gibt es für eine bestimmte Aufgabe, um .net / c ++ / java / ... -Dokumente und -Beispiele in ihre Python-Entsprechungen zu übersetzen? (Aus welcher Sprache kann man am besten arbeiten?) und von welchem ​​Index oder welcher Zielseite aus kann man am besten beginnen? Auf welche Dinge sollte man sich konzentrieren und wahrscheinlich mindestens genauso wichtig, was kann frei ignoriert werden?

Angenommen, Ihr Publikum verfügt zumindest über etwas Python-Kenntnisse und ist in anderen Entwicklungssprachen Analphabeten. Führen Sie uns durch eine kleine Codierungsübung, von der ersten Idee und Forschung bis hin zu funktionierenden Python-Ergebnissen.

matt wilkie
quelle
1
Es wird hier möglicherweise nichts zur Konversation hinzugefügt, aber ich möchte für die Aufzeichnung angeben, dass ich wirklich daran interessiert wäre, zu sehen, wie sich diese exemplarischen Vorgehensweisen entwickeln. Danke Matt. Ich fand einen Artikel von Darren Wiens , der eine MXD von Grund auf neu erstellte und das Layout mit Anleitungen füllte. Es scheint auch, dass Mark Cederholms Snippets-Modul wirklich hilfreich ist / häufig bei diesen Bemühungen verwendet wird.
Jim
Ein mögliches Beispiel: gis.stackexchange.com/questions/86007/… (Offenlegung: Es ist das Problem, an dem ich gearbeitet habe, das das Q veranlasst hat. Schlagen Sie mich zu der (gut ausgearbeiteten) Antwort und erhalten Sie alle Gutschriften ! ;-)
Matt Wilkie
Es kann schwierig sein, auf Arcobjects zuzugreifen. Die Hilfedokumente sind in Ordnung, aber die Beispiele sind besser: Eines der größten Probleme besteht darin, die Vererbung eines Objekts an ein anderes zu ermitteln, so wie ich Objekt X habe. Wie erhalte ich nun Objekt Y. ? Wenn Sie Visual Studio 2008 oder 2010 Express in die Hände bekommen können (kostenloser Download, wenn Sie es finden), installieren Sie das SDK. Sie erhalten die Hilfedokumente und eine Reihe von Beispielen vor Ort.
Michael Stimson
1
@mattwilkie hoffentlich trübt dies das Wasser nicht zu sehr ... aber um vorhandenen .NET-Code nach Python zu portieren und die Typ-Casting-Syntax herauszufinden, sieht Python für .NET etwas einfacher aus als der comtypes-Ansatz. Trotzdem habe ich Python für .NET gerade erst entdeckt und noch nicht getestet.
user2856
1
@mattwilkie hat gerade Python.Net entdeckt. Für ArcGIS SDK muss zusätzlich zu ArcGIS Desktop das ArcGIS SDK installiert sein (es sei denn, die Assembly-Wrapper-DLLs werden mit dem Skript verteilt ...), daher nicht ganz so portabel wie der Comtypes-Ansatz.
user2856

Antworten:

9

Ich bin auch in diesem Bereich nicht sehr stark, aber ich habe das Snippets-Modul modifiziert und ein paar Wrapper für sehr einfache Aufgaben erstellt. Ich habe ein Beispiel für das Hinzufügen von Linienelementen. Das Beispiel unter dem Hauptblock bildet ein Dreieck mit der Layout - Ansicht gerade außerhalb des Dokuments.

Ich verwende dieses Skript in Verbindung mit einem anderen und bogenförmigen Suchcursor, um aus einzelnen Zeilen und Textelementen grafische Tabellen im Layout zu erstellen, aber das entfernt sich schnell vom "einfachen" Beispiel. Der folgende Code ist ziemlich einfach und verwendet eine modifizierte Version von Snippets:

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)

Geben Sie hier die Bildbeschreibung ein

Bearbeiten:

@ Matt Wilkie

Um die Importe herauszufinden, müssen Sie in den ArcObjects-Modelldiagrammen nachsehen, von welchem ​​Namespace eine bestimmte Klasse oder Schnittstelle in den .NET SDK-Hilfedokumenten aufgerufen wird. In einigen Fällen kann aufgrund der Vererbung mehr als ein Namespace verwendet werden.

Ich bin kein Experte für ArcObjects, daher brauche ich normalerweise eine Weile, um herauszufinden, wann ich Dinge mit CType () umsetzen soll. Das meiste davon habe ich online von Proben abgeholt. Auch die Syntax aus den VB.NET-Beispielen scheint näher an der in Python zu sein, aber die C # -Beispiele sind für mich in Bezug auf die Lesbarkeit sinnvoller (wenn dies sinnvoll ist). Als Faustregel befolge ich normalerweise die folgenden Schritte:

  1. Erstellen Sie eine Variable für ein neues COM-Objekt (normalerweise eine Klasse), um ein Objekt zu instanziieren
  2. Verwenden Sie CType, um das COM-Objekt in eine oder mehrere Schnittstellen umzuwandeln und den Zugriff auf Methoden und Eigenschaften zu ermöglichen. CType gibt auch den comtypes Interface Pointer über QueryInterface () zurück. Sobald der Zeiger zurückgegeben wird, können Sie mit seinen Eigenschaften und Methoden interagieren.

Ich bin mir nicht sicher, ob ich die richtige Terminologie verwende oder nicht ... Ich bin in erster Linie ein Python-Entwickler, der sich in einigen ArcObjects "versucht" ... Ich habe jedoch nur die Spitze des Eisbergs berührt.

Diese Hilfsfunktion lädt auch alle ArcObjects-Objektbibliotheken (.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]
Crmackey
quelle
danke für das nützliche beispiel! Der Schwerpunkt des Q liegt (soll) weniger auf bestimmten Aufgabenrezepten als vielmehr darauf, wie man die Informationen erhält und schreibt, um das Rezept überhaupt zu erstellen. Woher wussten Sie zum Beispiel, wie Sie es import comtypes.gen.esriArcMapUI as esriArcMapUIspäter verwenden pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(und die Syntax in dieser Anweisung aufdecken)?
Matt Wilkie
Ich habe meine ursprüngliche Antwort bearbeitet, um zu versuchen, Ihre Fragen zu beantworten. Ich habe noch ein paar andere Beispiele, aber das obige Snippet ist wahrscheinlich am besten lesbar.
Crmackey
Außerdem habe ich dieses Buch letztes Jahr gekauft: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey
7

In einem anderen, verwandten, aber etwas anderen Beitrag habe ich eine Antwort gegeben, die für Python-Benutzer von Interesse sein könnte, die versuchen, ihre Köpfe um die Hilfedokumente von Esri ArcObjects zu wickeln.

Ich kam von der anderen Seite: Ich kannte ArcObjects schon lange (lange, lange), bevor ich überhaupt von Python gehört hatte, und dank solcher Beiträge kann ich einige wichtige ArcObjects in die einfache Skripterstellung von Python einbeziehen ( ein Beispiel finden Sie in diesem Beitrag ). Ich erinnere mich an die Frustration, Vererbung, Methoden und Eigenschaften zu verstehen. Dilemmata wie ich habe X, das irgendwie mit Y zusammenhängt ... also wie komme ich von X zu Y.Method ()?

Die Antwort besteht darin, sich die CoClasses anzusehen, die die Schnittstelle implementieren (siehe Volltext hier ). Ein grundlegendes Beispiel, wenn ich sehen möchte, ob eine Ebene eine Definitionsabfrage hat, und wenn ja, was ist das?

In 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);

Anstelle von ctypeC # verwendet (was in VB prominent ist) ()oder aszum Gießen, zum Beispiel IObject x = (IObject)y;ist (grundsätzlich) die gleiche wie IObject x = y as IObject;der wäre dim x as IObject = ctype(y,IObject)in VB.

Ich kann sagen, dass ich einen IFeatureLayer benötige , um zu IFeatureLayerDefinition zu gelangen, weil: Geben Sie hier die Bildbeschreibung ein

Und wenn Sie das Hilfedokument für IFeatureLayer lesen, sehen Sie: Geben Sie hier die Bildbeschreibung ein

Dies zeigt an, dass es sicher ist, ILayer-> IFeatureLayer-> IFeatureLayerDef zu wählen, vorausgesetzt, der ILayer ist vom Typ FeatureLayer (oder einer der anderen CoClasses).

Also, was ist los mit dem Ich und nein Ich? Das I bedeutet Schnittstelle, es ist das Bit, das die Arbeit erledigt, ohne dass ein I eine CoClass (ein Typ ) ist. Alles, was Sie tatsächlich verwenden möchten, sollte mit einem I beginnen und wenn Sie ein neues erstellen oder den Typ überprüfen Überspringen Sie dann das I. Eine Schnittstelle kann viele CoClasses haben und eine CoClass kann viele Schnittstellen unterstützen, aber es ist die Schnittstelle, die tatsächlich die Arbeit erledigt.

In 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)

Dieses Beispiel macht etwas mehr als das C, da es den Weg zur aktuellen Anwendung findet, die nur im Python-Fenster oder in einem Add-In verfügbar wäre. Wenn Sie versuchen, dies über die Befehlszeile auszuführen, ist die Anwendung Null und das Skript würde dann Absturz mit einer Nullreferenzausnahme.

Michael Stimson
quelle
Wow, vielen Dank für das Posten! Ich hatte einige Probleme, die ArcObject-Diagramme zu verstehen. Es ist schön, Beiträge von jemandem wie Ihnen zu erhalten, der von der anderen Seite des Zauns kommt (viel Erfahrung mit .NET ArcObjects). Ich hatte einige Probleme damit, über comtypes und python auf eine Feature-Class zuzugreifen, die sich in einem Feature-Dataset befindet. Ich denke, in der Vergangenheit habe ich versucht, zuerst den Feature-Datensatz und dann die Feature-Class zu öffnen, hatte aber kein Glück (einige Nullzeiger zu bekommen). Hast du irgendwelche Python-Samples dafür?
Crmackey
1
Nicht so sehr, ich beginne eigentlich nur mit comtypes in Python, aber für das Öffnen einer Feature-Class aus einem Arbeitsbereichsobjekt (IFeatueWorkspace) verwenden Sie einfach den Namen, schließen Sie das Feature-Dataset überhaupt nicht ein - es spielt keine Rolle, ob Es befindet sich in einem Feature-Dataset, alle Namen sind eindeutig ... siehe help.arcgis.com/de/sdk/10.0/arcobjects_net/componenthelp/… Können Sie eine neue Frage mit etwas Code öffnen und ich werde einen Blick darauf werfen. Das Feature-Dataset kann mit einer Iteration von Datasets (IFeatureDataset.Subsets) verwendet werden, aber es ist sauberer, nur mit dem Namen zu öffnen.
Michael Stimson
1
Vielen Dank an Michael Miles-Stimson. Ich werde es noch einmal versuchen. Wenn ich es nicht herausfinden kann, werde ich eine neue Frage mit meinem aktuellen Code posten.
Crmackey
@MichaelStimson Ich verstehe, dass ich Arcobjects in Python mit Comtypes verwenden kann. Ich habe noch nie Arcobjects verwendet. Muss ich zuerst Arcobjects verstehen, bevor ich Comtypes verwenden kann, da es nirgendwo Beispiele für die Ausführung von Aufgaben gibt, z. B. zum Erstellen eines Netzwerkdatensatzes mit comtypes und arcpy? Oder kann ich Comtypes einfach selbst lernen, um Arcpy und Comtypes zu verwenden?
Ketar
1
@ketar, es ist eine gute Idee, ein wenig über ArcObjects zu wissen, bevor Sie versuchen, sie in Python zu verwenden. Obwohl es (noch) nicht viele Beispiele für ArcObjects in Python gibt, gibt es Beispiele in der ArcObjects-Hilfe wie resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/… für Netzwerk-Datasets (Build ist das letzte Element dazu Seite). ArcObjects-Code ist wesentlich ausführlicher als Python (arcpy). persönlich würde ich in VB oder C # codieren und dann, wenn ich mit den Ergebnissen zufrieden bin, in Python kopieren / einfügen.
Michael Stimson