Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 8 additions & 16 deletions .github/workflows/updates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ jobs:
run: npm install

- name: Run NOAA stations update
run: tools/update-noaa-stations.ts
env:
WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
tools/update-noaa-stations.ts 2>&1 | tee /tmp/noaa-update.log
# Extract the markdown summary (everything after "## Summary")
sed -n '/^## Summary$/,$p' /tmp/noaa-update.log > /tmp/noaa-summary.md
printf '\n### Workflow\n\n This PR was automatically created by the monthly NOAA station update workflow.Run: %s\n' "$WORKFLOW_URL" >> /tmp/noaa-summary.md

- name: Check for changes
id: changes
Expand All @@ -46,19 +52,5 @@ jobs:
with:
commit-message: "chore: update NOAA tide station data"
title: "Update NOAA Stations"
body: |
This PR contains updates to NOAA tide station data from the official NOAA API.

### Changes
- Updated harmonic constituents for existing stations
- Added any new stations discovered

### Source
- Stations list: https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.json?type=tidepredictions
- Harmonic constituents: https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/{id}/harcon.json

### Workflow
Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

This PR was automatically created by the monthly NOAA station update workflow.
body-path: /tmp/noaa-summary.md
branch: update-noaa-stations-${{ github.run_number }}
63 changes: 59 additions & 4 deletions tools/update-noaa-stations.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env node

import createFetch from "make-fetch-happen";
import { normalize, save } from "./station.ts";
import { normalize, save, DATA_DIR } from "./station.ts";
import type { StationData } from "../src/index.ts";
import { loadGeocoder } from "./geocode.ts";
import { readFile } from "fs/promises";
import { join } from "path";

const fetch = createFetch.defaults({
cachePath: "node_modules/.cache",
Expand All @@ -17,22 +19,75 @@ const STATIONS_URL =

const geocoder = await loadGeocoder();

async function readExisting(id: string): Promise<string | null> {
try {
return await readFile(join(DATA_DIR, "noaa", `${id}.json`), "utf-8");
} catch {
return null;
}
}

async function main() {
const { stations } = await fetch(
`${STATIONS_URL}?type=tidepredictions&expand=details,tidepredoffsets&units=metric`,
).then((r) => r.json());

console.log(`Fetched metadata for ${stations.length} stations.`);

const added: string[] = [];
const updated: string[] = [];
let unchanged = 0;
let skipped = 0;

for (const meta of stations) {
// At least one station lists itself as its own reference, but doesn't have harmonic data
if (meta.id === meta.tidepredoffsets?.refStationId) continue;
if (meta.id === meta.tidepredoffsets?.refStationId) {
skipped++;
continue;
}

const station = await buildStation(meta);
const newContent = JSON.stringify(station, null, 2) + "\n";
const existing = await readExisting(meta.id);

await save("noaa", station);

if (existing === null) {
added.push(`${meta.id} (${station.name})`);
} else if (existing !== newContent) {
updated.push(`${meta.id} (${station.name})`);
} else {
unchanged++;
}

await save("noaa", await buildStation(meta));
process.stdout.write(".");
}

console.log(`\nDone. Created ${stations.length} stations.`);
console.log("\n");
console.log("## Summary\n");
console.log(`Processed ${stations.length} stations (${skipped} skipped)\n`);

if (added.length > 0) {
console.log(`### ${added.length} stations added\n`);
for (const s of added) console.log(`- ${s}`);
console.log();
}

if (updated.length > 0) {
console.log(`### ${updated.length} stations updated\n`);
for (const s of updated) console.log(`- ${s}`);
console.log();
}

console.log(`${unchanged} stations unchanged\n`);

console.log("### Source\n");
console.log(
`- Stations list: https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations.json?type=tidepredictions`,
);
console.log(
`- Harmonic constituents: https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi/stations/{id}/harcon.json`,
);
}

async function buildStation(meta: any): Promise<StationData> {
Expand Down