mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
fix(maps): nominatim autocomplete and search fallback keep the location bias
On an install without the TREK Places index and without a Google key the suggestions come from Nominatim, and autocompleteNominatim asked it without the box the client had sent, so every suggestion was ranked globally. The search fallback on the same install dropped the bias the same way. Both now pass it on as a viewbox with bounded=0, which ranks results near the trip without hiding anything further away. Refs #2620
This commit is contained in:
@@ -393,7 +393,7 @@ export class MapsService {
|
||||
// first call carried a viewbox with bounded=0, which orders results
|
||||
// without changing which ones exist, so a second call can only return the
|
||||
// same empty list a throttle-interval later.
|
||||
const places = osmAnswer ?? (await this.osm.searchNominatim(query, lang));
|
||||
const places = osmAnswer ?? (await this.osm.searchNominatim(query, lang, 'interactive', locationBias));
|
||||
return { places, source: 'openstreetmap' };
|
||||
}
|
||||
|
||||
@@ -532,7 +532,7 @@ export class MapsService {
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
return this.osm.autocompleteNominatim(input, lang);
|
||||
return this.osm.autocompleteNominatim(input, lang, boxCentre);
|
||||
}
|
||||
|
||||
const google = this.googlePlaces.provider({ key: apiKey, source: keySource, userId });
|
||||
|
||||
@@ -267,9 +267,10 @@ export class OsmClient {
|
||||
async autocompleteNominatim(
|
||||
input: string,
|
||||
lang?: string,
|
||||
bias?: { lat: number; lng: number },
|
||||
): Promise<{ suggestions: { placeId: string; mainText: string; secondaryText: string }[]; source: 'nominatim' }> {
|
||||
try {
|
||||
const places = await this.searchNominatim(input, lang);
|
||||
const places = await this.searchNominatim(input, lang, 'interactive', bias);
|
||||
const suggestions = places
|
||||
.filter((p) => p.osm_id && p.osm_id.includes(':') && p.osm_id.split(':')[1] !== '')
|
||||
.slice(0, 5)
|
||||
|
||||
@@ -1413,6 +1413,20 @@ describe('searchPlaces (fetch stubbed)', () => {
|
||||
expect(Array.isArray(result.places)).toBe(true);
|
||||
});
|
||||
|
||||
// The same gap on the explicit search: with the index off and no key the
|
||||
// fallback asked Nominatim again and dropped the bias it had been given (#2620).
|
||||
it('MAPS-038b: the Nominatim fallback keeps the location bias', async () => {
|
||||
indexOff();
|
||||
const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => [] });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await svc.searchPlaces(999, 'Starbucks', 'en', { lat: 35, lng: 135.75 });
|
||||
|
||||
const url = new URL(String(fetchMock.mock.calls[0][0]));
|
||||
expect(url.searchParams.get('viewbox')).toBe('135.25,34.5,136.25,35.5');
|
||||
expect(url.searchParams.get('bounded')).toBe('0');
|
||||
});
|
||||
|
||||
// Session tokens: the keystrokes of one search and the details lookup that
|
||||
// ends it must reach Google under the same token, or every request is billed
|
||||
// on its own. The body field and the query parameter are what Google's
|
||||
@@ -1625,6 +1639,38 @@ describe('autocompletePlaces (fetch stubbed)', () => {
|
||||
expect(result.suggestions[0].placeId).toBe('node:1');
|
||||
});
|
||||
|
||||
// Without the index and without a key the suggestions come from Nominatim,
|
||||
// and the box the client already computed has to reach it (#2620). It ranks,
|
||||
// it does not restrict: bounded=0, so a far away place still comes back.
|
||||
it('MAPS-081b: hands the location bias to Nominatim as a viewbox', async () => {
|
||||
indexOff();
|
||||
const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => [] });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await svc.autocompletePlaces(999, 'Starbucks', 'en', {
|
||||
low: { lat: 34.9, lng: 135.6 },
|
||||
high: { lat: 35.1, lng: 135.9 },
|
||||
});
|
||||
|
||||
const url = new URL(String(fetchMock.mock.calls[0][0]));
|
||||
expect(url.pathname).toContain('/search');
|
||||
expect(url.searchParams.get('bounded')).toBe('0');
|
||||
const [west, south, east, north] = String(url.searchParams.get('viewbox')).split(',').map(Number);
|
||||
expect((west + east) / 2).toBeCloseTo(135.75);
|
||||
expect((south + north) / 2).toBeCloseTo(35);
|
||||
});
|
||||
|
||||
it('MAPS-081c: asks Nominatim without a viewbox when no bias was sent', async () => {
|
||||
indexOff();
|
||||
const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => [] });
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
await svc.autocompletePlaces(999, 'Starbucks', 'en');
|
||||
|
||||
const url = new URL(String(fetchMock.mock.calls[0][0]));
|
||||
expect(url.searchParams.has('viewbox')).toBe(false);
|
||||
});
|
||||
|
||||
it('MAPS-082: uses Google when user has an API key', async () => {
|
||||
mockDbGet.mockReturnValueOnce({ maps_api_key: 'ENCRYPTED' }).mockReturnValueOnce(null);
|
||||
vi.stubGlobal(
|
||||
|
||||
Reference in New Issue
Block a user