2013-12-07

Tcl is Useful for Geometric Operations?

Through learning about the basic usage of Tcl in FME, I understand that Tcl is very useful for string processing. But, comparing with Python, I didn't think Tcl in FME has much capability for geometric operations. Really so?

Consider creating a line geometry based on a list attribute containing coordinates.
Assuming that the input feature has no geometry, but has a list attribute like this.
_coord{}.x
_coord{}.y
_coord{}.z

TclCaller with this compact script creates a 2D line geometry from the list.
-----
proc createLine {} {
for {set i 0} {[FME_AttributeExists "_coord{\$i}.x"] == 1} {incr i} {
set x [FME_GetAttribute "_coord{\$i}.x"]
set y [FME_GetAttribute "_coord{\$i}.y"]
}
}
-----
Fine.

Then, create a 3D line. I expected this script would work fine as well.
-----
proc create3DLine {} {
FME_Coordinates dimension 3
for {set i 0} {[FME_AttributeExists "_coord{\$i}.x"] == 1} {incr i} {
set x [FME_GetAttribute "_coord{\$i}.x"]
set y [FME_GetAttribute "_coord{\$i}.y"]
set z [FME_GetAttribute "_coord{\$i}.z"]
}
}
-----
But it failed unfortunately. Created line was still in 2D, Z-values were missing.
It seems that "dimension" option of FME_Coordinates command is invalid when the feature has no geometry. If I set dimension to 3D after creating line, every Z-value becomes 0 although the line becomes 3D. It works like the 3DForcer transformer, but is not the expected functionality.

A workaround I found is: Add a dummy coordinate, set dimension to 3D, and remove the dummy coordinate before creating a line.
Note: There is more appropriate workaround as after-mentioned (2013-12-14).
-----
proc create3DLine {} {
FME_Coordinates dimension 3
FME_Coordinates resetCoords
for {set i 0} {[FME_AttributeExists "_coord{\$i}.x"] == 1} {incr i} {
...
}
}
-----
It worked, but I don't like wasting steps for the dummy coordinate.
I expect the geometry to become 3D automatically when Z is given as the third argument for "FME_Coordinates addCoord".

=====
2013-12-14
If I set both geometry type and dimension like this, a 3D line will be created expectedly. It seems that "dimension" option will be valid after setting geometry type.
This is more desirable script. Thanks, Dan@Safe.
-----
proc create3DLine {} {
FME_Coordinates geomType fme_line
FME_Coordinates dimension 3
for {set i 0} {[FME_AttributeExists "_coord{\$i}.x"] == 1} {incr i} {
...
}
}
=====

For geometric operations, FME_Execute procedure can be also used. FME_Execute calls an FME Function directly.
"Close" function, for example, can be used to change a line to a polygon when the line consists of 3 or more coordinates. It's similar to the LineCloser transformer.
Just append a line to the procedure like this.
-----
proc createPolygon {} {
for { ...
...
}
FME_Execute Close
}
-----

There are many FME Functions for geometric operations. If we could see detailed documentations about them, Tcl could be used much more effectively.
I remember that we could see the documentation "FME Functions and Factories" a few years ago, but it cannot be accessed now (2013-12-07).
FMEpedia > Documentation: FME Functions and Factories

...so, my answer to the title is maybe a little negative, currently.
(FME 2013 SP4 Build 13547)
=====
2014-03-21: I discovered that the documentation has been enabled. So I now change my answer to the title into "positive"!
> FME Factory and Function Documentation

=====
2013-12-14: Found another way to change an unclosed line to a polygon.
FME_Coordinates procedure with "geomType" option can be also used to do that.
If I set explicitly the geometry type to "fme_polygon" before or after creating an unclosed line, then the line will be closed automatically and the resultant geometry will be a polygon.
-----
FME_Coordinates geomType fme_polygon
-----
There seems to be many things which are not told in published documentations.

=====
2013-12-08: For comparison...
-----
# Python Script Example: Create 2D Line
import fmeobjects
def createLine(feature):
xs = feature.getAttribute('_coord{}.x')
ys = feature.getAttribute('_coord{}.y')
coords = [(float(x), float(y)) for x, y in zip(xs, ys)]
feature.setGeometry(fmeobjects.FMELine(coords))
-----
# Python Script Example: Create 3D Line
import fmeobjects
def create3DLine(feature):
xs = feature.getAttribute('_coord{}.x')
ys = feature.getAttribute('_coord{}.y')
zs = feature.getAttribute('_coord{}.z')
coords = [(float(x), float(y), float(z)) for x, y, z in zip(xs, ys, zs)]
feature.setGeometry(fmeobjects.FMELine(coords))
-----
# Python Script Example: Create 3D Polygon
import fmeobjects
def createPolygon(feature):
xs = feature.getAttribute('_coord{}.x')
ys = feature.getAttribute('_coord{}.y')
zs = feature.getAttribute('_coord{}.z')
coords = [(float(x), float(y), float(z)) for x, y, z in zip(xs, ys, zs)]
boundary = fmeobjects.FMELine(coords)
feature.setGeometry(fmeobjects.FMEPolygon(boundary))
-----

Of course FME Transformers without any scripting can do those operations.
I think a general approach will be like this.
ListExploder --> 2D/3DPointReplacer --> PointConnector (--> LineCloser LineJoiner)
2013-12-15: corrected a typo of "LineCloser". sorry.