PC版では描画されるが、スマホ版では東北、関東の森林が描画されない。 しかし、kanto.osm では描画される。
低ズームではレコード数が大きく、空間検索が打ち切られたようだ。 低ズームでは描画できないデータが多いはずであるから、これを除外すべきであろう。
これにより、正常に描画されるようになった。
GPS記録が止まっていたので、onResumeの瞬間に止まったのではなく、もっと前に止まったものと思われる。
ログを少しずつ増やして、どういうタイミングで止まるのかを調べよう。
ただし、これまでの経験では急に止まるのではなく、最初に、ボタンやメニューが効かなくなるが、 スクロールやピンチイン・アウトなどはできる。そのうち、これらもできなくなる。 メモリに余裕はあるが、何らかのリソースの解放を忘れており徐々に無くなっているのかも知れない。
以前はref、nameタグの順で表示する仕様にしていた。ref がないためかと思ったがそうではないようだ。
【解決】文字列データファイルが更新されたため、スマホに転送する必要があった。転送で解決した。
データ作成の変更には手間どったが、レンダリング(描画)は簡単化されたので、修正は楽であった。 これで、バス路線データが増大しても心配はなくなった。 バス路線描画を別の層(別のBitmap)にした方が、バス路線選択変更時の画面のちらつきは少なくなるかも知れないが、 急ぐ話ではない。
全体としては、プログラムの分かりやすさを向上させる方向で進みたい。
japan, kanto, hot, gsi, ... のように、レンダリング用を前に移動した。
バス路線リレーションId、nameコード、refコードの3要素からなる CSVファイルとした。 現状では、kanto.osm 対する busroutes_kanto.csv が 約6000路線、146KB、 japan.osm 対する busroutes_japan.csv が 約11,000路線、256KB となった。
バイナリファイルにすればファイルサイズは半減するが当面その必要はない。
japan.osm より、kanto,osm、hot.osm の方が新しいデータでテストすることがあるため、 メモリには同時に読込んでおき、使い分ける。
メモリ使用量は僅かなため、一本化するよりプログラムが簡単である。
地名、山のアイコンは描画されるが、ラインやポリゴンの描画が皆無である。 Parser(Cull.java)、OSMDivider.java、Map(block.cs) のいずれかにバグがある。 一か所に絞れないので、手間取る。
まず、block.cs をチェックした。空間検索で道路や森林ポリゴンが抽出されない。
ブロックファイル自体に type==1 がないことを確認した。OSMDividerの出力が極端に少ないことが分かった。
やはりレコードフォーマットを変えると、デバッグに時間がかかるが、解決した。
低中ズームデータ作成で次のエラーが出た。
Parser と OSMDivider で同名で、実体が少し異なる classファイルが同じディレクトリに置かれていた。
c:\map>java -Dfile.encoding=UTF-8 -classpath ./class Parser kanto low in: c:/map/data/kanto_norm.dat, out:c:/map/data/kanto_low.dat Exception in thread "main" java.lang.NoSuchFieldError: vals at Cull.Simplify(Cull.java:286) at Parser.main(Parser.java:87)
wayレコードの共有が少ない(精々2)。登録ミスと思われる。
赤字部分の追加で解決した。
if (strType.equals("way") && !mapRelWays.containsKey(idRef)) {
mapRelWays.put(idRef, new RelWay(idRef));
}
まず、mapWays を mapRelWays に置き換えた。この段階では、結果は同じはずだが小さくなった。 バグが入り込んだ可能性がある。long[] を class RelWay でカプセル化しただけであるが、変更ミスがあったようである。
まず、次のように修正した。これで、ファイルサイズは同じになった。念のため、PC版で描画も確認した。
//mapRelWays.put(idRef, null); mapRelWays.put(idRef, new RelWay());
valueOfを使えば、mapKeys、 mapValsは要らない。
Key k = Key.valueOf("amenity");
japan_norm.dat のサイズは OSMParser版と同じになった。バグは入り込んでいないのかも知れない。
OSMParser版よりファイルサイズがごくわずかであるが小さくなった。 バグが入り込んだ可能性があるが、レンダリング・描画結果の目視では今のところ異常は見られない。
バグフィックスを通じてリファインしてゆく。
多くの地物はアイコンの下に名称を描画する。 この場合、アイコンの中心から文字列の最上端までの高さを引数 dy で指定する。 名称(文字列)が何行になっても最上端の位置は同じとする。 しかし、現在のプログラムでは、行数により、最上端の位置が少し上下する。
現在のプログラムから関連個所を抜粋したものを下に示す。
まず、描画領域の計算では、行間は考慮せず、実際の描画では行間を height*1.1 としている。 これを改める必要がある。
次に最初の行の位置 y - totalHeight/2 + yo は行数の影響を受けてはならない。 (line数/2)*height と (line数*height)/2 では値が異なる。line数は整数であるため、 line数=2、3では line数/2=1となる。一方 height は float 型であるから line数*height は float型である。 つまり、line数が偶数の場合は (line数/2)*height と (line数*height)/2 の値は同じであるが、奇数の時は前者の方が小さくなる。
まず、top の値がいくらであるべきかを考える。
float height = 0, width = 0; for (String line : lines) { paint.getTextBounds(line, 0, line.length(), bounds); if (bounds.width() > width) { width = bounds.width(); } if (bounds.height() > height) { height = bounds.height(); } } // 文字列の描画領域 float totalHeight = lines.length*height; float yo = (dy > 0 ? (lines.length/2)*height + dy : 0) + height; float left = x - width/2; float top = y - (lines.length/2)*height + yo - height; float right = x + width/2; float bottom = top + totalHeight; tile.cvMain.drawText(line, x-width/2, y - totalHeight/2 + yo, paint); yo += height*1.1;
次のように変えることにより、満足な結果が得られるようになった。
// 文字列の描画領域 float totalHeight = lines.length*height*1.1f; float top = dy > 0 ? y + dy : y - totalHeight/2; float left = x - width/2; float right = x + width/2; float bottom = top + totalHeight; if (tile.addRect(left, top, right, bottom)) { float yo = top+height; for (String line : lines) { if (halo > 0) { // 縁取りを先に描画 Paint paintHalo = tile.paintHalo; paintHalo.setStrokeWidth(halo*2); // 描画の幅 paintHalo.setTextSize(paint.getTextSize()); // テキストサイズ tile.cvMain.drawText(line, x-width/2, yo, paintHalo); } tile.cvMain.drawText(line, x-width/2, yo, paint); yo += height; } // 文字の位置指定は左端と下端 }
その日の移動ルート(GPS軌跡)はスクロールやzoomの変更毎に表示し直している。 移動ルート(GPS軌跡)はListに格納しているが、表示では毎回配列に変換していた。 これは負荷が重く、動的なメモリ確保が起きる。
Listにおいたままで表示するように変更した。
メインスレッド、特に onDraw では new を極力避けなければならない。この鉄則に反していた。
これまで、頻繁に、突然、メニュー操作やボタン操作に反応しなくなることがあった。これ以外の原因もあるだろうが、 メインスレッド、とりわけ onDraw での思い負荷が関係している可能性がある。
これ以外の処理も見直し、できるだけメインスレッドの負荷を下げたい。
PC版(C#)では間違いは起きていない。
【原因判明】PC版では道路名描画と矢印描画を分けている。道路名の場合は見やすいように角度によっては反転させている。 矢印の場合はこのような反転を行ってはならない。
赤字の判定処理の追加で解決した。
if (name.length() > 1 || (!name.equals("→") && !name.equals("←")) ) { if (deg > 90 || deg < -90) { if (deg > 90) deg -= 180; if (deg < -90) deg += 180; if (fAdmin) dy = -dy; } }
よくよく見ないと気づかない程度であるが、中央タイルの公園の色が少し濃くなっている。 zoom 20では中央の左のタイルの緑がはきっりと濃くなる。 4月16日のエラーと似ている。この時の原因は cvMain の初期化忘れであった。
また、危険区域のハッチングでは、PorterDuff演算が原因であった。
エラーに再現性がない。まだ、どこかに初期化忘れがあるのかも知れない。
道路描画では Paintインスタンスの再利用で問題があった。 ポリゴン塗りつぶしでも Paintインスタンスに問題があるのかも知れない。 何故かエラーが起きなくなったので、これで様子を見る。
OSMLib.java で、ラベルやアイコンの中心位置計算の初期値としていた。-float.MAX_VALUE に置き換えた。 この場合は大きな問題はないはずであるが、中心座標の位置が少し変わるケースがあるかも知れない。
スマホの地図アプリのソースにはエクスプローラーで検索した限りでは、float.MIN_VALUEを使っているところはなくなった。
橋名はそこそこに描画されているが、ところどころ描画が欠けている。 もう少し、描画を増やしたい。
【解決】原因は float.MIN_VALUE は絶対値最大の負数と誤解していたことである。これは非常に小さい正の数である。 C# の float.MinValue とは全く別物である。
橋名だけでなく、道路名の描画が著しく改善した。
これで前地図アプリGISと同等の仕様をサポートした。この後は、ゆっくりとリファインしていきたい。
zoom 20での橋の描画がおかしい。右下が消えている。他のズームでは異常はない。 下の図は PC版のスクリーンショットであるが、スマホ版でも全く同じ現象がみられる。 エラーがPC版とスマホ版で一致するのは非常に珍しい。プログラムのバグではなく、 OSMデータレコードの問題である可能性がある。
この近辺のデータを JOSM で調べると、ほぼ道路に沿って、 タグのない way (osm_id=1039748261, 2022-03-13)がある。
OSMParserではタグのない OSMデータは無視しているはずであるが、 この way データが入り込んでいないかチェックしてみる。
PC版でチェックしたがこのようなレコードはなかった。仮にあっても、タグがないから、 描画は起きないはずである。 このタグ無し way が原因ではないようだ。C# と Android Java で同じことが起きるのが不思議である。
元のOSMデータには異常はない。
fill描画を止めると、橋の右下の case描画の幅が少し狭くなっていることが分かる。
C# と Android Java では同じアルゴリズムが使われており、 たまたま、ある座標値、線の幅で起きる現象であるのかも知れない。
【原因判明】ふと気づいた。タイル境界が関係している。右端は隣のタイルである。 wayに対してもマージン処理を行っているが、その値が小さい。 もう少し大きくする必要がある。 空間検索か OSMDivider のマージンを大きくする必要がある。
空間検索でのマージンは次のようにしている
// point line poly multipoly static double[] left_margin = { 0.10, 0.03, 0.01, 0.01 }; static double[] top_margin = { 0.15, 0.03, 0.01, 0.01 }; static double[] right_margin = { 0.10, 0.03, 0.01, 0.01 }; static double[] bottom_margin = { 0.30, 0.03, 0.01, 0.01 };
lineのマージンを 0.03 から 0.05 に変えると、橋は正常に描画される ようになった。 スマホについてもこの修正により正常に描画されるようになった。
Join.Round が有効に働いていない。 基本テストでは問題がないため、 自作プログラムのどこかに問題があるのであろう。
Paintインスタンスの再利用および道路のレンダリングは renderRoadCase と renderRoadFill の協調によるため プログラムの動作がいささか複雑である。
renderRoadCase では
osm.drawLine(tile, 0xff000000, width, Dash.d0, RoundJoin);を実行するが、RoundJoin が有効に働いていない。RoundCapとした場合は RounJoin | RoundCap のような結果が得られる。
再利用に問題があるのかもしれない。
橋専用の Paintインスタンスを使うと解決した。やはり、再利用に問題があるようだ。いずれ、再検討する。
目立たないが、ほぼ中央の右側の緑が濃くなっている。濃くなっている部分はタイルの左40%、上70%位にあたる。 zoom を大きくしても、小さくしてもこのエラーは生じない。 また、パソコン版ではこのエラーは起きない。
再利用でクリアを忘れているときにおこるような現象である。
cvMainの初期化を入れると、この公園のエラーはなくなった。 cvMainに対しては最初に海か陸地ポリゴンを描画するため、初期化は要らないはずだが、 PorterDuff演算か何かの都合で必要になったのであろう。
公園ではペデストリアンデッキが正常に描画されていないことに気づいた。 これについては polygon で area=yes があるときはOKで、multipolygon により、area=yesタグが省略されている時、 描画がおかしくなるようだ。multipolygon の場合、polygon で area=yes があるのと同等に扱えばよいだろう。
スマホ版とPC版では状況が異なるが、やはり、同等の描画になっていない。
境界線は multipolygonは outer polygon だけとするか、inner polygon も対象とするかであるが、 現在は outer だけとして、PC版では次のようにしている。DrawOuterPolygonで境界線が描画できていないようである。 あるいはこの後塗りつぶしがあり、消えたのかも知れない。
if (osm.type == 3) osm.DrawOuterPolygon(g, 0xff777777, width*1.2f, tile); else osm.DrawLine(g, 0xff777777, width*1.2f, tile);
public void DrawOuterPolygon(Graphics g, uint color, float width, TileToRender tile) {
if (type <= 1 || width <= 0) return;
Pen pen = new Pen(Color.FromArgb(unchecked((int)color)), width);
int num_outer = type==2 ? num_nodes : buff[pos+ixMulti+1]; // outer polygon ノード数
PointF[] pnts = GetPointArray(tile, 0, num_outer);
if (pnts == null) return;
g.DrawLines(pen, pnts);
}
まず、赤字の +1 は不要である。これでも描画されない。そもそも DrawOuterPolygonがコールされないことが分かった。
この理由は分かり、PC版のエラーはなくなった。境界線はスマホ版とPC版のプログラムはほぼ同じでエラー原因は同じだった。
塗りつぶしはちょっと異なるようだ。ペデストリアンデッキより先に実行する公園の塗りつぶしが影響している。
【解決】軍関連の危険区域は薄い赤色でオーバレイしている。この場合、元の画像を薄く残すように描画する。 手法がPCと異なり、PorterDuff演算を使っている。 この手法は危険区域に限り、通常のマルチポリゴンは、下地は残さず、完全な上書きとすればよい。
Paint paint = pattern==SuperRenderer.bmpDanger ? tile.pPorterDuffOVERLAY : tile.pPorterDuffSRC_OVER;
まれに複数行文字列表示エラーがある。パソコンでは問題ない。
【解決】最後の行が「ー」(全角長音)の場合、heightが小さくなり、この最後の height を全体に使っていた。 heightの最大値を求めて、全行にこの値を使うように変更した。
色とサイズを統一して見栄えを良くした。
これで当面使うには不自由がないものになった。
バス時刻表関連プログラムはかなり煩雑である。バス路線表示と合わせて、見直しを図りたい。
Step Counter関連プログラムはスリム化した。
GPS関連はGISのプログラムをほぼそのまま Map で再現した。危惧したほどのトラブルはなかった。 ただし、バグが残っている可能性はある。
この後、実際に使いながら開発を進めるために、次には、ニーズの高いバス時刻表を移植する。
起動直後の鉄道レンダリングがおかしい。
dash配列を作成してからレンダリングスレッドを生成しているので、 dash配列が存在しないわけではない。
再利用リソースの初期化忘れはないだろうか。
【解決】TileToRenderに次の mapPaint でラインレンダリング用Paint を管理するように変更した。 色と線の太さはその都度変更するため、複数のスレッドで共有することはできない。
final HashMapmapPaint = new HashMap<>();
おそらく、現地図アプリGISでも起きるのであろうが、 日常では、ボタン操作やメニュー操作をあまりしないため気づかないだけであろう。
しかし、極力、解決したい。ライフサイクルの管理に問題があるのであろうか。
今日はWi-Fi接続デバッグがストップしたときに起きた。なぜか、Wi-Fiデバッグは突然切れることが多い。 もし、これが関係しているならば、自分のプログラムにバグがあるのではないかもしれない。
気がかりだった文字列描画の位置決めも無事終わった。 ブロックファイル(OSMバイナリレコードファイル)の事前読み込みはやめたが、 メモリ使用量は 300MB前後であり、将来、OSMデータが増えても心配はない。
void drawIconMulti(TileToRender tile, Bitmap bmp, float space)にバグがある。 別のzoomでは正常にレンダリングされる。
PointF[] Simplify(PointF[] pnts, double epsilon) では、ラインが1点になることがある。 これは誤り。
【解決】Float.MIN_VALUE を絶対値最大の負の値と誤解していた。Float.MIN_VALUEは最も小さい正の数であった。
個々には残っているが、大雑把には終わった。リファインしながら、完成させてゆく。
陸地ポリゴンおよび単純な建物レコード(building=yes;houseタグなどだけの場合)では タグおよび osm_id を省略する。
これまでは Tag.Key、Tag.Val であったが、これを Key、Val とする。
ここ1か月ほどPC版地図アプリ PC_Map の開発に専心していた。ほぼ完成したため、スマホ版の 地図アプリMap開発を再スタートする。PC_Map開発での成果を取り入れるため、再スタートとなる。