signalk-tides
Version:
Tidal predictions for the vessel's position from various online sources.
106 lines (105 loc) • 4.59 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
const geolib_1 = require("geolib");
const moment_1 = __importDefault(require("moment"));
const stationsUrl = `https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.json?type=tidepredictions`;
const dataGetterUrl = 'https://api.tidesandcurrents.noaa.gov/api/prod/datagetter';
const datum = 'MLLW';
function default_1(app) {
return {
id: 'noaa',
title: 'NOAA (US only)',
async start() {
const stations = await StationList.load(app);
return async ({ date } = {}) => {
const position = app.getSelfPath("navigation.position.value");
if (!position)
throw new Error("no position");
const station = stations.closestTo(position);
const endpoint = new URL(dataGetterUrl);
endpoint.search = new URLSearchParams({
product: "predictions",
application: "signalk.org/node-server",
begin_date: (0, moment_1.default)(date).format("YYYYMMDD"),
end_date: (0, moment_1.default)(date).add(7, "days").format("YYYYMMDD"),
datum,
station: station.id,
time_zone: "gmt",
units: "metric",
interval: "hilo",
format: "json",
}).toString();
app.debug(`Fetching tides from NOAA: ${endpoint}`);
try {
const res = await fetch(endpoint.toString());
if (!res.ok)
throw new Error("Failed to fetch NOAA tides: " + res.statusText);
const body = await res.json();
app.debug("NOAA response: \n" + JSON.stringify(body, null, 2));
if (body.error)
throw new Error(body.error.message);
return {
station: {
name: station.name,
position: {
latitude: station.lat,
longitude: station.lng,
},
},
extremes: body.predictions.map(({ t, v, type }) => ({
type: type === "H" ? "High" : "Low",
value: Number(v),
time: new Date(`${t}Z`).toISOString(),
})),
};
}
catch (err) {
app.setPluginError(`Failed to fetch NOAA tides: ${err}`);
// @ts-expect-error: app.error should accept more than just a string
app.error(err);
throw err;
}
};
}
};
}
;
class StationList extends Map {
static async load(app) {
const filename = path_1.default.join(app.config.configPath, "noaastations.json");
let data;
try {
data = JSON.parse(await promises_1.default.readFile(filename, 'utf-8'));
app.debug("NOAA: Loaded cached tide stations from " + filename);
}
catch (e) {
app.debug(`NOAA: failed to load cached tide stations: ${e}`);
app.debug('NOAA: Downloading tide stations');
const res = await fetch(stationsUrl);
if (!res.ok)
throw new Error(`Failed to download stations: ${res.statusText}`);
data = await res.json();
await promises_1.default.writeFile(filename, JSON.stringify(data));
}
return new this(data.stations);
}
constructor(data) {
super(data.map((station) => [station.id, station]));
}
closestTo(position) {
return this.near(position, 1)[0];
}
near(position, limit = 10) {
const stationsWithDistances = Array.from(this.values()).map((station) => ({
...station,
distance: (0, geolib_1.getDistance)(position, { latitude: station.lat, longitude: station.lng })
}));
return stationsWithDistances.sort((a, b) => a.distance - b.distance).slice(0, limit);
}
}