ADR 002: Maritime Location Naming Convention and Island Priority Rules¶
- Status: Accepted
- Date: 2026-08-07
- Deciders: SailEver Architecture & Engineering Team
- Technical Context:
sailever-railsBackend,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.