Humanize turn-by-turn distances; drop arrival step distance
All checks were successful
CI / test-and-build (push) Successful in 1m2s

This commit is contained in:
hermes-explorigin 2026-08-16 19:43:31 +00:00
parent fb6ad3eb0c
commit 1d4ae6c76d

View File

@ -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 @@
<div class="turns-head"><span>Turn-by-turn</span></div>
<ol class="turnlist">
{#each turns.list as t, i (i + '-' + t.instruction)}
<li><span class="tdist">{formatDistance(t.distanceM)}</span><span class="tins">{t.instruction}</span></li>
{#if isArrival(t)}
<li><span class="tarrival">{t.instruction}</span></li>
{:else}
<li><span class="tdist">{humanizeDistance(t.distanceM)}</span><span class="tins">{t.instruction}</span></li>
{/if}
{/each}
</ol>
<div class="turns-total">{turns.list.length} steps · {formatDistance(turns.total)} total</div>
@ -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; }
</style>