Рекомендации по использованию ArcObjects из Python

10

Безусловно, Доступ к ArcObjects из Python? мои наиболее часто задаваемые вопросы и ответы на GIS Stack Exchange. Несмотря на этот успех, это, вероятно, одна из моих самых слабых областей, когда дело доходит до реального использования. Большая часть этого плохого показа проистекает из моей плохой способности читать и понимать документы ArcObjects .

Итак, для какой-либо конкретной задачи, каковы некоторые рекомендации по переводу .net / c ++ / java / ... документов и примеров в их эквиваленты python? (на каком языке лучше всего работать?) и с какого индекса лучше всего начинать индексную или целевую страницу? на какие вещи следует обратить внимание, и, по крайней мере, так важно, что можно свободно игнорировать?

Предположим, что ваша аудитория, по крайней мере, немного грамотна на Python и неграмотна на других языках разработки. Проведите нас через небольшое упражнение по программированию, от первоначальной идеи и исследований до результатов работы с Python.

Мэтт Уилки
источник
1
Это может ничего не добавить к разговору здесь, но я хочу заявить для записи, что мне было бы действительно интересно увидеть развитие этого набора прохождений. Спасибо Мэтт. Я нашел одну статью Даррена Винса о создании MXD с нуля и заполнении макета направляющими. Кажется также, что модуль фрагментов кода Марка Седерхольма действительно полезен / часто используется в этих усилиях.
Джим
Возможный пример для использования: gis.stackexchange.com/questions/86007/… (раскрытие: проблема, над которой я работал, подтолкнула Q. Побей меня (хорошо продуманным) ответом, возьми весь кредит ! ;-)
Мэтт Уилки
В Arcobjects может быть сложно попасть, справочные документы в порядке, но примеры лучше: одна из самых больших проблем - это наследование одного объекта другому, как у меня есть объект X, теперь как мне получить объект Y ? Если вы можете получить Visual Studio 2008 или 2010 Express (скачать бесплатно, если сможете), установите SDK, вы получите справочные документы и несколько примеров локально.
Майкл Стимсон
1
@mattwilkie, надеюсь, это не слишком мутит воду ... но для переноса существующего кода .NET на python и выяснения синтаксиса приведения типов Python для .NET выглядит немного более простым, чем подход с использованием comtypes. Тем не менее, я только что обнаружил Python для .NET и еще не тестировал его.
user2856
1
@mattwilkie только что обнаружил, что python.Net требует, чтобы ArcGIS SDK был установлен (если только библиотеки обертки сборки не распространяются вместе со сценарием ...) в дополнение к ArcGIS Desktop, поэтому он не такой переносимый, как подход с comtypes.
user2856

Ответы:

9

Я тоже не очень силен в этой области, но я изменил модуль Snippets и сделал пару упаковщиков для очень простых задач. У меня есть пример просто добавления линейных элементов. Пример под основным блоком образует треугольник с видом макета за пределами документа.

Я использую этот скрипт в сочетании с другими и дугообразными поисковыми курсорами, чтобы создавать графические таблицы в макете из отдельных строк и текстовых элементов, но это быстро отходит от «простого» примера. Код ниже довольно прост и использует модифицированную версию фрагментов:

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)

введите описание изображения здесь

Редактировать:

@matt Wilkie

Что касается определения импорта, то здесь вам нужно будет просмотреть диаграммы моделей ArcObjects или увидеть, из какого пространства имен вызывается конкретный класс или интерфейс в справочных документах .NET SDK. В некоторых случаях из-за наследования может использоваться более одного пространства имен.

Я не являюсь экспертом в ArcObjects, поэтому мне обычно требуется время, чтобы выяснить, когда кастовать вещи с помощью CType (). Большую часть этого я взял из образцов онлайн. Кроме того, синтаксис из примеров VB.NET, кажется, ближе к тому, что вы делаете в Python, но примеры C # имеют для меня больше смысла с точки зрения читабельности (если это имеет смысл). Но, как правило, я обычно делаю следующие шаги:

  1. Создайте переменную для нового COM-объекта (обычно класса) для создания экземпляра объекта
  2. Используйте CType для приведения объекта COM в интерфейс (ы), чтобы разрешить доступ к методам и свойствам. CType также будет возвращать указатель интерфейса comtipes через QueryInterface (). Как только указатель возвращается, вы можете взаимодействовать с его свойствами и методами.

Не уверен, что я использую правильную терминологию или нет ... Я в первую очередь разработчик 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]
crmackey
источник
спасибо за полезный пример! Суть Q (предполагается, что) меньше в рецептах конкретных задач и больше в том, как получить и записать информацию для создания рецепта в первую очередь. Например, как вы узнали, import comtypes.gen.esriArcMapUI as esriArcMapUIа затем использовали pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(и раскрыли синтаксис в этом утверждении)?
Мэтт Уилки
Я отредактировал свой оригинальный ответ, чтобы попытаться ответить на ваши вопросы. У меня есть и несколько других примеров, но приведенный выше фрагмент, вероятно, наиболее читаемый.
crmackey
Кроме того, я купил эту книгу в прошлом году: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey
7

В другом связанном, но немного отличном посте я предоставил ответ, который может быть интересен для пользователей Python, пытающихся навести порядок в справочных документах Esri ArcObjects.

Я пришел с другой стороны: я уже знал ArcObjects задолго до того, как услышал о python, и благодаря таким публикациям я могу включить некоторые важные ArcObjects в простой сценарий python (см. Этот пост для примера ). Я помню разочарование от попыток понять наследование, методы и свойства; Дилеммы, как у меня есть X, который в некотором роде связан с Y ... так как мне перейти от X к Y.Method ()?

Ответ заключается в том, чтобы взглянуть на классы CoClasses, которые реализуют интерфейс (см. Полный текст здесь ) .. для базового примера, если я хочу посмотреть, есть ли у слоя запрос определения, и если да, то что это:

В 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-> IFeatureLayer-> IFeatureLayerDef, при условии, что ILayer имеет тип FeatureLayer (или любой из других CoClasses).

Так что случилось с "я" и "нет"? Интерфейс I означает, что это бит, который выполняет работу, без I является CoClass ( тип ), поэтому все, что вы хотите использовать на самом деле, должно начинаться с I, и если вы создаете новый или проверяете тип затем пропустите I. Интерфейс может иметь много CoClass, и CoClass может поддерживать много интерфейсов, но это интерфейс, который на самом деле делает работу.

В питоне:

# 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. Приятно получить информацию от кого-то вроде вас, который приходит с другой стороны забора (опыт работы с .NET ArcObjects). У меня были некоторые трудности с доступом к классу объектов, который находится в наборе классов объектов через comtypes и python. Я думаю, что в прошлом я пытался сначала открыть набор данных объектов, а затем класс объектов, но безуспешно (получить несколько нулевых указателей). У вас есть образцы питона для этого?
crmackey
1
Не так уж и много, я на самом деле только начинаю с comtypes в python, но что касается открытия класса объектов из объекта рабочей области (IFeatueWorkspace), просто используйте имя, вообще не включайте набор классов объектов - не имеет значения, он находится в наборе классов объектов , все имена уникальны ... см. help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/… Можете ли вы открыть новый вопрос с некоторым кодом, и я посмотрю. Набор классов объектов может использоваться с итерацией наборов данных (IFeatureDataset.Subsets), но проще открыть его только с именем.
Майкл Стимсон
1
Спасибо @Майкл Майлз-Стимсон. Я сделаю еще один снимок. Если я не могу понять это, я отправлю новый вопрос с моим текущим кодом.
crmackey
@MichaelStimson Я понимаю, что я могу использовать arcobjects в Python, используя комтипы. Я никогда не использовал arcobjects. Учитывая, что нигде нет примеров для выполнения задач, например, для построения набора сетевых данных с использованием comtypes и arcpy, нужно ли мне сначала понимать arcobjects, прежде чем я смогу использовать comtipes? Или я могу просто изучить комтипы для использования arcpy и comtypes?
Кетар
1
@ketar, неплохо бы немного узнать о ArcObjects, прежде чем пытаться использовать их в python. Хотя в python не так много примеров ArcObjects (пока), есть примеры в справке ArcObjects, например resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/… для наборов сетевых данных (сборка - последний элемент на этом страница). Код ArcObjects значительно более многословен, чем python (arcpy); лично я написал бы код на VB или C #, а затем, когда доволен результатами, скопировал / вставил в python.
Майкл Стимсон