BETA

ワールドファイルのはなし

投稿日:2020-04-02
最終更新:2020-04-02

はじめに

PostGISラスタの仕様に触れる前に、ラスタってそもそも何なの?というのを若干触れたいと思います。特にワールドファイルについて説明したいと思います。

ラスタデータとは

ラスタデータ=ビットマップ+地理参照情報+NoData値

ラスタデータは、地面を規則正しい格子に区切り、格子ごとにデータを入れたものです。データは数値スカラまたは数値ベクトルに限られ、ベクトルの次元は全ての格子で同じでなければなりません。

規則正しい格子をピクセルに見立てるとビットマップになりそうにかんじたあなたはするどい。ラスタデータは、データ表現上はビットマップデータと同じです。JPEGはRGB各1バイトのベクトルになります。PNGはRGBAのベクトル、MAGはRGBI各1ビットのベクトルです。なのでラスタデータになりえます。

しかしながら、一般のビットマップデータには地理参照情報が入っていないので、地図上のどの領域に表示していいか全く分かりません。

そこで、ビットマップに地理参照情報を付与する必要があります。地理参照情報を付与する際に、外部ファイルを使う場合と内部に埋め込む場合とがあります。

ワールドファイルは、外部ファイルを使う場合によく使われるものです。ビットマップデータのサフィックス(ドット以降)を変えたファイル名にしたワールドファイルを置いておくと、それを地理参照情報として読み込んでくれるようになっています。

内部に埋め込む場合としては、たとえばGeoTIFFというものがあります。なんてことはない、TIFFのタグで地理参照情報を埋め込んでいます。

地理参照情報

地理参照情報として必要なものは、ビットマップの隅のひとつ(原則として左上隅)の位置と、ピクセル1つあたりの地図上でのサイズです。ここまで出たら、各ピクセルが地図上のどこを指すかは簡単な掛け算と足し算からなる計算で算出できますね。

これだと拡大縮小とシフトだけですが、もう少し拡張して、アフィン変換と考えると、回転も含めた地理参照が可能になります。

| a11 a12 a13 |  
| a21 a22 a23 |  
|   0   0   1 |  

という行列を適用すると、ピクセルの位置(px, py)と地図上の位置(ux, uy)との関係は、次のようになります。

ux = a11*px + a12*py + a13  
uy = a21*px + a22*py + a23  

NoData値

一般的なビットマップは人に見せるものであることが多いかと思います。「ラスタデータ」の場合は、計算に使うデータセットとしての側面もあるので、欠測は欠測として保存できなければなりません。PNG+ワールドファイルでは対応できませんが、ラスタデータとしてしっかりしているフォーマットでは、NoData値(ベクトル)が設定できるようになっています。NoData値は、そのデータセットでは取りえない値をデータ作成者が選択します。

たとえば、国土地理院発行の標高データでは、-9999.0 をNoData値としています。標高 -9999.0[m] は、海水面から1万メートルといったら、いくつかの海溝ぐらいで、陸域ではありえない値です。

NoData値が納められているピクセルの扱いはデータ利用者が決めます。たとえば、集計計算から除外したり、周りのピクセルの値にあわせたり、固定値を持たせたり、さまざまあることでしょう。ラスタデータを表示する場合には、表示しないようにするのが一般的です。いずれにしても、NoData値の値をそのまま使うことは、ほとんどありません。

「バンド」という言葉

ラスタデータでは、頻繁に「バンド」という言葉が出てきます。「バンド」とは、個々のピクセルが持つベクトルの成分です。
一般的なビットマップでは R, G, B の3次元ベクトルを持ち、個々の色、つまりチャネルが、ラスタデータのバンドに相当します。

PNGでは符号なし4バンドともに1バイト整数ですが、ラスタデータの場合は、バンド数がまちまちですし、空中写真が必ずしもR,G,Bのバンドとは限りません。たとえば植生指数を計算するには、RとIR(近赤外線)を使用するため、G, Bバンドは不要となります。

また、バンドごとの数値型もさまざまで、符号付き4バイト整数でも、倍精度浮動小数点数であったりします。

ワールドファイル

ワールドファイルには空間参照系情報もNoData値も無い

ワールドファイルは、アフィン変換のパラメータしか持っていません。

よって、空間参照系ID (SRID) に関する情報は持ちません。GISでファイルをロードする際に、空間参照系をユーザが手動で指定する必要があります。

また、NoData値も持っていません。

対して、GeoTIFFなど、通常のラスタデータは、空間参照系IDもNoData値も持つことができます。

ワールドファイルの書式

ワールドファイルは、6行からなり、各行に数値スカラが入っています。これは実はアフィン変換の行列の要素を単純に書いているだけです。

先ほど提示した行列

| a11 a12 a13 |  
| a21 a22 a23 |  
|   0   0   1 |  

は、ワールドファイルでは、次のようになります。

a11  
a21  
a12  
a22  
a13  
a23  

a22は負数を取るのが普通

ピクセル座標系は左上を原点とし、Xは右方向に、Yは下方向に進む、左手系です。対して、たいていの空間参照系は、左下を原点とし、Yについては上方向に進み、右手系になります。

このため、a22 (Y方向のセルサイズ)は、たいてい負の数を取ります。

ピクセル座標系の原点

ワールドファイルでは、ピクセル座標系の原点は、左上隅ピクセルの中心です。左上隅ピクセルの左上隅ではありません。

ワールドファイルで遊んでみよう

まず「ラスタデータ」のもととなるビットマップファイルをダウンロードして下さい。

なんてことはない、20px * 20px のPNG画像です。

これを 0.png とします。

a21, a12が0の場合

ワールドファイルがたとえば次のようになっているとしましょう。

1.pgw

1  
0  
0  
-1  
10  
20  

行列で表現すると次の通りとなります。

|  1  0 10 |  
|  0 -1 20 |  

ピクセル(左上隅原点)の座標値を(px, py)とすると、投影された座標値は次のようになります。

ux =  1 * px + 10  
uy = -1 * py + 20  

これは、左上隅ピクセルの中心が(10,20)で、セルサイズが横方向に1, 縦方向に1となることを示しています。

同じフォルダで、先ほどダウンロードした 0.png を単純に複写して 1.png を作り、GISで読み込んでみてください。次のように表示されると思います。

これに、(10,20)の位置に円を描いてみます。

左上隅のピクセルの中心あたりに円が描かれました。

次に、横長にしてみましょう。

2.pgw

2  
0  
0  
-1  
10  
20  

(ux,uy)(px,py)の関係は次のようになります。

ux =  2 * px + 10  
uy = -1 * py + 20  

同じフォルダで、先ほどダウンロードした 0.png を単純に複写して 2.png を作り、GISで読み込んでみてください。

たしかに横長になりました。

回転する例

1.png + 1.pgw の画像を、反時計回りにcos(t) = 0.6, sin(t) = 0.8となるt(おおむね52度)で回転させてみましょう。

2*2の回転行列は、右手系(右下隅原点の座標系)だと、数学の教科書で出てくると思います。

| cos(t) -sin(t) |  
| sin(t)  cos(t) |  

右から順に、Yを逆転して左手系(左上隅座標系)から右手系にして、tだけ回転させ、(10, 20) ぶんシフトさせてみます。

| 1 0 10 |   | 0.6 -0.8 0 |   | 1  0 0 |   | 0.6  0.8 10 |  
| 0 1 20 | * | 0.8  0.6 0 | * | 0 -1 0 | = | 0.8 -0.6 20 |  
| 0 0  1 |   |   0    0 1 |   | 0  0 1 |   |   0    0  1 |  

合成された行列の3行目を捨てると、次のようになります。

| 0.6  0.8 10 |  
| 0.8 -0.6 20 |  

これをワールドファイルにすると、次のようになります。

3.pgw

0.6  
0.8  
0.8  
-0.6  
10  
20  

(ux,uy)(px,py)の関係は次のようになります。

ux = 0.6 * px + 0.8 * py + 10  
uy = 0.8 * px - 0.6 * py + 20  

これもやはり、同じフォルダで、先ほどダウンロードした 0.png を単純に複写して 3.png を作り、GISで読み込んでみてください。

回転しているのが見えます。

1.png と重ねると、次のようになります。

スキューのある行列

Y方向へのスキューを行ってみましょう。

4.pgw

1  
0.5  
0  
-1  
10  
20  

繰り返しになりますが、(ux,uy)(px,py)の関係は次のようになります。

ux = 1.0 * px + 10  
uy = 0.5 * px - 1 * py + 20  

これは、pxが1増えるたびに、uxは1.0, uyは0.5増えて、pxはux, uyの双方に影響を与えることになります。pyが1増えるたび、uyは1減るだけで、pyはuxのみに影響を与え、uxに影響は与えません。

同じフォルダで、先ほどダウンロードした 0.png を単純に複写して 4.png を作り、GISで読み込んでみてください。

Yの上方向にスキューが行われました。

1.png と重ねてみましょう。

ここでは示しませんが、2行目と3行目を入れ替えるとX方向へのスキューとなります。

a21, a12 は 0 にする方が無難

ワールドファイルは読むけど、a21, a12 はまともに読まずに0にしてしまうGISもあるとか聞いてますので、使わなくていいなら使わない方が無難です。スキューを持つ画像については、リサンプリングしていいなら、リサンプリングを使ってスキューを持たない画像にした方がいいです。ただ、NoData値を含むラスタデータについては、リサンプリングが許されず、どうしてもスキューを持つラスタデータも必要になる可能性もあります。

おわりに

ラスタデータというものがあり、ラスタデータは、ビットマップデータと地理参照情報、NoData値からなることを示しました。また、地理参照情報付与手法としてワールドファイルとよぱれるものがあり、これはアフィン変換の行列の要素値を記述していることを説明しました。

ラスタデータの基本は、どんな種類のデータでも、これと大差ありません。

ただし、GISで扱うラスタデータは一般的なデジカメ写真をはるかに超えるピクセル数を扱うことが結構ありますので、ステップアップするごとに苦しくなっていくと思います(利用者も計算機も)。

本記事のライセンス


この記事は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@boiledorange73の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう