@tak-ps/node-cot 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,11 @@
10
10
 
11
11
  ## Version History
12
12
 
13
+ ### v2.6.0
14
+
15
+ - :tada: Add support for passing style properties via GeoJSON Properties
16
+ - :rocket: Add support for encoding/decoding 32bit signed ARGB values
17
+
13
18
  ### v2.5.0
14
19
 
15
20
  - :tada: Automatically perform basic schema validation on CoT Creation
package/lib/color.js ADDED
@@ -0,0 +1,46 @@
1
+ import _color from 'color';
2
+
3
+ /**
4
+ * Helper functions for working with CoT Colours
5
+ *
6
+ * @param {Number|Number[]} color 32bit packged ARGB or [A, R, G, B]
7
+ * @class
8
+ */
9
+ export default class Color {
10
+ constructor(color) {
11
+ if (!isNaN(Number(color))) {
12
+ this.r = (color >> 16) & 255;
13
+ this.g = (color >> 8) & 255;
14
+ this.b = (color >> 0) & 255;
15
+ this.a = ((color >> 24) & 255) / 255;
16
+ } else if (Array.isArray(color)) {
17
+ this.a = color[0];
18
+ this.r = color[1];
19
+ this.g = color[2];
20
+ this.b = color[3];
21
+ } else {
22
+ const c = _color(color);
23
+
24
+ this.a = c.valpha;
25
+ this.r = c.color[0];
26
+ this.g = c.color[1];
27
+ this.b = c.color[2];
28
+ }
29
+ }
30
+
31
+ as_32bit() {
32
+ return (this.a << 24) | (this.r << 16) | (this.g << 8) | this.b;
33
+ }
34
+
35
+ as_opacity() {
36
+ return this.a;
37
+ }
38
+
39
+ as_argb() {
40
+ return [this.a, this.r, this.b, this.g];
41
+ }
42
+
43
+ as_rgb() {
44
+ return [this.r, this.b, this.g];
45
+ }
46
+ }
package/lib/xml.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import xmljs from 'xml-js';
2
2
  import Util from './util.js';
3
+ import Color from './color.js';
3
4
  import PointOnFeature from '@turf/point-on-feature';
4
5
  import AJV from 'ajv';
5
6
  import fs from 'fs';
@@ -71,36 +72,48 @@ export default class XMLCot {
71
72
  cot.event.point._attributes.lon = feature.geometry.coordinates[0];
72
73
  cot.event.point._attributes.lat = feature.geometry.coordinates[1];
73
74
  } else if (['Polygon', 'LineString'].includes(feature.geometry.type)) {
74
- cot.event._attributes.type = 'u-d-f';
75
+ const stroke = new Color(feature.properties.stroke || -1761607936);
76
+ if (feature.properties['stroke-opacity']) stroke.a = feature.properties['stroke-opacity'];
77
+ cot.event.detail.strokeColor = { _attributes: { value: stroke.as_32bit() } };
75
78
 
76
- if (feature.geometry.type === 'Polygon') {
77
- cot.event._attributes.type = 'u-d-r';
79
+ if (!feature.properties['stroke-width']) feature.properties['stroke-width'] = 3;
80
+ cot.event.detail.strokeWeight = { _attributes: {
81
+ value: feature.properties['stroke-width']
82
+ } };
83
+
84
+ if (!feature.properties['stroke-style']) feature.properties['stroke-style'] = 'solid';
85
+ cot.event.detail.strokeStyle = { _attributes: {
86
+ value: feature.properties['stroke-style']
87
+ } };
88
+
89
+ if (feature.geometry.type === 'LineString') {
90
+ cot.event._attributes.type = 'u-d-f';
78
91
 
79
- // Inner rings are not yet supported
80
92
  cot.event.detail.link = [];
81
- feature.geometry.coordinates[0].pop(); // Dont' Close Loop in COT
82
- for (const coord of feature.geometry.coordinates[0]) {
93
+ for (const coord of feature.geometry.coordinates) {
83
94
  cot.event.detail.link.push({
84
95
  _attributes: { point: `${coord[1]},${coord[0]}` }
85
96
  });
86
97
  }
87
- } else if (feature.geometry.type === 'LineString') {
88
- cot.event._attributes.type = 'u-d-f';
98
+ } else if (feature.geometry.type === 'Polygon') {
99
+ cot.event._attributes.type = 'u-d-r';
89
100
 
101
+ // Inner rings are not yet supported
90
102
  cot.event.detail.link = [];
91
- for (const coord of feature.geometry.coordinates) {
103
+ feature.geometry.coordinates[0].pop(); // Dont' Close Loop in COT
104
+ for (const coord of feature.geometry.coordinates[0]) {
92
105
  cot.event.detail.link.push({
93
106
  _attributes: { point: `${coord[1]},${coord[0]}` }
94
107
  });
95
108
  }
109
+
110
+ const fill = new Color(feature.properties.fill || -1761607936);
111
+ if (feature.properties['fill-opacity']) fill.a = feature.properties['fill-opacity'];
112
+ cot.event.detail.fillColor = { _attributes: { value: fill.as_32bit() } };
96
113
  }
97
114
 
98
115
  cot.event.detail.labels_on = { _attributes: { value: 'false' } };
99
116
  cot.event.detail.tog = { _attributes: { enabled: '0' } };
100
- cot.event.detail.strokeColor = { _attributes: { value: '-256' } };
101
- cot.event.detail.strokeWeight = { _attributes: { value: '3.0' } };
102
- cot.event.detail.strokeStyle = { _attributes: { value: 'solid' } };
103
- cot.event.detail.fillColor = { _attributes: { value: '-1761607936' } };
104
117
 
105
118
  const centre = PointOnFeature(feature);
106
119
  cot.event.point._attributes.lon = centre.geometry.coordinates[0];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tak-ps/node-cot",
3
3
  "type": "module",
4
- "version": "2.5.0",
4
+ "version": "2.6.0",
5
5
  "description": "Lightweight JavaScript library for parsing and manipulating TAK messages",
6
6
  "main": "index.js",
7
7
  "scripts": {
@@ -11,6 +11,7 @@
11
11
  "dependencies": {
12
12
  "@turf/point-on-feature": "^6.5.0",
13
13
  "ajv": "^8.11.2",
14
+ "color": "^4.2.3",
14
15
  "protobufjs": "^7.1.2",
15
16
  "uuid": "^9.0.0",
16
17
  "xml-js": "^1.6.11"
@@ -68,10 +68,10 @@ test('XML.from_geojson - Polygon', (t) => {
68
68
  ],
69
69
  labels_on: { _attributes: { value: 'false' } },
70
70
  tog: { _attributes: { enabled: '0' } },
71
- strokeColor: { _attributes: { value: '-256' } },
72
- strokeWeight: { _attributes: { value: '3.0' } },
71
+ strokeColor: { _attributes: { value: 16776960 } },
72
+ strokeWeight: { _attributes: { value: 3 } },
73
73
  strokeStyle: { _attributes: { value: 'solid' } },
74
- fillColor: { _attributes: { value: '-1761607936' } }
74
+ fillColor: { _attributes: { value: 16776960 } }
75
75
  });
76
76
 
77
77
  t.end();
@@ -116,10 +116,9 @@ test('XML.from_geojson - LineString', (t) => {
116
116
  ],
117
117
  labels_on: { _attributes: { value: 'false' } },
118
118
  tog: { _attributes: { enabled: '0' } },
119
- strokeColor: { _attributes: { value: '-256' } },
120
- strokeWeight: { _attributes: { value: '3.0' } },
119
+ strokeColor: { _attributes: { value: 16776960 } },
120
+ strokeWeight: { _attributes: { value: 3 } },
121
121
  strokeStyle: { _attributes: { value: 'solid' } },
122
- fillColor: { _attributes: { value: '-1761607936' } }
123
122
  });
124
123
 
125
124
  t.end();
@@ -0,0 +1,33 @@
1
+ import test from 'tape';
2
+ import { XML } from '../index.js';
3
+
4
+ test('XML.from_geojson - Polygon Style', (t) => {
5
+ const geo = XML.from_geojson({
6
+ type: 'Feature',
7
+ properties: {
8
+
9
+ },
10
+ geometry: {
11
+ type: 'Point',
12
+ coordinates: [1.1, 2.2]
13
+ }
14
+ });
15
+
16
+ t.equals(geo.raw.event._attributes.version, '2.0');
17
+ t.equals(geo.raw.event._attributes.type, 'a-f-G');
18
+ t.equals(geo.raw.event._attributes.how, 'm-g');
19
+ t.equals(geo.raw.event._attributes.uid.length, 36);
20
+ t.equals(geo.raw.event._attributes.time.length, 24);
21
+ t.equals(geo.raw.event._attributes.start.length, 24);
22
+ t.equals(geo.raw.event._attributes.stale.length, 24);
23
+
24
+ t.deepEquals(geo.raw.event.point, {
25
+ _attributes: { lat: 2.2, lon: 1.1, hae: 0, ce: 9999999, le: 9999999 }
26
+ });
27
+
28
+ t.deepEquals(geo.raw.event.detail, {
29
+ contact: { _attributes: { callsign: 'UNKNOWN' } }
30
+ });
31
+
32
+ t.end();
33
+ });
@@ -1,29 +0,0 @@
1
- const path = require('path')
2
- const {cot, proto} = require(path.join(__dirname, '../index.js'))
3
-
4
- const run = (message) => {
5
- if (!message) {
6
- console.error('Enter a TAK message')
7
- return
8
- }
9
-
10
- const bufferMessage = typeof message !== Buffer ? Buffer.from(message, 'hex') : message
11
-
12
- if (bufferMessage[0] === 191) { // TAK message format 0xbf
13
- console.log('TAK message received')
14
- const trimmedBuffer = bufferMessage.slice(3, bufferMessage.length) // remove tak message header from content
15
- if (bufferMessage[1] === 0) { // is COT XML
16
- console.error('Enter a TAK proto message')
17
- } else if (bufferMessage[1] === 1) { // is Protobuf
18
- console.log('TAK protobuf format')
19
- const protoMessage = proto.proto2js(trimmedBuffer)
20
- const cotMessage = proto.protojs2cotjs(protoMessage)
21
- console.log(cotMessage)
22
- console.log(cot.js2xml(cotMessage))
23
- }
24
- } else { // not TAK message format
25
- console.error('Enter a TAK proto message')
26
- }
27
- }
28
-
29
- run(process.argv[2])
package/scripts/parse.js DELETED
@@ -1,34 +0,0 @@
1
- const path = require('path')
2
- const {cot, proto} = require(path.join(__dirname, '../index.js'))
3
-
4
- // https://github.com/deptofdefense/AndroidTacticalAssaultKit-CIV/blob/master/commoncommo/core/impl/protobuf/protocol.txt
5
-
6
- const run = (message) => {
7
- if (!message) {
8
- console.error('Enter a TAK message')
9
- return
10
- }
11
-
12
- const bufferMessage = typeof message !== Buffer ? Buffer.from(message, 'hex') : message
13
-
14
- if (bufferMessage[0] === 191) { // TAK message format 0xbf
15
- console.log('TAK message received')
16
- const trimmedBuffer = bufferMessage.slice(3, bufferMessage.length) // remove tak message header from content
17
- if (bufferMessage[1] === 0) { // is COT XML
18
- console.log('COT XML format')
19
- console.log(cot.xml2js(trimmedBuffer)) // try parsing raw XML
20
- } else if (bufferMessage[1] === 1) { // is Protobuf
21
- console.log('TAK protobuf format')
22
- console.log(proto.proto2js(trimmedBuffer))
23
- }
24
- } else { // not TAK message format
25
- try {
26
- console.log('COT XML received')
27
- console.log(cot.xml2js(message)) // try parsing raw XML
28
- } catch (e) {
29
- console.error('Failed to parse message', e)
30
- }
31
- }
32
- }
33
-
34
- run(process.argv[2])