2014-12-13

Read Schemas from Database with Schema (Any Format) Reader

Schema (Any Format) リーダーでデータベースからスキーマを読む

I mentioned a quick way to create a look-up table for the Dynamic Schema in this thread.
このスレッドでダイナミックスキーマ用のルックアップテーブルを作成する簡単な方法に言及しました。
Community Answers > Can you use SchemaMapper or BulkAttributeRenamer with a dynamic schema writer?

Related articles 関連記事:
FMEpedia > Dynamic Workflow Tutorial: Destination Schema is Derived from a Lookup Table
This Blog > Common Table for SchemaMapper and Dynamic Schema

The Schema (Any Format) reader followed by a ListExploder can be used to create a table that contains feature type names, attribute names and their data type names of source feature types. It will cut down your time and effort for creating a look-up table for the Dynamic Schema.
It's a wisdom of a lazy person who doesn't want to move his/her hands :)
Schema (Any Format) リーダーとListExploderは、ソースフィーチャータイプのフィーチャータイプ名、属性名、およびそれらのデータ型名を含むテーブルを作成するのに使えます。これは、ダイナミックスキーマ用のルックアップテーブルを作成するための時間と労力を削減するでしょう。
手を動かしたくない怠け者の智恵です (^^)

When adding the Schema reader, you need to specify the source dataset to its Dataset field.
Schemaリーダーを追加する際、Datasetフィールドにソースデータセットを指定する必要があります。













If the physical form of the source dataset was a file, you can select the file with the general Select File dialog box. And if it was a folder (e.g. ESRI File Geodatabase), you can enter the folder path into the Dataset field directly.
ソースデータセットの物理的な形態がファイルである場合は、通常のファイル選択ダイアログボックスによって選択することができます。 また、フォルダ(例えばESRI File Geodatabase)である場合は、Datasetフィールドにフォルダパスを直接入力することができます。

So then, how can you set a database as its source dataset?
では、ソースデータセットとしてデータベースを設定するには?

The Schema reader doesn't have parameters for database connection, such as host name, port number, user name, password. Even though you specified the database name to the Dataset, naturally you cannot configure the connection to the database.
Schemaリーダーには、ホスト名、ポート番号、ユーザー名、パスワードなどのデータベース接続のためのパラメーターがありません。Datasetにデータベース名を指定したとしても、当然のことながらデータベースとの接続を確立することはできません。

To read schemas from a database, add the database format specific reader to the workspace and set its parameters (host name, port number etc.) properly.
And then, for the Schema reader,
- set the "Source Dataset" parameter to the database name,
- set the "Input Format" parameter to the format name explicitly.
After this, the Schema reader will be able to connect to the database, and to read schemas of the tables, since it can refer to configuration of another reader in the same workspace.
データベースからスキーマを読むには、ワークスペースにそのデータベースフォーマット固有のリーダーを追加し、パラメーター(ホスト名、ポート番号など)を適切に設定してください。
そして、Schemaリーダーについては、
- "Source Dataset"パラメーターにデータベース名を設定し,
- "Input Format"パラメーターにフォーマット名を明示的に設定します。
Schemaリーダーは同じワークスペース内の他のリーダーの設定を参照できるので、この後、データベースに接続してテーブルのスキーマを読むことができるようになります。

This is an example of the configuration to read schemas from a PostgreSQL database.
これはPostgreSQLデータベースからスキーマを読み込むための設定例です。

























You can select feature types (tables) with the "Feature Types to Read" parameter of the POSTGRES reater. If you don't need to read data from the database, you can set the "Max Features to Read" parameter to 1 (0 seems to be invalid unfortunately).
フィーチャータイプ(テーブル)は、POSTGREASリーダーの"Feature Types to Read"パラメーターで選択できます。データベースからデータを読む必要がなければ、"Max Features to Read"パラメーターを1に設定できます(残念ながら0は無効なようです)。

I added the POSTGRES reader as a usual reader in the above example, but you can also add it as a Workspace Resource (Menu: Readers > Add Reader as Resource).
This may be better if you don't need to read the actual data.
上の例ではPOSTGRESリーダーは通常のリーダーとして追加しましたが、ワークスペースリソースとして追加することもできます(Menu: Readers > Add Reader as Resource)。
実際のデータを読む必要がない場合は、この方が良いかも知れません。

















In addition, the "referring to another reader configuration" can also be applied to read schemas from a web dataset like the Google Maps Engine.
加えて、この「他のリーダーの設定を参照する」機能は、Google Maps Engineなどのウェブデータセットからスキーマを読み込むのにも応用できます。

FME 2014 SP4 build 14433

Wow, FME 2014 SP5 build 14440 has been released!
おぉ、FME 2014 SP5 build 14440 がリリースされている!
The Safe Software Blog > FME Service Packs: The Dawning of the Age of .0

=====
Scripting is also possible but I would not recommend to use it in a practical workspace.
Just a self-training concerning Python FME Objects API.
スクリプトも可能ですが、実際のワークスペースで使うことはお勧めしません。
Python FME Objects API に関するトレーニングです。
-----
# Script Example for PythonCreator, 2014-12-14 Updated
import fmeobjects
class PostgresSchemaReader(object):
    def close(self):
        directives = [
            'IDLIST', 'table1,table2,table3',
        ]
        parameters = [
            '_HOST', 'localhost',
            '_PORT', '5432',
            '_USER_NAME', 'username',
            '_PASSWORD', 'foobar',
        ]
        reader = fmeobjects.FMEUniversalReader('POSTGRES', False, directives)
        reader.open('test', parameters)
        while True:
            schema = reader.readSchema()
            if schema == None:
                break
            featureTypeName = schema.getFeatureType()
            schema.setAttribute('fme_feature_type_name', featureTypeName)
            for i, name in enumerate(schema.getSequencedAttributeNames()):
                fmeDataType = schema.getAttribute(name)
                nativeDataType = schema.getAttribute('*%s' % name)
                schema.setAttribute('attribute{%d}.name' % i, name)
                schema.setAttribute('attribute{%d}.fme_data_type' % i, fmeDataType)
                schema.setAttribute('attribute{%d}.native_data_type' % i, nativeDataType)
            self.pyoutput(schema)
        reader.close()
-----

2014-12-12

3/2 returns 1 or 1.5?

3/2は1と1.5のどちらを返すか?

The answer is 1, in FME 2014 and earlier.
答は1です。FME 2014以前では。

In almost all the programming languages e.g. C, Python 2.x, Tcl, the / operator returns an integer value if both operands are integers. As well, 3/2 entered with the Arithmetic Editor of FME Workbench has returned 1 for a long time. I think the manner of math operations of FME has been basically inherited from C.
3/2 returns 1, 3.0/2, 3/2.0, or 3.0/2.0 returns 1.5.
C, Python 2.x, Tclなどのほとんど全てのプログラミング言語では、/演算子は、両オペランドが整数のときは整数値を返します。同様に、FMEワークベンチの数式エディタで入力された3/2は、長い間1を返してきました。FMEにおける算術演算の仕方は、基本的にはCから継承されているのだと思います。
3/2は1を返し、3.0/2, 3/2.0 または 3.0/2.0は1.5を返します。
=====
2014-12-17:
Added "2.x" to Python since 3/2 returns 1.5 in Python 3.
See also the comments in this article.
Python 3では、3/2は1.5を返すので、Pythonに「2.x」を追記しました。
この記事についてのコメントも参照してください。
=====

In some cases, the rule "int/int returns int" can be used effectively.
For example, 0-based sequential index of a grid cell can be converted to row, column index with these expressions.
row index = sequential index / number of columns
column index = sequential index % number of columns
あるケースでは、この「整数/整数は整数を返す」というルールは効果的に使えます。
例えば、次の式により、グリッドセルに与えられた0から始まる連番を行, 列のインデクスに変換することができます。
行インデクス = 連番 / 列数
列インデクス = 連番 % 列数













On the other hand, however, it's also a fact that the rule has brought a confusion to many users who expect 3/2 returns 1.5. If you don't know the rule, you will have to consume long time to find the issue in the workspace. I've called it "integer division trap".
しかし一方では、このルールが、3/2が1.5を返すことを期待する多くのユーザーに混乱をもたらしてきたのも事実です。このルールを知らなければ、ワークスペースにおける問題点を見つけるのに長い時間を費やさなければなりません。私はこれを「整数除算のワナ」と呼んできました。

Now, I got an important information that says the rule will be changed. The / operator in FME 2015 will always perform floating point division, even if both operands are integers.
さて、このルールが変更されるという重要な情報を得ました。FME 2015における/演算子は、両オペランドが整数であっても、常に小数点数の除算を行うことになるのです。

Yes. The answer will be 1.5, in FME 2015.
そう。FME 2015では、答は1.5になります。

I was surprised when I heard it from Safe, and it was hard to accept such a radical change, honestly. Although I now understand it's reasonable for many users, I'm afraid of side-effects against existing workspaces.
Safe社からこれを聞いたときは驚き、正直なところ、そのような過激な変更は受け入れ難いものでした。今は多くのユーザーにとって妥当なことであると理解していますが、既存のワークスペースに対する副作用は心配です。

In my quick test, math expressions in existing workspaces created with FME 2014 were performed in the current rule "int/int returns int" when I ran it with FME 2015 Workbench.
It's good.
But, if you once open the expression with the Arithmetic Editor of FME 2015 and closed the transformer parameters dialog with OK button, the rule will be changed to the new one "/ operator always performs floating point division".
We need to be aware it.
簡単なテストをしたところ、FME 2014で作成した既存のワークスペースにおける数式は、FME 2015のワークベンチで実行した場合でも現在のルール「整数/整数は整数を返す」で行われました。
これは良い。
ただ、FME 2015の数式エディタでその式を開き、トランスフォーマーのパラメータ設定ダイアログボックスをOKボタンで閉じると、新しいルール「/演算子は常に小数点数の除算を行う」に変更されます。
これには注意が必要です。

=====
2014-12-18:
As Mark@Safe commented, when you open a math expression containing / operator that was entered with FME 2014 (or earlier) with the Arithmetic Editor in FME 2015, this large warning message will appear.
Safe社マークさんがコメントしたように、FME 2014以前で入力された / 演算子を含む数式を FME 2015 の数式エディタで開いたときは、次のような大きな警告メッセージが表示されます。













However, it seems to appear only when opening the expression with the Arithmetic Editor.
If you modified the expression without using the Arithmetic Editor, or edited other parameter values, and then you close the transformer parameters dialog with [OK] button, the division behavior change will be applied to the transformer without the message.
I hope that such an implicit change will not happen.
Tested with FME 2015 beta build 15230.
しかし、これはその式を数式エディタで開くときだけ表示されるようです。
数式エディタを使わずにその式を修正したり、他のパラメータを編集したりしてからトランスフォーマーのパラメーター設定ダイアログボックスを [OK] ボタンで閉じると、そのトランスフォーマーについて、メッセージなしで除算の動作の変更が適用されます。
そのような暗黙の変更は起こらないことを望みます。
FME 2015 ベータ版 build 15230 でテストしました。
=====

In addition, I believe that the rule in Python and Tcl scripts will not change. Absolutely?
なお、Python、Tclスクリプトにおけるこのルールは変わらないはずです。もちろんですよね?
=====
2014-12-15
...was not "absolutely". The / operator in Python 3 has changed its behavior (3/2 returns 1.5). See also David's comment.
... 「絶対に」ということはありませんでした。Python 3 の / 演算子の動作には変更があります(3/2は1.5を返す)。ディビッドさんのコメントも参照してください。

Note: FME 2015 uses Python 2.7, at least in beta build 15225.
注: FME 2015はPython 2.7を使用しています、少なくともベータ版ビルド15225では。
=====

FME 2014 SP4 build 14433, FME 2015 Beta build 15225

2014-12-06

Regular Expressions and FME String Functions

正規表現とFME文字列関数

From this thread.
このスレッドより。
Community Answers > Stringrepalcer

The requirement can be summarized as:
Split a string consisting of space-separated parts; extract each first four characters from the first and the second parts; concatenate them with a comma delimiter.
要求は次のように要約できます。
空白で区切られたいくつかの部分で構成された文字列を分割し、1番目と2番目の部分からそれぞれ先頭の4文字を抽出し、それらをカンマ区切りで連結する。
e.g. 例えば:
Input: "54960000.0 643600000.63 0.0"
Output: "5496,6436"

As a basic approach, it can be realized with an AttributeSplitter, two SubstringExtractors, and a StringConcatenator.
基本的なアプローチとして、これは一つのAttributeSplitter、二つのSubstringExtractor、一つのStringConcatenatorで実現できます。











Of course this approach is good, but the question is how to do that with one step.
My first inspiration was the StringReplacer with this regular expression.
もちろんこのアプローチで良いのですが、質問は、これを一つのステップで行うにはどうするか、です。
最初のインスピレーションはこの正規表現を使ったStringReplacerでした。
-----
^([0-9]{4})[^\s]*\s+([0-9]{4}).*$
-----

Regular expressions are very flexible. I think that a regular expression can be used in almost all the cases where you need to extract some parts of a character string based on specific patterns.
正規表現は非常に柔軟で、文字列から特定のパターンに該当する部分を抽出したいときには、ほとんど全てのケースで使えると思います。

However, in some cases, the FME String Functions could also be convenient.
In the case above, if the original string is stored by a feature attribute called "coord", this expression written in the Value column of an AttributeCreator returns the required result.
しかし、あるケースでは、FME文字列関数が便利なこともあります。
上記の場合、オリジナルの文字列が"coord"という属性に格納されているならば、AttributeCreatorのValue列に書かれた次の式は必要な結果を返します。
-----
@Left(@Value(coord),4),@Left(@GetWord(@Value(coord),1),4)
-----

@Left function returns first N characters of specified string (N is the number of characters);
@GetWord function returns the Nth part (N is 0-based index) of space-separated parts.
@Left関数は指定された文字列の最初のN文字を返し(Nは文字数)、
@GetWord関数は空白で区切られた第N番目(Nは0から始まるインデクス)の部分を返します。

=====
2014-12-08: Alternatively, @ReplaceRegEx function can also be used.
あるいは、@ReplaceRegEx関数も使えます。
-----
@ReplaceRegEx(@Value(coord),^([0-9]{4})[^\s]*\s+([0-9]{4}).*$,"\1,\2")
=====

The FME String Functions are relatively easy to understand, so I think it's worth to consider using them in many cases.
FME文字列関数は比較的分かり易いので、多くの場合で検討に値すると思います。
FME Workbench Transformers > String Functions

But, more than enough is too much...
ただし、過ぎたるは及ばざるがごとし。。。
FME String Functions


Addition.
If you prefer scripting, assuming "coord" is storing a space-separated strings:
おまけ。
スクリプトを好むならば、"coord"が空白区切りの文字列を格納していると仮定して:

Tcl script examples for the "Value" in an AttributeCreator (enter with the Arithmetic Editor)
AttributeCreator "Value"のためのTclスクリプト例 (Arithmetic Editorで入力)
-----
[regsub {^([0-9]{4})[^\s]*\s+([0-9]{4}).*$} "@Value(coord)" {\1,\2}]
-----
[format {%s,%s} [string range "@Value(coord)" 0 3] [string range [lindex "@Value(coord)" 1] 0 3]]
-----

Script examples for the "Tcl Expression" in a TclCaller
TclCaller "Tcl Expression"のためのスクリプト例
-----
return [regsub {^([0-9]{4})[^\s]*\s+([0-9]{4}).*$} [FME_GetAttribute coord] {\1,\2}]
-----
return [format {%s,%s} [string range [FME_GetAttribute coord] 0 3] [string range [lindex [FME_GetAttribute coord] 1] 0 3]]
-----

Script examples for a PythonCaller
PythonCallerのためのスクリプト例
-----
import re
def processFeature(feature):
    src = feature.getAttribute('coord')
    feature.setAttribute('coord', re.sub(r'^([0-9]{4})[^\s]*\s+([0-9]{4}).*$', r'\1,\2', src))
-----
def processFeature(feature):
    src = feature.getAttribute('coord')
    feature.setAttribute('coord', '%s,%s' % (src[:4], src.split(' ')[1][:4]))
-----
and so on.
などなど。

In fact, the StringReplacer uses the Tcl command "regsub" internally.
実のところ、StringReplacerは内部でTclコマンド"regsub"を使っています。

Déjà vu (already seen) :-)
デジャヴ (^^)
Community Answers > Leading zeros
Poor Cat

=====
2014-12-08: Forgot the JavaScriptCaller transformer.
JavaScriptCallerトランスフォーマーを忘れていました。
-----
var src = fme_get_attribute("coord");
fme_set_attribute("coord", src.replace(/^([0-9]{4})[^\s]*\s+([0-9]{4}).*$/, "$1,$2"));
-----
var src = fme_get_attribute("coord");
fme_set_attribute("coord", src.substr(0, 4) + "," + src.split(' ')[1].substr(0, 4));
-----
hmm... the JavaScriptCaller may be convenient for string operations, although I have never used yet in a practical workspace.
... but Japanese characters written in a script seems not to be interpreted correctly. Unfortunate.
うむむ。実際のワークスペースではまだ一度も使ったことはないけど、JavaScriptCallerは文字列の操作をするのに便利かも知れない。
... しかし、スクリプト中に書かれた日本語文字は正しく解釈されないみたいだ。残念。
=====

FME 2014 SP4 build 14433

2014-11-29

Enumerate 2-Permutations of List Elements

リスト要素の2-順列を列挙する

This is a question on how to enumerate every 2-permutation of elements in a list.
この問題は、リスト要素の全ての2-順列を列挙する方法に関するものです。
Community Answers > permute a list, output to csv

Source = [11, 22, 33]
2-Permutations = [[11, 22], [11, 33], [22, 11], [22, 33], [33, 11], [33, 22]]

I suggested these 3 ways.
次の3つの方法を提案しました。
1. ListExploder+AttributeRenamer+FeatureMerger+ListExploder+Tester
2. ListExploder+InlineQuerier (SQL statement)
3. PythonCaller (Python script)

Here, add one more way.
ここではもうひとつ追加します。
4. TclCaller (Tcl script)+ListExploder








-----
# TclCaller Script Example
proc extractPermutation {} {
    set values {}
    set range {}
    for {set i 0} {[FME_AttributeExists "List{$i}"]} {incr i} {
        lappend values [FME_GetAttribute "List{$i}"]
        lappend range $i
    }
    set index 0
    foreach i $range v1 $values {
        foreach j $range v2 $values {
            if {$i != $j} {
                FME_SetAttribute "_join{$index}.Field1" $v1
                FME_SetAttribute "_join{$index}.Field2" $v2
                incr index
            }
        }
    }
}
-----

My favorite is the SQL approach, and Tcl is also difficult to throw away. But Python could be the most efficient solution in this case.
SQLアプローチが気に入っているしTclも捨てがたいのですが、この場合はPythonが最も効率的かも知れません。
-----
# PythonCaller Script Example
import fmeobjects
class FeatureProcessor(object):
    def input(self, feature):
        L = feature.getAttribute('List{}')
        if L:
            for v1, v2 in [(v1, v2) for i, v1 in enumerate(L) for j, v2 in enumerate(L) if i != j]:
                newFeature = fmeobjects.FMEFeature()
                # If you need to retain the List{} and other attributes,
                # replace the line above with this.
                # newFeature = feature.cloneAttributes()
                newFeature.setAttribute('Field1', v1)
                newFeature.setAttribute('Field2', v2)
                self.pyoutput(newFeature)
-----

FME 2014 SP4 build 14433

=====
2014-12-01: Probably this script works a treat for enumerating general K-Permutations (K is an arbitrary number).
おそらくこのスクリプトは、一般のK-順列をうまく列挙します(Kは任意の数)。
-----
# PythonCaller Script Example
# Enumerates K-Permutations of List Elements.
# Results will be stored by a nested list named "_perm{}.item{}".
# Assume that "K" (number of items in a permutation) will be specified
# by a feature attribute called "_k".
class PermutationEnumerator(object):
    def input(self, feature):
        src = feature.getAttribute('_list{}')
        self.k = int(feature.getAttribute('_k'))
        if self.k <= len(src):
            self.i = 0
            for i in range(len(src)):
                s = src[:]
                t = [s.pop(i)]
                self.enumeratePermutation(feature, s, t, 1)
        else:
            feature.setAttribute('_rejected', 1)
        self.pyoutput(feature)
         
    def enumeratePermutation(self, feature, s, t, depth):
        if self.k <= depth:
            for j in range(self.k):
                feature.setAttribute('_perm{%d}.item{%d}' % (self.i, j), t[j])
            self.i += 1
        else:
            for i in range(len(s)):
                c, d, = s[:], t[:]
                d.append(c.pop(i))
                self.enumeratePermutation(feature, c, d, depth + 1)
-----
=====
2014-12-05: See also the ListSubsetEnumerator transformer in the FME Store.
FME Store の ListSubsetEnumerator トランスフォーマーも参照してください。

2014-11-23

Grouping Geometric Features Not Sharing Attribute

属性を共有しないジオメトリのグループ化

In this thread, I suggested a solution which includes a Python script to group consecutively adjoining triangular polygons. The point of the script is to use "set" skilfully.
このスレッドでは、連続して隣接する三角形ポリゴンをグループ化するために、Pythonスクリプトを含む解決策を提案しました。スクリプトのポイントは"set"(集合)をうまく使うことです。
FME Community Answers > SpatialRelator with 3D data

The logic could be applied to some other similar scenarios.
For example, consider a case where you need to group discrete areas based on their closeness, like this image. i.e. make a group for areas which are close within a certain distance each other.
Assume that the features don't have any attributes which can be used to group them.
そのロジックは他の似たようなシナリオにも応用できそうです。
例えば次の画像のように、離散的な領域を近さに基づいてグループ化する必要がある場合、つまり、相互に一定の距離以内にある領域をひとつのグループにするような場合を考えます。
フィーチャーは、それらをグループ化するのに使える属性は持っていないものとします。

Input: Individual area features
入力: 個々の領域フィーチャー







Required Result: Grouped based on their closeness
求められる結果: 近さに基づいてグループ化








This is a possible way to group them (add group ID attribute).
1) Use a Counter to add area ID (sequential number, e.g. "_area_id") to the input features.
2) Add a NeighborFinder; send the all features to the Candidate port, set the "Maximum Distance" and the "Close Candidate List Name" parameter (e.g. "_close").
3) Send features output from the Matched port and the UnmatchedCandidate port to a PythonCaller with this script; expose the group ID attribute ("_group_id" in this script example).
以下はそれらをグループ化する(グループID属性を与える)ための可能な方法です。
1) Counterを使い、領域ID(連番。例えば"_area_id")を入力フィーチャーに与える。
2) NeighborFinderを追加し、全てのフィーチャーをCandidateポートに送り、"Maximum Distance"パラメーターと"Close Candidate List Name"パラメーター(例えば"_close")を設定する。
3) MatchedポートとUnmuchedCandidateポートから出力されるフィーチャーを次のスクリプトを設定したPythonCallerに送り、グループID属性(このスクリプトの例では"_group_id")を公開する。
-----
# Python Script Example
import fmeobjects

class GroupIdSetter(object):
    def __init__(self):
        self.features = {} # Key: Area ID => Value: Feature Object
        self.groups = {} # Key: Area ID => Value: Set of Area IDs in Group

    def input(self, feature):
        areaId = int(feature.getAttribute('_area_id'))
        self.features[areaId] = feature
        grp = self.groups.setdefault(areaId, set([areaId]))
        neighbors = feature.getAttribute('_close{}._area_id')
        if neighbors:
            for id in [int(s) for s in neighbors]:
                grp |= self.groups.setdefault(id, set([id]))
        for id in grp:
            self.groups[id] = grp
     
    def close(self):
        groupId, finished = 0, set([])
        for id, grp in self.groups.items():
            if id not in finished:
                for areaId in grp:
                    feature = self.features[areaId]
                    feature.setAttribute('_group_id', groupId)
                    self.pyoutput(feature)
                groupId += 1
                finished |= grp
=====
2014-11-26: The "close" method might be a little more efficient if written like this.
"close"メソッドは次のようにも書いた方がもう少し効率的かも知れない。
-----
    def close(self):
        groupId = 0
        for grp in self.groups.values():
            if grp:
                while grp:
                    feature = self.features[grp.pop()]
                    feature.setAttribute('_group_id', groupId)
                    self.pyoutput(feature)
                groupId += 1
=====

But there is also another approach which does not use any script in this example case..
もっとも、この例の場合にはスクリプトを使わないアプローチもあるのですが。









... yup, found out the solution at last, in which I've used the buffering trick.
... お、やっと見つけました。バッファリングを使ったソリューション。
FME Community Answers > Aggregating polygons near to one another

=====
2014-11-27: I published a custom transformer called "ListCombiner" in the FME Store, which can be applied to the scenario example above. This screenshot illustrates a usage example.
上のシナリオ例に適用できる"ListCombiner"というカスタムトランスフォーマーをFME Storeで公開しました。このスクリーンショットは使用例を示しています。

2014-11-15

Sorting Lines Divided by PointOnLineOverlayer

PointOnLineOverlayerによって分割されたラインの並べ替え

The PointOnLineOverlayer can be used to divide a line at one or more points located onto the line. But the output order of the resultant lines is arbitrary, they will not be sorted along the original line in the direction to the end node from the start node.
The outputting order may not be important in many cases, but there could also be some cases where sorting is required.
PointOnLineOverlayerは、ライン上にあるひとつ以上のポイントによってそのラインを分割するのに使えます。しかし、作成されたラインの出力順は任意であり、元のラインの始点から終点の方向の順になるとは限りません。
多くの場合、出力順は重要ではないかも知れませんが、並べ替えが必要になる場合もあり得ます。

























If the original line is always pointing to east (max X) from west (min X) like the image, divided lines can be sorted by the X coordinate of the start node. In general cases, similarly, they can be sorted by measure values of their start nodes.
1) Insert a MeasureGenerator before the PointOnLineOverlayer.
2) Add a MeasureExtractor to extract measure value of the start node for each resulting line.
Type: Individual Vertex by Index
Destination Attribute of Point or Vertex: _point_measure
Index: 0
3) Add a Sorter to sort the lines by "_point_measure" ascending.
Naturally, use the original line ID attribute as the primary sort key if there are multiple input lines.
図のように、元のラインが常に西(X最小)から東(X最大)の方向を向いているならば、分割されたラインは始点のX座標によって並べ替えることができます。同様に、一般的にはそれらの始点のメジャー値によって並べ替えられます。
1) MeasureGeneratorをPointOnLineOverlayerの前に挿入する。
2) MeasureExtractorを追加し、結果として得られた各ライン始点のメジャー値を抽出する。
Type: Individual Vertex by Index
Destination Attribute of Point or Vertex: _point_measure
Index: 0
3) Sorterを追加し、"_point_measure"の昇順によってラインを並べ替える。
入力ラインが複数ある場合は、当然、元のラインのID属性を第一ソートキーとして使用します。

























Another approach.
Once transform the divided lines into a path (a line consisting of one or more segment lines) using a LineJoiner, and then split again the path into individual segment lines with the PathSplitter. LineJointer parameter setting is:
Preserve Original Orientation: yes
Input Feature Topology: End noded
Preserve Lines as Path Segments: yes
別のアプローチ。
LineJoinerを使って、一旦、分割されたラインをひとつのパス(ひとつ以上のセグメントラインで構成されるライン)に変換し、PathSplitterによって再度それを個々のセグメントラインに分割します。LineJoinerのパラメーター設定は次のとおり。
Preserve Original Orientation: yes
Input Feature Topology: End noded
Preserve Lines as Path Segments: yes

Notes for the another approach:
1) Since the LineJoiner removes all the attributes except ones that were specified to the "Group By" parameter, you will have to restore the attributes afterward if necessary (FeatureMerger etc.).
-----
I've overlooked a way to retain attributes when using the LineJoiner.
- Specify a list name to the "List Name" parameter of the LineJoiner.
- Add a ListIndexcer to demote attributes of the list element.
- Remove the list by an AttributeRemover if you don't use it any more.
a little bit troublesome...
-----
2) In this case, the PathBuilder cannot be used to transform the divided lines into a path, since the order of segments output from the PointOnLineOverlayer is not preferable.
3) If an input line could be a path, it will have to be transformed into a non-path line beforehand. Because the PointOnLineOverlayer preserves path segment boundaries and the PathSplitter will split the path at segment boundaries including the original ones.
別のアプローチに関する注:
1) LineJoinerは"Group By"パラメーターに設定したもの以外の全ての属性を削除するので、必要に応じて後でそれを復元しなければなりません(FeatureMergerなど)。
-----
LineJoinerを使う際に属性を維持する方法を見落としていました。
- LineJoinerの"List Name"パラメーターにリスト名を指定する。
- ListIndexcerを追加してリストの要素を通常の属性に変換する。
- それ以降リストが必要なければ、AttributeRemoverによって削除する。
ちょっと面倒な。。。
-----
2) この場合、PointOnLineOverlayerから出力されるセグメントの順番が望ましいものではないので、PathBuilderは分割されたラインをパスに変換するのに使用できません。
3) 入力ラインがパスである可能性がある場合は、それは、事前にパスではないラインに変換する必要があります。PointOnLineOverlayerはパスのセグメント境界を維持し、PathSplitterはオリジナルを含むセグメント境界によってパスを分割するからです。

Oops, looks like there isn't a simple way to transform a path to a non-path line.
The PathSplitter+LineJoiner does that, but it requires an extra procedure to restore attributes.
Use Python?
おっと、パスを非パスラインに変換する簡単な方法はないみたいだ。
PathSplitter+LineJoinerはそれをしますが、属性を復元するための余分な手続きを必要とします。
Pythonを使う?
-----
# Script Example for PythonCaller
# Converts Path to Line.
import fmeobjects
def convertToLine(feature):
    geom = feature.getGeometry()
    if isinstance(geom, fmeobjects.FMEPath):
        feature.setGeometry(fmeobjects.FMEGeometryTools().convertToLine(geom))
-----

FME 2014 SP4 build 14433

2014-11-08

Create Trapezoidal Buffered Areas from Line

ラインから台形状のバッファーエリアを作成

Continued from this thread.
このスレッドからの続き。
FME Community Answers > Is it possible to create a " trapezoids" shaped line buffer?

It's an interesting subject.
I tried replacing the procedure except the Dissolver with a concise Python script.
For what it's worth.
興味深い題材です。
Dissolver以外の処理手順を簡潔なPythonスクリプトに置き換えてみました。
何かの役に立つかも。
-----
# Script Example for PythonCaller
# Creates Trapezoid Buffered Areas for Each Line Segment.
# Assumes input feature geometry is a Line.
import fmeobjects, math

class TrapezoidBufferer(object):
    def input(self, feature):
        rb = float(feature.getAttribute('_beg_width')) * 0.5
        re = float(feature.getAttribute('_end_width')) * 0.5
        k = (re - rb) / float(feature.performFunction('@Length()'))
        coordSys = feature.getCoordSys()    
        coords = feature.getAllCoordinates()
        if feature.getDimension() == fmeobjects.FME_THREE_D:
            coords = [(x, y) for (x, y, z) in coords]          
        x0, y0, measure = coords[0][0], coords[0][1], 0.0
        g0 = fmeobjects.FMEEllipse(fmeobjects.FMEPoint(x0, y0), rb, rb, 0.0, 0)
        for x1, y1 in coords[1:]:
            measure += math.hypot(x1 - x0, y1 - y0)
            rm = rb + k * measure
            g1 = fmeobjects.FMEEllipse(fmeobjects.FMEPoint(x1, y1), rm, rm, 0.0, 0)
            geom = fmeobjects.FMEAggregate()
            geom.appendPart(g0)
            geom.appendPart(g1)
            hull = feature.cloneAttributes()
            hull.setCoordSys(coordSys)
            hull.setGeometry(geom)
            hull.performFunction('@ConvexHull()')
            self.pyoutput(hull)
            x0, y0, g0 = x1, y1, g1
-----

The script transforms a polyline into multiple polygons like this image. To get final geometry, dissolve them with a Dissolver.
このスクリプトはひとつのポリラインを図のような複数のポリゴンに変換します。最終的なジオメトリを得るには、これらをDissolverでディゾルブします。










FME 2014 SP4 build 14433

=====
Really interesting :)
実におもしろい (^^)










=====
2014-11-10: I didn't think that "dissolving" can be performed with the Python FME Objects API. Probably that's true but I noticed that the required result can be generated by "buffering with specifying 0 as amount" against an aggregate geometry, in this case.
「ディゾルブ(dissolving)」は Python FME Objects API でできるとは思っていませんでした。おそらくそれはその通りでしょうが、このケースでは、集約ジオメトリ(aggregate)に対する「バッファ量0のバッファリング(buffering)」によって求められる結果が得られることに気がつきました。
-----
# Script Example for PythonCaller
# Creates a Tapered Buffer Area for a Line.
# Assumes input feature geometry is a Line.
import fmeobjects, math

class TaperLineBufferer(object):
    def input(self, feature):
        rb = float(feature.getAttribute('_beg_width')) * 0.5
        re = float(feature.getAttribute('_end_width')) * 0.5
        k = (re - rb) / float(feature.performFunction('@Length()'))
        coords = feature.getAllCoordinates()
        hullCollection = fmeobjects.FMEAggregate()
        x0, y0, measure = coords[0][0], coords[0][1], 0.0
        g0 = fmeobjects.FMEEllipse(fmeobjects.FMEPoint(x0, y0), rb, rb, 0.0, 0)
        for coord in coords[1:]:
            x1, y1 = coord[0], coord[1]
            measure += math.hypot(x1 - x0, y1 - y0)
            rm = rb + k * measure
            g1 = fmeobjects.FMEEllipse(fmeobjects.FMEPoint(x1, y1), rm, rm, 0.0, 0)
            geom = fmeobjects.FMEAggregate()
            geom.appendPart(g0)
            geom.appendPart(g1)
            hull = fmeobjects.FMEFeature()
            hull.setGeometry(geom)
            hull.performFunction('@ConvexHull()')
            hullCollection.appendPart(hull.getGeometry())
            x0, y0, g0 = x1, y1, g1
        feature.setGeometry(hullCollection)
        feature.buffer(0.0, 0.0) # dissolve parts
        self.pyoutput(feature)
-----
Like a strange living thing...
ヘンな生き物のような...






I published a custom transformer named "TaperLineBufferer" in the FME Store, which  has been implemented based on the Python script.
このPythonスクリプトに基づいて実装した"TaperLineBufferer"という名前のカスタムトランスフォーマーを FME Store で公開しました。

2014-11-02

Split String based on Byte Numbers in Specific Encoding

特定のエンコーディングにおけるバイト数による文字列の分割

FME treats the number of characters as the length of a character string, will not consider the byte number of multi-byte characters which are used in Japanese locale. I believe that it's reasonable and convenient in almost all the cases, since you don't need to think of difference in the number of bytes among different encoding.
FMEは文字数を文字列の長さとして扱い、日本語のロケールで使用されるマルチバイト文字のバイト数を考慮しません。異なるエンコーディングにおけるバイト数の違いを考えなくて良いので、ほとんどの場合これは合理的でかつ便利だと思います。

However, I sometimes need to split a string based on byte numbers counted in a specific encoding when reading datasets in some legacy formats that determine column widths in a record based on byte numbers. Not only Japanese, but also Korean and Chinese users may encounter a similar situation.
しかし時には、レコードの各列幅をバイト数で規定しているレガシーなフォーマットのデータセットを読むような場合に、特定のエンコーディングでのバイト数に基づいて文字列を分割する必要があります。日本だけでなく、韓国や中国のユーザーも似たような状況に遭遇するかも知れません。

The Column Aligned Text (CAT) reader can be used to read a dataset in fixed-length columns format. But it seems to always consider one character as one byte, so it's useless for a dataset that could contain multi-byte characters, unfortunately.
How can you split a string based on byte number of each column?
Column Aligned Text (CAT) リーダーは、固定列幅のフォーマットのデータセットを読むのに使えますが、これは常に1文字を1バイトとみなすようなので、残念ながらマルチバイト文字を含む可能性があるデータセットには使えません。
各列のバイト数に基づいて文字列を分割するにはどうするか?

=====
2014-11-03: The above description about the CAT reader is not exact. If there were a line consisting of only ASCII characters in the source text file, the proper field boundaries could be set to the CAT reader in the parameters dialog, and the reader could read the data as expected. Otherwise, the field boundaries cannot be set properly to the reader. That's a limitation of the current CAT reader for reading data including multi-byte characters.
CATリーダーについての上記の説明は正確ではありません。ソーステキストファイルにASCII文字のみで構成されている行があれば、パラメーター設定ダイアログボックスでCATリーダーに列の境界を適切に設定することができ、また、リーダーはデータを期待どおりに読み込むことができますが、そうでない場合に、列の境界をリーダーに正しく設定することができません。それがマルチバイト文字を含むデータ読込における現在のCATリーダーの制約です。
=====

Tcl scripting is one of quick ways. For example, a TclCaller with this script splits a string into tokens of 4 bytes and 6 bytes counted in cp932 encoding. If the "src" holds "あいうえお", the new attributes "col0" and "col1" will store "あい" and "うえお", since a Japanese character is represented by 2 bytes in cp932. cp932 is the default encoding of Japanese Windows.
Tclスクリプトは簡単な方法のひとつです。例えば、次のスクリプトを設定したTclCallerは、ある文字列を、cp932エンコーディングでの4バイトと6バイトのトークンに分割します。日本語の文字はcp932では2バイトで表現されるので、"src" が「あいうえお」を保持していれば、新しい属性 "col0" と "col1" はそれぞれ、「あい」と「うえお」を格納することになります。cp932は日本語版Windowsのデフォルトのエンコーディングです。
-----
proc byteSplit {} {
    set src [encoding convertto cp932 [FME_GetAttribute "src"]]
    FME_SetAttribute "col0" [encoding convertfrom cp932 [string range $src 0 3]]
    FME_SetAttribute "col1" [encoding convertfrom cp932 [string range $src 4 9]]
}
-----

Based on this idea, I created a custom transformer named MbStringByteSplitter and published it in FME Store. Find it in this folder.
Transformer Gallery / FME Store / Pragmatica
これを基本にして MbStringByteSplitter というカスタムトランスフォーマーを作成し、FME Storeで公開しました。このフォルダ内を見てください。
Transformer Galley / FME Store / Pragmatica

I think the transformer works fine in any locales theoretically, but I cannot test in locales other than Japanese. If you find some issues in your locale, please let me know it.
理屈ではどんなロケールでも動作すると思いますが、日本語以外のロケールでテストすることはできません。もし何か問題を見つけたときはお知らせください。

FME 2014 SP4 build 14433

=====
2014-11-07: I thought the string splitting by byte count was a special requirement for a few specific cultures such as Japanese, Korean, and Chinese. So I've never asked any improvement on this issue to Safe (There are many things that have to be improved before this!).
However, surprisingly, this article got many accesses from around the world including Europe and America in a few days. Perhaps there are similar requirements in alphabetic cultures - Latin, Cyrillic, Greece, Arabic, Hebrew etc.?
If so, the CAT reader (Field Boundaries setting) will have to be improved as soon as possible, and also adding some new Transformers that process a character string based on byte count in a specified encoding might be helpful to many users.
How do you feel?
バイト数による文字列分割は日本語、韓国語、中国語のような特定の文化における特殊な要求だと思っていたので、この件についてSafe社に何らかの改良を依頼したことはありません(それよりも前に改善されるべきことがたくさんある!)。
しかし意外なことに、この記事には欧米を含む世界中から数日で多くのアクセスがありました。もしかしてラテン、キリル、ギリシャ、アラビア、ヘブライなどのアルファベット文化圏でも似たような要求があるのでしょうか?
もしそうならば、CATリーダー(Field Boundaries 設定)はできるだけ早く改良されるべきだし、指定したエンコーディングでのバイト数によって文字列を処理する新しいトランスフォーマーを追加することは多くのユーザーにとって有用かも知れません。
どう思います?

2014-10-16

Found Dynamic Workflow Tutorial !

ダイナミック・ワークフロー チュートリアル発見!

Although the Dynamic Schema is a frequently used functionality in practical workspaces, I had felt that technical materials about that were too few.
ダイナミック・スキーマは実用的なワークスペースでしばしば使われる機能ですが、それに関する技術資料が少なすぎると感じていました。

However, when I was surfing on the Knowledge Base today, by chance I found that the related documentations have been updated and enhanced wonderfully in this summer.
しかし今日、ナレッジ・ベースをながめていた時にたまたま、この夏に関連のドキュメントが更新され、素晴らしく強化されていることを見つけました。

I would recommend you to start to learn about the Dynamic Schema from this page.
ダイナミック・スキーマについて次のページから学びはじめることをお勧めします。
> Dynamic Workflow Tutorial: Introduction

2014-10-13

010 < 9 is True or False?

010 < 9 は真か偽か?

The answer depends on the context.
In some cases, a string which consists of digits beginning with zero will be interpreted as an octal number (010 is equal to 8 in decimal number). But there also be cases where a digit string will be always interpreted as a decimal number, i.e. leading zero(s) will be ignored.
その答はコンテキストに依存します。
ある場合は、数字からなる文字列で先頭がゼロのものは八進数として解釈されますが(010は十進数の8に等しい)、常に十進数として解釈される(つまり、先頭部のゼロが無視される)場合もあります。

=====
2014-12-09: I heard that the numeric interpretation will be changed in FME 2015. That is, new transformer instances in FME 2015 always attempts to interpret a numeric value as a decimal number even if its representation begins with zero.
Note that this article is true for FME 2014 and earlier, but will not be true for FME 2015+.
2014-12-09: FME 2015では数値の解釈に変更があると聞きました。つまり、FME 2015の新しいトランスフォーマーインスタンスは、ゼロから始まっていたとしても常に十進数として解釈しようとします。
この記事はFME 2014以前には当てはまりますが、FME 2015以降には当てはまらないことに注意してください。
=====

Arithmetic Expressions 数式
In an arithmetic expression that is evaluated by @Evaluate function, "010" will be interpreted as an octal number; the value is treated as 8 in decimal number. But if the representation is "010.0" - having decimal fraction, the value is treated as 10.0 in decimal number even though it begins with zero(s).
@Evaluate関数で評価される数式においては、"010"は八進数として解釈され、十進数での値8として扱われます。ただし、表現が"010.0"である(小数部がある)場合は、ゼロから始まっていたとしても十進数での値10.0として扱われます。

Check resultant values generated by the AttributeCreator with the following settings.
- n1 will store "010" (the representation was assigned as-is),
- n2 will store "8" (evaluated as an octal number),
- n3 will store "10.0" (evaluated as a decimal number), after the processing.
次の設定で AttributeCreator が生成する結果を確認してください。処理後、
- n1 は"010"(文字列表現がそのまま割り当てられた)、
- n2 は"8"(八進数として評価された)、
- n3 は"10.0"(十進数として評価された)を格納することになります。













Comparison in Test Clauses テスト句における比較
In a test clause (Automatic or Numeric Mode), the transformer attempts to interpret a string as a decimal number; ignores leading zero(s). For example, if an attribute named "src" contains "010", the Tester with this setting routes the feature to the Failed port (10 < 9 is false).
テスト句(AutomaticまたはNumericモード)では、トランスフォーマーは文字列を十進数として解釈しようとし、先頭部のゼロを無視します。例えば、"src"という名前の属性が"010"を格納している場合、次の設定をした Tester は、フィーチャーを Failed ポートに送ります (10 < 9 は偽)。













However, if the Left Value was an arithmetic expression which evaluate the attribute value, the feature goes to the Passed port, since "010" will be evaluated as an octal number (8 < 9 is true).
しかし、Left Value(左辺値)が属性値を評価する数式である場合は、"010"は八進数として評価されるため、フィーチャーは Passed ポートから出力されます (8 < 9 は真)。













Sorter transformer  Sorter トランスフォーマー
The Sorter (Numeric mode) seems to always consider a numeric representation as a decimal number. This is a sorted result by numeric ascending.
Sorter(Numericモード)は、数値表現を常に十進数と見なすようです。これは数値の昇順でソートした結果です。










I sometimes come across a case where code numbers beginning with 0 should be treated as numeric values. In such a case, be aware that there are differences on the interpretation depending on the context.
ときどき、0から始まるコード番号を数値として扱わなければならないような場合がありますが、そのような場合、コンテキストに応じて値の解釈が異なることに注意しましょう。

2014-10-12

Cases where Null and Missing are considered as Empty String

Null(ナル値)、Missing(属性の欠落)が Empty String(空文字列)と見なされるケース

As you know, Null has been introduced in FME 2014. It has brought some big benefits to us.
In FME 2014+, many transformers can distinguish Null, Missing, and Empty String strictly.
On the other hand, there also are several cases where Null and Missing are considered as Empty String.
ご存じの通りFME 2014ではNull(ナル値)が導入され、大いに役立っています。
FME 2014以降では、多くのトランスフォーマーはNull, Missing(属性の欠落)及びEmpty String(空文字列) を厳密に区別することができますが、一方では、Null, Missingが空文字列とみなされるケースもあります。

A typical case is the = operator in a test condition setting for a transformer.
For example, the Tester with this setting sends a feature which stores Null as "attr" or doesn't have the "attr", to the Passed port.
It doesn't distinguish both Null and Missing from Empty String.
テスト条件における = 演算子は典型的なケースです。
例えば、次のような設定をしたTesterは、属性"attr"にNullを保持する、あるいは、"attr"を持たないフィーチャーをPassedポートから出力します。つまり、NullやMissingを空文字列と区別しません。
=====
2014-10-14: Some users may feel this behavior strange, but I think it's reasonable as a general manner in FME processing. Dale@Safe added some explanations about the basic concept and the background. See also his comment.
この振る舞いを奇妙に思う方もいらっしゃるかも知れませんが、私は、FMEの処理における一般的な方法として合理的であると思います。Safe社のデールさんが基本的な考え方と背景について説明を加えてくださいました。彼のコメントも参照してください。
=====














If you need to test whether the attribute value is Null or the attribute is Missing, you should use "Attribute Is Null" and "Attribute Is Missing" operators, like this.
属性値がNullであるかどうか、あるいは、属性が欠落しているかどうかを調べる必要がある場合は、次のように Attribute Is Null 演算子や Attribute Is Missing 演算子を使う必要があります。














As well, the Group By parameter does not distinguish them.
If you set an attribute to Group By parameter of a transformer, the transformer will configure one group for every feature which holds Null or Empty String as the attribute, or doesn't have the attribute (i.e. Missing).
There may be some users who feel such a behavior strange. However, it seems to be related to the fundamental mechanism regarding parameters, so I don't think it will change in the future.
* See also the following Note (2014-12-09).
同様に、Group Byパラメーターにおいてもそれらは区別されません。
Group Byパラメーターにある属性が設定されたトランスフォーマーは、その属性にナル値または空文字列を格納する、あるいは、その属性を持たない全てのフィーチャーをひとつのグループとして取り扱います。この振る舞いを奇妙に感じるユーザーもいるかも知れませんが、これはパラメーターの基本的なメカニズムに関係しているようであり、今後もこれが変わることはないと思います。
* 次の注(2014-12-09)もご覧ください。
=====
Note (2014-12-09): Got a great information. I heard that Safe is going to improve the behavior of Group By concerning Null, Missing, and Empty. Now, I update the last paragraph above to:
"Although it seems to be related to the fundamental mechanism regarding parameters, it might be improved in the future."
注 (2014-12-09): 素晴らしい情報を得ました。Safe社は、Null, Missing, 空文字列に関するGroup Byの振る舞いについて改良する予定であるとのことです。上の最後の段落を次のように更新します。
これはパラメーターの基本的なメカニズムに関係しているようですが、将来は改良される可能性があります。
=====

Well, how can you make groups separately for Null, Missing, and Empty String?
A possible way I can think of is to assign special values to identify "Null" and "Missing" with the NullAttributeMapper before grouping.
Of course the special values must not be equal to any other possible values for the attribute, but it's not so difficult in many cases, I think.
では、Null、Missing、Empty Stringを区別してグループ化するにはどうすれば良いでしょう?
考えられる方法は、グループ化する前に NullAttributeMapper を使ってNull, Missingを識別するための特別な値を割り当てることです。もちろんその特別な値は、その属性の値として可能な全ての他の値と異ならなければなりませんが、多くの場合、それはそんなに難しいことではないと思います。

Anyway, we need to know that there are cases where Null and Missing are considered as Empty String.
ともあれ、Null, Missing が空文字列として扱われるケースがあることは知っておく必要があります。

2014-10-08

Recent New Transformers for FME Store

FME Store における新作トランスフォーマー

Naturally, all is free to use. If you found an issue in these, please contact me.
もちろん全て無料で自由に使えます。何か問題を見つけたときはお知らせください。
Transformer Gallery / FME Store / Pragmatica
Requirement: FME 2014 SP3+

2DVectorCalculator
Calculates (x, y) components of a 2D vector defined by the parameter settings, and stores them as new attributes. Optionally this transformer replaces feature geometry with a line segment representing the resulting vector.
Since it can also calculate a normal vector, may be useful to create a perpendicular line against the original geometry.
The core processing is implemented with Tcl.
パラメータによって定義される二次元ベクトルのx成分とy成分を求め、新たな属性に格納します。また、オプションとして、フィーチャーのジオメトリを、求められたベクトルを表す線分と置き換えます。法線ベクトルも求められるので、元のジオメトリに対する垂線を作成するのに便利かも知れません。
主要な処理は Tcl で実装されています。

RankCalculator
Adds rank number to each feature as an attribute; the rank will be determined based on the descending or ascending order of numeric values stored by an attribute.
If every value is unique, the resulting ranks will be same as the count numbers which can be generated with a combination of Sorter and Counter. Otherwise - i.e. if two or more input features have same value, their ranks will become identical number.
The core processing is implemented with Python.
各フィーチャーに属性としてランク(ある属性に格納されている値の降順または昇順での順位)を与えます。全ての値が一意であれば、ランクは、Sorter と Counter の組み合わせで生成できるカウント値と同じですが、そうでない場合、つまり複数のフィーチャーが同じ値を持っている場合に、それらのランク(順位)は同じになります。
主要な処理は Python で実装されています。

StepSampler
Selects features by an identical interval from the incoming feature sequence.
This transformer is similar to the regular Sampler (Sampling Type: Every Nth Feature), but determines the sampling interval automatically based on the specified number of samples and the number of entire input features.
The core processing is implemented with Python.
入力フィーチャーのシーケンスから一定の間隔でフィーチャーを選択します。
このトランスフォーマーは標準の Sampler (Sampling Type: Every Nth Feature) と似ていますが、ランプリングの間隔は、指定したサンプル数と入力フィーチャー数に基づいて自動的に決定されます。
主要な処理はPythonで実装されています。

2014-10-02

ZIP Archive and Dataset Fanout

ZIPアーカイブと Dataset Fanout (データセット・ファンアウト)

FME 2014 SP3 build 14391

There are many files in a directory; the files are grouped by the prefix of those names.
たくさんのファイルがひとつのディレクトリ内にあり、それらはファイル名のプレフィクスによってグループ分けされています。
-----
a_01.ext, a_02.ext, a_03.ext, ...
b_01.ext, b_02.ext, b_03.ext, ...
c_01.ext, c_02.ext, c_03.ext, ...
and so on.

I needed to archive them with zip-compression for each group. The required output is:
それらを次のように、グループごとにzip圧縮、アーカイブする必要がありました。
-----
a.zip, b.zip, c.zip ...

At first, I thought it can be realized easily with the Directory and File Pathnames reader [PATH], some Transformers, and the File Copy writer [FILECOPY] with Dataset Fanout, like this.
はじめは、次のように Directory and File Pathnames リーダー [PATH]、いくつかのトランスフォーマー、および、データセット・ファンアウトを使用した File Copy ライター [FILCOPY] によって簡単に実現できるだろうと考えました。













The workspace was able to compress the source files, but didn't fanout. Just one zip file which has archived every file was created as the result. This may be a limitation of the current FME.
このワークスペースはソースファイルを圧縮することはできましたが、ファンアウトはしませんでした。全てのファイルをアーカイブしたひとつのzipファイルが作成されただけです。これはおそらく、現在のFMEにおける制約でしょう。
=====
2014-10-11: I found this description in FME Workbench documentation.
"Note: You cannot create multiple zip files using Dataset Fanout (therefore, you cannot set the Fanout Suffix parameter to .zip)."
Yes, that's indeed a limitation of the current FME.
FME Woekbench のドキュメントで次のような説明をみつけました。
「注: データセット・ファンアウトを使用して複数のzipファイルを作成することはできません(したがって Fanout Suffix パラメーターに .zip を設定することはできません)」
そうです。確かに現在のFMEにおける制約でした。
=====
2015-11-24: FME 2016.0 beta build 16133
In FME 2016, the ability to create multiple zip files will be added to the Dataset Fanout. See here.
FME 2016 では、データセットファンアウトに複数の zip ファイルを作成する機能が追加されます。ここを参照してください。
=====

The first approach failed, unfortunately.
But don't give up. The WorkspaceRunner would be a workaround in the interim.
最初のアプローチは残念ながら失敗でした。
しかし、あきらめることはありません。WorkspaceRunner が当面の回避策になります。

The main workspace is simple. It just copies every file to a directory from the source directory. The only point is that it publishes the "Path Filter" parameter of the PATH reader.
メインワークスペースは単純で、全てのファイルをソースディレクトリから他のディレクトリにコピーするだけです。唯一のポイントは、PATH リーダーの"Path Filter"パラメーターを公開していることです。









Then, create another workspace with a WorkspaceRunner to run the main workspace for each prefix. The data flow contains these transformers.
1) AttributeSplitter, AttributeRenamer: extract prefix from every file name, store it as attribute named "_prefix". The renaming is not essential since "_list{0}" can be used instead of "_prefix".
2) DuplicateRemover: remove duplicate prefixes.
3) StringConcatenator: create a preferable zip file path string based on the specified output directory path and the extracted prefix. i.e. "<directory path>\<prefix>.zip"
そして、そのメインワークスペースをプレフィクスごとに実行するために、WorkspaceRunner を含むもうひとつのワークスペースを作成します。データフローには以下のトランスフォーマーが含まれます。
1) AttributeSplitter, AttributeRenamer: 全てのファイル名からプレフィクスを抽出し、"_prefix"という属性に格納します。"_list{0}"が"_prefix"の代わりに使えるので属性名の変更は必須ではありません。
2) DuplicateRemover: 重複したプレフィクスを削除します。
3) StringConcatenator: 出力先のディレクトリパスと抽出されたプレフィクスに基づいて、適切なzipファイルパス"<directory path>\<prefix>.zip"を作成します。






Finally, pass the source directory path, a path filter (i.e. "<prefix>_*"), and the zip file path to the main workspace through the WorkspaceRunner.
最後に、WorkspaceRunner 経由でソースディレクトリのパス、パス・フィルター("<prefix>_*")、および zipファイルパスをメインワークスペースに渡します。














It worked fine as expected. But I believe that the first approach - the Dataset Fanout will be available in the near future.
これは期待した通りに動作しました。しかし、最初のアプローチ - データセット・ファンアウトも、近い将来には利用可能になると思います。
=====
2015-11-24: FME 2016.0 beta build 16133
In FME 2016, you can create multiple zip files using Dataset Fanout. See here.
FME 2016 では、データセットファンアウトで複数の zip ファイルが作成できます。ここを参照してください。
=====

2014-09-28

Define New Transformer with Tcl Commands

This message appears in the splash window of FME Workbench: "Loading Transformers..."







FME Transformers are defined in "*.fmx" files; the Workbench is loading the definitions from the files saved in these folders.
<FME HOME>\transformes
C:\Users\<username>\Documents\FME\Transformers
=====
2014-10-12: And also here, if there are transformers downloaded from the FME Store.
C:\Users\<username>\AppData\Roaming\Safe Software\FME\FME Store\Transformers
=====

If you define a transformer with the correct syntax and save it to one of these folders as "*.fmx" file, it will be added to the Transformer Gallery after restarting Workbench.
=====
Alternatively, click the "refresh" button of Transformer Gallery to reload transformer definitions. It was pointed out by Dale@Safe. See also his comment.
=====
There are several ways to define a transformer. Although I don't know everything, I recently learned about a way to define a feature-based transformer using Tcl commands.
A Tcl-based transformer can be defined with a text editor such as Notepad.

Example: AttributeMultiplier Version 0

Download "AttributeMultiplier_v0.fmx", save it to one of the folders mentioned above, and restart Workbench (or click the "refresh" button of Transformer Gallery). Then the transformer will be added to the Transformer Gallery.
This transformer multiplies every specified attribute by a value that you specified to the "Multiplier" parameter. If the original attribute value is non-numeric, this transformer does nothing against the attribute. i.e. leaves the original value.












Open the fmx file with a text editor to see the definition. And see these documentations to learn more about the syntax.
Transformer Definition Documentation
FME GUI Type Documentation
TclCaller (FME Tcl Variables and Functions)

You can also see the definitions of existing transformers in this file as good examples.
But be careful not to destroy it. I cannot support you to restore the file ;)
<FME HOME>\transformes\fmesuite.fmx

Example: AttributeMultiplier Version 1

Upgrade is easy. Just create new definition with new version number which is greater than the previous one. You can write the new definition in either the same file or another new file.
=====
2014-09-29: I found that two same transformer names appear in the Transformer Gallery when two files exist for two versions simultaneously, although both of them refer to the new version. So, it seems better to insert the new definition before the old definition in the same file if you need to retain the old version after upgrading.
=====
The version 1 example is here: "AttributeMultiplier_v1.fmx"
In the version 1, you can choose one of these actions which should be performed against non-numeric attributes.
- Do nothing
- Replace with a value that you specified to the "New Value" parameter
- Replace with an empty string
- Replace with <null>

Interesting?
To uninstall the transformer, just remove the fmx file.

FME 2014 SP3 build 14391

P.S. I wanted to create a transformer that will hide specified attributes/lists. The functionality should be same as "Attributes to Hide" and "Lists to Hide" in the PythonCaller. Lists were able to be hidden, but I have not achieved a way to hide attributes yet. It seems not to be easy...

2014-09-26

FME 2015: Advanced Text Editor Improvement for International Characters

The representation of a Japanese word usually consists of "Kanji" and/or "Hiragana" characters.
A "Kanji" represents a meaning of something. Its pronunciation consists of one or more syllables. Many "Kanji" have two or more different pronunciations depending on the context. And also, there can be several different "Kanji" for the same pronunciation.
A "Hiragana" basically represents a syllable. e.g. a, i, u, e, o, ka, ki, ku, ke, ko.
In fact, there is another character type called "Katakana" for representing syllables, but I don't mention about it here.
You feel so troublesome? But I have to use mixing the Japanese character set and the ASCII characters also in FME workspaces.

In a general text editor such as Notepad, you can enter a Japanese word with these steps in Japanese input mode.
1. Enter the word with English characters according to the pronunciation. The characters will be converted to "Hiragana" automatically on the screen, one by one syllable.
2. After entering entire syllables of the word, push the Space key to convert it to a candidate representation including "Kanji".
3. If the candidate is the preferable one, push the Enter key to decide the input.
4. If not, push the Space key again to display a list of candidate representations, and select the preferable one.









In all the parameter fields of FME Workbench, you can enter Japanese words with the same way above.

FME 2014 and earlier
However, the Advanced Text Editor in FME 2014 (and earlier) does not display characters while entering them in the step 1 and 2. Although the list of candidates will appear after entering entire syllables and pushing the Space key (step 4), it's too inconvenient since there could be a typo while entering, and it causes mis-conversions easily.
So I often enter a Japanese word in an external text editor and then copy-and-paste it to the Advanced Text Editor. It's also troublesome, but is a little more efficient than the "blind-entering" in some cases.

FME 2015
I found that some enhancements for the editor have been done in FME 2015, so that the "blind-entering" has been resolved.
The new Advanced Text Editor displays the characters as same as Notepad, so I can enter Japanese characters directly and efficiently. The usability became much higher.
I believe that the improvement brings a big benefit also to every character set which needs a specific character input mode. e.g. Chinese Hanzi, Korean Hangul.

Anyway, I had been waiting for this for long time.
Thanks for the great improvement!

FME 2015 beta build 15179

2014-09-25

How to Specify List Name through Custom Transformer Parameter

In many existing transformers, users can specify name of list attribute that will be created by the transformer. e.g. ListBuilder, AttributeSplitter, StringSearcher etc.
As far as I know, the ListRenamer is the only way to add an interface to a custom transformer, so that users can specify list name, like the existing transformers.

Custom transformer implementation:
1) Create a published user parameter to receive list name specified by the user.
2) Add a ListRenamer to rename a target list to the parameter value.













Then, users can specify the list name through the parameter.
FME 2014 SP3 build 14391








=====
2014-09-30: This is a workspace that contains a custom transformer example which renames attribute/list based on parameter values specified by the user. FME 2014 SP3+
MyRenamer_FME2014.fmw
=====

In FME 2015 beta build 15179, such a custom transformer created with FME 2014 works fine to rename list, but the default name ("_list" in this example) is always visible in the Main Canvas.
FME 2015 Beta build 15179









Result of the translation is fine, but the GUI is not fun...

And also, I found that much enhancements have been done for the ListRenamer itself in FME 2015 beta. All the enhancements are great, but a user parameter cannot be used as new list name, at least in build 15179.
I hope one more enhancement for that, if possible.

=====
2014-11-13: I found another trick by chance in the implementation of the StringMultiSearcher (FME Store transformer). This screenshot illustrates the essence of the trick.













Transformers in the Bookmark expose list names specified by Published Parameters named "listName1" and "listName2" (default values are "_list1" and "_list2"). ​They do nothing other than the exposing; actual list attributes will be created by the PythonCaller.
The upper stream in the Bookmark exposes a structured list name:
$(listName1){}.sub1
$(listName1){}.sub2
The lower stream exposes a simple list name:
$(listName2){}
There are no inputs for the streams, it looks too strange but worked fine as expected in both FME 2014 SP4 build 14433 and FME 2015 Beta build 15210.

In addition, I tried using ListBuilders instead of the Aggregators so that the AttributeRemover_2 can be removed.
FME 2014: It worked fine with no problems.
FME 2015: It also worked fine but the default list names have appeared again in the interface of the custom transformer...orz
I'm not sure what has caused the difference.
Stop the investigation here in order not to fall into the FME darkness.

2014-09-23

Replace String using Regular Expression {.*}

Python re.sub method:
-----
>>> import re
>>> re.sub('.*', 'bar', 'foo')
'bar'
-----

Tcl regsub command:
-----
% regsub -all {.*} foo bar
barbar
-----

I feel the result of the Tcl expression is a little bit strange. If the regex was {^.*} or {^.*$}, the expression returns bar, but {.*$} returns the same result above - barbar.
How do you think about it?

Anyway, FME seems to be using Tcl regsub command internally for string replacement operations in some parts. e.g. StringReplacer (Use Regular Expressions: yes), BulkAttributeRenamer (Action: Regular Expression Replace), etc.
Maybe we will have to check carefully the replacing result with {.*}, especially in FME 2015.

2014-09-21

Why not publish Parallel Processing parameters for the Custom Transformer?

Maybe someone is wondering why I didn't publish Parallel Processing parameters in the RandomPointGenerator implementation (see the previous post).













If you publish the "Parallel Process By" parameter (Right-click > Create User Parameter), you can make the transformer as a block-based one easily. i.e. the parameter works like "Group By".

Originally I had published the parameter while developing the transformer, but I noticed that this warning message appears on the Log window after the translation.
The message always appears if the workspace has the following conditions.
-----
Warning: not all FMESessions that were created were destroyed before shutdown. This may cause instability
-----
Conditions:
1) The custom transformer contains a StatisticsCalculator.
2) The Main workspace containing the custom transformer also contains a StatisticsCalculator.
3) Specify attribute(s) to the "Parallel Process By" parameter of the custom transformer.
4) Select "No Parallelism" (default) for the "Parallel Processing Level" parameter of the custom transformer.
-----
FME 2014 SP3 build 14391

I know that the warning may not cause any problem if the workspace is running with FME Desktop. But I'm afraid that it could cause a serious problem if the workspace is running with FME Server. And also I cannot confirm whether there are any other conditions which cause the same situation.

Therefore, I will not publish Parallel Processing parameters for a custom transformer unless the issue is resolved.

P.S. The same warning has occurred when using the AttributePivoter. These two transformers - StatisticsCalculator and AttributePivoter are implemented with Python. I guess there is an issue in the architecture of Python based transformers.

2014-09-20

RandomPointGenerator

I created a custom transformer named RandomPointGenerator, which creates random points within entire input area features.
Find the transformer in Transformer Gallery (FME Store / Pragmatica).
FME 2014 SP3 build 14391

The implementation has been inspired by this thread in FME Community Answers.
Stratified random point in FME

Here, I illustrate outline of the procedure.

1. Input areas example.
Assume that these areas belong to a same group; these could be parts of an aggregated feature; there may be overlapped areas.











2. Resolve the overlapped area; calculate area for every area.

Deaggregator
AreaOnAreaOverlayer
AreaCalculator







3. Create bounding boxes for each individual area.

BoundingBoxReplacer









4. Dissolve the boxes, and create bounding boxes again; calculate area for every box.

Dissolver
BoundingBoxReplacer
AreaCalculator







5. Compute preferable number of points for each box  (based on the area calculation results and the required number of points), and then randomly create points within the boxes, so that the density will be approximately identical.

StatisticsCalculator
BoundsExtractor
AttributeCreator
ExpressionEvaluator
VertexCreator
Cloner
Offsetter



6. Filter the points by the original areas.

SpatialFilter









7. Randomly take points of specified number.

ExpressionEvaluator
Sorter
Sampler

2014-09-19

Concatenate Attribute Values based on Attribute List Parameter

A custom transformer can receive space delimited attribute names through "Attribute List (space delimited)" type user parameter.












Typically, a parameter of this type would be used to link to "Group By" of transformer(s) in the custom transformer. In such a case, it's not necessary to retrieve values of the attributes.
But there could also be some cases where those values are necessary. e.g. use concatenated attribute values as Counter Name of a Counter.

Value of the parameter is a string. If specified attributes are "attr1", "attr2", and "attr3", for example, the value of parameter will be "attr1 attr2 attr3" (space delimited attribute names). And if an attribute name contains white spaces, it will be quoted by double quotations.

I think Tcl scripting is an easy way to concatenate specified attribute values.
First, use a ParameterFetcher to store the parameter value (i.e. specified attribute names) as an attribute. e.g. named "_attr_names".
And then, use a TclCaller with this script to create a concatenated string.
If no attributes were specified, this procedure returns an empty string.
-----
proc concatAttributeValues {} {
    set values {}
    foreach attr [FME_GetAttribute "_attr_names"] {
        lappend values [FME_GetAttribute $attr]
    }
    return [join $values {_}]
}
-----

It worked fine even if specified attribute names contain white spaces.









FME 2014 SP3 build 14391