トップ地図アプリMap1 > 地図アプリMap1開発記録

地図アプリMap1開発記録

2024.5.10 レンダリングに着手

レンダリングは複数スレッドで同時実行するが、バイナリレコードファイルの読み込みはシリアルに行う。 複数のスレッドが同じバイナリレコードファイルを使うことが多いため、リクエストも重なる。

現在の方法をそのまま使う前に、より簡単な方法がないか検討したい。

地図表示アプリにはタイルキャッシュ、サーバーアプリにはブロックキャッシュを置く。 共に配列とする。

地図表示アプリからタイルへのリクエストは broadcast である。主にタイルアドレスと優先度である。 地図ソースやズームの変更はリクエストから推測できるが、直接、その情報を broadcast する方が 簡単で正確であろう。

地図アプリはタイルキャッシュ(配列)を使い、リクエストがダブらないようにする。

サーバアプリはリクエストはキューに入れる。複数のレンダリングスレッドがリクエストを取り出す。 レンダリングではブロックファイルが必要となる。複数のスレッドが同じブロックファイルを使う。 ブロックキャッシュにあれば、それを使うだけで済むが、なかったときは、ブロックの読み込みが必要になる。

同じブロックファイルを使う後続のスレッドはどうするか? このリクエストはすぐには処理できないので、

2024.5.10 境界ボックスはバイナリレコードに含めておく[srv105]

境界ボックスの設定までをパソコンで行っておけば、読み込み時にはページ割り当てだけになる。 ファイルサイズは大きくなるため、読み込み時間は増えるが、読み込み処理が簡単になる。

14MB 0.36秒、12MB 0.34秒、もう一つのファイルではエラーが起きた。パフォーマンス上は大差はない。

読み込み用 byteバッファを大きくするとエラーはなくなったので、分割読み込みにバグがあるようだ。 読み込みバッファが大きい方が読み込み時間が短くなるので、なるべく大きなバッファがいいようだ。

最大ファイルサイズ分のバッファを用意して置き、一度に読込むことにした。

12MB 0.27秒、14MB 0.08秒、43MB 0.43秒となった。 測定の都度、時間は変動するが、概して、速くなった。

2024.5.9 読み込みテスト完了[srv104]

読み込み時のエラーはなくなった。読み込みレコードが正しいかどうかはいずれレンダリングで確認する。

読み込み時間は 9MB 0.5秒、33MB 0.9秒、10.5MB 0.27秒となった。

ガーベージコレクションの要因ではなくなるが、プログラムは複雑になる。 パフォーマンスが大幅に向上することもなさそう。ページ管理にするかどうかはよく考えたい。

2024.5.9 ページ管理

バイナリレコードファイルの管理をページ管理とした。

かなり読み込みが終わった段階でエラーが起きる。

Devider.javaのバグがないとは断定できないが、おそらくは、読み込みプログラムにバグがあるのであろう。 point、line、polygon ともかなりのレコードの読み込みに成功している。 multipolygon 自体はまだ読み込み経験がない。

レコードの先頭でエラーが検出される。

レコードの長さでレコード処理が終わったかチェックしてみよう。

2024.5.8 管理データ作成

管理データ作成を含めて、8.7MB 0.32秒、30MB 1.06秒となった。

実行時間は問題ないが、メモリ使用量が 9、30、10MB の読み込みで 27、82、98MB となった。およそ、ファイルサイズの2倍のメモリ使用量となる。

メモリをレコード部と管理部に分けるのは好ましくない。 例えば、固定長サイズのメモリをチェーンで管理する場合、レコード部と管理部が分かれていると面倒である。

short配列から int を取り出すのは簡単であるから、管理部もレコードに含めてしまう方がよい。

レコードの過半数が簡単な建物であることを考えると、境界ボックスを 4バイトx4=16バイトにするのには 抵抗がある。座標値列データの先頭座標との差分が大抵は2バイトで表せるであろうから、 全体を8バイトで表せることが多いであろう。

まずは、バイナリレコードファイルは現状のままで、byte配列をshort配列に移す時点で管理データを挿入する。 最初は管理部は固定長で length、minlon、minlat、maxlon、maxlat の 20バイトとする。 メモリは 40~50%増しでテストする。最終的には 20~30%増しとなろう。

50%増しのエリアを確保した場合、9、30、10MB の読み込みで 0.6秒 21MB、1.3秒 66MB、0.37秒 81MB となった。 最終的には 20~30%増しでよいので、メモリ使用量としてはこの方がよい。

管理部をファイルに含めた場合、ファイルサイズが大きくなり、読み込み時間が増えるため、 Android機で挿入する方がいいだろう。しかし、プログラム的には、レコードに含めておく方がトラブルは少ないだろう。

もう少し考えてから決めよう。 メモリは動的確保ではなく、2MBを100ブロックほどプールして置き、複数ブロックをつないで使うのがいいであろう。 読み込みブロックは一時的なものとして管理部を含めた最終ブロックとは別物にした方がよい。

2024.5.8 Androidでの読み込みテスト

8.7MBファイルの読み込み 1.24秒、30MBファイルで 4.1秒となった。

タグのパースをやめると、8.7MB 0.45秒、30MB 0.86秒となった。

ブロックファイルの読み込み時点ではタグのパースは要らないので、この段階のパフォーマンスは問題ない。

2024.5.7 wayレコードに osm_id4 を追加

デバッグ上、Way.java でタグ部に wayオブジェクトの osm_id を 4バイトで追加した。

エラーレコード#60794837で抽出しているタグは osm_id4=60794837(6バイト)、 building=office(4バイト)、height=115.4(6バイト)、name=NTTドコモ中野ビル(2+2+10*2=16バイト) である。計算上32バイトである。 tags_length=52バイトはおかしい。また、nameタグの抽出に失敗している。

Way.java でこのレコードの出力を調べてみる。

文字列末尾には 0x0000 があるのかも知れない。入力時の tags_length は 36バイトで、 osm_id を4バイトで加えたことで、出力は 42バイトとなった。

文字列には印字されていない何かが4バイトあるようだ。

parseWay
60794837: パーサ入力tags_length=36
building=office;height=115.4';name='NTTドコモ中野ビル';
60794837: パーサ出力tags_length=42

テストプログラムでは tags_length=52 となり、

4506: type=2 tags_length=52byte osm_id4=60794837;building=office;height=115.4';132096/132111 length=26
?NTTドコモ中野ビルY???
java.lang.ArrayIndexOutOfBoundsException: Index -257 out of bounds for length 96
        at Tag.parseTags(Tag.java:198)

building=officeからをダンプした。name のコードが 0x1a、文字列の長さ 22バイト (文字は10文字だが先頭に UTF-16を意味する FEFF が付く)

 0006 0100 002E 42E6 CCCD 001A 0016 FEFF FF2E FF34 FF34 30C9 30B3 30E2 4E2D 91CE 
 bldg offi heig   115.4   name 22 utf-16  N    T    T    ド  コ  モ   中  野

30D3 30EB 0059 533E E300 1548 6400 0001 0002 533E 5AF7 1548 425B FEB0 0E98 000A
 ビ  ル
[解決]

ix += 2 を ix += 4 に間違えていた。タグの最後に float型が一つの場合、 エラーが検出されないので、逆に、発見に手間取った。

   } else if (ikey <= Key.endFloatKey.ordinal()) {
     tag.fval = getFloat(sa, ix);
     ix += 2;	// 2024.5.7
     if (D) System.out.print(tag.key.name() + "=" + tag.fval + "';");

2024.5.6 place=unknownなど

place=unknown は ival の設定漏れだった。

もう一つのエラーはノード内挿によるノード数変化が設定されていなかったことだった。

しかし、ikey (0~94)が 不適切というエラーが残っている。

heightは初めてかも知れないが、widthは何度も現れている。

buildingであるから、多くのタグはないのが普通である。 tags_length=46バイトは大きい。文字列タグがあれば不自然ではない。 height の次が -257 というのもおかしい。 tags_length に間違いがあって、これでタグが終わりだとすると、 次はレコードの始まりであるから 0~3 でなければならない。

4505: type=1 tags_length=4byte highway=residential;
4506: type=2, num_nodes=29
4506: type=2 tags_length=46byte building=office;height=115.4';118616/118631 length=23
java.lang.ArrayIndexOutOfBoundsException: Index -257 out of bounds for length 95
<.pre>

heightタグの後ろは文字列タグであることが分かった。 何故、キー(2バイト)、文字列の長さ(2バイト)がないのかが分からない。

今の所、大半は buiding でエラーが起きる。しかし、highway でも起きる。 また、文字列タグとは限らないようである。

2024.5.6 place=unknown

全て place=unknown となっている。 Encoder.java からチェックしながらやり直す。 Parserでのチェックは分散してやりにくいが、Devider ではきちんとチェックしよう。

Parserでのタグチェックには問題はなさそう。

Deviderの入力時のタグチェックには問題はなさそう。

下のエラーはタグ部の長さが奇数であることからしておかしい。

タグの前の10ノードをチェックしたが異常はない。ただし、差分コードのため、万全のチェックができない。

 type=1 tags_length=84byte highway=primary;name='環八通り';name__en='Kampachi dori';
name__ja='環八通り';oneway=yes;ref='311';surface=asphalt;
 type=1 tags_length=35byte 1080/1096 length=17
 java.lang.ArrayIndexOutOfBoundsException: length=95; index=8120
     at com.example.wds.Tag.parseTags(Tag.java:47)

この場合、num_nodes=10 でエラーが起きた。これを 11 に変えてみると、先に進んだ、その後、 num_nodes=10 を 11 に変えたためと思われるが #26 でエラーが起きた。

なぜか #19 は 11 の所が 10 になったようだ。 恐らく、ノードを内挿したときの修正漏れか修正ミスであろう。

また、別のファイルでは相変わらず place=unknown が続く。Android ではなく、パソコン側で、 Devider の出力ファイルを読み込んでみよう。

 19: type=1, num_nodes=10
 type=1 tags_length=94byte highway=primary;layer=-1';name='環八通り';
name__en='Kampachi dori';name__ja='環八通り';oneway=yes;ref='311';surface=paved;tunnel=yes;
 20: type=1, num_nodes=13
 type=1 tags_length=34byte highway=secondary;name='笹目通り';oneway=yes;ref='443';
 21: type=1, num_nodes=3
 type=1 tags_length=86byte admin_level=6';highway=primary;name='環八通り';
name__en='Kampachi dori';name__ja='環八通り';oneway=yes;ref='311';
 22: type=1, num_nodes=20
 type=1 tags_length=90byte highway=primary;name='環八通り';
name__en='Kampachi dori';name__ja='環八通り';oneway=yes;ref='311';surface=paved;bus_route_id=7226579;
 23: type=1, num_nodes=15
 type=1 tags_length=90byte highway=primary_link;name='環八通り';
name__en='Kampachi dori';name__ja='環八通り';oneway=yes;ref='311';surface=paved;bus_route_id=7226601;
 24: type=1, num_nodes=2
 type=1 tags_length=128byte bridge=yes;bridge__name='方南八幡陸橋';highway=primary;
layer=1';name='方南通り(方南町交差点)';name__en='Honan dori';name__ja='方南通り';ref='14';
surface=paved;bus_route_id=3985982;bus_route_id=3985983;
 25: type=1, num_nodes=9
 type=1 tags_length=90byte highway=primary_link;name='環七通り';
name__en='Kannana dori';name__ja='環七通り';oneway=yes;ref='318';bus_route_id=915429;bus_route_id=2251563;
 26: type=1, num_nodes=10
 type=1 tags_length=6byte oneway=yes;surface=asphalt;
 Error: nRec=27, type=644
 0284 0001 0002 5337 CBE9 1546 00C4 EF87 90A7 0070 0031 002C 001D 000E FEFF 9AD8 

2024.5.5 バイナリレコードファイルの読み込みテスト

小さいファイルでは読み込みに成功したが、別のファイルではエラーが起きる。

 Error: nRec=2008, type=1012
 03F4 3EC5 04F1 0004 0007 0000 0001 0003 5108 C352 1496 1A72 16FB 115A 3677 166C 

データを見る限りではタグが続いているように見える。 「環八通り」であろう。タグのキー(name:enか?)や文字列の長さがない。

readAll開始 /storage/36C5-1401/Map1/japan-high12/3636/1612.dat
 Error: nRec=20, type=75
 004B 0061 006D 0070 0061 0063 0068 0069 0020 0064 006F 0072 0069 001B 000A FEFF 
 K    a    m    p     a    c    h    i        d    o    r    i        

全てのタグを表示した結果、以下のようになった。 全てのタグが同じレコードが他にあるが、そこではエラーは起きず、 このレコードの tags_length が実際よりも大きいような現象である。

highway=primary;name='環八通り';name__en='Kampachi dori';name__ja='環八通り';
oneway=yes;ref='311';surface=asphalt;

2024.5.2 タグをコード化

Deviderで、タグをコード化する。現時点では Key は1バイトで表せるが、将来を考え、 これまで通り、Key、Val とも2バイトとする。

Encoder でおこなっていたことを Devider で行う。

タグ部の長さも変わる。レコード長も算出しなおす。

差分コード化は必須ではないが、プログラムは簡単で、3割程度のファイル縮小が見込めることから、 いずれ、Devider で行う。

ByteEncoder をやめるため、ここでの処理の一部を Devider に移す必要があるかもしれない。

空間検索用管理データはファイルには持たず、Androidアプリで作成する。

2024.5.2 タグをコード化するとき java.nio.BufferUnderflowException が起きる

parsewayの先頭近くで起きている。文字列の長さ 25185、key がおかしい。

parseWay
remain=56, vlen=25185 key= ,   *     首都高速11号台場線  Dai
java.nio.BufferUnderflowException

ディレクトリの修正漏れであった。

これで Parser のチェックはひとまず完了した。

ここでのタグのコード化はチェックだけで、Parserの出力は Encoderの出力をそのままコピーしたものである。

2024.5.2 Encoderでのタグの範囲を変更

キーについては、enum Key に登録されているものに限定する。 値については 全てを対象とする。 enum Val にないものは Val.unknown とする。

kanto の場合、node0 54,493KB、way0 730,514KB、relation0 16,585KB に減った。 japanの総サイズは 3.70GB に減少した。

2024.5.1 Encoder

タグ部はkey、valともそのまま文字列として出力した。 Map4 では kanto の場合、node0 38,923KB、way0 626,490KB、relation0 15,071KB であったのが node0 99,910KB、way0 893,787KB、relation0 19,298KB に増えた。

japan の場合、総サイズは 3.19GB から 5.71GB に増えた。

全体のファイルサイズは 1.5倍程度になったが、途中段階のファイルであるため、問題はない。 レンダリングには使わない全てのOSMタグを含んでいるため、レンダリング対象のタグが増えても このファイルの作り直しは必要ない。このファイルでの文字コードは UTF-8 とした。

wayを構成するノードの座標値だけの nodeオブジェクトは way に吸収されるため、 node0 は小さい。

Encoder.java は300行弱の小さなプログラムである。

リファレンス