Pautas para usar ArcObjects de Python

10

¿De lejos, acceder a ArcObjects desde Python? es mi Q&A más leída y referenciada en GIS Stack Exchange. A pesar de ese éxito, es probable que sea una de mis áreas más débiles en lo que respecta al uso real. Una gran parte de esa mala presentación se deriva de mi poca capacidad para leer y comprender los documentos de ArcObjects .

Entonces, para cualquier tarea, ¿cuáles son algunas pautas para traducir documentos .net / c ++ / java / ... y ejemplos a sus equivalentes en Python? (¿en qué idioma es el mejor para trabajar?) y ¿cuál es el mejor índice o página de inicio para comenzar? ¿En qué cosas debería centrarse, y probablemente al menos tan importante, qué se puede ignorar libremente?

Suponga que su audiencia es al menos un poco alfabetizada en Python y analfabeta en otros lenguajes de desarrollo. Indíquenos un pequeño ejercicio de codificación, desde la idea inicial y la investigación hasta los resultados de Python.

wilkie mate
fuente
1
Puede que no agregue nada a la conversación aquí, pero quiero declarar para el registro que estaría realmente interesado en ver este conjunto de tutoriales desarrollar. Gracias Matt Encontré un artículo de Darren Wiens creando un MXD desde cero y rellenando el diseño con guías. Parece también que el módulo de fragmentos de Mark Cederholm es realmente útil / utilizado a menudo en estos esfuerzos.
Jim
Un posible ejemplo para usar: gis.stackexchange.com/questions/86007/… (divulgación: es el problema en el que he estado trabajando, lo que provocó la Q. Gáneme a la respuesta (bien elaborada), obtenga todo el crédito ! ;-)
matt wilkie
Puede ser difícil entrar en los objetos de arco, los documentos de ayuda están bien, pero los ejemplos son mejores: uno de los mayores problemas es resolver la herencia de un objeto a otro, como tengo el objeto X, ahora ¿cómo obtengo el objeto Y? ? Si puede obtener Visual Studio 2008 o 2010 express (descarga gratuita si puede encontrarlo), instale el SDK y obtendrá los documentos de ayuda y un montón de ejemplos a nivel local.
Michael Stimson
1
@mattwilkie, con suerte, esto no enturbia las aguas demasiado ... pero para portar el código .NET existente a Python y descubrir la sintaxis de conversión de tipos, Python para .NET parece un poco más directo que el enfoque de los tipos. Dicho esto, acabo de descubrir Python para .NET y aún no lo he probado.
user2856
1
@mattwilkie acaba de descubrir que python.Net requiere que se instale el SDK de ArcGIS (a menos que los dlls del contenedor de ensamblado se distribuyan con el script ...) además de ArcGIS Desktop, por lo que no es tan portátil como el enfoque de los tipos.
user2856

Respuestas:

9

Tampoco soy muy fuerte en esta área, pero he modificado el módulo Snippets y he hecho un par de envoltorios para tareas muy simples. Tengo un ejemplo de solo agregar elementos de línea. El ejemplo debajo del bloque principal forma un triángulo con la vista de diseño justo afuera del documento.

Utilizo este script junto con otros cursores de búsqueda para crear tablas gráficas en el diseño a partir de líneas individuales y elementos de texto, pero eso se aleja rápidamente del ejemplo "simple". El siguiente código es bastante simple y utiliza una versión modificada de fragmentos:

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)

ingrese la descripción de la imagen aquí

Editar:

@matt wilkie

En cuanto a la determinación de las importaciones, allí es donde tendrá que revisar los Diagramas de modelo de ArcObjects o ver desde qué espacio de nombre se llama a una Clase o Interfaz en particular en los documentos de ayuda de .NET SDK. En algunos casos, se puede usar más de un espacio de nombres debido a la herencia.

No soy un experto en ArcObjects, por lo que generalmente me lleva un tiempo descubrir cuándo lanzar cosas con CType (). La mayoría de esto, lo he recogido de muestras en línea. Además, la sintaxis de los ejemplos de VB.NET parece estar más cerca de lo que haces en Python, pero los ejemplos de C # tienen más sentido para mí en términos de legibilidad (si eso tiene sentido). Pero, como regla general, generalmente sigo estos pasos:

  1. Cree una variable para un nuevo objeto COM (generalmente una clase) para crear una instancia de un objeto
  2. Use CType para transmitir el objeto COM a una interfaz (s) para permitir el acceso a métodos y procedimientos. CType también devolverá el puntero de interfaz de los tipos a través de QueryInterface (). Una vez que se devuelve el puntero, puede interactuar con sus propiedades y métodos.

No estoy seguro de si estoy usando la terminología adecuada o no ... Soy principalmente un desarrollador de Python que "incursiona" en algunos ArcObjects ... Sin embargo, solo he tocado la punta del iceberg.

Además, esta función auxiliar cargará todas las bibliotecas de objetos de 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]
crmackey
fuente
gracias por el útil ejemplo! El objetivo de la Q es (destinado a ser) menos en recetas de tareas específicas y más en cómo obtener y escribir la información para construir la receta en primer lugar. Por ejemplo, ¿cómo lo supo import comtypes.gen.esriArcMapUI as esriArcMapUIy luego lo usó pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(y descubrió la sintaxis en esa declaración)?
Matt Wilkie
Edité mi respuesta original para tratar de responder sus preguntas. También tengo algunos otros ejemplos, pero el fragmento anterior es probablemente el más legible.
crmackey
Además, compré este libro el año pasado: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey
7

En otra publicación, relacionada pero ligeramente diferente, proporcioné una respuesta que puede ser de interés para los usuarios de python que intentan entender los documentos de ayuda de Esri ArcObjects.

Vengo del otro lado: ya conocía ArcObjects mucho (mucho, mucho) antes de haber oído hablar de Python y gracias a publicaciones como estas, puedo incluir algunos ArcObjects críticos en la fácil creación de secuencias de comandos de Python (consulte esta publicación para ver un ejemplo ) Recuerdo la frustración de tratar de entender la herencia, los métodos y las propiedades; dilemas como Tengo X, que está relacionado con Y ... entonces, ¿cómo llego de X a Y? Método ()?

La respuesta es mirar las CoClasses que implementan la interfaz (vea el texto completo aquí ) ... para un ejemplo básico, si quiero ver si una capa tiene una consulta de definición, y si es así, ¿qué es?

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

En lugar de ctype(que es prominente en VB), C # utiliza ()o aspara la conversión, por ejemplo, IObject x = (IObject)y;es (fundamentalmente) el mismo IObject x = y as IObject;que sería dim x as IObject = ctype(y,IObject)en VB.

Puedo decir que necesito un IFeatureLayer para llegar a IFeatureLayerDefinition porque: ingrese la descripción de la imagen aquí

Y cuando lees el documento de ayuda para IFeatureLayer ves: ingrese la descripción de la imagen aquí

Lo que indica que es seguro ir a ILayer-> IFeatureLayer-> IFeatureLayerDef, siempre que ILayer sea del tipo FeatureLayer (o cualquiera de las otras CoClasses).

Entonces, ¿qué pasa con los yoes y no yoes? La interfaz I significa, es el bit que hace el trabajo, sin un I es un CoClass (un tipo ), por lo que cualquier cosa que quiera usar realmente debe comenzar con un I y si está creando uno nuevo o verificando el tipo luego omita el I. Una interfaz puede tener muchas CoClasses y una CoClass puede admitir muchas interfaces, pero es la interfaz la que realmente hace el trabajo.

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

Esta muestra hace un poco más que la C, ya que encuentra su camino hacia la aplicación actual, que solo estaría disponible en la ventana de Python o en un complemento, si intenta ejecutar esto desde la línea de comandos, la aplicación es Null y el script luego bloqueo con una excepción de referencia nula.

Michael Stimson
fuente
¡Guau, muchas gracias por publicar esto! He tenido dificultades para comprender los diagramas de ArcObject. Es bueno recibir información de alguien como usted que viene del otro lado de la cerca (mucha experiencia en .NET ArcObjects). Una cosa con la que he tenido algunas dificultades es acceder a una clase de entidad que reside en un conjunto de datos de entidades a través de comtypes y python. Creo que en el pasado intenté abrir primero el conjunto de datos de entidades y luego la clase de entidad, pero no tuve suerte (obtuve algunos punteros nulos). ¿Tienes alguna muestra de Python para eso?
crmackey
1
No tanto, en realidad solo estoy comenzando con comtypes en python, pero en cuanto a abrir una clase de entidad desde un objeto de espacio de trabajo (IFeatueWorkspace) solo use el nombre, no incluya el conjunto de datos de la entidad, no importa si está en un conjunto de datos de características, todos los nombres son únicos ... consulte help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/…. ¿Puede abrir una nueva pregunta con algún código? El conjunto de datos de características se puede usar con una iteración de conjuntos de datos (IFeatureDataset.Subsets), pero es más limpio simplemente abrir con el nombre.
Michael Stimson
1
Gracias @Michael Miles-Stimson. Le daré otra oportunidad. Si no puedo resolverlo, publicaré una nueva pregunta con mi código actual.
crmackey
@MichaelStimson Entiendo que puedo usar arcobjects en python usando comtypes. Nunca he usado arcobjects. Dado que no hay muestras en ninguna parte para realizar tareas, por ejemplo, crear un conjunto de datos de red utilizando comtypes y arcpy, ¿debo entender primero los arcobjects antes de poder usar comtypes? ¿O puedo aprender comtypes solo para usar arcpy y comtypes?
ketar
1
@ketar, es una buena idea saber un poco sobre ArcObjects antes de intentar usarlos en Python. Aunque no hay muchas muestras de ArcObjects en python (todavía) hay muestras en la ayuda de ArcObjects como resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/… para los conjuntos de datos de red (la compilación es el último elemento en eso página). El código de ArcObjects es significativamente más detallado que python (arcpy); personalmente codificaría en VB o C # y luego, cuando esté satisfecho con los resultados, copie / pegue en Python.
Michael Stimson