Instructions pour l'utilisation d'ArcObjects à partir de Python

10

De loin, accéder à ArcObjects à partir de Python? est mon Q&A le plus lu et référencé sur GIS Stack Exchange. Malgré ce succès, c'est probablement l'un de mes points faibles en matière d'utilisation réelle. Une grande partie de cette mauvaise performance provient de ma faible capacité à lire et à comprendre les documents ArcObjects .

Donc, pour une tâche donnée, quelles sont les lignes directrices pour traduire des documents et des exemples .net / c ++ / java / ... dans leurs équivalents python? (quelle langue est la meilleure pour travailler d'ailleurs?) et quel est le meilleur index ou page de destination pour commencer? quels éléments devraient être axés sur, et probablement au moins aussi importants, ce qui peut être librement ignoré?

Supposons que votre public soit au moins un peu alphabétisé en python et analphabète dans d'autres langages de développement. Guidez-nous à travers un petit exercice de codage, de l'idée initiale et de la recherche aux résultats de travail en python.

Matt Wilkie
la source
1
Cela n'ajoute rien à la conversation ici, mais je tiens à dire pour le compte rendu que je serais vraiment intéressé à voir cet ensemble de procédures pas à pas se développer. Merci Matt. J'ai trouvé un article de Darren Wiens créant un MXD à partir de zéro et remplissant la mise en page avec des guides. Il semble également que le module d'extraits de Mark Cederholm soit vraiment utile / souvent utilisé dans ces efforts.
Jim
Un exemple possible à utiliser: gis.stackexchange.com/questions/86007/… (divulgation: c'est le problème sur lequel j'ai travaillé, qui a incité le Q. Battez-moi à la réponse (bien conçue), obtenez tout le crédit ! ;-)
matt wilkie
Les Arcobjects peuvent être difficiles à accéder, les documents d'aide sont corrects mais les exemples sont meilleurs: l'un des plus gros problèmes est de déterminer l'héritage d'un objet à un autre, comme j'ai l'objet X, maintenant comment obtenir l'objet Y ? Si vous pouvez mettre la main sur Visual Studio 2008 ou 2010 express (téléchargement gratuit si vous le trouvez), installez le SDK, vous obtiendrez les documents d'aide et un tas d'exemples localement.
Michael Stimson
1
@mattwilkie j'espère que cela ne trouble pas trop les eaux ... mais pour le portage du code .NET existant vers python et pour trouver la syntaxe de conversion de type, python pour .NET semble un peu plus simple que l'approche comtypes. Cela dit, je viens de découvrir python pour .NET et je ne l'ai pas encore testé.
user2856
1
@mattwilkie vient de découvrir que python.Net nécessite que le SDK ArcGIS soit installé (à moins que les DLL de wrapper d'assembly ne soient distribuées avec le script ...) en plus d'ArcGIS Desktop, donc pas aussi portable que l'approche des comtypes.
user2856

Réponses:

9

Je ne suis pas très fort dans ce domaine non plus, mais j'ai modifié le module Snippets et j'ai créé quelques wrappers pour des tâches très simples. J'ai un exemple d'ajout d'éléments de ligne. L'exemple sous le bloc principal forme un triangle vers la vue de mise en page juste à l'extérieur du document.

J'utilise ce script en conjonction avec un autre et des curseurs de recherche arcpy pour créer des tableaux graphiques dans la mise en page à partir de lignes individuelles et d'éléments de texte, mais cela s'éloigne rapidement de l'exemple "simple". Le code ci-dessous est assez simple et utilise une version modifiée des extraits de code:

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)

entrez la description de l'image ici

Éditer:

@matt wilkie

En ce qui concerne les importations, c'est là que vous devrez parcourir les diagrammes de modèle ArcObjects ou voir à partir de quel espace de noms une classe ou une interface particulière est appelée dans les documents d'aide du kit de développement .NET SDK. Dans certains cas, plusieurs espaces de noms peuvent être utilisés en raison de l'héritage.

Je ne suis pas un expert en ArcObjects, donc cela me prend généralement un certain temps pour savoir quand lancer des choses avec CType (). La plupart de cela, j'ai récupéré des échantillons en ligne. De plus, la syntaxe des exemples VB.NET semble être plus proche de ce que vous faites en Python, mais les exemples C # ont plus de sens pour moi en termes de lisibilité (si cela a un sens). Mais, en règle générale, je suit généralement ces étapes:

  1. Créer une variable pour un nouvel objet COM (généralement une classe) pour instancier un objet
  2. Utilisez CType pour convertir l'objet COM en une ou plusieurs interfaces pour autoriser l'accès aux méthodes et aux propriétés. CType renverra également le pointeur d'interface comtypes via QueryInterface (). Une fois le pointeur renvoyé, vous pouvez alors interagir avec ses propriétés et méthodes.

Je ne sais pas si j'utilise la terminologie appropriée ou non ... Je suis principalement un développeur Python qui "touche" à certains ArcObjects ... Je n'ai cependant touché que la pointe de l'iceberg.

De plus, cette fonction d'assistance chargera toutes les bibliothèques d'objets 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
la source
merci pour l'exemple utile! L'idée maîtresse du Q est (censée être) moins sur des recettes de tâches spécifiques et plus sur la façon d'obtenir et d'écrire les informations pour construire la recette en premier lieu. Par exemple, comment avez-vous su import comtypes.gen.esriArcMapUI as esriArcMapUIet ensuite utilisé dans pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(et découvert la syntaxe dans cette déclaration)?
matt wilkie
J'ai modifié ma réponse d'origine pour essayer de répondre à vos questions. J'ai également quelques autres exemples, mais l'extrait ci-dessus est probablement le plus lisible.
crmackey
J'ai également acheté ce livre l'année dernière: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey
7

Dans un autre article, connexe mais légèrement différent, j'ai fourni une réponse qui peut être intéressante pour les utilisateurs de python essayant de faire le tour des documents d'aide d'Esri ArcObjects.

Je venais de l'autre côté: je connaissais ArcObjects depuis longtemps (très longtemps) avant même d'avoir entendu parler de python et grâce à des articles comme ceux-ci, je suis en mesure d'inclure des ArcObjects critiques dans le scriptage facile de python (voir cet article pour un exemple ). Je me souviens de la frustration d'essayer de comprendre l'héritage, les méthodes et les propriétés; dilemmes comme j'ai X qui est en quelque sorte lié à Y ... alors comment puis-je passer de X à Y.Method ()?

La réponse est de regarder les CoClasses qui implémentent l'interface (voir le texte complet ici ) .. pour un exemple de base, si je veux voir si une couche a une requête de définition, et si oui, qu'est-ce que c'est:

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

Au lieu de ctype(qui est prédominant dans VB), les utilisations de C # ()ou aspour le casting, par exemple, IObject x = (IObject)y;sont (fondamentalement) les mêmes que celles IObject x = y as IObject;qui seraient dim x as IObject = ctype(y,IObject)dans VB.

Je peux dire que j'ai besoin d'un IFeatureLayer pour accéder à IFeatureLayerDefinition parce que: entrez la description de l'image ici

Et lorsque vous lisez le document d'aide pour IFeatureLayer, vous voyez: entrez la description de l'image ici

Ce qui indique qu'il est sûr d'aller ILayer-> IFeatureLayer-> IFeatureLayerDef, à condition que l'ILayer soit du type FeatureLayer (ou l'une des autres CoClasses).

Alors qu'est-ce qui se passe avec les je et non les je? L'interface I signifie, c'est le bit qui fait le travail, sans un I est une CoClass (un type ), donc tout ce que vous voulez réellement utiliser doit commencer par un I et si vous en créez un nouveau ou vérifiez le type puis sautez le I. Une interface peut avoir plusieurs CoClasses et une CoClasse peut prendre en charge de nombreuses interfaces mais c'est l'interface qui fait le travail.

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)

Cet exemple fait un peu plus que le C en ce qu'il trouve son chemin vers l'application actuelle, qui ne serait disponible que dans la fenêtre python ou un complément, si vous avez essayé de l'exécuter à partir de la ligne de commande, l'application est Null et le script serait alors planter avec une exception de référence nulle.

Michael Stimson
la source
Wow, merci beaucoup d'avoir posté ça! J'ai eu quelques difficultés à comprendre les diagrammes ArcObject. C'est agréable d'avoir des commentaires de quelqu'un comme vous qui vient de l'autre côté de la clôture (beaucoup d'expérience .NET ArcObjects). Une chose avec laquelle j'ai eu quelques difficultés est d'accéder à une classe d'entités qui réside dans un jeu de données d'entités via comtypes et python. Je pense que dans le passé, j'ai essayé d'ouvrir d'abord l'ensemble de données d'entités, puis la classe d'entités, mais je n'ai pas eu de chance (obtenir des pointeurs nuls). Avez-vous des exemples de python pour cela?
crmackey
1
Pas tant que ça, je ne commence vraiment qu'avec des comtypes en python, mais en ce qui concerne l'ouverture d'une classe d'entités à partir d'un objet d'espace de travail (IFeatueWorkspace), utilisez simplement le nom, n'incluez pas du tout l'ensemble de données d'entités - peu importe si c'est dans un jeu de données d' entité , tous les noms sont uniques ... voir help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/… Pouvez-vous ouvrir une nouvelle question avec du code et je vais y jeter un œil. Le jeu de données d'entité peut être utilisé avec une itération de jeux de données (IFeatureDataset.Subsets) mais il est plus simple de simplement l'ouvrir avec le nom.
Michael Stimson
1
Merci @Michael Miles-Stimson. Je vais lui donner un autre coup. Si je ne peux pas le comprendre, je posterai une nouvelle question avec mon code actuel.
crmackey
@MichaelStimson Je comprends que je peux utiliser des arcobjects en python en utilisant des comtypes. Je n'ai jamais utilisé d'arcobjects. Étant donné qu'il n'y a aucun exemple pour effectuer des tâches, par exemple créer un ensemble de données réseau à l'aide de comtypes et d'arcpy, dois-je d'abord comprendre les arcobjects avant de pouvoir utiliser des comtypes? Ou puis-je simplement apprendre les comtypes par lui-même afin d'utiliser arcpy et comtypes?
ketar
1
@ketar, c'est une bonne idée d'en savoir un peu plus sur ArcObjects avant d'essayer de les utiliser en python. Bien qu'il n'y ait pas beaucoup d'échantillons d'ArcObjects en python (pour le moment), il existe des exemples dans l'aide d'ArcObjects comme resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/… pour les jeux de données réseau (la construction est le dernier élément à ce sujet). page). Le code ArcObjects est beaucoup plus verbeux que python (arcpy); personnellement, je coderais en VB ou en C #, puis, quand je serais satisfait des résultats, copiez / collez en python.
Michael Stimson