-
Notifications
You must be signed in to change notification settings - Fork 2
Add TICON-4 data #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add TICON-4 data #16
Conversation
514bda0 to
5d941f9
Compare
ed6768c to
b8fd033
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds functionality to import 4,838 global tide stations from the TICON-4 (TIdal CONstants based on GESLA-4) dataset. The implementation includes a bash script to download the data, a TypeScript module to parse and convert the data to the repository's station JSON schema, and a new utility module for computing tidal datums.
Key changes:
- New import tooling for TICON-4 dataset with automatic data download and processing
- New datum computation utilities using synthetic tidal predictions
- Updated @neaps/tide-predictor dependency to version 0.3.0 to support additional harmonic constituents
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/import-ticon | Bash script that downloads TICON-4 data and invokes the TypeScript import script |
| tools/import-ticon.ts | TypeScript module that parses TICON-4 CSV files and converts stations to JSON schema |
| tools/datum.ts | New utility module for computing tidal datums from harmonic constituents using synthetic predictions |
| package.json | Updates @neaps/tide-predictor dependency from 0.2.1 to 0.3.0 |
| .gitignore | Adds tmp directory to exclude downloaded data from version control |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tools/import-ticon.ts
Outdated
|
|
||
| function dayMonthYearToDate(date: string) { | ||
| const [ day, month, year ] = date.split('/').map((v) => parseInt(v, 10)) | ||
| if(!day || !month || !year) { |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after 'if' keyword. The code style should be consistent with if statements having a space between 'if' and the opening parenthesis.
| const constituents: HarmonicConstituent[] = rows.map((row) => ({ | ||
| name: row.con, | ||
| amplitude: parseFloat(row.amp) / 100, // convert cm to m | ||
| phase: ((parseFloat(row.pha) % 360) + 360) % 360, // lag in degrees; normalize to [0, 360) | ||
| speed: constituents_by_name[row.con]?.speed, | ||
| })) | ||
|
|
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The speed property could be undefined if the constituent name in row.con is not found in constituents_by_name. This would result in harmonic constituents with undefined speed values. Consider adding validation to skip constituents that aren't recognized or provide a clear error message when a constituent is not found in the lookup table.
| const constituents: HarmonicConstituent[] = rows.map((row) => ({ | |
| name: row.con, | |
| amplitude: parseFloat(row.amp) / 100, // convert cm to m | |
| phase: ((parseFloat(row.pha) % 360) + 360) % 360, // lag in degrees; normalize to [0, 360) | |
| speed: constituents_by_name[row.con]?.speed, | |
| })) | |
| const constituents: HarmonicConstituent[] = rows.map((row) => { | |
| const constituentMeta = constituents_by_name[row.con] | |
| if (!constituentMeta || typeof constituentMeta.speed !== 'number') { | |
| throw new Error(`Unknown or invalid constituent '${row.con}' in TICON data for station '${rows[0].tide_gauge_name}'`) | |
| } | |
| return { | |
| name: row.con, | |
| amplitude: parseFloat(row.amp) / 100, // convert cm to m | |
| phase: ((parseFloat(row.pha) % 360) + 360) % 360, // lag in degrees; normalize to [0, 360) | |
| speed: constituentMeta.speed, | |
| } | |
| }) |
tools/import-ticon.ts
Outdated
| throw new Error('No rows to convert') | ||
| } | ||
|
|
||
| const gesla = metadata[rows[0].tide_gauge_name] |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The metadata lookup at line 90 could be undefined if the tide_gauge_name is not found in the metadata. This would cause a runtime error when trying to access gesla['SITE NAME'] at line 106. Consider adding a check to handle the case where metadata is missing for a station.
| const gesla = metadata[rows[0].tide_gauge_name] | |
| const gesla = metadata[rows[0].tide_gauge_name] | |
| if (!gesla) { | |
| throw new Error(`Missing metadata for tide gauge: ${rows[0].tide_gauge_name}`) | |
| } |
tools/import-ticon.ts
Outdated
|
|
||
| function dayMonthYearToDate(date: string) { | ||
| const [ day, month, year ] = date.split('/').map((v) => parseInt(v, 10)) | ||
| if(!day || !month || !year) { |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation check at line 76 will fail for day, month, or year values of 0, but these can never occur in valid dates. However, the check doesn't handle NaN values that could result from parsing non-numeric strings. Consider using isNaN() or checking if the parsed values are valid numbers to properly catch parsing errors.
| if(!day || !month || !year) { | |
| if (![day, month, year].every((v) => Number.isInteger(v) && v > 0)) { |
tools/datum.ts
Outdated
| if (times.length === 0) { | ||
| throw new Error('times array is empty') | ||
| } | ||
| const firstTime = times[0] | ||
| const lastTime = times[times.length - 1] | ||
| if (!firstTime || !lastTime) { | ||
| throw new Error('times array is empty') |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check at line 72 already validates that times.length is non-empty, making the subsequent check at line 83 redundant. Additionally, the error messages at lines 84 and 89 both say "times array is empty" when line 89 should indicate a different issue (array contains undefined values). Consider removing the redundant check or providing distinct error messages.
| if (times.length === 0) { | |
| throw new Error('times array is empty') | |
| } | |
| const firstTime = times[0] | |
| const lastTime = times[times.length - 1] | |
| if (!firstTime || !lastTime) { | |
| throw new Error('times array is empty') | |
| const firstTime = times[0] | |
| const lastTime = times[times.length - 1] | |
| if (!firstTime || !lastTime) { | |
| throw new Error('times array contains invalid or undefined Date values') |
tools/import-ticon.ts
Outdated
| process.stdout.write('.') | ||
| } | ||
|
|
||
| console.log(`\nDone. Created ${created} files`) |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The message says "Created X files" but the counter represents the number of stations processed, which corresponds to the number of files created only if all operations succeeded. Since the script could potentially skip some stations or fail silently in edge cases, consider using a more precise message like "Created X station files" or tracking successful saves explicitly.
| console.log(`\nDone. Created ${created} files`) | |
| console.log(`\nDone. Created ${created} station files`) |
…so copilot can focus on more meaningful feedback
This pull request adds a script to import stations from TICON-4, TIdal CONstants based on GESLA-4 sea-level records.
It includes 4,838 global stations.
TODO: