Diretrizes para usar o ArcObjects do Python

10

De longe, Acessando o ArcObjects a partir do Python? é a minha sessão de perguntas e respostas mais lida e referenciada no GIS Stack Exchange. Apesar desse sucesso, é provavelmente uma das minhas áreas mais fracas quando se trata de uso real. Grande parte dessa exibição ruim deriva da minha pouca capacidade de ler e entender os documentos do ArcObjects .

Portanto, para qualquer tarefa específica, quais são algumas diretrizes para traduzir documentos e exemplos .net / c ++ / java / ... em seus equivalentes em python? (qual idioma é o melhor para trabalhar nesse assunto?) e qual é o melhor índice ou página de destino para começar? que material deve ser focado e, provavelmente, pelo menos tão importante, o que pode ser livremente ignorado?

Suponha que seu público seja alfabetizado em python e analfabeto em outras linguagens de desenvolvimento. Siga-nos através de um pequeno exercício de codificação, desde a ideia inicial e a pesquisa até os resultados de python em funcionamento.

Matt Wilson
fonte
11
Pode não adicionar nada à conversa aqui, mas quero declarar que estou realmente interessado em ver esse conjunto de orientações se desenvolver. Obrigado Matt. Encontrei um artigo de Darren Wiens criando um MXD do zero e preenchendo o layout com guias. Parece também que o módulo de trechos de Mark Cederholm é realmente útil / frequentemente usado nesses esforços.
Jim
Um exemplo possível de usar: gis.stackexchange.com/questions/86007/… (divulgação: é o problema no qual estou trabalhando, que levou o P. Bata-me na resposta (bem-criada), obtenha todo o crédito ! ;-)
Matt Wilkie
Pode ser difícil entrar em objetos de arco, os documentos de ajuda são bons, mas os exemplos são melhores: um dos maiores problemas é descobrir a herança de um objeto para outro, como eu tenho o objeto X, agora como obtenho o objeto Y ? Se você conseguir usar o Visual Studio 2008 ou 2010 express (faça o download gratuito, se puder encontrá-lo) e, em seguida, instale o SDK, você receberá os documentos de ajuda e vários exemplos localmente.
Michael Stimson
11
@mattwilkie espero que isso não atrapalhe muito as águas ... mas, para portar o código .NET existente para python e descobrir a sintaxe de conversão de tipo, o python para .NET parece um pouco mais direto do que a abordagem de tipos. Dito isto, eu acabei de descobrir o python para .NET e ainda não o testei.
precisa saber é
11
O @mattwilkie acabou de descobrir python.Net exige que o ArcGIS SDK esteja instalado (a menos que as DLLs do wrapper de montagem sejam distribuídas com o script ...) além do ArcGIS Desktop, portanto, não é tão portátil quanto a abordagem de tipos.
precisa saber é o seguinte

Respostas:

9

Também não sou muito forte nessa área, mas modifiquei o módulo Snippets e criei alguns wrappers para tarefas muito simples. Eu tenho um exemplo de apenas adicionar elementos de linha. O exemplo no bloco principal forma um triângulo para a visualização do layout, fora do documento.

Eu uso esse script em conjunto com outros cursores de pesquisa e arcpy para criar tabelas gráficas no layout a partir de linhas individuais e elementos de texto, mas isso rapidamente se afasta do exemplo "simples". O código abaixo é bastante simples e usa uma versão modificada dos trechos:

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)

insira a descrição da imagem aqui

Editar:

@matt wilkie

Quanto a descobrir as importações, é aí que você terá que examinar os diagramas de modelos do ArcObjects ou ver em qual namespace uma classe ou interface específica está sendo chamada nos documentos de ajuda do .NET SDK. Em alguns casos, mais de um espaço para nome pode ser usado devido à herança.

Como não sou especialista em ArcObjects, normalmente levo um tempo para descobrir quando lançar as coisas com CType (). A maior parte disso, eu peguei de amostras online. Além disso, a sintaxe dos exemplos do VB.NET parece estar mais próxima do que você faz no Python, mas os exemplos em C # fazem mais sentido para mim em termos de legibilidade (se isso faz algum sentido). Mas, como regra geral, eu costumo seguir estas etapas:

  1. Crie uma variável para um novo objeto COM (geralmente uma classe) para instanciar um objeto
  2. Use CType para converter o objeto COM em uma interface para permitir o acesso a métodos e propriedades. CType também retornará o ponteiro de interface comtypes via QueryInterface (). Depois que o ponteiro é retornado, você pode interagir com suas propriedades e métodos.

Não tenho certeza se estou usando a terminologia adequada ou não ... Eu sou principalmente um desenvolvedor Python que "brinca" em alguns ArcObjects ... Mas apenas toquei a ponta do iceberg.

Além disso, essa função auxiliar carregará todas as bibliotecas de objetos do 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
fonte
obrigado pelo exemplo útil! O impulso do Q é (deve ser) menos em receitas de tarefas específicas e mais em como obter e escrever as informações para criar a receita em primeiro lugar. Por exemplo, como você sabia import comtypes.gen.esriArcMapUI as esriArcMapUIe depois usou pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(e descobriu a sintaxe nessa instrução)?
214156
Editei minha resposta original para tentar responder às suas perguntas. Também tenho alguns outros exemplos, mas o snippet acima é provavelmente o mais legível.
crmackey
Além disso, eu comprei este livro no ano passado: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/...
crmackey
7

Em outro post, relacionado, mas um pouco diferente, forneci uma resposta que pode ser de interesse para usuários de python que tentam entender os documentos de ajuda do Esri ArcObjects.

Eu vim do outro lado: eu já conhecia o ArcObjects muito (muito tempo) antes mesmo de ouvir falar de python e, graças a posts como esses, sou capaz de incluir alguns ArcObjects críticos no fácil script de python (veja este post, por exemplo) ) Lembro-me da frustração de tentar entender herança, métodos e propriedades; dilemas como eu tenho X, que é meio que relacionado a Y ... então, como faço para ir de X a Y.Method ()?

A resposta é olhar para as CoClasses que implementam a interface (veja o texto completo aqui ) .. para um exemplo básico, se eu quiser ver se uma camada tem uma consulta de definição e, em caso afirmativo, qual é:

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

Em vez de ctype(que é destaque no VB), o C # usa ()ou aspara transmissão, por exemplo, IObject x = (IObject)y;é (fundamentalmente) o mesmo IObject x = y as IObject;que seria dim x as IObject = ctype(y,IObject)no VB.

Posso dizer que preciso de um IFeatureLayer para chegar ao IFeatureLayerDefinition porque: insira a descrição da imagem aqui

E quando você lê o documento de ajuda do IFeatureLayer, vê: insira a descrição da imagem aqui

O que indica que é seguro acessar ILayer-> IFeatureLayer-> IFeatureLayerDef, desde que o ILayer seja do tipo FeatureLayer (ou qualquer um dos outros CoClasses).

Então, o que há com os "eu" e "não"? A interface I significa, é a parte que faz o trabalho, sem um I é um CoClass (um tipo ), então tudo o que você deseja realmente usar deve começar com um I e se você estiver criando um novo ou verificando o tipo depois pule o I. Uma interface pode ter muitas CoClasses e uma CoClass pode suportar muitas interfaces, mas é a interface que realmente faz o trabalho.

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

Este exemplo faz um pouco mais do que o C, na medida em que encontra o caminho para o aplicativo atual, que só estaria disponível na janela python ou em um complemento, se você tentasse executá-lo na linha de comando, o aplicativo seria Nulo e o script seria falha com uma exceção de referência nula.

Michael Stimson
fonte
Uau, muito obrigado por postar isso! Eu tive algumas dificuldades para entender os diagramas do ArcObject. É bom ter alguma contribuição de alguém como você que vem do outro lado da barreira (muita experiência no .NET ArcObjects). Uma coisa com a qual tive algumas dificuldades é acessar uma classe de recurso que reside em um conjunto de dados de recursos por meio de tipos e python. Acho que no passado tentei abrir o conjunto de dados do recurso primeiro e depois a classe do recurso, mas não tive sorte (obtendo alguns ponteiros nulos). Você tem alguma amostra de python para isso?
crmackey
11
Na verdade, estou começando apenas com comtypes em python, mas quanto à abertura de uma classe de recurso a partir de um objeto da área de trabalho (IFeatueWorkspace), basta usar o nome, não incluir o conjunto de dados do recurso - não importa se está em um conjunto de dados de recursos, todos os nomes são únicos ... consulte help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/… Você pode abrir uma nova pergunta com algum código e eu darei uma olhada. O conjunto de dados do recurso pode ser usado com uma iteração de conjuntos de dados (IFeatureDataset.Subsets), mas é mais fácil abrir apenas com o nome.
Michael Stimson
11
Obrigado @Michael Miles-Stimson. Vou tentar outra vez. Se não conseguir descobrir, postarei uma nova pergunta com meu código atual.
crmackey
@MichaelStimson Entendo que posso usar arcobjects em python usando comtypes. Eu nunca usei objetos de arco. Dado que não há amostras em nenhum lugar para executar tarefas, por exemplo, criar um conjunto de dados de rede usando comtypes e arcpy, preciso primeiro entender os objetos de arcada antes de poder usar comtypes? Ou posso apenas aprender comtypes por si só para usar arcty e comtypes?
ketar
11
@ketar, é uma boa idéia conhecer um pouco sobre o ArcObjects antes de tentar usá-los em python. Embora não haja muitos exemplos de ArcObjects em python (ainda), existem exemplos na ajuda do ArcObjects como resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/… para conjuntos de dados de rede (build é o último item nesse página). O código ArcObjects é significativamente mais detalhado que python (arcpy); pessoalmente, eu codificaria em VB ou C # e, quando estiver satisfeito com os resultados, copie / cole em python.
precisa