トップPC地図システム > 曲線に沿ってアイコンを描画する

曲線に沿ってアイコンを描画する

OSM地図における崖のレンダリング

OSM(OpenStreetMap)標準地図では、崖(natural=cliff)のレンダリングには小さなアイコン「 」を使う。このアイコンは中央に細い線があり、中央から下に向かって小さな三角「▼」が描かれている。

この小さな画像を曲線に沿って▼が上から下に向かうように描きならべると、下のようになる。 これが標準OSM地図における崖のレンダリングである。

最初のプログラム

これを C# で実現するには、曲線に沿って文字列を描画するのと同じ方法を使う。 曲線といっても、拡大してみれば無数の直線線分がつながったものである。

プログラムとしては、大きくは線分の数だけループして、 線分ではそこに並ぶアイコン(小さな画像データ)の数だけループする。

角度計算は線分ごとでよいが、アイコンの中心座標の計算はアイコン毎となる。 g.TranslateTransform(x1-dx/2, y1-dy/2) により、画像操作の中心をアイコンの中心とする。 次に、g.RotateTransform(deg)により、アイコンを水平方向に描画すれば、回転されるように設定しておく。

DrawImageメソッドの引数は、アイコンの中心ではなく、左上座標であるから、 g.DrawImage(img, -img.Width/2, -img.Height/2) とすることにより、線分の中心とアイコンの中心が一致する。

    // iconをラインに沿って繰り返し描画する
    public void DrawIconMulti(Graphics g, Image img, TileToRender tile) {
        PointF[] points = GetPointArray(tile, 0, num_nodes);
        if (points == null || points.Length <= 1) return;

        float prv_x = points[0].X, prv_y = points[0].Y;
        for (int k = 1; k < points.Length; k++) {
            float x = points[k].X;
            float y = points[k].Y;
            float dist = (float)Distance(x, y, prv_x, prv_y);
            int num_icons = (int)((dist + 0.5f)/img.Width);
            float dx = (x - prv_x) / num_icons;
            float dy = (y - prv_y) / num_icons;
            float deg = (float)Degree(prv_x, prv_y, x, y);
            float x1 = x, y1 = y;
            for (int n = 0; n < num_icons; n++, x1 -= dx, y1 -= dy) {
                g.ResetTransform();
                g.TranslateTransform(x1-dx/2, y1-dy/2); // 中心
                g.RotateTransform(deg);
                g.DrawImage(img, -img.Width/2, -img.Height/2);
            }
            g.ResetTransform();
            prv_x = x;
            prv_y = y;
        }
    }

線分間隔が短く、一つの線分にアイコンが描画できない場合、 近似的に複数の線分に一つのアイコンを割り当てる必要がある。

地図システムとしては、崖のレンダリングだけでなく、一般に、 レンダリング精度上必要とする以上にノード数が多いのはパフォーマンス上無駄であるから、 間引き等により、ノード数を減らした方がよい。

したがって、近似曲線を求めるプログラムは DrawIconMultiメソッドとは切り離し、 全レコードに適用する。

A.リファレンス

[1] 道路や川に沿って文字列を描画する