转载请注明作者及出处,谢谢

上文提到了等值线追踪解决方案,在此基础上,我们就可以把等值线画出来了,但是只光秃秃的线条,没有标注还是不行的,别人哪知道那条像蚯蚓一样的线条代表什么呢,本文我们就来讨论下如何在等值线上进行标注。

感谢《等值线标注的一种算法探讨》一文的作者,我正在是使用这篇论文中的重要算法指导了我的工作。

首先标注那些小的封闭式的等值线。

这里我也没有想出来好的方法,就使用方法,找出封闭式等值线中点坐标X最小值,Y最小值,X最大值及最大值;如果XMax - XMin < 指定值以及YMax - YMin < 指定值,则在P((XMin + XMax) / 2,(YMin + YMax) / 2)处把等值线的值画出来,如下代码所示:

                            if (points[0].X == points[points.Count - 1].X && points[0].Y == points[points.Count - 1].Y)
                            {
                                float xMin = points.Min<VPoint>(p => p.X), xMax = points.Max<VPoint>(p => p.X), yMin = points.Min<VPoint>(p => p.Y), yMax = points.Max<VPoint>(p => p.Y);
                                if (xMax - xMin < 25 && yMax - yMin < 25)
                                {
                                    g.DrawString(value.ToString(), font, brush, (xMax + xMin - sf.Width) / 2f, (yMax + yMin - sf.Height) / 2f);
                                    continue;
                                }
                            }

效果如图中红圈所示:

图1

把过于小的封闭式等值线标注后,大点的封闭式和开放式等值线的标注方法就一样了。

根据《等值线标注的一种算法探讨》一文中的指导思想,需要把曲线转换为折线或是多边形,啥意思咧,看下图所示:

如果我告诉你,现在如果每条线段的长度大于高程值的字符串(value.ToString())所需的长度 - 空白长度,就在那里画一个标注应该就问题不大了吧,对于那些太小的线段,就不要去画了,小到不像话的封闭式等值线,我们在上一步中已经处理过了。

那么如何把一条曲线转换为折线或是多边形的呢?《等值线标注的一种算法探讨》一文中告诉我们(见论文2.2):“设一个dif参数,用来控制多边形近似等值线的误差,将曲线上的点从第0个开始,偶数点相连作为多边形的边,依次查看等值线上的3个点,如果中间的等值点(奇数点)到多边形的边的距离小于参数dif,舍弃中间的等值点,否则保存中间的点。如此不断循环,直到最后生成的多边形的边数不在(再 think8848注)减少为止。这样就得到了等值线近似的多边形。”dif越大,表明曲线越陡,dif越小,表明曲线越平缓。

算法代码示例:

                            int n = (points[0].X != points[points.Count - 1].X && points[0].Y != points[points.Count - 1].Y) ? points.Count : points.Count - 1,
                                minEdge = n + 1, k = n;
                            float tolerance = 8f;
                            var indexes = new List<int>(); for (int i = 0; i < points.Count; i++) { indexes.Add(i); }
                            while (k < minEdge)
                            {
                                minEdge = k;
                                var p = 0;
                                while (p < minEdge - (n % 2 == 0 ? 3 : 2))
                                {
                                    float straight = this.GetPointToStraight(points[indexes[p + 1]], points[indexes[p]], points[indexes[p + 2]]);
                                    if (Math.Abs(straight) < tolerance)
                                    {
                                        indexes[p + 1] = -1;
                                        k -= 1;
                                    }
                                    p++; p++;
                                }
                                indexes = indexes.Where<int>(index => index != -1).ToList<int>();
                            }

另附求点到直线的距离的方法,下列代表示例如何计算p点到直线p1p2最短距离

        private float GetPointToStraight(VPoint p, VPoint p1, VPoint p2)
        {
            if (p1.X == p2.X)
            {
                return (float)Math.Abs(p1.Y - p2.Y);
            }
            if (p1.Y == p2.Y)
            {
                return (float)Math.Abs(p1.X - p2.X);
            }
            double k = (p2.Y - p1.Y) / (p2.X - p1.X);
            double c = (p2.X * p1.Y - p1.X * p2.Y) / (p2.X - p1.X);
            return (float)((k * p.X - p.Y + c) / (Math.Sqrt(k * k + 1)));
        }

最终indexes里面保存了在等值点列表中构成多边形的点的索引。

在本文的最后,我们再来谈谈如果标注字符旋转的问题,有一条线段作为基准,将画布旋转与线段倾斜角度相同度数应该不是一件难事,是的,.NET很容易就能做到,唯一的问题是如何线段的倾斜角度:

var alpha = (float)(Math.Atan((p2.Y - p1.Y) / (p2.X - p1.X)) * 180 / Math.PI);

就是这个角度了,三角函数已经忘了的兄弟可以到网上重新温习下高中数学,呵呵。

                                    g.TranslateTransform(xOffset, yOffset);
                                    g.RotateTransform(alpha);
                                    g.DrawString(value.ToString(), font, brush, new PointF(0, 0));
                                    g.RotateTransform(-alpha);
                                    g.TranslateTransform(-xOffset, -yOffset);

变换坐标系的原点到将要绘制标注的左上角位置,然后旋转画布,(注意,角度为正时为顺时针,角度为负时为逆时针方向,好像和我们数学课中的方向不一致。)绘制完成后,再把坐标系归位,循环,直至将所有的标注都绘制完成。

至此,使用C#绘制等值线的工作基本完成。

posted on 2011-05-04 10:29  think8848  阅读(6291)  评论(1编辑  收藏  举报