Wskazówki dotyczące korzystania z ArcObjects z Python

10

Jak dotąd korzystasz z ArcObjects z Pythona? to moje najczęściej czytane i wymieniane pytania i odpowiedzi na temat wymiany stosów GIS. Mimo tego sukcesu jest to prawdopodobnie jeden z moich najsłabszych obszarów, jeśli chodzi o faktyczne wykorzystanie. Duża część tego kiepskiego pokazu wynika z mojej słabej umiejętności czytania i rozumienia dokumentów ArcObjects .

Więc, dla każdego zadania, jakie są wskazówki dotyczące tłumaczenia dokumentów .net / c ++ / java / ... i przykładów na ich odpowiedniki w Pythonie? (który język jest najlepszy do pracy w tej sprawie?) i od jakiego indeksu lub strony docelowej najlepiej zacząć? na czym należy się skupić, a co najmniej tak samo ważne, co można dowolnie ignorować?

Załóżmy, że twoi odbiorcy są co najmniej w pewnym stopniu znający język Python i niepiśmienni w innych językach programowania. Przeprowadź nas przez małe ćwiczenie kodowania, od wstępnego pomysłu i badań po działające wyniki Pythona.

matowe wilkie
źródło
1
To może nic nie dodawać do rozmowy tutaj, ale chciałbym powiedzieć, że naprawdę chciałbym zobaczyć, jak rozwija się ten zestaw instrukcji. Dzięki Matt. Znalazłem jeden artykuł Darrena Wiensa, który od zera tworzy MXD i wypełnia układ przewodnikami. Wydaje się również, że moduł fragmentów Marka Cederholma jest naprawdę pomocny / często wykorzystywany w tych wysiłkach.
Jim
Możliwy przykład użycia: gis.stackexchange.com/questions/86007/… (ujawnienie: to problem, nad którym pracowałem, który skłonił Q. Przejdź do (dobrze spreparowanej) odpowiedzi, zdobądź całą zasługę ! ;-)
matt wilkie
Arcobjects mogą być trudne do zdobycia, dokumenty pomocy są w porządku, ale przykłady są lepsze: Jednym z największych problemów jest wypracowanie dziedziczenia jednego obiektu na drugi, tak jak mam obiekt X, teraz jak uzyskać obiekt Y ? Jeśli możesz dostać Visual Studio 2008 lub 2010 express (pobierz bezpłatnie, jeśli go znajdziesz), zainstaluj SDK, a otrzymasz dokumenty pomocy i kilka przykładów lokalnie.
Michael Stimson
1
@mattwilkie nadzieją to nie błotnistej wody dlugich wiele ... ale za przeniesienie istniejącego kodu .NET do pytona i zastanawianie się składni typ odlewniczą, Python dla .NET wygląda trochę bardziej proste niż zbliżyć się comtypes. To powiedziawszy, właśnie odkryłem Python dla .NET i jeszcze go nie testowałem.
user2856
1
@mattwilkie właśnie odkryło, że Python.Net wymaga zainstalowania ArcGIS SDK (chyba że dlls asemblera są dystrybuowane wraz ze skryptem ...) oprócz ArcGIS Desktop, więc nie tak przenośny jak podejście do typów.
user2856

Odpowiedzi:

9

Nie jestem zbyt silny w tym obszarze, ale zmodyfikowałem moduł Snippets i stworzyłem kilka opakowań dla bardzo prostych zadań. Mam przykład dodawania elementów liniowych. Przykład pod głównym blokiem tworzy trójkąt do widoku układu tuż poza dokumentem.

Używam tego skryptu w połączeniu z innymi kursorami wyszukiwania, aby tworzyć tabele graficzne w układzie z pojedynczych linii i elementów tekstowych, ale to szybko odchodzi od „prostego” przykładu. Poniższy kod jest dość prosty i wykorzystuje zmodyfikowaną wersję fragmentów:

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)

wprowadź opis zdjęcia tutaj

Edytować:

@matt wilkie

Jeśli chodzi o ustalenie importu, w tym miejscu należy przejrzeć diagramy modeli ArcObjects lub sprawdzić, z której przestrzeni nazw wywoływana jest dana klasa lub interfejs w dokumentacji pomocy .NET SDK. W niektórych przypadkach ze względu na dziedziczenie można użyć więcej niż jednej przestrzeni nazw.

Nie jestem ekspertem od ArcObjects, więc zwykle zajmuje mi trochę czasu, aby dowiedzieć się, kiedy rzucić rzeczy za pomocą CType (). Większość z nich wybrałem z próbek online. Ponadto składnia z przykładów VB.NET wydaje się być bliższa temu, co robisz w Pythonie, ale przykłady C # mają dla mnie większy sens pod względem czytelności (jeśli ma to jakiś sens). Z reguły jednak postępuję zgodnie z następującymi krokami:

  1. Utwórz zmienną dla nowego obiektu COM (zazwyczaj klasy), aby utworzyć instancję obiektu
  2. Użyj CType, aby rzutować obiekt COM na interfejs (interfejsy), aby umożliwić dostęp do metod i właściwości. CType zwróci również typ interfejsu Wskaźnik interfejsu za pośrednictwem QueryInterface (). Po zwróceniu wskaźnika możesz następnie wchodzić w interakcje z jego właściwościami i metodami.

Nie jestem pewien, czy używam odpowiedniej terminologii, czy nie ... Jestem przede wszystkim programistą Pythona, który „dabled” w niektórych ArcObjects… Dotknąłem tylko wierzchołka góry lodowej.

Ponadto ta funkcja pomocnicza załaduje wszystkie biblioteki obiektów 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
źródło
dzięki za przydatny przykład! Nacisk Q jest (miał być) mniej na konkretne przepisy na zadania, a więcej na to, jak zdobyć i napisać informacje, aby zbudować przepis w pierwszej kolejności. Na przykład, skąd wiedziałeś, import comtypes.gen.esriArcMapUI as esriArcMapUIa następnie używałeś pMxDoc = CType(pDoc, esriArcMapUI.IMxDocument)(i odkryłeś składnię w tej instrukcji)?
matt wilkie
Zredagowałem oryginalną odpowiedź, aby spróbować odpowiedzieć na twoje pytania. Mam też kilka innych przykładów, ale powyższy fragment jest prawdopodobnie najbardziej czytelny.
crmackey,
Również kupiłem tę książkę w zeszłym roku: amazon.com/Beginning-ArcGIS-Desktop-Development-using/dp/…
crmackey
7

W innym, pokrewnym, ale nieco innym poście podałem odpowiedź, która może być interesująca dla użytkowników Pythona, którzy próbują owinąć głowy wokół Esri ArcObjects pomocy w dokumentacji.

Przyszedłem z drugiej strony: znałem już ArcObjects długo (długo długo), zanim jeszcze usłyszałem o pythonie, a dzięki takim postom jestem w stanie uwzględnić niektóre krytyczne ArcObjects w łatwym skryptowaniu Pythona (zobacz ten post na przykład ). Pamiętam frustrację związaną z próbą zrozumienia dziedzictwa, metod i właściwości; dylematy, takie jak mam X, które są trochę związane z Y ... więc jak mogę przejść z X do Y.Method ()?

Odpowiedź brzmi: spójrz na klasy CoClasses, które implementują interfejs (patrz pełny tekst tutaj ) .. na podstawowy przykład, jeśli chcę zobaczyć, czy warstwa ma zapytanie definicji, a jeśli tak, to co to jest:

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

Zamiast ctype(co jest widoczne w VB) C # zastosowań ()lub asdo odlewu, na przykład IObject x = (IObject)y;jest (zasadniczo) takie same jak IObject x = y as IObject;co byłoby dim x as IObject = ctype(y,IObject)w VB.

Mogę powiedzieć, że potrzebuję IFeatureLayer, aby dostać się do IFeatureLayerDefinition, ponieważ: wprowadź opis zdjęcia tutaj

A kiedy czytasz dokument pomocy dla IFeatureLayer, widzisz: wprowadź opis zdjęcia tutaj

Co oznacza, że ​​bezpiecznie jest przejść ILayer-> IFeatureLayer-> IFeatureLayerDef, pod warunkiem, że ILayer jest typu FeatureLayer (lub dowolnej innej klasy CoClasses).

Więc co jest z ja i nie ja? Interfejs I oznacza, że ​​to bit, który działa, bez I to CoClass ( typ ), więc wszystko, czego naprawdę chcesz użyć, powinno zaczynać się od I, a jeśli tworzysz nowy lub sprawdzasz typ następnie pomiń I. Interfejs może mieć wiele klas CoClass, a CoClass może obsługiwać wiele interfejsów, ale to interfejs faktycznie działa.

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

Ta próbka robi nieco więcej niż C, ponieważ trafia do bieżącej aplikacji, która byłaby dostępna tylko w oknie Pythona lub dodatku, jeśli spróbowałbyś uruchomić ją z wiersza poleceń, aplikacja ma wartość Null, a skrypt wtedy awaria z wyjątkiem wyjątku odniesienia zerowego.

Michael Stimson
źródło
Wow, bardzo dziękuję za opublikowanie tego! Miałem pewne problemy ze zrozumieniem schematów ArcObject. Fajnie jest mieć wkład od kogoś takiego jak ty, który pochodzi z drugiej strony ogrodzenia (dużo doświadczenia .NET ArcObjects). Jedną z rzeczy, z którymi miałem pewne trudności, jest dostęp do klasy funkcji, która znajduje się w zestawie danych funkcji za pomocą typów komend i python. Myślę, że w przeszłości próbowałem najpierw otworzyć zestaw danych funkcji, a potem klasę funkcji, ale nie miałem szczęścia (otrzymałem kilka zerowych wskaźników). Czy masz na to jakieś próbki Pythona?
crmackey
1
Nie tak naprawdę, tak naprawdę zaczynam od typów komend w pythonie, ale jeśli chodzi o otwieranie klasy obiektów z obiektu obszaru roboczego (IFeatueWorkspace), po prostu użyj nazwy, nie dołączaj zestawu danych funkcji w ogóle - nie ma znaczenia, czy jest w zestawie danych funkcji, wszystkie nazwy są unikalne ... zobacz help.arcgis.com/en/sdk/10.0/arcobjects_net/componenthelp/... Czy możesz otworzyć nowe pytanie z kodem, a ja zajrzę . Zestawu danych funkcji można używać z iteracją zestawów danych (IFeatureDataset.Subsets), ale łatwiej jest otworzyć po prostu z nazwą.
Michael Stimson
1
Dzięki @Michael Miles-Stimson. Dam mu kolejny zastrzyk. Jeśli nie potrafię tego rozgryźć, opublikuję nowe pytanie z moim bieżącym kodem.
crmackey,
@MichaelStimson Rozumiem, że mogę używać arcobjectów w pythonie za pomocą comtypów. Nigdy nie korzystałem z Arcobjectów. Biorąc pod uwagę, że nigdzie nie ma próbek do wykonywania zadań, np. Budowania zestawu danych w sieci za pomocą typów komputerów i arkad, czy muszę najpierw zrozumieć obiekty arcob, zanim będę mógł korzystać z typów komputerów? Czy mogę po prostu sam nauczyć się typów, aby używać arkad i rodzajów?
ketar
1
@ ketar, dobrze jest dowiedzieć się trochę o ArcObjects, zanim spróbujesz użyć ich w pythonie. Mimo że w Pythonie nie ma wielu próbek ArcObjects, istnieją próbki w pomocy ArcObjects, takie jak resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/... dla zestawów danych sieciowych (kompilacja jest ostatnim elementem na tym strona). Kod ArcObjects jest znacznie bardziej szczegółowy niż python (arcpy); osobiście napisałbym kod w VB lub C #, a następnie, gdy jestem zadowolony z wyników, skopiuj / wklej do Pythona.
Michael Stimson,