2016-03-31 15 views
4

Ich habe ein Diagramm, auf dem ich eine Heatmap darstellen möchte; Die einzigen Daten, die ich habe, sind Luftfeuchtigkeit und Temperatur, die einen Punkt im Diagramm darstellen.Plotten 2D Heat Map

Wie bekomme ich den rechteckigen Typ der Heatmap auf dem Diagramm in C#?

Was ich will, ist ähnlich nachfolgendes Bild:

enter image description here

Was ich wirklich will, ein rechteckiger Bereich in der Tabelle ist, die in verschiedenen Farben auf der Basis des Punkt aufgetragen ist, dass ich aus der Liste erhalten von Punkte und bilden den bunten Abschnitt in der Tabelle.

+1

Das Problem ist, dass Sie nicht wissen, was Ihr Problem ist. Hast du Code? Warum funktioniert es nicht? Bitte lesen Sie die [FAQ] und [Fragen]. – Marco

Antwort

7

Sie haben die Wahl zwischen mindestens drei Möglichkeiten, ein Diagramm mit farbigen Rechtecken zu erstellen, die eine Heatmap bilden.

Hier ist eine example , die eine DataGridView verwendet/missbraucht. Während ich dies nicht vorschlagen würde, enthält der Beitrag eine nützliche Funktion, die schöne Farblisten für Ihre Aufgabe erstellt.

Dann gibt es die Möglichkeit, das Diagramm mit GDI + Methoden zu zeichnen, nämlich Graphics.FillRectangle. Das ist nicht schwer, aber wenn Sie einmal die schönen Extras bekommen wollen, die eine Chart-Steuerung bietet, wie Skalierung, Achsen, Tooltips usw., summiert sich die Arbeit ... Siehe unten!

Schauen wir uns Option drei an: Mit dem Chart Steuerelement aus dem Namespace DataVisualization.

Lassen Sie uns zunächst davon ausgehen, dass Sie eine Liste der Farben erstellt haben:

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

Und, dass Sie es geschafft haben, Ihre Daten auf ein 2D-Array von int-Indizes zu projizieren, die in der Farbliste zeigen:

int[,] coloredData = null; 

Als nächstes müssen Sie eine ChartType für Ihren Pick Series S1 Es ist wirklich nur ein ich daran denken kann helfen:

S1.ChartType = SeriesChartType.Point; 

Punkte werden von Markers angezeigt. Wir wollen die nicht wirklich als eine der Standard MarkerTypes angezeigt.

Square wäre in Ordnung, wenn wir Quadrate anzeigen wollten; aber für Rechtecke wird es nicht gut funktionieren: Auch wenn wir sie überlappen noch Punkte an den Grenzen sein, die eine andere Größe haben, weil sie nicht vollständig überlappen ..

So verwenden wir einen benutzerdefinierten Marker durch Setzen Sie die MarkerImage von jedem Punkt auf eine Bitmap einer geeigneten Größe und Farbe.

Hier ist eine Schleife, die die DataPoints in unsere Series und setzen jeweils ein MarkerImage zu haben, fügen hinzu: ein MarkerImage, dass es auf der Platte nicht auf einem Pfad gesetzt ist,:

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]; 

    } 

Dies geschieht einiges zu erklären muss sich in der Chart's Images Sammlung befinden.Dies bedeutet, dass es vom Typ NamedImage sein muss. Jedes Bild reicht aus, aber es muss eine eindeutige Namenszeichenfolge hinzugefügt werden, um es im NamedImagesCollection zu identifizieren. Ich wählte die Namen zu "NI1", "NI2" ..

Offensichtlich müssen wir alle diese Bilder erstellen; Hier ist eine Funktion, um das zu tun:

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)); 
    } 
} 

Wir wollen alle Marker mindestens in etwa die richtige Größe haben; so whenevet dass Größenänderungen haben wir es wieder:

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); 
} 

Das ist nicht viel wie die InnerPlotPosition über Details schert, das heißt die tatsächliche Fläche zu ziehen; also hier ist ein bisschen Platz für Verfeinerung ..!

Wir nennen das, wenn wir die Karte gesetzt wird, sondern auch auf Ändern der Größe:

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

Schauen wir uns das Ergebnis einen Blick ein paar billige Testdaten verwenden:

enter image description hereenter image description here

Wie Sie können sehen, Größenanpassung funktioniert ok ..

Hier ist der vollständige Code aufgebaut mein Beispiel:

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]; 
     } 
} 

Ein paar Anmerkungen zu Einschränkungen:

  • Der Punkt wird oben auf jeder Rasterlinie immer sitzen. Wenn Sie diese wirklich benötigen, müssen Sie sie oben in einem der Paint Ereignisse zeichnen.

  • Die Bezeichnungen beziehen sich auf die Ganzzahl-Indizes des Datenarrays. Wenn Sie die Originaldaten anzeigen möchten, können Sie zum Beispiel CustomLabels zu den Achsen hinzufügen. Siehe here for an example!

Dies sollten Sie eine Vorstellung davon, was man mit einem Chart Kontrolle tun können; abzuschließen hier Ihre Verwirrung ist, wie man diese Rechtecke in GDI + mit den gleichen Farben und Daten zeichnen:

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; 
} 

Die resultierende Bitmap kommt mir bekannt vor:

enter image description here

Das war einfach; aber, um alle Extrakosten in den Raum hinzuzufügen, der durch das Auffüllen reserviert wird, ist nicht so einfach.

+0

Danke für die Antwort Ich übersetze etwas Logik daraus. – LittleThunder