バス路線図の仕様は MapX と同じものでよい。しかし、Map3 では MapX にあった TileToRender は Renderer に吸収したため、 実装方法の変更が必要である。また、できれば、この際、MapXよりもシンプルにしたい。
路線データそのものは道路レコードに含まれるが、リレーションで表現されるバス路線管理データは MapX ではメモリに常駐させている。
バス停をタップすると、このバス停を使用するバス路線番号を抽出して、地図下部にバス路線番号ボタン列を表示する。 バス路線を選択すると、そのバス路線が地図上に表示される。
具体的にはレンダリングをやり直している。
具体的なプログラムについては、一から見直したい。道路レコードにはバス路線ID列データを含めている。 現在のバス路線管理データの詳細は忘れたが、おそらく、バス停毎のバス路線ID列であろう。
バス停毎のバス路路線ID列はバス停レコードに含めることもできるが、多分、現在は含めていないだろう。 もし、含む場合にはバス路線管理データはID、路線番号(略称)、路線名だけでよい。
MapXのバス管理データは
int id; // 路線選択ボタン番号 long osm_id; // relation id boolean selected = false; int iname, iref;だけであった。バス停レコードには道路レコードと同様に、バス路線ID列が含まれているようだ。
MapX では、下のプログラムでタップされたバス停を探している。TileToRender は Renderer に含めたため、 Map3 ではそのままでは使えない。
別の方法として、getOSMでレコードを取り出すのではなく、レンダリング時にバス停の位置情報をメモリに蓄えておき、 その全バス停をチェックする方が簡単であろう。
ただし、タイルキャッシュを使うため、絶えず、全タイルをレンダリングするわけではない。 アプリ起動時からバス停情報を蓄積しておくのが楽であろう。
バス路線表示モードに変えたとき、キャッシュをクリアして、そこから蓄積してもよい。
PopupBusRoute pbr = null; private void bus_route(double x, double y) { if (pbr != null && pbr.isShowing()) { return; // 多重起動はしない } //System.out.println("Map#bus_route"); TileToRender tile = new TileToRender(); tile.reset(); tile.zoom = zoom; tile.x = (int)x; tile.y = (int)y; tile.ixRecord = 0; // osmsのixFromからレンダリング対象レコードを格納する synchronized (Block.blocks) { Block.getOSMs(src, Tile.Range.norm, 7, tile, bus_osms); Block.getOSMs(src, Tile.Range.norm, 13, tile, bus_osms); } // 一番近いバス停を探す double minDistance = Double.MAX_VALUE; OSM minOsm = null; for (int n = 0; n < tile.ixRecord; n++) { OSM osm = bus_osms[n]; if (osm.type != 0 || (osm.tag_flags&SuperRenderer.bitHighway) == 0 || osm.num_busroutes <= 0 || osm.getVal(Key.highway) != Val.bus_stop) { continue; } double dx = (double)osm.xc / (1 << (30-zoom)) - x; double dy = (double)osm.yc / (1 << (30-zoom)) - y; double distance = Math.sqrt(dx*dx + dy*dy); if (distance < minDistance && distance < 0.1) { minDistance = distance; minOsm = osm; } } if (minOsm == null) return; BusRoute.routes = new BusRoute[minOsm.num_busroutes]; for (int n = 0; n < BusRoute.routes.length; n++) { long id = minOsm.buff[minOsm.pos+minOsm.ixBusRoutes+n]; BusRoute.routes[n] = BusRoute.mapBusRoutes[src.ordinal()].get(id); } pbr = new PopupBusRoute(this, context); pbr.showAtLocation(textViewTop, Gravity.BOTTOM, 0, 0); Tile.clear(); invalidate(); }
バス停の情報はレンダリング時に HashMap mapBusstops に蓄積している。これを使ってどのバス停がタップされたかを決める。 distance < 0.1 の 0.1 は大きな値に変える。世界地図のサイズが 230 である。地球一周が 40,075 kmであるから 1メートルはおよそ 1,000 x 1,000 x 1000 ÷ 40,000,000 = 25 である。 20~100メートル相当(500~2000)程度でいいだろう。
private void bus_route(double tx, double ty) { // tx, ty: タイル座標 if (pbr != null && pbr.isShowing()) { return; // 多重起動はしない } double x = tx * GPSLog.B30 / (1 << zoom); // zoom 0 に換算して double y = ty * GPSLog.B30 / (1 << zoom); // 2^30 をかける // 一番近いバス停を探す double minDistance = Double.MAX_VALUE; BusStop minBs = null; for (BusStop bs : BusStop.mapBusstops.values()) { double dx = bs.x - x; double dy = bs.y - y; double distance = Math.sqrt(dx*dx + dy*dy); if (distance < minDistance /*&& distance < 0.1*/) { minDistance = distance; minBs = bs; } } if (minBs == null) return; BusRoute.routes = new BusRoute[minBs.routes.length]; for (int n = 0; n < BusRoute.routes.length; n++) { long id = minBs.routes[n]; BusRoute.routes[n] = BusRoute.mapBusRoutes[src.ordinal()].get(id); } pbr = new PopupBusRoute(this, context); pbr.showAtLocation(textViewTop, Gravity.BOTTOM, 0, 0); Tile.initialize(); invalidate(); }