feat(map): control ghost pin movement speed

This commit is contained in:
DCCONSTRUCTIONS
2026-07-29 18:53:00 +03:00
parent 0c3855eac5
commit 2678295ca7
9 changed files with 121 additions and 8 deletions
+4 -1
View File
@@ -26,6 +26,7 @@ test("ghost pins are generated inside the requested current-view radius", () =>
anchor: { longitude: 37.618423, latitude: 55.751244 },
count: 3,
radiusMeters: 1_000,
maximumSpeedMetersPerSecond: 12,
random: () => samples[index++ % samples.length],
timestamp: 42,
});
@@ -34,7 +35,8 @@ test("ghost pins are generated inside the requested current-view radius", () =>
assert.equal(new Set(pins.map((pin) => pin.label)).size, 3);
assert.ok(pins.every((pin) => /^\d{6}$/.test(pin.label)));
assert.ok(pins.every((pin) => pin.id.startsWith("ghost-16-")));
assert.ok(pins.every((pin) => pin.speedMetersPerSecond >= 3));
assert.ok(pins.every((pin) => pin.speedMetersPerSecond >= 2.4));
assert.ok(pins.every((pin) => pin.speedMetersPerSecond <= 12));
});
test("numeric pin names remain unique with a degenerate random source", () => {
@@ -42,6 +44,7 @@ test("numeric pin names remain unique with a degenerate random source", () => {
anchor: { longitude: 37.618423, latitude: 55.751244 },
count: 4,
radiusMeters: 1_000,
maximumSpeedMetersPerSecond: 18,
random: () => 0,
timestamp: 42,
});
@@ -92,6 +92,7 @@ function serverDocument(overrides = {}) {
ghost_pins: {
count: 12,
radius_meters: 5000,
maximum_speed_meters_per_second: 18,
simulation_enabled: false,
anchor: null,
positions: [],
@@ -134,6 +135,7 @@ test("map view starts from the canonical Foundry camera without a fabricated sub
assert.equal(document.view.cacheIntent.enabled, true);
assert.equal(document.view.cacheIntent.noOverwrite, true);
assert.equal(document.view.ghostPins.count, 12);
assert.equal(document.view.ghostPins.maximumSpeedMetersPerSecond, 18);
assert.equal(document.view.ghostPins.positions.length, 0);
});
@@ -160,6 +162,7 @@ test("map view decodes and re-encodes the complete versioned contract", () => {
assert.equal(decoded.view.inspectorOpenSections[0], "camera");
assert.equal(encoded.view.camera.latitude, 55.7558);
assert.equal(encoded.view.cache_intent.no_overwrite, true);
assert.equal(encoded.view.ghost_pins.maximum_speed_meters_per_second, 18);
assert.equal(encoded.view.ghost_pins.presentation.stem_height_meters, 1500);
assert.equal("schema_version" in encoded, false);
});
@@ -184,6 +187,29 @@ test("map view fails closed on credentials, runtime URLs and unbound selection",
);
});
test("map view rejects a ghost pin faster than its configured maximum", () => {
assert.throws(
() => mapView.decodeMapViewDocument(serverDocument({
view: {
...serverDocument().view,
ghost_pins: {
...serverDocument().view.ghost_pins,
maximum_speed_meters_per_second: 5,
positions: [{
id: "ghost-fast-1",
label: "123456",
longitude: 37.618423,
latitude: 55.751244,
heading_degrees: 90,
speed_meters_per_second: 6,
}],
},
},
})),
/превышает настроенный максимум/i,
);
});
test("map workspace keeps the canonical top actions without a bottom toolbar", async () => {
const source = await readFile(
new URL("../src/workspaces/map/WorldMapWorkspace.tsx", import.meta.url),