绘制2D热图

我有一张图表,我想要绘制热图; 我所拥有的唯一数据是湿度和温度,它代表图表中的一个点。

如何在c#中获取图表上的矩形热图?

我想要的是类似于下图:

在此处输入图像描述

我真正想要的是图表中的一个矩形区域,它根据我从点列表中得到的点以不同的颜色绘制,并在图表中形成彩色部分。

您可以选择至少三种方法来创建具有组成热图的彩色矩形的图表。

这是一个使用/滥用DataGridView 示例 。 虽然我不建议这样做,但post包含一个有用的function,可以创建在任务中使用的漂亮颜色列表。

然后可以选择使用GDI +方法绘制图表,即Graphics.FillRectangle 。 这一点并不难,但是一旦你想获得Chart控件提供的那些漂亮的附加function,比如缩放,轴,工具提示等,工作就会增加..见下文!

那么让我们看一下选项三:使用DataVisualization命名空间中的Chart控件。

我们首先假设您已经创建了一个颜色列表:

 List colorList = new List(); 

并且您已设法将数据投影到指向颜色列表的2D索引数组:

 int[,] coloredData = null; 

接下来,你必须为你的Series S1选择一个ChartType 。我真的只能想到一个有用的方法:

 S1.ChartType = SeriesChartType.Point; 

点数由Markers显示。 我们希望DataPoints不会真正显示为标准MarkerTypes之一 。

如果我们想要显示正方形, Square就可以了; 但是对于矩形,它将无法正常工作:即使我们让它们重叠,在边界处仍然会有不同大小的点,因为它们没有完全重叠。

因此,我们通过将每个点的MarkerImage设置为合适大小和颜色的位图来使用自定义标记。

这是一个循环,它将DataPoints添加到我们的Series并设置每个都有一个MarkerImage

 for (int x = 1; x < coloredData.GetLength(0); x++) for (int y = 1; y < coloredData.GetLength(1); y++) { int pt = S1.Points.AddXY(x, y); S1.Points[pt].MarkerImage = "NI" + coloredData[x,y]; } 

这需要一些解释:要设置不在磁盘上的路径的MarkerImage ,它必须驻留在Chart's Images集合中。 这意味着需要是NamedImage类型。 任何图像都可以,但必须添加一个唯一的名称字符串以在NamedImagesCollection标识它。 我选择的名字是'NI1','NI2'..

显然我们需要创建所有这些图像; 这是一个function:

 void createMarkers(Chart chart, int count) { // rough calculation: int sw = chart.ClientSize.Width / coloredData.GetLength(0); int sh = chart.ClientSize.Height / coloredData.GetLength(1); // clean up previous images: foreach(NamedImage ni in chart1.Images) ni.Dispose(); chart.Images.Clear(); // now create count images: for (int i = 0; i < count; i++) { Bitmap bmp = new Bitmap(sw, sh); using (Graphics G = Graphics.FromImage(bmp)) G.Clear(colorList[i]); chart.Images.Add(new NamedImage("NI" + i, bmp)); } } 

我们希望所有标记的尺寸至少大致合适; 所以每当这个尺寸改变时,我们再次设置它:

 void setMarkerSize(Chart chart) { int sx = chart1.ClientSize.Width / coloredData.GetLength(0); int sy = chart1.ClientSize.Height / coloredData.GetLength(1); chart1.Series["S1"].MarkerSize = (int)Math.Max(sx, sy); } 

这并不关心像InnerPlotPosition这样的InnerPlotPosition ,即要绘制的实际区域; 所以这里有一些细化的空间..!

我们在设置图表时调用它,但也在resize时调用:

 private void chart1_Resize(object sender, EventArgs e) { setMarkerSize(chart1); createMarkers(chart1, 100); } 

让我们看看使用一些便宜的testdata的结果:

在此处输入图像描述 在此处输入图像描述

你可以看到resize工作正常..

以下是设置我的示例的完整代码:

 private void button6_Click(object sender, EventArgs e) { List stopColors = new List() { Color.Blue, Color.Cyan, Color.YellowGreen, Color.Orange, Color.Red }; colorList = interpolateColors(stopColors, 100); coloredData = getCData(32, 24); // basic setup.. chart1.ChartAreas.Clear(); ChartArea CA = chart1.ChartAreas.Add("CA"); chart1.Series.Clear(); Series S1 = chart1.Series.Add("S1"); chart1.Legends.Clear(); // we choose a charttype that lets us add points freely: S1.ChartType = SeriesChartType.Point; Size sz = chart1.ClientSize; // we need to make the markers large enough to fill the area completely: setMarkerSize(chart1); createMarkers(chart1, 100); // now we fill in the datapoints for (int x = 1; x < coloredData.GetLength(0); x++) for (int y = 1; y < coloredData.GetLength(1); y++) { int pt = S1.Points.AddXY(x, y); // S1.Points[pt].Color = coloredData[x, y]; S1.Points[pt].MarkerImage = "NI" + coloredData[x,y]; } } 

关于限制的一些注意事项:

  • 该点始终位于任何网格线的顶部。 如果你真的需要那些,那么你必须在其中一个Paint事件中将它们绘制在顶部。

  • 所示的标签指的是数据数组的整数索引。 如果要显示原始数据,可以通过一种方法将CustomLabels添加到轴中。有关示例,请参见此处 !

这应该可以让您了解使用Chart控件可以执行的操作; 在这里完成你的困惑是如何使用相同的颜色和数据在GDI +中绘制这些矩形:

 Bitmap getChartImg(float[,] data, Size sz, Padding pad) { Bitmap bmp = new Bitmap(sz.Width , sz.Height); using (Graphics G = Graphics.FromImage(bmp)) { float w = 1f * (sz.Width - pad.Left - pad.Right) / coloredData.GetLength(0); float h = 1f * (sz.Height - pad.Top - pad.Bottom) / coloredData.GetLength(1); for (int x = 0; x < coloredData.GetLength(0); x++) for (int y = 0; y < coloredData.GetLength(1); y++) { using (SolidBrush brush = new SolidBrush(colorList[coloredData[x,y]])) G.FillRectangle(brush, pad.Left + x * w, y * h - pad.Bottom, w, h); } } return bmp; } 

生成的Bitmap看起来很熟悉:

在此处输入图像描述

那很简单; 但要将所有附加内容添加到填充保留的空间中将不会那么容易..