Getting started

Get acquainted with the FME Objects Python API and learn how to accomplish some common tasks.

PythonCreator transformer

The PythonCreator transformer can be used to create new features in the workspace. It contains a Python code template that looks similar to this:

import fme
from fme import BaseTransformer
import fmeobjects

class FeatureCreator(BaseTransformer):

    def __init__(self):
        pass

    # create features before first reader feature is processed
    def input(self, feature):
        newFeature = fmeobjects.FMEFeature()
        self.pyoutput(newFeature)

    # output features in close() to create the features
    # after all reader features have been processed
    def close(self):
        pass

The first line, import fmeobjects, imports the FME Objects Python API. In FeatureCreator.input(), an FMEFeature is constructed, then returned using self.pyoutput().

The FeatureCreator class inherits from BaseTransformer, a base class that represents the interface expected by the FME infrastructure for Python-based transformer implementations.

PythonCaller transformer

The PythonCaller transformer can be used to manipulate existing features using Python. It contains a Python code template that looks similar to this:

import fme
from fme import BaseTransformer
import fmeobjects

class FeatureProcessor(BaseTransformer):

    def __init__(self):
        pass

    def input(self, feature):
        self.pyoutput(feature)

    def close(self):
        pass

This template is similar to the one in the PythonCreator transformer and also uses the BaseTransformer. The difference is that its input() method receives an FMEFeature as an argument. Each feature entering the PythonCaller’s input port is passed to the input() method in the PythonCaller, where it can be worked with. self.pyoutput() then emits the feature from the PythonCaller’s output port.

Though the PythonCaller is intended for manipulating existing features in a pipeline, it’s also possible to use it to create new features, just like PythonCreator. self.pyoutput() can be called multiple times in order to emit more than one feature for each feature entering the input port. The same feature can also be emitted multiple times: each feature from the output port is a copy.

Using FME’s Python REPL

Aside from using the PythonCreator and PythonCaller transformers described above, another good way to experiment with the FME Objects Python API is the FME Python interpreter’s interactive shell. To use it, open a terminal or command prompt in your FME install directory, and enter:

> fme python
Python 2.7.10 (default, May 23 2015, 09:44:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import fmeobjects
>>>

This brings up an interactive Python shell with all neccessary paths and dependencies set up.

Getting and setting the feature type

When using a PythonCaller to work with features that originated from a reader, it may be useful to know the feature’s feature type. getFeatureType() is used to obtain the feature type.

setFeatureType() is used to set the feature type of a feature.

Getting and setting attributes

Use setAttribute() to set all non-null attributes. Most attribute types are supported. To set a list attribute, pass a list as the value. To set null attribute values, see the next section.

Below is an example of getting and setting various attributes:

>>> from fmeobjects import FMEFeature, FME_ATTR_REAL32

>>> feature = FMEFeature()
>>> feature.setAttribute('beavers', 10)
>>> feature.setAttribute('park_name', 'Rat Park')
>>> feature.setAttribute('rangers', ['Alice', 'Bob'])
>>> feature.getAttribute('beavers'), feature.getAttribute('park_name'), feature.getAttribute('rangers')
10, 'Rat Park', ['Alice', 'Bob']

Working with null or missing attributes

getAttribute() cannot be used to determine whether an attribute is missing or has a null value. Similarly, setAttribute() cannot be used to set null values on attributes. Separate methods are dedicated to this task.

To set null attribute values, use setAttributeNullWithType(), passing it the name of the attribute to set to null, and the attribute type it would have been if it weren’t null. The attribute type information typically comes from any schema that your data is honouring, if any.

Note

By convention, when setting a null value and the type is unimportant, FME_ATTR_STRING is declared as the type.

To remove an attribute, or in other words, to make an existing attribute be missing, use removeAttribute().

To determine whether an attribute is missing, has a null value, and its type, use getAttributeNullMissingAndType(). It returns a 3-element tuple.

Below is an example of getting and setting null and missing attributes:

>>> from fmeobjects import FMEFeature, FME_ATTR_REAL32

>>> feature = FMEFeature()
>>> feature.setAttributeNullWithType('park_area', FME_ATTR_REAL32)
>>> feature.getAttributeNullMissingAndType('park_area')
True, False, 8
>>> feature.setAttribute('raccoons', 64)
>>> feature.removeAttribute('raccoons')
>>> feature.getAttributeNullMissingAndType('raccoons')
False, True, 0
>>> feature.getAttributeNullMissingAndType('nonexistent')
False, True, 0

Working with geometry

The FME Objects Python API has many geometry classes. The following is a simple example that constructs a 2D point and an offset 2D point, offsets the point by the offset point, fetches the new coordinates, and sets the new point to a feature in two different ways:

>>> from fmeobjects import FMEFeature, FMEPoint, FMEGeometryTools

>>> feature = FMEFeature()
>>> point = FMEPoint(10, 20)
>>> offset = FMEPoint(10, 10)
>>> point.offset(offset)
>>> point.getXYZ()
(20.0, 30.0, 0.0)
>>> feature.setGeometry(point)
>>> feature.getGeometry().getXYZ()
(20.0, 30.0, 0.0)
>>> offsetPoint = FMEGeomtryTools().offset(point, offset)
>>> offsetPoint.getXYZ()
(30.0, 40.0, 0.0)
>>> feature.setGeometry(offsetPoint)
>>> feature.getGeometry().getXYZ()
(30.0, 40.0, 0.0)

Many geometric operations are available through FMEGeometryTools. Consult their API for details.

Working with rasters

The FME Objects Python API has many raster classes. The following is an example of a PythonCreator transformer that creates new raster data, specifies the parameters of the raster and populates it when requested:

import fmeobjects


class MyBandTilePopulator(fmeobjects.FMEBandTilePopulator):
    """
    This is a subclass of the FMEBandTilePopulator superclass.
    It will be used when data is requested to create a new tile and
    and populate it to a new FMEBand.
    """
    def __init__(self, rasterData):
        self._rasterData = rasterData

    # required method
    def clone(self):
        """
        This method is used to create a copy of the data
        multiple times while creating a new band
        """
        return MyBandTilePopulator(self._rasterData)

    # required method
    def getTile(self, startRow, startCol, tile):
       """
       Creates a new tile that's sized based on the input tile.
       Populates that tile using this populator's raster data beginning
       at the startRow and startCol.
       """
       numRows, numCols = tile.getNumRows(), tile.getNumCols()
       newTile = fmeobjects.FMEUInt8Tile(numRows, numCols)
       data = newTile.getData()
       for row in range(startRow, startRow+numRows):
           for col in range(startCol, startCol+numCols):
               if row < len(self._rasterData) and col < len(self._rasterData[0]):
                   data[row-startRow][col-startCol] = self._rasterData[row][col]
       newTile.setData(data)
       return newTile


class FeatureCreator(object):
    def __init__(self):
        pass

    def input(self, feature):
        pass

    def close(self):

        # creating the raster data and specifying the formatting of the new raster
        rasterData = [
            [0, 128, 0, 128, 0, 128, 0],
            [128, 0, 128, 0, 128, 0, 128],
            [0, 128, 0, 128, 0, 128, 0],
            [128, 0, 128, 0, 128, 0, 128],
            [0, 128, 0, 128, 0, 128, 0]
        ]

        # specifying all of the properties for the new FMERaster
        numRows, numCols = len(rasterData), len(rasterData[0])
        xCellOrigin, yCellOrigin = 0.5, 0.5
        xSpacing, ySpacing = 1.0, 1.0
        xOrigin, yOrigin = 0, 0
        xRotation, yRotation = 0.0, 0.0

        # creating the new FMERaster
        rasterProperties = fmeobjects.FMERasterProperties(numRows, numCols,
                                                          xSpacing, ySpacing,
                                                          xCellOrigin, yCellOrigin,
                                                          xOrigin, yOrigin,
                                                          xRotation, yRotation)
        raster = fmeobjects.FMERaster(rasterProperties)

        # Populating the contents of the band and appending it to the raster
        bandTilePopulator = MyBandTilePopulator(rasterData)
        bandName = ''
        bandProperties = fmeobjects.FMEBandProperties(bandName,
                                                      fmeobjects.FME_INTERPRETATION_UINT8,
                                                      fmeobjects.FME_TILE_TYPE_FIXED,
                                                      numRows, numCols)
        band = fmeobjects.FMEBand(bandTilePopulator, rasterProperties,
                                  bandProperties)
        raster.appendBand(band)

        # creating a new feature with the FMERaster geometry to be output
        feature = fmeobjects.FMEFeature()
        feature.setGeometry(raster)
        self.pyoutput(feature)

Coordinate systems and reprojection

Coordinate systems are set at the feature level instead of the geometry level. By default, features do not have a coordinate system. To assign one, pass a coordinate system name to setCoordSys():

>>> from fmeobjects import FMEFeature, FMEPoint

>>> feature = FMEFeature()
>>> feature.setGeometry(FMEPoint(-122.842764, 49.177847))
>>> feature.setCoordSys('LL84')

When a feature has a coordinate system, it can be reprojected using reproject(). This operation is similar to the Reprojector transformer. For example:

>>> feature.reproject('SPHERICAL_MERCATOR')
>>> feature.getCoordSys()
'SPHERICAL_MERCATOR'
>>> feature.getGeometry().getXYZ()
(-13674793.936118279, 6305092.363454558, 0.0)

The FMECoordSysManager and FMEReprojector classes are available for more advanced operations with coordinate systems and reprojection.

Writing messages to the FME log

Python’s print() function can be used to log messages, but for best results in FME, use FMELogFile:

>>> from fmeobjects import FMELogFile

>>> FMELogFile().logMessageString("hello world")

If this code executed in Workbench, this immediately prints “hello world” as an informational message in the log.

The message severity can be set using the second parameter of logMessageString(). Typical values are:

  • FME_INFORM (default): Informational message.

  • FME_WARN: Warning that appears in blue.

  • FME_ERROR: Error message that appears in red.

  • FME_DEBUG: Message only appears if debug messages are enabled in FME Options.

FMEFeature instances can also be logged using logFeature(). The result is similar to using the Logger transformer. This method also supports severity levels.