go.ngs.io/tides-api

A high-performance tidal prediction API written in Go, providing harmonic tidal analysis with support for multiple data sources.

go get go.ngs.io/tides-api
Version:
License:
NOASSERTION
Author:
ngs
Repository:
https://github.com/ngs/tides-api
Documentation:
https://pkg.go.dev/go.ngs.io/tides-api
Last Updated:
2025-10-27T09:39:20Z

Tide API

A high-performance tidal prediction API written in Go, providing harmonic tidal analysis with support for multiple data sources.

Features

  • Harmonic Tidal Analysis: Calculate tide heights using standard tidal constituents (M2, S2, K1, O1, etc.)
  • Astronomical Nodal Corrections: Accurate predictions using nodal corrections based on Schureman (1958)
  • Extrema Detection: Automatically identify high and low tides with parabolic interpolation
  • Multiple Data Sources:
    • Mock CSV data for development and testing
    • FES2014/2022 NetCDF support with bilinear interpolation
    • JMA hourly data calibration for Japanese ports
  • Flexible Configuration: Datum offsets, timezone selection, and phase conventions
  • Clean Architecture: Hexagonal architecture with clear separation of concerns
  • Production Ready: Docker support, comprehensive tests, and monitoring
  • RESTful API: Simple JSON API with ISO8601 timestamps

Quick Start

Prerequisites

  • Go 1.22 or later
  • Make (optional, for convenience commands)
  • Docker (optional, for containerized deployment)

Installation

# Clone the repository
git clone git@github.com:ngs/tides-api.git
cd tides-api

# Install dependencies
go mod download

# Copy environment configuration
cp .env.example .env

# Run the server
make run

The API will be available at http://localhost:8080.

Using Docker

# Build Docker image
make docker-build

# Run container
make docker-run

API Endpoints

1. Get Tide Predictions

Endpoint: GET /v1/tides/predictions

Query Parameters:

ParameterTypeRequiredDescriptionExample
station_idstring*Station identifiertokyo
latfloat*Latitude (-90 to 90)35.6762
lonfloat*Longitude (-180 to 180)139.6503
startstringYesStart time (RFC3339)2025-10-21T00:00:00Z
endstringYesEnd time (RFC3339)2025-10-21T12:00:00Z
intervalstringNoTime interval (default: 10m)10m, 1h
datumstringNoVertical datum (default: MSL)MSL, LAT
sourcestringNoData source (auto-detect)csv, fes
datum_offset_mfloatNoConstant vertical offset [m] applied to all predicted heights0.768
timezonestringNoOutput timezone for timestampsutc, jst
phase_conventionstringNoPhase convention (fes_greenwich default, or vu)fes_greenwich, vu

* Either station_id OR lat+lon must be provided (mutually exclusive)

Example Request:

curl 'http://localhost:8080/v1/tides/predictions?station_id=tokyo&start=2025-10-21T00:00:00Z&end=2025-10-21T12:00:00Z&interval=10m'

Example Response:

{
  "source": "csv",
  "datum": "MSL",
  "timezone": "+00:00",
  "constituents": ["M2", "S2", "K1", "O1", "N2", "K2", "P1", "Q1"],
  "predictions": [
    {"time": "2025-10-21T00:00:00Z", "height_m": 0.823},
    {"time": "2025-10-21T00:10:00Z", "height_m": 0.791},
    ...
  ],
  "extrema": {
    "highs": [
      {"time": "2025-10-21T03:18:00Z", "height_m": 1.342}
    ],
    "lows": [
      {"time": "2025-10-21T09:42:00Z", "height_m": -0.187}
    ]
  },
  "meta": {
    "model": "harmonic_v0",
    "attribution": "Mock CSV (for dev). Replace with FES later."
  }
}

2. Get Constituents

Endpoint: GET /v1/constituents

Returns information about all available tidal constituents.

Example Request:

curl http://localhost:8080/v1/constituents

Example Response:

{
  "constituents": [
    {
      "name": "M2",
      "speed_deg_per_hr": 28.9841042,
      "description": "Principal lunar semidiurnal"
    },
    ...
  ],
  "count": 18
}

3. Health Check

Endpoint: GET /healthz

Returns server health status.

Example Request:

curl http://localhost:8080/healthz

Example Response:

{
  "status": "ok",
  "time": "2025-10-21T12:00:00Z"
}

Data Sources

CSV Mock Data (Development)

For testing without FES data, use station-based queries with mock CSV files:

  1. Create a CSV file in data/mock_{station_id}_constituents.csv
  2. Format:
constituent,amplitude_m,phase_deg
M2,0.62,145.0
S2,0.21,170.0
K1,0.18,30.0
O1,0.16,85.0
  1. Query with station_id:
curl 'http://localhost:8080/v1/tides/predictions?station_id=tokyo&...'

See data/README_DATA.md for more details.

FES NetCDF Data (Production) ✅ NOW IMPLEMENTED

For production use with FES2014/2022:

Quick Setup:

# 1. Install NetCDF library (macOS)
brew install netcdf

# 2. Setup AVISO credentials
make fes-setup

# 3. Download FES data
make fes-download-major  # Downloads M2, S2, K1, O1, N2, K2, P1, Q1

# 4. Start server
make run

# 5. Test with lat/lon
curl 'http://localhost:8080/v1/tides/predictions?lat=35.6762&lon=139.6503&start=2025-10-21T00:00:00Z&end=2025-10-21T12:00:00Z&interval=10m'

Features:

  • ✅ Full NetCDF file reading
  • ✅ Bilinear interpolation for any lat/lon
  • ✅ Automatic grid caching
  • ✅ Support for multiple file naming conventions
  • ✅ Automatic constituent detection

Documentation:

JMA Calibration & Station Overrides

For Japanese ports we can now calibrate directly against JMA’s published hourly prediction files:

  1. Download the annual TXT file for a station from https://www.data.jma.go.jp/kaiyou/data/db/tide/suisan/txt/<year>/<station>.txt.
  2. Fit harmonic constituents using the new CLI:
# Example: fit Kisarazu (KZ) using the downloaded file
go run ./cmd/jma-harmonics \
  -jma_file /path/to/KZ.txt \
  -station KZ \
  -name "Kisarazu (KZ)" \
  -lat 35.38153 -lon 139.867951 \
  -radius_km 40 \
  > data/jma_station_overrides.json
  1. 一括更新は Go 製ユーティリティで実行できます(TXT を tmp/jma_txt/{CODE}.txt に置いた上で):
go run ./cmd/jma-overrides \
  -stations tmp/jma-stations.json \
  -txt_dir tmp/jma_txt \
  -overrides_out data/jma_station_overrides.json \
  -datum_out data/jma_datum_offsets.json

cmd/jma-overrides は必要なら tmp/bin/jma-harmonics を自動ビルドし、全コード分を順次フィットします。 4. 個別に調整したい場合は cmd/jma-harmonics を直接叩いて JSON を追記できます。data/jma_datum_offsets.json も同じコマンドで併せて再生成されます。

Environment variables:

VariableDefaultPurpose
DATUM_OFFSETS_PATHdata/jma_datum_offsets.jsonCustom path for datum offsets
STATION_OVERRIDES_PATHdata/jma_station_overrides.jsonCustom path for constituent overrides

With the provided Kisarazu overrides the RMSE against JMA’s official hourly predictions drops below 5 cm without manual tweaking.

Development

Project Structure

tides-api/
├── cmd/
│   ├── server/              # Main API server
│   ├── jma-harmonics/       # JMA harmonic analysis tool
│   ├── jma-compare/         # JMA vs API comparison tool
│   ├── jma-overrides/       # Batch JMA station processor
│   └── fes-generator/       # FES NetCDF test data generator
├── internal/
│   ├── domain/              # Core business logic
│   │   ├── tide.go          # Tidal prediction engine
│   │   ├── constituents.go  # Standard constituent definitions
│   │   ├── nodal.go         # Astronomical nodal corrections
│   │   └── nodal_coeffs.go  # External coefficient loader
│   ├── usecase/             # Application use cases
│   │   ├── predict.go       # Prediction orchestration
│   │   └── station_adjustments.go  # JMA calibration
│   ├── adapter/             # External adapters
│   │   ├── store/           # Data stores
│   │   │   ├── csv/         # CSV mock data
│   │   │   ├── fes/         # FES NetCDF loader
│   │   │   └── bathymetry/  # GEBCO bathymetry
│   │   ├── interp/          # Bilinear interpolation
│   │   └── geoid/           # EGM2008 geoid heights
│   ├── http/                # HTTP handlers and routing
│   └── jma/                 # JMA fixed-width data parser
├── data/                    # Tidal data files
│   ├── astro_coeffs.json    # Nodal correction coefficients
│   ├── jma_datum_offsets.json      # JMA datum offsets
│   ├── jma_station_overrides.json  # JMA constituent overrides
│   └── mock_*_constituents.csv     # Mock station data
├── Makefile                 # Development commands
├── Dockerfile               # Container configuration
└── README.md

Make Commands

make help              # Show all available commands
make run               # Run server locally
make build             # Build binary
make test              # Run all tests with coverage
make test-unit         # Run unit tests only
make clean             # Clean build artifacts
make fmt               # Format code
make docker-build      # Build Docker image
make docker-run        # Run in Docker
make curl-health       # Test health endpoint
make curl-tokyo        # Test Tokyo predictions

Running Tests

# Run all tests with coverage
make test

# Run unit tests only (fast)
make test-unit

# Generate HTML coverage report
make test-coverage
open coverage.html

Testing the API

# Start the server
make run

# In another terminal, test endpoints
make curl-health          # Health check
make curl-constituents    # List constituents
make curl-tokyo          # Tokyo predictions
make curl-tokyo-extrema  # Show high/low tides

Architecture

Domain Layer (internal/domain/)

Core tidal physics and calculations:

  • constituents.go: Tidal constituent definitions and angular speeds
  • tide.go: Harmonic analysis, tide height calculation, extrema detection

Use Case Layer (internal/usecase/)

Application logic:

  • predict.go: Orchestrates tide prediction workflow

Adapter Layer (internal/adapter/)

External interfaces:

  • store/csv/: CSV file loader for mock data
  • store/fes/: FES NetCDF loader (stub for future implementation)
  • interp/: Bilinear interpolation for gridded data

HTTP Layer (internal/http/)

API interface:

  • handler.go: Request handlers
  • router.go: Route configuration

Configuration

Environment variables (see .env.example):

VariableDefaultDescription
PORT8080Server port
DATA_DIR./dataCSV data directory
FES_DIR./data/fesFES NetCDF directory
GEBCO_PATH-Path to GEBCO bathymetry NetCDF file
MSS_PATH-Path to MSS (Mean Sea Surface) NetCDF file
GEOID_PATH-Path to EGM2008 geoid NetCDF file
ASTRO_COEFFS_PATHdata/astro_coeffs.jsonPath to nodal correction coefficients
DATUM_OFFSETS_PATHdata/jma_datum_offsets.jsonPath to JMA datum offsets
STATION_OVERRIDES_PATHdata/jma_station_overrides.jsonPath to JMA station overrides
TZAsia/TokyoDisplay timezone

Tidal Physics

Harmonic Analysis

Tide height is calculated using:

η(t) = Σ f_k · A_k · cos(ω_k · Δt + φ_k - u_k) + MSL + datum_offset

Where:

  • A_k: Amplitude of constituent k (meters)
  • φ_k: Greenwich phase (degrees)
  • ω_k: Angular speed (degrees/hour)
  • f_k, u_k: Nodal corrections computed from astronomical arguments (Schureman 1958)
  • MSL: Mean Sea Level offset
  • datum_offset: Optional vertical datum adjustment (e.g., for JMA calibration)

Supported Constituents

The API supports 18 standard tidal constituents:

Semidiurnal (period ~12 hours):

  • M2, S2, N2, K2

Diurnal (period ~24 hours):

  • K1, O1, P1, Q1

Shallow Water:

  • M4, M6, MK3, S4, MN4, MS4

Long Period:

  • Mf, Mm, Ssa, Sa

See /v1/constituents endpoint for full details.

Extrema Detection

High and low tides are detected using:

  1. First derivative sign change detection
  2. Parabolic interpolation for sub-interval accuracy

Implemented Features

✅ Completed

  • FES NetCDF integration with bilinear interpolation
  • Nodal corrections for improved accuracy (Schureman 1958)
  • Astronomical arguments (V0+u)
  • JMA hourly data calibration and harmonic fitting
  • Datum offset support for vertical adjustments
  • Custom timezone support (UTC/JST)
  • Phase convention options (FES Greenwich / V+u)
  • Automatic longitude wrapping for NetCDF grids
  • Complex-valued constituent support (Re/Im pairs)
  • Bathymetry data integration (GEBCO)
  • Geoid height corrections (EGM2008)

Planned Features

  • Additional vertical datums (LAT, MLLW, etc.)
  • Prediction caching layer
  • GraphQL API
  • WebSocket streaming
  • Multiple station batch queries
  • OpenAPI/Swagger documentation

Extension Points

The codebase is designed for easy extension:

  • Nodal Corrections: External coefficient files supported via ASTRO_COEFFS_PATH environment variable
  • New Data Sources: Implement ConstituentLoader interface in adapter/store/store.go
  • Custom Datums: Use datum_offset_m parameter or extend PredictionParams in domain/tide.go
  • Station Overrides: Add entries to data/jma_station_overrides.json for custom calibrations

Performance

  • Latency: <50ms for 144 points (24h @ 10min intervals)
  • Memory: ~5MB base + ~1KB per prediction point
  • Concurrency: Stateless design supports horizontal scaling

License

[Specify your license here]

Attribution

FES Tidal Model

If using FES2014/2022 data:

Carrère L., Lyard F., Cancet M., Guillot A. (2016). FES 2014, a new tidal model—Validation results and perspectives for improvements. In Proceedings of the ESA living planet symposium (pp. 9-13).

FES data is available from AVISO+ and requires registration.

References

  1. Schureman, P. (1958). Manual of Harmonic Analysis and Prediction of Tides. U.S. Coast and Geodetic Survey Special Publication No. 98. U.S. Government Printing Office, Washington, D.C.

  2. Foreman, M. G. G. (1977). Manual for tidal heights analysis and prediction. Institute of Ocean Sciences, Patricia Bay.

  3. Pawlowicz, R., Beardsley, B., & Lentz, S. (2002). Classical tidal harmonic analysis including error estimates in MATLAB using T_TIDE. Computers & Geosciences, 28(8), 929-937.

  4. Carrère L., Lyard F., Cancet M., Guillot A. (2016). FES 2014, a new tidal model—Validation results and perspectives for improvements. In Proceedings of the ESA living planet symposium (pp. 9-13).

Support

For issues, questions, or contributions:

  • Create an issue in the repository
  • Contact: Atsushi Nagase a@ngs.io

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass (make test)
  5. Submit a pull request

Acknowledgments

  • FES team at LEGOS/CNES/CLS for tidal model data
  • Japan Meteorological Agency (JMA) for hourly tide predictions data
  • NOAA for EGM2008 geoid model
  • GEBCO for bathymetry data
  • pyTMD project for nodal correction coefficient references
  • Go community for excellent libraries (Gin, go-netcdf)