Skip to content

Commit

Permalink
Merge pull request #12 from dfpc-coe/typebox
Browse files Browse the repository at this point in the history
Typebox Base Types
  • Loading branch information
ingalls authored Apr 10, 2024
2 parents 89847b1 + 840e565 commit 3fddfdf
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 752 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

<p align=center>Javascript Cursor-On-Target Library</p>

Lightweight JavaScript library for parsing and manipulating TAK messages, primarily Cursor-on-Target (COT)
Lightweight JavaScript library for parsing and manipulating TAK related messages, primarily [Cursor-on-Target (COT)](https://git.tak.gov/standards/takcot)

## About

tak.js converts between TAK message protocols and a Javascript object/JSON format. This makes it easy to read and write TAK messages in a Node.js application.
`node-tak` converts between TAK message protocols and a Javascript object/JSON format.
It also can bidirectionally convert CoT messages into a GeoJSON format

## Installation

Expand Down Expand Up @@ -35,10 +36,10 @@ understanding of the spec primarily developed through reverse engineering TAK cl
| `version` | CoT Version, currently `2.0` | `version="2.0"` |
| `uid` | Globally unique name for this information on this event | `uid="any-unique-string-123"` |
| `type` | Hierarchically organized hint about event type | `type="a-f-G-E"' |
| `time` | The time at which the event was generated | `time="2023-07-18T15:25:09.00Z"` |
| `how` | Gives a hint about how the coordinates were generated | `how=""`
| `start` | | |
| `stale` | | |
| `time` | The time at which the event was generated | `time="2023-07-18T15:25:09.00Z"` |
| `start` | The time at which the event starts or is relevant | `start="2023-07-18T15:25:09.00Z"` |
| `stale` | The time at which the event ends or is not relevant | `stale="2023-07-18T15:25:09.00Z"` |

## CoT GeoJSON Spec

Expand Down
3 changes: 2 additions & 1 deletion lib/chat.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Util from './util.js'
import CoT from './cot.js';
import JSONCoT from './types.js';
import { Static } from '@sinclair/typebox';
import { randomUUID } from 'node:crypto';

export type DirectChatMember = {
Expand All @@ -23,7 +24,7 @@ export type DirectChatInput = {

export class DirectChat extends CoT {
constructor(chat: DirectChatInput) {
const cot: JSONCoT = {
const cot: Static<typeof JSONCoT> = {
event: {
_attributes: Util.cot_event_attr('b-t-f', 'h-g-i-g-o'),
point: Util.cot_point(),
Expand Down
29 changes: 17 additions & 12 deletions lib/cot.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import xmljs from 'xml-js';
import { Static } from '@sinclair/typebox';
import { Feature } from 'geojson';
import { AllGeoJSON } from "@turf/helpers";
import Util from './util.js';
import Color from './color.js';
import PointOnFeature from '@turf/point-on-feature';
import JSONCoT from './types.js'
import JSONCoT, { MartiDest } from './types.js'
import AJV from 'ajv';
import fs from 'fs';

const schema = JSON.parse(String(fs.readFileSync(new URL('./schema.json', import.meta.url))));
const pkg = JSON.parse(String(fs.readFileSync(new URL('../package.json', import.meta.url))));

const ajv = (new AJV({
allErrors: true,
allowUnionTypes: true
}))
.compile(schema);
.compile(JSONCoT);

/**
* Convert to and from an XML CoT message
Expand All @@ -26,19 +26,19 @@ const ajv = (new AJV({
* @prop raw Raw XML-JS representation of CoT
*/
export default class CoT {
raw: JSONCoT;
raw: Static<typeof JSONCoT>;

constructor(cot: Buffer | JSONCoT | string) {
constructor(cot: Buffer | Static<typeof JSONCoT> | string) {
if (typeof cot === 'string' || cot instanceof Buffer) {
if (cot instanceof Buffer) cot = String(cot);

const raw = xmljs.xml2js(cot, { compact: true });
this.raw = raw as JSONCoT;
this.raw = raw as Static<typeof JSONCoT>;
} else {
this.raw = cot;
}

if (!this.raw.event._attributes.uid) this.raw.event._attributes.uuid = Util.cot_uuid();
if (!this.raw.event._attributes.uid) this.raw.event._attributes.uid = Util.cot_uuid();

ajv(this.raw);
if (ajv.errors) throw new Error(`${ajv.errors[0].message} (${ajv.errors[0].instancePath})`);
Expand All @@ -47,7 +47,7 @@ export default class CoT {
if (!this.raw.event.detail['_flow-tags_']) this.raw.event.detail['_flow-tags_'] = {};
this.raw.event.detail['_flow-tags_'][`NodeCoT-${pkg.version}`] = new Date().toISOString()

if (this.raw.event.detail.archived && Object.keys(this.raw.event.detail.archived).length === 0) this.raw.event.archived = { _attributes: {} };
if (this.raw.event.detail.archived && Object.keys(this.raw.event.detail.archived).length === 0) this.raw.event.detail.archived = { _attributes: {} };

}

Expand All @@ -62,7 +62,7 @@ export default class CoT {
if (feature.type !== 'Feature') throw new Error('Must be GeoJSON Feature');
if (!feature.properties) throw new Error('Feature must have properties');

const cot: JSONCoT = {
const cot: Static<typeof JSONCoT> = {
event: {
_attributes: Util.cot_event_attr(
feature.properties.type || 'a-f-G',
Expand Down Expand Up @@ -92,7 +92,11 @@ export default class CoT {
const dest = !Array.isArray(feature.properties.dest) ? [ feature.properties.dest ] : feature.properties.dest;

cot.event.detail.marti = {
dest: dest.map((dest: object) => {
dest: dest.map((dest: {
uid?: string;
mission?: string;
callsign?: string;
}) => {
return { _attributes: { ...dest } };
})
}
Expand Down Expand Up @@ -208,7 +212,7 @@ export default class CoT {
* Return a GeoJSON Feature from an XML CoT message
*/
to_geojson(): Feature {
const raw: JSONCoT = JSON.parse(JSON.stringify(this.raw));
const raw: Static<typeof JSONCoT> = JSON.parse(JSON.stringify(this.raw));
if (!raw.event.detail) raw.event.detail = {};
if (!raw.event.detail.contact) raw.event.detail.contact = { _attributes: { callsign: 'UNKNOWN' } };
if (!raw.event.detail.contact._attributes) raw.event.detail.contact._attributes = { callsign: 'UNKNOWN' };
Expand Down Expand Up @@ -262,7 +266,7 @@ export default class CoT {
if (raw.event.detail.marti && raw.event.detail.marti.dest) {
if (!Array.isArray(raw.event.detail.marti.dest)) raw.event.detail.marti.dest = [raw.event.detail.marti.dest];

const dest = raw.event.detail.marti.dest.map((d) => {
const dest = raw.event.detail.marti.dest.map((d: Static<typeof MartiDest>) => {
return { ...d._attributes };
});

Expand Down Expand Up @@ -306,6 +310,7 @@ export default class CoT {
const coordinates = [];

for (const l of raw.event.detail.link) {
if (!l._attributes.point) continue;
coordinates.push(l._attributes.point.split(',').map((p: string) => { return Number(p.trim()) }).splice(0, 2).reverse());
}

Expand Down
Loading

0 comments on commit 3fddfdf

Please sign in to comment.