From 1d4ae6c76d30dae1283e7a3a58a6e62f3e04bae9 Mon Sep 17 00:00:00 2001 From: hermes-explorigin Date: Sun, 16 Aug 2026 19:43:31 +0000 Subject: [PATCH] Humanize turn-by-turn distances; drop arrival step distance --- src/lib/components/DirectionsPanel.svelte | 67 ++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/lib/components/DirectionsPanel.svelte b/src/lib/components/DirectionsPanel.svelte index d64d121..cd4cfde 100644 --- a/src/lib/components/DirectionsPanel.svelte +++ b/src/lib/components/DirectionsPanel.svelte @@ -119,6 +119,66 @@ return min ? `${h} h ${min} min` : `${h} h`; } +// ---- Humanized (spoken-style) distance phrasing for the turn-by-turn list ---- +// e.g. "500 ft", "half a mile", "a quarter mile", "1 and a half miles", "2 km". +function fracWords(f: number): string { + if (Math.abs(f - 0.25) < 0.001) return 'a quarter'; + if (Math.abs(f - 0.5) < 0.001) return 'a half'; + if (Math.abs(f - 0.75) < 0.001) return 'three-quarters'; + return `${f.toFixed(2)}`; + } + function friendlyMiles(mi: number): string { + if (mi >= 10) return `${Math.round(mi)} miles`; + const q = Math.round((mi + Number.EPSILON) * 4) / 4; // nearest quarter mile + const whole = Math.floor(q); + const frac = q - whole; + const unit = whole === 1 ? 'mile' : 'miles'; + if (whole === 0) { + if (Math.abs(frac - 0.25) < 0.001) return 'a quarter mile'; + if (Math.abs(frac - 0.5) < 0.001) return 'half a mile'; + if (Math.abs(frac - 0.75) < 0.001) return 'three-quarters of a mile'; + return `${q.toFixed(2)} miles`; + } + if (frac === 0) return whole === 1 ? '1 mile' : `${whole} miles`; + return `${whole} and ${fracWords(frac)} miles`; + } + function friendlyKms(km: number): string { + if (km >= 10) return `${Math.round(km)} km`; + const q = Math.round((km + Number.EPSILON) * 4) / 4; + const whole = Math.floor(q); + const frac = q - whole; + if (whole === 0) { + if (Math.abs(frac - 0.25) < 0.001) return 'a quarter km'; + if (Math.abs(frac - 0.5) < 0.001) return 'half a km'; + if (Math.abs(frac - 0.75) < 0.001) return 'three-quarters of a km'; + return `${q.toFixed(2)} km`; + } + if (frac === 0) return whole === 1 ? '1 km' : `${whole} km`; + return `${whole} and ${fracWords(frac)} km`; + } + function humanizeDistance(m: number): string { + if ($prefs.system === 'imperial') { + const ft = m * 3.28084; + const mi = m / 1609.344; + if (mi < 0.12) { + let f = Math.round(ft / 50) * 50; // '500 ft' + if (f < 75) f = Math.round(ft / 10) * 10; // below ~75ft be more precise + return `${Math.max(20, f)} ft`; + } + return friendlyMiles(mi); + } + if (m < 200) { + let d = Math.round(m / 25) * 25; + if (d < 50) d = Math.round(m); + return `${Math.max(10, d)} m`; + } + return friendlyKms(m / 1000); + } + // Detects the terminal step so we can drop its (always 0) distance. + function isArrival(t: TurnStep): boolean { + return t.instruction === 'Arrive at destination'; + } + // --- destination (set via the context dialog or search/picked below) --- function setDestination(lat: number, lon: number, label?: string) { onDestinationChange({ lat, lon, label }); @@ -360,7 +420,11 @@
Turn-by-turn
    {#each turns.list as t, i (i + '-' + t.instruction)} -
  1. {formatDistance(t.distanceM)}{t.instruction}
  2. + {#if isArrival(t)} +
  3. {t.instruction}
  4. + {:else} +
  5. {humanizeDistance(t.distanceM)}{t.instruction}
  6. + {/if} {/each}
{turns.list.length} steps ยท {formatDistance(turns.total)} total
@@ -444,6 +508,7 @@ .turnlist { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; } .turnlist li { display: flex; gap: 8px; align-items: baseline; font-size: 0.74rem; } .tdist { color: #0ea5e9; font-weight: 600; min-width: 52px; } + .tarrival { font-weight: 700; color: #16a34a; } .tins { color: #0f172a; } .turns-total { color: #94a3b8; font-size: 0.68rem; margin-top: 4px; } \ No newline at end of file