绘制等值线的基本步骤基本上都是差不多的(当然每个人会有些差别因个人而异)(PS:下面的是矩阵点的计算不是离散点的计算)
数据处理->获取网格数据->通过三角剖分法计算等值线(当然还有其他的各种剖分方式)->绘制等值线(在绘制之前肯定会记录等值线的一些数据方便之后进行追踪)->填充等值线
关于等值线的一些基础概念和三角剖分的基本思想可以参考这篇->
[http://www.cnblogs.com/think8848/archive/2011/04/29/2032751.html](http://www.cnblogs.com/think8848/archive/2011/04/29/2032751.html)
///
/// 数据结构
///
public class DataPoint
{
///
/// 坐标X
///
public float X;
///
/// 坐标Y
///
public float Y;
///
/// 温度T
///
public float T;
///
/// 矩阵坐标i
///
public float PosI;
///
/// 矩阵坐标j
///
public float PosJ;
///
/// 构造函数
///
/// 坐标X
/// 坐标Y
/// 温度T
public DataPoint(float x, float y, float t)
{
X = x;
Y = y;
T = t;
}
///
/// 默认构造函数
///
public DataPoint()
{
}
///
/// 判断2个对象的XY是否相等
///
/// 点坐标
///
public bool EqualXY(Point point)
{
return (this.X == point.X && this.Y == point.Y) ? true : false;
}
///
/// 判断2个对象的共有值是否都相等
///
/// 左边对象
/// 右边对象
/// 是否相等
public static bool operator ==(DataPoint left, DataPoint right)
{
if (left.X == right.X && left.Y == right.Y && left.T == right.T)
return true;
else
return false;
}
///
/// 判断2个对象的共有值是否不等
///
///
///
///
public static bool operator !=(DataPoint left, DataPoint right)
{
if (left.X != right.X || left.Y != right.Y || left.T != right.T)
return true;
else
return false;
}
///
/// 哈希值
///
///
public override int GetHashCode()
{
return base.GetHashCode();
}
///
/// 是否指向同一个对象地址
///
///
///
public override bool Equals(object obj)
{
return base.Equals(obj);
}
}
-这是我的代码中作为传入数据点的类的结构,X,Y作为在外面赋值好的原始坐标,后面要转换到PosI和PosJ中 当然如果已经计算好的可以直接赋值PosI和PosJ(后面简称I和J),I和J的含义是指这个数据点是矩阵点的第几行和第几列的点。
///
/// 等值线分析及绘图
///
///
private void CurLines(Size size)
{
DataPoint maxPoint = new DataPoint(); ///三角网格中最大温度值的点
DataPoint midPoint = new DataPoint(); ///三角网格中中间温度值的点
DataPoint minPoint = new DataPoint(); ///三角网格中最低温度值的点
int pos1, pos2, pos3;
float temp = 0;
for (int j = 0; j < Data.GetLength(1) - 1; j++)
{
for (int i = 0; i < Data.GetLength(0) - 1; i++)
{
//网格四个角坐标,变量值
CurPoints[0] = Data[i, j];
CurPoints[1] = Data[i + 1, j];
CurPoints[2] = Data[i + 1, j + 1];
CurPoints[3] = Data[i, j + 1];
CurPoints[4] = new DataPoint();
CurPoints[4].T = 0.25F * (CurPoints[0].T + CurPoints[1].T + CurPoints[2].T + CurPoints[3].T);
CurPoints[4].PosI = 0.5F * (CurPoints[0].PosI + CurPoints[1].PosI);
CurPoints[4].PosJ = 0.5F * (CurPoints[1].PosJ + CurPoints[2].PosJ);
pos3 = 4;
//巡检单元格范围内所有的点信息
for (pos1 = 0; pos1 < 4; pos1++)
{
pos2 = pos1 + 1;
if (pos1 == 3) pos2 = 0;
PointF p1, p2; ///绘制用的点
OrderDataPoint(CurPoints[pos1], CurPoints[pos2], CurPoints[pos3], ref maxPoint, ref midPoint, ref minPoint);
//巡检点范围内所有的点信息
for (int lines = 0; lines < ContourValues.Length; lines++)
{
temp = ContourValues[lines];
if (minPoint.T < temp && temp < maxPoint.T)
{
p1 = interp(minPoint, maxPoint, temp, this.Size);
if (temp > midPoint.T)
{
p2 = interp(midPoint, maxPoint, temp, this.Size);
}
else
{
p2 = interp(minPoint, midPoint, temp, this.Size);
}
Drawings.Add(new DrawingPoints(p1, p2, GetTempColor(temp), temp));
}
}
}
}
}
}
这段代码就是利用上面图上的三角剖分法去计算等值线的函数,最重要的在计算的时候一定要吧相邻的点保存下来Drawings.Add(new DrawingPoints(p1, p2, GetTempColor(temp), temp));。。。。这里就是吧等值线最小单位线段保存下来,数据有线段的2个端点,该线段的等值线的值,以及绘制的颜色。下面就是这个保存线段的类的结构
///
/// 画等值线时候的点的集合
///
public class DrawingPoints
{
///
/// 起始点
///
public PointF StartPoint;
///
/// 结束点
///
public PointF EndPoint;
///
/// 等值线的值
///
public float Value;
///
/// 2个点之间的连线的颜色
///
public Color LineColor;
///
/// 计算的时候使用表示是否被使用过
///
public bool IsUser;
///
/// 构造函数
///
/// 起始点
/// 结束点
public DrawingPoints(PointF p1, PointF p2, Color color, float Value = 10, bool IsUser = false)
{
StartPoint = p1;
EndPoint = p2;
LineColor = color;
this.Value = Value;
this.IsUser = IsUser;
}
///
/// 设置是否使用过
///
///
public void SetUser(bool isUser)
{
this.IsUser = isUser;
}
}
由于上面我们保存下来所有等值线的最小单位:线段以及线段对应的等值线的值。这样下面就可以开始检索。根据等值线的值把所有的和该等值线的值相同的线段找出来,由于线段都是2个端点的所有我们只要根据2根线段首位相同就可以吧线段的点组合起来这样就能变成一条完整的等值线。
-根据上图可以吧同样值的等值线线段 连接起来就是等值线。
///
/// 检索
///
///
///
///
/// 当不是边界的时候也需要倒置一次进行寻找
protected bool FindNode(List listPoints, ref PointF firstPoint, float temp, bool isUpSideNotSide = false)
{
for (int i = 0; i < Drawings.Count; i++)
{
DrawingPoints point = Drawings[i];
if (point.Value == temp && !point.IsUser)
{
if (firstPoint.X == -1)
{
listPoints.Add(point.StartPoint);
listPoints.Add(point.EndPoint);
firstPoint = point.EndPoint;
point.IsUser = true;
return true;
}
else if (GetEqualPointF(firstPoint, point.StartPoint))
{
listPoints.Add(point.EndPoint);
firstPoint = point.EndPoint;
point.IsUser = true;
///检测边界
return IsUpSide(ref firstPoint, ref listPoints);
}
else if (GetEqualPointF(firstPoint, point.EndPoint))
{
listPoints.Add(point.StartPoint);
firstPoint = point.StartPoint;
point.IsUser = true;
///检测边界
return IsUpSide(ref firstPoint, ref listPoints);
}
}
}
/* 加快运行速度
///当不是边界的时候也需要倒置一次进行寻找
if (!isUpSideNotSide && listPoints.Count > 1)
{
firstPoint = listPoints[0];
Tools.UpsideArray(ref listPoints);
FindNode(listPoints, ref firstPoint, temp, true);
}
*/
return false;
}
这个是检索函数,这个只是部分 (原先写的递归的方式检索的感觉不是很理想后来改成循环检索)检索完后 吧相对应的点都组合成了对应的等值线,当然同一个值可能有多条等值线,所以检索的时候当我们随着一个线段的一端检索成一个环的时候就可以退出 或者到达边界,到达边界的情况必须是2端都是边界,所以当一端到达边界的时候需要倒置然后再次检索。
这样一个值的等值线最后出来的结构是PointF [] [] pointfs 这样的二维数组。
最后得出的等值线的情况就是上图所示的情况,不管是那种情况我们都可以让它们和自己或者和边界形成一个多边形,不管是凸多边形还是凹多边形。这时候我们可以计算多变的面积
这个就是任意多边形的面积公式,推导过程我就不再说明了网上自行查阅,我也是看来的。
最后吧我们得到的图形面积计算,然后排序将面积从大到小排列,最后依次填充就完成所有工作了。
如果有问题,欢迎指出我会及时修改。谢谢!