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"]
    FME_Coordinates addCoord  $x $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"]
    FME_Coordinates addCoord  $x $y $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 addCoord 0 0 0
  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
I had requested Safe support about this problem; they provided a more appropriate workaround. That is, to set a geometry type beforehand.
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.

No comments:

Post a Comment