Skip to content

Latest commit

 

History

678 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Njord

Logo

Documentation

Design documentation

NOAA ENC update cron

RELEASE_NOTES

Project Overview

Njord is a Marine Electronic Navigational Chart (ENC) server that ingests S-57 hydrographic chart files and serves them as MVT (Mapbox Vector Tiles). It does not strictly follow IHO S-52 display specifications. Live demo: https://openenc.com

Screenshot

Build System

Gradle with Kotlin DSL. Use ./gradlew at the root.

Development Workflow

# 0. Start podman (if needed)
podman machine start 

# 1. Start the database (PostGIS)
cd chart_server_db && podman-compose up

# 2. Build frontend
./gradlew :web:jsBrowserDistribution

# 3a. Run the API server (Kotlin/Native executable)
./gradlew :server:runDebugExecutable

# 3b. Option - build and run production
./gradlew :server:linkReleaseExecutableArch
CHART_SERVER_OPTS='{ "webStaticContent": "./web/build/dist/js/productionExecutable" }' \
./server/build/bin/arch/releaseExecutable/server.kexe ./server/src/nativeMain/resources

# 3c. Option - build and run in container
./gradlew makeImg
podman run --rm --network host ghcr.io/manimaul/njord-chart-server:<version>


# 4. Frontend with hot-reload (separate terminal)
./gradlew :web:jsBrowserDevelopmentRun --continuous

Testing

# Run all tests
./gradlew allTests

# Run tests for a specific module
./gradlew :geojson:allTests
./gradlew :libgdal:allTests
./gradlew :server:allTests

# Run tests for a specific module and build target
./gradlew :geojson:jsTest
./gradlew :geojson:jsBrowserTest
./gradlew :geojson:archTest
./gradlew :geojson:jvmTest

NOAA update job

NOAA ENC update cron

Containerization & Deployment

./gradlew :makeImg      # Build Podman image
./gradlew :pubImg       # Push image to registry
./gradlew :k8sApply     # Deploy to Kubernetes
./gradlew :deploy       # Full pipeline (build → push → deploy)

Architecture

Module Structure

Module Target Purpose
server Kotlin/Native HTTP server: ingestion, tile serving, API endpoints
web Kotlin/JS Compose frontend with MapLibre GL map
shared Multiplatform (JVM/JS/Native) Shared data models, serialization
shared_fe Kotlin/JS Frontend UI components, MapLibre bindings
libgdal Kotlin/Native C interop bindings to GDAL 3.6.2
libpq Kotlin/Native C interop bindings to PostgreSQL client
libsqlite Kotlin/Native C interop bindings to SQLite (region export for mobile)
libexpat Kotlin/Native C interop bindings to expat (streaming XML SAX parsing)
enc_cron Kotlin/Native Nightly NOAA ENC catalog diff and download job
geojson Multiplatform GeoJSON RFC 7946 implementation

Data Pipeline

  1. Ingestion: User uploads a zip of S-57 .000 files via the web UI
  2. Processing: server/src/nativeMain/kotlin/ingest/ChartIngest.kt reads files via GDAL (libgdal), extracts chart metadata and features, converts all geometries to WGS84 (EPSG:4326), stores as GeoJSON FeatureCollections in PostGIS
  3. Tile serving: On tile request, TileEncoder queries PostGIS for charts and features within the tile envelope, clips geometries, and encodes as protobuf MVT
  4. Styling: Each S-57 object class has a corresponding layer class in server/src/nativeMain/kotlin/layers/ that defines Mapbox GL style rules. The TileEncoder adds symbol properties (e.g., SY) to feature properties so the style JSON can reference them via ["get","SY"].
  5. Region export: After ingestion completes, RegionExportWorker generates SQLite archive files for configured geographic regions. Mobile clients download these archives and replay the data into a local SQLite database for offline chart rendering.
  6. NOAA updates: The enc_cron job (Kubernetes CronJob, nightly) streams NOAA's ISO-19115 product catalog, diffs it against GET /v1/chart_editions, and downloads only the cells whose revision changed - bundling them into zips dropped in the ingest save/ directory.

Key Files in server

  • Main.kt — entry point; initializes GDAL, database, HTTP server
  • geo/TileEncoder.kt — MVT tile assembly
  • ingest/ChartIngest.kt — S-57 file processing pipeline
  • ingest/RegionExporter.kt — region SQLite archive generation
  • ingest/RegionExportWorker.kt — post-ingestion region export scheduling
  • layers/ — one file per S-57 object class (e.g., Depare.kt, Soundg.kt, Boyspp.kt)
  • endpoints/ — HTTP/WebSocket route handlers

Database

PostgreSQL 13 + PostGIS. For development, run via Podman Compose in chart_server_db/. The features table stores GeoJSON FeatureCollections indexed by chart coverage polygon for spatial queries.

System Requirements

  • openjdk-17-jre-headless — required for Gradle and Kotlin/Native toolchain
  • libgdal-dev — Geospatial Data Abstraction Library - Development files
  • libpq-dev - header files for libpq5 (PostgreSQL library)
  • libexpat1-dev - header files for libexpat1 (XML parsing, used by enc_cron)
  • PostGIS 13 — run in Podman for development (see chart_server_db/README.md)

Important Notes

  • GitHub Packages authentication uses GH_USER and GH_TOKEN environment variables
  • The docs/DESIGN.md links reference an older JVM implementation; the current codebase is Kotlin/Native
  • Platform targets: macOS x64/ARM64, Linux x64/ARM64 (backend), JS (frontend / browser)
  • Targets not used: Windows x64 (Native)

Development on MacOS

Required packages

brew install gdal libpq gd libzip sqlite expat openssl@3 openjdk@21

Useful packages

brew install podman podman-compose

You'll need to set memory to at leas 8192 to build container image(s)

podman machine set --memory 8192 

About

Open Source Electronic Navigation Chart Server

Resources

Stars

49 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages