はじめに
日記機能を使い始めてまもなく3か月、数回書いてみて、使い方がかなり分かってきた。
山歩きでは、しばしば、等高線がほしくなる。 これまではOSM地図を国土地理院地図に切り替えていた。 一度表示した国土地理院のタイル地図画像ファイルはSDカードに保存しているが、 全zoomが保存されているわけではなく、山歩きではスマホの電波も届かないことが多く、 実時間でのダウンロードができず、使い勝手が悪い。
国土地理院の標高タイルについても、事情は同じであるが、 5mメッシュDEMの zoom 15 にあたるため、必要範囲を予めダウンロードしておくこと は容易である。
そこで dem5a を使って、OSM地図に等高線を付け加えることを考えた。
QGISなどを使って、オフラインで等高線を求める記事[3]は他にもあるが、 自分が欲しいのは、自作OSM地図アプリに組み込むプログラムである。 これに関するものは少なかった。
最初に試した簡易プログラム[5]
void getContour(Bitmap bmp) { for (int j = 0; j < 255; j++) { for (int i = 0; i < 255; i++) { float c = ele[j][i]; // check pixel float r = ele[j][i+1]; // right pixel float d = ele[j+1][i]; // down float g = ele[j+1][i+1]; // diagonal int ic = (int)(c/20); int ir = (int)(r/20); int id = (int)(d/20); int ig = (int)(g/20); boolean flag = (ic != ir) || (ic != id) || (ic != ig); bmp.setPixel(j, i, flag ? 0xffff0000 : 0x00000000); } } }
東京の高尾山の山頂を含む zoom 15 のタイルの等高線を描いた結果を下に示す。 その下に、国土地理院の標準地図タイルを載せた。
プログラムは非常に簡単であるが、ギザギザが目立つ。線の太さは1画素であり、 これより細く見える線は簡単に描けない。
4画素の比較で塗りつぶしは常に右上にしているのがギザギザの原因であろう。 4画素中、等高線が一番近くを通る画素を塗りつぶすように変えれば、ギザギザは いくらか目立たなくなるかもしれない。
しかし、zoom 16以上をどうするか、簡単な方法を思いつかない。
CONREC A Contouring Subroutine[7]
これは、40年近く前に発表された古い技術である。デジタル地図用ではなく、 CAD(ワイヤーフレームモデル)用かもしれない。
元々はC、Basic、Fortran言語プログラムであったが、その後、多くの人が 色んな言語に移植しているので、実用性は高いと推察される。
文献[7]にアルゴリズムが述べられているが、自分はまだ十分には理解していない。
Java言語に移植したプログラムを Android OS上の自作OSM地図アプリに 組み込んだ。Android Javaと本家Javaでは特にUI回りで互換性がないが、 等高線を求めるアルゴリズムの部分については、何も修正する必要はなかった。
zoom 15
5mメッシュの標高タイルDEM5aは zoom 15 の256x256画素の中心点(多分)の 標高を表すものである。座標で言えば、0.5 から 255.5 までとなる。 下の図をよくよく見れば、タイル境界で等高線の途切れがある。 これはあるタイルの最終座標 255.5 と次のタイルの 0.5 の間に1画素分の間隙がある ためである。いずれ、両隣の標高タイルの値を使って、このタイル境界での途切れをなくしたい。
タイル境界での途切れをなくした[2024.10.6]
zoom 15で言えば、座標値 0.5 から 255.5 ではなく、前と後ろに1点を加えて、 座標値を -0.5 から 256.5 とした。
標高タイルはキャッシュするように変えた。 スマホの場合、1画面に最大3x4タイルが表示される。周辺の標高タイルを使うために、 画面全体としては、最大で5x6標高タイルの標高値を使って等高線を算出する。
ファイル読み込み時間は増えるが、等高線算出処理は 256x256配列が 258x258配列に変わるだけで、処理時間の増加は僅かである。
zoom 16以上
zoom 16の場合には、zoom 15の DEM5a を縦横2分割して、 128x128地点の標高データを使って等高線を描くことになる。
zoom 17の場合、DEM5aを縦横4分割して、64x64地点の標高データを使う。
zoom が大きくなるほど、使うデータが少なるため、誤差は大きくなるであろう。 下に、zoom 17、zoom 19 の例を載せた。等高線は乱れはない。
二つの図ではタイル境界での等高線のとぎれもない。 実は、zoom 17の場合、64x64個のデータで等高線を描いているのではなく、 66x66個のデータで等高線を描いている。 zoom 15で言えば、座標値 0.5 から 255.5 ではなく、前と後ろに1点を加えて、 座標値を -0.5 から 256.5 としている。 -0.5~0 および 256.0~256.5 の部分はタイルからはみ出すが、描画されないだけであり、 エラーが起きるわけではない。
zoom 17で言えば、 -1~64、63~128、127~192、191~256 とする。 -1は前の標高タイル、256は次の標高タイルとなる。中間のオーバラップは 同じ標高タイルからのデータ取り出しのため、サポートが簡単である。 したがって、ここでは等高線の途切れが起こらないようにしている。
zoom 17
zoom 19
山頂の標高
等高線とは関係ないが、DEM5a とOSMの natural=peak の ele と 比較するのは簡単である。 1m以内の誤差はいいとして、ほんの少し調べただけでも、5m前後の誤差が散見された。何故だろう。 著名な山では誤差が少ないが、無数の無名の山頂には誤差が含まれているのかもしれない。ただ、一般の人にとっては、数mの誤差は問題ではない。
今後の予定
前後の標高タイルに含まれる標高値も使い、タイル境界での等高線とぎれをなくする。 (10月7日に完了)
100m、200mといった太い等高線には数値を書き込む(10月8日に完了)。
数値(label)は下図のような書き方は簡単、これでいいであろう。タイル毎、数値毎に、 1回だけ書き込んでいる。
リファレンス
[1] 基盤地図情報1m・5mメッシュDEM 標高データ(数値PNGタイル)[2] 標高タイルの詳細仕様
[3] 【実習編】非専門家のためのQGIS ~DEMから等高線を描く~
[4] 等高線表示機能(試験公開)
[5] Leaflet で地理院標高タイルから等高線を描いてみる
[6] 地図情報の新たな整備・更新技術の開発-5m メッシュ DEM から地図情報レベル 25000 の等高線を作成する手法の検討(第 1 年次)-
[7] CONREC A Contouring Subroutine
[8] Conrec.java
Discussion