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メソッドとは切り離し、 全レコードに適用する。