ラベル 地理情報標準 の投稿を表示しています。 すべての投稿を表示
ラベル 地理情報標準 の投稿を表示しています。 すべての投稿を表示

2015/10/07

文系のための「地理情報標準」(1)

様々な情報管理システムの中でも、特に空間情報を扱うシステムのことを、
地理情報システム(GIS: Geographic Information Syste)あるいは、
地理空間情報システム(GIS: Geospatial Information System)と呼ぶ。

略称を使うとどちらも同じで、略称でGISと呼ばれているものがある。

教科書的な定義では、「地理空間情報を取得、保存、統合、管理、分析、
伝達して、空間的意思決定を支援するコンピュータベースの技術」である。
(村山裕司・柴崎亮介(騙)(2008). 『シリーズGIS第一巻 GISの理論』, 朝倉書店.)

おそらく、このGISというのが一体どのようなシステムなのか、
今となっては、当たり前すぎる技術となっていて、
明確な定義をすること自体が困難なものとなっている。

良い言い方ではないけれど「高度な電子地図システム」としておく。

実を言うと、このGISの歴史を述べるのは少々厄介で、
学界、政界、財界、のそれぞれの視点という思惑によって、
その起源は異なったコンテキストで語られている。

今では、GISのたとえ話で頻繁に登場するカーナビやGoogle Mapなど、
かつては、GISとは異なるものとして扱われていたし、
一緒にすることを極端に避けようする人も少なかった…。

いやいや、語り始めると本題に入れないので、今は、この話は置いておく。

そうそう、ここで議論するべきことは、GISの歴史や定義の話ではなく、
もっと技術的な内容である。すなわち、GISのデータ管理の話で、
特に、その基礎的な設計方法について整理するのであった。

GISにおけるデータを設計する上で一番厄介な問題は、
GISが現実世界のありのままの姿に対する人の認識、
というのが基礎的な概念として必要となっている点である。

つまり、「哲学的な話」と「技術的な話」の両方が必要となってくる。
実際に、GISのデータに関わる国際標準『地理情報標準』は、
かなり抽象的な認識論に基づいていて、多くの技術者を悩ましている。

おそらく、この国際標準を理解できる素養があるのは、
高度な技術力を持つ理工系エンジニアではなく、高い教養と
観念論的な思考が可能な文系出身のエンジニアであると思う。

というのは、GISでは対象となる現実世界をコンピュータ上に
モデル化する必要があって、したがって、現実世界の捉え方
というのが、どうしても避けられない問題となってくる。

そうした捉え方の一つが「オブジェクト指向GIS(OOGIS)」であり、
『地理情報標準』という標準は、このOOGISの考え方に基づいているのだが、
まずは、この標準について、簡単に整理しておくことにする。

そもそも、先ほどから『地理情報標準』と言っているけれど、
国際的には「ISO 19100」シリーズと呼ばれていて、
国際標準化機構という機関が国際標準として策定している。

この国際標準化機構は、ISOと言う略称で知られていて、
生産品、製品、素材、材料、プロセスに関する規格が中心に扱われる。
有名なものに、クレジットカードの寸法やネジの規格などがある。

ISOでは、技術委員会の下で標準化が進められていて、
ISO 19100シリーズの場合は、211番目の技術委員会「ISO/TC 211」で
検討が重ねられている。

この委員会での議論の範囲は、「地球上の場所に直接的、
あるいは間接的に関連付けられた事象の情報について、
体系的な一連の標準として確立すること」である。

また、具体的には、「異なるユーザーやシステム間で電子化された
データを管理、取得、加工、分析、接続、表現、伝達するための
方法論、ツール、サービスを明確化する」ことを目的としている。

何やら、難しい話に聞こえるが、要するに、地理的な情報に
関わる情報の取扱全般におよぶプロセスを整理するための
国際標準を作るのがISO/TC 211の目的となっている。

ISO/TC 211では、さらに、議論するべき内容によって、
ワーキング・グループが置かれていて、
それぞれのワーキング・グループで標準化を行っている。

  • WG 1: フレームワークと参照モデル
    • ISO 19101: 参照モデル
    • ISO 19103: 概念スキーマ言語
    • ISO 19104: 専門用語
    • ISO 19105:  適合性と検証
    • ISO 19121: 画像とグリッドデータ
  • WG 2: 地理空間データモデルと操作(解散)
    • ISO 19107: 空間スキーマ
    • ISO 19108: 時間スキーマ
    • ISO 19109: アプリケーション・スキーマの規則
    • ISO 19123: 被覆の幾何と機能のためのスキーマ
  • WG 3: 地理空間データの管理(解散)
    • ISO 19110: 地物カタログの方法論
    • ISO 19112: 地理識別子による空間参照
  • WG 4: 地理空間サービス
    • ISO 19116: 位置情報サービス
    • ISO 19117: 描画法
    • ISO 19118: 符号化
    • ISO 19119: サービス
    • ISO 19125: シンプル・フィーチャー・アクセス
    • ISO 19128: Webマップ・サーバーのインタフェース
    • ISO 19136: Geography Markup Language(GML)
    • ISO 19142: Webフィーチャー・アクセス
    • ISO 19149: 地理情報のための権利表示言語(GeoREL)
    • ISO 19161: 測地系
    • ISO 19164: 登録サービス
  • WG 5: プロファイルと機能に関する標準(解散)
    • ISO 19106: プロファイル
  • WG 6: 画像
    • ISO 19120: 機能的標準
    • ISO 19129: 画像、グリッド、被覆の枠組み
    • ISO 19130: 地理情報取得のための画像センサーのモデル
    • ISO 19159: リモートセンシング・センサーのキャリブレーション
    • ISO 19163: 画像とグリッドデータの内容コンポーネントと符号化規則
  • WG 7: 情報の公共性
    • ISO 19115: メタデータ
    • ISO 19122: 人材の資格と認定
    • ISO 19126: フィーチャーの概念辞書と登録
    • ISO 19137: 空間スキーマのためのコア・プロファイル
    • ISO 19139: メタデータの実装
    • ISO 19144: 分類システム
    • ISO 19150: オントロジー
    • ISO 19152: 土地管理ドメイン(LADM)
    • ISO 19160: 住所表記
    • ISO 19165: デジタルデータとメタデータの保存
  • WG 8: ロケーション・ベース・サービス(解散)
    • ISO 19132: ロケーション・ベース・サービスの参照モデル
    • ISO 19133: ロケーション・ベース・サービスの追跡とナビゲーション
    • ISO 19134: ロケーション・ベース・サービスのマルチモーダルルーティング
  • WG 9: 情報管理
    • ISO 19111: 座標による空間参照
    • ISO 19127: 測地系コードとパラメータ
    • ISO 19131: データの製品仕様書
    • ISO 19135: 項目登録のための手順
    • ISO 19145: 地点の表現の登録
    • ISO 19146: クロス・ドメインの語彙
    • ISO 19153: 地理空間の電子的な権利管理参照モデル(GeoDRM RM)
    • ISO 19156: 観察と計測
    • ISO 19157: データ品質
    • ISO 19158: データ提供の品質の確実性
    • ISO 19162: WKTによる座標参照系の表現
  • WG 10: ユビキタスと公共アクセス
    • ISO 19147: 移送ノード
    • ISO 19148: 線形参照
    • ISO 19154: ユビキタス公共アクセス参照モデル
実際には、同じ標準を異なるワーキング・グループで議論されていたり、
審議内容が別のワーキング・グループに移管されたり、
あるいは、解散したワーキング・グループも存在するので、
ここに書かれている通りでは無い部分もある。

そういった、細かい点は置いておいても、これを見てみると解るように、
様々な内容について議論されていて、各ワーキング・グループで、
関連する標準について議論されていることがわかる。

残念ながら、私自身、全ての標準の中身を理解できている訳ではないし、
これらの標準の全てを理解できている人は、世界でも居るのか…。

ところで、本ブログでは、ISO 19100 シリーズとは呼ばずに、
『地理情報標準』という言葉を使っているが、
その理由に関しても、少し、説明しておく必要がある。

実は、国際的な標準としてはISO 19100として通用しているけれど、
日本国内に同標準を適用するには、日本にのみ関わる部分や、
日本の特有の事情で補足あるいは追加するべき点がある。

そういった部分を精査して、国内向けに別の標準が作られている。
その標準は、日本工業規格(JIS)のJIS X7100シリーズとして、
国内での普及が始まっている。

さらに、国土地理院は、JISX7100シリーズのうち、
標準の中でも実用上必要な部分を取り出して体系化し、
『地理情報標準プロファイル(JPGIS)』を策定している。

これとは別に、かつては、国土地理院と国内民間企業が共同研究として、
ISO/TC 211の議論に準拠して国内への適合を検討していた時期があって、
その成果が『地理情報標準(JSGI)』としてまとめられている。

細かい内容を見てみると、当然のことながら、それぞれに相違はあるけれど、
全体的な体系や考え方は同じなので、総称して『地理情報標準』としている。
この呼び方の方が、内容がよく解るというメリットもある。

とりあえず、『地理情報標準』については、この辺りで止めておいて、
少しずつ、同標準の考え方というか、OOGISの考え方について、
次回から、理解を進めていきたい。

2012/11/03

自分のための「基盤地図情報からshapefile」(2)

さて、前回は、基盤地図情報をshapefile に変換するための前準備として、
PyShp の設定方法について述べた。今回は、実際に実装を試みる。

ちなみに、自分用を前提としているので、エラー処理はしていないし、
何せ、Python を使いはじめて、9ヶ月くらい?
我流で書いているので正しい書き方かは解らない。

大したコードでは無いので、流用は自由。一言、書いて頂ける有難いが、
無断転用も含めてご自由に。ただし、一切、責任は取りませんので悪しからず。
その代わり、バグがあったら教えて下さい。

なお、pyshp のライセンスは、MITライセンスとなっている。詳細は以下にあるので、
本当に、利用したいという人は、一読しておいた方が良いかもしれない。

http://code.google.com/p/pyshp/

さてさて、では早速、今回実験したコードを見てみる。
最初に、宣言部分と今回利用したライブラリから。
このブログは自分ため。だから、親切な解説はしない。解らない人は自力で。

ソースコードは以下からダウンロード可能(2012年12月11日現在)。
https://docs.google.com/open?id=0B9OtIs5BjRVha1VmemsxTVAta2s

最初は、クラスの定義から。色々と書いていくと、どうも煩雑になってしまう。
そのため、今回は、UMLクラス図を使って、クラスを整理してみた。
まずは、基本となるGMLオブジェクトに関わるクラス。


ここでは、GMLの「幾何オブジェクト」と「時間オブジェクト」を定義している。
地理情報標準の空間スキーマ(ISO19107)に基づいているので、少々複雑。
しかも、突貫で作っているので、多少の不都合はあるが、今はこの状態で。

これらのクラスの最大の役割は、GML形式のXML要素が与えられた時に、
必要な属性情報を、入力された要素から抽出しインスタンス化すること。
クラスとして定義することで、汎用的に使えるようになっている。

このクラス図が読めることが前提なので、細かい説明はしない。
いずれ、APIリファレンスも作る予定なので、併せてみれば理解できるハズ。
UMLもPython も解らない人は、少々、厳しいか。

あえて、注釈をつけておくとしたら、Gmlクラスの役割について。
ネームスペースの部分を毎回書くのが面倒なので、定数として定義しているだけ。
以下のように実装してある。

class Gml(object):
__slots__ = ("__nsGml")

def __init__(self):
self.__nsGml = '{http://www.opengis.net/gml/3.2}'

def __getNsGml(self):
return self.__nsGml

getNsGml = property(__getNsGml)


一応、簡単な解説のみ。class Gml(object): の部分でGmlというクラスを定義し、
このクラスが、Python におけるObject型を継承しているを示している。

__slots__ の部分では、このクラスで利用可能な属性名を限定している。
Python という言語、かなり奇妙な言語で、勝手に属性を加えることができてしまう。
この機能を封じるために、__slots__の部分で使用する属性を限定する。

__init__ の部分は、このクラスが実装された際に、行う初期化処理。
__nsGml という属性に、'{http://www.opengis.net/gml/3.2}' という値を代入している。
属性前に、「__」 が付いているのは、この属性がプライベートになっていることを示す。

属性の読み書きは、setter と getter で設定することができる。ちなみに、
__nsGml 属性は、getterのみを設定している。つまり、読み取り専用属性。
最初に初期化して以降、この値を変更することはできない。

この属性を外部から読む時の名前、つまり属性名は、getNsGml であり、
外から呼び出すときには、この名前を使って、{http://www.opengis.net/gml/3.2}
という文字列を取得する。

他のクラスに関しては...ダラダラと書いているだけなので、説明する必要はなかろう。

次に、基盤地図情報をマッピングするためのクラスの定義。
まぁ、基本的には、このような感じになる。「やっつけ仕事」で実装したので、
製品仕様書を確認しながら修正した方が良さそうである。現段階は、実験段階。



ここで定義されているクラスも、基本的には、GML形式のデータを読み込み、
入力されたXML要素から、属性情報を抽出するのが主たる目的である。
これに加えて、Shapefile に出力機能を持たせている。

現段階では、行政区画(AdministrativeArea)、行政区画代表点(AdministrativePoint)、
行政区画線(AdministrativeBoundary)、道路構成線(LoadEdge)の4クラスを実装できるようになっていて、
それぞれ、これらのクラスを集約するクラスを持っている。

3つの集約クラス、AdministrativeAreas、AdministrativePoints、AdministrativeBoundaries、
は、複数のインスタンスを集約するのが目的。このクラスが、shapefile への出力や、
XML形式のデータを読み込んだり、といった処理をまとめて行う。

重要な点は、これら3つのクラスの初期化の部分。基本的に、どれも同じなので、
以下は、AdministrativePoints の初期化部分の例。

class AdministrativePoints(JPGISClass):
__slots__ = ("__admPts", "__shp")

def __init__(self, shape = None):
JPGISClass.__init__(self)
self.__admPts = set()
self.__shp = shapefile.Writer(shapefile.POINT)
self.__shp.field('FID')
self.__shp.field('LFSPANFR')
self.__shp.field('DEVDATE')
self.__shp.field('ORGGILVL')
self.__shp.field('ORGMDID')
self.__shp.field('VIS')
self.__shp.field('TYPE')
self.__shp.field('NAME')
self.__shp.field('ADMCODE')

初期化する際に、PyShp のshapefile オブジェクトを実装し、
さらに、このタイミングで、shapefile の形式と、
必要な属性(フィールド)を設定しておく。

いずれ、shapefile からのインスタンス化も、と考えていて、
引数に、shape=None を定義しているが、このコードを見て解る通り、
現段階では、全く意味を成していない。

それは、まぁ、良いことにする。


さらに、3つのクラスを集約するクラスは、「JPGISClass」というクラスを継承している。
このクラスでは、唯一、convertEncという関数が定義されているだけ。
この関数は、GML形式のXMLデータのエンコードをShift-JISからUTFに変換する。

基盤地図情報は、Shift-JIS を使っているが、そのおかげで色々と厄介な問題が発生する。
UTF-8に変換するための処理がここで定義されている。Shift-JIS は、やっぱり困る。
このクラスを実装すると、以下のようになる。

class JPGISClass(object):
def convertEnc(self, filename):
# Read a original xml file as a text file format.
txt = open(filename).read()

# Convert the text file to unicode.
unic = unicode(txt, 'cp932')

# Convert the encoded unicode text file to the UTF-8 file.
utf8 = unic.replace("Shift_JIS", "utf-8").encode('utf-8')

# Return converted text string.
return utf8



とにかく、この部分の処理が重要。Python は、日本語には優しくない。
特に、Shif-JIS というローカライズド・エンコーディングの扱いは大変。
ファイルサイズの問題があるとは言え、この点は、何とかしてもらいたいものである。

さて、ダラダラと、各クラスとその説明を書いても仕方ないので、
ここで、全体的な処理の流れを確認してみる。
ダウンロード済みのデータが、以下のようになっているとして...

/path/to/download/PackDLMap
├── FG-GML-14101-05-Z001.zip
├── FG-GML-14101-06-Z001.zip
├── FG-GML-14101-08-Z001.zip
     ・
     ・
     ・
├── FG-GML-14118-05-Z001.zip
├── FG-GML-14118-06-Z001.zip
└── FG-GML-14118-08-Z001.zip


今回、書いたPython スクリプトの実行方法は以下の通り。

python cis.jpgis.py -d '/path/to/download/PackDLMap' 

このように、するだけで、ファイルの解凍も含めて自動処理してくれる。
我ながら、便利なツール。これができれば、QGIS から直接読み込むこともできる。
まぁ、QGISを使いたいのであれば、別の方法もあるらしいが。

さて、このスクリプトが、実際に何をしているかというと、
def main(argv): で定義されている部分が、最初に動作しはじめて、
zipファイルの解凍、xmlデータのパース、shpefileへの書き出しを順次行う。



要するに、別のルーチンに飛んで処理を行なっているだけ。
個々の処理は、それほど、複雑では無いので、まぁ、見たら解るだろう。
そもそも、これは、あくまで「自分のため」のメモ程度。

さてさて、最終的に上手く出力できると、以下のような図が出てくるハズ。
この図は、横浜市のデータを一括ダウンロードして、shapefile に変換したもの。
同様に、クラスを拡張すれば、他のデータも変換できるハズ。
ただし、一点だけ、注意すべきことがある。
現在のところ、ドーナツポリゴンが処理できていない...。
PyShp の仕様を読んでも良く解らない。

やはり、PostGIS に実装した方が良いのかもしれない。
それにしても、shapefile は、どうも使い勝手が悪いので、好きに慣れない。
DBに入れる方が、早いかもしれない。

2012/10/30

自分のための「基盤地図情報からshapefile」(1)

GISを日常的に使っている人で、「基盤地図情報」を知らない人は居まい。
日本全国の基本的なGISデータは、ここで入手するのが一般的。
最近では、OSMの方を利用している人も多いようであるが...。

基盤地図情報は、GISの国際標準「ISO19100シリーズ」という標準に準拠していて、
国土地理院からは、XML形式のファイルをダウンロードして利用できる。
まぁ、ここまでは、どこにでも書いている話。

2012年10月30日現在、ダウンロード可能なXMLファイルは、
GML形式(ISO19136)と、符号化規則「附属書8」形式(ISO19118)の二種類の形式で、
何れかの形式のものをダウンロードして変換しGISソフトで利用する。

この変換には、通常、国土地理院の「基盤地図情報閲覧コンバートソフト」を使うが、
実は、このツールは、Windows専用であり、Mac ユーザとUbuntuユーザは苦労する。
ということで、今回は、OSに依存しないツールの開発を試みる。

今回は、Ubuntu上で、Pythonを使って変換する方法を考えてみる。
大雑把な流れは、次の通り。
  1. 最初にXMLの要素をパースする。
  2. 基盤地図情報クラスをインスタンス化する。
  3. shapefile に変換する。
2.は必ずしも必要は無いが、再利用を考えるとこの方が良い。
問題は、shapefile に変換する部分であるが、この部分に関しては、
pyshp というライブラリを使う。とりあえず、インストール。

インストールは、easy_install というツールを利用するのが便利であるが、
このツールを利用するためには、予め、python-setuptools が必要となる。
念の為に、このツールのインストール方法もメモしておく。

sudo apt-get install python-setuptools
sudo easy_install pyshp

さて、実は、 pyshp というライブラリ、日本語が入ると色々とエラーが発生し、
上手く、shapefile に変換することができない。
そこで、インストールしたソースを修正し、日本語に対応できるようにする。

まず、インストールされているソースを開く。
バージョンが変わっているかもしれない。注意する。
とりあえず、この記事の執筆時点では、以下のパスの下に存在していた。

/usr/local/lib/python2.7/dist-packages/

もちろん、インストールの仕方によって場所は異なる可能性がある。
まぁ、この記事を読んでいる人であれば、余計な話か。
とにかく、編集する。「vi」と言いたいところであるが「gedit」での編集。

sudo gedit /usr/local/lib/python2.7/dist-packages/pyshp-1.1.4-py2.7.egg/shapefile.py

最初に修正するべき箇所は、エンコーディングのオマジナイ。
とにかく、以下の二行を、ソースの先頭に入れておく。

#!/usr/bin/python
# -*- coding: utf-8 -*-

次に、エラー回避を行うためのコードを追加。376行目辺りとなるか?
def __dbfRecords(self): という関数の部分を修正する。
size = int(size) の後ろに、value = str(value.encode("utf-8")) を追加するだけ。

ようするに、エンコーディングエラーの対策。
とりあえず、以下のようになる。

def __dbfRecords(self):
"""Writes the dbf records."""
f = self.__getFileObj(self.dbf)
for record in self.records:
   if not self.fields[0][0].startswith("Deletion"):
f.write(b(' ')) # deletion flag
   for (fieldName, fieldType, size, dec), value in zip(self.fields, record):
fieldType = fieldType.upper()
size = int(size)
value = str(value.encode("utf-8"))
if fieldType.upper() == "N":
   value = str(value).rjust(size)
elif fieldType == 'L':
   value = str(value)[0].upper()
else:
   value = str(value)[:size].ljust(size)
assert len(value) == size
value = b(value)
f.write(value)

これで準備完了。あとは、変換するためのプログラムを書くだけ。
自分でコーディング人は、ここまでの情報があれば良いかもしれない。
ということで、次回は、実際にコーディングをしてみる。