バス路線図の仕様は 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();
}