Reuse stops when zooming within loaded map bounds
This commit is contained in:
parent
e8d0ff5c2c
commit
8ed7b8d112
2 changed files with 17 additions and 23 deletions
|
|
@ -17,6 +17,7 @@ let hoveredMarkerHalo = null;
|
||||||
let userMapInteractionPending = false;
|
let userMapInteractionPending = false;
|
||||||
let mapMoveTimer = null;
|
let mapMoveTimer = null;
|
||||||
let mapRequestController = null;
|
let mapRequestController = null;
|
||||||
|
let loadedMapBounds = null;
|
||||||
let returnToResults = false;
|
let returnToResults = false;
|
||||||
let returnViaHistory = false;
|
let returnViaHistory = false;
|
||||||
const MIN_MAP_STOP_ZOOM = 15;
|
const MIN_MAP_STOP_ZOOM = 15;
|
||||||
|
|
@ -167,25 +168,6 @@ async function loadSharedStop(type, id) {
|
||||||
const response = await fetch(stopApiUrl(API_URLS.stopTemplate, type, id));
|
const response = await fetch(stopApiUrl(API_URLS.stopTemplate, type, id));
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.message || 'Could not load that stop.');
|
if (!response.ok) throw new Error(data.message || 'Could not load that stop.');
|
||||||
let nearbyData = {
|
|
||||||
kind: 'location',
|
|
||||||
location: {lat: data.stop.lat, lon: data.stop.lon, label: data.stop.name},
|
|
||||||
stops: [data.stop],
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
const nearbyParams = new URLSearchParams({lat: data.stop.lat, lon: data.stop.lon});
|
|
||||||
const nearbyResponse = await fetch(`${API_URLS.stops}?${nearbyParams}`);
|
|
||||||
const nearbyResult = await nearbyResponse.json();
|
|
||||||
if (nearbyResponse.ok) nearbyData = nearbyResult;
|
|
||||||
} catch (_) {
|
|
||||||
// The selected stop is still useful if the optional nearby lookup fails.
|
|
||||||
}
|
|
||||||
const selectedIndex = nearbyData.stops.findIndex(
|
|
||||||
stop => stop.type === data.stop.type && stop.id === data.stop.id
|
|
||||||
);
|
|
||||||
if (selectedIndex === -1) nearbyData.stops.unshift(data.stop);
|
|
||||||
else nearbyData.stops[selectedIndex] = data.stop;
|
|
||||||
renderStops(nearbyData, true);
|
|
||||||
returnToResults = true;
|
returnToResults = true;
|
||||||
returnViaHistory = false;
|
returnViaHistory = false;
|
||||||
await selectStop(data.stop, false);
|
await selectStop(data.stop, false);
|
||||||
|
|
@ -245,6 +227,7 @@ function setSearchMarker(locationResult, label) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderStops(data, fitMap = true, sortOrigin = null, preserveSelection = false) {
|
function renderStops(data, fitMap = true, sortOrigin = null, preserveSelection = false) {
|
||||||
|
if (data.kind !== 'map') loadedMapBounds = null;
|
||||||
const origin = data.location || sortOrigin;
|
const origin = data.location || sortOrigin;
|
||||||
stops = [...data.stops]
|
stops = [...data.stops]
|
||||||
.map(stop => ({...stop, distance_metres: distanceMetres(stop, origin)}))
|
.map(stop => ({...stop, distance_metres: distanceMetres(stop, origin)}))
|
||||||
|
|
@ -592,6 +575,7 @@ async function loadVisibleMapStops() {
|
||||||
if (map.getZoom() < MIN_MAP_STOP_ZOOM) {
|
if (map.getZoom() < MIN_MAP_STOP_ZOOM) {
|
||||||
markers.clearLayers();
|
markers.clearLayers();
|
||||||
clearHoveredStop();
|
clearHoveredStop();
|
||||||
|
loadedMapBounds = null;
|
||||||
byId('map-status').textContent = 'Zoom in to load transport stops';
|
byId('map-status').textContent = 'Zoom in to load transport stops';
|
||||||
show('map-status');
|
show('map-status');
|
||||||
return;
|
return;
|
||||||
|
|
@ -609,6 +593,14 @@ async function loadVisibleMapStops() {
|
||||||
lat: center.lat.toFixed(6), lon: center.lng.toFixed(6),
|
lat: center.lat.toFixed(6), lon: center.lng.toFixed(6),
|
||||||
}), true);
|
}), true);
|
||||||
}
|
}
|
||||||
|
if (loadedMapBounds && loadedMapBounds.contains(bounds)) {
|
||||||
|
if (mapRequestController) {
|
||||||
|
mapRequestController.abort();
|
||||||
|
mapRequestController = null;
|
||||||
|
}
|
||||||
|
show('map-status', false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
byId('map-status').textContent = 'Loading stops in this area…';
|
byId('map-status').textContent = 'Loading stops in this area…';
|
||||||
show('map-status');
|
show('map-status');
|
||||||
if (mapRequestController) mapRequestController.abort();
|
if (mapRequestController) mapRequestController.abort();
|
||||||
|
|
@ -619,6 +611,8 @@ async function loadVisibleMapStops() {
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.message || 'Could not load this map area.');
|
if (!response.ok) throw new Error(data.message || 'Could not load this map area.');
|
||||||
|
// A capped result may be incomplete, so do not reuse its coverage when zooming in.
|
||||||
|
loadedMapBounds = data.stops.length < 1000 ? bounds : null;
|
||||||
renderStops(data, false, map.getCenter(), Boolean(selectedStop));
|
renderStops(data, false, map.getCenter(), Boolean(selectedStop));
|
||||||
show('map-status', false);
|
show('map-status', false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ def test_search_and_stop_details_in_browser(chromium_browser: Any, flask_url: st
|
||||||
|
|
||||||
|
|
||||||
def test_moving_map_loads_only_when_zoomed_in(chromium_browser: Any, flask_url: str) -> None:
|
def test_moving_map_loads_only_when_zoomed_in(chromium_browser: Any, flask_url: str) -> None:
|
||||||
"""Panning a close map loads visible stops while a wide map makes no query."""
|
"""Map loads skip wide views and zoom-ins already covered by fetched bounds."""
|
||||||
page = chromium_browser.new_page(viewport={"width": 1280, "height": 800})
|
page = chromium_browser.new_page(viewport={"width": 1280, "height": 800})
|
||||||
requests: list[str] = []
|
requests: list[str] = []
|
||||||
|
|
||||||
|
|
@ -154,13 +154,13 @@ def test_moving_map_loads_only_when_zoomed_in(chromium_browser: Any, flask_url:
|
||||||
playwright_api.expect(page.locator("#stop-name")).to_have_text("Temple Meads")
|
playwright_api.expect(page.locator("#stop-name")).to_have_text("Temple Meads")
|
||||||
page.wait_for_timeout(700)
|
page.wait_for_timeout(700)
|
||||||
playwright_api.expect(page.locator("#stop-detail")).to_be_visible()
|
playwright_api.expect(page.locator("#stop-detail")).to_be_visible()
|
||||||
assert len(requests) == 3
|
assert len(requests) == 2
|
||||||
page.evaluate("userMapInteractionPending = true; map.panBy([80, 0])")
|
page.evaluate("userMapInteractionPending = true; map.panBy([2500, 0])")
|
||||||
page.wait_for_timeout(700)
|
page.wait_for_timeout(700)
|
||||||
playwright_api.expect(page.locator("#stop-name")).to_have_text("Temple Meads")
|
playwright_api.expect(page.locator("#stop-name")).to_have_text("Temple Meads")
|
||||||
playwright_api.expect(page.locator("#stop-detail")).to_be_visible()
|
playwright_api.expect(page.locator("#stop-detail")).to_be_visible()
|
||||||
playwright_api.expect(page).to_have_url(re.compile(r"\?node=456$"))
|
playwright_api.expect(page).to_have_url(re.compile(r"\?node=456$"))
|
||||||
assert len(requests) == 4
|
assert len(requests) == 3
|
||||||
page.close()
|
page.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue