Bathymetry as web map tiles for MapLibre / Mapbox GL, built from a mosaic of global and regional sources. Served as raster DEM tiles for depth shading and hillshade, and vector contour tiles for crisp lines and labels.
Coverage is global through z8 (~15″) with regional detail to z14 (~0.25″) where high-res sources exist.
Warning
Not for navigational use. Do not use this bathymetry for navigation, or in any situation where inaccuracies could result in harm to people or property. It is intended for general-purpose web mapping and visualization.
Depth datum varies by source. Some regional sources use a low-water chart datum (such as LAT or MLLW), while other sources are approximately MSL. The data is interpolated and merged from sources of differing age, resolution, and datum, then smoothed during tiling, so values are approximate and do not account for the current tide or water level. Gridded bathymetry also omits navigational hazards (rocks, wrecks, obstructions, shoals, aids to navigation) shown on official charts.
Always consult official nautical charts for navigation.
A ready-made nautical chart style with depth shading, contour lines and labels, spot soundings, and drying areas is published alongside the tiles at https://tiles.openwaters.io/seascape/style.json.
Point MapLibre (or Maputnik) straight at it:
import maplibregl from "maplibre-gl";
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.openwaters.io/seascape/style.json",
center: [-73.96, 40.55],
zoom: 11,
});Parameters:
?unit=m|ft|fm: switches depth units in the style (default: metres)?safety=<metres>: shades water shallower than the given depth as a hazard (default: 2 m)
The style is generated by the
@openwaters/seascape npm package, which also supports
composing the layers into your own style and runtime controls (switching units,
safety depth, reading depth at a point).
The bathymetry is available through three TileJSON documents:
- Raster DEM — TileJSON; Terrarium-encoded depth and categorical land/water pixels for color relief, hillshade, and 3D terrain.
- Chart vectors — TileJSON; MVT depth areas, contour lines, and spot soundings.
- Source coverage — TileJSON; MVT source-provenance footprints.
Use the TileJSON URLs so clients receive the current zoom range, bounds, and required attribution.
Point a raster-dem source and a vector source at the two TileJSONs:
import maplibregl from "maplibre-gl";
const BASE = "https://tiles.openwaters.io/seascape";
const map = new maplibregl.Map({
container: "map",
center: [-73.96, 40.55],
zoom: 11,
style: {
version: 8,
sources: {
"bathymetry-dem": {
type: "raster-dem",
url: `${BASE}/raster.json`,
encoding: "terrarium", // required — MapLibre defaults to Mapbox encoding
tileSize: 512,
},
bathymetry: {
type: "vector",
url: `${BASE}/vector.json`,
},
},
// See https://tiles.openwaters.io/seascape/style.json for styles
layers: [],
},
});
// Optional: 3D seafloor from the same DEM
// map.on("load", () => map.setTerrain({ source: "bathymetry-dem", exaggeration: 1 }));For depth color-relief shading, contour labels, and a layer-toggle UI, see the
demo viewer in index.js. Attribution is carried in each TileJSON, so
MapLibre's attribution control credits the sources automatically.
The full contract — every layer, field, pixel-value meaning, and the semantics no field advertises (chart-datum zero, shoal bias, sign conventions) — is defined in docs/schema.md and versioned by the integer schema field each TileJSON serves. The short version:
- Raster decodes Terrarium to
v = R × 256 + G + B / 256 − 32768metres:v < 0is depth below the winning source's datum (shallow-biased — quantization never deepens); the non-negative domain is three flat category codes —0water of unknown depth,1drying foreshore,2land. MapLibre must be toldencoding: "terrarium"directly on theraster-demsource — it does not read that setting from TileJSON. - Vector layers:
deparedepth-area polygons (positive-downdrval1/drval2bounds, drying as negativedrval1) andcontoursisobath lines, each cut in metric and fathom-curve ladders selected bysys; plussoundingsspot depths, a single set carrying positive-downdepth_m/depth_ft/depth_fmdirectly. - Coverage is its own tileset: per-source footprint polygons for click-to-identify provenance; keep it a separate MapLibre source so its low-zoom tiles overzoom independently.
The reasoning behind these choices — why land carries no elevation, why every rounding errs shallow, why feet mode gets real fathom curves — is in docs/cartography.md.
Seascape does not yet normalize every source to one vertical datum. Each raster pixel and derived vector feature retains the winning source's datum: some regional sources use a low-water chart datum such as LAT or MLLW, while GEBCO, EMODnet, and several other sources are approximately MSL.
MSL is above a low-water chart datum, so an MSL-referenced depth reads deeper than the equivalent chart-datum depth by the local MSL−LAT separation. That difference is commonly about 0.1–0.5 m in microtidal areas and 2–8 m in macrotidal areas. Source boundaries can therefore also be datum boundaries. The shallow-biased encoding prevents quantization from deepening a source value; it does not correct the source datum or account for the current tide.
GEBCO provides the global base. Regional and local datasets fill in finer detail where available, with higher-resolution or datum-authoritative sources winning where they overlap coarser data.
See sources/README.md for the complete source catalog, coverage, resolution, datum, attribution, licensing, and source requests.
The build pipeline, local development, container usage, and CI/deploy live in CONTRIBUTING.md.
Code — BSD-3-Clause. The pipelines/*.py vendored/adapted from mapterhorn also carry its BSD-3 copyright.
Tiles — the tile compilation published at tiles.openwaters.io/seascape is licensed CC BY 4.0: free for any use with the following attribution:
© Open Waters (https://openwaters.io/charts/seascape#license)
The underlying data remains under each source's own open terms. The full attribution list is available in the attribution field of the TileJSON endpoints (raster.json, vector.json). It is displayed by MapLibre's attribution control automatically, which satisfies each source's terms.
