zoukankan      html  css  js  c++  java
  • [原]C#绘制等值线三 等值线标注

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

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

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

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

    这里我也没有想出来好的方法,就使用方法,找出封闭式等值线中点坐标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#绘制等值线的工作基本完成。

  • 相关阅读:
    10 个雷人的注释,就怕你不敢用!
    Java 14 之模式匹配,非常赞的一个新特性!
    poj 3661 Running(区间dp)
    LightOJ
    hdu 5540 Secrete Master Plan(水)
    hdu 5584 LCM Walk(数学推导公式,规律)
    hdu 5583 Kingdom of Black and White(模拟,技巧)
    hdu 5578 Friendship of Frog(multiset的应用)
    hdu 5586 Sum(dp+技巧)
    hdu 5585 Numbers
  • 原文地址:https://www.cnblogs.com/think8848/p/2036179.html
Copyright © 2011-2022 走看看