Skip to content

Commit f39fd15

Browse files
DennisOSRMCopilot
andauthored
feat: add ?dst and ?src URL params for address-based deep-linking (#405)
Allow third-party sites to link to the OSRM frontend with a pre-set destination (and optionally an origin) by address string, without needing to look up coordinates first. New URL parameters: ?dst=<address> destination address (geocoded on load) ?src=<address> origin address (geocoded on load, optional) These are one-time deep-link parameters. On page load they are resolved via Nominatim and replaced in the URL by loc= coordinate pairs through the existing state machinery. They are ignored when loc= waypoints are already present, so existing sessions are not affected. Example usage: https://map.project-osrm.org/?dst=Berlin https://map.project-osrm.org/?src=Paris&dst=Berlin Changes: - src/links.js: parse q.src/q.dst → originAddress/destinationAddress - src/index.js: geocode address params after LRM init, set waypoints - test/links.test.js: 10 new unit tests for parse/format behaviour Closes #337 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 47db963 commit f39fd15

3 files changed

Lines changed: 121 additions & 0 deletions

File tree

src/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,42 @@ plan.on('waypointgeocoded', function(e) {
208208
}
209209
});
210210

211+
// If dst/src address params were passed and no loc= waypoints exist, geocode them now.
212+
(function applyAddressParams() {
213+
var hasLocWaypoints = mergedOptions.waypoints && mergedOptions.waypoints.some(function(wp) {
214+
return wp && wp.latLng;
215+
});
216+
if (hasLocWaypoints) return;
217+
218+
var srcAddr = mergedOptions.originAddress;
219+
var dstAddr = mergedOptions.destinationAddress;
220+
if (!srcAddr && !dstAddr) return;
221+
222+
var geocoder = createGeocoder.coordPreserving();
223+
224+
function geocodeAddress(addr, cb) {
225+
if (!addr) {
226+
cb(null);
227+
return;
228+
}
229+
geocoder.geocode(addr, function(results) {
230+
cb(results && results.length > 0 ? results[0] : null);
231+
});
232+
}
233+
234+
geocodeAddress(srcAddr, function(srcResult) {
235+
geocodeAddress(dstAddr, function(dstResult) {
236+
var origin = srcResult
237+
? L.Routing.waypoint(srcResult.center, srcResult.name)
238+
: L.Routing.waypoint(null, srcAddr || '');
239+
var destination = dstResult
240+
? L.Routing.waypoint(dstResult.center, dstResult.name)
241+
: L.Routing.waypoint(null, dstAddr || '');
242+
lrmControl.setWaypoints([origin, destination]);
243+
});
244+
});
245+
}());
246+
211247
// add onClick event
212248
map.on('click', function (e) {
213249
addWaypoint(e.latlng);

src/links.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ function parseLink(link) {
8080
parsedValues.units = q.df;
8181
parsedValues.layer = q.ly;
8282
parsedValues.service = q.srv;
83+
parsedValues.originAddress = q.src;
84+
parsedValues.destinationAddress = q.dst;
8385
} catch (e) {
8486
console.log("Exception " + e.name + ": " + e.message);
8587
}

test/links.test.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
3+
// Mock leaflet and leaflet-routing-machine so tests run in Node without a DOM
4+
jest.mock('leaflet', () => ({
5+
latLng: function(lat, lng) { return { lat: lat, lng: lng }; },
6+
Routing: {
7+
waypoint: function(latlng, name) { return { latLng: latlng, name: name || '' }; }
8+
}
9+
}));
10+
jest.mock('jsonp', () => {});
11+
12+
const links = require('../src/links');
13+
14+
describe('links.parse — dst/src address parameters', () => {
15+
test('parses ?dst into destinationAddress', () => {
16+
const result = links.parse('dst=Berlin');
17+
expect(result.destinationAddress).toBe('Berlin');
18+
});
19+
20+
test('parses ?src into originAddress', () => {
21+
const result = links.parse('src=Paris');
22+
expect(result.originAddress).toBe('Paris');
23+
});
24+
25+
test('parses both ?src and ?dst together', () => {
26+
const result = links.parse('src=Paris&dst=Berlin');
27+
expect(result.originAddress).toBe('Paris');
28+
expect(result.destinationAddress).toBe('Berlin');
29+
});
30+
31+
test('parses address strings with spaces and special characters', () => {
32+
const result = links.parse('dst=New%20York%2C%20NY');
33+
expect(result.destinationAddress).toBe('New York, NY');
34+
});
35+
36+
test('returns undefined originAddress when src is absent', () => {
37+
const result = links.parse('dst=Berlin');
38+
expect(result.originAddress).toBeUndefined();
39+
});
40+
41+
test('returns undefined destinationAddress when dst is absent', () => {
42+
const result = links.parse('src=Paris');
43+
expect(result.destinationAddress).toBeUndefined();
44+
});
45+
46+
test('dst/src absent from result when empty string', () => {
47+
const result = links.parse('dst=&src=');
48+
// Empty strings are filtered out by the existing options filtering logic
49+
expect(result.destinationAddress).toBeUndefined();
50+
expect(result.originAddress).toBeUndefined();
51+
});
52+
});
53+
54+
describe('links.format — dst/src are not serialized', () => {
55+
test('formatLink does not include dst in output', () => {
56+
const L = require('leaflet');
57+
const output = links.format({
58+
zoom: 13,
59+
center: L.latLng(52.5, 13.4),
60+
waypoints: [],
61+
language: 'en',
62+
destinationAddress: 'Berlin',
63+
originAddress: 'Paris'
64+
});
65+
expect(output).not.toContain('dst=');
66+
expect(output).not.toContain('src=');
67+
});
68+
});
69+
70+
describe('links.parse — existing loc= params still work', () => {
71+
test('parses loc= coordinate pairs normally', () => {
72+
const result = links.parse('loc=52.5,13.4&loc=48.8,2.3');
73+
expect(result.waypoints).toHaveLength(2);
74+
expect(result.waypoints[0].latLng.lat).toBeCloseTo(52.5);
75+
expect(result.waypoints[1].latLng.lat).toBeCloseTo(48.8);
76+
});
77+
78+
test('dst and src are parsed alongside loc= without conflict', () => {
79+
const result = links.parse('loc=52.5,13.4&loc=48.8,2.3&dst=Lyon');
80+
expect(result.waypoints).toHaveLength(2);
81+
expect(result.destinationAddress).toBe('Lyon');
82+
});
83+
});

0 commit comments

Comments
 (0)