-
Notifications
You must be signed in to change notification settings - Fork 8
/
WebMap.json
1 lines (1 loc) · 18.9 KB
/
WebMap.json
1
[{"id":"db2b165142e8c36a","type":"tab","label":"web Map 2","disabled":false,"info":"Gets the flow of COT from FTS and displays on a web map (credit @ampledata#8354).\n The FTH WebMap integration can be installed as part of FTS UI or a stand alone, e.g. for a dashboard. ","env":[]},{"id":"9946c2419fd3c3ae","type":"inject","z":"db2b165142e8c36a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"120","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":170,"y":160,"wires":[["33593e8ece650844"]]},{"id":"33593e8ece650844","type":"function","z":"db2b165142e8c36a","name":"CoT Ping","func":" msg.host = global.get('FTH_FTS_URL');\n msg.port = global.get('FTH_FTS_TCP_Port');\nconst dt = Date.now();\nconst dtD = new Date(dt).toISOString();\nconst dtD5 = new Date(dt + 250000).toISOString();\n\nmsg.payload = {\n event: {\n \"$\": { \n version: \"2.0\", \n type: \"t-x-d-d\", \n uid : \"node-red\",\n time: dtD,\n start: dtD,\n stale: dtD5,\n how: \"m-g\"\n }\n }\n \n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":160,"wires":[["7a0f7d3e4099e946"]]},{"id":"7a0f7d3e4099e946","type":"xml","z":"db2b165142e8c36a","name":"","property":"payload","attr":"","chr":"","x":450,"y":160,"wires":[["52446a6c25605721"]]},{"id":"7c52084b496b83bc","type":"comment","z":"db2b165142e8c36a","name":"Keep Server Connection Alive","info":"","x":200,"y":120,"wires":[]},{"id":"52446a6c25605721","type":"tcp request","z":"db2b165142e8c36a","server":"","port":"","out":"sit","ret":"buffer","splitc":" ","name":"FTS Server","x":590,"y":160,"wires":[["3c62054705949d69","4134eaa89c5c71e0"]]},{"id":"3c62054705949d69","type":"function","z":"db2b165142e8c36a","name":"buf->str","func":"/*\nlet oldPayload = msg.payload;\nmsg.payload = oldPayload.toString();\nreturn msg;\n*/\n\nvar rest = context.rest || \"\";\nvar p = rest + msg.payload.toString();\nvar b = p.split(\"</event>\");\nwhile (b.length > 1) {\n var m = b.shift();\n node.send({ payload: m + \"</event>\" });\n}\ncontext.rest = b;\nreturn;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":740,"y":160,"wires":[["91213e2448bab185"]]},{"id":"91213e2448bab185","type":"xml","z":"db2b165142e8c36a","name":"","property":"payload","attr":"","chr":"","x":870,"y":160,"wires":[["4b3df29696d9fc8f"]]},{"id":"05f4656c40958267","type":"function","z":"db2b165142e8c36a","name":"CoT to Map","func":"/*\nThe following block of code transforms a JSONified CoT Event into a Worldmap \nPoint Payload.\n*/\n\nlet invalid = 9999999;\nlet event = msg.payload[\"event\"];\n\n/* \nIf the CoT Event contains Detail Elements, extract the first one.\nIf there are no Detail Elements, break.\n*/\nlet _detail = event[\"detail\"];\nif (_detail === undefined) { return null; }\nlet detail = _detail[0];\n\n/*\nIf the CoT Event contains a Point element, use it. If not, break.\n*/\nlet point = event[\"point\"];\nif (point === undefined) { return null; }\n\n/* We'll use UID a couple of times, so lets set it as a variable here. */\nlet uid = event[\"$\"][\"uid\"];\n\n/* Extract the Event Type and Affiliation. */\nlet eventType = event[\"$\"][\"type\"];\nif (eventType === \"t-x-d-d\") { return; } // ignore pings\nconsole.log(eventType);\nlet et = eventType.split(\"-\");\nlet affil = et[1];\n\n/* There is no '.' notation in SDR, so mark Neutral. */\nif (affil.includes(\".\")) { affil = \"n\"; } \n\n/* Ram the CoT Event Type portions into a SIDR Type */\nlet SIDC = `s${affil}${et[2]}p${et[3] || \"-\" }${et[4] || \"-\" }${et[5] || \"-\" }--------`;\nSIDC = SIDC.substr(0,12).toUpperCase();\n\n/* Now handle all the weird extra CoT ones... */\nif (eventType === \"a-h-X-i-o\") { \n console.log(\"XIO--->\",SIDC)\n SIDC = \"EHIP--------\" }\nelse if (eventType === \"a-h-X-i-m-d\") { \n console.log(\"XIMD--->\",SIDC)\n SIDC = \"EHNPBB------\" }\nelse if (eventType === \"a-h-X-i-g-e\") { \n console.log(\"XIGE--->\",SIDC)\n SIDC = \"EHNPAC------\" }\n\n\nconsole.log(SIDC);\n\n/* \nPoints on the Worldmap can only have one uniquite identifier, which is also\nthat Points display name. If possible, use a Callsign, otherwise use UID.\n*/\nlet callsign;\nlet _contact = detail[\"contact\"];\nif (_contact === undefined) {\n callsign = uid;\n} else {\n callsign = _contact[0][\"$\"][\"callsign\"]; \n}\n\n/* Mouse-over Label */\nlet label = `Callsign: ${callsign} UID: ${uid}<br/>Type: ${eventType} SIDC: ${SIDC}`\n\nlet remarks = detail[\"remarks\"];\nif (remarks) {\n let remark = remarks[0][\"$\"];\n label = `${label}<br/>${remark}`;\n}\n\nlet track = detail[\"track\"]\nlet bearing = null;\nlet speed = null;\n\nif (track) {\n let course = +track[0][\"$\"][\"course\"];\n if (course) {\n if (course !== invalid && course.toString() !== \"0\") {\n bearing = course;\n }\n }\n\n let _speed = track[0][\"$\"][\"speed\"];\n if (_speed) {\n if (_speed.toString() !== invalid) {\n speed = _speed;\n }\n }\n}\n\n/* \nIf CoT Point CE is set and is not invalid, use that as Worldmap Point Accuracy. \n*/\nlet accuracy = null;\nlet ce = event[\"point\"][0][\"$\"][\"ce\"];\nif (ce.toString() != invalid) { accuracy = ce; }\n\n/* Add a helpful weblink to Worldmap Points. */\nlet weblink = null;\nif (uid.includes(\"ICAO\")) {\n weblink = `https://globe.adsbexchange.com/?icao=${uid.replace(\"ICAO-\", \"\")}`;\n} else if (uid.includes(\"APRS\")) {\n weblink = `https://qrz.com/db/${uid.replace(\"APRS.\", \"\").split(\"-\")[0]}`;\n}\n\nlet lat = event[\"point\"][0][\"$\"][\"lat\"];\nlet lon = event[\"point\"][0][\"$\"][\"lon\"];\nlet alt = event[\"point\"][0][\"$\"][\"hae\"];\n\nif (lat == 0 && lon == 0 && alt == 0) {\n console.log(callsign,\"is at null island.\",eventType)\n lat = Math.round(1000000 * lat + Math.random() * 10000) / 1000000;\n lon = Math.round(1000000 * lon + Math.random() * 10000) / 1000000;\n}\n\n/* Serialize as a Worldmap compatible Payload. */\nmsg.payload = {\n name: callsign,\n tooltip: label,\n lat: lat,\n lon: lon,\n alt: alt,\n speed: speed,\n bearing: bearing,\n accuracy: accuracy,\n SIDC: SIDC,\n ttl: 600,\n weblink: weblink,\n layer: eventType,\n options: {scale:0.8}\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":990,"y":40,"wires":[[]]},{"id":"8c35a77a0d0d3f3d","type":"comment","z":"db2b165142e8c36a","name":"Connect to FTS","info":"","x":600,"y":120,"wires":[]},{"id":"586a2364ecb46529","type":"comment","z":"db2b165142e8c36a","name":"Convert to Worldmap Payload","info":"","x":1060,"y":120,"wires":[]},{"id":"885b808dd8a4895d","type":"debug","z":"db2b165142e8c36a","name":"TAK map output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1260,"y":80,"wires":[]},{"id":"3a1205e793d055fa","type":"worldmap","z":"db2b165142e8c36a","name":"TAK Map","lat":"43","lon":"13","zoom":"4","layer":"EsriDG","cluster":"","maxage":"5000","usermenu":"show","layers":"show","panit":"false","panlock":"false","zoomlock":"false","hiderightclick":"false","coords":"deg","showgrid":"true","allowFileDrop":"true","path":"/tak-map","overlist":"DR,CO,RA,DN,BU,RW,SN,AC,TL,HM","maplist":"OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,OpTop,SW","mapname":"","mapurl":"","mapopt":"","mapwms":false,"x":1240,"y":220,"wires":[]},{"id":"71787ea1acdd3c9a","type":"worldmap in","z":"db2b165142e8c36a","name":"","path":"/tak-map","events":"connect","x":200,"y":280,"wires":[["3f1c45fae45fc2ae"]]},{"id":"361f42b16f36cb0d","type":"worldmap in","z":"db2b165142e8c36a","name":"event","path":"/tak-map","events":"other","x":190,"y":380,"wires":[["9e3dcf9240c85d5a"]]},{"id":"bf877041be384805","type":"debug","z":"db2b165142e8c36a","name":"No form!!","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":540,"wires":[]},{"id":"3f1c45fae45fc2ae","type":"function","z":"db2b165142e8c36a","name":"form","func":"msg.payload = { command: {\ncontextmenu: String.raw`\n<table border=0><tr>\n<th>Attitude</th><td><select id=\"myatt\">\n <option value=\"friend\">Friend</option>\n <option value=\"hostile\">Hostile</option>\n <option value=\"neutral\">Neutral</option>\n <option value=\"unknown\">Unknown</option>\n <option value=\"pending\">Pending</option>\n <option value=\"suspect\">Suspect</option>\n <option value=\"assumed\">Assumed Friend</option>\n </select></td>\n</tr><tr>\n<th>Type</th><td><select id=\"mytyp\" value=\"Ground\" onchange=\"document.getElementById('mynam').value = this.value+'-'+parseInt(Math.random()*9999);\">\n <option value=\"Rifleman\">Rifleman</option>\n <option value=\"Grenadier\">Grenadier</option>\n <option value=\"Gnd Combat Infantry Mortar\">Mortar</option>\n <option value=\"Medic\">Medic</option>\n <option value=\"Gnd Combat Infantry anti Tank\">anti Tank</option>\n <option value=\"Sniper\">Sniper</option>\n <option value=\"Recon\">Recon</option>\n <option value=\"Gnd Combat Infantry air defense\">AA</option>\n <option value=\"Engineer\">Engineer</option>\n</select></td>\n</tr><tr>\n<th>Timeout</th><td><input type=\"number\" id=\"mytout\" step=\"10\" value=\"600\">S</td>\n</tr><tr>\n<th>Name</th><td><input type=\"text\" id=\"mynam\"></td>\n</tr><tr>\n<th><input type=\"button\" value=\"Send\" onclick=\n'feedback(\"myform\",{\n \"attitude\":document.getElementById(\"myatt\").value,\n \"geobject\":document.getElementById(\"mytyp\").value,\n \"timeout\":document.getElementById(\"mytout\").value,\n \"name\":document.getElementById(\"mynam\").value,\n},null,true)'\n></th>\n</tr></table>\n`\n}}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1010,"y":260,"wires":[["3a1205e793d055fa"]]},{"id":"17650e362d95de70","type":"config","z":"db2b165142e8c36a","name":"FTH Global Config","properties":[{"p":"FTH_FTS_URL","pt":"global","to":"0.0.0.0","tot":"str"},{"p":"FTH_FTS_TCP_Port","pt":"global","to":"8087","tot":"str"},{"p":"FTH_FTS_API_Port","pt":"global","to":"19023","tot":"str"},{"p":"FTH_FTS_API_Auth","pt":"global","to":"token","tot":"str"},{"p":"FTH_FTS_STREAM_Port","pt":"global","to":"8554","tot":"str"},{"p":"FTH_FTS_VIDEO_URL","pt":"global","to":"0.0.0.0","tot":"str"},{"p":"FTH_FTS_VIDEO_API_PORT","pt":"global","to":"9997","tot":"str"}],"active":true,"x":190,"y":80,"wires":[]},{"id":"f9c1c3d2b0dcf4da","type":"http request","z":"db2b165142e8c36a","name":"Post COT to FTS","method":"POST","ret":"txt","paytoqs":"ignore","url":"{{{addr}}}:{{{port}}}/ManageGeoObject/postGeoObject","tls":"","persist":false,"proxy":"","authType":"bearer","senderr":false,"x":890,"y":440,"wires":[["e42b1fdd3dd68980"]]},{"id":"64334cba1273e06c","type":"function","z":"db2b165142e8c36a","name":"","func":"\n msg.addr = global.get('FTH_FTS_URL');\n msg.port = global.get('FTH_FTS_API_Port');\nlet lon = msg.payload.lon;\n let lat = msg.payload.lat;\n let attitude = msg.payload.value.attitude;\n let geoobject = msg.payload.value.geobject;\n let timeOut = msg.payload.name.timeout;\n let aName = msg.payload.value.name;\n let token = global.get('FTH_FTS_API_Auth');\n\n \nmsg.headers = {};\nmsg.headers['Authorization'] = \"Bearer \"+ token;\n \n\nmsg.payload = [];\n\n msg.payload={\n longitude: lon,\n latitude: lat,\nattitude: attitude,\ngeoObject: geoobject,\nhow: \"nonCoT\",\nname: aName,\ntimeout: timeOut \n };\n \nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":680,"y":440,"wires":[["f9c1c3d2b0dcf4da"]]},{"id":"e42b1fdd3dd68980","type":"debug","z":"db2b165142e8c36a","name":"API Response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1120,"y":440,"wires":[]},{"id":"9e3dcf9240c85d5a","type":"switch","z":"db2b165142e8c36a","name":"check message","property":"payload.value.name","propertyType":"msg","rules":[{"t":"nempty"},{"t":"empty"}],"checkall":"true","repair":false,"outputs":2,"x":440,"y":420,"wires":[["64334cba1273e06c"],["bf877041be384805"]]},{"id":"647af514a3fc61f2","type":"comment","z":"db2b165142e8c36a","name":"Add the menu","info":"","x":210,"y":240,"wires":[]},{"id":"57d4f22db956d69b","type":"comment","z":"db2b165142e8c36a","name":"Get the result from the post","info":"","x":230,"y":340,"wires":[]},{"id":"0565f229206163d0","type":"comment","z":"db2b165142e8c36a","name":"CONFIG ME!!!!","info":"please set the variables to the proper IP","x":170,"y":40,"wires":[]},{"id":"4134eaa89c5c71e0","type":"debug","z":"db2b165142e8c36a","name":"Strem output","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":770,"y":80,"wires":[]},{"id":"4b3df29696d9fc8f","type":"function","z":"db2b165142e8c36a","name":"CoT to Map","func":"/*\nThe following block of code transforms a JSONified CoT Event into a Worldmap \nPoint Payload.\n*/\nlet ttl = 3600;\nlet deleted = false; //= 'undefined';\nlet invalid = 9999999;\nlet event = msg.payload[\"event\"];\n\n/* \nIf the CoT Event contains Detail Elements, extract the first one.\nIf there are no Detail Elements, break.\n*/\nlet _detail = event[\"detail\"];\nif (_detail === undefined) { return null; }\nlet detail = _detail[0];\n\n/*\nIf the CoT Event contains a Point element, use it. If not, break.\n*/\nlet point = event[\"point\"];\nif (point === undefined) { return null; }\n\n/* We'll use UID a couple of times, so lets set it as a variable here. */\nlet uid = event[\"$\"][\"uid\"];\n\n/* Extract the Event Type and Affiliation. */\nlet eventType = event[\"$\"][\"type\"];\nif (eventType === \"t-x-d-d\") { return; } // ignore pings\n\nlet et = eventType.split(\"-\");\nlet affil = et[1];\n\n/* There is no '.' notation in SDR, so mark Neutral. */\nif (affil.includes(\".\")) { affil = \"n\"; }\n\n/* Ram the CoT Event Type portions into a SIDR Type */\nlet SIDC = `s${affil}${et[2]}p${et[3] || \"-\"}${et[4] || \"-\"}${et[5] || \"-\"}--------`;\n\n/* Now handle all the weird extra CoT ones... */\nif ((/a-.-X-i-o/).test(eventType)) { SIDC = `E${affil}IP--------` }\nelse if ((/a-.-X-i-g-a/).test(eventType)) { SIDC = `EFNPAB------` }\nelse if ((/a-.-X-i-g-e/).test(eventType)) { SIDC = `EFNPAC------` }\nelse if ((/a-.-X-i-g-l/).test(eventType)) { SIDC = `EFNPAD------` }\nelse if ((/a-.-X-i-g-s/).test(eventType)) { SIDC = `EFNPAE------` }\nelse if ((/a-.-X-i-g-v-e/).test(eventType)) { SIDC = `WFSPWSVE----` }\nelse if ((/a-.-X-i-g-v/).test(eventType)) { SIDC = `EFNPAG------` }\nelse if ((/a-.-X-i-g/).test(eventType)) { SIDC = `E${affil}NPA-------` }\nelse if ((/a-.-X-i-m-c/).test(eventType)) { SIDC = `WFSPWSTSS---` }\nelse if ((/a-.-X-i-m-d/).test(eventType)) { SIDC = `EFNPBB------` }\nelse if ((/a-.-X-i-m-n/).test(eventType)) { SIDC = `EFNPBM------` }\nelse if ((/a-.-X-i-f/).test(eventType)) { SIDC = `E${affil}IPC-------` }\nelse if ((/a-.-X-i-h/).test(eventType)) { SIDC = `E${affil}OPA-------` }\nelse if ((/a-.-X-i-t-v-a/).test(eventType)) { SIDC = `E${affil}IPHA------` }\n\nelse if ((/a-.-G-I-X-H/).test(eventType)) { SIDC = `S${affil}GPIXH---H-` }\nelse if ((/a-.-G-I-i-l/).test(eventType)) { SIDC = `E${affil}OPDJC-----` }\nelse if ((/a-.-G-I-R-P/).test(eventType)) { SIDC = `S${affil}GPIRP---H-` }\nelse if ((/a-.-G-I-U-E/).test(eventType)) { SIDC = `S${affil}GPIUE---H-` }\nelse if ((/a-.-G-I-U-T/).test(eventType)) { SIDC = `S${affil}GPIUT---H-` }\nelse if ((/a-.-G-I-i-e/).test(eventType)) { SIDC = `E${affil}OPBC------` }\n\nelse if ((/a-.-G-U-C-V-R-E/).test(eventType)) { SIDC = `S${affil}APMFO-----` }\nelse if ((/a-.-A-M-F-Q-H/).test(eventType)) { SIDC = `S${affil}APMFH-----` }\n\nelse if ((/a-.-G-I-U-T/).test(eventType)) { SIDC = `S${affil}GPIUT---H-` }\nelse if ((/b-r-.-O-I-V/).test(eventType)) { SIDC = `S${affil}EFIPAC------` }\n// emergency open send symbol and reduce ttl\nelse if ((/b-a-o-opn/).test(eventType)) {\n SIDC = `S${affil}GDU-------`;\n //{ SIDC = `SFGPUCI-----` ;\n ttl = 36;\n node.warn('emergency Open');\n}\nelse if ((/b-a-o-tbl/).test(eventType)) {\n SIDC = `S${affil}GDU-------`;\n //{ SIDC = `SFGPUCI-----` ;\n ttl = 36;\n node.warn('trouble emergency Open');\n}\nelse if ((/b-a-o-pan/).test(eventType)) {\n SIDC = `S${affil}GXU-------`;\n //{ SIDC = `SFGPUCI-----` ;\n ttl = 36;\n node.warn('ring the bell emergency Open');\n}\n//emergency is closed SFGDU-------\nelse if ((/b-a-o-can/).test(eventType)) {\n //deleted = true\n SIDC = `SFGPUCI-----`; \n node.warn('emergency closed');\n }\n\n\nconsole.log(eventType, \"--->\", SIDC);\nSIDC = SIDC.substr(0, 12).toUpperCase();\n\n/* \nPoints on the Worldmap can only have one uniquite identifier, which is also\nthat Points display name. If possible, use a Callsign, otherwise use UID.\n*/\nlet callsign;\nlet _contact = detail[\"contact\"];\nif (_contact === undefined) {\n callsign = uid;\n} else {\n callsign = _contact[0][\"$\"][\"callsign\"];\n}\n\n/* Mouse-over Label */\nlet label = `Callsign: ${callsign} UID: ${uid}<br/>Type: ${eventType} SIDC: ${SIDC}`\n\nlet remarks = detail[\"remarks\"];\nif (remarks) {\n let remark = remarks[0][\"$\"];\n label = `${label}<br/>${remark}`;\n}\n\nlet track = detail[\"track\"]\nlet bearing = null;\nlet speed = null;\n\nif (track) {\n let course = +track[0][\"$\"][\"course\"];\n if (course) {\n if (course !== invalid) {\n bearing = course;\n }\n }\n\n let _speed = +track[0][\"$\"][\"speed\"];\n if (_speed) {\n if (_speed !== invalid) {\n speed = _speed;\n }\n }\n}\n\n/* \nIf CoT Point CE is set and is not invalid, use that as Worldmap Point Accuracy. \n*/\nlet accuracy = null;\nlet ce = +event[\"point\"][0][\"$\"][\"ce\"];\nif (ce != invalid) { accuracy = ce; }\n\n/* Add a helpful weblink to Worldmap Points. */\nlet weblink = null;\nif (uid.includes(\"ICAO\")) {\n weblink = `https://globe.adsbexchange.com/?icao=${uid.replace(\"ICAO-\", \"\")}`;\n} else if (uid.includes(\"APRS\")) {\n weblink = `https://qrz.com/db/${uid.replace(\"APRS.\", \"\").split(\"-\")[0]}`;\n}\nelse {\n weblink = 'https://spatialillusions.com/unitgenerator/';\n}\n\nlet lat = +event[\"point\"][0][\"$\"][\"lat\"];\nlet lon = +event[\"point\"][0][\"$\"][\"lon\"];\nlet alt = +event[\"point\"][0][\"$\"][\"hae\"];\nif (alt == invalid) { alt = undefined; }\n\nif (lat == 0 && lon == 0 && !alt) {\n console.log(callsign, \"is at null island.\", eventType)\n lat = Math.round(1000000 * lat + Math.random() * 10000) / 1000000;\n lon = Math.round(1000000 * lon + Math.random() * 10000) / 1000000;\n}\n\n/* Serialize as a Worldmap compatible Payload. */\nmsg.payload = {\n name: callsign,\n tooltip: label,\n deleted: deleted,\n lat: lat,\n lon: lon,\n alt: alt,\n speed: speed,\n bearing: bearing,\n accuracy: accuracy,\n type: eventType,\n SIDC: SIDC,\n ttl: ttl,\n weblink: weblink,\n layer: eventType,\n options: { scale: 0.8 }\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1010,"y":160,"wires":[["3a1205e793d055fa","885b808dd8a4895d"]]},{"id":"bbaeafe55e6385e4","type":"comment","z":"db2b165142e8c36a","name":"APi Token here","info":"","x":880,"y":400,"wires":[]}]