ctypes, une bibliothèque de fonctions étrangères, est une nouveauté de la version 2.5 de Python. Elle fournit des types de données compatibles avec le langage C et permet d'appeler des fonctions qui résident dans des fichiers DLL ou des bibliothèques partagées. Le module ctypes dans Python permet d'utiliser le code ArcObjects écrit en C++ dans un outil de script de géotraitement.
Grâce à ctypes dans Python, vous pouvez facilement modifier les paramètres et types auxquels l'outil de script s'attend sans avoir à recompiler notre code C++. Le module ctypes prend en charge les fonctions appelées en C avec des types de données de base, tels que char, int, float et double, ainsi que structs et les pointeurs.
La possibilité d'appeler une DLL à partir d'un script Python présente de nombreux avantages. Vous pouvez bénéficier des classes ArcObjects détaillées dans vos tâches de géotraitement, votre propriété intellectuelle est protégée et l'implémentation est beaucoup plus simple que si vous deviez faire appel aux interfaces IGPFunction2 et IGPFunctionFactory. Vous créez votre objet dans Python et appelez la méthode Execute, pour transférer les paramètres requis à partir du script dans la structure de géotraitement.
fonctionnement
La procédure est la suivante :
Créez un projet de C++ Win32 dans Visual Studio 2008 chargé d'exporter une fonction simple avec le prototype :
int GpExecutetool(char* parameter1, char* parameter2)
Veillez à inclure le répertoire ArcGIS\com\ dans le projet et à importer les fichiers .olb ArcObjects.
Créez un outil de script dans une boîte à outils personnalisée qui valide les deux paramètres et les transmet au script.
Votre script Python effectuera les opérations suivantes :
- Importez arcpy et ctypes.
- Obtenir les paramètres de l'outil de script.
- Importer la DLL dans la mémoire.
- Orienter un pointeur vers la fonction dans la DLL.
- Spécifier les types d'argument requis de fonctions exportées depuis les DLL en définissant l'attribut argtypes ainsi que le type de retour.
- Transférer les paramètres vers le code C++ de la DLL.
Détails
Le projet C++ (nommé GPToolAsSimpleDLL dans cet exemple) est un projet Win32 simple qui ajoute un champ AREA à la classe d'entités et calcule la valeur.
Le fichier d'en-tête
#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*);
Le fichier source GPToolAsSimpleDLL ouvre la classe d'entités surfaciques spécifiée et définit le champ spécifié sur la surface de chaque entité surfacique. Chaque fonction de géotraitement que vous programmez peut être implémentée avec un point d'entrée de fonction C simple, dans la même DLL, avec des outils de script complémentaires chargés d'exposer chaque fonction auprès d'ArcToolBox.
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;
}
Le script Python joue le rôle de gestionnaire. Il accepte les deux paramètres de l'outil de script en tant que texte et les envoie à la fonction DLL en tant que chaînes se terminant par zéro char*. Le script utilise également l'outil de géotraitement Ajouter un champ avant l'appel de la fonction dans la bibliothèque (DLL). Le fait d'implémenter votre fonctionnalité dans C++ et les tâches courantes dans Python présente l'avantage de pouvoir rendre votre workflow plus robuste.
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")