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

@BartButenaers, my flow (using your heatmap) works, but I am not happy as on top of 1Hz update rate, there is about 5s lug most likely due to nodered reading output from command line. Without nodered the application produces output smoothly every 1s. Anyhow, I am looking for another way of reading data from MLX90640 to nodered to make it better (faster, more reliable, higher frame rate). Thank you for your support.
Cheers

[{"id":"27b40865.2d54b","type":"exec","z":"1f1e04cf.2f4383","command":"/temp/RPiMLX90640/./MLX90640 0.5","addpay":true,"append":"0.5","useSpawn":"true","timer":"","oldrc":true,"name":"","x":370,"y":160,"wires":[["d58d97fa.e54e2"],[],[]]},{"id":"d58d97fa.e54e2","type":"function","z":"1f1e04cf.2f4383","name":"Concatenate","func":"if (msg.payload)\n{\n    console.log(\"here\");\n    var status = flow.get('status') || 0;\n    var goodmsg = flow.get('goodmsg') || \"\";\n    var tempmsg = flow.get('tempmsg') || \"\";\n    \n// status:    \n// 0 - searching for 'HERE'\n// 1 - 'HERE' found, searching for 'END'\n// 2 - no 'END' within the msg.payload, concat next msg.payload  \n\n    var fullpayload = msg.payload;\n    \n    if(status === 0)\n    {('')\n        // searching for start ('HERE') PRINTING IMAGE FROM DISPALY IMAGE MAIN')\n        var start1 = fullpayload.indexOf(\"DISPALY IMAGE MAIN\"); \n        if (start1 >= 0)\n        {\n            var s1 = fullpayload.substring(start1+48, fullpayload.length); // was start+49\n            // see if 'END' within the same msg\n            var end1 = s1.indexOf(\"END\");\n            if (end1 >= 0)\n            {\n                // full message within a single output\n                var res1 = s1.substring(0, end1);\n                goodmsg = res1;\n                msg.payload = \"FIRST and FULL\" + goodmsg;\n                // reset for a new search\n                // ?????\n                goodmsg = \"\";\n                tempmsg = \"\";\n                status = 0;\n                flow.set('goodpmsg', goodpmsg);\n                flow.set('tempmsg', tempmsg);\n                flow.set('status', status);\n                return msg;\n            } else\n                // found \"HERE\", not \"END\"\n                {\n                    status = 1;\n                    tempmsg = s1.substring(0, s1.length);\n                    flow.set('tempmsg', tempmsg);\n                    flow.set('status', status);\n                    //msg.payload = status + \" HERE not END\";\n                    //return msg;\n                }\n        }\n\n    }else\n        // status > 0, search for \"END\" ************COLORIZED Data\n        {\n            var end2 = fullpayload.indexOf(\"************COLORIZED Data\");\n            if (end2 >= 0)\n            {\n                var s2 = fullpayload.substring(0, end2);\n                tempmsg = tempmsg + s2;\n                goodmsg = tempmsg;\n                tempmsg = {};\n                status =  0; // ready for a new search                \n                flow.set('tempmsg', tempmsg);                \n                flow.set('goodmsg', goodmsg);\n                flow.set('status', status);\n                msg.payload = goodmsg; \n                \n///////////////////////////////////////////////////////////                \n                // search for a start of new frame at the end of current frame               \n                var s3 = fullpayload.substring(end2, fullpayload.length);\n                var start2 = s3.indexOf(\"HERE\");\n                if (start2 >= 0)\n                    {\n                    var remaining = s3.substring(start2+48, s3.length);\n                    tempmsg = remaining;\n                    goodmsg =\"\";\n                    status = 1;\n                    flow.set('status', status);\n                    flow.set('tempmsg', tempmsg); \n                    flow.set ('goodmsg', goodmsg);\n                    //msg.payload = \"remaining\";\n                    //return msg;\n                    } \n                    //else {\n                    //        status = 0;\n                    //        tempmsg = \"\";\n                    //        goodmsg = \"\";\n                    //        flow.set('status', status);\n                    //        flow.set('tempmsg', tempmsg);\n                    //        flow.set('goodmsg', goodmsg);\n                    //        }\n                          \n/////////////////////////////////////////////////////////////////////                            \n                return msg; \n            }\n        }\n}","outputs":1,"noerr":0,"x":610,"y":140,"wires":[["303b4057.574518","4378e1b.4ed432"]]},{"id":"ddef02ab.ef6ae8","type":"inject","z":"1f1e04cf.2f4383","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":150,"y":80,"wires":[["4062222.84acadc"]]},{"id":"4062222.84acadc","type":"function","z":"1f1e04cf.2f4383","name":"Init flow variables","func":"flow.set('status', 0);\n// 0 - searching for 'HERE'\n// 1 - 'HERE' found, searching for 'END'\n// 2 - no 'END' within the msg.payload, concat next msg.payload\nflow.set('goodmsg', \"\");\nflow.set('tempmsg', \"\");","outputs":1,"noerr":0,"x":350,"y":80,"wires":[[]]},{"id":"303b4057.574518","type":"debug","z":"1f1e04cf.2f4383","name":"","active":false,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":820,"y":100,"wires":[]},{"id":"d2a2b2c5.3e0458","type":"ui_template","z":"1f1e04cf.2f4383","group":"58403db.6185d44","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":140,"y":40,"wires":[[]]},{"id":"4378e1b.4ed432","type":"function","z":"1f1e04cf.2f4383","name":"Create Array","func":"if(msg.payload)\n    {\n    var s = msg.payload ;   \n    var start = 0;\n    var end = 0;\n    // create an array     \n    var heat = [];\n        for(i = 0; i < 768; i++) \n        {\n            end = s.indexOf(\";\", start)\n            //heat[i] = s.substring(start, end); \n            var temp = parseFloat(s.substring(start, end));\n            temp /= 10;\n            (Math.round(temp * 10) / 10).toFixed(1);\n            if (isNaN(temp)) temp = 0.1;\n            if(temp > 150) temp = 100;\n            if(temp < 0.1) temp = 0.1;\n            heat[i] = temp;\n            start = end+1;\n        }        \n    msg.payload = heat;\n    return msg;\n    }","outputs":1,"noerr":0,"x":810,"y":140,"wires":[["9c10f24c.3bc9","7c497721.125df8"]]},{"id":"9c10f24c.3bc9","type":"debug","z":"1f1e04cf.2f4383","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","x":1040,"y":80,"wires":[]},{"id":"7c497721.125df8","type":"heat-map","z":"1f1e04cf.2f4383","group":"58403db.6185d44","order":0,"width":"16","height":"12","name":"","rows":"32","columns":"24","minMax":false,"minimumValue":"1","maximumValue":"50","backgroundColor":"#ffffff","radius":"40","opacity":0.6,"blur":0.85,"showValues":true,"valuesDecimals":"1","showLegend":true,"legendDecimals":"0","legendCount":"5","x":1020,"y":140,"wires":[[]]},{"id":"4b743966.c26368","type":"inject","z":"1f1e04cf.2f4383","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"2","x":120,"y":160,"wires":[["27b40865.2d54b"]]},{"id":"58403db.6185d44","type":"ui_group","z":"","name":"Heatmap","tab":"de218ad6.e9ef6","order":2,"disp":true,"width":"16","collapse":true},{"id":"de218ad6.e9ef6","type":"ui_tab","z":"","name":"NewMLX90640","icon":"dashboard","order":4,"disabled":false,"hidden":false}]

@janek,
This bug has been fixed in node-red-contrib-ui-heatmap@1.0.0-beta.9

@BartButenaers, thanks works well. I managed to make it work fast and reliable using other peopleā€™s work and modifying it slightly. Ready to share it if youā€™re interested
Cheers,
Janek

1 Like

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

Hi @janek,
I have published version 1.0.0 on NPM

can we create a heat map which will show the number of peoples available in the particular area by using any sensors and cloud database??

Hi @purnima,

That is an interesting use case. It remembered me that I didn't have a step by step heatmap tutorial yet, so I have added a tutorial to my readme page.

But if you want to know how to count people in images, it is better that you create a new post on this forum. This post is dedicated to visualizing heatmaps, not to people counting. You 'might' get more feedback in a dedicated post. But it is a rather specific question that you are asking, so I'm not sure whether the Node-RED forum is the best place to ask it. But you can always give it a try ...

Bart

okk .. thank you

Dear BartButenaers, i appreciated a lot your node-red-heatmap contribution, but i have a question, which is partially related to the @purnima made to you, so please excuse me for the repeating, but i think the question he/she made is very related to the heatmap. I was wondering for something similar, how to show some countable variables on a heatmap, for instance the occurance of some object in specific location. So basically i do have a matrix: ["location_id1": 5," location_id2":10," location_id3":25, "location_id4":8 .... etc]. Would it be possible to show it in the heatmap, using the key as a legend and a value as a value on a heatmap. The thing i able to do now is just to show the values using your code. Thanks a lot in advance!

Hi @Miliks,
Could you please make a quick drawing of how you want the heatmap with the legend to look like. I think that I'm misinterpreting your request. When you inject a matrix ["location_id1": 5," location_id2":10," location_id3":25, "location_id4":8 .... etc], do you mean it needs to be visualized like this:

image

Although I don't think that is what you mean, because:

  • The legend would become very long
  • Don't think there is a visual relation between the legend and the numbers in the heatmap
  • There would be better ways to display such a legend (outside of my heatmap widget)

Bart

@BartButenaers sir, can we plot a heat map using latitude and longitudes here.

Hi @purnima,
Could you please give some more information?

  • What do want to achieve?
  • What is the content of the input message for the heatmap node?
  • How do you expect the output to look like? If possible a quick scetch.
  • ...

The heatmap node just displays an array of numerical values. What the numbers represent is not really up to the heatmap widget, and I want to keep it that way: the node should be generally usable for any application. So if you have any proposals to add 'general' functionality, please be my guest ...

Hi @BartButenaers, thanks for your response.
Indeed the way u draw it in not exactly what i have in mind. I would think about something like an array around 10 (max 15) elements with value and a key, something like this

The key is not suppose to be long. Another cool thing would be if the matrix could be 3 dimentional adding timestamp or timeframe as a thierd parameter, what is if we can have something like this:
But this could be made for sure just generating more single heatmaps on the same canvas, i guess.
Thanks,
Mila

@BartButenaerssir , i have number of ip addresses . so i generated the latitude and longitude for these ip addresses , now i want to display a heat map which shows the coordinates .

you could potentially use the worldmap node that actually plots a real map - but can include a heatmap overlay

@dceejay thank you for your reply sir , i m trying that .

@Miliks surely it would make more sense if it was the (short) key that was shown on the map rather than the numbers , as the colour represents the numbers. the keys would have to be really short though , maybe only 3 chars max.
For multiple times can't you just draw multiple n x 1 maps instead ?

@dceejay, the numbers on the map are the numbers, so there no problem with it, my question was to put the key below the heatmap to specify there this numers belong to, becouse it is not just sequential order, but the specific correspondence with the "location".
For the multiple time, u r right, the re-drawing would work fine.

indeed - I was just thinking that if the key was on the map it would show where they are directly. In your case they are 1 dimensional so the key under would be easy to line up - but for most situations where they are 2d then having a list underneath would not help much.

Well, yes, but since u can specify the min and max value, it would not so stright forward relation between the color and the number, but for some cases it would be helpful for sure.
So can u give me any suggestion on how i can draw the legend on the botton line of the heatmap?