- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一张图表,我想在上面绘制热图;我仅有的数据是湿度和温度,它们代表图表中的一个点。
如何在c#中获取图表上矩形类型的热图?
我想要的是类似于下图:
我真正想要的是图表中的一个矩形区域,该区域根据我从点列表中获得的点以不同颜色绘制并形成图表中的彩色部分。
最佳答案
您至少可以选择三种方法来创建带有构成热图的彩色矩形的图表。
这是一个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);
}
让我们看看使用一些廉价测试数据的结果:
如您所见,调整大小工作正常..
这是设置我的示例的完整代码:
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;
}
生成的位图看起来很熟悉:
这很简单;但是要将所有额外内容添加到填充保留的空间中并不是那么容易..
关于c# - 绘制二维热图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36342635/
我想要类似于以下伪代码的东西: while input is not None and timer = 5: print "took too long" else: print inp
如何将 MainEngine Observable 转换为 Cold?来自这个例子: public IObservable MainEngine { get
自从手表被发明以来,表盘的方圆之争就始终没有停下来过,在漫长的岁月中,无论是方形还是圆形表盘,人们都为其寻找到足够多的设计元素,让其肆意成长,这种生机与活力后来也延续到了智能手表上,在2014年,这
我正在学习 CUDA,试图解决一些标准问题。例如,我正在使用以下代码求解二维扩散方程。但我的结果与标准结果不同,我无法弄清楚。 //kernel definition __global__ void
我的 Web 应用程序使用 native dll 来实现其部分功能(其位置在 PATH 中提供)。一切正常,直到我对 WAR 进行更改并且 JBoss 热部署此 WAR。此时dll已经找不到了,需要手
我看到这个问题here 。这是关于实现每个发出的项目的延迟。这是根据accepted answer如何实现的: Observable.zip(Observable.range(1, 5) .g
我最近一直在进行冷迁移...这意味着我无法在进行迁移时从应用程序级别读取/写入数据库(维护页面)。 这样就不会因为更改结构而发生错误,而且如果负载很大,我也不希望 mysql 在迁移过程中崩溃。 我的
我是一名优秀的程序员,十分优秀!