2017-08-18

属性の階層構造の表現 - 構造化リスト, XML, JSON

一般的なGISデータフォーマットでは、地物の属性は表形式で格納されることが多いと思いますが、ひとつの属性項目が下位の複数の属性で構成される階層構造を持っていたり、同一の属性項目の複数の値が単一の地物に関連付けられたりするなど、フラットな表だけでは、本来の構造をストレートに表現するのが難しいケースもあります。

例えば、国土数値情報「バス停留所」

国土数値情報「バス停留所」データの製品仕様書によれば、バス停留所フィーチャーは次の属性を持つことになっています。

・バス停名
・バス路線情報(0個以上、個数上限なし)

このうち「バス路線情報」属性は単一の値ではなく、次の3つの下位属性で構成されています。

・バス区分: 運行形態の種類(路線バス(民間)、路線バス(公営)、コミュニティバスなど)を示すコード
・事業者名: 路線を運営する事業者の名称。
・バス系統: バスの系統番号または系統名。

GML形式のデータでは、属性本来の階層構造がXMLドキュメントツリーの構造に反映されています。

国土数値情報「バス停留所」GML形式: 地物XML要素の記述例
注1: 実データからの引用ですが、事業者名、地名等の固有名詞は英文字の記号に置き換えました。
柱2: コメント <!-- *** --> は、記述内容を分かりやすくするために追記したものです。
<ksj:BusStop gml:id="ED01_5">  <!-- バス停留所 -->
    <ksj:position xlink:href="#n5"/>  <!-- gml:Point 要素 (空間属性) 参照 -->
    <ksj:busStopName>図書館前</ksj:busStopName>  <!-- バス停名 -->
    <ksj:busRouteInformation>  <!-- バス路線情報 -->
        <ksj:BusRouteInformation>
            <ksj:busType>1</ksj:busType>  <!-- バス区分 -->
            <ksj:busOperationCompany>XXバス(株)</ksj:busOperationCompany>  <!-- 事業者名 -->
            <ksj:busLineName>AA01</ksj:busLineName>  <!-- バス系統 -->
        </ksj:BusRouteInformation>
    </ksj:busRouteInformation>
    <ksj:busRouteInformation>  <!-- バス路線情報 -->
        <ksj:BusRouteInformation>
            <ksj:busType>1</ksj:busType>  <!-- バス区分 -->
            <ksj:busOperationCompany>XXバス(株)</ksj:busOperationCompany>  <!-- 事業者名 -->
            <ksj:busLineName>深夜(AA01)</ksj:busLineName>  <!-- バス系統 -->
        </ksj:BusRouteInformation>
    </ksj:busRouteInformation>
    <ksj:busRouteInformation>  <!-- バス路線情報 -->
        <ksj:BusRouteInformation>
            <ksj:busType>3</ksj:busType>  <!-- バス区分 -->
            <ksj:busOperationCompany>YY市</ksj:busOperationCompany>  <!-- 事業者名 -->
            <ksj:busLineName>BB線(CC循環コース)</ksj:busLineName>  <!-- バス系統 -->
        </ksj:BusRouteInformation>
    </ksj:busRouteInformation>
</ksj:BusStop>

しかし、フラットな表ではこのような属性の階層構造を表現することはできません。そのため、Shapefile形式の国土数値情報「バス停留所」データでは、バス路線情報に属する下位の属性もそれぞれ単一の属性項目として扱い、複数の値がある場合は、それらをカンマ区切りで連結した文字列によって記録しています。

国土数値情報「バス停留所」Shapefile形式: 属性定義
属性名内容上記XML地物要素例に対応するレコードの値
P11_001バス停名図書館前
P11_002バス路線情報_バス区分1,1,3
P11_003_1バス路線情報_事業者名XXバス(株),XXバス(株),YY市
P11_003_2~19(同上 x 18列)(空欄 x 18列)
P11_004_1バス路線情報_バス系統AA01,深夜(AA01),BB線(CC循環コース)
P11_004_2~19(同上 x 18列)(空欄 x 18列)

事業者名、バス系統は、論理的にはひとつずつの属性項目ですが、表の物理構造としては、それぞれ19列ずつ値の格納領域が確保されています。これは、Shapefile形式の仕様上、1列に格納できるデータサイズの上限は255バイトまでであり、路線数が多いバス停留所では1列に収められないことがあるためと思われます。

この方式では、カンマで区切られた個別要素の並び順でみて同じ位置にあるバス区分、事業者名、バス系統によって、ひとつの「バス路線情報」が構成されます。つまり、上の例の「図書館前」バス停は、次の3つのバス路線情報を持っていることになります。

「図書館前」バス停のバス路線情報 (カンマ区切り文字列)
(並び順)バス区分事業者名バス系統
01XXバス(株)AA01
11XXバス(株)深夜(AA01)
23YY市BB線(CC循環コース)

以下、このShapefile形式の属性データを、階層構造の表現が可能な次の3種類のデータ構造に変換するワークスペース例を紹介します。

a. 構造化リスト (Structured List)
b. XML要素
c. JSONオブジェクト


FMEワークスペース例(FME 2017.1.0.0 build 17539)

ステップ 1: 共通の前処理

























AttributeManager: P11_001 (バス停名), P11_002 (バス区分) については属性名を変更し、P11_003_1~19 (事業者名 x 19列), P11_004_1~19 (バス系統 x 19列) については、各列の値をカンマをはさんで連結することによってそれぞれ単一のカンマ区切り文字列とし、新しい属性に格納します。

AttributeTrimmer: 事業者名、バス系統の2列目以降 (P11_003_2~, P11_004_2~) が空欄の場合、連結後の文字列の末尾には1個以上の余分なカンマが付加されるので、ここでそれらを削除します。

AttributeSplitter x 3個: カンマ区切りで列挙されている複数のバス区分、事業者名、バス系統をそれぞれカンマで分割し、分割後の個別のバス区分、事業者名、バス系統を要素とする次の3つのリスト属性を作成します。

type{}
company{}
lineName{}

「図書館前」バス停のバス路線情報 (リスト属性)
i (index)type{i}company{i}lineName{i}
01XXバス(株)AA01
11XXバス(株)深夜(AA01)
23YY市BB線(CC循環コース)

後続のワークフローで、これらの3つのリスト属性を構造化リスト、XML要素、または、JSONオブジェクトに変換します。


ステップ 2-a: 構造化リスト (Structured List) への変換











ステップ 1 で作成された3つのリスト属性から同一のインデクスの要素 - (バス区分, 事業者名, バス系統) を集めることによってひとつのバス路線情報を構成できますが、データ構造として、それらが関連づけられているわけではありません。

これらのリストを、ひとつの「構造化リスト(Structured List または 複合リスト: Complex List)」に変換することによって、共通の名前(リスト名)とインデクスで特定のバス路線情報 = (バス区分, 事業者名, バス系統) を識別できるようになります。

ステップ 2-a の BulkAttributeRenamer (Action: Regular Expression Replace) は、ステップ 1 で作成された3つのリストを、次のように3つのメンバーを持つひとつの構造化リストに変換します。

busRoute{}.type
busRoute{}.company
busRoute{}.lineName

これらの名前は自動的には Workbench のインターフェース上に現れないので、AttributeExposer を使って現しました。

「図書館前」バス停のバス路線情報 (構造化リスト属性)
i (index)busRoute{i}.typebusRoute{i}.companybusRoute{i}.lineName
01XXバス(株)AA01
11XXバス(株)深夜(AA01)
23YY市BB線(CC循環コース)

構造化リストは、ワークスペース内でのリストの操作において、リスト内の要素の位置(インデクス)がメンバー間で同期するという性質を持っています。例えば、ひとつのメンバーの値の昇順あるいは降順でリストの要素を並べ替えたときには他のメンバーも自動的に同じ順で並べ替えられ、個々の要素に属するメンバーの組み合わせは維持されます。

また、ListExploder によって構造化リストの要素ごとにフィーチャーを展開したときに、各要素のメンバーの値は、展開後の各フィーチャーではメンバー名と同じ名前の属性に格納されるということも重要な性質です。

ステップ 2-a のワークフローの後に ListExploder を追加して busRoute リストについてフィーチャーを展開すれば、展開後の各フィーチャーは、バス停名のほかに、ひとつのバス路線情報を構成する3つの属性: type (バス区分), company (事業者名), および lineName (バス系統) を持つことになります。元のデータセットが「バス停名」を主キーとしてそのポイントジオメトリを格納した空間データテーブルであると考えれば、この変換は、「バス停名」を外部キーとするバス路線情報テーブルを作成することに他なりません。

リスト属性については、「文字列の分割とリスト属性の展開」もご参照ください。


ステップ 2-b: XML要素への変換

















XML要素またはJSONオブジェクトならば、属性の階層構造をさらに直接的に表現することができます。

XML、JSON関係のいくつかのトランスフォーマーは、XQuery式の実行をサポートしており、また、リスト属性をシーケンス (Sequence) に変換するXQuery関数 "fme:get-list-attribute" も提供されているので、ステップ 1 で作成した3つのリスト属性に基づいて、属性の階層構造を表現するXML要素またはJSONオブジェクトを作成するのは容易です。

ステップ 2-b の XMLTemplater では、Template Expression パラメーターに次の式を設定することにより、バス停留所フィーチャーの属性セットをひとつのXML要素に変換し、「バス停留所」属性に格納しました。

XMLTempleter | Template Expression:
<busStop>
    <name>{fme:get-attribute("バス停名")}</name>
    <busRoutes>{
        let $companies := fme:get-list-attribute("company{}")
        let $lineNames := fme:get-list-attribute("lineName{}")
        for $type at $i in fme:get-list-attribute("type{}")
        return
        <route>
            <type>{$type}</type>
            <company>{$companies[$i]}</company>
            <lineName>{$lineNames[$i]}</lineName>
        </route>
    }</busRoutes>
</busStop>

XMLTemplater によって作成されたXML要素(「バス停留所」属性)の例
<?xml version="1.0" encoding="UTF-8"?>
<busStop>
    <name>図書館前</name>
    <busRoutes>
        <route>
            <type>1</type>
            <company>XXバス(株)</company>
            <lineName>AA01</lineName>
        </route>
        <route>
            <type>1</type>
            <company>XXバス(株)</company>
            <lineName>深夜(AA01)</lineName>
        </route>
        <route>
            <type>3</type>
            <company>YY市</company>
            <lineName>BB線(CC循環コース)</lineName>
        </route>
    </busRoutes>
</busStop>


ステップ 2-c: JSONオブジェクトへの変換
















ステップ 2-c の JSONTemplater では、Template Expression パラメーターに次の式を設定することにより、バス停留所フィーチャーの属性セットをひとつのJSONオブジェクトに変換し、「バス停留所」属性に格納ました。

JSONTemplater | Template Expression:
{
    "name": fme:get-attribute("バス停名"),
    "busRoutes": [
        let $companies := fme:get-list-attribute("company{}")
        let $lineNames := fme:get-list-attribute("lineName{}")
        for $type at $i in fme:get-list-attribute("type{}")
        return
        {
            "type": $type,
            "company": $companies[$i],
            "lineName": $lineNames[$i]
        }
    ]
}

JSONTemplater によって作成されたJSONオブジェクト(「バス停留所」属性)の例
{
   "name" : "図書館前",
   "busRoutes" : [
      {
         "type" : "1",
         "company" : "XXバス(株)",
         "lineName" : "AA01"
      },
      {
         "type" : "1",
         "company" : "XXバス(株)",
         "lineName" : "深夜(AA01)"
      },
      {
         "type" : "3",
         "company" : "YY市",
         "lineName" : "BB線(CC循環コース)"
      }
   ]
}

ステップ 2-a の構造化リストはFME固有の仕様であり、ソースデータセットから読み込んだフィーチャーのデータ構造を出力先のスキーマに合わせて変換する過程で、必要に応じて中間的なデータ構造としてのみ使えるのに対して、XMLとJSONは標準化されたデータフォーマットなので、出力先データセットに書き込む最終形にもなり得ます。

XMLTemplater, JSONTemplater は、一般に、フィーチャーの属性セット(リスト属性を含む)に基づいて任意のスキーマでXML要素、JSONオブジェクトを作成する場合に使うことができます。


補足: XQuery標準関数の利用

XMLTemplater, JSONTemplater など、XQuery式をサポートするトランスフォーマーでは、XQueryの標準関数もサポートされます。XQuery標準関数の中には、指定した区切り文字で文字列を分割してシーケンスを返す fn:tokenize 関数があるので、例えば、XML要素への変換については、次のXQuery式を XMLTemplater の Template Expression に設定すれば、ステップ 1 の3つの AttributeSplitter は不要になります。JSONTemplater についても同様です。

XMLTemplatere | Template Expression:
<busStop>
    <name>{fme:get-attribute("バス停名")}</name>
    <busRoutes>{
        let $companies := fn:tokenize(fme:get-attribute("事業者名"), ',')
        let $lineNames := fn:tokenize(fme:get-attribute("バス系統"), ',')
        for $type at $i in fn:tokenize(fme:get-attribute("バス区分"), ',')
        return
        <route>
            <type>{$type}</type>
            <company>{$companies[$i]}</company>
            <lineName>{$lineNames[$i]}</lineName>
        </route>
    }</busRoutes>
</busStop>

2017-07-29

国土数値情報ベクターデータ(GML)の読込

国土数値情報ダウンロードサービスサイトで公開されているデータのうち、GML形式(JPGIS2.1準拠)のベクターデータ(点、線、面)を読み込むためのカスタムトランスフォーマー JpKsjVectorReader を FME Hub で公開しました。

今回は、このカスタムトランスフォーマーの基本的な使用方法を示すワークスペース例を掲げます。

FME Hub で公開されているカスタムトランスフォーマーは、通常のトランスフォーマーと同じ操作(Quick Add または Transformer Gallery からのドラッグ・アンド・ドロップ等)によって編集中のワークスペースに追加できます。ただし、ワークスペース編集に使用している FME Workbench のバージョンが当該カスタムトランスフォーマー作成時のバージョン以降であること、および、少なくとも初回はインターネットに接続していることが必要です。JpKsjVectorReader は FME 2017.0.1.1 で作成しました。

2017-08-02: FME Hub の JpKsjVecotorReader を更新しました。更新理由、内容については、本文後の「2017-08-02 更新について」で説明します。


FMEワークスペース例1: 国土数値情報「行政区域」データ読込 (FME 2017.0.1.1 build 17291)






Creator: 処理開始用のフィーチャーを1個作成、出力します。

JpKsjVectorReader: 次のようにパラメーターを設定することによって、国土数値情報「行政区域」データを読み込み、行政区域の範囲(面)を表すジオメトリと、その区域の主題属性を保持した "xml_fragment" 属性(地物クラスXML要素をルートとするXML断片)をもつフィーチャーを出力します。

  • KSJ Vector Dataset (GML): <国土数値情報ダウンロードサービスサイトからダウンロードした「行政区域」データ解凍後の *.xml ファイルパス、または、解凍前の *.zip ファイルパス。複数可>
  • Feature Types to Read: AdministrativeBoundary <「行政区域」データの「製品仕様書」で規定されている地物クラスのXML要素名>

JpKsjVectorReader パラメーター設定画面(行政区域)















国土数値情報の「製品仕様書」は、国土数値情報ダウンロードサービスサイトにおけるデータの種類別の説明ページ「データフォーマット(符号化)」の項で公開(PDF文書にリンク)されています。

XMLFlattener: "xml_fragment" 属性の値(地物クラスXML要素をルートとするXML断片)を平坦化して、個別の属性を抽出します。詳細は後述します。

Inspector: 変換終了後、全てのフィーチャーを FME Data Inspector で表示します。ベクタージオメトリの保存をサポートするデータフォーマットのライターをワークスペースに追加し、Inspector の代わりにライターのフィーチャータイプをここに接続すれば、そのフォーマットの「行政区域」データセットが作成できます。

国土数値情報「行政区域」データ変換結果: 平成29年度, 四国4県(徳島県, 香川県, 愛媛県, 高知県)
FME Data Inspector による表示(背景: Esri ArcGIS Online Map Service - World Light Gray Base)。




















XMLFlattener による属性の抽出

KsjVectorReader が出力するフィーチャーは、地物クラスのXML断片を "xml_fragment" 属性として持っているので、それを XMLFlattener によって平坦化することにより、下位のXML要素/属性に記述されている内容をフィーチャーの属性として抽出することができます。XMLの断片化 (fragmentation)、平坦化 (flattening) については「XMLの読込 - 断片化と平坦化」もご参照ください。

国土数値情報「行政区域」AdministrativeBoundary クラスXML要素(XML断片)の例
<?xml version="1.0" encoding="UTF-8"?>
<ksj:AdministrativeBoundary xmlns:ksj="http://nlftp.mlit.go.jp/ksj/schemas/ksj-app" xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="gy0">
<ksj:bounds xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sf0"/>
<ksj:prefectureName>愛媛県</ksj:prefectureName>
<ksj:subPrefectureName/>
<ksj:countyName/>
<ksj:cityName>松山市</ksj:cityName>
<ksj:administrativeAreaCode codeSpace="AdministrativeAreaCode.xml">38201</ksj:administrativeAreaCode>
</ksj:AdministrativeBoundary>

このXML断片を平坦化すると、次の表に掲げる属性が抽出されます。

国土数値情報「行政区域」フィーチャーの属性
参考: 国土数値情報(行政区域)製品仕様書 第 2.3 版(平成28年3月 国土交通省国土政策局)
属性名 内容 備考
AdministrativeBoundary.id フィーチャーID //ksj:AdministrativeBoundary/@gml:id
bounds.href 範囲(面)を表すジオメトリのID 参照先の //gml:Surface/@gml:id
prefectureName 都道府県名
subPrefectureName 支庁・振興局名 北海道の支庁・振興局の名称
countyName 郡・政令都市名
cityName 市区町村名
administrativeAreaCode 行政区域コード JIS X 0401, JIS X 0402
注: 大正9年時点のデータは、上記の他に次の2つの属性を持ちます。
  • formationDate.TimeInstant.timePosition (成立年月日)
  • disappearanceDate.TimeInstant.timePosition (消滅年月日)

上記のような平坦化を行うための XMLFlattener のパラメーター設定は、次のとおりです。
  • XML Source Type: Attribute with XML document
  • XML Attribute: xml_fragment
  • Elements to Match: AdministrativeBoundary
  • Attributes to Expose: <平坦化によって抽出される属性のうち、Workbench のインターフェース上に現す必要があるものの名前。上の表の全ての属性名を現す必要はなく、後続のトランスフォーマー等で利用したい属性だけで構いません>

XMLFlattener パラメーター設定画面




















FMEワークスペース例2: 国土数値情報「河川」データ読込 (FME 2017.0.1.1 build 17291)










JpKsjVectorReader: 国土数値情報「河川」データでは、1つのデータセット(ファイル)に2つの地物クラス: Stream (流路) と RiverNode (河川端点) のデータが記述されています。どちらも同時に読み込む場合には、JpKsjVectorReader の Feature Types to Read パラメーターに、空白類文字(半角スパース、タブ、または改行)区切りでそれらを列挙します。

JpKsjVectorReader パラメーター設定画面(河川)















FeatureTypeFilter: 地物クラス名(Stream, RiverNode) はフィーチャータイプ名("fme_feature_type" 属性の値)として扱われるので、JpKsjVectorReader が出力するフィーチャーは、FeatureTypeFilter によって地物クラス(フィーチャータイプ)別のデータフローに振り分けることができます。

XMLFlattener: その後、前述の行政区域と同様に XMLFlattener で属性を抽出します。

国土数値情報「河川」データ変換結果: 四国4県(徳島県, 香川県, 愛媛県, 高知県)流路のみ表示
FME Data Inspector による表示(背景: Esri ArgGIS Online Map Service - World Light Gray Base)。



















XMLFlattener による属性の抽出方法は、行政区域の場合と同じです。以下、Stream, RiverNode クラスのXML断片(例)と、それを平坦化することによって得られる属性の一覧表を掲げます。

国土数値情報「河川」 Stream (流路) クラスXML要素(XML断片)の例
<?xml version="1.0" encoding="UTF-8"?>
<ksj:Stream xmlns:ksj="http://nlftp.mlit.go.jp/ksj/schemas/ksj-app" xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="r-3700001">
<ksj:waterSystemCode codeSpace="WaterSystemTypeCode.xml">880807</ksj:waterSystemCode>
<ksj:location xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#c-3700001"/>
<ksj:riverCode codeSpace="RiverTypeCode.xml">8808070000</ksj:riverCode>
<ksj:sectionType>0</ksj:sectionType>
<ksj:riverName>宮川</ksj:riverName>
<ksj:originalDataType>2</ksj:originalDataType>
<ksj:flowDirection>1</ksj:flowDirection>
<ksj:startRiverNode xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#t-3701506"/>
<ksj:endRiverNode xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#t-3701551"/>
<ksj:startStreamNode xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#t-3701506"/>
<ksj:endStreamNode xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#t-3701504"/>
</ksj:Stream>

国土数値情報「河川」 Stream (流路) フィーチャーの属性
参考: 国土数値情報(河川)製品仕様書 第 3.1 版(平成24年3月 国土交通省国土政策局)
属性名 内容 備考
Stream.id フィーチャーID //ksj:Stream/@gml:id
waterSystemCode 水系域コード
location.href 場所(線)を表すジオメトリのID 参照先の //gml:Curve/@gml:id
riverCode 河川コード
sectionType 区間種別コード
riverName 河川名
originalDataType 原点資料種別コード
flowDirection 流下方向判定
startRiverNode.href 河川始点 河川の始点に該当する河川端点フィーチャーのID
endRiverNode.href 河川終点 河川の終点に該当する河川端点フィーチャーのID
startStreamNode.href 流路始点 流路の始点に該当する河川端点フィーチャーのID
endStreamNode.href 流路終点 流路の終点に該当する河川端点フィーチャーのID


国土数値情報「河川」 RiverNode (河川端点) クラスXML要素(XML断片)の例
<?xml version="1.0" encoding="UTF-8"?>
<ksj:RiverNode xmlns:ksj="http://nlftp.mlit.go.jp/ksj/schemas/ksj-app" xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="t-3601133">
<ksj:waterSystemCode codeSpace="WaterSystemTypeCode.xml">880807</ksj:waterSystemCode>
<ksj:position xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#p-3601133"/>
<ksj:elevation>289</ksj:elevation>
</ksj:RiverNode>

国土数値情報「河川」 RiverNode (河川端点) フィーチャーの属性
参考: 国土数値情報(河川)製品仕様書 第 3.1 版(平成24年3月 国土交通省国土政策局)
属性名 内容 備考
RiverNode.id フィーチャーID //ksj:RiverNode/@gml:id
waterSystemCode 水系域コード
position.href 地点を表すジオメトリのID 参照先の //gml:Point/@gml:id
elevation 標高


地物クラスのXML要素名(フィーチャータイプ名)やその下位のXML要素/属性名は「製品仕様書」で規定されていますが、階層構造を持っている属性については全ての子孫要素名や構造が分からない場合があり、また、あってはならないことなのですが、現実には「製品仕様書」で規定されている符号化規則と実際のデータの記述形式が矛盾している場合もあります。

そのため、 JpKsjVectorReader や XMLFlattener のパラメーターの設定にあたっては、「製品仕様書」を調べるだけでなく、データ本体をテキストエディタで開く等により、実際のデータに記述されているXML要素/属性名や階層構造も確認することをお勧めします。

「製品仕様書」とデータの記述形式の間の矛盾に関しては、データの不具合と思われるケースと「製品仕様書」の誤記と思われるケースの両方がありますが、FMEはデータしか読みませんので、前者(データの不具合)であったとしても、データが修正されない限りは、データの記述形式の方に合わせてワークスペースを構成する必要があります。

ただし、以下に掲げるデータ不具合の修復については、JpKsjVectorReader が自動的に行います。
  1. gml:Point 要素下位の gml:position は gml:pos とみなす(GMLスキーマでは、座標を記述するXML要素名は "gml:pos" が正)。
  2. gml:OrientableCurve/gml:baseCurve/@xlink:href の値と一致するIDをもつ gml:Curve 要素が見つからなかった場合、サフィクス _* を削除したIDによってリトライする。
今後、新たに自動修復可能なデータの不具合を発見したときは、修復機能を追加して JpKsjVectorReader を更新する方針です。


2017-08-02 更新について

国土数値情報(GML形式)のXML文書では、ジオメトリを記述するGML要素(gml:Point, gml:Curve, gml:Surface) と地物クラスXML要素 (例えば、行政区域であれば ksj:AdministrativeBoundary) は同一の階層レベルで記述されており、地物クラスXML要素に属する特定の子要素の xlink:href 属性によって、地物のジオメトリを表すGML要素を参照する仕組みとなっています。

例えば、次の行政区域XML要素は、bounds 要素 xlink:href 属性の値 "#sf0" によって、同一文書内の他の場所に記述されている特定の gml:Surface 要素を参照し、そのジオメトリと結合することができます。

<?xml version="1.0" encoding="UTF-8"?>
<ksj:AdministrativeBoundary xmlns:ksj="http://nlftp.mlit.go.jp/ksj/schemas/ksj-app" xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="gy0">
<ksj:bounds xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sf0"/>
<ksj:prefectureName>愛媛県</ksj:prefectureName>
<ksj:subPrefectureName/>
<ksj:countyName/>
<ksj:cityName>松山市</ksj:cityName>
<ksj:administrativeAreaCode codeSpace="AdministrativeAreaCode.xml">38201</ksj:administrativeAreaCode>
</ksj:AdministrativeBoundary>

国土数値情報では、GML形式のデータの公開開始以来、多くのデータで、xlink:href 属性によってGML要素を参照するためのXML要素の名前として
  • position (gml:Point を参照)
  • location (gml:Curve を参照)
  • bounds (gml:Surface を参照)
が使われてきました。このことについての関連文書はない(あったとしても現在は公開されていない)ものの、事実上の共通仕様であると考えることができました。

そのため、当初の JpKsjVectorReader では、上記3種の要素名 + 既知の例外 are (gml:Surface を参照) のみをサポートし、もしその他の例外があった場合には、発見した時点で拡充していく方針としていました。

しかし、近年のデータをいくつか調べたところ、例外的な要素名が予想以上の頻度で見つかりました(例: 都市公園データでは loc で gml:Point を参照、バスルートデータでは brt で gml:Curve を参照)。最近は、場当たり的にジオメトリ参照用のXML要素名が決定されているように見受けられます。

ジオメトリ参照用のXML要素名としてどんな名前が現れるか予測不能であるだけでなく、例外的な名前が現れる頻度も高いと思われることから、発見した時点で拡充していくという方針を改め、今回の更新によって、ワークスペースの作成者が、読み込もうとするデータで実際に使われているジオメトリ参照用のXML要素の名前を指定できるよう、以下のパラメーターを追加しました。

パラメーター名設定内容初期設定値
Elements to Refer Point点ジオメトリ参照用のXML要素の名前position
Elements to Refer Curve線ジオメトリ参照用のXML要素の名前location
Elements to Refer Surface面ジオメトリ参照用のXML要素の名前bounds

ジオメトリ参照用のXML要素名は、データの種類別に「製品仕様書」(符号化規則)で規定されています。また、データ本体(XML文書)をテキストエディタ等で開き、内容を直接確認することもできます。

例えば、点ジオメトリ参照用のXML要素名が loc であるデータを読み込む場合は、Elements to Refer Point パラメーターに loc を設定してください。

これらのパラメーターには、空白類文字(半角スペース、タブ、改行)で区切ることにより、それぞれ複数のXML要素名を同時に指定することもできます。例えば、複数の異なる種類のデータを連続して読み込む場合で、ジオメトリ参照用のXML要素の標準的な名前(初期設定値: position, location, bounds)と既知の例外的な名前(loc, brt, are)が全て現れるのであれば、次の図ように設定します。





















また、これらのパラメーターのうち、読み込もうとするデータ内には存在しないことが明らかなジオメトリの種類(点、線、または面)に対応するものの設定は省略できます。ただし、これらの3パラメーターの設定を全て省略した場合は、このトランスフォーマーはデータを読み込まず、入力フィーチャーを <Rejected> ポートから出力します。

2017-07-18

歩行空間ネットワークのジオメトリ作成

G空間情報センターで公開されている「歩行空間ネットワークデータ」(CSV形式)に基づいて、ネットワークのノードを表すポイントと、ノード間のリンクを表すラインを作成するFMEワークスペース例を紹介します。

ソース 1:(東京都)台東区データ「ノード情報.csv」
1行1ノードで、ノードID (ユニークな識別子)、緯度、経度、高さ、その他の属性が記述されています。緯度、経度は特殊な書式(緯度: DD.MM.SS.SS, 経度: DDD.MM.SS.SS)で記述されているので、ジオメトリ(ポイント)を作成するには、それぞれ十進度に変換する必要があります。「高さ」は標高ではなく、屋内の場合は階層数を示す値(中間階は *.5)、屋外の場合は0です。













ソース 2:(東京都)台東区データ「リンクの情報.csv」
1行1リンクで、リンクID (ユニークな識別子)、起点ノードID、終点ノードID、その他の属性が記述されています。位置情報は記述されていないので、ジオメトリ(ライン)を作成するには、起点/終点ノードIDと一致するノードIDを持つノード情報を参照する必要があります。













変換結果: (東京都)台東区データ - 浅草寺付近のノード (ポイント) とリンク (ライン)
FME Data Inspector による表示。背景は地理院地図「標準地図」です。



















FMEワークスペース例 (FME 2017.0.1.1 build 17291)

1. ノードのジオメトリ(ポイント)作成












ノードの座標(緯度、経度)と「高さ」に基づいて VertexCreator によって3Dポイントジオメトリ (Z=「高さ」) を作成することができますが、ソースデータに記述されている座標の書式が特殊(緯度: DD.MM.SS.SS, 経度: DDD.MM.SS.SS)であるため、事前に十進度に変換する必要があります、この例では、StringSearcher (2個) と AttributeManager を使ってそれを行いました。

StringSearcher x 2: 次の正規表現によって、緯度/経度文字列から度、分、秒の3数値を抽出して _lat{}.part (緯度), _lon{}.part (経度) リストに格納します。

正規表現: ^(\d+)\.(\d+)\.(.+)$

注: 日本語版Windowsでは、バックスラッシュ \ を半角¥記号に読み替えてください。


リストの要素格納される値
_lat{0}.part緯度
_lat{1}.part
_lat{2}.part
_lon{0}.part経度
_lon{1}.part
_lon{2}.part

StringSearcher パラメーター設定画面 (左: 緯度, 右: 経度)















AttributeManager: 緯度、経度の値として次の式を Arithmetic Editor (数式エディタ) で入力することにより、実行時に、それらの値が式の計算結果(十進度)で置き換えられます。

緯度: @Value(_lat{0}.part)+@Value(_lat{1}.part)/60.0+@Value(_lat{2}.part)/3600.0
経度: @Value(_lon{0}.part)+@Value(_lon{1}.part)/60.0+@Value(_lon{2}.part)/3600.0

注: "@Value(属性名)" は、実行時に属性の値を抽出するための式であり、エディタ左側のペインの Feature Attributes セクションに表示されている属性名のダブルクリック、あるいは、ドラッグ・アンド・ドロップによって編集中の式に挿入することができます。リストの個別の要素({ } 内でインデクスを指定したもの)は、属性名として取り扱われます。

注: DecimalDegreesCalculator トランスフォーマーによって、度、分、秒から十進度への換算を行うこともできます。

AttributeManager パラメーター設定画面














VertexCreator: 十進度に変換済みの緯度、経度、および高さに基づいて3Dポイントジオメトリを作成します。


2. リンクのジオメトリ(ライン)作成
リンク情報フィーチャーは多数の属性を持っていますが、下図では、説明上必要な属性のみを現しています。

















FeatureMerger: CSVリーダーで読み込んだリンク情報フィーチャーに、その「起点ノードID」と一致する「ノードID」を持つノードフィーチャー(属性とジオメトリ=起点ノードのポイント)を結合します。

FeatureMerger_2: 起点ノード結合後のリンク情報フィーチャーに、その「終点ノードID」と一致する「ノードID」を持つノードフィーチャー(属性のみ)を結合します。

VertexCreator_2: 終点ノード結合後のリンク情報フィーチャーのジオメトリ(起点ノードのポイント)に、終点ノードのポイントを追加し、リンクのジオメトリ(起点ノードと終点ノードを結ぶライン)に変換します。

FeatureMerger は、Supplier フィーチャーの属性のみを Requestor フィーチャーに結合するために使うことが多く、Feature Merge Type パラメーターの初期設定は "Attributes Only" (属性のみ) となっていますが、それを "Attributes and Geometry" (属性とジオメトリ) に変更することにより、属性とあわせてジオメトリも結合できるようになります(結合先の Requestor フィーチャーがジオメトリを持っていた場合には、Supplier のジオメトリに置き換えられます)。

また、Accumulation Mode パラメーターを "Prefix Supplier" に設定することにより、Supplier フィーチャーの属性名に "Prefix" で指定した接頭辞(プレフィクス)を付加してから結合することができます。この例では、起点ノードを結合するときに「起点」、終点ノードを結合するときに「終点」を接頭辞として付加しました。


歩行空間ネットワークデータのノードは「高さ」(標高ではなく、階層数を示す値)を持っているので、上記ワークスペース例のように、リンクのジオメトリを3Dライン (Z=「高さ」) として作成することにより、起点と終点の座標(緯度、経度)が等しく、「高さ」が異なる鉛直のリンク(例: エレベーター)も表現できます。

しかし、今回使用した台東区データでは、起点と終点の座標が等しく、「高さ」も等しいリンクが10箇所ありました。このデータを業務で利用しようとするならば、「高さ」も含めて起点と終点が一致するリンクが何を意味するのか追求しなければなりませんが、FMEケーススタディとしてはここまでとします。

2017-07-13

複数のExcel単票から一覧表への変換

ここ数年、ウェブ上で公開される統計データの種類と量は急速に拡大してきました。製本された統計表を購入したり図書館で閲覧したりしなければならなかった頃を思うと、隔世の感を覚えます。

主要な政府統計は e-Stat (政府統計の総合窓口) で検索、閲覧、ダウンロードできますが、ダウンロードできるデータのうちExcel形式のものは、主に印刷物の形態で統計表を公開していた時代の名残なのでしょうか、印刷用に体裁が整えられているために、そのままではデータとして利用するのが難しいことがあります。

「神エクセル」というネットスラングがあるそうですが、その伝で言えば、e-Statで提供されているExcelデータにはかなり神がかっているものもあり、作成者も利用者も無用な労力=コストの支出を強いられているように思います。「働き方改革」が求められている昨今ですが、統計表に限らず、あらゆる文書について過度に体裁を整えるのを止めるだけでも、労働生産性の向上にかなり寄与できるのではないでしょうか。

また、印刷製本が必要だとしても、そのために体裁を整える作業はプロの印刷業者に任せれば良いことであって、その点では素人の公務員が手間ひまをかけるのは、非常にもったいないことであるとも思います。

閑話休題。印刷用の体裁にこだわるあまり、データとしては利用しにくいExcelワークシートでも、その「こだわり方」に規則性があるならば、FMEによって単純なテーブルに変換するのは容易かも知れません。

例えば、e-Statで公開されている「電力需要調査」(資源エネルギー庁)統計表 "kekka.xls" には、1四半期あたり1ワークシートの単票により、平成12年第2四半期から平成23年第1四半期までの44期分の調査結果 (ただし「高圧」は平成16年第2四半期以降) が収録されています (2017-07-13現在)。

ソース: e-Stat 電力需要調査結果「単票」 (1ブック, 44期分44ワークシート)
見るからに印刷用の様式です。ブック全体では、特別高圧: 9セル x 44期 (シート)  = 396セル, 高圧: 9セル x 28期 = 252セルにデータが記入されています。

















およそ10年分のデータが公開されているので、時系列での変動状況を知りたくなります。需要家別、項目別にデータを時系列で整理した一覧表があれば、項目ごとの変動状況を折れ線グラフ等で表現することはExcelの機能を使って簡単にでき、一目で変動状況が把握できますが、44ワークシートから必要なデータをひとつずつコピー・アンド・ペーストして一覧表を作成しなければならないとなると、気が滅入ります。

しかし、幸いなことに、44ワークシートは全て同じ様式で、データ項目ごとの記述先のセルの位置は決まっており、また、調査年・四半期が識別できるようにワークシート名の書式も統一されています。これならば、FMEによって、全てのデータ項目について時系列で整理した一覧表を一気に作成することができます。

ここでは、特別高圧、高圧の別に、全四半期の調査結果を時系列で整理した一覧表(Excelワークシート)に変換するワークスペース例を掲げます。結果は、次のようなテーブルになります。

変換結果: 電力需要調査結果「一覧表」 (1ブック, 2ワークシート:「特別高圧」「高圧」)
書式(フォントの種類・サイズ、数値の桁区切り・小数部桁数、列幅、罫線)は、変換後にExcelによって設定しました。この程度の書式設定ならば、1~2分もあれば誰にでもできることであり、仮に公開するとしても、これで十分であると考えます。
















FMEワークスペース例 (FME 2017.0.1.1 build 17291)

【ステップ1】





















[XLSXR] (Excel) リーダー: 電力需要調査結果Excelブック "kekka.xls" の全てのワークシートから、それぞれ 5~10 行目を読み込みます。それを行うためのパラメーターの設定は、次の [1]~[3] のとおりです。

[1] Excelリーダーをワークスペースに追加するとき、リーダーのパラメーター設定画面でワークシートをひとつ(図の例では「H12.4~6」)だけ選択し、フィールド名の行 (Field Names Row) の指定を解除します。これにより、Excelの列名 A, B, C, ... が各列のセルの値を格納する属性の名前となります。

ここで全てのワークシートを選択することもできますが、そうすると、全ワークシートについて、ひとつずつ Field Names Row の指定を解除しなくてはなりません。それは手間なので、ここではひとつのワークシートだけで設定を行い、後述するように、リーダーをワークスペースに追加した後で、全ワークシートを選択し直すことにしました。







































[2] 上記のパラメーター設定によってリーダーをワークスペースに追加した後、キャンバスに現れたフィーチャータイプで次の設定をします。

  • Merge Feature Type をチェック(任意のフィーチャータイプ名=ワークシート名を受け入れる)
  • Table セクションの Start Row, End Row で読込開始、終了行番号を指定
  • Format Attributes (フォーマット属性) タブで "fme_feature_type" (フィーチャータイプ名=ワークシート名を格納する属性) と "xlsx_row_id" (行番号を格納する属性) を現す (Expose)



























[3] そして、リーダーの Feature Types to Read (読み込むフィーチャータイプ) パラメーターで全てのフィーチャータイプ(ワークシート)を選択します。以上の設定により、実行時には、全てのワークシートの 5~10行目 が読み込まれることになります。

















TestFilter: 行番号 ("xlsx_row_id" の値) に応じて、フィーチャー(レコード)を「産業用」(5, 8行)、「業務用」(6, 9行)、「計」(7, 10行)に振り分けます。

AttributeRenamer x 3: TestFileter による振り分け先に応じて、C(販売電力量)、D(販売額)、E(単価)の属性名を変更します。

Aggregator: "fme_feature_type"(フィーチャータイプ名=入力ワークシート名=調査期)と "A"(需要家: 「特別高圧」または「高圧」)の組み合わせが同じフィーチャーを集約します。

ここまでで、需要家(出力先ワークシート)別に、調査期(出力先行)単位で [産業用, 業務用, 計] x [販売電力量, 販売額, 単価] 9項目のデータを属性として持つフィーチャーに変換されました。


【ステップ2】
















StringSearcher: "fme_feautre_type"(フィーチャータイプ名=入力ワークシート名)から「和暦年」と「四半期開始月」(1, 4, 7, 10)を抽出します。

AttributeManager: 入力ワークシート名から抽出した「和暦年」と「四半期開始月」に基づき、「西暦年」と「四半期番号」(1, 2, 3, 4)を求めるとともに、以後の処理では使わない属性を削除します。

AttributeManager パラメーター設定画面






































Sorter: フィーチャーの出力順を調査期(年、四半期番号)の昇順にします。

[XLSXW] (Excel) ライター: 需要家(属性A:「特別高圧」と「高圧」)別のワークシートに振り分けてフィーチャーを出力します。「年-四半期」列には、AttributeManager で作成した「西暦年」と「四半期番号」の値を含む文字列 "<yyyy>-Q<n>" (yyyy: 年, n: 四半期番号)を作成して出力しました。

ワークスペースの実行結果は、冒頭に示した変換結果: 電力需要調査結果「一覧表」のとおりです。

2017-06-13

既存ファイルのZip圧縮・アーカイブ

FME のライターのうち、ファイルにデータを保存するタイプのフォーマットに対応するものは、出力先データセットを zip 形式で圧縮・アーカイブするオプションを持っており、必要に応じて、データ変換結果を圧縮・アーカイブした zip ファイルとして出力することもできます。

また、複数のファイルで構成される既存のデータセットについて、データの内容を変更する必要はないが、複数の zip ファイルに振り分けて圧縮・アーカイブしたいという場合には、Directory and File Pathnames [PATH] リーダーFile Copy [FILECOPY] ライターが効果的に使えます。

前回の「土地利用細分メッシュ (ラスター) データセットの作成 (2017-06-10)」のワークスペース例は、ディスクシステムのひとつのフォルダー内に、変換結果の全てのGeoTIFFファイル (全国の陸部のほぼ全域をカバーする1次メッシュ175区画分の土地利用細分メッシュGeoTIFFファイル x 175個) を出力しました。

今回は、それらのGeoTIFFファイルがすでにディスクに保存されている状態を出発点として、[PATH] リーダーと [FILECOPY] ライターを使い、1次メッシュ区画単位で、当該メッシュ区画に対応するGeoTIFFファイルといくつかの共通ファイルを zip 形式で圧縮・アーカイブするワークスペース例を紹介します。

圧縮・アーカイブ前: common_files フォルダーに共通ファイル一式、geotiff_files フォルダーに全てのGeoTIFFファイルが保存されているものとします。
















圧縮・アーカイブ後: 1次メッシュ単位で、GeoTIFFファイル1個 (1次メッシュ1区画分) と共通ファイル一式のセットをひとつの zip ファイルにアーカイブします。zip ファイル名は、アーカイブした GeoTIFF ファイル名の拡張子を除いた部分に ".zip" を連結した文字列、つまり L03-b-09_<1次メッシュコード>.zip とします。















FME 2017.0.1.1 build 17291

FMEワークスペース例



















1. GeoTIFFファイル情報の取得

[PATH] リーダー: 次のパラメーター設定により、geotiff_files フォルダー直下の全ての *.tif ファイルについて、フルパス、ファイル名等の情報を読み取り、それらの情報を属性として持ったフィーチャー (1ファイルあたり1フィーチャー) を出力します。
  • データセット: <geotiff_files フォルダーのパス>
  • Path Filter: *.tif
  • Allowed Path Type: FILE
AttributeKeeper: [PATH] リーダーは、指定したフォルダー内の各ファイルについて多数の情報を読み取り、path_ で始まる名前のフィーチャー属性に格納しますが、全ての情報が常に必要とは限りません。ここでは、後の処理で必要な情報を格納した次の属性のみを残し、他は削除しました。
  • path_unix: ファイルのフルパス (UNIXスタイル: パス区切り文字としてスラッシュ / を使用)
  • path_rootname: 拡張子を「除く」ファイル名 -> 拡張子 .zip を付加して zip ファイル名として使用
  • path_filename: 拡張子を「含む」ファイル名 -> 当該 GeoTIFF ファイルのアーカイブ名として使用

[PATH] リーダーが出力するフィーチャーは、パス区切り文字としてバックスラッシュ \ (日本語版Windowsのエクスプローラー等では、半角¥で表示される) を使用するWindowsスタイルのフルパスを格納した path_windows 属性も持っています。Windowsは、スラッシュ / とバックスラッシュ \ のどちらもパス区切り文字として取り扱うので、ワークスペースをWindows版FMEのみで実行する場合は path_unix, path_windows のどちらでも構いませんが、Mac版やLinux版で実行する場合は path_unix を使う必要があります。


2. 共通ファイル情報の取得

[PATH] リーダー: 次のパラメーター設定により、common_files フォルダー直下の全てのファイル、サブフォルダーについて、フルパス、ファイル・フォルダー名等の情報を読み取り、それらの情報を属性として持ったフィーチャー (1ファイル・フォルダーあたり1フィーチャー) を出力します。
  • データセット: <common_files フォルダーのパス>
  • Path Filter: *
  • Allowed Path Type: ANY
AttributeKeeper_2: 共通ファイルは各GeoTIFFファイルのアーカイブ先 (zip ファイル) に一緒にアーカイブするので、必要な情報は、ファイル・フォルダーのフルパスと、拡張子を「含む」ファイル名・フォルダー名のみです。
  • path_unix: ファイル・フォルダーのフルパス 
  • path_filename: 拡張子を「含む」ファイル名・フォルダー名
ListBuilder: 共通ファイルの情報を持ったフィーチャーはGeoTIFFファイル数分コピーし、それぞれのアーカイブ先 (zipファイル) に送る必要があります。この例では、共通ファイル情報を持ったフィーチャーは2つ (ファイル x 1, サブフォルダー x 1) 読み込まれますが、ListBuilder によってそれらの情報をリスト属性として持つ単一のフィーチャーに集約し、次の FeatureMerger で、拡張子を除くGeoTIFFファイル名 (アーカイブ先zipファイル名) を格納した path_rootname 属性を持った全てのフィーチャーに結合することにしました。

FeatureMerger: Requestor ポートに path_rootname (拡張子を除くGeoTIFFファイル名 = アーカイブ先zipファイル名) 属性を持ったフィーチャー (1フィーチャーが1GeoTIFFファイルに対応する)、Supplier ポートに ListBuilder が出力する単一のフィーチャー (全ての共通ファイル情報をリスト属性として持つ) を入力し、Join On パラメーターの Requestor 側、Supplier 側の両方に同じ定数値 (例: 1) を設定することにより、Supplier フィーチャーが持っているリスト属性を、全ての Requestor フィーチャーに無条件に結合します。

ListExploder: 共通ファイル情報を格納しているリストを展開することにより、個々のファイル・フォルダー単位のフィーチャーに分解します。各フィーチャーは、AttributeKeeper_2 によって残された2属性 path_unix, path_filename の他、path_rootname 属性 (拡張子を除くGeoTIFFファイル名 = アーカイブ先zipファイル名) を持つことになります。


3. Zip圧縮・アーカイブ

Sorter: GeoTIFFファイル情報を持っているフィーチャー、共通ファイル情報を持っているフィーチャーを、path_rootname 属性の値 (拡張子を除くGeoTIFFファイル名) でソートします。必須ではありませんが、次の [FILECOPY] ライターの処理の効率が、わずかながら良くなることが期待できます。

[FILECOPY] ライター: [FILECOPY] ライターは、ディスクシステム内のファイルのコピーや移動を行うものですが、出力先データセット名 (フォルダー名) の末尾に ".zip" を付加することにより、ファイルを zip 形式で圧縮・アーカイブした zip ファイルを作成する機能もあります。後述するように、Fanout Dataset オプションによって属性値に応じて出力先を振り分ける場合も、振り分け先のフォルダ名末尾に ".zip" を付加することにより、振り分け先ごとの複数の zip ファイルが作成できます。

まず、ライターフィーチャータイプの User Attributes タブで、filecopy_source_dataset 属性: コピー元ファイル・フォルダーパス、filecopy_dest_filename 属性: コピー先ファイル・フォルダー名 (zip 圧縮・アーカイブする場合は zip ファイル内のアーカイブ名) の値 (Value) を設定します。この例では、filecopy_dest_dataset は使用しません。

[FILECOPY] ライターフィーチャータイプ User Attributes の設定

  • filecopy_source_dataset <- path_unix: コピー元ファイルパス
  • filecopy_dest_filename <- path_filename: コピー先ファイル名 (アーカイブ名)























次に、Navigator ウィンドウで、次のようにライターのパラメーターを設定します。

[FILECOPY] ライターのパラメーター設定 (Navigator)


























ここで重要なのは、次の2つのパラメーターです。

Fanout Expression: path_rootname 属性の値 (拡張子を除くGeoTIFFファイル名) と ".zip" を連結する文字列式を設定することにより、GeoTIFFファイル、共通ファイルが、その zip ファイルに振り分けられてアーカイブされます。

Copy Source Folder: Yes を設定することにより、コピー元としてフォルダーのパスが渡されたときに、そのフォルダーと下位のファイル・フォルダーが、元の階層構造と同じ構造でコピーされます。このパラメーターは、[FILECOPY] ライターをワークスペースに追加する際に、Add Writer 画面 [Parameters] ボタンからパラメーター設定画面を開いて設定することもできます。


ワークスペースの実行結果は、冒頭「圧縮・アーカイブ後」スクリーンショットに示したとおりです。


FME のライターで作成した zip ファイルを Windows 7 エクスプローラーの「すべて展開」コマンドによって展開すると、展開後のファイル名内の日本語文字が文字化けすることがあります。これは Windows 7 の既知の問題に起因するものであり、次のウェブページで Microsoft から配布されている修正プログラムをインストールすることによって解消できます。

Japanese characters in file names are displayed as garbled text after you decompress a .zip file in Windows 7 or in Windows Server 2008 R2

Windows 8 以降では、この問題は生じません。

2017-06-10

土地利用細分メッシュ (ラスター) データセットの作成

つい最近、国土数値情報ダウンロードサイトで「土地利用細分メッシュ(ラスタ版)」データ (平成26年度 GeoTIFF形式, JGD2000) が公開 (2017-06-02) されたことに気がつきました。

従来から提供されてきたShapefile形式やXML形式よりも軽量で扱いやすいGeoTIFF形式のデータが提供されるようになったのは歓迎です。FMEユーザーとしては、過去のデータについても、今回公開された「ラスタ版」データ (平成26年度) と対比可能なラスターデータセットの作成にチャレンジしたくなります。

「メッシュデータのラスター化 - 土地利用細分メッシュ (2015-07-18)」および「同 [更新] (2017-04-07)」 で示したように、Shapefile形式またはXML形式の土地利用細分メッシュデータをラスター化すること自体は簡単なのですが、ウェブブラウザの操作によって、国土数値情報ダウンロードサービスサイトから全国分のデータファイルをひとつひとつダウンロードするのは面倒で退屈、かつ、非生産的な作業であり、これに人員が拘束される時間はできるだけ短くしたいところです。

幸いなことに「国土数値情報 Web API(試行版)」が試行運用中です (2017-06-10現在) 。それを使えば、国土数値情報の任意のデータセットについてデータ (zipファイル) のダウンロードURLを取得することができ、データダウンロードも自動化できます。

以下、同APIを使って平成21年度 (2009年度) の「土地利用細分メッシュ」データ(XML形式、Shapefile形式のデータを圧縮・アーカイブした zip ファイル)のURLを取得し、全国分のデータ (1次メッシュ175区画=175ファイル) のダウンロードからラスターへの変換までの処理を自動化するワークスペース例を掲げます。

国土数値情報 Web API(試行版)の仕様、利用規約については、国土数値情報ダウンロードサービスサイトの Web API 関連ページを参照してください。

FME 2017.0.1.1 build 17291

FMEワークスペース例
Step 1: 土地利用細分メッシュデータ (zip) ダウンロード












HTTPCaller: 国土数値情報 Web API によって、平成21年度 (2009年度) の土地利用細分メッシュデータ (zipファイル) のダウンロードURL情報を記述したXML文書を取得します。リクエスト先のURLやAPIのパラメーター、レスポンス (XML) のスキーマについては、「国土数値情報 Web API(試行版)」で公開されているAPI仕様を参照してください。

HTTPCaller の主要なパラメーター
Request:
    Request URL: http://nlftp.mlit.go.jp/ksj/api/1.0b/index.php/app/getKSJURL.xml
    HTTP Method: GET
    Query String Parameters:
        
Name Value
appId ksjapibeta1
dataformat 1
identifier L03-b
fiscalyear 2009
Response:
    Save Response Body To: Attribute
    Response Body Attribute: _response_body
    Response Body Encoding: auto-detect

レスポンスとして得られたXML文書には、次のように zipFileUrl 要素の内容 (テキスト) としてデータ (zipファイル) のダウンロードURLが記述されています。

国土数値情報 Web API (URL情報取得) レスポンスの一部 (XML文書 item 要素)
(略)
  <item>
  <identifier>L03-b</identifier>
  <title>土地利用細分メッシュ</title>
  <field>国土(水・土地)</field>
  <year>2009</year>
  <areaType>4</areaType>
  <areaCode>3036</areaCode>
  <datum>1</datum>
  <zipFileUrl>http://nlftp.mlit.go.jp/ksj/gml/data/L03-b/L03-b-09/L03-b-09_3036-jgd_GML.zip </zipFileUrl>
  <zipFileSize>0.53MB</zipFileSize>
  </item>
  <item>
  <identifier>L03-b</identifier>
  <title>土地利用細分メッシュ</title>
  <field>国土(水・土地)</field>
  <year>2009</year>
  <areaType>4</areaType>
  <areaCode>3036</areaCode>
  <datum>2</datum>
  <zipFileUrl>http://nlftp.mlit.go.jp/ksj/gml/data/L03-b/L03-b-09/L03-b-09_3036-tky_GML.zip </zipFileUrl>
  <zipFileSize>0.53MB</zipFileSize>
  </item>
(略)


XMLXQueryExtractor: 次のパラメーター設定によって、ダウンロードURLのうち "*jgd_GML.zip" (JGD2000のデータ) のみを抽出して zipFilUrl{} リストに格納しました。

XMLXQueryExtractor の主要なパラメーター
XQuery Type:
    XQuery Input: XQuery expression
    XQuery Expression:
        for $x in //item/zipFileUrl[matches(text(), 'jgd_GML\.zip$')]
        return $x/text()
XML Source:
    XML Input: Attribute specifying XML
    XML Attribute: _response_body
    Remove Source XML Attribute?: No
Results:
    Write XML Header?: No
    Return Value: List attribute
    List Result Attribute: zipFileUrl


ListExploder: zipFilUrl{} リストを展開し、個々のダウンロードURL文字列 (zipFileUrl 属性) ごとのフィーチャーを作成します。


TempPathnameCreator: ダウンロードしたデータの保存先ファイルパス (拡張子 .zip) を作成し、_pathname 属性に格納します。このトランスフォーマーが作成するファイルパスは、FMEが変換実行時に使用する作業用フォルダ内のユニークな場所を指し、ワークスペース内でそのパスに保存したファイルは、変換終了後に自動的に削除されます。Windows の場合、FMEの作業用フォルダは、デフォルトでは "<Windows ユーザー>/AppData/Local/Temp" ですが、環境変数 FME_TEMP によって他の場所を設定することもできます。


HTTPCaller_2: zipFileUrl に HTTPリクエスト (Get メソッド) を発行し、レスポンス (zipファイル) を TempPathnameCreator で作成したファイルパスに保存します。

HTTPCaller_2 の主要なパラメーター
Request:
    Request URL: @Value(zipFileUrl)
    HTTP Method: GET
Response:
    Save Response Body To: File
    Save Response Body To File:
        Create a New File Per Feature: No
        Output Filename: @Value(_pathname)
        File Path Attribute: _pathname


Step 2: 土地利用細分メッシュデータ XML から GeoTIFF への変換














Step 2 は、基本的には「2017-04-07記事」のワークスペースと同じです。以下、異なる点のみ説明します。

FeatureReader: Step 1 の HTTPCaller_2 が出力するフィーチャーは、ダウンロードしたzipファイルパスを _pathname 属性として持っています。FeatureReader (Format: XML) で HTTPCaller_2 が出力したフィーチャーを受け取り、データセット名として、_pathname 属性の値とXMLファイル名のパターンで構成されるファイルパス文字列 @Value(_pathname)/L03-b-*.xml を指定することにより、実行時にzipファイルが自動的に展開され、ファイル名のパターンが一致するXMLデータが読み込まれます。リーダーパラメーターの設定は「2017-04-07記事」のXMLリーダーと同じです。


AttributeFileReader: XMLデータに記述されている各土地利用種別コードとラスター画素値の対応は、次のようなJSON文書によって定義し、AttributeFileReader によって読み込んで _codeMap 属性に格納しました。各土地利用種別コード ("0100", "0200", "0500", ...) に対応する画素値 (10, 20, 50, ...) は、国土数値情報「土地利用細分メッシュ(ラスタ版)」(平成26年度)の画素値の定義と一致させました。

土地利用種別コードと画素値の対応定義 (JSON) 平成21年度, 平成26年度用
土地改良種別コード (メンバー名) とそれに対応する画素値 (値) のペアで構成されるJSONオブジェクト
{
  "0100": 10,
  "0200": 20,
  "0500": 50,
  "0600": 60,
  "0700": 70,
  "0901": 91,
  "0902": 92,
  "1000": 100,
  "1100": 110,
  "1400": 140,
  "1500": 150,
  "1600": 160,
  "0000": 0
}


PythonCaller:「2017-04-07記事」のワークスペース例の PythonCaller に設定したスクリプトのうち、KsjLanduseSubdivisionMeshRasterizer クラスの input メソッド定義の一部を次のように変更し、上記JSON文書 (_codeMap 属性値) を Python の辞書インスタンスに変換して使用しました。

    def input(self, feature):
        # 土地利用種別コードから画素値への変換用辞書
        import json
        codeToValue = json.loads(feature.getAttribute('_codeMap'))
        (以下略)



AttributeFileReader_2: パレット (画素値に対応するRGBカラー) の定義は、国土数値情報「土地利用細分メッシュ(ラスタ版)」(平成26年度)のパレットと一致させました。

パレット定義 (テキスト) 平成21年度, 平成26年度用
RGB24
0 255,255,255
10 255,255,0
20 255,204,153
50 0,170,0
60 255,153,0
70 255,0,0
91 140,140,140
92 180,180,180
100 200,70,15
110 0,0,255
140 255,255,153
150 0,204,255
160 0,255,0


FeatureWriter: 作成した全てのラスター (1次メッシュ175区画分) を、出力先データセットとして指定したフォルダ内に GeoTIFF 形式のファイル (ファイル名: "L03-b-09_<1次メッシュコード>.tif") で出力します。座標系 (JGD2000緯度経度) も、ここで設定しました。


変換結果: 土地利用細分メッシュ (平成21年度) 1次メッシュ5339, 5340区画
この画像は、上記ワークスペースによって作成したラスターデータセット: 1次メッシュ175区画=175GeoTIFFファイルのうち2区画分をモザイクし、PNG形式に変換したものです。解像度 (横: 800x2区画=1600px, 縦: 800px) 、各画素値、パレットの定義は、元のGeoTIFFファイルの内容と同じです。1/10分割メッシュ区画 (東西4.5秒 x 南北3秒) が1画素 (正方形) に相当するので、地物が南北方向に引き延ばされたように見えます。

















上記ワークスペース例では、FeatureWriter の出力先データセットとして指定したフォルダ内に、変換後の全ての GeoTIFF ファイルが出力されます。仮に、平成26年度のデータと同じ要領でそれらのGeoTIFFファイルをウェブサイトで提供することにするならば、1次メッシュ区画ごとに、GeoTIFFファイルと土地利用種別コード一覧表 (HTMLドキュメント) をzip形式で圧縮・アーカイブする必要があります。

当初、Step 2 までで作成されたGeoTIFFファイルを圧縮・アーカイブするプロセスを Step 3 としてワークスペース例に加えることも検討しましたが、ケーススタディの記事としてボリュームが大きくなりすぎることから取りやめました。

また、ダウンロード -> 変換の過程で不測のエラーが生じる可能性はゼロではないので、実務上も、GeoTIFFファイルの作成までで工程を区切り、全ファイルが正常に作成されたことを確認してから圧縮・アーカイブ工程に移ることにした方が良いと思われます。

多数のファイルの圧縮・アーカイブを自動化することも、FMEの効果的な利用例として面白いテーマです。それについては、「既存ファイルのZip圧縮・アーカイブ (2017-06-13)」をご参照ください。

2017-05-31

統計ダッシュボードAPIによるデータ取得

2017年5月12日、「統計ダッシュボード」(総務省統計局)の運用が始まりました。同サイトでは、ウェブブラウザ画面上で主要な統計データのグラフや表が閲覧できるほか、HTTP プロトコルによるリクエスト/レスポンスの方式によって XML, JSON, またはCSV形式のデータを取得するための API も提供されています。

FMEでは、HTTP プロトコルによる API が公開されているウェブサービスには、一般に HTTPCaller トランスフォーマーによって接続することができ、また、XML, JSON, CSV 形式のデータは自在に変換できます。

ここでは、統計ダッシュボードAPIによるデータ取得の実験として、[時間] 2010~2015年の年別・[地域] 都道府県別の [系列] 人口総数をXML形式で取得し、PostgreSQL データベース (次の2テーブル) に格納するワークスペース例を掲げます。

出力先データベーステーブル
テーブル名列名内容
population region_code 地域コード
year
population 人口総数
region code 地域コード
name 地域名称 (都道府県名)

統計ダッシュボードAPIの利用規約、技術的な仕様 (リクエストURL・パラメーター、レスポンスのスキーマ等) については、統計ダッシュボードサイトのAPI関連ページを参照してください。

FME 2017.0.1.1 build 17291

FMEワークスペース例
























HTTPCaller: Creator から出力されたフィーチャーを受け取ったタイミングで、次のパラメーター設定に基づいて2010~2015年の年別・都道府県別の人口総数データをXML形式で取得するためのHTTPリクエスト (GETメソッド) を発行し、レスポンスとして得られたXML文書を属性として持ったフィーチャーを出力します。

HTTPCaller の主要パラメーター
Request:
    Request URL: http://data.e-stat.go.jp/dashboard/api/1.0/Xml/getData
    HTTP Method: 
GET
    Query String Parameters:

Name Value 備考
Lang JP 取得するデータの言語 (JP:日本語)
MetaGetFlg Y メタ情報取得有無のフラグ (Y:取得する)
IndicatorCode 0201010000000010000 系列コード
RegionLevel 3 地域レベル (3:都道府県)
TimeFrom 2010CY00 時間軸 (開始)
TimeTo 2015CY00 時間軸 (終了)
これらの API パラメーターの全部または一部を、通常の REST API パラメーターの書式で Request URL 文字列に含めても構いません。

Response:
    Response Body Attribute: 
_response_body


XMLFragmenter: XML文書から全ての VALUE 要素をXML断片として抽出するともに、それらを平坦化して regionCode 属性の値 (地域コード)、time 属性の値 (時間軸コード)、および VALUE 要素の内容 (系列の値。この例では人口総数) を取得します。

VALUE 要素 (XML断片) の例
<VALUE indicator="0201010000000010000" unit="090" stat="20020101" regionCode="01000" time="2010CY00" cycle="3" regionRank="3" isSeasonal="1" isProvisional="0">5506419</VALUE>

XMLFragmenter の主要パラメーター
XML Source:
    XML Attribute: _response_body
Feature Paths Configuration:
   
 Elements to Match: VALUE
Customize Attributes:

    Flatten Options:
        Enable Flattening: <checked> 
Expose Attributes:
   
 Attributes to Expose:
        VALUE.regionCode (地域コード)
        VALUE.time (時間軸コード)
        VALUE (系列の値。この例では人口総数)


XMLXQueryExploder: 地域の識別子 (コードと名称) を記述している CLASS 要素をXML断片として抽出します。統計ダッシュボードから取得したXML文書に含まれる CLASS 要素は、いくつかの CLASS_OBJ 要素の子要素としてグループ化されており、何を記述している CLASS 要素であるかは、親の CLASS_OBJ 要素の id 属性によって識別する仕組みになっています。ここでは、地域コードと地域名称を記述している CLASS 要素 (親要素の id 属性値が regionCode であるもの) のみを抽出したいので、断片化の対象とするXML要素の条件を XQuery 式によって自由に指定できる XMLXQueryExploder を使いました。

CLASS_OBJ 要素 (地域) と CLASS 要素の一部
<CLASS_OBJ id="regionCode" name="地域">
    <CLASS code="01000" name="北海道"/>
    <CLASS code="02000" name="青森県"/>
    ....
</CLASS_OBJ>

XMLXQueryExploder の主要パラメーター
XQuery Type:
    XQuery Expression: //CLASS_OBJ[@id="regionCode"]/CLASS
XML Source:
    XML Attribute: 
_response_body
    Remove Source XML Attribute?: Yes
Results:
    Result Attribute: 
_result


XMLFlattener: XMLXQueryExploder によって抽出された各 CLASS 要素を平坦化して、code 属性の値 (地域コード) と name 属性の値 (地域名称。この例では都道府県名) を取得します。

XMLFlattener の主要パラメーター
XML Source:
    XML Attribute: _result
Flatten Paths:
    
Elements to Match: CLASS
Expose Attributes:
    
Attributes to Expose:
        CLASS.code (地域コード)
        CLASS.name (地域名称)


最後に PostgreSQL ライターをワークスペースに追加、2つのフィーチャータイプ (population, region) で出力先テーブルのスキーマを定義し、XMLFragmenter, XMLFlattener トランスフォーマーと接続しました。

VALUE 要素の time 属性の値 (時間軸) は、単位が暦年の場合、"<西暦年>CY00" という書式のコードで記述されています。そのため、"population" ライターフィーチャータイプの User Attributes タブで、時間軸コードの先頭から4文字 (西暦年の部分) のみを year 列に書き込むように設定しました。

PostgreSQL ライター | population フィーチャータイプ (テーブル) の属性 (列) 定義













注: FME  2017.0 では、上の図のように、ライターフィーチャータイプ User Attributes タブ (Value 列) でも属性値を調製できるようになりました。


PostgreSQL データベースに対する SQL クエリ (問い合わせ) と結果テーブルの例:
2015年の都道府県別人口総数 (pgAdmin III クエリーツール)





























2017-05-12

メッシュコードに基づくメッシュポリゴンの作成

e-Stat (政府統計の総合窓口) では、「平成22年国勢調査500mメッシュ」データ (平成22年国勢調査結果に基づいて推計された1/2分割地域メッシュ区画単位の男女別人口総数及び世帯総数) が公開されており、1次メッシュ区画ごとにCSV形式のデータファイルがダウンロードできます。

e-Stat 平成22年国勢調査500mメッシュデータ例















各行1列目には1/2分割地域メッシュのメッシュコード (9桁の数字) が記述されています。ここでは、そのメッシュコードに基づいて、当該メッシュ区画を表す矩形ポリゴン (以下「メッシュポリゴン」と言います) を作成することにより、CSV形式のデータをGIS用のデータセットに変換するワークスペース例を掲げます。

FME 2017.0.1.0 build 17288

FMEワークスペース例












[CSV2] リーダー: 平成22年国勢調査500mメッシュデータ (CSV形式) を読み込む
AttributeSplitter: 1/2分割地域メッシュコード (9桁文字列) を各要素に分割する
AttributeManager: 属性名変更、メッシュ区画南西隅の座標計算等
2DBoxReplacer: メッシュポリゴン作成
Inspector: FME Data Inspector に結果出力 (確認用)

1次メッシュ区画 5339, 5340 の変換例 (FME Data Inspector による表示)
背景は地理院地図 (淡色地図) です。

















[CSV2] リーダーでは、ソースデータの2行目に記述されている列名を属性名とし、3行目以降の各行をフィーチャーとして読み込みました。ただし、第2行1列目は空文字列なので、[CSV2] リーダーは1列目にデフォルトの属性名 "col0" を与え、これにメッシュコードが格納されることになります。

1/2分割地域メッシュコードは9桁の数字で構成されており、各桁を AABBCDEFG で表せば、以下の式でメッシュ区画の南西隅の座標 (xmin, ymin) を求めることができます。

xmin (秒) = (100 + BB) x 3600 + D x 450 + F x 45 + dx
ymin (秒) = AA x 2400 + C x 300 + E x 30 + dy
dx = G が 2 または 4 ならば 22.5, そうでなければ 0
dy = G が 3 または 4 ならば 15.0, そうでなければ 0

メッシュ区画の幅と高さは一定 (1/2分割地域メッシュの場合は 22.5秒 x 15.0秒) なので、南西隅の座標からメッシュ区画を表す矩形の範囲が決定できます。この例では、AttributeSplitter によってメッシュコード (9桁の数字) を AA, BB, C, D, E, F, G の7つの部分に分割したうえで、AttributeManager で上記式によって南西隅の座標を求める方針です。

AttributeSplitter の Delimiter or Format String パラメーターには、Attribute to Split パラメーターで指定した属性に格納されている文字列を分割するための区切り文字 (列) (カンマなど) を指定するほか、#s#s#s... (#は抽出する部分文字列の文字数) という書式によって、先頭から順番に連続して抽出する部分文字列の文字数を指定することもできます。いずれの場合でも、分割・抽出後の部分文字列は、List Name パラメーターで指定した名前のリスト属性に格納されます。

この例では、AttributeSplitter のパラメーターを次のように設定し、_c{} リストに AA ~ G を格納しました。

AttributeSplitter パラメーター設定画面

_c{} リストに格納される値
_c{0} = AA
_c{1} = BB
_c{2} = C (0 ~ 7)
_c{3} = D (0 ~ 7)
_c{4} = E (0 ~ 9)
_c{5} = F (0 ~ 9)
_c{6} = G (1 ~ 4)











AA ~ G が得られたので、次のような設定をした AttributeManeger によってメッシュ区画の南西隅の座標が求められます。ここで、G の値 (1 ~ 4) に応じて決まる dx, dy は、Conditional Value (条件別の値) として設定しています。また、浮動小数点数の計算誤差を回避する観点から、計算過程では数値を整数で扱った方が有利であるため、ここで求める座標の単位は1/10秒としました (そのため、式中の定数は、前述の式における定数の10倍になっています)。

AttributeManager パラメーター設定画面

























2DBoxReplcer は、パラメーターとして左下 (南西) 隅の座標、右上 (北東) 隅の座標を設定することにより、それらを結ぶ直線を対角線とする矩形ポリゴンを作成します。ここで、[1/10秒] から [度] への換算もあわせて行いました。

2DBoxReplacer パラメーター設定画面


















ワークスペース例では、Inspector を接続して FME Data Inpector によって変換結果を確認しているだけにしていますが、ベクターデータの格納をサポートする適当なフォーマットのライターをワークスペースに追加すれば、そのフォーマットの「平成22年国勢調査500mメッシュ」データセットが作成できます。


文字列の分割または部分文字列の抽出

文字列をカンマなどの特定の区切り文字 (列) で分割する場合は、AttributeSplitter を使うのが一般的です。

メッシュコードのように、文字数が固定されている複数の部分文字列で構成されている文字列も、上記例のように AttributeSplitter によって個別の部分文字列に分割することができますが、分割後の部分文字列の一部しか利用しない場合には、SubstringExtractor も選択肢のひとつになります。

特定の区切り文字 (列) がなく、部分文字列の文字数も固定されていない場合でも、元の文字列の構成パターンを正規表現で表すことができる場合には、StringSearcher (部分式を含む正規表現) によって文字列の分割や部分文字列の抽出ができます。

例えば、ひとつのデータセット内で、1/2分割地域メッシュコードが9桁の連続した数字だけでなく、数字以外の区切り文字 (ハイフンなど) で1次 ~ 3次、1/2分割メッシュコードを連結した文字列 (例: 5339-23-45-1) で記録されている可能性もある場合には、次のような正規表現を設定した StringSearcher によって、どちらの書式であってもメッシュ区画南西隅を求めるために必要な各要素を抽出することができます。

^(\d\d)(\d\d)\D*([0-7])([0-7])\D*(\d)(\d)\D*([1-4])$