トップ地図アプリMap3 > バス路線図

バス路線図

はじめに

バス路線図の仕様は 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での実装

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();
    }

Map3での実装

バス停の情報はレンダリング時に 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();
    }

リファレンス

[1]