从Python使用ArcObjects的准则


10

到目前为止,从Python访问ArcObjects吗?是我在GIS Stack Exchange上阅读和引用最多的问答。尽管取得了成功,但是在实际使用方面,它可能是我最薄弱的领域之一。表现不佳的很大一部分是由于我阅读和理解ArcObjects文档的能力很差。

那么,对于任何给定的任务,将.net / c ++ / java / ...文档和示例转换为python等价物有哪些准则?(哪种语言最适合该语言?),什么是最好的索引或登录页面?应该重点关注什么东西,并且至少可能同样重要,可以自由忽略的东西是什么?

假设您的读者至少具有python素养,并且不懂其他开发语言。从最初的构想和研究到有效的python结果,带我们进行一次小型编码练习。


1
它可能不会在此处添加任何内容,但我想声明一下,我非常希望看到这组演练的发展。谢谢马特。我发现Darren Wiens撰写了一篇文章,从头开始创建MXD,并使用指南填充布局。似乎Mark Cederholm的 摘要模块确实很有帮助/经常在这些工作中使用。
吉姆(Jim)

可能使用的示例:gis.stackexchange.com/questions/86007/…(公开:这是我一直在研究的问题,这促使问题解答。将我击败(精心设计)的答案,得到所有荣誉!;-)
马特·威尔基

Arcobjects可能很难进入,帮助文档还可以,但是示例更好:最大的问题之一是计算一个对象到另一个对象的继承,就像我拥有对象X一样,现在如何获得对象Y ?如果您可以使用Visual Studio 2008或2010 Express(如果可以找到,则可以免费下载),然后安装SDK,您将在本地获得帮助文档和大量示例。
Michael Stimson

1
@mattwilkie希望这不会给太多的麻烦带来麻烦...但是为了将现有的.NET代码移植到python并弄清楚类型转换语法,.NET的python比comtypes方法更直接。就是说,我只是为.NET发现了python,还没有对其进行测试。
user2856

1
@mattwilkie刚发现python.Net,除了ArcGIS Desktop之外,还需要安装ArcGIS SDK(除非程序集包装器dll与脚本一起分发...),因此不像comtypes方法那样可移植。
user2856

Answers:


9

我在这方面也不是很坚强,但是我修改了Snippets模块,并为非常简单的任务制作了一些包装。我有一个仅添加线元素的示例。位于块下方的示例在文档外部形成了一个布局视图的三角形。

我将此脚本与另一个和arcpy搜索光标一起使用,以通过单独的行和文本元素在布局中创建图形表,但是该表很快脱离了“简单”示例。以下代码非常简单,并使用了代码段的修改版本:

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)

在此处输入图片说明

编辑:

@马特·威尔基

至于确定导入,则必须在其中查看ArcObjects模型图,或者查看.NET SDK帮助文档中正在从哪个名称空间调用特定类或接口。在某些情况下,由于继承,可以使用多个名称空间。

我不是ArcObjects方面的专家,因此通常需要花费我一段时间才能弄清楚何时使用CType()进行转换。其中大部分是我从网上样本中挑选的。另外,VB.NET示例中的语法似乎更接近于Python中的语法,但是C#示例在可读性方面对我来说更有意义(如果有任何意义)。但是,根据经验,我通常遵循以下步骤:

  1. 为新的COM对象(通常是类)创建变量以实例化对象
  2. 使用CType将COM对象转换为接口,以允许访问方法和属性。CType也将通过QueryInterface()返回comtypes接口指针。返回指针后,便可以与其属性和方法进行交互。

不知道我是否使用了正确的术语...我主要是一位Python开发人员,他“涉猎”某些ArcObjects ...不过,我只是触及到了冰山一角。

同样,此帮助器函数将加载所有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]

感谢您的有用示例!Q的主旨(预期是)较少于特定的任务配方,而更多地在于如何获取和编写信息以首先构建配方。例如,您如何知道import comtypes.gen.esriArcMapUI as esriArcMapUI然后再使用pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(并发现该语句中的语法)?
马特·威尔基

我修改了原始答案,以尝试回答您的问题。我还有其他一些示例,但是上面的代码片段可能是最易读的。
crmackey


7

另一篇相关但又略有不同的文章中,我提供了一个答案,这可能是python用户尝试将头缠绕在Esri ArcObjects帮助文档上的兴趣所在。

我来自另一面:在听说python之前,我早就知道ArcObjects(很长很长),并且由于有这样的帖子,我能够在python的简单脚本中包含一些关键的ArcObjects(请参阅示例) )。我确实记得尝试理解继承,方法和属性的挫败感。像我有X之类的难题与X有点相关...所以我如何从X到Y.Method()?

答案是查看实现该接口的CoClass(请参阅此处的全文)..作为一个基本示例,如果我想查看某个图层是否具有定义查询,那么它是什么:

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

代替的ctype(这是在VB突出)C#的用途()as用于铸造,例如IObject x = (IObject)y;是(基本上)相同的IObject x = y as IObject;这将是dim x as IObject = ctype(y,IObject)在VB。

我可以说我需要一个IFeatureLayer才能到达IFeatureLayerDefinition,因为: 在此处输入图片说明

当您阅读IFeatureLayer的帮助文档时,您会看到: 在此处输入图片说明

如果ILayer是FeatureLayer类型(或其他任何CoClasses),则表明ILayer-> IFeatureLayer-> IFeatureLayerDef是安全的。

那么我和不我怎么了?我的意思是接口,是工作的一部分,没有I的是CoClass(一个type),因此您要实际使用的任何内容都应该以I开头,如果您要创建一个新的或检查该类型然后跳过I的接口可以有许多组件类和伴生类可支持多种接口,但它的接口实际上做的工作。

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

该示例比C的功能要多得多,因为它找到了通往当前应用程序的方式,该方法仅在python窗口或外接程序中可用,如果您尝试从命令行运行,则应用程序为Null,然后脚本将使用空引用异常崩溃。


哇,非常感谢您发布此信息!我在理解ArcObject Diagrams方面有些挣扎。很高兴能收到像您这样来自栅栏另一端的人(很多.NET ArcObjects经验)的投入。我遇到一些困难的事情是通过comtypes和python访问驻留在要素数据集中的要素类。我认为过去我曾尝试先打开要素数据集,然后再打开要素类,但没有任何运气(获取一些空指针)。你有任何python示例吗?
crmackey

1
并不是很多,我实际上只是从python中的类型开始,但是从工作区(IFeatueWorkspace)对象中打开要素类只使用名称,根本不包括要素数据集-是否无关紧要它在要素数据集中,所有名称都是唯一的...请参阅help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp / ... 您可以用一些代码打开一个新问题,让我看看。可以将要素数据集与数据集的迭代(IFeatureDataset.Subsets)一起使用,但是使用名称来打开它更干净。
Michael Stimson

1
谢谢@Michael Miles-Stimson。我会再试一试。如果无法弄清楚,我将用当前代码发布一个新问题。
crmackey

@MichaelStimson我知道我可以使用comtypes在python中使用arcobjects。我从未使用过arcobjects。鉴于没有任何地方可以执行任务(例如,使用comtypes和arcpy构建网络数据集)的示例,在使用comtypes之前我是否需要首先了解arcobjects?还是我可以自己学习字型以使用arcpy和字型?
凯塔尔

1
@ketar,最好先了解一下ArcObjects,然后再尝试在python中使用它们。尽管python中的ArcObjects样本还不多(但是),但ArcObjects帮助中的样本却像网络数据集的resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/…(构建是该项目的最后一项页)。ArcObjects代码比python(arcpy)更冗长。我个人使用VB或C#进行编码,然后对结果满意时将其复制/粘贴到python中。
Michael Stimson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.