mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +00:00
fix(street-level): Panoramax UX QA — connection budget, coverage to 60 km, Esc, attribution
From a UX QA round in the browser against the live API:
- A line click could time out while coverage loaded: the meta-catalogue
speaks HTTP/1.1 only (six connections), and six tiles plus four
searches queued the lookup behind megabyte tiles. Tiles now take
three connections and searches two; a search that timed out is asked
once more.
- Between 30 and ~100 km Paris read as empty: the density grid is ~10 km
a cell. Lines now draw to 60 km (z10), as Mapillary's do; above, dots
grow with the pictures they stand for.
- Esc in the docked viewer collapsed the whole panel instead of clearing
the sequence the panel says it clears (shared photo viewer: KartaView
too). The viewer now offers Esc to the provider first; expanded, the
dialog keeps it.
- A picture whose producer is an email address is credited to the
instance holding it (its `via` host), so Licence Ouverte pictures name
their source.
- Empty states say why: "no Panoramax photos here match the filters"
when the filter hid every line, and nothing to zoom into over empty
sea. Wording follows KartaView's ("photos", "click a line for its
nearest photo").
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
bce3044e0c
commit
5526666894
14 files changed
+317
-45
No files matched your search
@@ -29,15 +29,17 @@ export const COLORS = Object.freeze({
|
||||
});
|
||||
|
||||
/**
|
||||
* Sequence lines come from z7–15 tiles; z11–14 are drawn (a dense z11 tile
|
||||
* over Paris is ~1.4 MB and 3,800 sequences, z12 ~800 kB). Higher up, the
|
||||
* z0–5 `grid` layer's density cells (0.1°, ~10 km) draw as points: below
|
||||
* 30 km they would be a few dots over a whole city.
|
||||
* Sequence lines come from z7–15 tiles; z10–14 are drawn below 60 km, as
|
||||
* Mapillary's are (a dense tile over Paris: z10 ~2.1 MB and 8,600
|
||||
* sequences, z11 ~1.4 MB, z12 ~800 kB). Higher up, the z0–5 `grid` layer's
|
||||
* density cells (0.1°, ~10 km) draw as points: lower down they would be a
|
||||
* handful of dots over a whole city, which reads as no coverage.
|
||||
*/
|
||||
export const COVERAGE_Z14_BELOW_M = 1_800;
|
||||
export const COVERAGE_Z13_BELOW_M = 5_000;
|
||||
export const COVERAGE_Z12_BELOW_M = 12_000;
|
||||
export const COVERAGE_LINES_BELOW_M = 30_000;
|
||||
export const COVERAGE_Z11_BELOW_M = 30_000;
|
||||
export const COVERAGE_LINES_BELOW_M = 60_000;
|
||||
export const COVERAGE_TUNING = Object.freeze({
|
||||
maxTiles: 9,
|
||||
overviewMaxTiles: 6,
|
||||
@@ -62,12 +64,14 @@ export const OVERVIEW_BINS_PER_AXIS = 96;
|
||||
|
||||
export const RATE_LIMITED_MESSAGE =
|
||||
'Panoramax is busy — coverage resumes on its own';
|
||||
export const NO_COVERAGE_HINT = 'no Panoramax pictures in view';
|
||||
export const NO_COVERAGE_HINT = 'no Panoramax photos in view';
|
||||
/** Lines in view, all hidden by the imagery filter. */
|
||||
export const FILTERED_HINT = 'no Panoramax photos here match the filters';
|
||||
export const UNREADABLE_TILE_MESSAGE =
|
||||
'Panoramax sent a map tile that could not be read';
|
||||
export const GONE_MESSAGE = 'This Panoramax picture is no longer available';
|
||||
/** From high up the dots are where pictures are, not sequences to click. */
|
||||
export const OVERVIEW_HINT = 'zoom in to click a Panoramax sequence';
|
||||
export const OVERVIEW_HINT = 'zoom in to see Panoramax sequences';
|
||||
|
||||
/** Cones come from z15 tiles' `pictures` layer (~110 bytes a picture). */
|
||||
export const PICTURE_TILE_ZOOM = 15;
|
||||
@@ -78,9 +82,13 @@ export const TILE_TIMEOUT_MS = 20_000;
|
||||
export const API_TIMEOUT_MS = 12_000;
|
||||
/** Real tiles are under 6 MB (z6 over France); refuse anything absurd. */
|
||||
export const MAX_RESPONSE_BYTES = 16 * 1024 * 1024;
|
||||
/** Requests at once to the API (tiles have their own budget). */
|
||||
export const API_CONCURRENCY = 4;
|
||||
export const TILE_CONCURRENCY = 6;
|
||||
/**
|
||||
* The meta-catalogue speaks HTTP/1.1 only, so the browser opens six
|
||||
* connections to it: tiles take three, searches two, so a click's lookup
|
||||
* never queues behind megabyte tiles (it timed out doing so).
|
||||
*/
|
||||
export const API_CONCURRENCY = 2;
|
||||
export const TILE_CONCURRENCY = 3;
|
||||
|
||||
/** Pictures asked for around a point; the API returns them nearest first. */
|
||||
export const NEAREST_LIMIT = 10;
|
||||
@@ -110,7 +118,7 @@ export const SD_WIDTH = 2_048;
|
||||
export const PANORAMAX_CREDIT_HTML =
|
||||
'Street Level: imagery © <a href="https://panoramax.fr" target="_blank" rel="noopener">Panoramax</a> contributors, licence per picture';
|
||||
|
||||
export const LINE_CLICK_HINT = 'click a line for the photo there';
|
||||
export const LINE_CLICK_HINT = 'click a line for its nearest photo';
|
||||
|
||||
/** Licence ids Panoramax uses, as the caption names them. */
|
||||
const LICENSE_LABELS = Object.freeze({
|
||||
@@ -194,8 +202,20 @@ function creatorOf(item) {
|
||||
: []),
|
||||
];
|
||||
const name = names.find((value) => typeof value === 'string' && value.trim());
|
||||
if (!name || looksLikeEmail(name)) return null;
|
||||
return name.trim().slice(0, 80);
|
||||
if (name && !looksLikeEmail(name)) return name.trim().slice(0, 80);
|
||||
// Licences ask for the source to be named: the instance that holds it.
|
||||
return instanceOf(item);
|
||||
}
|
||||
|
||||
/** The federated instance holding a picture (its `via` link's host), or null. */
|
||||
function instanceOf(item) {
|
||||
const href = item?.links?.find?.((link) => link?.rel === 'via')?.href;
|
||||
try {
|
||||
const url = new URL(String(href));
|
||||
return url.protocol === 'https:' ? url.hostname.slice(0, 80) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** A 360° picture: a full-sphere field of view or an equirectangular projection. */
|
||||
|
||||
Reference in new issue
Block a user