Skip to content

ADR 002: Maritime Location Naming Convention and Island Priority Rules

  • Status: Accepted
  • Date: 2026-08-07
  • Deciders: SailEver Architecture & Engineering Team
  • Technical Context: sailever-rails Backend, Sailever::Geo::MaritimeLocationNamingService, Logbook Track Editors (start_name / end_name)

1. Context & Problem Statement

Sailing logbooks rely heavily on intuitive, recognizable geographical names for track endpoints (start_name and end_name). Standard reverse-geocoders (such as Google Maps or generic street-level Nominatim) are designed for land vehicles and pedestrians. When given maritime coordinates: 1. They frequently return street addresses, postal codes, or inland town centers (e.g. returning "Via Roma" or "San Teodoro" for a yacht anchored in a coastal cove). 2. They do not distinguish between an island context and a mainland context. For sailors operating in archipelagos (e.g., La Maddalena, Balearic Islands, Cyclades, Caribbean), the island name is the primary geographical anchor.

We need a standardized, deterministic naming convention and a standalone service that converts (latitude, longitude) into a human-readable maritime location name.


2. Decision Rules & Naming Format

Rule 1: Island Priority Formatting

Whenever a harbour, marina, anchorage, or bay is located on an island, the formatted name MUST ALWAYS place the island name first, followed by a comma, followed by the specific location name:

$$\text{Format: } \text{"[Island Name], [Location/Harbour/Anchorage Name]"}$$

Examples: - La Maddalena, Porto Gavetta - Elba, Portoferraio Marina - Corsica, Cala di Volpe - Ischia, Porto d'Ischia

Rule 2: Mainland Coastal Formatting

When a harbour, marina, or anchorage is located on a mainland coast: - Harbours / Marinas: "[Harbour/Marina Name]" (e.g., Porto Cervo Marina, Marina di Olbia). - Anchorages / Bays: "[Bay Name] Anchorage" or "[Bay Name]" (e.g., Golfo di Arzachena Anchorage).

Rule 3: Offshore / At-Sea Formatting

When a position is $> 2.0\text{ NM}$ from any harbour or island shoreline: - "[Distance] NM Off [Nearest Landmark/Cape]" (e.g., 5 NM Off Capo Caccia, 12 NM Off Bonifacio).


3. Resolution Hierarchy & Algorithm

When Sailever::Geo::MaritimeLocationNamingService.call(lat, lon) is invoked:

[ Input Coordinate (lat, lon) ]
               │
               ├── Proximity Check (Spatial / Reverse Geo)
               │
               ├── 1. Island Check: Is (lat, lon) on or within 2.5 NM of a named island?
               │      ├── YES -> Save island_name (e.g., "La Maddalena")
               │      └── NO  -> island_name = nil
               │
               ├── 2. Feature Classification:
               │      ├── Within 1.0 NM of Harbour/Marina   -> type = :harbour
               │      ├── Within 1.5 NM of Anchorage/Bay   -> type = :anchorage / :bay
               │      └── > 2.0 NM offshore                -> type = :offshore
               │
               └── 3. Formatter Output:
                      ├── island_name present: "#{island_name}, #{location_name}"
                      ├── mainland harbour:   "#{location_name}"
                      └── offshore:           "#{dist_nm} NM Off #{landmark_name}"

4. Decoupled Service Architecture

To keep data ingestion and track computation decoupled from external geocoding providers, all naming logic is isolated in a standalone service class:

result = Sailever::Geo::MaritimeLocationNamingService.call(latitude: 47.3012, longitude: -14.1245)
# Returns Struct / Hash:
# {
#   proposed_name: "La Maddalena, Porto Gavetta",
#   island_name: "La Maddalena",
#   location_name: "Porto Gavetta",
#   feature_type: :harbour,
#   is_island: true
# }

5. Consequences

  • Positive: Logbook track names automatically read naturally to sailors (La Maddalena, Porto Gavetta), endpoint inheritance behaves predictably, and naming logic is 100% testable in isolation.
  • Negative: Requires reverse-geocoding data source alignment (OpenStreetMap seamarks + island polygon datasets) or structured fallback heuristics.