Acessando o ArcObjects a partir do Python?

132

Eu gostaria de poder criar scripts para algumas coisas que não são expostas via arcgisscriptingou ArcPy.

Como acesso o ArcObjects a partir do Python?

Matt Wilson
fonte
3
Alguém pensou em usar esses métodos em ambientes de produção? Alguns anos atrás, na UC, conversei com um dos desenvolvedores ESRI C ++ que escreve a maioria de seus módulos Python e ele era fortemente contra o uso do IronPython em um ambiente de produção - mas isso foi há alguns anos atrás.
Chad Cooper

Respostas:

113

Baixe e instale comtypes *, coloque o Snippetsmódulo de Mark Cederholm em PYTHONPATH e está tudo pronto.

from snippets102 import GetLibPath, InitStandalone
from comtypes.client import GetModule, CreateObject
m = GetModule(GetLibPath() + "esriGeometry.olb")
InitStandalone()
p = CreateObject(m.Point, interface=m.IPoint)
p.PutCoords(2,3)
print p.X, p.Y

Para mais informações, consulte as apresentações de Mark Cederholm para o UberPyGeeks em "Usando o ArcObjects em Python" . Existem outros para as perspectivas do desenvolvedor de VBA e C ++. Eles usam o Visual Studio (sim, Express está ok) e o Windows SDK , mas eles não são necessários, apenas ArcGIS, Python e comtypes são suficientes.

Obtendo o módulo Snippets

* Observe que para 10.1+ você precisa fazer uma pequena alteração automation.pyno módulo comtypes. Veja ArcObjects + comtypes em 10.1 .


Próximos passos

... ou: cérebro enlouquecido ? Olhar para os exemplos de código c # faz com que seus olhos nadem e tente da mesma maneira que você não consegue pensar como um guindaste ? Olhe aqui:

wilkie mate
fonte
10
Este tópico foi trazido à minha atenção e parece que existem alguns conceitos errados. Você NÃO precisa do Visual Studio ou do compilador MIDL para usar o ArcObjects no Python; tudo o que você precisa é do pacote de tipos. Minha apresentação incluiu um exercício avançado na criação de um componente COM usando Python, mas foi feito para o UberPyGeeks. O arquivo snippets.py contém todos os exemplos que você precisa.
1
@ Mark, muito obrigado pela correção. Para ficar claro: na receita acima, pode-se remover as etapas 1,3,4 enquanto o ctypes já estiver instalado?
mate Wilkie
2
Sim. Testei no laboratório. Você só precisa instalar os comtypes.
RK
@RK, ótimo! Obrigado pela verificação e atualização da resposta.
226126 Mattel
1
O módulo Snippets é reformulado como o módulo instalável ao aqui: github.com/maphew/arcplus/tree/master/arcplus/ao (atualizado para 10.3, consulte as versões anteriores do arquivo para 10.2). Irá atualizar a resposta principal assim que eu corrigir alguns erros.
214156
32

Sim, a apresentação de Mark Cederholm que Matt Wilkie menciona acima é um ótimo lugar para começar. A receita / código que Matt apresenta é certamente uma mancha e provavelmente a melhor maneira de fazer as coisas. Eu queria mencionar, no entanto, o método de força bruta que estou usando no ArcGIS 10.0. Tenho vários scripts de automação (autônomos, fora do limite do aplicativo) que são executados dessa maneira, e eles funcionam muito bem. Se a velocidade máxima é uma preocupação, no entanto, você pode simplesmente optar pela solução de Matt e terminar com ela.

Eu uso o pacote comtypes para forçar a quebra de todas as bibliotecas do ArcObjects (.olb). Então, o Python tem acesso a todos os ArcObjects. Eu recebi o código de embalagem de Frank Perks através de uma postagem no fórum da ESRI . Eu tinha meu próprio código que essencialmente fazia a mesma coisa, mas era inchado e meramente funcional, enquanto o dele é muito mais bonito. Assim:

import sys, os
if '[path to your Python script/module directory]' not in sys.path:
    sys.path.append('[path to your Python script/module directory]')

import comtypes
#force wrapping of all ArcObjects libraries (OLBs)
import comtypes.client
# change com_dir to whatever it is for you
com_dir = r'C:\Program Files (x86)\ArcGIS\Desktop10.0\com'
coms = [os.path.join(com_dir, x) for x in os.listdir(com_dir) if os.path.splitext(x)[1].upper() == '.OLB']
map(comtypes.client.GetModule, coms)

Então, praticamente direto da apresentação de Mark Cederholm:

import comtypes.gen.esriFramework

pApp = GetApp()

def GetApp():
    """Get a hook into the current session of ArcMap"""
    pAppROT = NewObj(esriFramework.AppROT, esriFramework.IAppROT)
    iCount = pAppROT.Count

    if iCount == 0:
        print 'No ArcGIS application currently running.  Terminating ...'
        return None
    for i in range(iCount):
        pApp = pAppROT.Item(i)  #returns IApplication on AppRef
        if pApp.Name == 'ArcMap':
            return pApp
    print 'No ArcMap session is running at this time.'
    return None

def NewObj(MyClass, MyInterface):
    """Creates a new comtypes POINTER object where\n\
    MyClass is the class to be instantiated,\n\
    MyInterface is the interface to be assigned"""
    from comtypes.client import CreateObject
    try:
        ptr = CreateObject(MyClass, interface=MyInterface)
        return ptr
    except:
        return None

É isso aí. Você deve ter acesso total ao ArcObjects começando com o objeto pApp que é IApplication no objeto AppRef. Na minha experiência, o agrupamento das bibliotecas do ArcObjects na primeira execução não é objetivamente lento e, para execuções subsequentes, o agrupamento não acontece. As bibliotecas já estão agrupadas e compiladas, portanto as coisas são muito mais rápidas.

Adicionado: há um grande cuidado que vem com isso. A função NewObj fornecida aqui assume que o script Python está sendo executado no processo. Caso contrário, essa função criará objetos no processo Python (ou seja, fora de processo) e as referências a objetos estarão erradas. Para criar objetos de processo a partir de um script Python externo, você deve usar IObjectFactory. Veja os comentários e dicas de Kirk Kuykendall nesta postagem de stackexchange para obter mais informações.

flauta celta
fonte
1
O bom dessa abordagem é que ela não requer a instalação do Visual Studio, que é bastante pesado se a única coisa que você fizer com ela for registrar os objetos com. Se eu soubesse desse método python puro, provavelmente nunca teria tentado a rota de Cederholm. ;-)
matt wilkie
1
I ampliou esse conceito um pouco nesta resposta, tornando as importações e envolvendo o tipo de bibliotecas de um one-passo processo: gis.stackexchange.com/questions/5017/...
blah238
20

Como faço para acessar arcobjects a partir de python?

Se o que você está procurando é uma funcionalidade específica que existe e está no código de objetos C ++, sua melhor aposta seria criar métodos C ++ para chamá-los ... e criar um wrapper python para acessar esses métodos C ++.

Existem várias maneiras de acessar métodos C ++ a partir de python, e muitas pessoas usam uma ferramenta como SWIG para gerar automaticamente as classes python a partir das assinaturas do método C ++. Tem sido minha experiência que essas APIs geradas automaticamente ficam bastante desagradáveis ​​ao passar tipos C ++ não nativos (int, floats) e nunca são muito ' pitônicas '.

Minha solução recomendada seria usar a API ctypes. Um ótimo tutorial está aqui: http://python.net/crew/theller/ctypes/tutorial.html

Os passos básicos são:

  1. escreva um pouco da sua lógica principal em C ++, aquela em que você acredita que o desempenho do python pode ser um problema
  2. Compile essa lógica principal (nesse caso, usando as chamadas do método ArcObject C ++ API) de arquivos de objeto em uma biblioteca compartilhada (.so) ou dinâmica (.dll) usando qualquer compilador de sistema (nmake, make, etc.)
  3. Escreva um mapeamento ctypes entre a classe python e a assinatura do método C ++ [SWIG tenta automatizar isso, mas é fácil, mesmo se você estiver usando tipos de objetos malucos]
  4. Importe a biblioteca compilada para o seu programa python e use as ligações de classe / método vinculadas como qualquer outra classe python!

Essa é provavelmente a maneira mais geral de fazer referência ao código C / C ++ no python; provavelmente será muito mais simples a longo prazo, se você não precisar lidar com objetos COM. Também permitirá que toda a funcionalidade específica do sistema resida na compilação do objeto de biblioteca vinculada (para que o python não seja específico da implementação do sistema / python).

tmarthal
fonte
3
Este é de longe o melhor método para interagir com o ArcObjects. O uso de comtypes é ótimo para a criação de protótipos, mas escrever sua própria extensão C resultará em um bom design Pythonic (porque você precisa pensar sobre isso) e em um melhor desempenho porque você não cruza tanto a barreira de objetos C ++ / Python. Embora, para propósitos práticos, seja mais difícil projetar / desenvolver / depurar, a solução é sair pela janela.
Jason Scheirer
18

Outra opção é usar o Python para .NET - isso é muito fácil de configurar e pode funcionar com qualquer DLL do .NET, incluindo o ArcObjects.

Não tive problemas com defeitos no processo, e abrir uma instância do ArcMap e adicionar e manipular camadas funcionou bem para mim.

Os únicos requisitos são uma pasta que contém a biblioteca Python para .NET e uma instalação padrão do Python.

Mais detalhes e exemplo de script aqui . O script de exemplo também pode ser visto diretamente em http://gist.github.com/923954

Infelizmente, embora isso funcione sem problemas em uma máquina de desenvolvimento local, sua implantação em outros lugares exige a instalação do ArcObjects SDK e do Visual Studio (incluindo a edição gratuita do Express). Consulte Implantando DLLs do ArcObject .NET

geographika
fonte
A receita é clara, lúcida e bem apresentada. Obrigado Geographika! Convido você a incluir um trecho mínimo de script em sua resposta, para que, no caso de seu site sofrer reformas, a resposta possa permanecer por si mesma.
Matt Wilkie
1
Foi exatamente o que eu fiz neste post antigo ( gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html ) e funciona como esperado, mas certifique-se de retornar os resultados que seu script Python entende (cadeias, números ou nulo).
Samuel
5

Uma abordagem que não vejo mencionada nas outras respostas é usar os mesmos métodos usados ​​pelas próprias bibliotecas do arcpy. Por exemplo, em C: \ Arquivos de programas \ ArcGIS \ Desktop10.0 \ arcpy \ arcpy \ cartography.py, vemos o Python chamando as ferramentas do ArcObjects usando algumas funções de conversão de objetos e fixargs.

Não sei quanto postar sobre isso aqui, já que o código diz "SEGREDOS DO COMÉRCIO: ESRI PROPRIETÁRIOS E CONFIDENCIAIS"; mas você também o encontrará em outros lugares da web. De qualquer forma, parece uma maneira relativamente fácil de chamar funções, SimplifyBuilding_cartography()sem instalar tipos ou qualquer outra biblioteca extra.

Editar:

Veja os comentários de Jason abaixo. Parece que fazer o exposto acima não lhe comprará muito.

LarsH
fonte
Notei que também parece muito vodu.
precisa saber é o seguinte
1
Não está chamando um conjunto de objetos de arcada totalmente exposto.
Jason Scheirer
@JasonScheirer, seja como for, aparentemente permite um maior grau de acesso ao ArcObjects (eu acho) do que a API direta do arcpy, e tem a vantagem de não exigir a instalação de outra biblioteca. O último pode ser importante se você estiver desenvolvendo ferramentas para outras pessoas usarem. Gostaria de saber a extensão completa do que você pode acessar por meio desse método - pode ser suficiente para determinados fins. (Não consigo verificar agora - embora eu não estou na empresa LAN.)
Larsh
1
Posso garantir-lhe, de fato, que não. Eu desenvolvi muito disso.
Jason Scheirer
1
Você não pode. "ArcObject" é um pouco inadequado nesse caso. Não há como acessar os objetos subjacentes e não há ligação COM expondo todos os ArcObjects em qualquer lugar do arcgisscripting / arcpy.
precisa saber é o seguinte