Announce: node-red-contrib-ui-heatmap (beta release)

Hi folks,

Damn, I got distracted again from my planning. This time by an interesting question about heat maps (i.e. temperature maps): see discussion.

P.S. It would be nice if somebody could assist @janek in the above discussion. Something tells me that there should be a compact solution to read such an I2C device from Node-RED. But I have no experience with this kind of stuff ... But if we could manage to get that working, it would be a nice project in combination with this new UI node.

I have added a test flow and a series of use cases for my new node on the readme page. For those who don't have time to click on the link, here is a small example of this node in action:

heatmap_result

P.S. the heatmap is calculated on the client-side (i.e. in the dashboard running in your browser). Just set the matrix size to NxM, and make sure you send an array of numbers to it (length NxM).

As usual, all 'constructive' feedback is welcome!

Have fun!
Bart

1 Like

Good work Bart!

Hello, can you post Your flows so that i can understand ,i tried but i m not able to create heat ap ,please help .

@BartButenaers it seems the test flow is missing from the readme.

@purnima, @zenofmud,
Seems indeed I have forgotten to add the flow...
I'm now at work, so I will update the readme page this evening.

@purnima, @zenofmud,

I have added the example flow to the readme file.

As you can see on the readme page, the example flow is now also available via the Node-RED menu. But then you have to install my latest version:

npm install node-red-contrib-ui-heatmap@1.0.0-beta.3

Thanks to @dceejay for this tip ...

I have published version node-red-contrib-ui-heatmap@1.0.0-beta.4 with following changes:

  • The example flow contains now radius 40 instead of 5.

  • The spacing between the matrix points has been fixed, because they were incorrect at the right and bottom sides:
    image

  • @janek requested here to be able to show the input matrix values in the heatmap.
    So I have added a new checkbox in the config screen:

    image

    Which allows this new behaviour:

    heatmap_numbers

2 Likes

A new version node-red-contrib-ui-heatmap@1.0.0-beta.5 has been published, with following changes:

  • To download the third-party library script, now RED.httpNode.get is used instead of RED.httpAdmin.get. This is a tip from @dceejay for UI node developers ...

  • It is now possible to generate a legend below the heatmap (as requested by @janek), and specify how many numbers you want to see in the legend:

    image

    Those numbers will be displayed in the same colors as they would get in the heatmap:

    heatmap_legend

Unfortunately I haven't found how to solve following exception:

image

For some reason the heatmap library thinks incorrectly that the canvas height is 0 from time to time. And then it generates such an exception every time the numbers are set, and nothing is drawn. But I cannot find the cause :woozy_face:

@BartButenaers, thanks, almost there. For now works only once and stops receiving updates. Btw, the minimum and maximum values for the legend and colors should be taken (IMHO) from the input matrix dynamically for each frame

Yes, but to solve that I need help for this issue. I have no clue how I can solve that. So I REALLY hope somebody can help us with that... P.s. could you have a look in the console log to make sure your issue is due to the same error as mine!

Isn't that the case already? As you can see in the above animation, the min and max change. But if you select in the config screen a fixed min and max, then I show those fixed limits...

@janek:
About the DOMException: it looks to me that the dashboard gives us available height 0 only for a small amount of time (e.g. immediately after a deploy). Right after that small time interval, the height will get the correct value (in pixels). But our heatmap received that correct height not before the next deploy, so until then no heatmap updates were drawn ...

I have published a new version node-red-contrib-ui-heatmap@1.0.0-beta.6 which contains a fix for the DOMException.

  • As soon as I detect that the heatmap canvas height is 0, I again determine the available height (that we get from the dashboard) and I set the heatmap canvas height to that value. You can see in the browser console log when this happens:

    The heatmap height is being corrected

  • However sometimes (i.e. when the above correction is calculated during the small interval immediately after the deploy) the height I determine is still 0. In that case I don't draw the new input matrix, since it would fail anyway. As a result this input matrix will never be drawn, which you can see in the browser console log:

    The heatmap is skipped due to invalid canvas size

    But from the moment that the next input matrix arrives, the new matrices will be drawn in the heatmap (since the height has got meanwhile the correct value). For me this solution is sufficient at the moment ...

Could you please test it, and let me know if I can publish it as version 1.0.0 on NPM.

@BartButenaers, thanks works for me with one problem:
In my application, the values come with one decimal point accuracy, eg. 34.5
below is the updated random matrix generator for that:

// Generate some random data
// See https://www.patrick-wied.at/static/heatmapjs/example-minimal-config.html
var len = 200;

msg.payload = [];

while (len--) {
  var value = Math.floor(Math.random()*1000); // was 100
  value = value / 10;
(Math.round(value * 10) / 10).toFixed(1);
  msg.payload.push(value);
}

return msg;

Can you do the rounding to one decimal point in the legend as currently i can display very long numbers (unlimited decimal point) making it invisible

Cheers and thanks
Jan

![image|363x323](upload://nTD9qxqD4KDH9mvPLOc8GPrgIy6.png) 

@BartButenaers
update - within the legend can you make values rounded to integers (no decimal points)?

@janek,

I have released node-red-contrib-ui-heatmap@1.0.0-beta.7 on NPM, which allows you to round the numbers to be displayed (to N decimal places). The numbers that are drawn on top of the heatmap can have a different number of decimals, compared to the numbers in the legend:

image

By default the number of decimals is 0, which means you have nothing behind the decimal separator.

Remark: When you drag a new heatmap node on your flow, the decimals will be default 0. However for your existing heatmap node(s) in your flow, the decimals field will be empty. I haven't added code to migrate existing nodes, since you are the only person testing this node at the moment...

P.S. It is not clear to me whether you have accomplished already to get your MLX90640 temperature values into Node-RED?? If yes, it would be very interesting if you could explain in a new topic (category "share your projects") how you have done that:

  • Material list & links
  • Drawing of electrical wiring
  • A whole example together with this UI node
  • Flow
  • Explanation
  • ...

Since I'm busy developing for Node-RED, I have no time left for experimenting with hardware. So it would be handy if I could just buy such a MLX90640, and follow the setup in your tutorial ...

Thanks in advance! Bart

1 Like

@BartButenaers.
Thanks for your work. I will share my project once it's in a stable state

[{"id":"b966424a.8fbdf","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"38cb1bee.0c69ac","type":"heat-map","z":"b966424a.8fbdf","group":"4b91d6fb.ff5e4","order":0,"width":"16","height":"12","name":"","rows":"32","columns":"24","minMax":false,"minimumValue":"0","maximumValue":"0","backgroundColor":"#ffffff","radius":"40","opacity":0.6,"blur":0.85,"showValues":true,"valuesDecimals":"1","showLegend":true,"legendDecimals":"0","legendCount":"5","x":720,"y":80,"wires":[[]]},{"id":"b596b767.2d0c1","type":"function","z":"b966424a.8fbdf","name":"Generate random matrix","func":"// Generate some random data\n// See https://www.patrick-wied.at/static/heatmapjs/example-minimal-config.html\nvar len = 768;  // 32 x 24 was 200\n\nmsg.payload = [];\n\nwhile (len--) {\n  var value = Math.floor(Math.random()*1000); // was 100\n  value = value / 10;\n(Math.round(value * 10) / 10).toFixed(1);\n  msg.payload.push(value);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":510,"y":80,"wires":[["38cb1bee.0c69ac"]]},{"id":"760688a2.30c198","type":"inject","z":"b966424a.8fbdf","name":"Show heatmap","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":"1","x":280,"y":80,"wires":[["b596b767.2d0c1"]]},{"id":"8e3cd0b9.4c3008","type":"ui_template","z":"b966424a.8fbdf","group":"4b91d6fb.ff5e4","name":"Clock Toolbar","order":4,"width":"0","height":"0","format":"<script id=\"titleScript\" type=\"text/javascript\">\n    $('#clock').remove();\n    var toolbar = $('.md-toolbar-tools');\n    var div = $('<div/>');\n    var p = $('<p/ id=\"clock\">');\n    \n    $('#titleScript').parent().hide();\n    div.append(p);\n    div[0].style.margin = '5px 5px 5px auto';\n    toolbar.append(div);\n\n    function displayTitle(lh) {\n        p.text(lh); \n    }\n    \n    function upTime() {\n        var d = new Date();\n        p.text(d.toLocaleTimeString('de-AT'));\n    }\n\n    \n\n    // Watch the payload and update the title\n    (function(scope) {\n        scope.$watch('msg.payload', function(data) {\n            displayTitle(data);\n        });\n        setInterval(upTime,1000);\n    })(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":260,"y":120,"wires":[[]]},{"id":"4b91d6fb.ff5e4","type":"ui_group","z":"","name":"HeatMap","tab":"e141d62c.0125","order":1,"disp":true,"width":"16","collapse":true},{"id":"e141d62c.0125","type":"ui_tab","z":"","name":"HeatMap","icon":"dashboard","disabled":false,"hidden":false}]
The problem is with the legend displaying only 2 values (instead of 5 as in settings) - see attached
![Heatmap1|620x500](upload://f03PqOZofqXwupSF2P8kboWWBx3.jpeg) ![Heatmap2|352x500](upload://fbyD3tWEtHSPE1WuB3an2CLw3e4.jpeg) 

Hey @janek,

Indeed I can reproduce the problem.
Must be something simple, but for some reason I cannot find the cause :woozy_face:

In my Chrome developer tools, I see that the canvas element has a width of 846 pixels:

image

In the config screen, you have specified that 5 values need to be displayed in this canvas. So the loop will be executed 5 times (i.e. legendCount is 5):

image

The fillText function (on line 175) will display the value on the canvas for the calculated x coordinate. On the next line I show the x coordinates in the console log:

image

To me these x coordinates look fine ...
As I said, it must be something simple, but I cannot find it ...

Hello @janek,

Damn, completely wasted my evening due to this problem ...

There are two ways to set the size of a canvas:

  • As CSS style, which is the display size (i.e. the actual size of the canvas on the screen).
  • As DOM element attributes, which is the matrix of real pixel values in the canvas (ie. the drawing buffer). So this determines the coordinate system that the canvas API will use...

So I just had to multiply all the text X-coordinates with the ratio (CSS width / DOM width).
That is all. Told the solution would be simple ...

I have published a new version node-red-contrib-ui-heatmap@1.0.0-beta.8,which solves the issue when I run your test flow with the large heatmap:

image

@BartButenaers, thanks that works, but if I specify minimum and maximum values, the legend is not displayed