feat(street-level): IMAGERY and SINCE filters for KartaView

KartaView filters coverage on its server only in its PNG tiles (its JSON
tiles come back empty with a filter), so a filtered tile's PNG is a mask
over the cached JSON segments: a segment stays when at least half the
points along it lie on drawn pixels. A filter change reloads the lines
(lineCoverage.reload keeps the drawn ones until the new ones are in);
masks are cached per filter and tile, and a filtered tile KartaView is
still drawing (504 or a timeout) is asked again, four tries in all.
The nearest lookup asks the photo API for the projection and date, and
the cones follow the filter like Mapillary's. The gate fixture draws
filtered PNG tiles and adds an older flat-only street; a new gate step
checks 360 and LAST YEAR thin KartaView's lines and ALL brings them back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-14 15:02:14 +01:00
1 parent 88a3ee3d44
commit b42ab9bb02
16 files changed
+961 -78

No files matched your search

+68 -2
View File
@@ -888,14 +888,14 @@ async function main() {
await sleep(1000);
}
await step(
'KartaView: its chip draws keyless coverage, credits KartaView and hides the filters',
'KartaView: its chip draws keyless coverage, credits KartaView and offers Mapillary’s filters',
async () => {
await clickPanelControl(page, kvChip);
assert.ok(await lit('kartaview'), 'KartaView alone is lit');
await waitForCoverage(page);
const u = await ui();
assert.equal(u.keyRequired, false);
assert.deepEqual(u.filterControls, { pano: false, since: false });
assert.deepEqual(u.filterControls, { pano: true, since: true });
assert.equal(
await page.$eval(kvChip, (chip) =>
chip.style.getPropertyValue('--chip-color'),
@@ -924,6 +924,72 @@ async function main() {
);
},
);
await step(
'KartaView: 360° and SINCE thin its lines through KartaView’s filtered tiles, and ALL / ANY DATE bring them back',
async () => {
const before = (await ui()).coverage.count;
const setStop = (index) =>
page.$eval(
'#sl-since',
(input, value) => {
input.value = String(value);
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
},
index,
);
// Filtered tiles are drawn by KartaView on demand: give them time.
const settled = async (test, arg, what) =>
assert.ok(
await uiUntil(test, arg, { timeout: 60_000 }),
`${what}: ${JSON.stringify((await ui()).coverage)}`,
);
await clickPanelControl(page, '[data-sl-pano="pano"]');
await settled(
(u) => u.filter.pano === 'pano' && !u.coverage.loading,
null,
'360° drawn',
);
const panos = (await ui()).coverage.count;
assert.ok(panos <= before, `${panos} ≤ ${before}`);
// Fixtures: the old street has only flat photos, the drive 360° too.
if (fixtures)
assert.ok(panos > 0 && panos < before, `0 < ${panos} < ${before}`);
await clickPanelControl(page, '[data-sl-pano="all"]');
await settled(
(u, n) =>
u.filter.pano === 'all' &&
!u.coverage.loading &&
u.coverage.count === n,
before,
'every line again',
);
await setStop(5);
await settled(
(u) => u.filter.sinceDays === 365 && !u.coverage.loading,
null,
'LAST YEAR drawn',
);
const lastYear = (await ui()).coverage.count;
assert.ok(lastYear <= before, `${lastYear} ≤ ${before}`);
// Fixtures: the drive is a month old, the old street from 2019.
if (fixtures)
assert.ok(
lastYear > 0 && lastYear < before,
`0 < ${lastYear} < ${before}`,
);
await setStop(0);
await settled(
(u, n) =>
u.filter.sinceDays === 0 &&
!u.coverage.loading &&
u.coverage.count === n,
before,
'ANY DATE: every line again',
);
assert.equal((await ui()).coverage.error, null);
},
);
await step(
'KartaView: a click on a line opens the nearest photo where the camera is, cones follow its drive, and › steps along it, past an archived photo',
async ({ skip }) => {