Heat Map for LineCollection

A forum dedicated to WPF version of LightningChart Ultimate.

Moderator: Queue Moderators

Post Reply
hallo888
Posts: 43
Joined: Wed Mar 26, 2014 10:56 am

Heat Map for LineCollection

Post by hallo888 » Tue Oct 14, 2014 9:38 am

Hi,

I would like to create a chart as shown in the attached diagram. The Y-Axis represents of different machines while the X-Axis represents the operating start-end time of machines. Each machine can have multiple start/end time.

Apart from start/end time, each machine, at every time instance will have a temperature attribute to indicate the temperature of the machines at that particular time instance. Therefore the colour for each line will base on temperature at each time instance, according to the heat map.

According to the ExampleMultiColorLinePalette, we can create a palette and then tag the palette to an axis. The colour of the line will then based value on the axis, according the the palette. However for my case, the values to determine the colour is not based on any of the axis, its an attribute of my internal object which i can retrieve while going through every time instance.

How do i achieve what i need? Is it possible to create a heat map (palette) and subsequently colour a line collection based on that? Is there any example that does that?

Please advise.

Thank you in advance.
Attachments
lightning chart_sampel.png
lightning chart_sampel.png (14.12 KiB) Viewed 13344 times

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: Heat Map for LineCollection

Post by ArctionPasi » Tue Oct 14, 2014 10:21 am

Create an IntensityMeshSeries (irregular point interval) or IntensityGridSeries (regular point interval) to represent each line.
LightningChart Support Team, PT

hallo888
Posts: 43
Joined: Wed Mar 26, 2014 10:56 am

Re: Heat Map for LineCollection

Post by hallo888 » Wed Oct 15, 2014 2:50 am

Hi Pasi,

Thanks for the suggestion.

I have tried implementing IntensityMeshSeries. Based on the examples, i created a IntensityPoint[columns,rows] array which will contains all the nodes that i require.

For the following scenario (please refer to first post for an illustration of what i am trying to accomplish):
Machine A
-ON:0Secs OFF:600secs
-ON:700Secs OFF:1300secs

Machine B
-ON:1600secs OFF:2000secs

Machine C
-ON:5000secs OFF:6000secs

The temperature for each machine is updated at per second resolution

I implemented using 1 IntensityPoint array (i.e. 1 IntensityMeshSeries) for each machine. Every machine is represented by 2 rows of nodes(top nodes and bottom nodes to represent the top and bottom of each machine bar respectively), with exactly the same x position and value in order to see the gradient bar? Is there a way to set the thickness of the nodes, so that i do not need number of nodes*2 to represent 1 machine timeline?

I have a total of 254 IntensityMeshSeries in my chart with a total of 500k nodes. Is that too much for the application to handle? Is there a known limit which lightning chart can handle? This is because the larger the number of machines and the longer the machines are in operation, more nodes will be created.

The repainting of the chart also has some delay for example when i pan the chart, the chart is not updated as instantly as using LineCollection. The delay is more obvious when i have a LineCursor moving across the chart (as the chart gets updated every time the cursor position is updated). Any way to fix these performance/repainting issue? I have set the "Optimisation" flag to StaticData and "FullInterpolation" flag to false. Are they required? Are there any other optimisation settings that i need to set?

Though the chart is successfully drawn, i encounterd "SharpDX.SharpDXException: HRESULT: [0x8876086A, Module: [SharpDX.Direct3D9], ApiCode: [3DERR_INVALIDCALL/InvalidCall], Message: Unknown at SharpDX.Result.CheckError()" upon completion(display in the output window in VS2010). Is there any way to fix this? My application is compiled in "Any CPU" configuration running in 64bit windows.

Lastly, the performance reduces when i show the heatmap in the legend. My application runs faster without showing the heatmap in the legend. Is this expected? Any way to show heatmap in the legend without affecting the performance?

Appreciate your help.

Thank You!

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: Heat Map for LineCollection

Post by ArctionPasi » Thu Oct 16, 2014 9:23 am

I'll try to provide an optimal example today.
LightningChart Support Team, PT

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: Heat Map for LineCollection

Post by ArctionPasi » Thu Oct 16, 2014 4:55 pm

Ok, I made brief example.

Code: Select all


const int SeriesCount = 254;

        //External values data array list 
        List<double[][]> m_listValuesDataBySeries = new List<double[][]>(SeriesCount);
        
        void InitChart()
        {
            //Initialize chart
            m_chart.ViewXY.LegendBox.Layout = LegendBoxLayout.Vertical;


            //Setup x-axis
            AxisX axisX = m_chart.ViewXY.XAxes[0];
            axisX.SetRange(0, 24 * 3600); //24 h 

            axisX.ScrollMode = XAxisScrollMode.None;
            axisX.ValueType = AxisValueType.Number;

            AxisY axisY = m_chart.ViewXY.YAxes[0]; 
            axisY.SetRange(0, 255);

            Random rand = new Random(); 

            for(int iSeries = 0; iSeries < SeriesCount ; iSeries ++)
            {
                IntensityGridSeries heatmap = new IntensityGridSeries(m_chart.ViewXY, axisX, axisY);

                heatmap.ContourLineType = ContourLineType.None;
                heatmap.WireframeType = SurfaceWireframeType.None;
                heatmap.Fill = IntensityFillStyle.Paletted;
                heatmap.ValueRangePalette = CreatePalette(heatmap);
                if (iSeries == 0)
                    heatmap.Title.ShowInLegendBox = true; 
                else
                    heatmap.Title.ShowInLegendBox = false; 
                double dMinX = 3600.0 * rand.NextDouble()*20.0;
                heatmap.SetRangesXY(dMinX, dMinX + SeriesLenSeconds, (double)(iSeries) - 0.5, (double)(iSeries) + 0.5);
                
                heatmap.PixelRendering = true;  

                heatmap.Tag = (int) SeriesLenSeconds; //Store data point count to Tag field
                SetHeatmapData(heatmap, (int) SeriesLenSeconds); 

                m_chart.ViewXY.IntensityGridSeries.Add(heatmap);
            }
            


            //Allow chart rendering
            m_chart.EndUpdate();
     }




 void SetHeatmapData(IntensityGridSeries heatmap, int dataPointCount)
        {
            int iCols = dataPointCount;

            double[][] data = new double[1][]; //When using PixelRendering, row count of 1 is enough. When NOT using PixelRendering, set row count = 2. 
            data[0] = new double[iCols];

            Random rand = new Random();

            for (int iC = 0; iC < iCols; iC++)
            {
                data[0][iC] = 50; 
            }

            heatmap.Data = null;//Don't use Data array, we use fast external values data array instead. Set Data to null to free memory.

            heatmap.SetValuesData(data, IntensityGridValuesDataOrder.RowsColumns);

            m_listValuesDataBySeries.Add(data); //Store to list, to access it later when updating the data
            
            
        }


 private ValueRangePalette CreatePalette(IntensityGridSeries ownerSeries)
        {
            ValueRangePalette palette = new ValueRangePalette(ownerSeries);

            palette.Steps.Clear();
            palette.Steps.Add(new PaletteStep(palette, Colors.Blue, 50));
            palette.Steps.Add(new PaletteStep(palette, Colors.Green, 80));
            palette.Steps.Add(new PaletteStep(palette, Colors.Yellow, 110));
            palette.Steps.Add(new PaletteStep(palette, Colors.Red, 150));
            palette.Type = PaletteType.Gradient;
            palette.MinValue = 50; 

            return palette;
        }


I made the update once per sec with a timer, it updates ALL data points in ALL series, so consider it as a worst case scenario.

Code: Select all


      void UpdateAllData()
      {
           m_chart.BeginUpdate();
            
            Random rand = new Random();
            int iSeriesIndex = 0; 
            foreach (IntensityGridSeries heatmap in m_chart.ViewXY.IntensityGridSeries)
            {
                int iDataPointCount = (int) heatmap.Tag;
                double[][] data = m_listValuesDataBySeries[iSeriesIndex];

                int iCols = iDataPointCount;

                for (int iC = 0; iC < iCols; iC++)
                {
                    data[0][iC] = 50 + rand.NextDouble() * 70.0;
                }

                heatmap.InvalidateValuesDataOnly();
                iSeriesIndex++;
            }

            m_chart.EndUpdate(); 
      }

254 heatmaps animated
254 heatmaps animated
heatmaps_animated.jpg (458.23 KiB) Viewed 13315 times
Zoomed some heatmaps
Zoomed some heatmaps
zoomed.jpg (356.52 KiB) Viewed 13315 times
The charts is usable and can be zoomed and panned.

This is the optimal way. I hope it works for you :geek:
LightningChart Support Team, PT

hallo888
Posts: 43
Joined: Wed Mar 26, 2014 10:56 am

Re: Heat Map for LineCollection

Post by hallo888 » Fri Oct 17, 2014 2:56 am

Hi Pasi,

THanks for your very detailed example. I have implemented it but i encountered an error at the series where the operating time stretches across the whole x-axis. For example, in my scenario, x-axis range from 100-70000s there are a total of 6 machines:
Machine A:100 - 5000s
Machine B: 400 - 1800s
Machine C: 110 - 70000s
Machine D: 6000-9000s

The chart only displays up till machine B with no axis label and chart title. The chart lags alot when i do panning and zooming. It seems like its incomplete. I also got the following error kept getting printed in the output window in VS2010:
1) "SharpDX.SharpDXException: HRESULT: [0x8876086A, Module: [SharpDX.Direct3D9], ApiCode: [3DERR_INVALIDCALL/InvalidCall], Message: Unknown at SharpDX.Result.CheckError()"

2) Unknown Exception occured System.NullReferenceException: Object reference not set to an instance of an object. at A.XI.VG(L A, Single B, Single C, Single F, Single G Boolean H, Boolean I)

On top of that i also got alot of "The thread 'ComposeIntensityPixelRenderingData' (0x1e40) has exited with code 0 (0x0)." printed in the output window in VS2010 as well. (i did call the set the heatmap between BeginUpdate(); and EndUpdate())

Do you have any clues on the error msgs? I have check my chart x-axis min and max value, the machine C is certainly within the range of the axis.

It seems like PixelRendering is the cause of the problem. There was no error when i set PixelRendering to false, but nothing was drawn when i did that. U mentioned that the data[][] need to have 2 rows if PixelRendering is false. I have tried doing that, setting the value to be the same as row 1, but sill nothing was printed.

If i draw the timeline for only the first 2 machines, those errors does not get printed. The chart is also completed with axis label, title and able to pan and zoom.

Please advise.

Thanks for your help once again.

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: Heat Map for LineCollection

Post by ArctionPasi » Fri Oct 17, 2014 6:24 am

The thread 'ComposeIntensityPixelRenderingData' (0x1e40) has exited with code 0 (0x0).
This is perfectly normal and only indicates the multithreading works when it constructs rendering data.

Could you perhaps create a small project to reproduce the lagging and problems you are experiencing? I'd appreciate also if you could share some info about your computer. Graphics adapter type, operating system, processor type at least?
LightningChart Support Team, PT

hallo888
Posts: 43
Joined: Wed Mar 26, 2014 10:56 am

Re: Heat Map for LineCollection

Post by hallo888 » Mon Oct 20, 2014 3:13 am

Hi Pasi,

I came up with the following simple project for testing.

Code: Select all

const int numberOfMachines = 254;
const int durationInSecs = 1000;

public MainWindow()
{
    InitializeComponent();
    InitChart();
    CreateHeatMap();
}

private void InitChart()
{
    //Initialize chart
    m_chart.ViewXY.LegendBox.Layout = LegendBoxLayout.Vertical;


    //Setup x-axis
    AxisX axisX = m_chart.ViewXY.XAxes[0];
    axisX.SetRange(0, durationInSecs); //24 h 
    axisX.ScrollMode = XAxisScrollMode.None;
    axisX.ValueType = AxisValueType.Number;

    AxisY axisY = m_chart.ViewXY.YAxes[0]; 
    axisY.SetRange(0, numberOfMachines);
}

private void CreateHeatMap()
{
    m_chart.BeginUpdate();
    for(int i = 1; i <= numberOfMachines ; ++i)
    {
                IntensityGridSeries heatmap = new IntensityGridSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
                heatmap.ContourLineType = ContourLineType.None;
                heatmap.WireframeType = SurfaceWireframeType.None;
                heatmap.Fill = IntensityFillStyle.Paletted;
                heatmap.ValueRangePalette = CreatePalette(heatmap);
                if (i == 1)
                    heatmap.Title.ShowInLegendBox = true; 
                else
                    heatmap.Title.ShowInLegendBox = false; 

                heatmap.SetRangesXY(0, 0+durationInSecs, i - 0.5, i + 0.5);
                
                heatmap.PixelRendering = true;  

                heatmap.Tag = (int) SeriesLenSeconds; //Store data point count to Tag field
                SetHeatmapData(heatmap, durationInSeconds); 

                m_chart.ViewXY.IntensityGridSeries.Add(heatmap);
    }
            
     //Allow chart rendering
     m_chart.EndUpdate();
}

private void SetHeatMap(IntensityGridSeries heatmap, int count)
{
    int iCols = dataPointCount;

    double[][] data = new double[1][];
    data[0] = new double[iCols];

    Random rand = new Random();

    for (int iC = 0; iC < iCols; iC++)
    {
        data[0][iC] = rand.Next(0,180); 
    }

    heatmap.Data = null;

    heatmap.SetValuesData(data, IntensityGridValuesDataOrder.RowsColumns);
}


private ValueRangePalette CreatePalette(IntensityGridSeries ownerSeries)
{
    ValueRangePalette palette = new ValueRangePalette(ownerSeries);

    palette.Steps.Clear();
    palette.Steps.Add(new PaletteStep(palette, Colors.Red, 0));
    palette.Steps.Add(new PaletteStep(palette, Colors.Yellow, 60));
    palette.Steps.Add(new PaletteStep(palette, Colors.Green, 120));
    palette.Steps.Add(new PaletteStep(palette, Colors.Cyan, 180));
    palette.Type = PaletteType.Gradient;
    palette.MinValue = 0; 

    return palette;
}
Tha above example where there are 254 machines, each operating for 1000secs, caused the chart to be quite laggy, when i perform zooming and panning actions.

My GPU info are as follows:
Intel(R) HD Graphics 4000
Render device created: Yes
Pure device: Yes
Fast Vertex format: No
Hardware Vertex Processing: Yes
Anti-Alising: Yes
Shader Model 3: Yes
Index Buffers: 32-Bit
Support arbitary texture size: Yes
Texture Max size (WxH): 8192x8192

System Info:
Intel i7-3520M CPU @ 2.90GHz
8GB Ram
Windows 7 64bit

Do you also have an idea of the following errors which i have encountered previously?

1) "SharpDX.SharpDXException: HRESULT: [0x8876086A, Module: [SharpDX.Direct3D9], ApiCode: [3DERR_INVALIDCALL/InvalidCall], Message: Unknown at SharpDX.Result.CheckError()"

2) Unknown Exception occured System.NullReferenceException: Object reference not set to an instance of an object. at A.XI.VG(L A, Single B, Single C, Single F, Single G Boolean H, Boolean I)

By the way, is it possible to change the width/height of the heatmap bar in the legend box?

Thank you for your help.

Administrator
Site Admin
Posts: 4
Joined: Sun Mar 24, 2013 11:14 am

Re: Heat Map for LineCollection

Post by Administrator » Tue Oct 21, 2014 12:18 pm

To continue with the support, please identify your subscription account to [email protected]. We couldn't find a valid subscription with this user name or e-mail address.

Post Reply