Neu in Python 2,5 ist das ctypes-Modul, eine Fremdfunktions-Bibliothek. Sie stellt C-kompatible Datentypen bereit und ermöglicht das Aufrufen von Funktionen in DLLs oder freigegebenen Bibliotheken. Mit dem ctypes-Modul in Python kann ArcObjects-Code, der in C++ geschrieben wurde, in einem Geoverarbeitungsskriptwerkzeug verwendet werden.
Wenn Sie ctypes in Python verwenden, können Sie die vom Skriptwerkzeug erwarteten Parameter und Typen leicht ändern, ohne den C++-Code ändern zu müssen. Das Modul ctypes unterstützt jede in C aufrufbare Funktion mit grundlegenden Datentypen wie char, int, float, double und structs sowie Zeiger.
Das Aufrufen einer DLL aus einem Python-Skript bietet eine Reihe von Vorteilen. Sie können komplexe ArcObjects-Classes in Ihren Geoverarbeitungstasks nutzen, Ihr geistiges Eigentum ist geschützt und lässt sich viel leichter implementieren als mit den Schnittstellen IGPFunction2 und IGPFunctionFactory. Erstellen Sie das Objekt in Python, und rufen Sie die execute-Methode auf. Übergeben Sie anschließend die erforderlichen Parameter vom Skript, und verwenden Sie dazu die Geoverarbeitungsumgebung.
Funktionsweise
Die Schritte lauten wie folgt:
Erstellen Sie in Visual Studio 2008 ein Win32-Projekt mit C++, das eine einfache Funktion mit dem Prototyp exportiert:
int GpExecutetool(char* parameter1, char* parameter2)
Stellen Sie sicher, dass Sie "ArcGIS\com\directory" in das Projekt einbeziehen und die .olb-Dateien von ArcObjects importieren.
Erstellen Sie ein Skriptwerkzeug in einer benutzerdefinierten Toolbox, mit dem die beiden Parameter überprüft und an das Skript übergeben werden.
Das Python-Skript führt folgende Aktionen aus:
- Importieren Sie arcpy und ctypes.
- Abrufen der Parameter aus dem Skriptwerkzeug
- Importieren der DLL in den Speicher
- Abrufen eines Zeigers auf die Funktion in der DLL
- Angeben der erforderlichen Funktionsargumenttypen, die aus DLLs exportiert wurden, durch Festlegen des argtypes-Attributs und des Rückgabetyps
- Übergeben der Parameter an den C++-Code in der DLL
Details
Das C++-Projekt ("GPToolAsSimpleDLL" in diesem Beispiel) ist ein einfaches Win32-Projekt, in dem der Feature-Class ein AREA-Feld hinzugefügt und der Wert berechnet wird.
Die Header-Datei
#ifdef GPTOOLASSIMPLEDLL_EXPORTS
#define GPTOOLASSIMPLEDLL_API extern "C"__declspec(dllexport)
#else
#define GPTOOLASSIMPLEDLL_API extern "C"__declspec(dllimport)
#endif
GPTOOLASSIMPLEDLL_API int GPexecutetool(char*, char*);
Die Quelldatei "GPToolAsSimpleDLL" öffnet die angegebene Polygon-Feature-Class und legt das angegebene Feld auf die Fläche der einzelnen Polygon-Features fest. Alle Geoverarbeitungsfunktionen, die Sie schreiben, können mit einem einfachen Einstiegspunkt für eine C-Funktion implementiert werden, alle in der gleichen DLL, sowie mit Zusätzen für Skriptwerkzeuge, die die einzelnen Funktionen für ArcToolbox verfügbar machen.
GPTOOLASSIMPLEDLL_API int GPexecutetool(char* featureclassPath, char* fieldname)
{
// Convert char*s to bstring
_bstr_t catalogPath;
catalogPath = featureclasspath;
_bstr_t newfieldname;
newfieldname = fieldname;
// Coinitialize GP utilities class
IGPUtilitiesPtr ipUtil(CLSID_GPUtilities);
// Feature class holder
IFeatureClassPtr ipFeatureclass(0);
HRESULT hr;
// Try to fetch feature class from catalog path
if (FAILED(hr = ipUtil->OpenFeatureClassFromString(catalogPath, &ipFeatureclass)))
{
return -1;
}
// Index position of the specified field
long fieldIndex;
ipFeatureclass->FindField(newfieldname, &fieldIndex);
// Set up query filter and feature cursor
IQueryFilterPtr ipFilter(CLSID_QueryFilter);
IFeatureCursorPtr ipCursor;
IFeaturePtr ipRow;
IGeometryPtr ipShape;
// Open search cursor on feature class
ipFeatureclass->Search(ipFilter, VARIANT_FALSE, &ipCursor);
// Iterate
esriGeometryType gt;
for (ipCursor->NextFeature(&ipRow);
ipRow != NULL;
ipCursor->NextFeature(&ipRow))
{
// Get row's associated geometry
ipRow->get_Shape(&ipShape);
// Ensure we've got a polygon
ipShape->get_GeometryType(>);
if (gt != esriGeometryPolygon)
return -2;
// Get area
IAreaPtr ipArea(ipShape);
double area;
ipArea->get_Area(&area);
// Pop double into a variant
VARIANT value;
value.vt = VT_R8;
value.dblVal = area;
// Set double variant onto target field
ipRow->put_Value(fieldIndex, value);
// Save
ipRow->Store();
}
return S_OK;
}
Das Python-Skript fungiert als Broker, indem es die beiden Parameter aus dem Skriptwerkzeug akzeptiert und als char*, durch Null terminierte Zeichenfolgen, an die DLL-Funktion sendet. Das Skript verwendet außerdem das Geoverarbeitungswerkzeug Feld hinzufügen, bevor die Funktion in der DLL aufgerufen wird. Dies kann den Workflow robuster machen, indem die proprietären Funktionen in C++ und die gängigen Tasks in Python implementiert werden.
import arcpy
import ctypes
# Get the parameters from the script tool dialog
#
shp = arcpy.GetParameterAsText(0)
fieldName = arcpy.GetParameterAsText(1)
# See if the field already exists in the feature class.
# If not, add it.
if len(arcpy.ListFields(shp, fieldName)) == 0:
arcpy.AddField_management(shp, fieldName, "DOUBLE")
# Import DLL into memory and get a pointer to the function
# Note: be sure the DLL is in your Python search path
#
dll = ctypes.cdll.GPToolAsSimpleDLL
perform_function = dll.GPExecutetool
# Tell ctypes the function accepts two char* arguments
#
perform_function.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
# Tell ctypes the function return type
#
perform_function.restype = ctypes.c_int
# Call the function in the DLL
#
retval = perform_function(shp, fieldName)
# Check the return value. If a 0 returned, success!
#
if retval == 0:
arcpy.AddMessage("Success")
elif retval == 1:
arcpy.AddError("Unable to open " + shp)
elif retval == 2:
arcpy.AddError("Not a polygon feature class")