เข้าถึง ArcObjects จาก Python หรือไม่


132

ฉันต้องการสคริปต์บางสิ่งที่ไม่ได้เปิดเผยผ่านarcgisscriptingหรือ ArcPy

ฉันจะเข้าถึงArcObjectsจาก Python ได้อย่างไร


3
ใครมีความคิดในการใช้วิธีการเหล่านี้ในสภาพแวดล้อมการผลิต ไม่กี่ปีที่ผ่านมาที่ UC ฉันได้พูดคุยกับหนึ่งใน ESRI C ++ devs ที่เขียนโมดูล Python ส่วนใหญ่และเขาต่อต้านการใช้ IronPython ในสภาพแวดล้อมการผลิตอย่างมาก - แต่นั่นก็เป็นเมื่อไม่กี่ปีที่ผ่านมา
Chad Cooper

คำตอบ:


113

ดาวน์โหลดและติดตั้งcomtypes * ใส่Snippetsโมดูลจาก Mark Cederholm ใน PYTHONPATH และคุณพร้อมแล้ว

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

สำหรับพื้นหลังเห็นมาร์ค Cederholm ของการนำเสนอสำหรับ UberPyGeeks ใน"การใช้ ArcObjects ในงูใหญ่" มีมุมมองแยกต่างหากสำหรับมุมมองนักพัฒนา VBA และ C ++ พวกเขาใช้Visual Studio (ใช่Expressคือ ok) และWindows SDKแต่ไม่จำเป็นต้องใช้เพียงแค่ ArcGIS, Python และ comtypes ก็เพียงพอแล้ว

การรับโมดูล Snippets

* หมายเหตุสำหรับ 10.1+คุณต้องทำการเปลี่ยนแปลงเล็กน้อยautomation.pyในโมดูล comtypes ดูArcObjects + comtypes ที่ 10.1


ขั้นตอนถัดไป

... หรือ: สมองหมดสติหรือเปล่า มองไปที่คตัวอย่าง # รหัสทำให้ดวงตาของคุณว่ายน้ำและพยายามที่คุณอาจคุณก็ไม่สามารถคิดเช่นเครน ? ดูนี่:


10
หัวข้อนี้ได้รับความสนใจของฉันและดูเหมือนว่ามีความเข้าใจผิดบางอย่างลอยอยู่รอบ ๆ คุณไม่จำเป็นต้องใช้ Visual Studio หรือคอมไพเลอร์ MIDL เพื่อใช้งาน ArcObjects ใน Python สิ่งที่คุณต้องการคือแพ็คเกจ comtypes งานนำเสนอของฉันรวมถึงแบบฝึกหัดขั้นสูงในการสร้างคอมโพเนนต์ COM โดยใช้ Python แต่นั่นก็มีไว้สำหรับ UberPyGeeks ไฟล์ snippets.py มีตัวอย่างทั้งหมดที่คุณต้องการ

1
@ มาร์คขอบคุณมากสำหรับการแก้ไข เพื่อให้ชัดเจน: ในสูตรด้านบนเราสามารถลบขั้นตอน 1,3,4 ตราบใดที่ติดตั้งctypesแล้ว?
แมตต์วิลคี

2
ใช่. ทดสอบที่ห้องแล็บ คุณเพียงแค่ต้องติดตั้ง comtypes
RK

@RK เยี่ยมมาก! ขอบคุณสำหรับการตรวจสอบและนำคำตอบที่ทันสมัย
matt wilkie

1
โมดูลโค้ดสร้างใหม่เป็นโมดูลao ที่ติดตั้งได้ที่นี่: github.com/maphew/arcplus/tree/master/arcplus/ao (อัปเดตเป็น 10.3 ดูไฟล์เวอร์ชันก่อนหน้าสำหรับ 10.2) จะอัปเดตคำตอบหลักเมื่อฉันได้รับการกำจัดข้อบกพร่องบางอย่าง
matt wilkie

32

ใช่งานนำเสนอของ Mark Cederholm ที่ Matt Wilkie กล่าวไว้ข้างต้นเป็นจุดเริ่มต้นที่ดี สูตร / รหัสที่ Matt นำเสนอนั้นเป็นวิธีที่ลื่นไหลและอาจเป็นวิธีที่ดีที่สุดในการทำสิ่งต่างๆ ฉันต้องการพูดถึง แต่วิธีการบังคับกำลังดุร้ายที่ฉันใช้ใน ArcGIS 10.0 ฉันมีสคริปต์อัตโนมัติหลายตัว (แบบสแตนด์อโลนนอกขอบเขตแอปพลิเคชัน) ที่ฉันใช้วิธีนี้และพวกมันก็ใช้ได้ดี ถ้าความเร็วสูงสุดเป็นสิ่งที่น่ากังวลคุณอาจต้องใช้วิธีแก้ปัญหาของ Matt และทำตามนั้น

ฉันใช้แพ็คเกจ comtypes เพื่อบังคับให้ห่อของ ArcObjects libraries (.olb) ทั้งหมด จากนั้น Python ก็สามารถเข้าถึง ArcObjects ทั้งหมดได้ ผมได้รับรหัสการตัดจากแฟรงก์ Perks ผ่านโพสต์ฟอรั่ม ESRI ฉันมีรหัสของตัวเองที่ทำสิ่งเดียวกันโดยหลักแล้ว แต่มันบวมและใช้งานได้ดีในขณะที่เขาสวยกว่ามาก ดังนั้น:

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)

จากนั้นค่อนข้างตรงไปตรงมาจากการนำเสนอของ 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

แค่นั้นแหละ. คุณควรมีสิทธิ์เข้าถึง ArcObjects อย่างเต็มรูปแบบเริ่มต้นด้วยวัตถุ pApp ที่เป็น IApplication บนวัตถุ AppRef จากประสบการณ์ของผมการห่อของ ArcObjects ไลบรารี่ในการรันครั้งแรกนั้นไม่ช้าอย่างไม่น่าเชื่อ ห้องสมุดได้รับการรวบรวมและเรียบเรียงแล้วดังนั้นสิ่งต่าง ๆ จึงเร็วกว่ามาก

ที่เพิ่มเข้ามา: มีข้อควรระวังขนาดใหญ่ที่มาพร้อมกับสิ่งนี้ ฟังก์ชัน NewObj ที่กำหนดในที่นี้สมมติว่าสคริปต์ Python กำลังถูกเรียกใช้อยู่ระหว่างดำเนินการ ถ้าไม่ฟังก์ชั่นนี้จะสร้างวัตถุในกระบวนการ Python (เช่นออกนอกกระบวนการ) และการอ้างอิงวัตถุจะผิด เพื่อสร้างวัตถุในกระบวนการจากสคริปต์ Python ภายนอกคุณควรใช้ IObjectFactory ดูความคิดเห็นและเคล็ดลับของ Kirk Kuykendall ในโพสต์แลกเปลี่ยนนี้สำหรับข้อมูลเพิ่มเติม


1
สิ่งที่ดีเกี่ยวกับวิธีนี้คือมันไม่จำเป็นต้องติดตั้ง Visual Studio ซึ่งค่อนข้างหนาถ้าสิ่งเดียวที่คุณจะทำคือลงทะเบียนวัตถุ com หากฉันรู้เกี่ยวกับวิธีการของงูหลามบริสุทธิ์ฉันคงไม่เคยลองใช้เส้นทางของ Cederholm ;-)
matt wilkie

1
ฉันขยายแนวคิดนี้เล็กน้อยในคำตอบนี้ทำให้การนำเข้าและการห่อไลบรารีชนิดเป็นกระบวนการขั้นตอนเดียว: gis.stackexchange.com/questions/5017/…
blah238

20

ฉันจะเข้าถึง arcobjects จาก python ได้อย่างไร

หากสิ่งที่คุณกำลังมองหาคือฟังก์ชั่นเฉพาะที่มีอยู่และอยู่ในรหัส C ++ Arcobjects ทางออกที่ดีที่สุดของคุณคือการสร้างวิธีการ C ++ เพื่อโทรหาพวกเขา .... แล้วสร้างไพเพอร์แรปเปอร์

มีหลายวิธีในการเข้าถึงวิธี C ++ จาก python และผู้คนมากมายที่ใช้เครื่องมือเช่นSWIGเพื่อสร้างคลาส python โดยอัตโนมัติจากลายเซ็นเมธอด C ++ เป็นประสบการณ์ของฉันที่ API ที่สร้างอัตโนมัติเหล่านี้ได้รับสิ่งที่น่ารังเกียจเมื่อผ่านประเภท C ++ ที่ไม่ใช่เจ้าของภาษา (int, float) และไม่เคย ' ไพเราะ ' มากนัก

โซลูชันที่แนะนำของฉันคือใช้ ctypes API บทแนะนำที่ยอดเยี่ยมอยู่ที่นี่: http://python.net/crew/theller/ctypes/tutorial.html

ขั้นตอนพื้นฐานคือ:

  1. เขียนตรรกะหลักของคุณลงใน C ++ ซึ่งเป็นสิ่งที่คุณเชื่อว่าประสิทธิภาพของงูหลามอาจเป็นปัญหา
  2. รวบรวมตรรกะหลักนั้น (ในกรณีนี้ใช้การเรียกใช้เมธอด ArcObject C ++ API) จากอ็อบเจ็กต์ไฟล์ไปเป็น shared (.so) หรือ dynamic library (.dll) โดยใช้คอมไพเลอร์ระบบใด ๆ (nmake, make, ฯลฯ )
  3. เขียนการแมป ctypes ระหว่างคลาสไพ ธ อนกับลายเซ็นเมธอด C ++ [SWIG พยายามทำให้สิ่งนี้เป็นแบบอัตโนมัติ แต่เป็นเรื่องง่ายแม้ว่าจะใช้ประเภทวัตถุบ้า]
  4. นำเข้าไลบรารีที่คอมไพล์ไปยังโปรแกรมไพ ธ อนของคุณและใช้การโยงคลาส / เมธอดที่ผูกไว้เช่นคลาสหลามอื่น ๆ !

นี่อาจเป็นวิธีทั่วไปในการอ้างอิงรหัส C / C ++ จากภายในไพ ธ อนมันอาจจะง่ายกว่ามากในระยะยาวหากคุณไม่ต้องจัดการกับวัตถุ COM นอกจากนี้ยังจะช่วยให้การทำงานเฉพาะของระบบทั้งหมดอยู่ในการรวบรวมวัตถุห้องสมุดที่เชื่อมโยง (ดังนั้นงูหลามจะไม่ใช้งานเฉพาะระบบ / งูหลาม)


3
นี่เป็นวิธีที่ดีที่สุดในการโต้ตอบกับ ArcObjects การใช้ comtypes นั้นยอดเยี่ยมสำหรับการสร้างต้นแบบ แต่การเขียนส่วนขยาย C ของคุณเองจะส่งผลให้มีการออกแบบ Pythonic ที่ดี (เพราะคุณต้องคิดเกี่ยวกับมัน) และประสิทธิภาพที่ดีขึ้นเพราะคุณไม่ข้ามกำแพงวัตถุ C ++ / Python มาก แม้ว่าจะใช้งานได้จริง แต่ก็ยากที่จะออกแบบ / พัฒนา / ดีบักดังนั้นสิ่งที่รวดเร็วและสกปรกก็ออกไปนอกหน้าต่าง
Jason Scheirer

18

อีกทางเลือกหนึ่งคือใช้Python สำหรับ. NET - ซึ่งง่ายต่อการติดตั้งและสามารถทำงานกับ. NET DLLs ใด ๆ รวมถึง ArcObjects

ฉันไม่เคยประสบปัญหาเกี่ยวกับการตรวจจับระหว่างดำเนินการและเปิดอินสแตนซ์ของ ArcMap และการเพิ่มและจัดการเลเยอร์ทำงานได้ดีสำหรับฉัน

ข้อกำหนดเพียงอย่างเดียวคือโฟลเดอร์ที่มีไลบรารี Python สำหรับ. NET และติดตั้ง Python มาตรฐาน

รายละเอียดเพิ่มเติมและสคริปต์ตัวอย่างที่นี่ สคริปต์ตัวอย่างสามารถดูได้โดยตรงที่http://gist.github.com/923954

น่าเสียดายที่ในขณะนี้สามารถใช้งานได้โดยไม่มีปัญหากับเครื่องพัฒนาท้องถิ่นการปรับใช้ที่อื่นจำเป็นต้องใช้ ArcObjects SDK และ Visual Studio (รวมถึงรุ่น Express ฟรี) ดูที่การปรับใช้ ArcObject .NET DLLs


สูตรชัดเจนชัดเจนและนำเสนอได้ดี ขอบคุณ Geographika! ฉันขอแนะนำให้คุณใส่ตัวอย่างสคริปต์ขั้นต่ำในคำตอบของคุณเพื่อให้ในกรณีที่ไซต์ของคุณได้รับการปรับปรุงคำตอบจะสามารถเป็นของตัวเองได้
matt wilkie

1
นั่นคือสิ่งที่ฉันทำใน blogpost เก่านี้ ( gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html ) และทำงานได้ตามที่คาดไว้ แต่ให้แน่ใจว่าได้ผลลัพธ์ที่สคริปต์ Python ของคุณเข้าใจ (สตริงตัวเลข หรือเป็นโมฆะ)
ซามูเอล


5

วิธีการหนึ่งที่ฉันไม่เห็นในคำตอบอื่น ๆ คือใช้วิธีเดียวกันกับที่ห้องสมุด arcpy ใช้ เช่นใน C: \ Program Files \ ArcGIS \ Desktop10.0 \ arcpy \ arcpy \ cartography.py, เราเห็น Python เรียกเครื่องมือ ArcObjects โดยใช้ fixargs และฟังก์ชั่นการแปลงวัตถุ

ฉันไม่ได้ตกลงที่จะโพสต์เกี่ยวกับที่นี่เนื่องจากโค้ดระบุว่า "ความลับทางการค้า: ESRI PROPRIETARY และความลับ"; แต่คุณจะพบมันที่อื่นบนเว็บด้วย อย่างไรก็ตามนี่ดูเหมือนจะเป็นวิธีที่ง่ายในการเรียกใช้ฟังก์ชั่นเช่นSimplifyBuilding_cartography()โดยไม่ต้องติดตั้ง comtypes หรือไลบรารีเพิ่มเติมอื่น ๆ

แก้ไข:

ดูความคิดเห็นของ Jason ด้านล่าง เสียงเหมือนทำข้างต้นจะไม่ซื้อคุณมาก


ฉันสังเกตเห็นว่าเช่นกันดูเหมือนว่าวูดูมากเกินไป
blah238

1
มันไม่ได้เรียกชุด arcobjects ที่เปิดเผยอย่างสมบูรณ์
Jason Scheirer

@ JasonScheirer อาจเป็นไปได้ว่ามันจะช่วยให้เข้าถึง ArcObjects (ฉันคิดว่า) ได้ดีกว่า API arcpy แบบตรงและมีข้อดีที่ไม่ต้องติดตั้งไลบรารีอื่น สิ่งหลังมีความสำคัญหากคุณกำลังพัฒนาเครื่องมือเพื่อให้ผู้อื่นใช้ ฉันต้องการทราบว่าคุณสามารถเข้าถึงวิธีนี้ได้เต็มที่เพียงใด - อาจเพียงพอสำหรับวัตถุประสงค์บางประการ (ฉันไม่สามารถตรวจสอบได้ในขณะนี้ - ฉันไม่ได้อยู่ใน บริษัท LAN)
LarsH

1
ฉันรับรองกับคุณได้ว่ามันไม่จริง ฉันพัฒนามันมาก
Jason Scheirer

1
คุณไม่สามารถ. "ArcObject" เป็นบิตของการเรียกชื่อผิดในกรณีนี้ ไม่มีวิธีที่จะเข้าถึงวัตถุต้นแบบและไม่มีการเชื่อม COM ที่เปิดเผย ArcObjects ทั้งหมดที่ใดก็ได้ใน arcgisscripting / arcpy
Jason Scheirer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.