Format every metre/kilometre distance through one shared helper

Directions, the Contacts marker and panel, placed annotation labels, the
drawn-line measure, the Cockpit and formatRouteDistance each chose the unit
and precision on their own, and most chose before rounding: #1450, #1433 and
formatDistanceText or kilometreDigits from src/data/distanceFormat.js, which
cut after rounding. Each keeps its own metre step and rendering.

That also fixes the 10 km cut the Contacts marker and panel, the drawn-line
measure and the Cockpit still had (9,960 m read 10.0 km). The helper returns
kilometres already rounded, so the plain marker and the localized panel no
longer disagree on half-tenths such as 1,450 m.

A negative annotation distance now leaves the label without a distance, and
a drawn line with a coordinate that is not a number shows no measure, rather
than printing 0 m or NaN km. The HUD altitude tag and the spoken view height
keep their own unit choice.
This commit is contained in:
Sameh Khamis committed 2026-11-01 22:40:28 -08:00
1 parent 2124f56f92
commit d14205160a
18 files changed
+169 -46

No files matched your search

+2 -6
View File
@@ -12,6 +12,7 @@ import {
sampleGroundHeight,
} from './annotationResolver.js';
import { ringCentroid } from './drawMode.js';
import { formatDistanceText } from '../data/distanceFormat.js';
// Dev convenience: expose the app's Cesium instance for console/preview probing
// (single shared module instance — avoids dual-Cesium state bugs when testing).
@@ -1572,12 +1573,7 @@ function greatCircleM(a, b) {
}
function formatDistance(m) {
if (!Number.isFinite(m)) return null;
// Round before each unit cut: 996 m was printed as "1000 m" and
// 9,960 m as "10.0 km".
const tens = Math.round(m / 10) * 10;
if (tens < 1000) return `${tens} m`;
return `${(m / 1000).toFixed(Math.round(m / 100) >= 100 ? 0 : 1)} km`;
return formatDistanceText(m, { metreStep: 10 });
}
function composeRouteLabel(baseLabel, distM, durS, mode, fallback) {
+2 -5
View File
@@ -21,6 +21,7 @@ import { t, tn } from '../i18n/index.js';
*/
import { greatCircleMeters } from '../geoDistance.js';
import { formatDistanceText } from '../data/distanceFormat.js';
export const DRAW_SHAPES = Object.freeze(['area', 'line', 'pin']);
export const MIN_VERTICES = Object.freeze({ area: 3, line: 2, pin: 1 });
@@ -207,11 +208,7 @@ export function ringCentroid(vertices) {
export function formatMeasure(session) {
if (!session) return '';
if (session.shape === 'line') {
const m = pathLengthM(session.vertices);
// Round before the kilometre cut. 999.6 m was printed as "1000 m".
const whole = Math.round(m);
if (whole < 1000) return `${whole} m`;
return `${(m / 1000).toFixed(m >= 10000 ? 0 : 1)} km`;
return formatDistanceText(pathLengthM(session.vertices)) ?? '';
}
if (session.shape === 'area') {
const m2 = ringAreaM2(session.vertices);
+8
View File
@@ -346,6 +346,14 @@ test('a line that rounds up to a kilometre reads 1.0 km, not 1000 m', () => {
assert.match(drawHint(line), /^1\.0 km · /);
});
test('a line that rounds to 10.0 km reads in whole kilometres', () => {
const metresPerDegree = (6371.0088 * 1000 * Math.PI) / 180;
const line = createDrawSession('line');
addVertex(line, { lon: 0, lat: 0 });
addVertex(line, { lon: 0, lat: 9960 / metresPerDegree });
assert.equal(formatMeasure(line), '10 km');
});
test('an area that rounds up to the next unit reads in that unit', () => {
// A square on the equator whose shoelace area is about `m2`.
const square = (m2) => {