Suite

Le multitraitement avec arcpy peut-il être exécuté dans un outil de script ?

Le multitraitement avec arcpy peut-il être exécuté dans un outil de script ?


J'ai bricolé le module de multitraitement pour résoudre certains traitements de données dans ArcMap 10.3.

J'ai créé un script qui fonctionne bien si je l'exécute en IDLE. Je vois tous mes cœurs au maximum dans le Gestionnaire des tâches et le code se termine sans erreur.

Maintenant, si je câble ce script en tant qu'outil de script dans ArcToolbox, il génère une erreur étrange

Impossible de trouver le fichier : de multiprocessing.forking import main ; main().mxd

En lisant maintenant divers fils de discussion, je constate régulièrement que le script doit être "hors processus". J'ai donc décoché la case "exécuter python en cours" dans la boîte de dialogue des propriétés du script et je m'attendais à ce que le code s'exécute mais ce n'est pas le cas, j'obtiens une erreur 000714 et je n'ai rien fait au code.

Ma question est donc la suivante : peut-on simplement créer un script qui utilise le module de multitraitement et l'exécuter à partir d'ArcToolbox ? D'après mon jeu limité, il semble que je ne puisse pas et c'est une technique qui ne peut être exécutée qu'à partir de IDLE ?


J'ai enfin réussi à le faire fonctionner, en utilisant l'aide de chacun. J'ai décidé de documenter entièrement mon exemple simple et de rassembler tous les problèmes que j'ai dû surmonter dans un document sur le site Web ESRI GeoNet. Comme la réponse de Luke, j'espère que cela fournira un point de départ à quiconque essaie de créer un outil de script python qui utilise le multitraitement. Le document est intitulé Créer un outil de script qui utilise le multitraitement.


Oui, vous pouvez exécuter des processus enfants de multitraitement à partir d'un script de boîte à outils. Vous trouverez ci-dessous du code à démontrer dans une boîte à outils Python (*.pyt).

Il existe un certain nombre de « pièges ». Certains (mais pas tous) seront applicables aux outils de script Python dans une boîte à outils binaire (*.tbx), mais je n'utilise que des boîtes à outils Python ces jours-ci, donc je n'ai pas testé.

Quelques « trucs »/conseils :

  • Assurez-vous que chaque processus enfant a son propre espace de travail (en particulier si vous utilisez Spatial Analyst ou des outils de couverture) pour les fichiers temporaires et utilisez uniquement le processus principal pour allouer le travail, rassembler les résultats et écrire les résultats finaux. C'est ainsi que toute écriture dans un ensemble de données final est effectuée par un processus évitant tout verrouillage ou conflit ;
  • Ne passez que des objets décapables, tels que des chaînes, des listes, des nombres ;
  • Toutes les fonctions que vous souhaitez exécuter en tant que processus enfant DOIVENT être dans un module importable (avec un nom de fichier différent du .pyt), pas le .pyt lui-même. Sinon tu aurasPicklingError : impossible de décaper : la recherche d'attribut __builtin__.function a échoué;
  • Assurez-vous que le code qui exécute le multitraitement est dans un module importable (avec un nom de fichier différent du .pyt), pas le .pyt lui-même. Sinon tu aurasAssertionError : main_name pas dans sys.modules, main_name

S'applique à*.pyscripts dans une boîte à outils binaire (*.tbx):

  • Assurez-vous que le code qui analyse les paramètres du script est protégé par unif __name__ == '__main__' :bloquer.
  • Vous pouvez conserver les fonctions que vous souhaitez appeler dans le script .py, mais vous devez importer le script dans lui-même.

Exemple de code

Boîte à outils Python

# test_multiprocessing.pyt import os, sys import multiprocessing import arcpy # Toutes les fonctions que vous souhaitez exécuter en tant que processus enfant DOIVENT être dans # un module importable. Un *.pyt n'est pas importable par python # Sinon, vous obtiendrez # PicklingError: Can't pickle : la recherche d'attribut __builtin__.function a échoué # Assurez-vous également que le code qui _fait_ le multitraitement est dans un module importable # Sinon, vous obtiendrez # AssertionError: main_name pas dans sys.modules, main_name de test_multiprocessing_functions import execute class Toolbox(object): def __init__(self):"Définir les propriétés de la boîte à outils (le nom de la boîte à outils est le nom de fichier .pyt)."self.label = 'Test Multiprocessing' self.alias = 'multiprocessing' # Liste des classes d'outils associées à cette boîte à outils self.tools = [ TestTool] class TestTool(object): def __init__(self): self.label = 'Test Multiprocessing' self.description = 'Test Multiprocessing Tool' self.canRunInBackground = True self.showCommandWindow = False def isLicensed(self): return True def updateParameters(self, parameters): return def updateMessages(self, parameters): return def getParameterInfo(self):"parameter definitions for GUI"return [arcpy.Parameter(displayName='Input Rasters', name="in_rasters", datatype= "DERasterDataset", paramètreT ype="Required", direction="Input", multiValue=True)] def execute(self, parameters, messages): # Assurez-vous que le code qui _fait_ le multitraitement est dans un module importable, pas un .pyt # Sinon vous' ll get # AssertionError: main_name pas dans sys.modules, main_name rasters = parameters[0].valueAsText.split(';') pour le raster dans les rasters: messages.addMessage(raster) execute(*rasters)

Module Python importable (peut également être utilisé comme outil de script dans une boîte à outils binaire (.tbx)

#test_multiprocessing_functions.py # - Toujours exécuté au premier plan - décoché # - Exécuter le script Python en cours - vérifié import os, sys, tempfile import multiprocessing import arcpy from arcpy.sa import * def execute(*rasters): pour raster dans rasters: arcpy .AddMessage(raster) #Définir l'exe multitraitement au cas où nous fonctionnerions en tant que processus intégré, c'est-à-dire qu'ArcGIS #get_install_path() utilise une requête de registre pour déterminer l'exe python 64 bits si disponible multiprocessing.set_executable(os.path.join(get_install_path( ), 'pythonw.exe')) #Créez un pool de travailleurs, gardez un processeur libre pour surfer sur le net. #Laissez chaque processus de travail ne gérer que 10 tâches avant d'être redémarré (en cas de fuites de mémoire désagréables) pool = multiprocessing.Pool(processes=multiprocessing.cpu_count() - 1, maxtasksperchild=10) # Le multitraitement le plus simple consiste à mapper un itérable (c'est-à-dire une liste de choses à traiter) à une fonction # Mais cela ne vous permet pas de gérer les exceptions en un seul processus ##output_rasters = pool.map(worker_function, rasters) # Utilisez plutôt apply_async pour que nous puissions gérer les exceptions avec élégance jobs={ } pour le raster dans les rasters : jobs[raster]=pool.apply_async(worker_function, [raster]) # les arguments sont passés sous forme de liste pour le raster,result in jobs.iteritems() : try: result = result.get() arcpy. AddMessage(result) sauf exception comme e : arcpy.AddWarning('{}
{}'.format(raster, repr(e))) pool.close() pool.join() def get_install_path() :"Return 64bit chemin d'installation python à partir du registre (s'il est installé et enregistré), sinon revenez au chemin d'installation du processus 32 bits actuel." si sys.maxsize > 2**32 : retournez sys.exec_prefix #Nous sommes s'exécutant dans un processus 64 bits #Nous sommes en 32 bits, alors voyez s'il y a un chemin d'installation 64 bits = r'SOFTWAREPythonPythonCore2.7' de _winreg import OpenKey, QueryValue de _winreg import HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_64KEY essayez : avec OpenKey_MACHINE , chemin, 0, KEY_READ | KEY_WOW64_64KEY) en tant que clé : return QueryValue(key, "InstallPath").strip(os.sep) #Nous avons une installation 64 bits, alors renvoyez-la. sauf : return sys.exec_prefix #No 64bit, donc return 32bit path def worker_function(in_raster):"Assurez-vous de transmettre un chemin de fichier à raster, PAS un objet arcpy.sa.Raster"## Exemple de "vrai" travail" (non testé) ## Créer un espace de travail temporaire unique #scratch = tempfile.mkdtemp() #out_raster = os.path.join(scratch, os.path.basename(in_raster)) #arcpy.env.workspace = scratch #arcpy.env.scratchWorkspace= scratch #ras = Raster(in_raster) #result = Con(IsNull(ras), FocalStatistics(ras), ras) #result.save(out_raster) #del ras, result #return out_raster # quitter le script appelant pour nettoyer tempdir. # pourrait également passer out_raster en tant qu'argument, # mais vous devrez vous assurer qu'aucun autre processus enfant # n'écrit ce répertoire lorsque le # processus enfant actuel est… # Faites un "faux" temps d'importation de travail, temps aléatoire.sleep( random.randint(0,20)/10.0) #dormir un peu pour simuler le travail return in_raster[::-1] #Renvoyer une version inversée de ce qui a été transmis if __name__=='__main__': # importer le script actuel vers éviter : # PicklingError : ne peut pas mariner : la recherche d'attribut __builtin__.function a échoué à importer test_multiprocessing_functions rasters = arcpy.GetParameterAsText(0).split(';') pour le raster dans les rasters : arcpy.AddMessage(raster) test_multiprocessing_functions.execute(*rasters)

Pool de problèmes de multitraitement

J'essaie d'utiliser le pool du multitraitement pour accélérer certaines opérations def worker (d) qui se produisent une fois pour chaque leayer dans le mxd. celui-ci est codé en dur sur D:TEMPUntitled4.mxd. il fonctionne mais un seul à la fois. Je peux le voir démarrer la piscine, mais seulement sur est utilisé. Toute aide est la bienvenue. Je l'exécute dans la boîte arctool dans ArcMap et je n'ai pas coché l'exécution en tant que processus. comme je l'ai dit, il fonctionne, mais un seul à la fois.

importer arcpy
importer le système d'exploitation
importer le multitraitement

def travailleur(d):
# couche tampon par les valeurs ci-dessous
bfs = [101, 200, 201, 400, 401, 750, 751, 1001,
1500, 1501, 2000, 2001, 2500]
pour bf dans bfs :
Sortie = os.path.basename(d)[:-4] + "_Buffer" + str(bf) + ".shp"
print "Buffering " + d + " at " + str(bf) + " Pieds"
if arcpy.Exists(d):
arcpy.Buffer_analysis(d, "D:Temp" + Sortie, str(bf) + " Pieds")
arcpy.Project_management("D:Temp" + Sortie, "D:TempTest" + Sortie, "C:Program Files (x86)ArcGISDesktop10.0Coordinate Systems Systèmes de coordonnées géographiquesAmérique du NordNAD 1983.prj")
arcpy.Delete_management("D:Temp" + sortie)
autre:
imprimer "Pas de données"


if __name__ == '__main__' :

#Ensembles MXD
mxd = arcpy.mapping.MapDocument("D:TEMPUntitled4.mxd")
#mxd = arcpy.mapping.MapDocument("CURRENT")

#définir certains environnements nécessaires pour obtenir les sorties correctes
arcpy.env.overwriteOutput = True
arcpy.env.workspace = "D:TEMPTest"
arcpy.env.outputCoordinateSystem = "C:Program Files (x86)ArcGISDesktop10.0Coordinate SystemsProjected Coordinate SystemsUTMNAD 1983NAD 1983 UTM Zone 16N.prj"

Nombre de processeurs à utiliser défini pour max moins 1
prc = int(os.environ["NUMBER_OF_PROCESSORS"]) - 1

# Créer et démarrer un pool de processus de travail
pool = multiprocessing.Pool(prc)

# Obtient toutes les couches dans le MXD actuel
lyrs = arcpy.mapping.ListLayers(mxd)

# Parcourt chaque couche et obtient le nom et le chemin des données sources
pour lyr en lyrs :
d = lyr.dataSource
print "Passer " + d + " au pool de traitement"
arcpy.AddMessage("Passer " + d + " au pool de traitement")
pool.apply_async(travailleur(d))


  • Pour rendre votre code plus facile à visualiser, lorsque vous le collez, sélectionnez tout votre code et cliquez sur le bouton # en haut - cela le présentera sous forme de code, conservant ainsi votre indentation.

  • Essayez d'imprimer prc et lyrs avant la boucle de multitraitement, avec arcpy.AddMessage(str(prc)+' - '+str(len(lyrs))' - '+str(lyrs)), afin de vérifier qu'ils vous ressemblent attendrait.

  • Je ne sais pas vraiment comment fonctionne exactement le multitraitement, mais je l'utilise un peu. D'après mon expérience, je pense que vous devez ajouter les travaux à un serveur de travaux (juste une liste Python) et obtenir un résultat de chaque travailleur. J'ai modifié votre code pour que chaque travailleur renvoie une chaîne, puis imprime un message vous indiquant quand chaque travailleur a terminé.

Faites-moi savoir comment vous vous en sortez.

Merci pour l'aide, j'ai travaillé dessus pendant 2 jours par intermittence, car je peux bénéficier de plusieurs choses en ayant un processus ou chaque couche dans un mxd. J'ai abandonné un député et utilisé le python pp. J'utilisais votre projet comme exemple http://pythongisandstuff.wordpress.com/author/stacyrendall/ . J'ai également eu un problème d'installation avec ArcView, ce qui a aggravé mes problèmes. Le code ci-dessous, que je viens de terminer, met en mémoire tampon les couches dans un mxd enregistré. Je l'ai exécuté comme un seul thread et cela a pris 420 secondes. J'ai couru en utilisant la bibliothèque pp et cela a pris 151 secondes à partir de python et 154 comme outil, donc une amélioration significative. Je n'ai jamais pu obtenir les emplois pour commencer à utiliser mp, maintenant que j'ai une certaine compréhension du pp, je pense que je vais m'y tenir.

J'ai testé ta modification. I il fonctionne toujours comme un seul processus. Merci d'avoir aidé votre page Web a été très utile

Je suppose qu'il n'y a qu'un seul moyen sûr de le savoir, mais ? Avez-vous des idées sur la façon dont l'utilisation de la mémoire est distribuée et/ou recyclée pour le python multiprocessus ou parallèle ? Autrefois, beaucoup d'entre nous étaient obligés de créer des processus distincts (un python .exe distinct) à l'aide de spawnv ou d'un sous-processus. Principalement, c'était simplement parce que l'objet gp (ou arcpy) avait de très gros problèmes de fuite de mémoire, et toute application compatible gp (python.exe) manquait progressivement de mémoire lors de l'exécution d'outils en boucle. En prime, vous pouvez écrire du code supplémentaire pour « ? gérer ? » les processus séparés en tant que processus pseudo-parallèle. Ces problèmes de fuite de mémoire gp/arcpy ont été considérablement améliorés, mais restent un problème important pour « ? ? ? ? ? ?

Quoi qu'il en soit, je suis curieux de savoir si pp ou multiprocess peut en quelque sorte éviter ces problèmes de fuite de mémoire. Dans l'exemple pp, je vois que vous transmettez au processus parallèle le même objet arcpy qui est instancié dans main. Est-ce que tous les processus enfants utilisent (et ajoutent en supposant à la consommation de mémoire) la même instance arcpy? Dans l'exemple mutiprocess I, puisque je ne vois pas de code aussi explicite que dans l'exemple pp, je suppose que les processus enfants par défaut ont simplement accès à toutes les variables globales dans main?

Je pense que pp et/ou le multitraitement souffriront toujours d'une consommation de mémoire gp/arcpy progressive et catastrophique, mais je ne peux pas attendre pour essayer quelques trucs par moi-même. Merci mille fois d'avoir mis ces exemples.

Chris dans mon exemple PP, j'utilise "In_memory", qui utilise une partie de votre RAM pour stocker des données, pour accélérer les fonctions de travail qui s'exécutent en parallèle. Chaque "travail" crée un objet "In_memory" unique, alors assurez-vous de le supprimer avant la fin de la fonction wrker. En ce qui concerne les fuites de mémoire du programme, je suppose qu'elles sont toutes toujours là et que ce serait un bien meilleur problème si le code était exécuté en tant que processus unique, pourrait peut-être effectuer un processus en deux parties pour nettoyer la mémoire à mi-chemin si vous rencontrer des problèmes de mémoire, mais je ne me suis même pas rapproché de mes affaires.

Le code pp (le seul que j'ai pu travailler) doit être épuisé, donc "prc" dans mon exemple définit le nombre de processus à démarrer (5 pour mon intel 980X). Chaque processus est un processus python (32 bits) avec sa propre allocation de mémoire, tout comme l'utilisation de spawn ou d'un sous-processus. Ensuite, tous les travaux sont alimentés au pool des 5 processus et ces mêmes 5 processus parcourent les travaux jusqu'à ce que tous les travaux soient terminés.

Lors de mes tests, je n'ai eu aucun problème de mémoire. Si vous utilisez "In_memory", assurez-vous de ne pas charger de très gros objets (comme une classe de fonctionnalités de 1 Go), il existe également plusieurs limitations quant à ce qui peut et ne peut pas utiliser "In_memory", mais je les utilise tout le temps et ils peut vraiment accélérer le traitement, parfois par un facteur de 4 fois plus rapide.

Le mp.pool "engendre" également de nouveaux processus, mais je n'ai jamais pu le faire fonctionner correctement, j'ai toujours voulu tout exécuter en un seul processus. Cela démarrerait le pool, mais n'alimenterait qu'un seul processus. Le PP est gratuit et semble bien fonctionner, c'est donc ce que je vais utiliser.

Veuillez consulter http://pythongisandstuff.wordpress.c. /stacyrendall/ pour certains problèmes connus avec mp et PP, c'est vers le bas de la page.

Il est intriguant que le multitraitement ne fonctionne pas pour vous, pouvez-vous confirmer qu'il n'y a pas d'erreur, mais qu'il n'exécute qu'un processus à la fois ? J'utilise exclusivement la bibliothèque Multiprocessing maintenant, car PP ne semble pas pouvoir gérer les extensions, mais je n'ai jamais eu de problèmes avec.

Je ne connais pas grand-chose aux spécificités de l'utilisation de la mémoire, mais j'ai appris quelques choses (toutes concernant l'utilisation de la bibliothèque Multiprocessing, mais elles peuvent également éclairer PP). Les processus enfants prennent un travail dans le pool de travaux, le terminent, prennent le prochain travail disponible et ainsi de suite. Cependant, ils semblent conserver une partie (pas toute) de la mémoire qu'ils utilisaient si vous observez les processus au fil du temps, l'empreinte mémoire de chacun augmentera. En exécutant certains modèles impliquant Network Analyst avec 4 processus enfants et un pool de travail de 56 éléments initialement, j'ai reçu une erreur de mémoire après que l'empreinte totale des quatre processus ait atteint environ 4 Go, environ à mi-chemin du pool. C'était loin des limites de l'ordinateur que j'utilisais. Ma meilleure supposition était que toute la mémoire appartenait toujours au processus principal et atteignait donc sa limite de 32 bits, mais les chiffres ne s'additionnent pas vraiment pour soutenir cette théorie.

La solution de facilité n'est pas corrigée avant Python 2.7, où vous pouvez définir une valeur maxtasksperchild bien que cela ne semble pas très bien fonctionner - nécessitant un code plus compliqué, qui se bloque ou s'exécute assez lentement.

La solution difficile consiste à recréer le pool toutes les n fois pour chaque enfant, en fonction de l'ampleur des opérations effectuées par les travailleurs. Évidemment, ce n'est pas joli, mais ça marche très bien. J'ai utilisé un certain nombre d'ordinateurs et un mélange de Windows 7 et XP pour tester tout cela, et il semble que parfois sur certains ordinateurs, les premiers processus parallèles via le pool soient beaucoup plus lents que les derniers, d'au moins 4 à 7x. Si cela se produit sur un ordinateur, vous voulez que n soit le plus élevé possible, pour minimiser le nombre de vos tâches qui passent en premier dans le pool. Sinon, vous pouvez tout aussi bien recréer le pool après que chaque processus parallèle s'est exécuté une fois.

Faites-moi savoir si vous voulez plus d'informations, et j'essaierai de mettre un exemple sur mon blog le plus tôt possible !


Traitement d'un jeu de données raster volumineux

L'augmentation de la résolution des jeux de données raster a conduit à des tailles de données de plus en plus grandes. Actuellement, les ensembles de données sont de l'ordre du gigaoctet et augmentent, avec des milliards de cellules raster. Alors que la puissance de calcul des processeurs et la taille de la mémoire des ordinateurs ont considérablement augmenté, les équipements et algorithmes existants adaptés à la manipulation de petites trames avec une résolution plus grossière rendent le traitement de ces sources de données améliorées coûteux. [1]

La décomposition des données, également connue sous le nom de diviser pour régner, est une stratégie populaire utilisée dans le calcul parallèle dont nous profiterons pour traiter en parallèle un grand jeu de données raster. Les algorithmes utilisés dans les outils d'analyse raster peuvent être classés en quatre catégories : opérations locales, focales, zonales et globales. Pour une plongée plus approfondie dans les types d'opérations raster basées sur des cellules, lisez cet article. Les opérations raster locales, focales et zonales sont simples à programmer en ce qui concerne la décomposition des données. Une fois les données décomposées de manière appropriée, chaque « bloc » de données peut être exploité indépendamment par un processus sans avoir besoin de communiquer avec d'autres processus. Cependant, les opérations raster globales sont plus difficiles à intégrer avec la décomposition des données et nécessitent une communication entre les processus. Voyons un exemple de traitement d'un jeu de données raster volumineux à l'aide d'une opération raster mathématique locale, Racine carrée. Les opérations locales, ou par cellule, sont les plus simples à paralléliser à l'aide d'une stratégie « diviser pour régner », car la valeur résultante à chaque cellule ne dépend que de la valeur d'entrée à cet emplacement de cellule. Pour chaque cellule, l'outil Racine carrée calcule la racine carrée de la valeur de cette cellule. L'utilisation de l'outil en série signifierait simplement exécuter l'outil Racine carrée sur l'ensemble de l'ensemble de données volumineux, mais ce processus peut prendre du temps.

Approche série par défaut et non optimisée pour le traitement d'un grand raster

Au lieu de cela, grâce à la décomposition des données, nous pouvons reconcevoir la tâche d'analyse pour utiliser plusieurs processus de travail simultanément, améliorant ainsi les performances de l'analyse globale. Le graphique ci-dessous illustre la division du domaine d'un grand raster en plusieurs morceaux plus petits et l'utilisation de plusieurs processus de travail pour effectuer simultanément une analyse sur chacune des sous-sections. Les résultats sont ensuite cousus ensemble pour la sortie finale.

Multitraitement de grands rasters en parallèle

Un exemple de script est partagé sur GitHub ici qui explique en détail comment ce problème peut être résolu par programmation à l'aide d'ArcGIS Desktop et de Python multitraitement module.


Multitraitement avec Arcpy

Je travaille sur l'itération d'un grand nombre d'ensembles de données (900+) et exécute de nombreux processus gourmands en mémoire sur chacun. Ce qui avait été configuré précédemment pour parcourir les classes d'entités gdb est trop lent et je cherche à passer au multitraitement (à l'aide de shp) pour tenter de résoudre ce problème.

Ma question est la suivante : lorsque j'exécute un script autonome qui inclut les méthodes de multitraitement et de mise en commun, le script s'exécute très bien et je mets en mémoire tampon 900+ classes de fonctionnalités en 5 minutes. Lorsque j'intègre cette partie dans mon script principal, le script expire, il semble même s'il ne devrait pas y avoir de problèmes de mémoire en suspens (pas de couches, rien d'écrit en mémoire).

Serais-je mieux servi en écrivant chaque étape du processus dans un fichier .py séparé et en important chaque étape ?

Je ne sais pas où cela tourne mal, la syntaxe est identique et aucune erreur n'est générée (le processus ne produit même rien lorsque je l'intègre dans le script principal).

edit : Pourquoi les downvotes ? Aurais-je dû préciser quelque chose ?


Temps de traitement pour le script Python à l'aide de pythonwin vs. Script Tool

Je me demande si quelqu'un a connu une différence dramatique dans le temps qu'il faut pour exécuter un script Python en tant qu'outil de script par rapport à directement dans Python ou Pythonwin IDE ? J'exécute mon script dans Pythonwin (Python 2.7.8, 32 bits) et cela prend environ 17 minutes mais lorsque j'exécute le même script qu'un outil de script dans ArcCatalog (version 10.3.1) Temps écoulé : 7 heures 39 minutes 44 secondes . C'est une différence incroyable et je me demande quel est le problème. C'est dans notre environnement de Citrix ainsi les programmes ne fonctionnent pas sur mon PC de bureau mais sur une lame de Citrix. Ce que je pensais être un script qui fournit une solution viable ne l'est pas si, lorsque je le distribue dans une boîte à outils, son exécution prend beaucoup plus de temps (comme toute la journée).

J'ai un script qui fait ce qui suit (je peux poster le code complet si cela peut être utile, bien qu'il soit assez long et qu'il s'agisse d'une question générale):

Résumé : crée des répliques unidirectionnelles vers un fichier gdb de notre instance SDE Enterprise (Oracle) d'entreprise vers notre système de classement de centre de données, puis les transfère vers notre périphérique de stockage en réseau à l'aide de Robocopy, puis envoie un e-mail du fichier journal.

Initie le module de journalisation au fichier journal

Vérifie les entrées SDE pour les exigences de réplication (les jeux de données sont versionnés et les classes d'entités/tables contiennent des GlobalID)

if schema == "S_R10_CNF" et str(child.isVersioned) == 'False' :

logging.info("L'enfant <0> n'est pas enregistré comme versionné".format(enfant.Nom))

logging.info("Tentative d'inscription. ")

logging.info("Enregistré <0>comme versionné avec succès".format(child.Name))

logging.warning("Impossible de s'inscrire comme versionné en raison d'une exception : <0>".format(e.message))

logging.warning(" <0> n'est PAS enregistré comme versionné et ne sera PAS répliqué".format(child.Name))

à partir des collections importer defaultdict

pour fc dans arcpy.ListFeatureClasses("","",ds):

pour tb dans arcpy.ListTables('S_R10_CNF.*'):

print('<0>est manquant GLOBALID dans le tableau <1>'.format(tb))

logging.info(" <0>il manque des GlobalID ! Tentative de les ajouter. ".format(i))

logging.info("Ajout des GlobalID à l'ensemble de données : <0>".format(i))

logging.warn("Impossible d'ajouter des GlobalID à l'ensemble de données <0> en raison d'une exception : <1>".format(i, e.message))

logging.warn("Les éléments suivants n'ont pas de GlobalID et ne seront pas répliqués : <0>".format(i))

Crée le fichier de sortie GDB s'il n'existe pas déjà

sinon arcpy.Exists(gdbName):

logging.info("GDB enfant cible créé " + nomgdb)

Génère un nom pour la réplique en fonction du nom de l'ensemble de données

ReplicName = ReplicBase + "_NAS1Way"

truncLen = 32 - len("_NAS1Way")

if len(replicaName) > 32 : le nom de la réplique ne peut pas contenir plus de 32 caractères ou la réplique échouera avec un vague message d'erreur

Vérifie si le réplica existe déjà, si c'est le cas, il synchronise et copie les métadonnées, sinon, il crée le réplica

# Vérifiez les noms de réplique dans le parent et l'enfant

replicaList = [x.name.partition(".")[2] pour x dans arcpy.da.ListReplicas()]

replicaListGDB = [x.name pour x dans arcpy.da.ListReplicas(outGDB)]

si le nom de réplique dans la liste de répliques :

si replicaName dans replicaListGDB :

arcpy.SynchronizeChanges_management(outGDB, "DBO."+Nom du réplica, arcpy.env.workspace, "FROM_GEODATABASE2_TO_1")

logging.info("Réplique unidirectionnelle synchronisée pour : <1>".format(Nom de la réplique, DS))

logging.warn("Nom du réplica <0>trouvé dans le SDE parent mais pas dans le GDB enfant".format(Nom du réplica))

logging.warn(" Annuler l'enregistrement du réplica <0>, supprimer les données enfants et réexécuter !".format(Nom du réplica))

logging.error("Impossible de synchroniser la réplique <0> en raison d'une exception : <1>".format(replicaName,e.message))

status='Synchronisation des métadonnées pour <0>'.format(DS)

logging.error("Impossible de synchroniser les métadonnées <0> en raison d'une exception : <1>".format(replicaName,e.message))

status='Création d'une réplique unidirectionnelle pour <0>'.format(DS)

arcpy.CreateReplica_management(featureList, "ONE_WAY_REPLICA", outGDB, replicaName, "FULL", "", "ALL_ROWS", "", "GET_RELATED")

logging.info("Réplique unidirectionnelle créée pour : <0>".format(DS))

logging.info(" La réplique est nommée : <0>".format(Nom de la réplique))

logging.error("Impossible de créer une réplique pour l'ensemble de données <0> en raison d'une exception : <1>".format(DS,e.message))

Appelle Robocopy via le module de sous-processus pour copier le fichier GDB sur le lecteur NAS

subprocess.call(r'NET USE Z: /del')

subprocess.call(r'NET USE Z: <0>/user:<username> pwd'.format(destPath))

status='Pushing réplica GDB change pour NAS Drive '.format(destPath)

subprocess.call(["robocopy", gdbPath, "Z:", "*.*", "/e", "/z", "/dcopy:T", "/purge", "/xo", " /xf", "*.lock", "<0>".format(os.path.join(gdbPath, "robocopy_log.txt")), "/fft", "/log:<0>".format( os.path.join(gdbPath, "robocopy_log.txt")), "/tee"])

Envoie un e-mail du fichier journal

mailserver.sendmail(fromEmail, toEmailList, msg)

Les parties les plus lentes du script semblent exécuter arcpy.SynchronizeChanges_management lorsqu'il se trouve dans un outil de script de boîte à outils, même lorsqu'il n'y a AUCUNE modification à synchroniser ! La case est cochée pour exécuter le script Python dans le processus.

Je cherche à utiliser le multitraitement car il y a 16 cœurs sur le serveur Citrix afin d'exécuter certaines des vérifications et de créer des répliques pour accélérer les choses. Quelqu'un sait-il pourquoi le même script prend ridiculement plus de temps en tant qu'outil de script que directement en Python sans la surcharge ArcGIS ?


1.5.1.1 Conversion du script en outil

Maintenant, convertissons ce script en outil de script dans ArcGIS Pro pour nous familiariser avec le processus et nous examinerons les différences entre ArcGIS Desktop et ArcGIS Pro lorsqu'il s'agit de travailler avec des outils de script (indice : il n'y en a pas d'autre que l'interface est légèrement différente).

Nous allons commencer par ouvrir ArcGIS Pro. Vous serez invité à vous connecter (utilisez votre compte Penn State ArcGIS Online que vous devriez déjà avoir) et à créer un projet au démarrage de Pro.

La connexion à ArcGIS Pro est un nouveau développement important pour l'exécution de code dans Pro par rapport à Desktop. Comme vous le savez peut-être, Pro fonctionne avec une structure de licence différente, de sorte qu'il téléphonera régulièrement aux serveurs de licences d'Esri pour vérifier que vous disposez d'une licence valide. Avec Desktop, une fois que vous l'avez installé et configuré votre licence, vous pouvez l'exécuter pendant les 12 mois de validité de la licence, en ligne ou hors ligne, sans aucun problème. Comme Pro se connectera régulièrement à Esri, nous devons garder à l'esprit que si notre code cesse de fonctionner en raison d'une erreur de non-licence d'une extension ou en raison d'un problème de licence plus générique, nous devons vérifier que Pro est toujours connecté. tout le monde, ce ne sera pas un problème car vous utiliserez généralement Pro sur un ordinateur connecté à Internet et vous ne remarquerez pas les vérifications de licence. Si vous mettez votre ordinateur hors ligne pendant une période prolongée, vous devrez étudier les options de licence hors ligne d'Esri.

Les projets sont le moyen pour Pro de garder toutes vos cartes, mises en page, tâches, données, boîtes à outils, etc. organisées. Si vous venez de Desktop, considérez-le comme un MXD avec quelques fonctionnalités supplémentaires (comme permettre plusieurs mises en page pour vos cartes).

Choisissez de créer un nouveau projet à l'aide du modèle vierge, donnez-lui un nom significatif et placez-le dans un dossier approprié pour votre ordinateur local.

Vous aurez alors Pro en cours d'exécution avec votre propre boîte à outils déjà créée. Dans la figure ci-dessous, j'ai cliqué sur les boîtes à outils pour la développer afin d'afficher la boîte à outils qui porte le même nom que mon projet.

Si nous faisons un clic droit sur notre boîte à outils, nous pouvons choisir de créer un nouveau script >.

Une fenêtre s'ouvrira nous permettant d'entrer un nom pour notre script ("Leçon1A") et une étiquette pour notre script ("Geog 489 Leçon 1A"), puis nous utiliserons l'icône de navigation de fichiers pour localiser le fichier de script que nous enregistré plus tôt. Si votre script n'apparaît pas dans ce dossier ou si vous recevez un message indiquant « Le conteneur est vide », appuyez sur F5 sur votre clavier pour actualiser la vue.

Nous ne choisirons pas « Importer le script » ou de définir des paramètres (encore) ou d'enquêter sur la validation (encore). Lorsque nous cliquons sur OK, notre outil de script sera créé dans Pro. Nous n'allons pas exécuter notre outil de script (encore) car il s'attend actuellement à trouver les données Foxlake DEM dans C:dataelevation et à écrire les résultats dans ce dossier, ce qui n'est pas très pratique. Il a également la coupure codée en dur de 3500 intégrée dans le code. Vous pouvez télécharger le FoxLake DEM ici.

Pour rendre le script plus convivial, nous allons apporter quelques modifications pour nous permettre de choisir l'emplacement des fichiers d'entrée et de sortie ainsi que pour permettre à l'utilisateur de saisir la valeur de coupure. Plus tard, nous utiliserons également la validation pour vérifier si cette valeur de coupure se situe dans la plage de valeurs présentes dans le raster et, si ce n'est pas le cas, nous la modifierons.

Nous pouvons modifier notre script à partir de Pro, mais si nous le faisons, il s'ouvre dans le Bloc-notes, ce qui n'est pas le meilleur environnement pour le codage. Vous pouvez utiliser le Bloc-notes si vous le souhaitez, mais je vous suggère d'ouvrir à nouveau le script dans votre éditeur de texte préféré (j'aime le Bloc-notes ++) ou simplement d'utiliser Spyder.

Si vous le souhaitez, vous pouvez changer cet éditeur préféré en modifiant les options de géotraitement de Pro (voir http://pro.arcgis.com/en/pro-app/help/analysis/geoprocessing/basics/geoprocessing-options.htm). Pour accéder à ces options dans Pro, cliquez sur Accueil -> Options -> Options de géotraitement. Ici, vous pouvez également choisir une option pour valider automatiquement les outils et les scripts pour la compatibilité Pro (vous n'avez donc pas besoin d'exécuter les outils d'analyse pour Pro manuellement à chaque fois).

Nous allons maintenant apporter quelques modifications à notre code, en remplaçant les chemins codés en dur aux lignes 8 et 17 et la valeur cutoffElevation codée en dur à la ligne 9. Nous configurons également une variable outPath à la ligne 10 et la définissons sur arcpy .env.espace de travail.

Vous vous souvenez peut-être de GEOG 485 ou de votre autre expérience avec Desktop que l'espace de travail par défaut dans Desktop est généralement default.gdb dans votre chemin utilisateur. Pro est plus intelligent que cela et définit l'espace de travail par défaut comme étant la géodatabase de votre projet. Nous en profiterons pour mettre notre raster en sortie dans notre espace de travail de projet. Notez la différence dans le type de paramètre que nous utilisons dans les lignes 8 & amp 9. Nous pouvons obtenir le chemin sous forme de texte, mais nous ne voulons pas obtenir le nombre dans cutoffElevation sous forme de texte car nous avons besoin que ce soit un numéro.

Pour simplifier la programmation, nous allons spécifier un type de paramètre différent dans Pro et le laisser passer à notre script. Pour ce faire, nous utiliserons GetParameter au lieu de GetParameterAsText.

Une fois que vous avez apporté ces modifications, enregistrez le fichier et nous retournerons à notre outil de script dans Pro et le mettrons à jour pour utiliser les paramètres que nous venons de définir. Cliquez avec le bouton droit sur l'outil de script dans la boîte à outils et choisissez Propriétés, puis cliquez sur Paramètres. The first parameter we defined (remember Python counts from 0) was the path to our input raster (inRaster), so let's set that up. Click in the text box under Label and type “Input Raster” and when you click into Name you’ll see that Name is already automatically populated for you. Next, click the Data Type (currently String) and change it to “Raster Dataset” and we’ll leave the other values with their defaults.

Click the next Label text box below your first parameter (currently numbered with a *) and type “Cutoff Value” and change the Data Type to Long (which is a type of number) and we’ll keep the rest of the defaults here too. The final version should look as in the figure below.

Click OK and then we’ll run the tool to test the changes we made by double-clicking it. Use the file icon alongside our Input Raster parameter to navigate to your foxlake raster (which is the FoxLake digital elevation model (DEM) in your Lesson 1 data folder) and then enter 3500 into the cutoff value parameter and click OK to run the tool.

The tool should have executed without errors and placed a raster called foxlake_hi_10 into your project geodatabase.


1.5.1 Making a Script Tool

Here’s another simple script that finds all cells over 3500 meters in an elevation raster and makes a new raster that codes all those cells as 1. Remaining values in the new raster are coded as 0. By now, you’re probably familiar with this type of “map algebra” operation which is common in site selection and other GIS scenarios.

Just in case you’ve forgotten, the expression Raster(inRaster) tells arcpy that it needs to treat your inRaster variable as a raster dataset so that you can perform map algebra on it. If you didn't do this, the script would treat inRaster as just a literal string of characters (the path) instead of a raster dataset.

You can probably easily work out what this script is doing but, just in case, the main points to remember on this script are:

  • Notice the lines of code that check out the Spatial Analyst extension before doing any map algebra and check it back in after finishing. Because each line of code takes some time to run, avoid putting unnecessary code between checkout and checkin. This allows others in your organization to use the extension if licenses are limited. The extension automatically gets checked back in when your script ends, thus some of the Esri code examples you will see do not check it in. However, it is a good practice to explicitly check it in, just in case you have some long code that needs to execute afterward, or in case your script crashes and against your intentions "hangs onto" the license.
  • inRaster begins as a string, but is then used to create a Raster object once you run Raster(inRaster). A Raster object is a special object used for working with raster datasets in ArcGIS. It's not available in just any Python script: you can use it only if you import the arcpy module at the top of your script.
  • cutoffElevation is a number variable that you declare early in your script and then use later on when you build the map algebra expression for your outRaster.
  • The expression outRaster = Raster(inRaster) > cutoffElevation is saying, in plain terms, "Make a new raster and call it outRaster. Do this by taking all the cells of the raster dataset at the path of inRaster that are greater than the number assigned to the variable cutoffElevation."
  • outRaster is also a Raster object, but you have to call the method outRaster.save() in order to make it permanent on disk. The save() method takes one argument, which is the path to which you want to save.

Copy the code above into a file called Lesson1A.py (or similar as long as it has a .py extension) in spyder or your favorite IDE or text editor and then save it.

We don’t need to do anything to this code to get it to work in Python 3, it will be fine just as it is. Feel free to check it against Analyze Tools for Pro if you like. Your results should say “Analyze Tools for Pro Completed Successfully” with the lack of warnings signifying that the code you supplied is compatible with Python 3.


4 réponses 4

Edit, this workaround is no longer needed as of 2.91.

This looks like a kind of ms-windows specific problem. at least a conflict with having an embedded Python interpreter and the environment which multiprocessing expects. (its not exactly a bug, in that its not like Blender devs can fix some mistake to get this working).

Currently multiprocessing makes the assumption that its running in python et ne pas running inside an application.

I managed to get multi-processing working on ms-windows, doing some workarounds.

  • sys.executable needs to point to Python executable.
  • The scripts __file__ needs to point to a file on-disk
    (not always the case - when executing a text block for example).

Heres an example of a workaround:

Blender+Python can do this, but ne pas using multiprocessing . $endgroup$ &ndash ideasman42 Jun 22 '15 at 22:22

I think this statement in the same docs says a lot:

"So far, no work has gone into making Blender’s python integration thread safe, so until its properly supported, best not make use of this. "

The statement that subprocess and multiprocess can be used with Blender isn't false, just may be interpreted in reverse with regards to multiprocess module.

  • subprocess can call external apps fine with .call or .Popen , non-blocking or blocking. Works as expected, threaded or non threaded.
  • multiprocess probably doesn't work (right now) as you might expect, how I interpreted this statement originally was, "you can call several instances of Blender from a python program using multiprocessing , that way you have control over what ends when and can use multiple CPU's.

In the end the warning is reasonable clear (I think?) that we aren't to expect threaded execution of calls to Blender functions within the one Blender executable. (for instance function calls that take a lot of time, like baking.. it is possible to write scripts to do all that and finally when all blender processes have ended , merge the result in an additional script. The work (scripting wise) may be a little bit more, but that seems to be where it's at at the moment.

Ubuntu 14.04
On Ubuntu that code leaves headless processes behind, which only finish when the main Blender executable is terminated. If there's a problem it's not entirely localized to windows.


Geoprocessing services can have a result map service to create a digital map image of task results. Digital maps contain visual representations of geographic datasets that communicate information. Digital maps are transported across the Web as images (such as a .jpeg ). A map image, byte for byte, contains far more human-interpretable information than raw features in a feature class. Map images are also manageable—they are easily compressed, they can be tiled into manageable chunks, and there are established methods for transporting and viewing them across the Web.

Map images are created by an ArcGIS for Server map service and are the result of publishing an ArcMap document ( .mxd ). Because of the characteristics of a map image, you may want to create one for the results of your geoprocessing task and transport the image across the Web rather than transporting the result dataset or datasets. Geoprocessing services can have a result map service used by ArcGIS for Server to create map images of your output data.


Syntaxe

The input feature classes or feature datasets whose coordinates are to be converted.

The location of each new output feature class or feature dataset.

The coordinate system to be used to project the inputs.

Valid values are a Spatial Reference object, a file with a .prj extension, or a string representation of a coordinate system.

The feature class or the feature dataset used to specify the output coordinate system used for projection.

Name of the geographic transformation to be applied to convert data between two geographic coordinate systems (datums).


Voir la vidéo: Learn How to Perform Statistical Spatial Data Analysis with R and ArcGIS