Skip to content

Commit

Permalink
Update CHANGELOG
Browse files Browse the repository at this point in the history
  • Loading branch information
ingalls committed Jan 31, 2024
1 parent 5d43964 commit 1c21089
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 117 deletions.
6 changes: 1 addition & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"license": "ISC",
"devDependencies": {
"@types/jsonwebtoken": "^9.0.1",
"@types/xml2js": "^0.4.11",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"eslint": "^8.31.0",
Expand All @@ -23,10 +22,7 @@
},
"dependencies": {
"@tak-ps/etl": "^2.0.1",
"@tmcw/togeojson": "^5.6.0",
"@types/geojson": "^7946.0.10",
"@xmldom/xmldom": "^0.8.7",
"moment": "^2.29.4",
"xml2js": "^0.5.0"
"moment": "^2.29.4"
}
}
140 changes: 28 additions & 112 deletions task.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'node:fs';
import moment from 'moment';
import { FeatureCollection, Feature, Geometry } from 'geojson';
import xml2js from 'xml2js';
import { FeatureCollection, Feature } from 'geojson';
import { JSONSchema6 } from 'json-schema';
import ETL, { Event, SchemaType } from '@tak-ps/etl';

Expand All @@ -15,49 +14,17 @@ try {
console.log('ok - no .env file loaded');
}

export interface Share {
ShareId: string;
CallSign?: string;
Password?: string;
}

export default class Task extends ETL {
static async schema(type: SchemaType = SchemaType.Input): Promise<JSONSchema6> {
if (type === SchemaType.Input) {
return {
type: 'object',
required: ['INREACH_MAP_SHARES'],
required: [],
properties: {
'INREACH_MAP_SHARES': {
type: 'array',
description: 'Inreach Share IDs to pull data from',
// @ts-ignore
display: 'table',
items: {
type: 'object',
required: [
'ShareID',
],
properties: {
CallSign: {
type: 'string',
description: 'Human Readable Name of the Operator - Used as the callsign in TAK'
},
ShareId: {
type: 'string',
description: 'Garmin Inreach Share ID or URL'
},
Password: {
type: 'string',
description: 'Optional: Garmin Inreach MapShare Password'
}
}
}
},
'DEBUG': {
type: 'boolean',
default: false,
description: 'Print ADSBX results in logs'
description: 'Print results in logs'
}
}
}
Expand All @@ -71,90 +38,39 @@ export default class Task extends ETL {
}

async control(): Promise<void> {
const layer = await this.layer();

if (!layer.environment.INREACH_MAP_SHARES) throw new Error('No INREACH_MAP_SHARES Provided');
if (!Array.isArray(layer.environment.INREACH_MAP_SHARES)) throw new Error('INREACH_MAP_SHARES must be an array');

const obtains: Array<Promise<Feature[]>> = [];
for (const share of layer.environment.INREACH_MAP_SHARES) {
obtains.push((async (share: Share): Promise<Feature[]> => {
try {
if (share.ShareId.startsWith('https://')) {
share.ShareId = new URL(share.ShareId).pathname.replace(/^\//, '');
} else if (share.ShareId.startsWith('share.garmin.com')) {
share.ShareId = share.ShareId.replace('share.garmin.com/', '');
}
} catch (err) {
console.error(err);
}

if (!share.CallSign) share.CallSign = share.ShareId;
console.log(`ok - requesting ${share.ShareId} ${share.CallSign ? `(${share.CallSign})` : ''}`);

const url = new URL(`/feed/Share/${share.ShareId}`, 'https://explore.garmin.com')
url.searchParams.append('d1', moment().subtract(30, 'minutes').utc().format());

const kmlres = await fetch(url);
const body = await kmlres.text();

const featuresmap: Map<string, Feature> = new Map();
const features: Feature[] = [];
//const layer = await this.layer();

if (!body.trim()) return features;
const dateTime = moment().toISOString();
const res = await fetch(`https://avalanche.state.co.us/api-proxy/avid?_api_proxy_uri=%2Fproducts%2Fall%2Farea%3FproductType%3Davalancheforecast%26datetime%3D${encodeURIComponent(dateTime)}%26includeExpired%3Dtrue`, {
method: 'GET'
});

const xml = await xml2js.parseStringPromise(body);
if (!xml.kml || !xml.kml.Document) throw new Error('XML Parse Error: Document not found');
if (!xml.kml.Document[0] || !xml.kml.Document[0].Folder || !xml.kml.Document[0].Folder[0]) return;
if (!res.ok) throw new Error('Error fetching Forecast Geometries');

console.log(`ok - ${share.ShareId} has ${xml.kml.Document[0].Folder[0].Placemark.length} locations`);
for (const placemark of xml.kml.Document[0].Folder[0].Placemark) {
if (!placemark.Point || !placemark.Point[0]) continue;
const feats: FeatureCollection = await res.json();
const featMap = new Map<string, Feature>();
feats.features.map((feat: Feature) => {
featMap.set(String(feat.id), feat);
});

const coords = placemark.Point[0].coordinates[0].split(',').map((ele: string) => {
return parseFloat(ele);
});

const feat: Feature<Geometry, { [name: string]: any; }> = {
id: `inreach-${share.CallSign}`,
type: 'Feature',
properties: {
callsign: share.CallSign,
time: new Date(placemark.TimeStamp[0].when[0]),
start: new Date(placemark.TimeStamp[0].when[0])
},
geometry: {
type: 'Point',
coordinates: coords
}
};

if (featuresmap.has(String(feat.id))) {
const existing = featuresmap.get(String(feat.id));

if (moment(feat.properties.time).isAfter(existing.properties.time)) {
featuresmap.set(String(feat.id), feat);
}
} else {
featuresmap.set(String(feat.id), feat);
}
}

features.push(...Array.from(featuresmap.values()))

return features;
})(share))
}
//const res2 = await fetch(`https://avalanche.state.co.us/api-proxy/avid?_api_proxy_uri=%2Fproducts%2Fall%3Fdatetime%3D${encodeURIComponent(dateTime)}%26includeExpired%3Dtrue`, {
// method: 'GET'
//});
//
//if (!res2.ok) throw new Error('Error fetching Forecast');
//const forecast = await res2.json();

const fc: FeatureCollection = {
type: 'FeatureCollection',
features: []
}

for (const res of await Promise.all(obtains)) {
if (!res || !res.length) continue;
fc.features.push(...res);
}
};

fc.features = Array.from(featMap.values()).map((feat: Feature) => {
feat.id = `caic-${feat.id}`;
delete feat.properties.centroid;
delete feat.properties.bbox;
return feat;
});

await this.submit(fc);
}
Expand Down

0 comments on commit 1c21089

Please sign in to comment.