c# - 绘制二维热图

标签 c# charts heatmap

我有一张图表,我想在上面绘制热图;我仅有的数据是湿度和温度,它们代表图表中的一个点。

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

我想要的是类似于下图:

enter image description here

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

最佳答案

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

这是一个example 使用/滥用 DataGridView。虽然我不建议这样做,但该帖子包含一个有用的功能,可以创建漂亮的颜色列表以供您在任务中使用。

然后可以选择使用 GDI+ 方法绘制图表,即 Graphics.FillRectangle。这一点都不难,但是一旦您想要获得图表控件提供的那些不错的额外功能,例如缩放、轴、工具提示等,工作就会加起来。请参见下文!

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

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

List<Color> colorList = new List<Color>();

并且您已成功将数据投影到指向颜色列表的 int 索引的二维数组中:

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”..

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

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 这样的细节,即要绘制的实际区域;所以这里还有一些改进的空间..!

我们在设置图表以及调整大小时调用它:

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

让我们看看使用一些廉价测试数据的结果:

enter image description here enter image description here

如您所见,调整大小工作正常..

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

private void button6_Click(object sender, EventArgs e)
{
    List<Color> stopColors = new List<Color>()
    { 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 添加到轴中。参见 here for an example !

这应该让您了解使用 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;
}

生成的位图看起来很熟悉:

enter image description here

这很简单;但是要将所有额外内容添加到填充保留的空间中并不是那么容易..

关于c# - 绘制二维热图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36342635/

相关文章:

c# - 如何从 C# 中的构造函数调用其他构造函数?

c# - 在 .NET 4.5 中并行运行 .exe 进程并更新进度条

c# - 如何实现不按字母顺序排列的接口(interface)属性

c# - ASP.NET 堆积柱形图 - 轴故障

javascript - Highcharts 热图 Legend Max

c# - 在 Windows Phone Runtime c# 中将字符串 HEX 转换为颜色

javascript - 如何将 Highcharts 时间线图表上的线条粘在一起?

java - 如何在 Android Studio 中使用像 AChartEngine 这样的 Java 库?

r - 默认情况下,R 热图如何对行进行排序?

r - 从热图或基础数据中提取颜色值矩阵