Skip choice for unique postcode matches

This commit is contained in:
Edward Betts 2026-08-15 12:09:24 +01:00
parent 71e1b57164
commit 7da704d837
4 changed files with 36 additions and 1 deletions

View file

@ -118,6 +118,7 @@ the search box. Selecting a stop shows its tags and stop-area-aware bus route
relations. Stop results identify their transport mode (such as bus, rail or
tram), and moving the map at zoom level 15 or closer loads stops in the visible
area. Wider map views deliberately do not query Overpass.
Postcode searches with exactly one Nominatim match skip the choice screen.
The current map bounds bias Nominatim's ranking without excluding UK matches
outside the visible area.

View file

@ -51,6 +51,11 @@ function parseCoordinates(value) {
return {lat, lon};
}
/** Return whether text has the shape of a complete UK postcode. */
function isUkPostcode(value) {
return /^(?:GIR\s*0AA|[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2})$/i.test(value.trim());
}
/** Put the current search in the address bar without reloading the page. */
function setSearchUrl(params, replace = false) {
const query = params.toString();
@ -305,6 +310,10 @@ function renderStops(data, fitMap = true, sortOrigin = null, preserveSelection =
/** Show UK-only Nominatim matches and wait for the user to choose one. */
function renderGeocodeChoices(data) {
if (data.locations.length === 1 && isUkPostcode(data.query)) {
chooseLocation(data.locations[0]);
return;
}
selectedStop = null;
markers.clearLayers();
if (searchMarker) { searchMarker.remove(); searchMarker = null; }

View file

@ -40,7 +40,7 @@
<tbody>
<tr>
<td><code>q</code></td>
<td>A postcode, street, place name, or exact ATCO code. Place searches show up to 20 UK-only matches to choose from.</td>
<td>A postcode, street, place name, or exact ATCO code. Place searches show up to 20 UK-only matches to choose from; a postcode with one match opens directly.</td>
<td><a href="{{ url_for('index', q='Bristol Temple Meads') }}"><code>{{ url_for('index') }}?q=Bristol+Temple+Meads</code></a></td>
</tr>
<tr>

View file

@ -191,6 +191,31 @@ def test_coordinate_url_and_search_input(chromium_browser: Any, flask_url: str)
page.close()
def test_single_postcode_match_opens_directly(chromium_browser: Any, flask_url: str) -> None:
"""A unique UK postcode result skips the location-choice panel."""
page = chromium_browser.new_page(viewport={"width": 1280, "height": 800})
page.route("**/api/search?*", lambda route: route.fulfill(json={
"kind": "geocode",
"query": "SW1A 1AA",
"locations": [{
"lat": 51.501, "lon": -0.142, "label": "Westminster, London",
"type": "postcode",
}],
}))
page.route("**/api/stops?*", lambda route: route.fulfill(json={
"kind": "location",
"location": {"lat": 51.501, "lon": -0.142, "label": "Westminster, London"},
"stops": [],
}))
page.goto(flask_url, wait_until="networkidle")
page.get_by_label("Location or ATCO code").fill("SW1A 1AA")
page.get_by_role("button", name="Search").click()
playwright_api.expect(page).to_have_url(re.compile(r"\?lat=51.501&lon=-0.142$"))
playwright_api.expect(page.locator("#stop-count")).to_have_text("0 found")
playwright_api.expect(page.locator("#geocode-panel")).to_be_hidden()
page.close()
def test_mobile_layout_opens_results_sheet(chromium_browser: Any, flask_url: str) -> None:
"""The finder exposes its search panel as an open mobile bottom sheet."""
page = chromium_browser.new_page(viewport={"width": 390, "height": 844})