Image Mosaic
Get images from Instagram and blit them into an image mosaic
Requirements
Note
- new images and positions are injected at regular intervals
- the
combine
function-node queues incoming messages so they can be processed in pairs
[{"id":"a77b2b14.9407e8","type":"tab","label":"Mosaic","disabled":false,"info":""},{"id":"d25dcadb.664408","type":"change","z":"a77b2b14.9407e8","name":"","rules":[{"t":"set","p":"mosaic","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":320,"wires":[["6b7efd53.e0e25c"]]},{"id":"63e2eaeb.172014","type":"jimp-image","z":"a77b2b14.9407e8","name":"","data":"{\"w\":750,\"h\":750,\"background\":\"#fff\"}","dataType":"json","ret":"img","parameter1":"","parameter1Type":"msg","parameter2":"","parameter2Type":"msg","parameter3":"","parameter3Type":"msg","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","parameterCount":0,"jimpFunction":"none","selectedJimpFunction":{"name":"none","fn":"none","description":"Just loads the image.","parameters":[]},"x":330,"y":320,"wires":[["d25dcadb.664408"]]},{"id":"1377f1ff.a50d36","type":"function","z":"a77b2b14.9407e8","name":"grid points","func":"\nmsg.payload = createGridPoints( msg.payload );\nreturn msg;\n\nfunction createGridPoints(ranges) {\n if (ranges.length > 0) {\n let newrange = [];\n let range = createRange(... ranges[0]);\n let rest = createGridPoints(ranges.slice(1));\n for(let i = 0; i < range.length; i++) {\n for(let j = 0; j < rest.length; j++) {\n let a = [ ...rest[j], ... range[i]];\n newrange.push(a);\n }\n }\n return newrange;\n } else {\n return [[]];\n }\n}\n\nfunction createRange(min, max, step) {\n let range = [];\n for(let i = min; i <= max; i+= step) {\n range.push([i]);\n }\n return range;\n}","outputs":1,"noerr":0,"x":350,"y":740,"wires":[["c8871beb.05c338"]]},{"id":"93665af7.33e48","type":"inject","z":"a77b2b14.9407e8","name":"","topic":"","payload":"[[0, 749, 150], [0, 749, 150]]","payloadType":"json","repeat":"75","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":740,"wires":[["1377f1ff.a50d36"]]},{"id":"6b7efd53.e0e25c","type":"image viewer","z":"a77b2b14.9407e8","name":"","width":"750","data":"mosaic","dataType":"flow","x":1410,"y":320,"wires":[[]]},{"id":"95c5dae6.27f74","type":"change","z":"a77b2b14.9407e8","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"image","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":600,"wires":[["ddb02377.c84ac8"]]},{"id":"e0cdea30.c10748","type":"change","z":"a77b2b14.9407e8","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"point","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":740,"wires":[["ddb02377.c84ac8"]]},{"id":"30c70103.2565ce","type":"jimp-image","z":"a77b2b14.9407e8","name":"","data":"mosaic","dataType":"flow","ret":"img","parameter1":"payload.image","parameter1Type":"msg","parameter2":"payload.point[0]","parameter2Type":"msg","parameter3":"payload.point[1]","parameter3Type":"msg","parameter4":"","parameter4Type":"none","parameter5":"","parameter5Type":"none","parameter6":"","parameter6Type":"none","parameter7":"","parameter7Type":"none","parameter8":"","parameter8Type":"msg","parameterCount":7,"jimpFunction":"blit","selectedJimpFunction":{"name":"blit","fn":"blit","description":"blit the image with another Jimp image at x, y, optionally cropped","parameters":[{"name":"src","type":"","required":true,"hint":"the source Jimp instance","defaultType":"msg","defaultValue":"payload"},{"name":"x","type":"num","required":true,"hint":"the x position to blit the image"},{"name":"y","type":"num","required":true,"hint":"the y position to blit the image"},{"name":"srcx","type":"num","required":false,"hint":"the x position from which to crop the source image"},{"name":"srcy","type":"num","required":false,"hint":"the y position from which to crop the source image"},{"name":"srcw","type":"num","required":false,"hint":"the width to which to crop the source image"},{"name":"srch","type":"num","required":false,"hint":"the height to which to crop the source image"}]},"x":1210,"y":660,"wires":[["d25dcadb.664408"]]},{"id":"18f90079.90f348","type":"jimp-image","z":"a77b2b14.9407e8","name":"","data":"payload","dataType":"msg","ret":"img","parameter1":"150","parameter1Type":"num","parameter2":"150","parameter2Type":"num","parameter3":"RESIZE_NEAREST_NEIGHBOR","parameter3Type":"resizeMode","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","parameterCount":3,"jimpFunction":"resize","selectedJimpFunction":{"name":"resize","fn":"resize","description":"resize the image. One of the w or h parameters can be set to automatic (\"Jimp.AUTO\" or -1).","parameters":[{"name":"w","type":"num|auto","required":true,"hint":"the width to resize the image to (or \"Jimp.AUTO\" or -1)"},{"name":"h","type":"num|auto","required":true,"hint":"the height to resize the image to (or \"Jimp.AUTO\" or -1)"},{"name":"mode","type":"resizeMode","required":false,"hint":"a scaling method (e.g. Jimp.RESIZE_BEZIER)"}]},"x":570,"y":600,"wires":[["95c5dae6.27f74"]]},{"id":"c8871beb.05c338","type":"split","z":"a77b2b14.9407e8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":570,"y":740,"wires":[["e0cdea30.c10748"]]},{"id":"ddb02377.c84ac8","type":"function","z":"a77b2b14.9407e8","name":"combine","func":"// combine elements from both topics\nlet points = context.get(\"points\") || [];\nlet images = context.get(\"images\") || [];\nlet timer = context.get(\"timer\");\n\n// reset queues if timer is exceeded (timeout 10000ms)\nlet now = Date.now();\nif(now - timer > 10000) {\n points = [];\n images = [];\n}\n\n// add new items to the queues\nif(msg.topic === \"image\") images.push( msg.payload );\nif(msg.topic === \"point\") points.push( msg.payload );\n\n// pop items pairwise off the queue + create a new message\nlet result = null;\nif( images.length > 0 && points.length > 0 ) {\n result = { \n payload: {\n image: images.shift(),\n point: points.shift()\n }\n }\n}\n\ncontext.set(\"timer\", now);\ncontext.set(\"images\", images);\ncontext.set(\"points\", points);\n\nreturn result;","outputs":1,"noerr":0,"x":960,"y":660,"wires":[["30c70103.2565ce"]]},{"id":"46371647.674d18","type":"inject","z":"a77b2b14.9407e8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":130,"y":320,"wires":[["63e2eaeb.172014"]]},{"id":"6e8eee4c.1c6888","type":"inject","z":"a77b2b14.9407e8","name":"hashtag: selfie","topic":"","payload":"selfie","payloadType":"str","repeat":"3","crontab":"","once":true,"onceDelay":0.1,"x":140,"y":100,"wires":[["38c4d5f5.db6cea"]]},{"id":"ed366462.794df","type":"http request","z":"a77b2b14.9407e8","name":"download JSON from URL","method":"GET","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":700,"y":100,"wires":[["7a8fe18f.f7292"]]},{"id":"38c4d5f5.db6cea","type":"template","z":"a77b2b14.9407e8","name":"construct instagram tag URL","field":"url","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"https://www.instagram.com/explore/tags/{{{payload}}}/?__a=1","output":"str","x":400,"y":100,"wires":[["ed366462.794df"]]},{"id":"7a8fe18f.f7292","type":"change","z":"a77b2b14.9407e8","name":"extract thumbnail url","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.graphql.hashtag.edge_hashtag_to_media.edges[0].node.thumbnail_resources[0].src","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":980,"y":100,"wires":[["39ebdf37.07ea68"]]},{"id":"39ebdf37.07ea68","type":"image viewer","z":"a77b2b14.9407e8","name":"download and preview","width":"150","data":"payload","dataType":"msg","x":1440,"y":100,"wires":[["18f90079.90f348"]]}]