fix(travel): revert to clean Ulys /legs implementation as requested

This commit is contained in:
ElPoyo
2026-06-05 10:14:19 +02:00
parent 20c44cfb8b
commit 8c01a21728
+29 -92
View File
@@ -49,14 +49,8 @@ function loadTollStations() {
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
async function getUlysTollLegs(encodedPolyline) { async function getUlysTollLegs(encodedPolyline) {
try { try {
// La polyline Google est encodée avec une précision de 5.
// L'API Ulys (precision=6) attend une précision de 6.
// On la décode (precision 5) puis on la réencode (precision 6).
const decodedCoords = polylineLib.decode(encodedPolyline, 5);
const polyline6 = polylineLib.encode(decodedCoords, 6);
const url = 'https://api-ulys.azure-api.net/placemark/v2/legs?precision=6&includeLayersIds=GaresPeage'; const url = 'https://api-ulys.azure-api.net/placemark/v2/legs?precision=6&includeLayersIds=GaresPeage';
const body = JSON.stringify(polyline6); const body = JSON.stringify(encodedPolyline);
const res = await axios.post(url, body, { const res = await axios.post(url, body, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -102,15 +96,12 @@ async function getUlysRate(vehicleCategory, passages) {
); );
const data = res.data; const data = res.data;
if (Array.isArray(data) && data.length > 0) { if (Array.isArray(data) && data.length > 0) {
if (passages.length === 2) { // Système fermé : 1 réponse avec entranceToll + exitToll
if (data.length !== 1 || !data[0].exitToll) return null;
return data[0].price > 0 ? data[0].price : null;
} else {
if (data.length === 1 && data[0].price > 0) return data[0].price; if (data.length === 1 && data[0].price > 0) return data[0].price;
// Système ouvert (barrières individuelles) : sommer les prix
const total = data.reduce((sum, d) => sum + (d.price || 0), 0); const total = data.reduce((sum, d) => sum + (d.price || 0), 0);
return total > 0 ? total : null; return total > 0 ? total : null;
} }
}
return null; return null;
} catch (e) { } catch (e) {
return null; return null;
@@ -122,101 +113,47 @@ async function getUlysRate(vehicleCategory, passages) {
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
async function calculateTollCost(encodedPolyline, vehicleCategory) { async function calculateTollCost(encodedPolyline, vehicleCategory) {
try { try {
const polylineCoords = polylineLib.decode(encodedPolyline, 5); // 1. Demander à Ulys les gares sur le tracé
// Appel direct à Ulys avec le segment entier (pas de sous-échantillonnage pour éviter de rater des gares)
const legsData = await getUlysTollLegs(encodedPolyline); const legsData = await getUlysTollLegs(encodedPolyline);
const items = Array.isArray(legsData) ? legsData : (legsData.features || []); if (legsData && Array.isArray(legsData.features) && legsData.features.length > 0) {
let tollGates = []; // Extraire les gares dans l'ordre du tracé
const tollGates = [];
for (const feature of legsData.features) {
const props = feature.properties || {};
if (items.length > 0) { // La réponse Ulys peut utiliser différents noms de champs
for (const item of items) { // On cherche l'identifiant de la gare dans tous les champs connus
const pm = item.Placemark || item.placemark || {}; const id =
const tags = pm.Tags || pm.tags || {}; props.id_gare ||
const props = pm.properties || {}; props.idGare ||
props.id ||
props.gareId ||
props.gare_id ||
props.tollStationId;
let rawId = tags.ID_PEAGE || tags.id_peage || (pm.Code || '').split('_')[0] || props.id_gare || props.idGare || props.id; if (!id) continue;
if (!rawId) continue; const idStr = String(id);
const idStr = String(rawId).replace(/\D/g, '');
if (idStr.length < 5) continue; if (idStr.length < 5) continue;
if (!tollGates.find((g) => g.id === idStr)) {
tollGates.push({ tollGates.push({
id: idStr, id: idStr,
operatorId: idStr.substring(0, 2), operatorId: idStr.substring(0, 2),
tollId: idStr.substring(2, 5), tollId: idStr.substring(2, 5),
name: pm.Preview || pm.preview || tags.nom || idStr, name: props.nom || props.name || props.label || idStr,
}); });
} }
}
}
const stations = await loadTollStations();
// Fallback: Si Ulys rate le premier système fermé entier (ex: ASF sur Lyon-Nice)
let missingSystemPrice = 0;
if (tollGates.length > 0 && polylineCoords.length > 0) {
const originLat = polylineCoords[0][0];
const originLng = polylineCoords[0][1];
const firstGateInfo = stations.find(s => s.id === tollGates[0].id);
if (firstGateInfo) {
const distToFirstGate = _distKm(originLat, originLng, firstGateInfo.lat, firstGateInfo.lon);
if (distToFirstGate > 50) {
logger.info(`[Travel] First gate is ${distToFirstGate.toFixed(1)}km from origin. Checking for missing system...`);
let firstGateIndex = 0;
for (let i = 0; i < polylineCoords.length; i++) {
if (_distKm(polylineCoords[i][0], polylineCoords[i][1], firstGateInfo.lat, firstGateInfo.lon) < 1) {
firstGateIndex = i; break;
}
}
const candidates = [];
stations.forEach(s => {
let minDist = Infinity;
let minIndex = -1;
for (let i = 0; i < firstGateIndex; i++) {
const d = _distKm(s.lat, s.lon, polylineCoords[i][0], polylineCoords[i][1]);
if (d < minDist) { minDist = d; minIndex = i; }
}
if (minDist < 2) {
candidates.push({ ...s, polyIndex: minIndex });
}
});
candidates.sort((a, b) => a.polyIndex - b.polyIndex);
if (candidates.length >= 2) {
let found = false;
for (let i = 0; i < Math.min(10, candidates.length); i++) {
for (let j = candidates.length - 1; j > i && j > candidates.length - 10; j--) {
if (candidates[i].operatorId !== candidates[j].operatorId) continue;
const price = await getUlysRate(vehicleCategory, [candidates[i], candidates[j]]);
if (price) {
logger.info(`[Travel] Found missing system: ${candidates[i].name} -> ${candidates[j].name} = ${price}`);
missingSystemPrice += price;
found = true;
break;
}
}
if (found) break;
}
}
}
}
}
if (tollGates.length === 0 && missingSystemPrice === 0) {
logger.info('[Travel] Ulys /legs returned no toll gates for this route');
return 0;
}
logger.info(`[Travel] Ulys /legs found ${tollGates.length} toll gates`); logger.info(`[Travel] Ulys /legs found ${tollGates.length} toll gates`);
const baseToll = await _computeTollFromGates(tollGates, vehicleCategory); if (tollGates.length === 0) return 0;
return baseToll + missingSystemPrice;
// Greedy: trouver les segments fermés + barrières ouvertes
return await _computeTollFromGates(tollGates, vehicleCategory);
}
// Fallback : pas de résultat Ulys /legs, retourner 0
logger.info('[Travel] Ulys /legs returned no toll gates for this route');
return 0;
} catch (e) { } catch (e) {
logger.error('[Travel] calculateTollCost error:', e); logger.error('[Travel] calculateTollCost error:', e);
return 0; return 0;