Accéder à ArcObjects à partir de Python?

132

J'aimerais pouvoir arcgisscriptingcréer un script pour certaines choses qui ne sont pas exposées via ou ArcPy.

Comment accéder à ArcObjects à partir de Python?

Matt Wilkie
la source
3
Quelqu'un a une idée sur l'utilisation de ces méthodes dans des environnements de production? Il y a quelques années, à l'UC, j'ai parlé à l'un des développeurs ESRI C ++ qui écrit la plupart de leurs modules Python. Il était fermement opposé à l'utilisation d'IronPython dans un environnement de production, mais c'était il y a quelques années.
Chad Cooper

Réponses:

113

Téléchargez et installez les types *, mettez le Snippetsmodule de Mark Cederholm dans PYTHONPATH, et le tour est joué.

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

Pour en savoir plus, reportez-vous aux présentations de Mark Cederholm pour UberPyGeeks sur "Utilisation d’ArcObjects en Python" . Il en existe différents pour les perspectives de développement VBA et C ++. Ils utilisent Visual Studio (oui Express est ok) et le Kit de développement logiciel (SDK) Windows , mais ils ne sont pas obligatoires. Seuls ArcGIS, Python et les types de caractères suffisent.

Obtenir le module Snippets

* Remarque pour 10.1+ vous devez apporter une petite modification automation.pydans le module comtypes. Voir les types de types ArcObjects + à la section 10.1 .


Prochaines étapes

... ou: le cerveau est devenu sournois ? En regardant les exemples de code c #, vos yeux vont nager et vous ne pouvez pas penser comme une grue . Regardez ici:

wilkie mat
la source
10
Ce fil a été porté à mon attention et il semble y avoir quelques idées fausses qui circulent. Vous n'avez PAS besoin de Visual Studio ni du compilateur MIDL pour utiliser ArcObjects en Python. tout ce dont vous avez besoin, c'est du paquet comtypes. Ma présentation incluait un exercice avancé sur la création d'un composant COM à l'aide de Python, mais elle était destinée à UberPyGeeks. Le fichier snippets.py contient tous les exemples dont vous avez besoin.
1
@ Mark, merci beaucoup pour la correction. Pour être clair: dans la recette ci-dessus, on pourrait supprimer les étapes 1,3,4 tant que ctypes est déjà installé?
mat Wilkie
2
Ouais. Testé au laboratoire. Vous avez juste besoin d'installer les comtypes.
RK
@RK, génial! Merci pour la vérification et la mise à jour de la réponse.
mat wilkie
1
Module Snippets refondus en installable ao modules ici: github.com/maphew/arcplus/tree/master/arcplus/ao (mis à jour pour 10.3, voir les versions précédentes de fichier pour 10.2). Met à jour la réponse principale une fois que certains bugs ont été supprimés.
Matt Wilkie
32

Oui, l'exposé de Mark Cederholm mentionné par Matt Wilkie est un excellent point de départ. La recette / le code présenté par Matt est certainement une excellente solution et constitue probablement la meilleure façon de procéder. Je voulais toutefois mentionner la méthode de force brute que j'utilise dans ArcGIS 10.0. J'ai plusieurs scripts d'automatisation (autonomes, en dehors des limites de l'application) que je lance de cette façon et ils fonctionnent parfaitement. Si la vitesse maximale vous préoccupe, vous pouvez simplement choisir la solution de Matt et en finir.

J'utilise le package comtypes pour forcer le wrapping de toutes les bibliothèques ArcObjects (.olb). Ensuite, Python a accès à tous les ArcObjects. Le code d’emballage de Frank Perks m’a été envoyé sur un forum ESRI . J'avais mon propre code qui faisait essentiellement la même chose, mais il était gonflé et simplement fonctionnel, alors que le sien est beaucoup plus joli. Alors:

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)

Ensuite, assez directement dans la présentation 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

C'est ça. Vous devriez avoir un accès complet à ArcObjects à partir de l'objet pApp qui est IApplication sur l'objet AppRef. D'après mon expérience, l'encapsulation des bibliothèques ArcObjects lors de la première exécution n'est pas trop lente, et pour les exécutions suivantes, l'encapsulation n'a pas lieu. Les bibliothèques sont déjà encapsulées et compilées, ce qui accélère considérablement les choses.

Ajouté: Il y a une grande prudence qui vient avec cela. La fonction NewObj donnée ici suppose que le script Python est en cours d'exécution. Sinon, cette fonction créera des objets dans le processus Python (c.-à-d. Hors processus) et les références aux objets seront fausses. Pour créer des objets en cours à partir d'un script Python externe, vous devez utiliser IObjectFactory. Voir les commentaires et astuces de Kirk Kuykendall dans cet article stackexchange pour plus d'informations.

flûte celtique
la source
1
La bonne chose à propos de cette approche est qu’elle ne nécessite pas l’installation de Visual Studio, ce qui est assez lourd si la seule chose à faire est d’enregistrer les objets com. Si j'avais eu connaissance de cette méthode python pure, je n'aurais probablement jamais essayé l'itinéraire de Cederholm. ;-)
matt wilkie le
1
J'ai développé un peu ce concept dans cette réponse, faisant des importations et encapsulant les bibliothèques de types un processus en une étape: gis.stackexchange.com/questions/5017/…
blah238
20

Comment accéder aux arcobjects depuis python?

Si vous recherchez des fonctionnalités spécifiques existantes dans le code Arcobjects C ++, il est conseillé de créer des méthodes C ++ pour les appeler, puis de créer un wrapper python pour accéder à ces méthodes C ++.

Il existe de nombreuses façons d'accéder aux méthodes C ++ à partir de python, et beaucoup de personnes qui le font utilisent un outil tel que SWIG pour générer automatiquement les classes python à partir des signatures de méthodes C ++. D'après mon expérience, ces API générées automatiquement sont plutôt méchantes lorsqu'elles transmettent des types C ++ non natifs (int, floats) et ne sont jamais très " pythoniques ".

Ma solution recommandée serait d'utiliser l'API ctypes. Un excellent tutoriel est ici: http://python.net/crew/theller/ctypes/tutorial.html

Les étapes de base sont les suivantes:

  1. écrivez une partie de votre logique de base en C ++, celles pour lesquelles vous pensez que les performances en python pourraient être un problème
  2. Compilez cette logique de base (dans ce cas en utilisant les appels de méthode de l'API ArcObject C ++) à partir de fichiers objet dans une bibliothèque partagée (.so) ou dynamique (.dll) à l'aide du compilateur système (nmake, make, etc.).
  3. Ecrivez un mappage ctypes entre la classe python et la signature de la méthode C ++ [SWIG essaie de l'automatiser, mais c'est facile, même si vous utilisez des types d'objet fous]
  4. Importez la bibliothèque compilée dans votre programme python et utilisez les liaisons de classe / méthode liées comme toute autre classe python!

C’est probablement une façon plus générale de référencer le code C / C ++ à partir de python. Cela sera probablement beaucoup plus simple à long terme si vous n’avez pas à traiter avec des objets COM. Cela permettra également à toutes les fonctionnalités spécifiques du système de résider dans la compilation de l'objet de bibliothèque lié (le python ne sera donc pas spécifique à l'implémentation système / python).

tmarthal
la source
3
C'est de loin la meilleure méthode pour interagir avec ArcObjects. L'utilisation de comtypes est excellente pour le prototypage, mais l'écriture de votre propre extension C se traduira par une bonne conception Pythonic (car il faut y penser) et de meilleures performances car vous ne franchissez pas autant la barrière d'objet C ++ / Python. Bien que, pour des raisons pratiques, il soit plus difficile de concevoir / développer / déboguer, le fast-and-------------------de-ne-passer à la fenêtre.
Jason Scheirer
18

Une autre option consiste à utiliser Python pour .NET . Il est très facile à configurer et peut fonctionner avec toutes les DLL .NET, y compris ArcObjects.

Je n'ai rencontré aucun problème d'objet en cours de processus, et l'ouverture d'une instance d'ArcMap et l'ajout et la manipulation de couches ont bien fonctionné pour moi.

Les seules conditions requises sont un dossier contenant la bibliothèque Python pour .NET et une installation Python standard.

Plus de détails et exemple de script ici . L'exemple de script est également visible directement à l' adresse http://gist.github.com/923954.

Malheureusement, bien que cela fonctionne sans problème sur une machine de développement locale, son déploiement ailleurs nécessite l'installation du SDK ArcObjects et de Visual Studio (y compris l'édition Express gratuite). Voir Déploiement de DLL ArcObject .NET

geographika
la source
La recette est claire, lucide et bien présentée. Merci Geographika! Je vous encourage à inclure un extrait de script minimal dans votre réponse, afin que, si votre site subissait des rénovations, la réponse soit indépendante.
mat wilkie
1
C’est exactement ce que j’ai fait dans cet ancien article de blog ( gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html ) et cela fonctionne comme prévu, mais assurez-vous de renvoyer les résultats que votre script Python comprend (chaînes, nombres ou nul).
Samuel
5

Une approche que je ne vois pas mentionnée dans les autres réponses consiste à utiliser les mêmes méthodes que celles utilisées par les bibliothèques arcpy. Par exemple, dans C: \ Program Files \ ArcGIS \ Desktop10.0 \ arcpy \ arcpy \ cartography.py, nous voyons Python appeler des outils ArcObjects à l'aide de fixargs et de fonctions de conversion d'objet.

Je ne sais pas combien il est acceptable de poster à ce sujet ici, car le code dit "SECRETS COMMERCIAUX: ESRI PROPRIÉTAIRE ET CONFIDENTIEL"; mais vous le trouverez ailleurs sur le Web. Quoi qu’il en soit, cela semble être un moyen relativement facile d’appeler des fonctions, SimplifyBuilding_cartography()sans installer de types de caractères ou d’autres bibliothèques supplémentaires.

Modifier:

Voir les commentaires de Jason ci-dessous. On dirait que faire ce qui précède ne vous achètera pas beaucoup.

Larsh
la source
J'ai aussi remarqué que cela semblait trop vaudou.
Blah238
1
Il n'appelle pas un ensemble entièrement exposé d'arcobjects.
Jason Scheirer
Quoi qu'il en soit, @JasonScheirer autorise apparemment un plus grand accès à ArcObjects (je pense) que l'API straight arcpy, et présente l'avantage de ne pas nécessiter l'installation d'une autre bibliothèque. Ce dernier peut être important si vous développez des outils que d'autres personnes peuvent utiliser. J'aimerais savoir dans quelle mesure vous pouvez avoir accès à cette méthode - cela pourrait suffire à certaines fins. (Je ne peux pas vérifier pour le moment, je ne suis pas sur le réseau local de l'entreprise.)
LarsH
1
Je peux vous assurer que ce n'est pas le cas. J'en ai développé beaucoup.
Jason Scheirer
1
Vous ne pouvez pas. "ArcObject" est un peu impropre dans ce cas. Il n'y a aucun moyen d'accéder aux objets sous-jacents et il n'y a pas de liaison COM qui expose tous les ArcObjects où que ce soit dans arcgisscripting / arcpy.
Jason Scheirer