import type { MapScaleUnits } from './units'; /** Round numbers a reader can actually divide by. */ const NICE_STEPS = [1, 2, 5]; /** * Picks the largest "nice" distance that still fits inside `maxWidthPx`. * * A bar labelled 1.37 km is useless for estimating anything; the whole point is * that the reader can halve it or triple it by eye. */ export function niceScaleDistance( metersPerPixel: number, maxWidthPx: number, units: MapScaleUnits ): { distance: number; widthPx: number; label: string } | null { if (!Number.isFinite(metersPerPixel) || metersPerPixel <= 0) return null; const unitMeters = units === 'imperial' ? 0.3048 : 1; const maxUnits = (metersPerPixel * maxWidthPx) / unitMeters; if (maxUnits <= 0) return null; const magnitude = Math.floor(Math.log10(maxUnits)); let best = NICE_STEPS[0] * 10 ** magnitude; for (let exponent = magnitude; exponent <= magnitude + 1; exponent += 1) { NICE_STEPS.forEach(step => { const candidate = step * 10 ** exponent; if (candidate <= maxUnits && candidate > best) best = candidate; }); } const widthPx = (best * unitMeters) / metersPerPixel; const label = units === 'imperial' ? best >= 5280 ? `${(best / 5280).toFixed(best / 5280 < 10 ? 1 : 0)} mi` : `${Math.round(best)} ft` : best >= 1000 ? `${(best / 1000).toFixed(best / 1000 < 10 ? 1 : 0)} km` : `${Math.round(best)} m`; return { distance: best, widthPx, label }; }