Linee guida per l'utilizzo di ArcObjects da Python


10

Di gran lunga, accedere ad ArcObjects da Python? è la mia domanda e risposta più letta e referenziata su GIS Stack Exchange. Nonostante questo successo, è probabilmente una delle mie aree più deboli quando si tratta di un uso effettivo. Gran parte di quel povero spettacolo deriva dalla mia scarsa capacità di leggere e comprendere i documenti di ArcObjects .

Quindi, per ogni dato compito quali sono alcune linee guida per la traduzione di documenti ed esempi .net / c ++ / java / ... nei loro equivalenti python? (quale lingua è la migliore da utilizzare per quella materia?) e qual è il miglior indice o pagina di destinazione da cui iniziare? su quali cose dovrebbe essere focalizzato, e probabilmente almeno altrettanto importante, cosa può essere liberamente ignorato?

Supponiamo che il tuo pubblico sia almeno un po 'esperto di pitone e analfabeta in altri linguaggi di sviluppo. Guidaci attraverso un piccolo esercizio di codifica, dall'idea iniziale e dalla ricerca ai risultati di pitone funzionanti.


1
Potrebbe non aggiungere nulla alla conversazione qui, ma voglio dichiarare per il disco che sarei davvero interessato a vedere sviluppare questo set di procedure dettagliate. Grazie Matt. Ho trovato un articolo di Darren Wiens che creava un MXD da zero e popolava il layout con le guide. Sembra anche che il modulo di frammenti di Mark Cederholm sia davvero utile / spesso usato in questi sforzi.
Jim,

Un possibile esempio da usare: gis.stackexchange.com/questions/86007/… (divulgazione: è il problema a cui ho lavorato, che mi ha spinto a battermi alla risposta (ben realizzata), ottenere tutto il merito ! ;-)
matt wilkie

Gli oggetti arco possono essere difficili da comprendere, i documenti di aiuto sono OK ma gli esempi sono migliori: uno dei maggiori problemi è capire l'eredità di un oggetto su un altro, come se avessi l'oggetto X, ora come ottengo l'oggetto Y ? Se riesci a mettere le mani su Visual Studio 2008 o 2010 express (download gratuito se lo trovi), installa l'SDK per ottenere localmente i documenti di aiuto e un mucchio di esempi.
Michael Stimson,

1
@mattwilkie si spera che questo non offuschi troppo le acque ... ma per il porting del codice .NET esistente su Python e per capire la sintassi del cast di tipo, Python per .NET sembra un po 'più semplice rispetto all'approccio dei comtypes. Detto questo, ho appena scoperto Python per .NET e non l'ho ancora testato.
user2856

1
@mattwilkie ha appena scoperto python.Net richiede che ArcGIS SDK sia installato (a meno che le DLL del wrapper di assembly non siano distribuite con lo script ...) oltre ad ArcGIS Desktop, quindi non portatile come l'approccio dei comtypes.
user2856

Risposte:


9

Nemmeno io sono molto forte in quest'area, ma ho modificato il modulo Snippets e ho creato un paio di wrapper per compiti molto semplici. Ho un esempio dell'aggiunta di elementi di linea. L'esempio sotto il blocco principale forma un triangolo per la vista del layout appena fuori dal documento.

Uso questo script insieme ad altri cursori di ricerca arcpy per creare tabelle grafiche nel layout da singole linee ed elementi di testo, ma questo si allontana rapidamente dall'esempio "semplice". Il codice seguente è abbastanza semplice e utilizza una versione modificata degli snippet:

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)

inserisci qui la descrizione dell'immagine

Modificare:

@matt wilkie

Per quanto riguarda la determinazione delle importazioni, è qui che dovrete guardare i diagrammi del modello ArcObjects o vedere da quale spazio dei nomi viene richiamata una determinata classe o interfaccia nei documenti della guida di .NET SDK. In alcuni casi è possibile utilizzare più di uno spazio dei nomi a causa dell'ereditarietà.

Non sono un esperto di ArcObjects, quindi di solito mi ci vuole un po 'per capire quando lanciare le cose con CType (). Gran parte di questo, ho raccolto da campioni online. Inoltre, la sintassi degli esempi VB.NET sembra essere più vicina a ciò che fai in Python, ma gli esempi C # hanno più senso per me in termini di leggibilità (se questo ha un senso). Ma, di regola, di solito seguo questi passaggi:

  1. Creare una variabile per un nuovo oggetto COM (in genere una classe) per creare un'istanza di un oggetto
  2. Utilizzare CType per trasmettere l'oggetto COM a un'interfaccia (s) per consentire l'accesso a metodi e proprietà. CType restituirà anche il puntatore a interfaccia dei tipi tramite QueryInterface (). Una volta restituito il puntatore, è quindi possibile interagire con le sue proprietà e metodi.

Non sono sicuro se sto usando la terminologia corretta o no ... Sono principalmente uno sviluppatore Python che "diletta" in alcuni ArcObjects ... Tuttavia ho solo toccato la punta dell'iceberg.

Inoltre, questa funzione di supporto caricherà tutte le librerie di oggetti 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]

grazie per l'esempio utile! La spinta del Q è (intesa per essere) meno su ricette di attività specifiche e di più su come si ottengono e scrivono le informazioni per costruire la ricetta in primo luogo. Ad esempio, come hai conosciuto import comtypes.gen.esriArcMapUI as esriArcMapUIe poi utilizzato in pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(e scopri la sintassi in quella dichiarazione)?
Matt Wilson

Ho modificato la mia risposta originale per cercare di rispondere alle tue domande. Ho anche alcuni altri esempi, ma lo snippet sopra è probabilmente il più leggibile.
Crmackey,

Inoltre, ho acquistato questo libro l'anno scorso: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey

7

In un altro post correlato ma leggermente diverso, ho fornito una risposta che potrebbe essere di interesse per gli utenti di Python che cercano di avvolgere la testa attorno ai documenti di aiuto di Esri ArcObjects

Venivo dall'altra parte: conoscevo già ArcObjects molto (molto a lungo) prima ancora di aver sentito parlare di Python e grazie a post come questi sono in grado di includere alcuni ArcObjects critici nella semplice creazione di script di Python (vedi questo post per un esempio ). Ricordo la frustrazione nel cercare di comprendere eredità, metodi e proprietà; dilemmi come se avessi X che è in qualche modo correlato a Y ... quindi come posso ottenere da X a Y.Method ()?

La risposta è guardare le CoClass che implementano l'interfaccia (vedere il testo completo qui ) .. per un esempio di base, se voglio vedere se un layer ha una query di definizione, e in caso affermativo di cosa si tratta:

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

Invece di ctype(che è prominente in VB) C # usa ()o asper il casting, per esempio IObject x = (IObject)y;è (fondamentalmente) lo stesso di quello IObject x = y as IObject;che sarebbe dim x as IObject = ctype(y,IObject)in VB.

Posso dire che ho bisogno di un IFeatureLayer per arrivare a IFeatureLayerDefinition perché: inserisci qui la descrizione dell'immagine

E quando leggi il documento di aiuto per IFeatureLayer vedi: inserisci qui la descrizione dell'immagine

Ciò indica che è sicuro utilizzare ILayer-> IFeatureLayer-> IFeatureLayerDef, a condizione che ILayer sia del tipo FeatureLayer (o una qualsiasi delle altre CoClass).

Allora, cosa succede con l'Io e no io? L'interfaccia I significa che è il bit che funziona, senza un I è una CoClass (un tipo ), quindi tutto ciò che si desidera effettivamente utilizzare dovrebbe iniziare con un I e se ne stai creando uno nuovo o controllando il tipo quindi salta la I. Un'interfaccia può avere molti CoClass e una CoClass può supportare molte interfacce ma è l'interfaccia che effettivamente fa il lavoro.

In pitone:

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

Questo esempio fa leggermente più della C in quanto trova la sua strada per l'applicazione corrente, che sarebbe disponibile solo nella finestra di Python o in un componente aggiuntivo, se provassi a eseguirlo dalla riga di comando l'applicazione è Null e lo script quindi arresto anomalo con un'eccezione di riferimento null.


Wow, grazie mille per aver pubblicato questo! Ho avuto delle difficoltà a comprendere i diagrammi ArcObject. È bello avere un input da qualcuno come te che proviene dall'altra parte della recinzione (molta esperienza di .NET ArcObjects). Una cosa con cui ho avuto delle difficoltà è accedere a una classe di funzionalità che risiede in un set di dati di funzionalità tramite comtypes e python. Penso che in passato ho provato ad aprire prima il set di dati delle funzionalità e poi la classe di funzionalità ma non ho avuto fortuna (ottenendo alcuni puntatori null). Hai qualche esempio di pitone per quello?
Crmackey,

1
Non tanto, sto davvero solo iniziando con i tipi di carattere in Python, ma per aprire una classe di caratteristiche da un oggetto dell'area di lavoro (IFeatueWorkspace) basta usare il nome, non includere affatto il set di dati della caratteristica - non importa se è in un set di dati di funzionalità, tutti i nomi sono univoci ... vedi help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/… Puoi aprire una nuova domanda con del codice e darò un'occhiata. Il set di dati di funzionalità può essere utilizzato con un'iterazione di set di dati (IFeatureDataset.Subset) ma è più semplice aprire solo con il nome.
Michael Stimson,

1
Grazie @Michael Miles-Stimson. Gli darò un altro colpo. Se non riesco a capirlo, posterò una nuova domanda con il mio codice attuale.
Crmackey,

@MichaelStimson Capisco che posso usare arcobjects in Python usando i comtypes. Non ho mai usato arcobjects. Dato che non ci sono campioni da nessuna parte per eseguire attività, ad esempio costruire un set di dati di rete usando comtypes e arcpy, devo prima capire arcobjects prima di poter usare comtypes? O posso semplicemente imparare i comtypes da solo per usare arcpy e comtypes?
chetar

1
@ketar, è una buona idea sapere qualcosa su ArcObjects prima di provare a usarli in Python. Sebbene non ci siano molti esempi di ArcObjects in Python (ancora), ci sono esempi nella guida di ArcObjects come risorse.arcgis.com/en/help/arcobjects-net/conceptualhelp/… per i set di dati di rete (build è l'ultimo elemento pagina). Il codice ArcObjects è significativamente più dettagliato di python (arcpy); personalmente codificherei in VB o C # e poi quando sarò contento dei risultati copierei / incollerei in Python.
Michael Stimson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.