@tak-ps/node-cot 2.8.1 → 3.1.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.
@@ -1,39 +1,94 @@
1
1
  import xmljs from 'xml-js';
2
+ import { Feature } from 'geojson';
3
+ import { AllGeoJSON } from "@turf/helpers";
2
4
  import Util from './util.js';
3
5
  import Color from './color.js';
4
6
  import PointOnFeature from '@turf/point-on-feature';
5
7
  import AJV from 'ajv';
6
8
  import fs from 'fs';
7
9
 
8
- const ajv = (new AJV({ allErrors: true })).compile(JSON.parse(fs.readFileSync(new URL('./schema.json', import.meta.url))));
10
+ const ajv = (new AJV({ allErrors: true }))
11
+ .compile(JSON.parse(String(fs.readFileSync(new URL('./schema.json', import.meta.url)))));
9
12
 
13
+ export interface Attributes {
14
+ version: string,
15
+ uid: string;
16
+ type: string;
17
+ how: string;
18
+ [k: string]: string;
19
+ }
20
+
21
+ export interface GenericAttributes {
22
+ _attributes: {
23
+ [k: string]: string;
24
+ }
25
+ }
26
+
27
+ export interface Detail {
28
+ contact?: GenericAttributes,
29
+ tog?: GenericAttributes,
30
+ strokeColor?: GenericAttributes,
31
+ strokeWeight?: GenericAttributes,
32
+ strokeStyle?: GenericAttributes,
33
+ labels_on?: GenericAttributes,
34
+ fillColor?: GenericAttributes,
35
+ link?: object[],
36
+ TakControl?: {
37
+ TakServerVersionInfo?: GenericAttributes
38
+ },
39
+ [k: string]: unknown
40
+ }
41
+
42
+ export interface Point {
43
+ _attributes: {
44
+ lat: string | number;
45
+ lon: string | number;
46
+ hae: string | number;
47
+ ce: string | number;
48
+ le: string | number;
49
+ [k: string]: string | number
50
+ }
51
+ }
52
+
53
+ export interface JSONCoT {
54
+ event: {
55
+ _attributes: Attributes,
56
+ detail: Detail,
57
+ point: Point,
58
+ [k: string]: unknown
59
+ },
60
+ [k: string]: unknown
61
+ }
10
62
 
11
63
  /**
12
64
  * Convert to and from an XML CoT message
13
65
  * @class
14
66
  *
15
- * @param {String|Object|Buffer} cot A string/buffer containing the XML representation or the xml-js object tree
67
+ * @param cot A string/buffer containing the XML representation or the xml-js object tree
16
68
  *
17
- * @prop {Object} raw Raw XML-JS representation of CoT
69
+ * @prop raw Raw XML-JS representation of CoT
18
70
  */
19
71
  export default class XMLCot {
20
- constructor(cot) {
21
- if (cot instanceof Buffer) String(cot);
72
+ raw: JSONCoT;
73
+
74
+ constructor(cot: Buffer | JSONCoT | string) {
75
+ if (typeof cot === 'string' || cot instanceof Buffer) {
76
+ if (cot instanceof Buffer) cot = String(cot);
22
77
 
23
- if (typeof cot === 'string') {
24
- this.raw = xmljs.xml2js(cot, { compact: true });
78
+ const raw: any = xmljs.xml2js(cot, { compact: true });
79
+ this.raw = raw as JSONCoT;
25
80
  } else {
26
81
  this.raw = cot;
27
82
  }
28
83
 
29
84
  // Attempt to cast all point to numerics
30
85
  for (const key of Object.keys(this.raw.event.point._attributes)) {
31
- if (!isNaN(parseFloat(this.raw.event.point._attributes[key]))) {
32
- this.raw.event.point._attributes[key] = parseFloat(this.raw.event.point._attributes[key]);
86
+ if (!isNaN(parseFloat(String(this.raw.event.point._attributes[key])))) {
87
+ this.raw.event.point._attributes[key] = parseFloat(String(this.raw.event.point._attributes[key]));
33
88
  }
34
89
  }
35
90
 
36
- if (!this.raw.event._attributes.uid) this.raw.event._attributes.uuid = Util.cot_uuid().uid;
91
+ if (!this.raw.event._attributes.uid) this.raw.event._attributes.uuid = Util.cot_uuid();
37
92
 
38
93
  ajv(this.raw);
39
94
  if (ajv.errors) throw new Error(ajv.errors[0].message);
@@ -46,11 +101,11 @@ export default class XMLCot {
46
101
  *
47
102
  * @return {XMLCot}
48
103
  */
49
- static from_geojson(feature) {
104
+ static from_geojson(feature: Feature) {
50
105
  if (feature.type !== 'Feature') throw new Error('Must be GeoJSON Feature');
51
106
  if (!feature.properties) throw new Error('Feature must have properties');
52
107
 
53
- const cot = {
108
+ const cot: JSONCoT = {
54
109
  event: {
55
110
  _attributes: Util.cot_event_attr(
56
111
  feature.properties.type || 'a-f-G',
@@ -64,23 +119,23 @@ export default class XMLCot {
64
119
  }
65
120
  };
66
121
 
67
- if (feature.id) cot.event._attributes.uid = feature.id;
122
+ if (feature.id) cot.event._attributes.uid = String(feature.id);
68
123
  if (feature.properties.callsign && !feature.id) cot.event._attributes.uid = feature.properties.callsign;
69
124
 
70
125
  if (!feature.geometry) throw new Error('Must have Geometry');
71
126
  if (!['Point', 'Polygon', 'LineString'].includes(feature.geometry.type)) throw new Error('Unsupported Geometry Type');
72
127
 
73
128
  if (feature.geometry.type === 'Point') {
74
- cot.event.point._attributes.lon = feature.geometry.coordinates[0];
75
- cot.event.point._attributes.lat = feature.geometry.coordinates[1];
129
+ cot.event.point._attributes.lon = String(feature.geometry.coordinates[0]);
130
+ cot.event.point._attributes.lat = String(feature.geometry.coordinates[1]);
76
131
  } else if (['Polygon', 'LineString'].includes(feature.geometry.type)) {
77
132
  const stroke = new Color(feature.properties.stroke || -1761607936);
78
133
  if (feature.properties['stroke-opacity']) stroke.a = feature.properties['stroke-opacity'];
79
- cot.event.detail.strokeColor = { _attributes: { value: stroke.as_32bit() } };
134
+ cot.event.detail.strokeColor = { _attributes: { value: String(stroke.as_32bit()) } };
80
135
 
81
136
  if (!feature.properties['stroke-width']) feature.properties['stroke-width'] = 3;
82
137
  cot.event.detail.strokeWeight = { _attributes: {
83
- value: feature.properties['stroke-width']
138
+ value: String(feature.properties['stroke-width'])
84
139
  } };
85
140
 
86
141
  if (!feature.properties['stroke-style']) feature.properties['stroke-style'] = 'solid';
@@ -111,15 +166,15 @@ export default class XMLCot {
111
166
 
112
167
  const fill = new Color(feature.properties.fill || -1761607936);
113
168
  if (feature.properties['fill-opacity']) fill.a = feature.properties['fill-opacity'];
114
- cot.event.detail.fillColor = { _attributes: { value: fill.as_32bit() } };
169
+ cot.event.detail.fillColor = { _attributes: { value: String(fill.as_32bit()) } };
115
170
  }
116
171
 
117
172
  cot.event.detail.labels_on = { _attributes: { value: 'false' } };
118
173
  cot.event.detail.tog = { _attributes: { enabled: '0' } };
119
174
 
120
- const centre = PointOnFeature(feature);
121
- cot.event.point._attributes.lon = centre.geometry.coordinates[0];
122
- cot.event.point._attributes.lat = centre.geometry.coordinates[1];
175
+ const centre = PointOnFeature(feature as AllGeoJSON);
176
+ cot.event.point._attributes.lon = String(centre.geometry.coordinates[0]);
177
+ cot.event.point._attributes.lat = String(centre.geometry.coordinates[1]);
123
178
  }
124
179
 
125
180
  return new XMLCot(cot);
@@ -127,16 +182,14 @@ export default class XMLCot {
127
182
 
128
183
  /**
129
184
  * Return a GeoJSON Feature from an XML CoT message
130
- *
131
- * @returns {Object}
132
185
  */
133
- to_geojson() {
186
+ to_geojson(): Feature {
134
187
  const raw = JSON.parse(JSON.stringify(this.raw));
135
188
  if (!raw.event.detail) raw.event.detail = {};
136
189
  if (!raw.event.detail.contact) raw.event.detail.contact = {};
137
190
  if (!raw.event.detail.contact._attributes) raw.event.detail.contact._attributes = {};
138
191
 
139
- const geojson = {
192
+ const geojson: Feature = {
140
193
  id: raw.event._attributes.uid,
141
194
  type: 'Feature',
142
195
  properties: {
@@ -174,6 +227,7 @@ export default class XMLCot {
174
227
  return new XMLCot({
175
228
  event: {
176
229
  _attributes: Util.cot_event_attr('t-x-c-t', 'h-g-i-g-o'),
230
+ detail: {},
177
231
  point: Util.cot_point()
178
232
  }
179
233
  });
package/package.json CHANGED
@@ -1,25 +1,37 @@
1
1
  {
2
2
  "name": "@tak-ps/node-cot",
3
3
  "type": "module",
4
- "version": "2.8.1",
4
+ "version": "3.1.0",
5
5
  "description": "Lightweight JavaScript library for parsing and manipulating TAK messages",
6
- "main": "index.js",
6
+ "main": "dist/index.js",
7
+ "types": "index.js",
7
8
  "scripts": {
8
- "test": "tape test/**.test.js",
9
- "lint": "eslint *.js lib/*.js test/*.js"
9
+ "test": "ts-node-test test/",
10
+ "lint": "eslint *.ts lib/*.ts",
11
+ "build": "tsc --build",
12
+ "pretest": "npm run lint"
10
13
  },
11
14
  "dependencies": {
15
+ "@turf/helpers": "^6.5.0",
12
16
  "@turf/point-on-feature": "^6.5.0",
17
+ "@types/color": "^3.0.3",
18
+ "@types/geojson": "^7946.0.10",
13
19
  "ajv": "^8.11.2",
14
20
  "color": "^4.2.3",
15
21
  "protobufjs": "^7.1.2",
16
- "uuid": "^9.0.0",
17
22
  "xml-js": "^1.6.11"
18
23
  },
19
24
  "devDependencies": {
20
- "eslint": "^8.28.0",
25
+ "@types/node": "^18.14.2",
26
+ "@types/tape": "^4.13.2",
27
+ "@typescript-eslint/eslint-plugin": "^5.54.0",
28
+ "@typescript-eslint/parser": "^5.54.0",
29
+ "eslint": "^8.35.0",
21
30
  "eslint-plugin-node": "^11.1.0",
22
- "tape": "^5.6.1"
31
+ "tape": "^5.6.1",
32
+ "ts-node": "^10.9.1",
33
+ "ts-node-test": "^0.3.0",
34
+ "typescript": "^4.9.5"
23
35
  },
24
36
  "repository": {
25
37
  "type": "git",
@@ -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: 16776960 } },
72
- strokeWeight: { _attributes: { value: 3 } },
71
+ strokeColor: { _attributes: { value: '16776960' } },
72
+ strokeWeight: { _attributes: { value: '3' } },
73
73
  strokeStyle: { _attributes: { value: 'solid' } },
74
- fillColor: { _attributes: { value: 16776960 } }
74
+ fillColor: { _attributes: { value: '16776960' } }
75
75
  });
76
76
 
77
77
  t.end();
@@ -116,8 +116,8 @@ 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: 16776960 } },
120
- strokeWeight: { _attributes: { value: 3 } },
119
+ strokeColor: { _attributes: { value: '16776960' } },
120
+ strokeWeight: { _attributes: { value: '3' } },
121
121
  strokeStyle: { _attributes: { value: 'solid' } }
122
122
  });
123
123
 
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "ts-node": {
3
+ "esm": true
4
+ },
5
+ "compilerOptions": {
6
+ "module": "es2022",
7
+ "esModuleInterop": true,
8
+ "target": "es2022",
9
+ "lib": ["es2022", "dom"],
10
+ "noImplicitAny": true,
11
+ "moduleResolution": "node",
12
+ "sourceMap": true,
13
+ "outDir": "dist",
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ "*": [
17
+ "node_modules/*"
18
+ ]
19
+ }
20
+ },
21
+ "include": [
22
+ "index.ts",
23
+ "test/**/*",
24
+ "lib/**/*"
25
+ ]
26
+ }
package/lib/util.js DELETED
@@ -1,111 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
-
3
- /**
4
- * Helper functions for generating CoT data
5
- * @class
6
- */
7
- export default class Util {
8
- /**
9
- * Return an event._attributes object with as many defaults as possible
10
- *
11
- * @param {String} type CoT Type
12
- * @param {String} how CoT how
13
- * @param {Date|string|null} time Time of CoT Message - if omitted, current time is used
14
- * @param {Date|string|null} start Start Time of CoT - if omitted, current time is used
15
- * @param {Date|string|null|numeric} stale Expiration of CoT - if null now+20s is used. Alternative an integer representing the ms offset
16
- *
17
- * @returns {Object}
18
- */
19
- static cot_event_attr(type, how, time, start, stale) {
20
- if (!type) throw new Error('type param required');
21
- if (!how) throw new Error('how param required');
22
-
23
- return {
24
- ...Util.cot_version(),
25
- ...Util.cot_uuid(),
26
- type,
27
- how,
28
- ...Util.cot_date(time, start, stale)
29
- };
30
- }
31
-
32
- /**
33
- * Return an event.detail object with as many defaults as possible
34
- *
35
- * @param {String} [callsign=UNKNOWN] Display Callsign
36
- *
37
- * @returns {Object}
38
- */
39
- static cot_event_detail(callsign = 'UNKNOWN') {
40
- return {
41
- contact: {
42
- _attributes: { callsign }
43
- }
44
- };
45
- }
46
-
47
- /**
48
- * Generate a random UUID
49
- *
50
- * @returns {Object}
51
- */
52
- static cot_uuid() {
53
- return {
54
- uid: uuidv4()
55
- };
56
- }
57
-
58
- /**
59
- * Return the current version number this library supports
60
- *
61
- * @returns {Object}
62
- */
63
- static cot_version() {
64
- return {
65
- version: '2.0'
66
- };
67
- }
68
-
69
- /**
70
- * Generate Null Island CoT point object
71
- *
72
- * @returns {Object}
73
- */
74
- static cot_point() {
75
- return {
76
- '_attributes': {
77
- 'lat': '0.000000',
78
- 'lon': '0.000000',
79
- 'hae': '0.0',
80
- 'ce': '9999999.0',
81
- 'le': '9999999.0'
82
- }
83
- };
84
- }
85
-
86
- /**
87
- * Generate CoT date objects
88
- *
89
- * cot_date() - Time: now, Start: now, Stale: now + 20s
90
- *
91
- * @param {Date|string|null} time Time of CoT Message - if omitted, current time is used
92
- * @param {Date|string|null} start Start Time of CoT - if omitted, current time is used
93
- * @param {Date|string|null|numeric} stale Expiration of CoT - if null now+20s is used. Alternative an integer representing the ms offset
94
- *
95
- * @returns {Object}
96
- */
97
- static cot_date(time, start, stale) {
98
- const now = Date.now();
99
-
100
- time = new Date(time || now).toISOString();
101
- start = new Date(start || now).toISOString();
102
-
103
- if (!stale) {
104
- stale = new Date(+new Date(start) + 20 * 1000).toISOString();
105
- } else if (!isNaN(parseInt(stale))) {
106
- stale = new Date(+new Date(start) + stale).toISOString();
107
- }
108
-
109
- return { time, start, stale };
110
- }
111
- }
File without changes
File without changes
File without changes