OpenDiveMap

Schema

Dive Site Data Model

Each dive site is stored as a row with typed, indexed columns for the fields you filter on most (location, environment, depth, entry, region, sea) and a flexible tags JSONB field for additional objective metadata (local names). This hybrid approach gives you fast queries without rigid migrations.

Site classification uses two independent dimensions: environment (where the dive happens — ocean, lake, river, etc.) and topologies (terrain features — reef, wall, wreck, etc.). A site has exactly one environment but can have multiple topologies.

All measurements use SI / metric units as the canonical format, following international diving standards (PADI, SSI). max_depth is always in meters. Client applications handle imperial conversion for display (meters × 3.28084 = feet).

Fields

id VARCHAR(6) auto-generated

Primary key. 6-character base36 string (a-z, 0-9).

name TEXT required

Canonical name of the dive site — the locally used name or the one most recognized by the international diving community. Not a translation. Localized alternatives go in tags as names_{lang} arrays.

latitude DOUBLE PRECISION required

Decimal degrees, WGS 84. Range: -90 to 90.

longitude DOUBLE PRECISION required

Decimal degrees, WGS 84. Range: -180 to 180.

country_code CHAR(2) required

ISO 3166-1 alpha-2 country code. Examples: MX, ID, BZ.

country_name TEXT computed

Human-readable country name resolved via JOIN from the countries lookup table. E.g. Mexico, Indonesia.

sea_mrgid INTEGER optional

Marine Regions MRGID identifying the IHO Sea Area. E.g. 4314 = Gulf of California, 4287 = Caribbean Sea. Foreign key to a local seas lookup table.

sea_name TEXT computed

Human-readable sea name resolved via JOIN from the seas lookup table. Not stored on the dive site row itself.

environment TEXT required

Where the dive happens. Exactly one value per site. Default: ocean.

oceanlakeriverspringquarryfjordpool
topologies TEXT[] required

Terrain features of the dive site. One or more values per site. Stored in a separate junction table, returned as a JSON array in the API.

reefwallpinnaclewreckcavecavernblue holemuckkelp forestchannelartificial reefopen water
max_depth SMALLINT optional

Maximum depth in meters. Always metric (SI).

entry TEXT required

How divers enter the water at this site. Default: boat.

shoreboatother
tags JSONB optional

Flexible key-value store for additional metadata. See tags reference below.

created_at TIMESTAMPTZ auto

Set automatically on insert.

updated_at TIMESTAMPTZ auto

Updated automatically on every edit via trigger.

Tags reference

The tags field accepts any valid JSON object. The following keys are conventionally used and understood by the API and frontend.

names_es / names_en / ... string[]

Localized names as arrays, keyed by names_{lang} using ISO 639-1 language codes. First element is the preferred name for that language. A site can have multiple names in the same language. Follows the OpenStreetMap / WoRMS pattern of one canonical ID with localized labels.

GeoJSON output

The API returns dive sites as GeoJSON (RFC 7946). A single site from GET /sites/:id returns a Feature:

Feature
{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [longitude, latitude]
  },
  "properties": {
    "id": "a7x3km",
    "name": "string",
    "country_code": "XX",
    "country_name": "string",
    "sea_mrgid": 1234,
    "sea_name": "string",
    "environment": "ocean | lake | river | ...",
    "topologies": ["reef", "wall", ...],
    "max_depth": 0,
    "entry": "shore | boat | other",
    "tags": { ... },
    "created_at": "ISO 8601",
    "updated_at": "ISO 8601"
  }
}

GET /sites returns a FeatureCollection wrapping an array of Features in the same shape. Supports filters: ?country=, ?entry=, ?environment=, ?topology=, ?region=, ?sea= (MRGID), ?bbox=west,south,east,north.

Example

A real record from the database — El Bajo Seamount, Gulf of California:

El Bajo Seamount
{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [-110.3981, 24.2156]
  },
  "properties": {
    "id": "a7x3km",
    "name": "El Bajo Seamount",
    "country_code": "MX",
    "country_name": "Mexico",
    "sea_mrgid": 4314,
    "sea_name": "Gulf of California",
    "environment": "ocean",
    "topologies": ["pinnacle"],
    "max_depth": 40,
    "entry": "boat",
    "tags": {
      "names_es": ["El Bajo", "El Bajito"]
    }
  }
}

Version history

Every update to a dive site automatically snapshots the previous version into dive_sites_history via a database trigger. Retrieve past versions with GET /sites/:id/history, which returns an array ordered by most recent change first.