Accedere ad ArcObjects da Python?


132

Vorrei essere in grado di scrivere alcune cose che non sono state scoperte tramite arcgisscriptingArcPy.

Come posso accedere ad ArcObjects da Python?


3
Qualcuno ha pensato di usare questi metodi negli ambienti di produzione? Qualche anno fa all'UC ho parlato con uno degli sviluppatori ESRI C ++ che scrive la maggior parte dei loro moduli Python ed era fortemente contrario all'utilizzo di IronPython in un ambiente di produzione, ma è stato qualche anno fa.
Chad Cooper,

Risposte:


113

Scarica e installa i comtypes *, metti il Snippetsmodulo da Mark Cederholm in PYTHONPATH e sei 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

Per lo sfondo vedi le presentazioni di Mark Cederholm per UberPyGeeks su "Usare ArcObjects in Python" . Ci sono quelli separati per le prospettive degli sviluppatori VBA e C ++. Usano Visual Studio (sì Express è ok) e Windows SDK , ma questi non sono necessari, solo ArcGIS, Python e i tipi sono sufficienti.

Ottenere il modulo Snippets

* Nota per 10.1+ è necessario apportare una piccola modifica al automation.pymodulo Comtypes. Vedi ArcObjects + comtypes alla 10.1 .


Prossimi passi

... o: cervello impazzito ? Guardare gli esempi di codice c # ti fa nuotare gli occhi e provare come potresti non riuscire a pensare come una gru ? Guarda qui:


10
Questo thread è stato portato alla mia attenzione e sembra che ci siano alcune idee sbagliate in giro. NON è necessario Visual Studio o il compilatore MIDL per utilizzare ArcObjects in Python; tutto ciò che serve è il pacchetto comtypes. La mia presentazione includeva un esercizio avanzato nella creazione di un componente COM usando Python, ma era pensato per UberPyGeeks. Il file snippets.py contiene tutti gli esempi necessari.

1
@Mark, molte grazie per la correzione. Per essere chiari: nella ricetta sopra si potrebbero rimuovere i passaggi 1,3,4 purché i ctypes siano già installati?
matt wilkie,

2
Si. Testato in laboratorio. Hai solo bisogno di installare i comtypes.
RK,

@RK, fantastico! Grazie per la verifica e l'aggiornamento della risposta.
Matt Wilson

1
Modulo snippet rifuso come modulo ao installabile qui: github.com/maphew/arcplus/tree/master/arcplus/ao (aggiornato per 10.3, vedere le versioni precedenti del file per 10.2). Aggiornerà la risposta principale una volta che avrò eliminato alcuni bug.
Matt Wilkie,

32

Sì, la presentazione di Mark Cederholm che Matt Wilkie menziona sopra è un ottimo punto di partenza. La ricetta / codice che Matt presenta è sicuramente una chiazza e probabilmente il modo migliore per affrontare le cose. Volevo menzionare, tuttavia, il metodo della forza piuttosto bruta che sto usando in ArcGIS 10.0. Ho diversi script di automazione (standalone, al di fuori del limite dell'applicazione) che eseguo in questo modo e funzionano perfettamente. Se la velocità massima è un problema, potresti semplicemente scegliere la soluzione di Matt e farla finita.

Uso il pacchetto comtypes per forzare il wrapping di tutte le librerie ArcObjects (.olb). Quindi Python ha accesso a tutti gli ArcObjects. Ho ricevuto il codice di wrapping da Frank Perks tramite un post sul forum ESRI . Avevo il mio codice che essenzialmente faceva la stessa cosa, ma era gonfio e semplicemente funzionale, mentre il suo è molto più carino. Così:

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)

Quindi, praticamente uscito dalla presentazione di 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

Questo è tutto. Dovresti avere pieno accesso ad ArcObjects a partire dall'oggetto pApp che è IApplication sull'oggetto AppRef. Nella mia esperienza, il wrapping delle librerie ArcObjects alla prima esecuzione non è discutibilmente lento e, per le successive, il wrapping non avviene. Le librerie sono già raggruppate e compilate, quindi le cose sono molto più veloci.

Aggiunto: c'è una grande cautela che ne deriva. La funzione NewObj qui fornita presuppone che lo script Python sia in esecuzione nel processo. In caso contrario, questa funzione creerà oggetti nel processo Python (es. Fuori processo) e i riferimenti agli oggetti saranno errati. Per creare oggetti di processo da uno script Python esterno è necessario utilizzare IObjectFactory. Vedi i commenti e i suggerimenti di Kirk Kuykendall in questo post di stackexchange per maggiori informazioni.


1
La cosa bella di questo approccio è che non richiede l'installazione di Visual Studio, che è piuttosto pesante se l'unica cosa che farai mai è registrare gli oggetti com. Se avessi saputo di questo puro metodo pitone, probabilmente non avrei mai provato la via di Cederholm. ;-)
wilkie opaco

1
Ho ampliato un po 'questo concetto in questa risposta, rendendo le importazioni e il wrapping delle librerie dei tipi un processo in un solo passaggio: gis.stackexchange.com/questions/5017/…
blah238,

20

Come posso accedere ad arcobjects da Python?

Se quello che stai cercando è una funzionalità specifica che esiste ed è nel codice Arcobjects C ++, allora la soluzione migliore sarebbe quella di creare metodi C ++ per chiamarli .... e quindi creare un wrapper Python per accedere a quei metodi C ++.

Esistono diversi modi per accedere ai metodi C ++ da Python e molte persone che lo fanno usano uno strumento come SWIG per generare automaticamente le classi Python dalle firme del metodo C ++. È stata la mia esperienza che queste API autogenerate diventano piuttosto brutte quando passano tipi C ++ non nativi (int, float) e non sono mai molto " pitoniche ".

La mia soluzione raccomandata sarebbe quella di utilizzare l'API ctypes. Un grande tutorial è qui: http://python.net/crew/theller/ctypes/tutorial.html

I passaggi di base sono:

  1. scrivi alcune delle tue logiche di base in C ++, quelle in cui ritieni che le prestazioni di Python possano essere un problema
  2. Compilare la logica di base (in questo caso utilizzando le chiamate al metodo API Arc ++ C ++) dai file oggetto in una libreria condivisa (.so) o dinamica (.dll) utilizzando qualsiasi compilatore di sistema (nmake, make, ecc.)
  3. Scrivi una mappatura di ctypes tra la classe python e la firma del metodo C ++ [SWIG cerca di automatizzare questo, ma è facile, anche se si usano tipi di oggetti pazzi]
  4. Importa la libreria compilata nel tuo programma python e usa i binding di classe / metodo associati come qualsiasi altra classe python!

Questo è probabilmente il modo più generale per fare riferimento al codice C / C ++ dall'interno di Python, probabilmente sarà molto più semplice a lungo termine se non si ha a che fare con oggetti COM. Consentirà inoltre a tutte le funzionalità specifiche del sistema di risiedere nella compilazione dell'oggetto libreria collegato (quindi il python non sarà specifico dell'implementazione di sistema / python).


3
Questo è di gran lunga il metodo migliore per interagire con ArcObjects. L'uso dei comtypes è ottimo per la prototipazione, ma scrivere la propria estensione C si tradurrà in un buon design Pythonic (perché bisogna pensarci) e prestazioni migliori perché non si attraversa la barriera di oggetti C ++ / Python. Anche se per scopi pratici, è più difficile progettare / sviluppare / eseguire il debug in modo che il quick-and-dirty esca dalla finestra.
Jason Scheirer,

18

Un'altra opzione è utilizzare Python per .NET : è molto facile da configurare e può funzionare con qualsiasi DLL .NET incluso ArcObjects.

Non ho riscontrato alcun problema con gli oggetti in-process e l'apertura di un'istanza di ArcMap e l'aggiunta e la manipolazione dei livelli ha funzionato bene per me.

Gli unici requisiti sono una cartella contenente la libreria Python per .NET e un'installazione standard di Python.

Maggiori dettagli e script di esempio qui . Lo script di esempio può anche essere visualizzato direttamente all'indirizzo http://gist.github.com/923954

Sfortunatamente, mentre questo funziona senza problemi su una macchina di sviluppo locale, la sua distribuzione altrove richiede ArcObjects SDK e Visual Studio (compresa l'edizione Express gratuita) da installare. Vedere Distribuzione di DLL ArcObject .NET


La ricetta è chiara, lucida e ben presentata. Grazie Geographika! Ti incoraggio a includere uno snippet di script minimo nella tua risposta, in modo che, nel caso in cui il tuo sito venga sottoposto a lavori di ristrutturazione, la risposta possa restare autonoma.
matt wilkie,

1
Questo è esattamente quello che ho fatto in questo vecchio blogpost ( gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html ) e funziona come previsto, ma assicurati di restituire risultati che il tuo script Python comprende (stringhe, numeri o vuoto).
Samuel,


5

Un approccio che non vedo menzionato nelle altre risposte è quello di utilizzare gli stessi metodi utilizzati dalle stesse librerie arcpy. Ad esempio in C: \ Programmi \ ArcGIS \ Desktop10.0 \ arcpy \ arcpy \ cartography.py, vediamo Python che chiama gli strumenti ArcObjects usando alcune funzioni di correzione e conversione degli oggetti.

Non so quanto sia giusto pubblicarlo qui, poiché il codice dice "SEGRETI COMMERCIALI: ESRI PROPRIETARI E RISERVATI"; ma lo troverai anche altrove sul web. Comunque, questo sembra un modo relativamente semplice per chiamare funzioni come SimplifyBuilding_cartography()senza installare comtype o altre librerie extra.

Modificare:

Vedi i commenti di Jason qui sotto. Sembra che fare quanto sopra non ti comprerà molto.


Ho notato anche quello, sembra troppo voodoo però.
blah238,

1
Non sta chiamando un insieme di oggetti arco completamente esposti.
Jason Scheirer,

@JasonScheirer, sia come sia, apparentemente consente un maggior grado di accesso ad ArcObjects (penso) rispetto all'API arcpy diretta, e ha il vantaggio di non richiedere l'installazione di un'altra libreria. Quest'ultimo può essere importante se stai sviluppando strumenti che altre persone possono usare. Mi piacerebbe conoscere a fondo ciò a cui è possibile accedere tramite questo metodo: potrebbe essere sufficiente per determinati scopi. (Non riesco a controllare in questo momento però - non sono sulla LAN aziendale.)
LarsH

1
Posso assicurarti per certo che non lo è. Ne ho sviluppato gran parte.
Jason Scheirer,

1
Non puoi. "ArcObject" è un po 'un termine improprio in questo caso. Non esiste alcun modo per raggiungere gli oggetti sottostanti e non esiste alcun legame COM che esponga tutti gli ArcObjects ovunque in arcgisscripting / arcpy.
Jason Scheirer,
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.