Comme dans tout bon logiciel de traitement des données spatiales, FME permet l’utilisation de Python. Il possède en fait son propre interpréteur (que l’on peut tout à fait utiliser en dehors de FME d’ailleurs). Nous allons voir comment tout cela fonctionne.
Versions de l’interpréteur
Quand on parle Python, on parle version. Nombre d’entre-vous savent que lorsque l’on utilise Python il faut souvent jongler entre différentes versions et donc faire attention à laquelle on fait référence (notamment en gérant correctement sa variable PATH).
FME peut lui aussi utiliser différentes versions de Python mais il gère cela à sa manière toute particulière.
En effet, dans FME, on peut indique une version préférée dans les options
Vous pouvez spécifier votre propre interpréteur que vous auriez installé par ailleurs.
Attention, car version préférée signifie pour FME :
- La compatibilité Python renseignée par défaut dans les nouveaux workspaces.
- L’interpréteur chargé lors de l’utilisation du shell Python de FME.
- La version de Python pour laquelle seront installés les différents modules via le shell.
Ce paramètre ne contrôle pas la version de Python qui sera utilisée par FME pour vos workspaces. En effet, dans un workspace, on spécifie une version de Python compatible, FME se débrouille donc pour utiliser l’interpréteur correspondant (voir la partie sur les workspaces).
Utiliser et gérer Python
Pour utiliser le shell Python de FME, il suffit d’utiliser la ligne de commande suivante :
fme.exe python
Notez que vous devrez peut être préciser l’emplacement de fme en préfixant le chemin vers le fichier fme.exe. D’ailleurs, faites attention, si vous avez plusieurs versions de fme sur votre ordinateur, le fme spécifié dans la variable PATH n’est peut être pas celui que vous souhaitez utiliser.
Vous devriez avoir un retour comme celui-ci vous précisant la version de l’interpréteur lancée :
C:\>fme.exe python INFORM: Using Python interpreter from `C:\Program Files\FME\FME_2023.0.0.3\fmepython311\python311.dll' with PYTHONHOME `C:\Program Files\FME\FME_2023.0.0.3\fmepython311' INFORM: Python version 3.11 loaded successfully Python 3.11.3 (tags/v3.11.3:f3909b8, Apr 4 2023, 23:49:59) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>
Le choix de l’interpréteur précisé dans la section précédente permet de définir le Python lancé avec la commande précédente.
Vous pouvez ainsi facilement installer les modules dont vous avez besoin grâce à la ligne de commande suivante :
fme.exe python -m pip install <package_name>
Python dans les workspaces
Safe met à disposition une documentation sur l’utilisation de ses modules Python : https://docs.safe.com/fme/html/fmepython/index.html
Il existe également un guide de démarrage avec Python dans FME : https://community.safe.com/s/article/python-and-fme-basics
Version
Pour déterminer la version de Python à utiliser avec un workspace, il faut se rendre dans ses paramètres :
Attention, il ne s’agit pas de la version de l’interpréteur qui sera utilisée mais d’une information de compatibilité. FME décide lui-même de l’interpréteur à utiliser (en général le plus récent).
Modules disponibles
import FME
Le module fme
permet notamment l’accès aux paramètres présents dans FME.
Ces paramètres sont stockés au sein de la variable de type dictionnaire macroValues
. Voici comment récupérer la valeur d’une variable :
import fme ma_variable = fme.macroValues['MON_PARAMETRE']
import fmeobjects
Le module fmeobjects
contient la très grande majorité des classes et méthodes que propose l’API Python de FME. C’est notamment grâce à ce module que vous pourrez manipulez les objets ou encore logguer différents messages.
La documentation sur ce module est disponible ici.
Voici les principales classes disponibles :
fmeobjects.FMEFeature
: objet (au sens FME) possédant une géométrie et des attributs.fmeobjects.FMEGeometryTools
: classe contenant un ensemble de méthodes pour manipuler les géométries vectorielles.fmeobjects.FMERasterTools
: classe contenant un ensemble de méthodes pour manipuler les données rasters.fmeobjects.FMEDialog
: classe permettant d’afficher et d’utiliser des boites de dialogue.fmeobjects.FMETransformer
: classe permettant d’accéder au paramétrage des diverses transformers présents dans le workspace.fmeobjects.FMEUniversalReader
: classe permettant d’instancier un reader afin de lire des données et de les importer dans le script Python.fmeobjects.FMEUniversalWriter
: classe permettant d’instancier un writer afin d’écrire des données présentes dans le script Python.fmeobjects.FMEWorkspaceRunner
: classe permettant d’instancier un workspacerunner afin de manipuler et de lancer un ou plusieurs workspace.fmeobjects.FMELogFile
: classe permettant de gérer les messages à logguer et plus globalement le loggueur.
De nombreuses constantes sont également disponibles et parmi elles :
- Les types d’attributs.
- Les types de géométries.
- La sévérité des messages loggués.
Voici un exemple d’utilisation du module pour créer un objet à partir d’un autre :
import fmeobjects def input(self, feature): # Instanciation d'un nouvel objet vide newFeature = fmeobjects.FMEFeature() # Ajout d'attributs en récupérant la valeur de l'objet entrant newFeature.setAttribute( "attribut_1", feature.getAttribute("attribut_A") ) newFeature.setAttribute( "attribut_2", feature.getAttribute("attribut_B") ) # Récupération de la géométrie newFeature.setGeometry( feature.getGeometry() ) # Sortie de l'objet nouvellement créé self.pyoutput(newFeature) # L'ancien objet est tout simplement abandonné car non renvoyé
Log
Pour logguer des données dans vos scripts, vous devez utiliser l’objet FMELogFile
du module fmeobjects
de la façon suivante :
logger = fmeobjects.FMELogFile() logger.logMessageString('Mon message')
Vous pouvez spécifier le niveau du message de log parmi les suivants :
FME_INFORM
: simple information.FME_WARN
: avertissement (en bleu dans la fenêtre de log).FME_ERROR
: erreur (en rouge dans la fenêtre de log).FME_FATAL
: arrêt du script.
FMELogFile.logMessageString('Mon message', fmeobjects.FME_INFORM)
Notez que vous pouvez également logguer un message au niveau FME_INFORM
en utilisant la fonction print()
.
Attention car dans paramètres scriptés ainsi que les scripts à la fermeture, il est possible d’utiliser le module fmeobjects
mais les messages seront toujours loggués au niveau FME_INFORM
. Ainsi, si vous spécifiez un autre niveau, votre message sera préfixé :
De plus, dans ces deux types de script, les messages de log ne sont présents que dans la console de log de FME, jamais dans le fichier de log. En effet, dans les paramètres scriptés, le fichier de log n’est pas encore initialisé et dans les scripts à la fermeture, le fichier de log est déjà fermé.
Il est complexe d’écrire dans le fichier de log depuis un paramètre scripté car vous devez créer le fichier de log puis indiquer à FME d’utiliser ce fichier.
Pour logguer dans le fichier depuis un script à la fermeture, vous devez récupérer l’emplacement du fichier de log via la variable logFileName
du module fme
, puis écrire directement dans ce fichier.
Transformers
PythonCreator
Ce transformer permet de créer un ou plusieurs objets durant la traduction et ceci à partir d’un script Python.
Pour cela, il suffit d’indiquer un nom de classe ou de fonction à utiliser :
Puis de définir la classe ou la fonction en question, voici une classe type que vous pouvez réutiliser :
# Import des modules FME import fmeobjects import fme class FeatureCreator(object): # Initialisation de la classe def __init__(self): #Définition variables # Logger : # Utilisation : self.logger.logMessageString('Message', fmeobjects.FME_ERROR) # Niveau parmi : FME_INFORM, FME_WARN, FME_ERROR, FME_FATAL self.logger = fmeobjects.FMELogFile() # Méthode appelée (1 seule fois) pour créer l'objet initial def input(self, feature): # La variable "feature" issue du paramètre peut être mise de côté, elle ne sert qu'a FME et ne contient rien # Création d'un objet newFeature = fmeobjects.FMEFeature() # Ajout d'attributs newFeature.setAttribute( "nom", "Bazin" ) newFeature.setAttribute( "prenom", "Arthur" ) # Sortie de l'objet self.pyoutput(newFeature) # Fermeture de la classe def close(self): pass
Notez que par défaut FME lance les méthodes input()
puis close()
pour toute classe lancée.
Il vous est tout à fait possible d’utiliser une boucle pour créer plusieurs objets. Par exemple :
def input(self, feature): for i in range(11): # Utilisez la fonction range pour générer les nombres de 0 à 10 # Création d'un objet newFeature = fmeobjects.FMEFeature() # Ajout d'attributs newFeature.setAttribute( "id", i ) # Sortie de l'objet self.pyoutput(newFeature)
A la sortie du transformer, n’hésitez pas à exposer les attributs créés pour pouvoir les utiliser dans d’autres transformers :
PythonCaller
Ce transformer permet de traiter des objets durant la traduction à l’aide d’un script Python.
Pour cela, il suffit d’indiquer un nom de classe ou de fonction à utiliser :
Puis de définir la classe ou la fonction en question, voici une classe type que vous pouvez réutiliser :
# Import des modules FME import fmeobjects import fme class FeatureProcessor(object): # Initialisation de la classe def __init__(self): #Définition variables # Logger : # Utilisation : self.logger.logMessageString('Message', fmeobjects.FME_ERROR) # Niveau parmi : FME_INFORM, FME_WARN, FME_ERROR, FME_FATAL self.logger = fmeobjects.FMELogFile() # Méthode appelée pour chaque objets (passé dans l'argument 'feature') def input(self, feature): # Affichage de la valeur du champ "ident" dans le log de FME self.logger.logMessageString('Objet : ' + feature.getAttribute("nom"), fmeobjects.FME_INFORM) # Ajout d'attributs feature.setAttribute( "sexe", "Homme" ) # Sortie de l'objet self.pyoutput(feature) # Fermeture de la classe def close(self): pass # Méthode appelée pour chaque groupe (Group processing) après que tous les objets du groupe aient été traités def process_group(self): pass
Notez que par défaut FME lance les méthodes input()
puis close()
pour toute classe lancée.
Il vous est tout à fait possible d’utiliser une boucle pour créer plusieurs objets à partir d’un seul.
Comme dans le PythonCreator, vous pouvez exposer des attributs mais vous pouvez également en cacher d’autres si ces attributs ont été supprimés dans votre script Python :
Autres scripts
D’autres scripts Python peuvent être lancés depuis FME. Les voici dans leur ordre d’exécution :
- Paramètres scriptés
- Script au démarrage
- Traduction (et donc éventuellement les différents PythonCreator et PythonCaller utilisés)
- Script à la fermeture
Paramètre scripté
Les paramètres d’un workspace permettent de définir des valeurs qui seront ensuite utilisées durant la traduction. Certains, sont prédéfinits par l’auteur du workspace, d’autres sont laissés modifiables à celui qui exécutera la traduction.
Il existe une troisième catégorie de paramètres, les paramètres scriptés. Il s’agit de paramètres dont la valeur est définie via à un script Python. Ils sont sont calculés avant la traduction et avant même les scripts au démarrage. Cela vous permet par exemple de définir des emplacements spécifiques en fonction de la valeur d’un autre paramètre.
Notez que le script doit comporter la déclaration return
pour renvoyer la valeur finale que prendra le paramètre.
Dans ces scripts, vous avez accès aux autres paramètres via la variable de type dictionnaire macroValues
ainsi qu’aux fonctions de log offertes par le module fmeobjects
mais sans pouvoir ajuster le niveau de log (les message seront préfixés par le niveau indiqué mais apparaitrons toujours comme ayant le niveau « FME_INFORM »).
Le fichier de log n’étant pas encore initialisé à ce stade, tout message loggué ici, ne sera présent que dans la console de log de FME, jamais dans le fichier.
Vous n’avez pas accès aux objets, ceux-ci n’ayant pas encore été chargés dans le workspace.
Script au démarrage
Il est possible d’exécuter un script Python avant le démarrage de la traduction.
Cela se passe dans les paramètres du workspace :
Pendant l’exécution du script, vous avez accès aux paramètres via la variable de type dictionnaire macroValues
ainsi qu’aux fonctions de log offertes par le module fmeobjects
. Contrairement aux paramètres scriptés, vous pouvez ici ajuster le niveau de vos messages et ces messages seront présents dans le fichier de log.
Vous n’avez en revanche pas accès aux objets, ceux-ci n’ayant pas encore été chargés dans le workspace.
Ainsi, vous pouvez, par exemple, récupérer l’emplacement des fichiers qui vont être lus pour en créer une sauvegarde ou encore afficher un message personnalisé dans le log comme décrit dans la documentation officielle.
Un autre exemple est le test de la version l’interpréteur avant de lancer la traduction :
import sys if sys.version_info < (3,10): raise Exception("Python 3.10 ou supérieur est nécessaire pour lancer le Workspace")
Notez qu’une exception levée par ce script stoppe immédiatement le processus et la traduction n’est jamais démarrée. Vous pouvez ainsi vous même lever des exceptions pour empêcher l’exécution du workspace.
Script à la fermeture
Il est possible d’exécuter un script Python après la fin de la traduction.
Cela se passe dans les paramètres du workspace :
Pendant l’exécution du script, vous avez accès aux paramètres via la variable de type dictionnaire macroValues
ainsi qu’aux fonctions de log offertes par le module fmeobjects
mais sans pouvoir ajuster le niveau de log (les message seront préfixés par le niveau indiqué mais apparaitrons toujours comme ayant le niveau « FME_INFORM »).
Le fichier de log étant déjà fermé, tout message loggué ici, ne sera présent que dans la console de log de FME, jamais dans le fichier.
Ici aussi vous n’avez pas accès aux objets traités car la traduction est terminée.
Vous avez en revanche la possibilité d’utiliser toute une liste de variables contenant des données sur la traduction qui vient de se dérouler (durée de la traduction, quantité max de RAM utilisée, nombre d’objets lus et écrits…).
Ainsi, vous pouvez, par exemple, envoyer un mail contenant différentes statistiques ou encore créer un fichier de log listant chaque exécution de la traduction comme décrit dans la documentation officielle.
Fonctions et variables globales
Saviez-vous que vous pouviez définir des fonctions et des variables globales, réutilisables partout ? Et oui, de deux façons différentes en plus :
- Dans un module spécifique
- Dans le script au démarrage
Dans un module spécifique
Vous pouvez créer votre propre module Python puis l’appeler dans votre workspace.
Pour cela, vous devez créer un fichier python (« .py ») dans le même répertoire que votre workspace. Celui-ci contiendra les fonctions et variables appartenant à votre module. Par exemple le fichier tuto_module.py :
def module_function(value): return "Ma fonction module retourne : " + value module_var = "Module"
Ensuite, il suffit d’importer le module et de l’utiliser comme suivant n’importe où (script au démarrage, PythonCreator, PythonCaller, script à la fermeture) :
import tuto_module print('Variable module à l\'initialisation : ' + tuto_module.module_var) print(tuto_module.module_function('initialisation'))
Vous devriez voir les lignes suivantes dans votre log FME :
... Variable globale à l'initialisation : Startup Python Script Ma fonction retourne : initialisation ...
Script au démarrage
Dans le script au démarrage, ajouter les lignes suivantes :
# Import des modules FME import fme import fmeobjects # Création d'un logger pour afficher les messages global_logger = fmeobjects.FMELogFile() # Premier message global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'Startup Python Script '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN ) # Tout ce qui est défini ici sera disponible dans l'ensemble des scripts Python lors de l'execution du Workspace # Définition d'une variable global_var = "Startup Python Script" global_logger.logMessageString(' Variable globale : ' + global_var) # Définition d'une fonction def global_function(value): return "Ma fonction globale retourne : " + value global_logger.logMessageString(' Résultat de fonction : ' + global_function('initialisation')) # Import d'un module import tuto_module global_logger.logMessageString(' Variable module : ' + tuto_module.module_var) global_logger.logMessageString(' Résultat fonction module : ' + tuto_module.module_function('initialisation')) global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'Startup Python Script '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN )
Ajoutez ensuite un PythonCreator et utilisez le code suivant :
# Import des modules FME non nécessaire car déjà fait dans le script au démarrage class FeatureCreator(object): # Initialisation de la classe def __init__(self): # On réutilise des variables créées dans le script au démarrage # Premier message global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'PythonCreator '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN ) # Tout ce qui a été défini est toujours présent # Variable global_logger.logMessageString(' Variable globale : ' + global_var) # Fonction global_logger.logMessageString(' Résultat de fonction : ' + global_function('shutdown')) # Module importé global_logger.logMessageString(' Variable module : ' + tuto_module.module_var) global_logger.logMessageString(' Résultat fonction module : ' + tuto_module.module_function('initialisation')) # Méthode appelée (1 seule fois) pour créer l'objet initial def input(self, feature): # La variable "feature" issue du paramètre peut être mise de côté, elle ne sert qu'a FME et ne contient rien # Création d'un objet newFeature = fmeobjects.FMEFeature() # Ajout d'attributs newFeature.setAttribute( "nom", "Bazin" ) newFeature.setAttribute( "prenom", "Arthur" ) # Sortie de l'objet self.pyoutput(newFeature) # Fermeture de la classe def close(self): global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'PythonCreator '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN )
Enfin, ajoutez le code suivant dans le script à la fermeture :
# Pas besoin d'import des modules FME car ils ont été importés dans le script de démarrage # Premier message global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'Shutdown Python Script '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN ) # Tout ce qui a été défini est toujours présent # Variable global_logger.logMessageString(' Variable globale : ' + global_var) # Fonction global_logger.logMessageString(' Résultat de fonction : ' + global_function('shutdown')) # Module importé global_logger.logMessageString(' Variable module : ' + tuto_module.module_var) global_logger.logMessageString(' Résultat fonction module : ' + tuto_module.module_function('initialisation')) global_logger.logMessageString( ''.ljust(100, '-') + '\n' + 'Shutdown Python Script '.ljust(100, '-') + '\n' + ''.ljust(100, '-'), fmeobjects.FME_WARN )
Lancer le workspace, votre log devrait afficher les éléments suivants :
... FME_BEGIN_PYTHON: evaluating python script from string... ---------------------------------------------------------------------------------------------------- Startup Python Script ------------------------------------------------------------------------------ ---------------------------------------------------------------------------------------------------- Variable globale : Startup Python Script Résultat de fonction : Ma fonction globale retourne : initialisation Variable module : Module Résultat fonction module : Ma fonction module retourne : initialisation ---------------------------------------------------------------------------------------------------- Startup Python Script ------------------------------------------------------------------------------ ---------------------------------------------------------------------------------------------------- FME_BEGIN_PYTHON: python script execution complete. ... ---------------------------------------------------------------------------------------------------- PythonCreator -------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------- Variable globale : Startup Python Script Résultat de fonction : Ma fonction globale retourne : shutdown Variable module : Module Résultat fonction module : Ma fonction module retourne : initialisation PythonCreator_Creator (CreationFactory): Created 1 features Optional `process_group' method not present; not called ---------------------------------------------------------------------------------------------------- PythonCreator -------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------- ... INFORM: FME_END_PYTHON: evaluating python script from string... WARN : ---------------------------------------------------------------------------------------------------- Shutdown Python Script ----------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------- INFORM: Variable globale : Startup Python Script INFORM: Résultat de fonction : Ma fonction globale retourne : shutdown INFORM: Variable module : Module INFORM: Résultat fonction module : Ma fonction module retourne : initialisation WARN : ---------------------------------------------------------------------------------------------------- Shutdown Python Script ----------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------- INFORM: FME_END_PYTHON: python script execution complete. ...
Vous voyez qu’il vous est possible de définir des fonctions et des variables puis de les réutiliser n’importe où ce qui peut grandement vous faciliter la tâche.
Cet article vous a plu ?
N'hésitez pas à le partager, il interessera surement certains de vos contacts.
Les thèmes suivants contiennent des articles en lien avec celui-ci, allez faire un tour :
FMEProgrammationPythonSIG