MapViewで画面外に描画しないようにする

GPSログをHTC Desire上でGoogleMapsに描画するアプリを作っている。 http://shokai.org/blog/archives/5180
線を10000本とか表示したらスクロールがガクガクになった。1000本ぐらいでようやくギリギリ耐えられる程度の重さになる。
3000 logs

画面内に線が全くなくてもガクガクになる。Google MapsのMapViewは画面外に線を引いても重くなってしまう。


MapView.getLatitudeSpan(), MapView.getLongitudeSpan()で上辺下辺の緯度差、左辺右辺の経度差が取得できるので、地図の中心と合わせれば画面上に見える範囲が求められる。
http://code.google.com/intl/ja/android/add-ons/google-apis/reference/com/google/android/maps/MapView.html#getLatitudeSpan()


じゃあ画面外に線を引かなければいいよねという事でこうなった。画面外へGPSログの線が伸びているというのは表現したいので、2点両方が画面外だったら線を引かないという条件で描画した。

    @Override
    public void draw(Canvas canvas, MapView view, boolean shadow) {
        List<GeoPoint> points = log.getPoints();
        if (points.size() < 2) return;
        Point pa = new Point();
        Point pb = new Point();

        GeoPoint center = view.getMapCenter();
        int top = center.getLatitudeE6() - view.getLatitudeSpan()/2; // 画面上に見える範囲
        int bottom = center.getLatitudeE6() + view.getLatitudeSpan()/2;
        int left = center.getLongitudeE6() - view.getLongitudeSpan()/2;
        int right = center.getLongitudeE6() + view.getLongitudeSpan()/2;

        GeoPoint ga, gb;
        for (int i = 0; i < points.size() - 1; i++) {
            ga = points.get(i);
            gb = points.get(i+1);
            if( !(ga.getLatitudeE6() < top && gb.getLatitudeE6() < top ||
                  ga.getLatitudeE6() > bottom && gb.getLatitudeE6() > bottom || 
                  ga.getLongitudeE6() < left && gb.getLongitudeE6() < left ||
                  ga.getLongitudeE6() > right && gb.getLongitudeE6() > right) ){ // 画面外は描画しない
            
                view.getProjection().toPixels(points.get(i), pa);
                view.getProjection().toPixels(points.get(i + 1), pb);
                canvas.drawLine(pa.x, pa.y, pb.x, pb.y, linePaint);
            }
        }
    }

10万件のログデータで試してみたが、全部が画面内に描画されるようなズームレベルで無ければサクサクスクロールできるようになった。

これで1000件ぐらいならなんとかなったし、画面上に1000本以上の線を描画したら何も見えなくなるから、これで描画する本数を絞りつつさらに1000本以下に間引くとかすれば使いやすいマップになるだろうな


JS版のGoogle Maps APIや、Google Earthでは画面のzoomレベルと中心の座標は取得できるが今実際に上下左右がどこからどこまで表示されているかは取得できない。
そのかわり、ズームレベルから1ピクセルあたりの実際の距離は求められるようになっているらしい。
http://twitter.com/kokogiko/status/15292284611


OpenGISに資料があった。ズームレベル0で赤道上では1ピクセル157km、1レベル上がる毎に1ピクセルあたりの実際の距離が半分になっていく。
http://www.opengis.co.jp/techguidej/76googleMapsStruc_J.pdf

ただし、地球は楕円球なので赤道上以外ではメルカトル座標と経緯度を変換しないと正確にはわからない。


今回使ったAPIandroidにしか無いみたいだ。

あと、MapView.getProjection.toPixels()はけっこう重いのであまり使わないようにしたい。