Vous appréciez mon travail ?
Je serais ravi de prendre un café !

Vous prenez du plaisir à lire mes articles ? Vous apprenez de nouvelles choses ? Je serais ravis que vous supportiez mon travail avec une petite participation

1 café Merci, vous financez ma dose quotidienne de théïne (oui, en vrai je ne bois pas de café).
5 cafés Génial, ça couvre mes frais de serveur mensuels.
10 cafés Fantastique, avec ça je peux investir dans du matériel et approfondir mes connaissances.
BazinGa's - Tips & tuto IT

Gérez le Python qui se cache dans FME

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).

La compatibilité est fixée à Python 3.10 mais l’interpréteur lancé est 3.11

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

50%